Browse Source

eagldisplay: Initial implementation.

This only works on iPhones at the moment. The code is quite fragile, but
I want to have a commit that gets something on the screen that I can
build off of. A lot of the code is based off of cocoadisplay, so there
may be some unneeded stuff that I haven't removed yet.

I also want to get rid of the NSNotification method of communicating the
created view between theads. It's a bit unstable at the moment.
Donny Lawrence 6 years ago
parent
commit
0a84246047

+ 1 - 0
panda/CMakeLists.txt

@@ -19,6 +19,7 @@ add_subdirectory(src/downloader)
 add_subdirectory(src/downloadertools)
 add_subdirectory(src/dxgsg9)
 add_subdirectory(src/dxml)
+add_subdirectory(src/eagldisplay)
 add_subdirectory(src/egg)
 add_subdirectory(src/egg2pg)
 add_subdirectory(src/egldisplay)

+ 7 - 0
panda/metalibs/pandagles/pandagles.cxx

@@ -12,6 +12,9 @@
 #if defined(ANDROID)
 #include "config_androiddisplay.h"
 #include "androidGraphicsPipe.h"
+#elif defined(IS_IOS)
+#include "config_eagldisplay.h"
+#include "EAGLGraphicsPipe.h"
 #else
 #include "config_egldisplay.h"
 #include "eglGraphicsPipe.h"
@@ -29,6 +32,8 @@ init_libpandagles() {
 
 #if defined(ANDROID)
   init_libandroiddisplay();
+#elif defined(IS_IOS)
+  init_libeagldisplay();
 #else
   init_libegldisplay();
 #endif
@@ -42,6 +47,8 @@ int
 get_pipe_type_pandagles() {
 #if defined(ANDROID)
   return AndroidGraphicsPipe::get_class_type().get_index();
+#elif defined(IS_IOS)
+  return EAGLGraphicsPipe::get_class_type().get_index();
 #else
   return eglGraphicsPipe::get_class_type().get_index();
 #endif

+ 14 - 0
panda/metalibs/pandagles2/pandagles2.cxx

@@ -9,8 +9,13 @@
 #define OPENGLES_2
 #include "config_gles2gsg.h"
 
+#ifdef IS_IOS
+#include "config_eagldisplay.h"
+#include "EAGLGraphicsPipe.h"
+#else
 #include "config_egldisplay.h"
 #include "eglGraphicsPipe.h"
+#endif
 
 /**
  * Initializes the library.  This must be called at least once before any of
@@ -21,7 +26,12 @@
 void
 init_libpandagles2() {
   init_libgles2gsg();
+  
+#ifdef IS_IOS
+  init_libeagldisplay();
+#else
   init_libegldisplay();
+#endif
 }
 
 /**
@@ -30,5 +40,9 @@ init_libpandagles2() {
  */
 int
 get_pipe_type_pandagles2() {
+#ifdef IS_IOS
+  return EAGLGraphicsPipe::get_class_type().get_index();
+#else
   return eglGraphicsPipe::get_class_type().get_index();
+#endif
 }

+ 37 - 0
panda/src/eagldisplay/CMakeLists.txt

@@ -0,0 +1,37 @@
+if(NOT IS_IOS OR NOT HAVE_GLES2 OR NOT HAVE_EAGL)
+  return()
+endif()
+
+set(P3EAGLDISPLAY_HEADERS
+  config_eagldisplay.h
+  eaglGraphicsPipe.h eaglGraphicsPipe.I
+  eaglGraphicsStateGuardian.h eaglGraphicsStateGuardian.I
+  eaglGraphicsWindow.h eaglGraphicsWindow.I
+  pandaEAGLView.h
+  PandaViewController.h
+)
+
+set(P3EAGLDISPLAY_SOURCES
+  config_eagldisplay.mm
+  eaglGraphicsPipe.mm
+  eaglGraphicsStateGuardian.mm
+  eaglGraphicsWindow.mm
+  pandaEAGLView.mm
+  PandaViewController.mm
+)
+
+composite_sources(p3eagldisplay P3EAGLDISPLAY_SOURCES)
+add_component_library(p3eagldisplay SYMBOL BUILDING_PANDA_EAGLDISPLAY
+  ${P3EAGLDISPLAY_HEADERS} ${P3EAGLDISPLAY_SOURCES})
+target_link_libraries(p3eagldisplay p3gles2gsg panda)
+target_compile_options(p3eagldisplay PRIVATE -fobjc-arc)
+
+# Frameworks:
+find_library(UIKIT_LIBRARY UIKit)
+find_library(QUARTZCORE_LIBRARY QuartzCore)
+target_link_libraries(p3eagldisplay ${UIKIT_LIBRARY} ${QUARTZCORE_LIBRARY})
+
+if(NOT BUILD_METALIBS)
+  install(TARGETS p3eagldisplay EXPORT GLES COMPONENT GLES DESTINATION lib)
+endif()
+install(FILES ${P3EAGLDISPLAY_HEADERS} COMPONENT GLES DESTINATION include/panda3d)

+ 12 - 0
panda/src/eagldisplay/EAGLGraphicsPipe.I

@@ -0,0 +1,12 @@
+/**
+ * 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."
+ *
+ * @file EAGLGraphicsPipe.h
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */

+ 72 - 0
panda/src/eagldisplay/EAGLGraphicsPipe.h

@@ -0,0 +1,72 @@
+/**
+ * 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."
+ *
+ * @file EAGLGraphicsPipe.h
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */
+
+#ifndef EAGLGRAPHICSPIPE_H
+#define EAGLGRAPHICSPIPE_H
+
+#include "pandabase.h"
+#include "graphicsWindow.h"
+#include "graphicsPipe.h"
+#include "lightMutex.h"
+#include "lightReMutex.h"
+
+class FrameBufferProperties;
+
+/**
+ * This graphics pipe will create GLES 2.0 contexts for systems that utilize
+ * EAGLContext (iOS, tvOS).
+ */
+class EAGLGraphicsPipe : public GraphicsPipe {
+public:
+  EAGLGraphicsPipe();
+  virtual ~EAGLGraphicsPipe();
+  
+  virtual PreferredWindowThread get_preferred_window_thread() const;
+  virtual std::string get_interface_name() const;
+  
+  static PT(GraphicsPipe) pipe_constructor();
+  
+protected:
+  virtual PT(GraphicsOutput) make_output(const std::string &name,
+                                         const FrameBufferProperties &fb_prop,
+                                         const WindowProperties &win_prop,
+                                         int flags,
+                                         GraphicsEngine *engine,
+                                         GraphicsStateGuardian *gsg,
+                                         GraphicsOutput *host,
+                                         int retry,
+                                         bool &precertify);
+//  virtual PT(GraphicsStateGuardian) make_callback_gsg(GraphicsEngine *engine);
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsPipe::init_type();
+    register_type(_type_handle, "EAGLGraphicsPipe",
+                  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;
+};
+
+#include "EAGLGraphicsPipe.I"
+
+#endif
+

+ 115 - 0
panda/src/eagldisplay/EAGLGraphicsPipe.mm

@@ -0,0 +1,115 @@
+/**
+ * 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."
+ *
+ * @file EAGLGraphicsPipe.mm
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */
+
+#include "eaglGraphicsPipe.h"
+#include "eaglGraphicsWindow.h"
+#include "eaglGraphicsStateGuardian.h"
+#include "config_eagldisplay.h"
+#include "frameBufferProperties.h"
+#include "displayInformation.h"
+
+#import <UIKit/UIKit.h>
+
+TypeHandle EAGLGraphicsPipe::_type_handle;
+
+/**
+ *
+ */
+EAGLGraphicsPipe::
+EAGLGraphicsPipe() {
+  // TODO: Support more than just GraphicsWindows.
+  _supported_types = OT_window;
+  _is_valid = true;
+}
+
+/**
+ *
+ */
+EAGLGraphicsPipe::
+~EAGLGraphicsPipe() {
+}
+
+/**
+ * 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.
+ */
+std::string EAGLGraphicsPipe::
+get_interface_name() const {
+  return "OpenGLES 2";
+}
+
+/**
+ * This function is passed to the GraphicsPipeSelection object to allow the
+ * user to make a default EAGLGraphicsPipe.
+ */
+PT(GraphicsPipe) EAGLGraphicsPipe::
+pipe_constructor() {
+  return new EAGLGraphicsPipe;
+}
+
+/**
+ * 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
+EAGLGraphicsPipe::get_preferred_window_thread() const {
+  // Anything inside UIKit must be called from the main thread.
+  return PWT_app;
+}
+
+/**
+ * Creates a new window on the pipe, if possible.
+ */
+PT(GraphicsOutput) EAGLGraphicsPipe::
+make_output(const std::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;
+  }
+  
+  EAGLGraphicsStateGuardian *eaglgsg = 0;
+  if (gsg != 0) {
+    DCAST_INTO_R(eaglgsg, gsg, NULL);
+  }
+  
+  // First thing to try: an EAGLGraphicsWindow.
+  
+  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)||
+        ((flags&BF_can_bind_layered)!=0)) {
+      return NULL;
+    }
+    return new EAGLGraphicsWindow(engine, this, name, fb_prop, win_prop,
+                                   flags, gsg, host);
+  }
+  
+  // Nothing else left to try.
+  return NULL;
+}

+ 21 - 0
panda/src/eagldisplay/EAGLGraphicsStateGuardian.I

@@ -0,0 +1,21 @@
+/**
+ * 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."
+ *
+ * @file EAGLGraphicsStateGuardian.mm
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */
+
+/**
+ * Gets the FrameBufferProperties for all windows and buffers that use this
+ * GSG.
+ */
+INLINE const FrameBufferProperties &EAGLGraphicsStateGuardian::
+get_fb_properties() const {
+  return _fbprops;
+}

+ 67 - 0
panda/src/eagldisplay/EAGLGraphicsStateGuardian.h

@@ -0,0 +1,67 @@
+/**
+ * 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."
+ *
+ * @file EAGLGraphicsStateGuardian.h
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */
+
+#ifndef EAGLGRAPHICSSTATEGUARDIAN_H
+#define EAGLGRAPHICSSTATEGUARDIAN_H
+
+#include "pandabase.h"
+#include "gles2gsg.h"
+#include "eaglGraphicsPipe.h"
+
+
+#import <OpenGLES/EAGL.h>
+
+/**
+ * A variation of GLES2GraphicsStateGuardian to support Apple's EGL
+ * implementation.
+ */
+class EAGLGraphicsStateGuardian : public GLES2GraphicsStateGuardian {
+public:
+  EAGLGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
+                            EAGLGraphicsStateGuardian *share_with);
+  virtual ~EAGLGraphicsStateGuardian();
+  
+  INLINE const FrameBufferProperties &get_fb_properties() const;
+  void get_properties(FrameBufferProperties &properties);
+  void choose_pixel_format(const FrameBufferProperties &properties,
+                           bool need_pbuffer);
+  
+  FrameBufferProperties _fbprops;
+  EAGLContext *_context;
+  
+  Mutex _context_lock;
+  
+protected:
+  virtual void *do_get_extension_func(const char *name);
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GLES2GraphicsStateGuardian::init_type();
+    register_type(_type_handle, "EAGLGraphicsStateGuardian",
+                  GLES2GraphicsStateGuardian::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 "EAGLGraphicsStateGuardian.I"
+
+#endif

+ 73 - 0
panda/src/eagldisplay/EAGLGraphicsStateGuardian.mm

@@ -0,0 +1,73 @@
+/**
+ * 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."
+ *
+ * @file EAGLGraphicsStateGuardian.mm
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */
+
+#include "eaglGraphicsStateGuardian.h"
+#include <dlfcn.h>
+
+TypeHandle EAGLGraphicsStateGuardian::_type_handle;
+
+/**
+ *
+ */
+EAGLGraphicsStateGuardian::
+EAGLGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
+                           EAGLGraphicsStateGuardian *share_with) :
+GLES2GraphicsStateGuardian(engine, pipe) {
+  
+}
+
+/**
+ *
+ */
+EAGLGraphicsStateGuardian::
+~EAGLGraphicsStateGuardian() {
+  
+}
+
+/**
+ * Gets the FrameBufferProperties to match the indicated config.
+ */
+void EAGLGraphicsStateGuardian::
+get_properties(FrameBufferProperties &properties) {
+  
+  properties.clear();
+  
+  // TODO: Don't make assumptions about the available properties.
+  properties.set_color_bits(32);
+  properties.set_alpha_bits(8);
+  properties.set_depth_bits(8);
+  properties.set_rgba_bits(8, 8, 8, 8);
+  properties.set_back_buffers(1);
+  properties.set_force_hardware(1);
+  properties.set_srgb_color(true);
+}
+
+/**
+ * Selects a visual or fbconfig for all the windows and buffers that use this
+ * gsg.  Also creates the GL context and obtains the visual.
+ */
+void EAGLGraphicsStateGuardian::
+choose_pixel_format(const FrameBufferProperties &properties,
+                    bool need_pbuffer) {
+  _fbprops.clear();
+  get_properties(_fbprops);
+  _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
+}
+
+/**
+ * Gets a pointer to the named GLES extension.
+ */
+void *EAGLGraphicsStateGuardian::
+do_get_extension_func(const char *name) {
+  return dlsym(RTLD_DEFAULT, name);
+}

+ 12 - 0
panda/src/eagldisplay/EAGLGraphicsWindow.I

@@ -0,0 +1,12 @@
+/**
+ * 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."
+ *
+ * @file EAGLGraphicsWindow.I
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */

+ 87 - 0
panda/src/eagldisplay/EAGLGraphicsWindow.h

@@ -0,0 +1,87 @@
+/**
+ * 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."
+ *
+ * @file EAGLGraphicsWindow.h
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */
+
+#ifndef EAGLGRAPHICSWINDOW_H
+#define EAGLGRAPHICSWINDOW_H
+
+#import "pandaEAGLView.h"
+#include "pandabase.h"
+#include "eaglGraphicsPipe.h"
+
+class EAGLGraphicsStateGuardian;
+
+/**
+ * Interface to a view containing an EAGLContext. Because there are no true
+ * "windows" in iOS or tvOS, a given GraphicsWindow will instead correspond to
+ * a view inside a single app, fullscreen or otherwise.
+ */
+class EAGLGraphicsWindow : public GraphicsWindow {
+public:
+  EAGLGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
+                     const std::string &name,
+                     const FrameBufferProperties &fb_prop,
+                     const WindowProperties &win_prop,
+                     int flags,
+                     GraphicsStateGuardian *gsg,
+                     GraphicsOutput *host);
+  ~EAGLGraphicsWindow();
+  
+  PandaEAGLView *_view;
+  
+  GLuint _colorRenderbuffer;
+  GLuint _colorFramebuffer;
+  GLuint _depthRenderbuffer;
+  
+  GLint _backingWidth;
+  GLint _backingHeight;
+  
+  virtual bool begin_frame(FrameMode mode, Thread *current_thread);
+  virtual void end_frame(FrameMode mode, Thread *current_thread);
+  
+  virtual void end_flip();
+  
+  void screen_size_changed();
+  void app_activated();
+  void app_deactivated();
+
+private:
+  void create_framebuffer(EAGLGraphicsStateGuardian *guardian);
+  void destroy_framebuffer();
+  
+//  virtual void set_properties_now(WindowProperties &properties);
+  
+protected:
+  virtual void close_window();
+  virtual bool open_window();
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsWindow::init_type();
+    register_type(_type_handle, "EAGLGraphicsWindow",
+                  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 "EAGLGraphicsWindow.I"
+
+#endif

+ 265 - 0
panda/src/eagldisplay/EAGLGraphicsWindow.mm

@@ -0,0 +1,265 @@
+/**
+ * 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."
+ *
+ * @file EAGLGraphicsWindow.mm
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */
+
+#include "eaglGraphicsWindow.h"
+#include "eaglGraphicsStateGuardian.h"
+// #import "iOSNSNotificationHandler.h"
+
+TypeHandle EAGLGraphicsWindow::_type_handle;
+
+/**
+ *
+ */
+EAGLGraphicsWindow::
+EAGLGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
+                    const std::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)
+{
+  _view = nil;
+}
+
+/**
+ *
+ */
+EAGLGraphicsWindow::
+~EAGLGraphicsWindow() {
+}
+
+/**
+ * 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 EAGLGraphicsWindow::
+begin_frame(FrameMode mode, Thread *current_thread) {
+  begin_frame_spam(mode);
+  PStatTimer timer(_make_current_pcollector, current_thread);
+  
+  EAGLGraphicsStateGuardian *eaglgsg;
+  DCAST_INTO_R(eaglgsg, _gsg, false);
+  nassertr(eaglgsg->_context != nil, false);
+  nassertr(_view != nil, false);
+  
+  eaglgsg->_context_lock.lock();
+  [EAGLContext setCurrentContext:eaglgsg->_context];
+  
+  _gsg->set_current_properties(&get_fb_properties());
+  return _gsg->begin_frame(current_thread);
+}
+
+/**
+ * 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 EAGLGraphicsWindow::
+end_frame(FrameMode mode, Thread *current_thread) {
+  end_frame_spam(mode);
+  nassertv(_gsg != (GraphicsStateGuardian *)NULL);
+  
+  EAGLGraphicsStateGuardian *eaglgsg;
+  DCAST_INTO_V(eaglgsg, _gsg);
+  
+  if (mode == FM_render) {
+    copy_to_textures();
+  }
+  
+  _gsg->end_frame(current_thread);
+  
+  [EAGLContext setCurrentContext:nil];
+  eaglgsg->_context_lock.unlock();
+  
+  if (mode == FM_render) {
+    trigger_flip();
+    clear_cube_map_selection();
+  }
+}
+
+/**
+ * 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 EAGLGraphicsWindow::
+end_flip() {
+  if (_gsg != (GraphicsStateGuardian *)NULL && _flip_ready) {
+    EAGLGraphicsStateGuardian *eaglgsg;
+    DCAST_INTO_V(eaglgsg, _gsg);
+    
+    eaglgsg->_context_lock.lock();
+    
+    // Presents the primary renderbuffer to the screen.
+    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
+    [eaglgsg->_context presentRenderbuffer:GL_RENDERBUFFER];
+    
+    eaglgsg->_context_lock.unlock();
+  }
+  GraphicsWindow::end_flip();
+}
+
+/**
+ * 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 EAGLGraphicsWindow::
+open_window() {
+  EAGLGraphicsPipe *eagl_pipe;
+  DCAST_INTO_R(eagl_pipe, _pipe, false);
+  
+  EAGLGraphicsStateGuardian *eaglgsg;
+  if (_gsg == 0) {
+    // There is no old gsg.  Create a new one.
+    eaglgsg = new EAGLGraphicsStateGuardian(_engine, _pipe, NULL);
+    eaglgsg->choose_pixel_format(_fb_properties, false);
+    _gsg = eaglgsg;
+  } else {
+    // If the old gsg has the wrong pixel format, create a new one that shares
+    // with the old gsg.
+    DCAST_INTO_R(eaglgsg, _gsg, false);
+    if (!eaglgsg->get_fb_properties().subsumes(_fb_properties)) {
+      eaglgsg = new EAGLGraphicsStateGuardian(_engine, _pipe, eaglgsg);
+      eaglgsg->choose_pixel_format(_fb_properties, false);
+      _gsg = eaglgsg;
+    }
+  }
+  
+  if (eaglgsg->_context == nil) {
+    // Could not obtain a proper context.
+    _gsg.clear();
+    close_window();
+    return false;
+  }
+  
+  // Just create the view. The application developer will have to attach it
+  // to something themselves.
+  // TODO: Make this more flexible
+  dispatch_sync(dispatch_get_main_queue(), ^{
+    _view = [[PandaEAGLView alloc] initWithFrame:UIScreen.mainScreen.bounds graphicsWindow:this];
+    [[NSNotificationCenter defaultCenter] postNotificationName:@"ViewCreatedNotification" object:nil userInfo:@{@"view": _view}];
+  });
+
+  [EAGLContext setCurrentContext:eaglgsg->_context];
+  
+  eaglgsg->reset_if_new();
+  
+  // Usually we would create the framebuffer here, but the view will do that itself when [_view layoutSubviews] is called.
+  
+  _fb_properties = eaglgsg->get_fb_properties();
+  
+  _properties.set_size(UIScreen.mainScreen.bounds.size.width,
+                       UIScreen.mainScreen.bounds.size.height);
+  _properties.set_open(true);
+  _properties.set_foreground(true);
+  _is_valid = true;
+  
+  return true;
+}
+
+/**
+ * Handles both screen autorotation and the size of the app changing
+ * due to iPad's split screen view.
+ */
+void EAGLGraphicsWindow::
+screen_size_changed() {
+  EAGLGraphicsStateGuardian *eaglgsg;
+  DCAST_INTO_V(eaglgsg, _gsg);
+  
+  eaglgsg->_context_lock.lock();
+  [EAGLContext setCurrentContext:eaglgsg->_context];
+
+  destroy_framebuffer();
+  create_framebuffer(eaglgsg);
+
+  [EAGLContext setCurrentContext:nil];
+  eaglgsg->_context_lock.unlock();
+
+  WindowProperties properties;
+  properties.set_size(_view.frame.size.width * 3, _view.frame.size.height * 3);
+  system_changed_properties(properties);
+  
+  eagldisplay_cat.debug() << "View size changed\n";
+}
+
+/**
+ * Creates new buffers using _view's CAEAGLLayer. Assumes a lock on the context is already held.
+ */
+void EAGLGraphicsWindow::
+create_framebuffer(EAGLGraphicsStateGuardian *guardian) {
+  glGenRenderbuffers(1, &_colorRenderbuffer);
+  glGenFramebuffers(1, &_colorFramebuffer);
+  
+  glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
+  glBindFramebuffer(GL_FRAMEBUFFER, _colorFramebuffer);
+  
+  [guardian->_context renderbufferStorage:GL_RENDERBUFFER
+                        fromDrawable:(CAEAGLLayer *)_view.layer];
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                            GL_RENDERBUFFER, _colorRenderbuffer);
+  
+  glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,
+                                         &_backingWidth);
+  glGetRenderbufferParameteriv(GL_RENDERBUFFER,
+                                         GL_RENDERBUFFER_HEIGHT,
+                                         &_backingHeight);
+  
+  glGenRenderbuffers(1, &_depthRenderbuffer);
+  glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderbuffer);
+  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16_OES,
+                        _backingWidth, _backingHeight);
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                            GL_RENDERBUFFER, _depthRenderbuffer);
+  
+  glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
+}
+
+/**
+ * Destroy any buffers created by create_framebuffer(). Called mostly when
+ * _view gets resized. Assumes a lock on the context is already held.
+ */
+void EAGLGraphicsWindow::
+destroy_framebuffer() {
+  if (_colorFramebuffer != 0) {
+    glDeleteFramebuffers(1, &_colorFramebuffer);
+    _colorFramebuffer = 0;
+  }
+  if (_colorRenderbuffer != 0) {
+    glDeleteRenderbuffers(1, &_colorRenderbuffer);
+    _colorRenderbuffer = 0;
+  }
+  if (_depthRenderbuffer != 0) {
+    glDeleteRenderbuffers(1, &_depthRenderbuffer);
+    _depthRenderbuffer = 0;
+  }
+}
+
+void EAGLGraphicsWindow::
+close_window() {
+  if (_gsg != (GraphicsStateGuardian *)NULL) {
+    EAGLGraphicsStateGuardian *eaglgsg = DCAST(EAGLGraphicsStateGuardian, _gsg);
+    if (eaglgsg != NULL && eaglgsg->_context != nil) {
+      eaglgsg->_context_lock.lock();
+      destroy_framebuffer();
+      eaglgsg->_context_lock.unlock();
+    }
+  }
+
+  GraphicsWindow::close_window();
+}

+ 13 - 0
panda/src/eagldisplay/PandaViewController.h

@@ -0,0 +1,13 @@
+#import <UIKit/UIKit.h>
+
+@interface PandaViewController : UIViewController
+
+// Whether this instance is functioning as the main GraphicsWindow.
+@property BOOL mainWindow;
+
+- (void)startPythonApp:(NSString *)modulePath;
+- (void)startPythonApp:(NSString *)modulePath pythonRoot:(NSString *)pythonRoot;
+- (void)startCPPApp:(void *)mainFunction;
+- (void)startPandaWithThreadFunction:(SEL)mainSelector;
+
+@end

+ 99 - 0
panda/src/eagldisplay/PandaViewController.mm

@@ -0,0 +1,99 @@
+#import "PandaViewController.h"
+
+#include "Python.h"
+
+#include "EAGLGraphicsWindow.h"
+#include "graphicsEngine.h"
+#include "asyncTaskManager.h"
+#include "load_prc_file.h"
+#include "throw_event.h"
+
+@interface PandaViewController () {
+  NSThread *_pandaThread;
+  NSURL *_fullModulePath;
+}
+- (void)pythonModuleMain;
+@end
+
+@implementation PandaViewController
+
+- (void)startPythonApp:(NSString *)modulePath {
+  [self startPythonApp:modulePath pythonRoot:@"Python"]; 
+}
+
+- (void)startPythonApp:(NSString *)modulePath pythonRoot:(NSString *)pythonRoot {
+  nassertv(modulePath != nil);
+  nassertv(pythonRoot != nil);
+
+  NSURL *resourceURL = [NSBundle.mainBundle resourceURL];
+
+  const char *python_home = [NSURL URLWithString:pythonRoot relativeToURL:resourceURL].fileSystemRepresentation;
+  wchar_t *wpython_home = Py_DecodeLocale(python_home, NULL);
+  Py_SetPythonHome(wpython_home);
+
+  // iOS provides a specific directory for temp files.
+  NSString *tmp_path = [NSString stringWithFormat:@"TMP=%@", NSTemporaryDirectory(), nil];
+  putenv((char *)[tmp_path UTF8String]);
+
+  _fullModulePath = [NSURL URLWithString:modulePath relativeToURL:resourceURL];
+
+  [self startPandaWithThreadFunction:@selector(pythonModuleMain)];
+}
+
+- (void)startCPPApp:(void *)mainFunction {
+  nassertv(mainFunction != NULL);
+  // (not yet implemented)
+}
+
+- (void)startPandaWithThreadFunction:(SEL)mainSelector {
+  // TODO: Make the notification name a constant
+  NSNotificationCenter *__weak notificationCenter = [NSNotificationCenter defaultCenter];
+  id __block notificationToken = [notificationCenter addObserverForName:@"ViewCreatedNotification" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
+    NSLog(@"Found the view!");
+    self.view = note.userInfo[@"view"];
+    self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+    [notificationCenter removeObserver:notificationToken];
+  }];
+  
+  _pandaThread = [[NSThread alloc] initWithTarget:self selector:mainSelector object:nil];
+  [_pandaThread start];
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+  PyGILState_STATE gstate = PyGILState_Ensure();
+  Py_Finalize();
+  PyGILState_Release(gstate);
+}
+
+- (void)pythonModuleMain {
+  PT(Thread) panda_thread = Thread::bind_thread("ios_app", "ios_app");
+
+  // TODO: Load a .prc file from bundle resources instead of hardocoding this.
+  const char *model_path_config = [NSString stringWithFormat:@"model-path %@", [_fullModulePath.path stringByDeletingLastPathComponent]].UTF8String;
+  
+  load_prc_file_data("", "notify-level-gles2gsg debug");
+  load_prc_file_data("", "notify-level-eagldisplay debug");
+  load_prc_file_data("", "notify-level-thread debug");
+  load_prc_file_data("", "notify-level-pipeline debug");
+  load_prc_file_data("", "notify-level-task debug");
+  load_prc_file_data("", "color-bits 8 8 8");
+  load_prc_file_data("", "alpha-bits 8");
+  load_prc_file_data("", "depth-bits 8");
+  load_prc_file_data("", "framebuffer-srgb true");
+  load_prc_file_data("", "threading-model Cull/Draw");
+  load_prc_file_data("", "default-model-extension .egg");
+  load_prc_file_data("", "load-display pandagles2");
+  load_prc_file_data("", "assert-abort true");
+  load_prc_file_data("", model_path_config);
+  
+  Py_Initialize();
+  
+  FILE *fp = fopen(_fullModulePath.fileSystemRepresentation, "r");
+  nassertv(fp != NULL);
+  
+  PyRun_SimpleFile(fp, _fullModulePath.lastPathComponent.UTF8String);
+  
+  Py_Finalize();
+}
+
+@end

+ 26 - 0
panda/src/eagldisplay/config_eagldisplay.h

@@ -0,0 +1,26 @@
+/**
+ * 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."
+ *
+ * @file config_eagldisplay.h
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */
+
+#ifndef CONFIG_EAGLDISPLAY_H
+#define CONFIG_EAGLDISPLAY_H
+
+#include "pandabase.h"
+#include "notifyCategoryProxy.h"
+#include "configVariableBool.h"
+#include "configVariableInt.h"
+
+NotifyCategoryDecl(eagldisplay, EXPCL_MISC, EXPTP_MISC);
+
+extern EXPCL_MISC void init_libeagldisplay();
+
+#endif  // CONFIG_IPHONE_H

+ 54 - 0
panda/src/eagldisplay/config_eagldisplay.mm

@@ -0,0 +1,54 @@
+/**
+ * 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."
+ *
+ * @file config_eagldisplay.mm
+ * @author D. Lawrence
+ * @date 2019-01-03
+ */
+
+#include "config_eagldisplay.h"
+#include "dconfig.h"
+#include "graphicsPipeSelection.h"
+#include "pandaSystem.h"
+#include "eaglGraphicsPipe.h"
+#include "eaglGraphicsWindow.h"
+#include "eaglGraphicsStateGuardian.h"
+#include <dispatch/dispatch.h>
+
+Configure(config_eagldisplay);
+
+NotifyCategoryDef(eagldisplay, "display");
+
+ConfigureFn(config_eagldisplay) {
+  init_libeagldisplay();
+}
+
+/**
+ * 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_libeagldisplay() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+  
+  EAGLGraphicsPipe::init_type();
+  EAGLGraphicsWindow::init_type();
+  EAGLGraphicsStateGuardian::init_type();
+  
+  GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
+  selection->add_pipe_type(EAGLGraphicsPipe::get_class_type(), EAGLGraphicsPipe::pipe_constructor);
+  
+  PandaSystem *ps = PandaSystem::get_global_ptr();
+  ps->set_system_tag("OpenGL ES 2", "window_system", "iOS");
+}

+ 5 - 0
panda/src/eagldisplay/p3eagldisplay_composite1.mm

@@ -0,0 +1,5 @@
+#include "config_eagldipslay.mm"
+#include "EAGLGraphicsPipe.mm"
+#include "EAGLGraphicsStateGuardian.mm"
+#include "EAGLGraphicsWindow.mm"
+#include "pandaEAGLView.mm"

+ 26 - 0
panda/src/eagldisplay/pandaEAGLView.h

@@ -0,0 +1,26 @@
+/**
+ * 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."
+ *
+ * @file PandaEAGLView.h
+ * @author D. Lawrence
+ * @date 2019-01-04
+ */
+
+#import <UIKit/UIKit.h>
+
+class EAGLGraphicsWindow;
+
+/**
+ * A small subclass of UIView that sets up a CAEAGLLayer as the view's backing
+ * layer, and sets a few options on it.
+ */
+@interface PandaEAGLView : UIView
+
+- (id)initWithFrame:(CGRect)frame graphicsWindow:(EAGLGraphicsWindow *)window;
+
+@end

+ 58 - 0
panda/src/eagldisplay/pandaEAGLView.mm

@@ -0,0 +1,58 @@
+/**
+ * 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."
+ *
+ * @file PandaEAGLView.m
+ * @author D. Lawrence
+ * @date 2019-01-04
+ */
+
+#import "pandaEAGLView.h"
+#include "eaglGraphicsWindow.h"
+
+@interface PandaEAGLView () {
+  EAGLGraphicsWindow *_window;
+}
+@end
+
+@implementation PandaEAGLView
+
+/**
+ * Initialize the view and specify some options for the backing layer.
+ */
+- (id)initWithFrame:(CGRect)frame graphicsWindow:(EAGLGraphicsWindow *)window {
+  if ((self = [super initWithFrame:frame])) {
+
+    CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
+    
+    eaglLayer.opaque = YES;
+    eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
+                                    [NSNumber numberWithBool:NO],
+                                    kEAGLDrawablePropertyRetainedBacking,
+                                    kEAGLColorFormatRGBA8,
+                                    kEAGLDrawablePropertyColorFormat, nil];
+    eaglLayer.contentsScale = 3.0;
+    
+    _window = window;
+  }
+  return self;
+}
+
+- (void)layoutSubviews {
+  NSLog(@"ARC enabled: %d", __has_feature(objc_arc));
+  _window->screen_size_changed();
+}
+
+/**
+ * Specify we want a CAEAGLLayer to be the backing layer for this view. For
+ * some reason you have to subclass UIView to do this.
+ */
++ (Class) layerClass {
+  return [CAEAGLLayer class];
+}
+
+@end