From 0631441b20386562641ce579c0bc5032f905f8f4 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Jan 2013 18:19:54 +0000 Subject: [PATCH] Android support, part 2 (display module) --- panda/metalibs/pandagles/Sources.pp | 2 +- panda/metalibs/pandagles/pandagles.cxx | 15 +- .../src/androiddisplay/androidGraphicsPipe.I | 14 + .../androiddisplay/androidGraphicsPipe.cxx | 233 ++++++ .../src/androiddisplay/androidGraphicsPipe.h | 94 +++ .../androidGraphicsStateGuardian.I | 25 + .../androidGraphicsStateGuardian.cxx | 403 +++++++++ .../androidGraphicsStateGuardian.h | 97 +++ .../androiddisplay/androidGraphicsWindow.I | 14 + .../androiddisplay/androidGraphicsWindow.cxx | 771 ++++++++++++++++++ .../androiddisplay/androidGraphicsWindow.h | 99 +++ .../androiddisplay/config_androiddisplay.cxx | 90 ++ .../androiddisplay/config_androiddisplay.h | 43 + .../p3androiddisplay_composite1.cxx | 6 + 14 files changed, 1901 insertions(+), 5 deletions(-) create mode 100644 panda/src/androiddisplay/androidGraphicsPipe.I create mode 100644 panda/src/androiddisplay/androidGraphicsPipe.cxx create mode 100644 panda/src/androiddisplay/androidGraphicsPipe.h create mode 100644 panda/src/androiddisplay/androidGraphicsStateGuardian.I create mode 100644 panda/src/androiddisplay/androidGraphicsStateGuardian.cxx create mode 100644 panda/src/androiddisplay/androidGraphicsStateGuardian.h create mode 100644 panda/src/androiddisplay/androidGraphicsWindow.I create mode 100644 panda/src/androiddisplay/androidGraphicsWindow.cxx create mode 100644 panda/src/androiddisplay/androidGraphicsWindow.h create mode 100644 panda/src/androiddisplay/config_androiddisplay.cxx create mode 100644 panda/src/androiddisplay/config_androiddisplay.h create mode 100644 panda/src/androiddisplay/p3androiddisplay_composite1.cxx diff --git a/panda/metalibs/pandagles/Sources.pp b/panda/metalibs/pandagles/Sources.pp index 4f070c75ec..06dd9c2837 100644 --- a/panda/metalibs/pandagles/Sources.pp +++ b/panda/metalibs/pandagles/Sources.pp @@ -9,7 +9,7 @@ #define BUILD_DIRECTORY $[HAVE_GLES] #define COMPONENT_LIBS \ - p3glesgsg p3egldisplay + p3glesgsg p3egldisplay p3androiddisplay #define LOCAL_LIBS p3gsgbase p3display p3express #define OTHER_LIBS p3interrogatedb:c p3dconfig:c p3dtoolconfig:m \ diff --git a/panda/metalibs/pandagles/pandagles.cxx b/panda/metalibs/pandagles/pandagles.cxx index 9e68de49c6..ff9ea8c43e 100644 --- a/panda/metalibs/pandagles/pandagles.cxx +++ b/panda/metalibs/pandagles/pandagles.cxx @@ -1,5 +1,5 @@ // Filename: pandagles.cxx -// Created by: pro-rsoft (8Jun09) +// Created by: rdb (8Jun09) // //////////////////////////////////////////////////////////////////// @@ -8,7 +8,10 @@ #define OPENGLES_1 #include "config_glesgsg.h" -#ifdef HAVE_EGL +#if defined(ANDROID) +#include "config_androiddisplay.h" +#include "androidGraphicsPipe.h" +#elif defined(HAVE_EGL) #include "config_egldisplay.h" #include "eglGraphicsPipe.h" #endif @@ -31,7 +34,9 @@ void init_libpandagles() { init_libglesgsg(); -#ifdef HAVE_EGL +#if defined(ANDROID) + init_libandroiddisplay(); +#elif defined(HAVE_EGL) init_libegldisplay(); #endif } @@ -43,7 +48,9 @@ init_libpandagles() { //////////////////////////////////////////////////////////////////// int get_pipe_type_pandagles() { -#ifdef HAVE_EGL +#if defined(ANDROID) + return AndroidGraphicsPipe::get_class_type().get_index(); +#elif defined(HAVE_EGL) return eglGraphicsPipe::get_class_type().get_index(); #endif diff --git a/panda/src/androiddisplay/androidGraphicsPipe.I b/panda/src/androiddisplay/androidGraphicsPipe.I new file mode 100644 index 0000000000..7af4f55bd2 --- /dev/null +++ b/panda/src/androiddisplay/androidGraphicsPipe.I @@ -0,0 +1,14 @@ +// Filename: androidGraphicsPipe.I +// Created by: rdb (11Jan13) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/androiddisplay/androidGraphicsPipe.cxx b/panda/src/androiddisplay/androidGraphicsPipe.cxx new file mode 100644 index 0000000000..53e6240700 --- /dev/null +++ b/panda/src/androiddisplay/androidGraphicsPipe.cxx @@ -0,0 +1,233 @@ +// Filename: androidGraphicsPipe.cxx +// Created by: rdb (11Jan13) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +//#include "androidGraphicsBuffer.h" +#include "androidGraphicsPipe.h" +//#include "androidGraphicsPixmap.h" +#include "androidGraphicsWindow.h" +#include "androidGraphicsStateGuardian.h" +#include "config_androiddisplay.h" +#include "frameBufferProperties.h" + +TypeHandle AndroidGraphicsPipe::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsPipe::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AndroidGraphicsPipe:: +AndroidGraphicsPipe() { + _is_valid = false; + _supported_types = OT_window | OT_buffer | OT_texture_buffer; + _egl_display = NULL; + + _display_width = 0; + _display_height = 0; + + _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!eglInitialize(_egl_display, NULL, NULL)) { + androiddisplay_cat.error() + << "Couldn't initialize the EGL display: " + << get_egl_error_string(eglGetError()) << "\n"; + } + + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + androiddisplay_cat.error() + << "Couldn't bind EGL to the OpenGL ES API: " + << get_egl_error_string(eglGetError()) << "\n"; + } + + _is_valid = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsPipe::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +AndroidGraphicsPipe:: +~AndroidGraphicsPipe() { + if (_egl_display) { + if (!eglTerminate(_egl_display)) { + androiddisplay_cat.error() << "Failed to terminate EGL display: " + << get_egl_error_string(eglGetError()) << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsPipe::get_interface_name +// Access: Published, Virtual +// Description: Returns the name of the rendering interface +// associated with this GraphicsPipe. This is used to +// present to the user to allow him/her to choose +// between several possible GraphicsPipes available on a +// particular platform, so the name should be meaningful +// and unique for a given platform. +//////////////////////////////////////////////////////////////////// +string AndroidGraphicsPipe:: +get_interface_name() const { + return "OpenGL ES"; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsPipe::pipe_constructor +// Access: Public, Static +// Description: This function is passed to the GraphicsPipeSelection +// object to allow the user to make a default +// AndroidGraphicsPipe. +//////////////////////////////////////////////////////////////////// +PT(GraphicsPipe) AndroidGraphicsPipe:: +pipe_constructor() { + return new AndroidGraphicsPipe; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsPipe::get_preferred_window_thread +// Access: Public, Virtual +// Description: Returns an indication of the thread in which this +// GraphicsPipe requires its window processing to be +// performed: typically either the app thread (e.g. X) +// or the draw thread (Windows). +//////////////////////////////////////////////////////////////////// +GraphicsPipe::PreferredWindowThread +AndroidGraphicsPipe::get_preferred_window_thread() const { + // Most of the Android NDK window functions can be + // called from any thread. Since we're creating the + // context at open_window time, let's choose "draw". + return PWT_app; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsPipe::make_output +// Access: Protected, Virtual +// Description: Creates a new window on the pipe, if possible. +//////////////////////////////////////////////////////////////////// +PT(GraphicsOutput) AndroidGraphicsPipe:: +make_output(const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsEngine *engine, + GraphicsStateGuardian *gsg, + GraphicsOutput *host, + int retry, + bool &precertify) { + + if (!_is_valid) { + return NULL; + } + + AndroidGraphicsStateGuardian *androidgsg = 0; + if (gsg != 0) { + DCAST_INTO_R(androidgsg, gsg, NULL); + } + + // First thing to try: an eglGraphicsWindow + + if (retry == 0) { + if (((flags&BF_require_parasite)!=0)|| + ((flags&BF_refuse_window)!=0)|| + ((flags&BF_resizeable)!=0)|| + ((flags&BF_size_track_host)!=0)|| + ((flags&BF_rtt_cumulative)!=0)|| + ((flags&BF_can_bind_color)!=0)|| + ((flags&BF_can_bind_every)!=0)) { + return NULL; + } + return new AndroidGraphicsWindow(engine, this, name, fb_prop, win_prop, + flags, gsg, host); + } + + // Second thing to try: a GLES(2)GraphicsBuffer + /*if (retry == 1) { + if ((host==0)|| + // (!gl_support_fbo)|| + ((flags&BF_require_parasite)!=0)|| + ((flags&BF_require_window)!=0)) { + return NULL; + } + // Early failure - if we are sure that this buffer WONT + // meet specs, we can bail out early. + if ((flags & BF_fb_props_optional)==0) { + if ((fb_prop.get_indexed_color() > 0)|| + (fb_prop.get_back_buffers() > 0)|| + (fb_prop.get_accum_bits() > 0)|| + (fb_prop.get_multisamples() > 0)) { + return NULL; + } + } + // Early success - if we are sure that this buffer WILL + // meet specs, we can precertify it. + if ((eglgsg != 0) && + (eglgsg->is_valid()) && + (!eglgsg->needs_reset()) && + (eglgsg->_supports_framebuffer_object) && + (eglgsg->_glDrawBuffers != 0)&& + (fb_prop.is_basic())) { + precertify = true; + } +#ifdef OPENGLES_2 + return new GLES2GraphicsBuffer(engine, this, name, fb_prop, win_prop, + flags, gsg, host); +#else + return new GLESGraphicsBuffer(engine, this, name, fb_prop, win_prop, + flags, gsg, host); +#endif + } + + // Third thing to try: a eglGraphicsBuffer + if (retry == 2) { + if (((flags&BF_require_parasite)!=0)|| + ((flags&BF_require_window)!=0)|| + ((flags&BF_resizeable)!=0)|| + ((flags&BF_size_track_host)!=0)) { + return NULL; + } + + if (!support_rtt) { + if (((flags&BF_rtt_cumulative)!=0)|| + ((flags&BF_can_bind_every)!=0)) { + // If we require Render-to-Texture, but can't be sure we + // support it, bail. + return NULL; + } + } + + return new eglGraphicsBuffer(engine, this, name, fb_prop, win_prop, + flags, gsg, host); + } + + // Fourth thing to try: an eglGraphicsPixmap. + if (retry == 3) { + if (((flags&BF_require_parasite)!=0)|| + ((flags&BF_require_window)!=0)|| + ((flags&BF_resizeable)!=0)|| + ((flags&BF_size_track_host)!=0)) { + return NULL; + } + + if (((flags&BF_rtt_cumulative)!=0)|| + ((flags&BF_can_bind_every)!=0)) { + return NULL; + } + + return new eglGraphicsPixmap(engine, this, name, fb_prop, win_prop, + flags, gsg, host); + }*/ + + // Nothing else left to try. + return NULL; +} diff --git a/panda/src/androiddisplay/androidGraphicsPipe.h b/panda/src/androiddisplay/androidGraphicsPipe.h new file mode 100644 index 0000000000..ad53d9af6a --- /dev/null +++ b/panda/src/androiddisplay/androidGraphicsPipe.h @@ -0,0 +1,94 @@ +// Filename: androidGraphicsPipe.h +// Created by: rdb (11Jan13) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANDROIDGRAPHICSPIPE_H +#define ANDROIDGRAPHICSPIPE_H + +#include "pandabase.h" +#include "graphicsWindow.h" +#include "graphicsPipe.h" + +#ifdef OPENGLES_2 + #include "gles2gsg.h" +// #define NativeDisplayType EGLNativeDisplayType +// #define NativePixmapType EGLNativePixmapType +// #define NativeWindowType EGLNativeWindowType +#else + #include "glesgsg.h" +#endif +#include + +class FrameBufferProperties; + +class AndroidGraphicsBuffer; +class AndroidGraphicsPixmap; +class AndroidGraphicsWindow; + +//////////////////////////////////////////////////////////////////// +// Class : AndroidGraphicsPipe +// Description : This graphics pipe represents the interface for +// creating OpenGL ES graphics windows on an X-based +// (e.g. Unix) client. +//////////////////////////////////////////////////////////////////// +class AndroidGraphicsPipe : public GraphicsPipe { +public: + AndroidGraphicsPipe(); + virtual ~AndroidGraphicsPipe(); + + virtual string get_interface_name() const; + static PT(GraphicsPipe) pipe_constructor(); + +public: + virtual PreferredWindowThread get_preferred_window_thread() const; + +protected: + virtual PT(GraphicsOutput) make_output(const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsEngine *engine, + GraphicsStateGuardian *gsg, + GraphicsOutput *host, + int retry, + bool &precertify); + +private: + EGLDisplay _egl_display; + GraphicsWindow *_window; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsPipe::init_type(); + register_type(_type_handle, "AndroidGraphicsPipe", + GraphicsPipe::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class AndroidGraphicsBuffer; + friend class AndroidGraphicsPixmap; + friend class AndroidGraphicsWindow; +}; + +#include "androidGraphicsPipe.I" + +#endif diff --git a/panda/src/androiddisplay/androidGraphicsStateGuardian.I b/panda/src/androiddisplay/androidGraphicsStateGuardian.I new file mode 100644 index 0000000000..49f95b326f --- /dev/null +++ b/panda/src/androiddisplay/androidGraphicsStateGuardian.I @@ -0,0 +1,25 @@ +// Filename: androidGraphicsStateGuardian.I +// Created by: rdb (11Jan13) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::get_fb_properties +// Access: Private +// Description: Gets the FrameBufferProperties for all windows and +// buffers that use this GSG. +//////////////////////////////////////////////////////////////////// +INLINE const FrameBufferProperties &AndroidGraphicsStateGuardian:: +get_fb_properties() const { + return _fbprops; +} diff --git a/panda/src/androiddisplay/androidGraphicsStateGuardian.cxx b/panda/src/androiddisplay/androidGraphicsStateGuardian.cxx new file mode 100644 index 0000000000..889fda8aa7 --- /dev/null +++ b/panda/src/androiddisplay/androidGraphicsStateGuardian.cxx @@ -0,0 +1,403 @@ +// Filename: androidGraphicsStateGuardian.cxx +// Created by: rdb (11Jan13) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "androidGraphicsStateGuardian.h" +#include "config_androiddisplay.h" +#include "lightReMutexHolder.h" + +#include + +TypeHandle AndroidGraphicsStateGuardian::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AndroidGraphicsStateGuardian:: +AndroidGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, + AndroidGraphicsStateGuardian *share_with) : +#ifdef OPENGLES_2 + GLES2GraphicsStateGuardian(engine, pipe) +#else + GLESGraphicsStateGuardian(engine, pipe) +#endif +{ + _share_context = 0; + _context = 0; + _egl_display = 0; + _fbconfig = 0; + _format = 0; + + if (share_with != (AndroidGraphicsStateGuardian *)NULL) { + _prepared_objects = share_with->get_prepared_objects(); + _share_context = share_with->_context; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AndroidGraphicsStateGuardian:: +~AndroidGraphicsStateGuardian() { + if (_context != (EGLContext)NULL) { + if (!eglDestroyContext(_egl_display, _context)) { + androiddisplay_cat.error() << "Failed to destroy EGL context: " + << get_egl_error_string(eglGetError()) << "\n"; + } + _context = (EGLContext)NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::get_properties +// Access: Private +// Description: Gets the FrameBufferProperties to match the +// indicated config. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsStateGuardian:: +get_properties(FrameBufferProperties &properties, + bool &pbuffer_supported, bool &pixmap_supported, + bool &slow, EGLConfig config) { + + properties.clear(); + + // Now update our framebuffer_mode and bit depth appropriately. + EGLint red_size, green_size, blue_size, + alpha_size, + depth_size, stencil_size, samples, surface_type, caveat; + + eglGetConfigAttrib(_egl_display, config, EGL_RED_SIZE, &red_size); + eglGetConfigAttrib(_egl_display, config, EGL_GREEN_SIZE, &green_size); + eglGetConfigAttrib(_egl_display, config, EGL_BLUE_SIZE, &blue_size); + eglGetConfigAttrib(_egl_display, config, EGL_ALPHA_SIZE, &alpha_size); + eglGetConfigAttrib(_egl_display, config, EGL_DEPTH_SIZE, &depth_size); + eglGetConfigAttrib(_egl_display, config, EGL_STENCIL_SIZE, &stencil_size); + eglGetConfigAttrib(_egl_display, config, EGL_SAMPLES, &samples); + eglGetConfigAttrib(_egl_display, config, EGL_SURFACE_TYPE, &surface_type); + eglGetConfigAttrib(_egl_display, config, EGL_CONFIG_CAVEAT, &caveat); + int err = eglGetError(); + if (err != EGL_SUCCESS) { + androiddisplay_cat.error() << "Failed to get EGL config attrib: " + << get_egl_error_string(err) << "\n"; + } + + pbuffer_supported = false; + if ((surface_type & EGL_PBUFFER_BIT)!=0) { + pbuffer_supported = true; + } + + pixmap_supported = false; + if ((surface_type & EGL_PIXMAP_BIT)!=0) { + pixmap_supported = true; + } + + slow = false; + if (caveat == EGL_SLOW_CONFIG) { + slow = true; + } + + if ((surface_type & EGL_WINDOW_BIT)==0) { + // We insist on having a context that will support an onscreen window. + return; + } + + properties.set_back_buffers(1); + properties.set_rgb_color(1); + properties.set_color_bits(red_size+green_size+blue_size); + properties.set_stencil_bits(stencil_size); + properties.set_depth_bits(depth_size); + properties.set_alpha_bits(alpha_size); + properties.set_multisamples(samples); + + // Set both hardware and software bits, indicating not-yet-known. + properties.set_force_software(1); + properties.set_force_hardware(1); +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::choose_pixel_format +// Access: Private +// Description: Selects a visual or fbconfig for all the windows +// and buffers that use this gsg. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsStateGuardian:: +choose_pixel_format(const FrameBufferProperties &properties, + bool need_pbuffer, bool need_pixmap) { + + _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + _fbconfig = 0; + _format = 0; + + int attrib_list[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, +#ifdef OPENGLES_1 + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, +#endif +#ifdef OPENGLES_2 + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, +#endif + EGL_NONE + }; + + // First get the number of matching configurations, so we know how much memory to allocate. + int num_configs = 0, returned_configs; + if (!eglChooseConfig(_egl_display, attrib_list, NULL, num_configs, &returned_configs) || returned_configs <= 0) { + androiddisplay_cat.error() << "eglChooseConfig failed: " + << get_egl_error_string(eglGetError()) << "\n"; + return; + } + + num_configs = returned_configs; + EGLConfig *configs = new EGLConfig[num_configs]; + + if (!eglChooseConfig(_egl_display, attrib_list, configs, num_configs, &returned_configs) || returned_configs <= 0) { + androiddisplay_cat.error() << "eglChooseConfig failed: " + << get_egl_error_string(eglGetError()) << "\n"; + delete[] configs; + return; + } + + int best_quality = 0; + int best_result = 0; + FrameBufferProperties best_props; + + for (int i = 0; i < num_configs; ++i) { + FrameBufferProperties fbprops; + bool pbuffer_supported, pixmap_supported, slow; + get_properties(fbprops, pbuffer_supported, pixmap_supported, + slow, configs[i]); + // We're not protecting this code by an is_debug() check, because if we do, + // some weird compiler bug appears and somehow makes the quality always 0. + const char *pbuffertext = pbuffer_supported ? " (pbuffer)" : ""; + const char *pixmaptext = pixmap_supported ? " (pixmap)" : ""; + const char *slowtext = slow ? " (slow)" : ""; + androiddisplay_cat.debug() + << i << ": " << fbprops << pbuffertext << pixmaptext << slowtext << "\n"; + int quality = fbprops.get_quality(properties); + if ((quality > 0)&&(slow)) quality -= 10000000; + + if (need_pbuffer && !pbuffer_supported) { + continue; + } + if (need_pixmap && !pixmap_supported) { + continue; + } + + if (quality > best_quality) { + best_quality = quality; + best_result = i; + best_props = fbprops; + } + } + + if (best_quality > 0) { + androiddisplay_cat.debug() + << "Chosen config " << best_result << ": " << best_props << "\n"; + _fbconfig = configs[best_result]; + eglGetConfigAttrib(_egl_display, _fbconfig, EGL_NATIVE_VISUAL_ID, &_format); + + androiddisplay_cat.debug() + << "Window format: " << _format << "\n"; + + _fbprops = best_props; + return; + } + + androiddisplay_cat.error() << + "Could not find a usable pixel format.\n"; + + delete[] configs; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::create_context +// Access: Private +// Description: Creates the context based on the config previously +// obtained in choose_pixel_format. +//////////////////////////////////////////////////////////////////// +bool AndroidGraphicsStateGuardian:: +create_context() { + if (_context != EGL_NO_CONTEXT) { + destroy_context(); + } + +#ifdef OPENGLES_2 + EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + _context = eglCreateContext(_egl_display, _fbconfig, _share_context, context_attribs); +#else + _context = eglCreateContext(_egl_display, _fbconfig, _share_context, NULL); +#endif + + int err = eglGetError(); + if (_context != EGL_NO_CONTEXT && err == EGL_SUCCESS) { + _needs_reset = true; + return true; + } + + androiddisplay_cat.error() + << "Could not create EGL context!\n" + << get_egl_error_string(err) << "\n"; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::destroy_context +// Access: Private +// Description: Destroys the context previously created by +// create_context. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsStateGuardian:: +destroy_context() { + if (_context == EGL_NO_CONTEXT) { + return; + } + + if (!eglMakeCurrent(_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + androiddisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + + release_all(); + + eglDestroyContext(_egl_display, _context); + _context = EGL_NO_CONTEXT; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::reset +// Access: Public, Virtual +// Description: Resets all internal state as if the gsg were newly +// created. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsStateGuardian:: +reset() { +#ifdef OPENGLES_2 + GLES2GraphicsStateGuardian::reset(); +#else + GLESGraphicsStateGuardian::reset(); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::egl_is_at_least_version +// Access: Public +// Description: Returns true if the runtime GLX version number is at +// least the indicated value, false otherwise. +//////////////////////////////////////////////////////////////////// +bool AndroidGraphicsStateGuardian:: +egl_is_at_least_version(int major_version, int minor_version) const { + if (_egl_version_major < major_version) { + return false; + } + if (_egl_version_minor < minor_version) { + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::gl_flush +// Access: Protected, Virtual +// Description: Calls glFlush(). +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsStateGuardian:: +gl_flush() const { +#ifdef OPENGLES_2 + GLES2GraphicsStateGuardian::gl_flush(); +#else + GLESGraphicsStateGuardian::gl_flush(); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::gl_get_error +// Access: Protected, Virtual +// Description: Returns the result of glGetError(). +//////////////////////////////////////////////////////////////////// +GLenum AndroidGraphicsStateGuardian:: +gl_get_error() const { +#ifdef OPENGLES_2 + return GLES2GraphicsStateGuardian::gl_get_error(); +#else + return GLESGraphicsStateGuardian::gl_get_error(); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::query_gl_version +// Access: Protected, Virtual +// Description: Queries the runtime version of OpenGL in use. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsStateGuardian:: +query_gl_version() { +#ifdef OPENGLES_2 + GLES2GraphicsStateGuardian::query_gl_version(); +#else + GLESGraphicsStateGuardian::query_gl_version(); +#endif + + // Calling eglInitialize on an already-initialized display will + // just provide us the version numbers. + if (!eglInitialize(_egl_display, &_egl_version_major, &_egl_version_minor)) { + androiddisplay_cat.error() << "Failed to get EGL version number: " + << get_egl_error_string(eglGetError()) << "\n"; + } + + // We output to glesgsg_cat instead of androiddisplay_cat, since this is + // where the GL version has been output, and it's nice to see the + // two of these together. +#ifdef OPENGLES_2 + if (gles2gsg_cat.is_debug()) { + gles2gsg_cat.debug() +#else + if (glesgsg_cat.is_debug()) { + glesgsg_cat.debug() +#endif + << "EGL_VERSION = " << _egl_version_major << "." << _egl_version_minor + << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::get_extra_extensions +// Access: Protected, Virtual +// Description: This may be redefined by a derived class (e.g. glx or +// wgl) to get whatever further extensions strings may +// be appropriate to that interface, in addition to the +// GL extension strings return by glGetString(). +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsStateGuardian:: +get_extra_extensions() { + save_extensions(eglQueryString(_egl_display, EGL_EXTENSIONS)); +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsStateGuardian::do_get_extension_func +// Access: Public, Virtual +// Description: Returns the pointer to the GL extension function with +// the indicated name. It is the responsibility of the +// caller to ensure that the required extension is +// defined in the OpenGL runtime prior to calling this; +// it is an error to call this for a function that is +// not defined. +//////////////////////////////////////////////////////////////////// +void *AndroidGraphicsStateGuardian:: +do_get_extension_func(const char *prefix, const char *name) { + string fullname = string(prefix) + string(name); + + return (void *)eglGetProcAddress(fullname.c_str()); +} diff --git a/panda/src/androiddisplay/androidGraphicsStateGuardian.h b/panda/src/androiddisplay/androidGraphicsStateGuardian.h new file mode 100644 index 0000000000..5e82cf3159 --- /dev/null +++ b/panda/src/androiddisplay/androidGraphicsStateGuardian.h @@ -0,0 +1,97 @@ +// Filename: androidGraphicsStateGuardian.h +// Created by: pro-rsoft (21May09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANDROIDGRAPHICSSTATEGUARDIAN_H +#define ANDROIDGRAPHICSSTATEGUARDIAN_H + +#include "pandabase.h" +#include "androidGraphicsPipe.h" + +//////////////////////////////////////////////////////////////////// +// Class : AndroidGraphicsStateGuardian +// Description : A tiny specialization on GLESGraphicsStateGuardian +// to add some egl-specific information. +//////////////////////////////////////////////////////////////////// +#ifdef OPENGLES_2 +class AndroidGraphicsStateGuardian : public GLES2GraphicsStateGuardian { +#else +class AndroidGraphicsStateGuardian : public GLESGraphicsStateGuardian { +#endif +public: + INLINE const FrameBufferProperties &get_fb_properties() const; + void get_properties(FrameBufferProperties &properties, + bool &pbuffer_supported, bool &pixmap_supported, + bool &slow, EGLConfig config); + void choose_pixel_format(const FrameBufferProperties &properties, + bool need_pbuffer, bool need_pixmap); + bool create_context(); + void destroy_context(); + + AndroidGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe, + AndroidGraphicsStateGuardian *share_with); + + virtual ~AndroidGraphicsStateGuardian(); + + virtual void reset(); + + bool egl_is_at_least_version(int major_version, int minor_version) const; + +protected: + EGLContext _share_context; + EGLContext _context; + EGLDisplay _egl_display; + EGLConfig _fbconfig; + EGLint _format; + FrameBufferProperties _fbprops; + +protected: + virtual void gl_flush() const; + virtual GLenum gl_get_error() const; + + virtual void query_gl_version(); + virtual void get_extra_extensions(); + virtual void *do_get_extension_func(const char *prefix, const char *name); + +private: + int _egl_version_major, _egl_version_minor; + + friend class AndroidGraphicsWindow; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { +#ifdef OPENGLES_2 + GLES2GraphicsStateGuardian::init_type(); + register_type(_type_handle, "AndroidGraphicsStateGuardian", + GLES2GraphicsStateGuardian::get_class_type()); +#else + GLESGraphicsStateGuardian::init_type(); + register_type(_type_handle, "AndroidGraphicsStateGuardian", + GLESGraphicsStateGuardian::get_class_type()); +#endif + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "androidGraphicsStateGuardian.I" + +#endif diff --git a/panda/src/androiddisplay/androidGraphicsWindow.I b/panda/src/androiddisplay/androidGraphicsWindow.I new file mode 100644 index 0000000000..f2c85ab105 --- /dev/null +++ b/panda/src/androiddisplay/androidGraphicsWindow.I @@ -0,0 +1,14 @@ +// Filename: androidGraphicsWindow.I +// Created by: rdb (11Jan13) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/androiddisplay/androidGraphicsWindow.cxx b/panda/src/androiddisplay/androidGraphicsWindow.cxx new file mode 100644 index 0000000000..fc1ad87ded --- /dev/null +++ b/panda/src/androiddisplay/androidGraphicsWindow.cxx @@ -0,0 +1,771 @@ +// Filename: androidGraphicsWindow.cxx +// Created by: rdb (11Jan13) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "androidGraphicsWindow.h" +#include "androidGraphicsStateGuardian.h" +#include "config_androiddisplay.h" +#include "androidGraphicsPipe.h" + +#include "graphicsPipe.h" +#include "keyboardButton.h" +#include "mouseButton.h" +#include "clockObject.h" +#include "pStatTimer.h" +#include "textEncoder.h" +#include "throw_event.h" +#include "nativeWindowHandle.h" + +#include "android_native_app_glue.h" +#include +#include + +extern struct android_app* panda_android_app; + +TypeHandle AndroidGraphicsWindow::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AndroidGraphicsWindow:: +AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host) : + GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) +{ + AndroidGraphicsPipe *android_pipe; + DCAST_INTO_V(android_pipe, _pipe); + + _egl_display = android_pipe->_egl_display; + _egl_surface = 0; + + _app = panda_android_app; + + GraphicsWindowInputDevice device = + GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); + add_input_device(device); +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +AndroidGraphicsWindow:: +~AndroidGraphicsWindow() { + destroy_surface(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::begin_frame +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// before beginning rendering for a given frame. It +// should do whatever setup is required, and return true +// if the frame should be rendered, or false if it +// should be skipped. +//////////////////////////////////////////////////////////////////// +bool AndroidGraphicsWindow:: +begin_frame(FrameMode mode, Thread *current_thread) { + PStatTimer timer(_make_current_pcollector, current_thread); + + begin_frame_spam(mode); + if (_gsg == (GraphicsStateGuardian *)NULL) { + return false; + } + + //XXX not open yet. + if (_egl_surface == EGL_NO_SURFACE) { + return false; + } + + AndroidGraphicsStateGuardian *androidgsg; + DCAST_INTO_R(androidgsg, _gsg, false); + { + if (eglGetCurrentDisplay() == _egl_display && + eglGetCurrentSurface(EGL_READ) == _egl_surface && + eglGetCurrentSurface(EGL_DRAW) == _egl_surface && + eglGetCurrentContext() == androidgsg->_context) { + // No need to make the context current again. Short-circuit + // this possibly-expensive call. + } else { + // Need to set the context. + if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, androidgsg->_context)) { + androiddisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + } + } + + // Now that we have made the context current to a window, we can + // reset the GSG state if this is the first time it has been used. + // (We can't just call reset() when we construct the GSG, because + // reset() requires having a current context.) + androidgsg->reset_if_new(); + + if (mode == FM_render) { + // begin_render_texture(); + clear_cube_map_selection(); + } + + _gsg->set_current_properties(&get_fb_properties()); + return _gsg->begin_frame(current_thread); +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::end_frame +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// after rendering is completed for a given frame. It +// should do whatever finalization is required. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsWindow:: +end_frame(FrameMode mode, Thread *current_thread) { + end_frame_spam(mode); + nassertv(_gsg != (GraphicsStateGuardian *)NULL); + + if (mode == FM_render) { + // end_render_texture(); + copy_to_textures(); + } + + _gsg->end_frame(current_thread); + + if (mode == FM_render) { + trigger_flip(); + clear_cube_map_selection(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::end_flip +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// after begin_flip() has been called on all windows, to +// finish the exchange of the front and back buffers. +// +// This should cause the window to wait for the flip, if +// necessary. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsWindow:: +end_flip() { + if (_gsg != (GraphicsStateGuardian *)NULL && _flip_ready) { + + // It doesn't appear to be necessary to ensure the graphics + // context is current before flipping the windows, and insisting + // on doing so can be a significant performance hit. + + //make_current(); + + if (_egl_surface != EGL_NO_SURFACE) { + eglSwapBuffers(_egl_display, _egl_surface); + } + } + GraphicsWindow::end_flip(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::process_events +// Access: Public, Virtual +// Description: Do whatever processing is necessary to ensure that +// the window responds to user events. Also, honor any +// requests recently made via request_properties() +// +// This function is called only within the window +// thread. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsWindow:: +process_events() { + GraphicsWindow::process_events(); + + // Read all pending events. + int looper_id; + int events; + struct android_poll_source* source; + + // Loop until all events are read. + while ((looper_id = ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) { + // Process this event. + if (source != NULL) { + source->process(_app, source); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::set_properties_now +// Access: Public, Virtual +// Description: Applies the requested set of properties to the +// window, if possible, for instance to request a change +// in size or minimization status. +// +// The window properties are applied immediately, rather +// than waiting until the next frame. This implies that +// this method may *only* be called from within the +// window thread. +// +// The return value is true if the properties are set, +// false if they are ignored. This is mainly useful for +// derived classes to implement extensions to this +// function. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsWindow:: +set_properties_now(WindowProperties &properties) { + if (_pipe == (GraphicsPipe *)NULL) { + // If the pipe is null, we're probably closing down. + GraphicsWindow::set_properties_now(properties); + return; + } + + GraphicsWindow::set_properties_now(properties); + if (!properties.is_any_specified()) { + // The base class has already handled this case. + return; + } + + // There's not really much we can change on Android. + if (properties.has_fullscreen()) { + uint32_t add_flags = 0; + uint32_t del_flags = 0; + if (_properties.get_fullscreen()) { + add_flags |= AWINDOW_FLAG_FULLSCREEN; + } else { + del_flags |= AWINDOW_FLAG_FULLSCREEN; + } + ANativeActivity_setWindowFlags(_app->activity, add_flags, del_flags); + + _properties.set_fullscreen(properties.get_fullscreen()); + properties.clear_fullscreen(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::close_window +// Access: Protected, Virtual +// Description: Closes the window right now. Called from the window +// thread. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsWindow:: +close_window() { + destroy_surface(); + + if (_gsg != (GraphicsStateGuardian *)NULL) { + _gsg.clear(); + } + + GraphicsWindow::close_window(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::open_window +// Access: Protected, Virtual +// Description: Opens the window right now. Called from the window +// thread. Returns true if the window is successfully +// opened, or false if there was a problem. +//////////////////////////////////////////////////////////////////// +bool AndroidGraphicsWindow:: +open_window() { + // GSG Creation/Initialization + AndroidGraphicsStateGuardian *androidgsg; + if (_gsg == 0) { + // There is no old gsg. Create a new one. + androidgsg = new AndroidGraphicsStateGuardian(_engine, _pipe, NULL); + androidgsg->choose_pixel_format(_fb_properties, false, false); + _gsg = androidgsg; + } else { + // If the old gsg has the wrong pixel format, create a + // new one that shares with the old gsg. + DCAST_INTO_R(androidgsg, _gsg, false); + if (!androidgsg->get_fb_properties().subsumes(_fb_properties)) { + androidgsg = new AndroidGraphicsStateGuardian(_engine, _pipe, androidgsg); + androidgsg->choose_pixel_format(_fb_properties, false, false); + _gsg = androidgsg; + } + } + + // Register the callbacks + assert(_app != NULL); + _app->userData = this; + _app->onAppCmd = handle_command; + _app->onInputEvent = handle_input_event; + + // Wait until Android has opened the window. + while (_app->window == NULL) { + process_events(); + } + + // create_surface should have been called by now. + if (_egl_surface == EGL_NO_SURFACE) { + return false; + } + + // Set some other properties. + _properties.set_origin(0, 0); + _properties.set_cursor_hidden(true); + _properties.set_undecorated(true); + + if (!androidgsg->get_fb_properties().verify_hardware_software + (_fb_properties, androidgsg->get_gl_renderer())) { + close_window(); + return false; + } + + //_fb_properties = androidgsg->get_fb_properties(); + + androiddisplay_cat.error() << "open_window done\n"; + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::destroy_surface +// Access: Protected, Virtual +// Description: Terminates the EGL surface. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsWindow:: +destroy_surface() { + if (_egl_surface != EGL_NO_SURFACE) { + if (!eglDestroySurface(_egl_display, _egl_surface)) { + androiddisplay_cat.error() << "Failed to destroy surface: " + << get_egl_error_string(eglGetError()) << "\n"; + } + _egl_surface = EGL_NO_SURFACE; + } + + // Destroy the current context. + if (_gsg != (GraphicsStateGuardian *)NULL) { + AndroidGraphicsStateGuardian *androidgsg; + DCAST_INTO_V(androidgsg, _gsg); + androidgsg->destroy_context(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::create_surface +// Access: Protected, Virtual +// Description: Creates the EGL surface. +//////////////////////////////////////////////////////////////////// +bool AndroidGraphicsWindow:: +create_surface() { + AndroidGraphicsStateGuardian *androidgsg; + DCAST_INTO_R(androidgsg, _gsg, false); + + // Reconfigure the window buffers to match that of our framebuffer config. + ANativeWindow_setBuffersGeometry(_app->window, 0, 0, androidgsg->_format); + + // Set any window flags + uint32_t add_flags = 0; + uint32_t del_flags = 0; + if (_properties.get_fullscreen()) { + add_flags |= AWINDOW_FLAG_FULLSCREEN; + } else { + del_flags |= AWINDOW_FLAG_FULLSCREEN; + } + ANativeActivity_setWindowFlags(_app->activity, add_flags, del_flags); + + // Create the EGL surface. + _egl_surface = eglCreateWindowSurface(_egl_display, androidgsg->_fbconfig, _app->window, NULL); + if (eglGetError() != EGL_SUCCESS) { + androiddisplay_cat.error() + << "Failed to create window surface.\n"; + return false; + } + + // Create a context. + if (androidgsg->_context == EGL_NO_CONTEXT) { + androiddisplay_cat.error() << "creating context\n"; + if (!androidgsg->create_context()) { + return false; + } + } + + // Switch to our newly created context. + if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, androidgsg->_context)) { + androiddisplay_cat.error() << "Failed to call eglMakeCurrent: " + << get_egl_error_string(eglGetError()) << "\n"; + } + + // Query the size of the surface. + //EGLint width, height; + //eglQuerySurface(_egl_display, _egl_surface, EGL_WIDTH, &width); + //eglQuerySurface(_egl_display, _egl_surface, EGL_HEIGHT, &height); + + androidgsg->reset_if_new(); + if (!androidgsg->is_valid()) { + close_window(); + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::handle_command +// Access: Private, Static +// Description: Android app sends a command from the main thread. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsWindow:: +handle_command(struct android_app *app, int32_t command) { + AndroidGraphicsWindow* window = (AndroidGraphicsWindow*) app->userData; + window->ns_handle_command(command); +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::ns_handle_command +// Access: Private +// Description: Android app sends a command from the main thread. +//////////////////////////////////////////////////////////////////// +void AndroidGraphicsWindow:: +ns_handle_command(int32_t command) { + WindowProperties properties; + + switch (command) { + case APP_CMD_SAVE_STATE: + // The system has asked us to save our current state. Do so. + //engine->app->savedState = malloc(sizeof(struct saved_state)); + //*((struct saved_state*)engine->app->savedState) = engine->state; + //engine->app->savedStateSize = sizeof(struct saved_state); + break; + case APP_CMD_INIT_WINDOW: + // The window is being shown, get it ready. + if (_app->window != NULL) { + create_surface(); + properties.set_minimized(false); + system_changed_properties(properties); + } + break; + case APP_CMD_TERM_WINDOW: + destroy_surface(); + properties.set_minimized(true); + system_changed_properties(properties); + break; + case APP_CMD_WINDOW_RESIZED: + break; + case APP_CMD_WINDOW_REDRAW_NEEDED: + break; + case APP_CMD_CONTENT_RECT_CHANGED: + properties.set_origin(_app->contentRect.left, _app->contentRect.top); + properties.set_size(_app->contentRect.right - _app->contentRect.left, + _app->contentRect.bottom - _app->contentRect.top); + system_changed_properties(properties); + break; + case APP_CMD_GAINED_FOCUS: + properties.set_foreground(true); + system_changed_properties(properties); + break; + case APP_CMD_LOST_FOCUS: + properties.set_foreground(false); + system_changed_properties(properties); + break; + case APP_CMD_DESTROY: + close_window(); + properties.set_open(false); + system_changed_properties(properties); + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::handle_input_event +// Access: Private, Static +// Description: Processes an input event. Returns 1 if the event +// was handled, 0 otherwise. +//////////////////////////////////////////////////////////////////// +int32_t AndroidGraphicsWindow:: +handle_input_event(struct android_app* app, AInputEvent *event) { + AndroidGraphicsWindow* window = (AndroidGraphicsWindow*) app->userData; + + int32_t event_type = AInputEvent_getType(event); + switch (event_type) { + case AINPUT_EVENT_TYPE_KEY: + return window->handle_key_event(event); + case AINPUT_EVENT_TYPE_MOTION: + return window->handle_motion_event(event); + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::handle_keystroke +// Access: Private +// Description: Processes a key event. +//////////////////////////////////////////////////////////////////// +int32_t AndroidGraphicsWindow:: +handle_key_event(const AInputEvent *event) { + /* + int32_t meta = AKeyEvent_getMetaState(event); + if (meta | AMETA_ALT_ON) { + _input_devices[0].button_down(KeyboardButton.alt()); + } + if (meta | AMETA_ALT_LEFT_ON) { + _input_devices[0].button_down(KeyboardButton.lalt()); + } + if (meta | AMETA_ALT_RIGHT_ON) { + _input_devices[0].button_down(KeyboardButton.ralt()); + } + if (meta | AMETA_SHIFT_ON) { + _input_devices[0].button_down(KeyboardButton.shift()); + } + if (meta | AMETA_SHIFT_LEFT_ON) { + _input_devices[0].button_down(KeyboardButton.lshift()); + } + if (meta | AMETA_SHIFT_RIGHT_ON) { + _input_devices[0].button_down(KeyboardButton.rshift()); + }*/ + + int32_t keycode = AKeyEvent_getKeyCode(event); + ButtonHandle button = map_button(keycode); + + if (button == ButtonHandle::none()) { + androiddisplay_cat.warning() + << "Unknown keycode: " << keycode << "\n"; + return 0; + } + + // Is it an up or down event? + int32_t action = AKeyEvent_getAction(event); + if (action == AKEY_EVENT_ACTION_DOWN) { + _input_devices[0].button_down(button); + } else if (action == AKEY_EVENT_ACTION_UP) { + _input_devices[0].button_up(button); + } + //TODO getRepeatCount, ACTION_MULTIPLE + + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::handle_motion_event +// Access: Private +// Description: Processes a motion event. +//////////////////////////////////////////////////////////////////// +int32_t AndroidGraphicsWindow:: +handle_motion_event(const AInputEvent *event) { + int32_t action = AMotionEvent_getAction(event); + action &= AMOTION_EVENT_ACTION_MASK; + + if (action == AMOTION_EVENT_ACTION_DOWN) { + _input_devices[0].button_down(MouseButton::one()); + } else if (action == AMOTION_EVENT_ACTION_UP) { + _input_devices[0].button_up(MouseButton::one()); + } + + float x = AMotionEvent_getX(event, 0); + float y = AMotionEvent_getY(event, 0); + + _input_devices[0].set_pointer_in_window(x, y); + + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: AndroidGraphicsWindow::map_button +// Access: Private +// Description: Given an Android keycode, returns an appropriate +// ButtonHandle object, or ButtonHandle::none() if +// a matching ButtonHandle does not exist. +//////////////////////////////////////////////////////////////////// +ButtonHandle AndroidGraphicsWindow:: +map_button(int32_t keycode) { + switch (keycode) { + case AKEYCODE_SOFT_LEFT: + case AKEYCODE_SOFT_RIGHT: + case AKEYCODE_HOME: + case AKEYCODE_BACK: + case AKEYCODE_CALL: + case AKEYCODE_ENDCALL: + break; + case AKEYCODE_0: + return KeyboardButton::ascii_key('0'); + case AKEYCODE_1: + return KeyboardButton::ascii_key('1'); + case AKEYCODE_2: + return KeyboardButton::ascii_key('2'); + case AKEYCODE_3: + return KeyboardButton::ascii_key('3'); + case AKEYCODE_4: + return KeyboardButton::ascii_key('4'); + case AKEYCODE_5: + return KeyboardButton::ascii_key('5'); + case AKEYCODE_6: + return KeyboardButton::ascii_key('6'); + case AKEYCODE_7: + return KeyboardButton::ascii_key('7'); + case AKEYCODE_8: + return KeyboardButton::ascii_key('8'); + case AKEYCODE_9: + return KeyboardButton::ascii_key('9'); + case AKEYCODE_STAR: + return KeyboardButton::ascii_key('*'); + case AKEYCODE_POUND: + return KeyboardButton::ascii_key('#'); + case AKEYCODE_DPAD_UP: + return KeyboardButton::up(); + case AKEYCODE_DPAD_DOWN: + return KeyboardButton::down(); + case AKEYCODE_DPAD_LEFT: + return KeyboardButton::left(); + case AKEYCODE_DPAD_RIGHT: + return KeyboardButton::right(); + case AKEYCODE_DPAD_CENTER: + case AKEYCODE_VOLUME_UP: + case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_POWER: + case AKEYCODE_CAMERA: + case AKEYCODE_CLEAR: + break; + case AKEYCODE_A: + return KeyboardButton::ascii_key('a'); + case AKEYCODE_B: + return KeyboardButton::ascii_key('b'); + case AKEYCODE_C: + return KeyboardButton::ascii_key('c'); + case AKEYCODE_D: + return KeyboardButton::ascii_key('d'); + case AKEYCODE_E: + return KeyboardButton::ascii_key('e'); + case AKEYCODE_F: + return KeyboardButton::ascii_key('f'); + case AKEYCODE_G: + return KeyboardButton::ascii_key('g'); + case AKEYCODE_H: + return KeyboardButton::ascii_key('h'); + case AKEYCODE_I: + return KeyboardButton::ascii_key('i'); + case AKEYCODE_J: + return KeyboardButton::ascii_key('j'); + case AKEYCODE_K: + return KeyboardButton::ascii_key('k'); + case AKEYCODE_L: + return KeyboardButton::ascii_key('l'); + case AKEYCODE_M: + return KeyboardButton::ascii_key('m'); + case AKEYCODE_N: + return KeyboardButton::ascii_key('n'); + case AKEYCODE_O: + return KeyboardButton::ascii_key('o'); + case AKEYCODE_P: + return KeyboardButton::ascii_key('p'); + case AKEYCODE_Q: + return KeyboardButton::ascii_key('q'); + case AKEYCODE_R: + return KeyboardButton::ascii_key('r'); + case AKEYCODE_S: + return KeyboardButton::ascii_key('s'); + case AKEYCODE_T: + return KeyboardButton::ascii_key('t'); + case AKEYCODE_U: + return KeyboardButton::ascii_key('u'); + case AKEYCODE_V: + return KeyboardButton::ascii_key('v'); + case AKEYCODE_W: + return KeyboardButton::ascii_key('w'); + case AKEYCODE_X: + return KeyboardButton::ascii_key('x'); + case AKEYCODE_Y: + return KeyboardButton::ascii_key('y'); + case AKEYCODE_Z: + return KeyboardButton::ascii_key('z'); + case AKEYCODE_COMMA: + return KeyboardButton::ascii_key(','); + case AKEYCODE_PERIOD: + return KeyboardButton::ascii_key('.'); + case AKEYCODE_ALT_LEFT: + return KeyboardButton::lalt(); + case AKEYCODE_ALT_RIGHT: + return KeyboardButton::ralt(); + case AKEYCODE_SHIFT_LEFT: + return KeyboardButton::lshift(); + case AKEYCODE_SHIFT_RIGHT: + return KeyboardButton::rshift(); + case AKEYCODE_TAB: + return KeyboardButton::tab(); + case AKEYCODE_SPACE: + return KeyboardButton::space(); + case AKEYCODE_SYM: + case AKEYCODE_EXPLORER: + case AKEYCODE_ENVELOPE: + break; + case AKEYCODE_ENTER: + return KeyboardButton::enter(); + case AKEYCODE_DEL: + return KeyboardButton::del(); + case AKEYCODE_GRAVE: + return KeyboardButton::ascii_key('`'); + case AKEYCODE_MINUS: + return KeyboardButton::ascii_key('-'); + case AKEYCODE_EQUALS: + return KeyboardButton::ascii_key('='); + case AKEYCODE_LEFT_BRACKET: + return KeyboardButton::ascii_key('['); + case AKEYCODE_RIGHT_BRACKET: + return KeyboardButton::ascii_key(']'); + case AKEYCODE_BACKSLASH: + return KeyboardButton::ascii_key('\\'); + case AKEYCODE_SEMICOLON: + return KeyboardButton::ascii_key(';'); + case AKEYCODE_APOSTROPHE: + return KeyboardButton::ascii_key('\''); + case AKEYCODE_SLASH: + return KeyboardButton::ascii_key('/'); + case AKEYCODE_AT: + return KeyboardButton::ascii_key('@'); + case AKEYCODE_NUM: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_FOCUS: + break; + case AKEYCODE_PLUS: + return KeyboardButton::ascii_key('+'); + case AKEYCODE_MENU: + case AKEYCODE_NOTIFICATION: + case AKEYCODE_SEARCH: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_MUTE: + break; + case AKEYCODE_PAGE_UP: + return KeyboardButton::page_up(); + case AKEYCODE_PAGE_DOWN: + return KeyboardButton::page_down(); + case AKEYCODE_PICTSYMBOLS: + case AKEYCODE_SWITCH_CHARSET: + case AKEYCODE_BUTTON_A: + case AKEYCODE_BUTTON_B: + case AKEYCODE_BUTTON_C: + case AKEYCODE_BUTTON_X: + case AKEYCODE_BUTTON_Y: + case AKEYCODE_BUTTON_Z: + case AKEYCODE_BUTTON_L1: + case AKEYCODE_BUTTON_R1: + case AKEYCODE_BUTTON_L2: + case AKEYCODE_BUTTON_R2: + case AKEYCODE_BUTTON_THUMBL: + case AKEYCODE_BUTTON_THUMBR: + case AKEYCODE_BUTTON_START: + case AKEYCODE_BUTTON_SELECT: + case AKEYCODE_BUTTON_MODE: + default: + break; + } + return ButtonHandle::none(); +} diff --git a/panda/src/androiddisplay/androidGraphicsWindow.h b/panda/src/androiddisplay/androidGraphicsWindow.h new file mode 100644 index 0000000000..d4314e3b21 --- /dev/null +++ b/panda/src/androiddisplay/androidGraphicsWindow.h @@ -0,0 +1,99 @@ +// Filename: androidGraphicsWindow.h +// Created by: rdb (11Jan13) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANDROIDGRAPHICSWINDOW_H +#define ANDROIDGRAPHICSWINDOW_H + +#include "pandabase.h" + +#include "androidGraphicsPipe.h" +#include "graphicsWindow.h" +#include "buttonHandle.h" + +#include +#include +#include +#include + +struct android_app; + +//////////////////////////////////////////////////////////////////// +// Class : AndroidGraphicsWindow +// Description : An interface to manage Android windows and their +// appropriate EGL surfaces. +//////////////////////////////////////////////////////////////////// +class AndroidGraphicsWindow : public GraphicsWindow { +public: + AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host); + virtual ~AndroidGraphicsWindow(); + + virtual bool begin_frame(FrameMode mode, Thread *current_thread); + virtual void end_frame(FrameMode mode, Thread *current_thread); + virtual void end_flip(); + + virtual void process_events(); + virtual void set_properties_now(WindowProperties &properties); + +protected: + virtual void close_window(); + virtual bool open_window(); + + virtual void destroy_surface(); + virtual bool create_surface(); + +private: + static void handle_command(struct android_app *app, int32_t command); + static int32_t handle_input_event(struct android_app *app, AInputEvent *event); + + void ns_handle_command(int32_t command); + int32_t handle_key_event(const AInputEvent *event); + int32_t handle_motion_event(const AInputEvent *event); + + ButtonHandle map_button(int32_t keycode); + +private: + struct android_app* _app; + + EGLDisplay _egl_display; + EGLSurface _egl_surface; + + const ARect *rect; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsWindow::init_type(); + register_type(_type_handle, "AndroidGraphicsWindow", + GraphicsWindow::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "androidGraphicsWindow.I" + +#endif diff --git a/panda/src/androiddisplay/config_androiddisplay.cxx b/panda/src/androiddisplay/config_androiddisplay.cxx new file mode 100644 index 0000000000..51990a8484 --- /dev/null +++ b/panda/src/androiddisplay/config_androiddisplay.cxx @@ -0,0 +1,90 @@ +// Filename: config_androiddisplay.cxx +// Created by: rdb (11Jan13) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "config_androiddisplay.h" +#include "androidGraphicsPipe.h" +#include "androidGraphicsWindow.h" +#include "androidGraphicsStateGuardian.h" +#include "graphicsPipeSelection.h" +#include "dconfig.h" +#include "pandaSystem.h" + +#include "config_display.h" + +Configure(config_androiddisplay); +NotifyCategoryDef(androiddisplay, "display"); + +ConfigureFn(config_androiddisplay) { + init_libandroiddisplay(); +} + +//////////////////////////////////////////////////////////////////// +// Function: init_libandroiddisplay +// Description: Initializes the library. This must be called at +// least once before any of the functions or classes in +// this library can be used. Normally it will be +// called by the static initializers and need not be +// called explicitly, but special cases exist. +//////////////////////////////////////////////////////////////////// +void +init_libandroiddisplay() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + init_libdisplay(); + display_cat.get_safe_ptr(); + + AndroidGraphicsPipe::init_type(); + AndroidGraphicsWindow::init_type(); + AndroidGraphicsStateGuardian::init_type(); + + GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr(); + selection->add_pipe_type(AndroidGraphicsPipe::get_class_type(), + AndroidGraphicsPipe::pipe_constructor); + + PandaSystem *ps = PandaSystem::get_global_ptr(); +#ifdef OPENGLES_2 + ps->set_system_tag("OpenGL ES 2", "window_system", "Android"); +#else + ps->set_system_tag("OpenGL ES", "window_system", "Android"); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: get_egl_error_string +// Description: Returns the given EGL error as string. +//////////////////////////////////////////////////////////////////// +const string get_egl_error_string(int error) { + switch (error) { + case 0x3000: return "EGL_SUCCESS"; break; + case 0x3001: return "EGL_NOT_INITIALIZED"; break; + case 0x3002: return "EGL_BAD_ACCESS"; break; + case 0x3003: return "EGL_BAD_ALLOC"; break; + case 0x3004: return "EGL_BAD_ATTRIBUTE"; break; + case 0x3005: return "EGL_BAD_CONFIG"; break; + case 0x3006: return "EGL_BAD_CONTEXT"; break; + case 0x3007: return "EGL_BAD_CURRENT_SURFACE"; break; + case 0x3008: return "EGL_BAD_DISPLAY"; break; + case 0x3009: return "EGL_BAD_MATCH"; break; + case 0x300A: return "EGL_BAD_NATIVE_PIXMAP"; break; + case 0x300B: return "EGL_BAD_NATIVE_WINDOW"; break; + case 0x300C: return "EGL_BAD_PARAMETER"; break; + case 0x300D: return "EGL_BAD_SURFACE"; break; + case 0x300E: return "EGL_CONTEXT_LOST"; break; + default: return "Unknown error"; + } +} diff --git a/panda/src/androiddisplay/config_androiddisplay.h b/panda/src/androiddisplay/config_androiddisplay.h new file mode 100644 index 0000000000..949c529e53 --- /dev/null +++ b/panda/src/androiddisplay/config_androiddisplay.h @@ -0,0 +1,43 @@ +// Filename: config_androiddisplay.h +// Created by: rdb (11Jan13) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_ANDROIDDISPLAY_H +#define CONFIG_ANDROIDDISPLAY_H + +#include "pandabase.h" +#include "notifyCategoryProxy.h" +#include "configVariableString.h" +#include "configVariableBool.h" +#include "configVariableInt.h" + +#if defined(OPENGLES_1) && defined(OPENGLES_2) + #error OPENGLES_1 and OPENGLES_2 cannot be defined at the same time! +#endif +#if !defined(OPENGLES_1) && !defined(OPENGLES_2) + #error Either OPENGLES_1 or OPENGLES_2 must be defined when compiling androiddisplay! +#endif + +#ifdef OPENGLES_2 + NotifyCategoryDecl(androiddisplay, EXPCL_PANDAGLES2, EXPTP_PANDAGLES2); + + extern EXPCL_PANDAGLES2 void init_libandroiddisplay(); + extern EXPCL_PANDAGLES2 const string get_egl_error_string(int error); +#else + NotifyCategoryDecl(androiddisplay, EXPCL_PANDAGLES, EXPTP_PANDAGLES); + + extern EXPCL_PANDAGLES void init_libandroiddisplay(); + extern EXPCL_PANDAGLES const string get_egl_error_string(int error); +#endif + +#endif diff --git a/panda/src/androiddisplay/p3androiddisplay_composite1.cxx b/panda/src/androiddisplay/p3androiddisplay_composite1.cxx new file mode 100644 index 0000000000..091a172a6e --- /dev/null +++ b/panda/src/androiddisplay/p3androiddisplay_composite1.cxx @@ -0,0 +1,6 @@ +#include "config_androiddisplay.cxx" +//#include "androidGraphicsBuffer.cxx" +#include "androidGraphicsPipe.cxx" +//#include "androidGraphicsPixmap.cxx" +#include "androidGraphicsStateGuardian.cxx" +#include "androidGraphicsWindow.cxx"