Initial import of Cobalt 2.8885 2016-07-27
diff --git a/src/glimp/egl/attrib_map.cc b/src/glimp/egl/attrib_map.cc
new file mode 100644
index 0000000..97af886
--- /dev/null
+++ b/src/glimp/egl/attrib_map.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "glimp/egl/attrib_map.h"
+
+namespace glimp {
+namespace egl {
+
+AttribMap ParseRawAttribList(const EGLint* attrib_list) {
+  AttribMap ret;
+  if (!attrib_list) {
+    return ret;
+  }
+
+  const int* current_attrib = attrib_list;
+  while (*current_attrib != EGL_NONE) {
+    int key = *current_attrib++;
+    int value = *current_attrib++;
+
+    ret[key] = value;
+  }
+
+  return ret;
+}
+
+}  // namespace egl
+}  // namespace glimp
diff --git a/src/glimp/egl/attrib_map.h b/src/glimp/egl/attrib_map.h
new file mode 100644
index 0000000..62fe5ac
--- /dev/null
+++ b/src/glimp/egl/attrib_map.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GLIMP_EGL_ATTRIB_MAP_H_
+#define GLIMP_EGL_ATTRIB_MAP_H_
+
+#include <EGL/egl.h>
+
+#include <map>
+
+namespace glimp {
+namespace egl {
+
+typedef std::map<int, int> AttribMap;
+
+// Many EGL functions take an "attribute list" as a parameter that all share a
+// similar format:  A list of integer key/value pairs and concluded with the
+// value EGL_NONE (like a null terminated C string).  This function parses
+// that attribute list into a map and returns it.
+AttribMap ParseRawAttribList(const EGLint* attrib_list);
+
+}  // namespace egl
+}  // namespace glimp
+
+#endif  // GLIMP_EGL_ATTRIB_MAP_H_
diff --git a/src/glimp/egl/config.cc b/src/glimp/egl/config.cc
new file mode 100644
index 0000000..1af0678
--- /dev/null
+++ b/src/glimp/egl/config.cc
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "glimp/egl/config.h"
+
+#include <algorithm>
+
+#include "starboard/log.h"
+
+namespace glimp {
+namespace egl {
+
+namespace {
+bool AttributeKeyAndValueAreValid(int key, int value) {
+  switch (key) {
+    // Deal with the size keys where all values are valid.
+    case EGL_RED_SIZE:
+    case EGL_GREEN_SIZE:
+    case EGL_BLUE_SIZE:
+    case EGL_ALPHA_SIZE:
+    case EGL_BUFFER_SIZE:
+    case EGL_LUMINANCE_SIZE:
+    case EGL_STENCIL_SIZE: {
+      return true;
+    }
+
+    // Deal with the mask keys where all values are valid.
+    case EGL_CONFORMANT:
+    case EGL_RENDERABLE_TYPE:
+    case EGL_SURFACE_TYPE: {
+      return true;
+    }
+
+    // Deal with boolean values.
+    case EGL_BIND_TO_TEXTURE_RGBA: {
+      switch (value) {
+        case EGL_DONT_CARE:
+        case EGL_TRUE:
+        case EGL_FALSE: {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    case EGL_COLOR_BUFFER_TYPE: {
+      switch (value) {
+        case EGL_RGB_BUFFER:
+        case EGL_LUMINANCE_BUFFER: {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
+  // If the switch statement didn't catch the key, this is an unknown
+  // key values.
+  // TODO: glimp doesn't support all values yet, and will return false for keys
+  //       that it doesn't support.
+  return false;
+}
+}  // namespace
+
+bool ValidateConfigAttribList(const AttribMap& attribs) {
+  for (AttribMap::const_iterator iter = attribs.begin(); iter != attribs.end();
+       ++iter) {
+    if (!AttributeKeyAndValueAreValid(iter->first, iter->second)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+namespace {
+// Returns whether or not a single attribute (the parameters |key| and |value|)
+// matches a config or not.
+bool ConfigMatchesAttribute(const Config& config, int key, int value) {
+  SB_DCHECK(AttributeKeyAndValueAreValid(key, value));
+  SB_DCHECK(value != EGL_DONT_CARE);
+
+  switch (key) {
+    case EGL_RED_SIZE:
+    case EGL_GREEN_SIZE:
+    case EGL_BLUE_SIZE:
+    case EGL_ALPHA_SIZE:
+    case EGL_BUFFER_SIZE:
+    case EGL_LUMINANCE_SIZE:
+    case EGL_STENCIL_SIZE: {
+      // We match if our config's bit depth is greater than or equal to the
+      // requested value.
+      return config.find(key)->second >= value;
+    }
+
+    case EGL_CONFORMANT:
+    case EGL_RENDERABLE_TYPE:
+    case EGL_SURFACE_TYPE: {
+      // We match if our config's bit mask includes the requested bit mask.
+      return (config.find(key)->second & value) == value;
+    }
+
+    case EGL_BIND_TO_TEXTURE_RGBA: {
+      // Our config matches booleans if the requested boolean is not true, or
+      // else if the config's corresponding boolean is also true.
+      return value != EGL_TRUE || config.find(key)->second == EGL_TRUE;
+    }
+
+    case EGL_COLOR_BUFFER_TYPE: {
+      // We match if our config value matches the requested value exactly.
+      return config.find(key)->second == value;
+    }
+  }
+
+  // The attributes should have been validated when this function is called,
+  // so if we reach this point, then there is an inconsistency between
+  // this function and AttributeKeyAndValueAreValid().
+  SB_NOTREACHED();
+  return false;
+}
+
+bool ConfigMatchesAttributes(const Config& config,
+                             const AttribMap& attrib_list) {
+  for (AttribMap::const_iterator iter = attrib_list.begin();
+       iter != attrib_list.end(); ++iter) {
+    if (iter->second != EGL_DONT_CARE) {
+      if (!ConfigMatchesAttribute(config, iter->first, iter->second)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+}  // namespace
+
+std::vector<Config*> FilterConfigs(const std::set<Config*>& configs,
+                                   const AttribMap& attrib_list) {
+  std::vector<Config*> ret;
+
+  for (std::set<Config*>::const_iterator iter = configs.begin();
+       iter != configs.end(); ++iter) {
+    if (ConfigMatchesAttributes(**iter, attrib_list)) {
+      ret.push_back(*iter);
+    }
+  }
+
+  return ret;
+}
+
+namespace {
+
+class ConfigSorter {
+ public:
+  explicit ConfigSorter(const AttribMap& attrib_list)
+      : attrib_list_(attrib_list) {}
+
+  // We define this such that it sorts in decreasing order of preference.
+  bool operator()(const Config* lhs, const Config* rhs) const {
+    // Bit depth must be sorted in ascending order as a total over all
+    // channels that are specified by the config.
+    if (GetTotalBitDepth(*lhs) > GetTotalBitDepth(*rhs)) {
+      return true;
+    }
+    return false;
+  }
+
+ private:
+  // Returns the bit depth for a given channel, or 0 if we don't care about
+  // it's value (e.g. it is not in the specified attribute list).
+  int GetTotalBitDepthForChannel(const Config& config, int key) const {
+    AttribMap::const_iterator found = attrib_list_.find(key);
+    if (found == attrib_list_.end() || found->second == EGL_DONT_CARE) {
+      return 0;
+    } else {
+      return found->second;
+    }
+  }
+
+  // Gets the total depth for all color channels, to be used to decide the
+  // sort order.
+  int GetTotalBitDepth(const Config& config) const {
+    int total_bit_depth = 0;
+    total_bit_depth += GetTotalBitDepthForChannel(config, EGL_RED_SIZE);
+    total_bit_depth += GetTotalBitDepthForChannel(config, EGL_GREEN_SIZE);
+    total_bit_depth += GetTotalBitDepthForChannel(config, EGL_BLUE_SIZE);
+    total_bit_depth += GetTotalBitDepthForChannel(config, EGL_ALPHA_SIZE);
+    return total_bit_depth;
+  }
+
+  const AttribMap& attrib_list_;
+};
+}  // namespace
+
+void SortConfigs(const AttribMap& attrib_list,
+                 std::vector<Config*>* in_out_configs) {
+  ConfigSorter config_sorter(attrib_list);
+  std::sort(in_out_configs->begin(), in_out_configs->end(), config_sorter);
+}
+
+EGLConfig ToEGLConfig(Config* config) {
+  return reinterpret_cast<EGLConfig>(config);
+}
+
+}  // namespace egl
+}  // namespace glimp
diff --git a/src/glimp/egl/config.h b/src/glimp/egl/config.h
new file mode 100644
index 0000000..5ac18fd
--- /dev/null
+++ b/src/glimp/egl/config.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GLIMP_EGL_CONFIG_H_
+#define GLIMP_EGL_CONFIG_H_
+
+#include <EGL/egl.h>
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "glimp/egl/attrib_map.h"
+
+namespace glimp {
+namespace egl {
+
+// Our underlying EGLConfig structure is just a mapping from config key to
+// value.
+typedef std::map<int, int> Config;
+
+// Examines the |attribs| list of attributes passed in to eglGetConfig(), and
+// checks if the attributes are invalid, in which case false is returned,
+// otherwise true is returned.
+bool ValidateConfigAttribList(const AttribMap& attribs);
+
+// Filters the input configs such that only those that match the specified
+// |attrib_list| are returned.
+std::vector<Config*> FilterConfigs(const std::set<Config*>& configs,
+                                   const AttribMap& attrib_list);
+
+// Sorts the input list in place according to what best metches the specified
+// |attrib_list|.  |in_out_configs| will be sorted after this function returns.
+void SortConfigs(const AttribMap& attrib_list,
+                 std::vector<Config*>* in_out_configs);
+
+// Convert our internal Config pointer type into a public EGLConfig type.
+EGLConfig ToEGLConfig(Config* config);
+
+}  // namespace egl
+}  // namespace glimp
+
+#endif  // GLIMP_EGL_CONFIG_H_
diff --git a/src/glimp/egl/display.cc b/src/glimp/egl/display.cc
new file mode 100644
index 0000000..0d02a74
--- /dev/null
+++ b/src/glimp/egl/display.cc
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "glimp/egl/display.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "glimp/egl/config.h"
+#include "glimp/egl/error.h"
+#include "starboard/log.h"
+
+namespace glimp {
+namespace egl {
+
+Display::Display(nb::scoped_ptr<DisplayImpl> display_impl)
+    : impl_(display_impl.Pass()) {}
+
+Display::~Display() {
+  SB_DCHECK(active_surfaces_.empty());
+}
+
+void Display::GetVersionInfo(EGLint* major, EGLint* minor) {
+  DisplayImpl::VersionInfo version_info = impl_->GetVersionInfo();
+  if (major) {
+    *major = version_info.major;
+  }
+  if (minor) {
+    *minor = version_info.minor;
+  }
+}
+
+bool Display::ChooseConfig(const EGLint* attrib_list,
+                           EGLConfig* configs,
+                           EGLint config_size,
+                           EGLint* num_config) {
+  if (!num_config) {
+    SetError(EGL_BAD_PARAMETER);
+    return false;
+  }
+  AttribMap attribs = ParseRawAttribList(attrib_list);
+  if (!ValidateConfigAttribList(attribs)) {
+    SetError(EGL_BAD_ATTRIBUTE);
+    return false;
+  }
+
+  std::vector<Config*> configs_vector =
+      FilterConfigs(impl_->GetSupportedConfigs(), attribs);
+  SortConfigs(attribs, &configs_vector);
+
+  if (configs) {
+    *num_config =
+        std::min(config_size, static_cast<int>(configs_vector.size()));
+    for (int i = 0; i < *num_config; ++i) {
+      configs[i] = ToEGLConfig(configs_vector[i]);
+    }
+  } else {
+    *num_config = static_cast<int>(configs_vector.size());
+  }
+
+  return true;
+}
+
+bool Display::ConfigIsValid(EGLConfig config) {
+  const DisplayImpl::ConfigSet& supported_configs =
+      impl_->GetSupportedConfigs();
+
+  return supported_configs.find(reinterpret_cast<Config*>(config)) !=
+         supported_configs.end();
+}
+
+EGLSurface Display::CreateWindowSurface(EGLConfig config,
+                                        EGLNativeWindowType win,
+                                        const EGLint* attrib_list) {
+  AttribMap attribs = ParseRawAttribList(attrib_list);
+  if (!ValidateSurfaceAttribList(attribs)) {
+    SetError(EGL_BAD_ATTRIBUTE);
+    return EGL_NO_SURFACE;
+  }
+
+  if (!ConfigIsValid(config)) {
+    SetError(EGL_BAD_CONFIG);
+    return EGL_NO_SURFACE;
+  }
+
+  if (!((*reinterpret_cast<Config*>(config))[EGL_SURFACE_TYPE] |
+        EGL_WINDOW_BIT)) {
+    // The config used must have the EGL_WINDOW_BIT set in order for us to
+    // be able to create windows.
+    SetError(EGL_BAD_MATCH);
+    return EGL_NO_SURFACE;
+  }
+
+  nb::scoped_ptr<SurfaceImpl> surface_impl = impl_->CreateWindowSurface(
+      reinterpret_cast<Config*>(config), win, attribs);
+  if (!surface_impl) {
+    return EGL_NO_SURFACE;
+  }
+
+  Surface* surface = new Surface(surface_impl.Pass());
+  active_surfaces_.insert(surface);
+
+  return ToEGLSurface(surface);
+}
+
+EGLSurface Display::CreatePbufferSurface(EGLConfig config,
+                                         const EGLint* attrib_list) {
+  AttribMap attribs = ParseRawAttribList(attrib_list);
+  if (!ValidateSurfaceAttribList(attribs)) {
+    SetError(EGL_BAD_ATTRIBUTE);
+    return EGL_NO_SURFACE;
+  }
+
+  if (!ConfigIsValid(config)) {
+    SetError(EGL_BAD_CONFIG);
+    return EGL_NO_SURFACE;
+  }
+
+  if (!((*reinterpret_cast<Config*>(config))[EGL_SURFACE_TYPE] |
+        EGL_PBUFFER_BIT)) {
+    // The config used must have the EGL_PBUFFER_BIT set in order for us to
+    // be able to create pbuffers.
+    SetError(EGL_BAD_MATCH);
+    return EGL_NO_SURFACE;
+  }
+
+  nb::scoped_ptr<SurfaceImpl> surface_impl =
+      impl_->CreatePbufferSurface(reinterpret_cast<Config*>(config), attribs);
+  if (!surface_impl) {
+    return EGL_NO_SURFACE;
+  }
+
+  Surface* surface = new Surface(surface_impl.Pass());
+  active_surfaces_.insert(surface);
+
+  return ToEGLSurface(surface);
+}
+
+bool Display::SurfaceIsValid(EGLSurface surface) {
+  return active_surfaces_.find(FromEGLSurface(surface)) !=
+         active_surfaces_.end();
+}
+
+bool Display::DestroySurface(EGLSurface surface) {
+  if (!SurfaceIsValid(surface)) {
+    SetError(EGL_BAD_SURFACE);
+    return false;
+  }
+
+  Surface* surf = FromEGLSurface(surface);
+  active_surfaces_.erase(surf);
+  delete surf;
+  return true;
+}
+
+namespace {
+// Returns -1 if the context attributes are invalid.
+int GetContextVersion(const EGLint* attrib_list) {
+  AttribMap attribs = ParseRawAttribList(attrib_list);
+
+  // According to
+  //   https://www.khronos.org/registry/egl/sdk/docs/man/html/eglCreateContext.xhtml,
+  // the default version of the GL ES context is 1.
+  if (attribs.empty()) {
+    return 1;
+  }
+
+  // EGL_CONTEXT_CLIENT_VERSION is the only valid attribute for CreateContext.
+  AttribMap::const_iterator found = attribs.find(EGL_CONTEXT_CLIENT_VERSION);
+  if (found == attribs.end()) {
+    // If we didn't find it, and the attribute list is not empty (checked above)
+    // then this is an invalid attribute list.
+    return -1;
+  } else {
+    return found->second;
+  }
+}
+}  // namespace
+
+EGLContext Display::CreateContext(EGLConfig config,
+                                  EGLContext share_context,
+                                  const EGLint* attrib_list) {
+  // glimp only supports GL ES versions 2 and 3.
+  int context_version = GetContextVersion(attrib_list);
+  if (context_version != 2 && context_version != 3) {
+    SetError(EGL_BAD_ATTRIBUTE);
+    return EGL_NO_CONTEXT;
+  }
+
+  if (!ConfigIsValid(config)) {
+    SetError(EGL_BAD_CONFIG);
+    return EGL_NO_CONTEXT;
+  }
+
+  // Ensure that |share_context| is either unspecified, or valid.
+  gles::Context* share = NULL;
+  if (share_context != EGL_NO_CONTEXT) {
+    if (!ContextIsValid(share_context)) {
+      SetError(EGL_BAD_CONTEXT);
+      return EGL_NO_CONTEXT;
+    }
+    share = reinterpret_cast<gles::Context*>(share_context);
+  }
+
+  nb::scoped_ptr<gles::ContextImpl> context_impl =
+      impl_->CreateContext(reinterpret_cast<Config*>(config), context_version);
+  if (!context_impl) {
+    return EGL_NO_CONTEXT;
+  }
+
+  gles::Context* context = new gles::Context(context_impl.Pass(), share);
+  active_contexts_.insert(context);
+
+  return reinterpret_cast<EGLContext>(context);
+}
+
+bool Display::ContextIsValid(EGLContext context) {
+  return active_contexts_.find(reinterpret_cast<gles::Context*>(context)) !=
+         active_contexts_.end();
+}
+
+bool Display::DestroyContext(EGLContext ctx) {
+  if (!ContextIsValid(ctx)) {
+    SetError(EGL_BAD_CONTEXT);
+    return false;
+  }
+
+  gles::Context* context = reinterpret_cast<gles::Context*>(ctx);
+  active_contexts_.erase(context);
+  delete context;
+  return true;
+}
+
+bool Display::MakeCurrent(EGLSurface draw, EGLSurface read, EGLContext ctx) {
+  if (draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE &&
+      ctx == EGL_NO_CONTEXT) {
+    if (!ContextIsValid(reinterpret_cast<EGLContext>(
+            gles::Context::GetTLSCurrentContext()))) {
+      SB_DLOG(WARNING)
+          << "Attempted to release a context not owned by this display.";
+      SetError(EGL_BAD_CONTEXT);
+      return false;
+    }
+    gles::Context::ReleaseTLSCurrentContext();
+    return true;
+  }
+
+  if (!ContextIsValid(ctx)) {
+    SetError(EGL_BAD_CONTEXT);
+    return false;
+  }
+
+  if (!SurfaceIsValid(draw)) {
+    SetError(EGL_BAD_SURFACE);
+    return false;
+  }
+
+  if (!SurfaceIsValid(read)) {
+    SetError(EGL_BAD_SURFACE);
+    return false;
+  }
+
+  return gles::Context::SetTLSCurrentContext(
+      reinterpret_cast<gles::Context*>(ctx), FromEGLSurface(draw),
+      FromEGLSurface(read));
+}
+
+bool Display::SwapBuffers(EGLSurface surface) {
+  if (!SurfaceIsValid(surface)) {
+    SetError(EGL_BAD_SURFACE);
+    return false;
+  }
+  Surface* surface_object = FromEGLSurface(surface);
+
+  gles::Context* current_context = gles::Context::GetTLSCurrentContext();
+  if (!ContextIsValid(reinterpret_cast<EGLContext>(current_context))) {
+    // The specification for eglSwapBuffers() does not explicitly state that
+    // the surface's context needs to be current when eglSwapBuffers() is
+    // called, but we enforce this in glimp as it is a very typical use-case and
+    // it considerably simplifies the process.
+    SB_DLOG(WARNING)
+        << "eglSwapBuffers() called when no or an invalid context was current.";
+    SetError(EGL_BAD_SURFACE);
+    return false;
+  }
+
+  if (current_context->draw_surface() != surface_object) {
+    SB_DLOG(WARNING)
+        << "eglSwapBuffers() called on a surface that is not the draw surface "
+        << "of the current context.";
+    SetError(EGL_BAD_SURFACE);
+    return false;
+  }
+
+  current_context->SwapBuffers();
+  return true;
+}
+
+}  // namespace egl
+}  // namespace glimp
diff --git a/src/glimp/egl/display.h b/src/glimp/egl/display.h
new file mode 100644
index 0000000..a61469a
--- /dev/null
+++ b/src/glimp/egl/display.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GLIMP_EGL_DISPLAY_H_
+#define GLIMP_EGL_DISPLAY_H_
+
+#include <set>
+
+#include "glimp/egl/config.h"
+#include "glimp/egl/display_impl.h"
+#include "glimp/gles/context.h"
+#include "nb/scoped_ptr.h"
+
+namespace glimp {
+namespace egl {
+
+// Encapsulates the concept of an EGL display.  There is usually only one of
+// these per process, and it represents the entire graphics system.  It is
+// the highest level object, and the "factory" responsible for generating
+// graphics contexts.  It is a platform-independent object that wraps a
+// platform-dependent DisplayImpl object which must be injected into the Display
+// upon construction.
+class Display {
+ public:
+  // In order to create a display, it must have a platform-specific
+  // implementation injected into it, where many methods will forward to.
+  explicit Display(nb::scoped_ptr<DisplayImpl> display_impl);
+  ~Display();
+
+  void GetVersionInfo(EGLint* major, EGLint* minor);
+
+  bool ChooseConfig(const EGLint* attrib_list,
+                    EGLConfig* configs,
+                    EGLint config_size,
+                    EGLint* num_config);
+  bool ConfigIsValid(EGLConfig config);
+
+  EGLSurface CreateWindowSurface(EGLConfig config,
+                                 EGLNativeWindowType win,
+                                 const EGLint* attrib_list);
+  EGLSurface CreatePbufferSurface(EGLConfig config, const EGLint* attrib_list);
+
+  bool SurfaceIsValid(EGLSurface surface);
+  bool DestroySurface(EGLSurface surface);
+
+  EGLContext CreateContext(EGLConfig config,
+                           EGLContext share_context,
+                           const EGLint* attrib_list);
+  bool ContextIsValid(EGLContext context);
+  bool DestroyContext(EGLContext ctx);
+
+  bool MakeCurrent(EGLSurface draw, EGLSurface read, EGLContext ctx);
+  bool SwapBuffers(EGLSurface surface);
+
+  DisplayImpl* impl() const { return impl_.get(); }
+
+ private:
+  nb::scoped_ptr<DisplayImpl> impl_;
+
+  // Keeps track of all created but not destroyed surfaces.
+  std::set<Surface*> active_surfaces_;
+
+  // Keeps track of all created but not destroyed contexts.
+  std::set<gles::Context*> active_contexts_;
+};
+
+}  // namespace egl
+}  // namespace glimp
+
+#endif  // GLIMP_EGL_DISPLAY_H_
diff --git a/src/glimp/egl/display_impl.h b/src/glimp/egl/display_impl.h
new file mode 100644
index 0000000..c430202
--- /dev/null
+++ b/src/glimp/egl/display_impl.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GLIMP_EGL_DISPLAY_IMPL_H_
+#define GLIMP_EGL_DISPLAY_IMPL_H_
+
+#include <EGL/egl.h>
+
+#include <set>
+
+#include "glimp/egl/attrib_map.h"
+#include "glimp/egl/config.h"
+#include "glimp/egl/surface.h"
+#include "glimp/gles/context_impl.h"
+#include "nb/scoped_ptr.h"
+
+namespace glimp {
+namespace egl {
+
+// All platform-specific aspects of a EGL Display are implemented within
+// subclasses of DisplayImpl.  Platforms must also implement
+// DisplayImpl::Create(), declared below, in order to define how
+// platform-specific DisplayImpls are to be created.
+class DisplayImpl {
+ public:
+  // Return value type for the method GetVersionInfo().
+  struct VersionInfo {
+    int major;
+    int minor;
+  };
+
+  typedef std::set<Config*> ConfigSet;
+
+  virtual ~DisplayImpl() {}
+
+  // Returns true if the given |native_display| is a valid display ID that can
+  // be subsequently passed into Create().
+  // To be implemented by each implementing platform.
+  static bool IsValidNativeDisplayType(EGLNativeDisplayType display_id);
+  // Creates and returns a new DisplayImpl object.
+  // To be implemented by each implementing platform.
+  static nb::scoped_ptr<DisplayImpl> Create(EGLNativeDisplayType display_id);
+
+  // Returns the EGL major and minor versions, if they are not NULL.
+  // Called by eglInitialize():
+  //   https://www.khronos.org/registry/egl/sdk/docs/man/html/eglInitialize.xhtml
+  virtual VersionInfo GetVersionInfo() = 0;
+
+  // Returns *all* configs for this display that may be chosen via a call to
+  // eglChooseConfig().
+  //   https://www.khronos.org/registry/egl/sdk/docs/man/html/eglChooseConfig.xhtml
+  virtual const ConfigSet& GetSupportedConfigs() const = 0;
+
+  // Creates and returns a SurfaceImpl object that represents the surface of a
+  // window and is compatible with this DisplayImpl object.  This will be called
+  // when eglCreateWindowSurface() is called.
+  //   https://www.khronos.org/registry/egl/sdk/docs/man/html/eglCreateWindowSurface.xhtml
+  virtual nb::scoped_ptr<SurfaceImpl> CreateWindowSurface(
+      const Config* config,
+      EGLNativeWindowType win,
+      const AttribMap& attributes) = 0;
+
+  // Creates and returns a SurfaceImpl object that represents the surface of a
+  // Pbuffer and is compatible with this DisplayImpl object.  This will be
+  // called when eglCreatePbufferSurface() is called.
+  //   https://www.khronos.org/registry/egl/sdk/docs/man/html/eglCreatePbufferSurface.xhtml
+  virtual nb::scoped_ptr<SurfaceImpl> CreatePbufferSurface(
+      const Config* config,
+      const AttribMap& attributes) = 0;
+
+  // Creates and returns a gles::ContextImpl object that contains the platform
+  // specific implementation of a GL ES Context, of the specified version that
+  // is compatible with the specified config.
+  virtual nb::scoped_ptr<gles::ContextImpl> CreateContext(const Config* config,
+                                                          int gles_version) = 0;
+
+ private:
+};
+
+}  // namespace egl
+}  // namespace glimp
+
+#endif  // GLIMP_EGL_DISPLAY_IMPL_H_
diff --git a/src/glimp/egl/display_registry.cc b/src/glimp/egl/display_registry.cc
new file mode 100644
index 0000000..c2f1d2d
--- /dev/null
+++ b/src/glimp/egl/display_registry.cc
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "glimp/egl/display_registry.h"
+
+#include "glimp/egl/display_impl.h"
+#include "glimp/egl/error.h"
+
+namespace glimp {
+namespace egl {
+
+int DisplayRegistry::num_connections_ = 0;
+DisplayRegistry::Connection
+    DisplayRegistry::connections_[DisplayRegistry::kMaxDisplays];
+
+EGLDisplay DisplayRegistry::GetDisplay(EGLNativeDisplayType native_display) {
+  // Check to see if a display already exists for this native_display.  If so,
+  // return it, otherwise create a new one and return that.
+  for (int i = 0; i < num_connections_; ++i) {
+    if (connections_[i].native_display == native_display) {
+      return reinterpret_cast<EGLDisplay>(&connections_[i]);
+    }
+  }
+
+  // If the platform-specific implementation does not accept the specified
+  // |native_display|, return in error.
+  if (!DisplayImpl::IsValidNativeDisplayType(native_display)) {
+    return EGL_NO_DISPLAY;
+  } else {
+    // Create a new display connection (i.e. EGLDisplay), add it to our
+    // display mapping so it can be looked up later, and then return it.
+    SB_CHECK(num_connections_ < kMaxDisplays);
+    connections_[num_connections_].native_display = native_display;
+    connections_[num_connections_].display = NULL;
+    ++num_connections_;
+    return reinterpret_cast<EGLDisplay>(&connections_[num_connections_ - 1]);
+  }
+}
+
+bool DisplayRegistry::InitializeDisplay(EGLDisplay display) {
+  SB_DCHECK(Valid(display));
+  Connection* connection = reinterpret_cast<Connection*>(display);
+  if (!connection->display) {
+    nb::scoped_ptr<DisplayImpl> display_impl =
+        DisplayImpl::Create(connection->native_display);
+    // If the platform-specific glimp implementation rejected the native
+    // display, then we return false to indicate failure.
+    if (!display_impl) {
+      return false;
+    }
+
+    connection->display = new Display(display_impl.Pass());
+  }
+
+  return true;
+}
+
+void DisplayRegistry::TerminateDisplay(EGLDisplay display) {
+  SB_DCHECK(Valid(display));
+  Connection* connection = reinterpret_cast<Connection*>(display);
+
+  if (connection->display) {
+    delete connection->display;
+    connection->display = NULL;
+  }
+}
+
+bool DisplayRegistry::Valid(EGLDisplay display) {
+  Connection* connection = reinterpret_cast<Connection*>(display);
+  for (int i = 0; i < num_connections_; ++i) {
+    if (connection == &connections_[i]) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// This function will either return the Display object associated with the
+// given EGLDisplay, or else set the appropriate EGL error and then return
+// NULL.
+egl::Display* GetDisplayOrSetError(EGLDisplay egl_display) {
+  if (!egl::DisplayRegistry::Valid(egl_display)) {
+    egl::SetError(EGL_BAD_DISPLAY);
+    return NULL;
+  }
+  egl::Display* display = egl::DisplayRegistry::ToDisplay(egl_display);
+  if (!display) {
+    egl::SetError(EGL_NOT_INITIALIZED);
+    return NULL;
+  }
+
+  return display;
+}
+
+}  // namespace egl
+}  // namespace glimp
diff --git a/src/glimp/egl/display_registry.h b/src/glimp/egl/display_registry.h
new file mode 100644
index 0000000..ff07ef1
--- /dev/null
+++ b/src/glimp/egl/display_registry.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GLIMP_EGL_DISPLAY_REGISTRY_H_
+#define GLIMP_EGL_DISPLAY_REGISTRY_H_
+
+#include "glimp/egl/display.h"
+#include "starboard/log.h"
+
+namespace glimp {
+namespace egl {
+
+// Maintains a registry of EGLDisplays as well as mappings from
+// EGLNativeDisplayType to those EGLDisplays.  Internally, an EGLDisplay is
+// represented as a DisplayRegistry::Connection object that has a possibly null
+// pointer to a Display object.  The Display object will be initialized when
+// DisplayRegistry::InitializeDisplay() is called, after which it may be
+// retrieved and have methods called on it.
+class DisplayRegistry {
+ public:
+  // Looks up the mapping from EGLNativeDisplayType to EGLDisplay, or creates
+  // one if it doesn't already exist, and returns the EGLDisplay associated
+  // with it.  No failures will be reported by this method, it is
+  // InitializeDisplay() that will ultimately have the platform check
+  // whether the native display is valid or not.
+  static EGLDisplay GetDisplay(EGLNativeDisplayType native_display);
+
+  // Construct the Display object, if it has not yet been initialized already.
+  // This method can fail if the native display associated with the EGLDisplay
+  // is rejected by the platform.  Returns true if the display is already
+  // initialized or successfully initialized, otherwise returns false.
+  static bool InitializeDisplay(EGLDisplay display);
+
+  // Terminates the display if it is initialized, otherwise it does nothing.
+  static void TerminateDisplay(EGLDisplay display);
+
+  // Returns true if the given |display| is valid, which will be true if it
+  // was at some point previously returned by GetDisplay() and false otherwise.
+  static bool Valid(EGLDisplay display);
+
+  // Returns the Display object associated with the given [valid] EGLDisplay
+  // object, or NULL if it is not initialized.
+  static Display* ToDisplay(EGLDisplay display) {
+    SB_DCHECK(Valid(display));
+    return reinterpret_cast<Connection*>(display)->display;
+  }
+
+ private:
+  // A Connection is the internal type of a EGLDisplay handle.  It
+  // maintains a mapping from EGLNativeDisplayType to a (possibly null if it
+  // hasn't yet been eglInitialize()d) Display object.
+  struct Connection {
+    EGLNativeDisplayType native_display;
+    Display* display;
+  };
+
+  static const int kMaxDisplays = 10;
+
+  // The number of display connections currently active.
+  static int num_connections_;
+
+  // The mapping from native type to possibly initialized connection.
+  static Connection connections_[kMaxDisplays];
+};
+
+// This function will either return the Display object associated with the
+// given EGLDisplay, or else set the appropriate EGL error and then return
+// NULL.
+egl::Display* GetDisplayOrSetError(EGLDisplay egl_display);
+
+}  // namespace egl
+}  // namespace glimp
+
+#endif  // GLIMP_EGL_DISPLAY_REGISTRY_H_
diff --git a/src/glimp/egl/error.cc b/src/glimp/egl/error.cc
new file mode 100644
index 0000000..b18a34d
--- /dev/null
+++ b/src/glimp/egl/error.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "glimp/egl/error.h"
+
+#include "starboard/once.h"
+#include "starboard/thread.h"
+
+namespace glimp {
+namespace egl {
+
+namespace {
+SbOnceControl s_error_once_control = SB_ONCE_INITIALIZER;
+SbThreadLocalKey s_error_tls_key = kSbThreadLocalKeyInvalid;
+
+void InitializeError() {
+  s_error_tls_key = SbThreadCreateLocalKey(NULL);
+}
+}  // namespace
+
+EGLint GetError() {
+  SbOnce(&s_error_once_control, &InitializeError);
+  void* local_value = SbThreadGetLocalValue(s_error_tls_key);
+  if (local_value == NULL) {
+    // The EGL error has never been set.  In this case, return EGL_SUCCESS as
+    // that is the initial value for eglGetError().
+    // Note that NULL or 0 are not valid EGL error codes.
+    // https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetError.xhtml
+    return EGL_SUCCESS;
+  }
+  return static_cast<EGLint>(reinterpret_cast<uintptr_t>(local_value));
+}
+
+void SetError(EGLint error) {
+  SbOnce(&s_error_once_control, &InitializeError);
+  SbThreadSetLocalValue(s_error_tls_key, reinterpret_cast<void*>(error));
+}
+
+}  // namespace egl
+}  // namespace glimp
diff --git a/src/glimp/egl/error.h b/src/glimp/egl/error.h
new file mode 100644
index 0000000..51df122
--- /dev/null
+++ b/src/glimp/egl/error.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GLIMP_EGL_ERROR_H_
+#define GLIMP_EGL_ERROR_H_
+
+#include <EGL/egl.h>
+
+namespace glimp {
+namespace egl {
+
+// Implements support for getting and setting the thread local EGL error
+// value.
+//   https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetError.xhtml
+
+// Returns the current thread local error code.
+EGLint GetError();
+
+// Sets the current thread local error code.
+void SetError(EGLint error);
+
+}  // namespace egl
+}  // namespace glimp
+
+#endif  // GLIMP_EGL_ERROR_H_
diff --git a/src/glimp/egl/get_proc_address_impl.h b/src/glimp/egl/get_proc_address_impl.h
new file mode 100644
index 0000000..5ff61c6
--- /dev/null
+++ b/src/glimp/egl/get_proc_address_impl.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GLIMP_EGL_GET_PROC_ADDRESS_IMPL_H_
+#define GLIMP_EGL_GET_PROC_ADDRESS_IMPL_H_
+
+namespace glimp {
+namespace egl {
+
+typedef void (*MustCastToProperFunctionPointerType)(void);
+
+// Calls to eglGetProcAddress() that are unhandled by platform-independent
+// glimp code are forwarded to this platform-specific call, so that different
+// platforms can implement custom extensions.  This function should return
+// a function pointer to the requested function upon success, and return NULL
+// if the requested function is not available.
+MustCastToProperFunctionPointerType GetProcAddressImpl(const char* procname);
+
+}  // namespace egl
+}  // namespace glimp
+
+#endif  // GLIMP_EGL_GET_PROC_ADDRESS_IMPL_H_
diff --git a/src/glimp/egl/scoped_egl_lock.cc b/src/glimp/egl/scoped_egl_lock.cc
new file mode 100644
index 0000000..4d8f546
--- /dev/null
+++ b/src/glimp/egl/scoped_egl_lock.cc
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "glimp/egl/scoped_egl_lock.h"
+
+namespace glimp {
+namespace egl {
+
+SbMutex ScopedEGLLock::mutex_ = SB_MUTEX_INITIALIZER;
+
+}  // namespace egl
+}  // namespace glimp
diff --git a/src/glimp/egl/scoped_egl_lock.h b/src/glimp/egl/scoped_egl_lock.h
new file mode 100644
index 0000000..10f6628
--- /dev/null
+++ b/src/glimp/egl/scoped_egl_lock.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GLIMP_EGL_SCOPED_EGL_LOCK_H_
+#define GLIMP_EGL_SCOPED_EGL_LOCK_H_
+
+#include "starboard/mutex.h"
+
+namespace glimp {
+namespace egl {
+
+// A helper class to enable easy locking of the glimp EGL global mutex.
+class ScopedEGLLock {
+ public:
+  ScopedEGLLock() { SbMutexAcquire(&mutex_); }
+  ~ScopedEGLLock() { SbMutexRelease(&mutex_); }
+
+ private:
+  static SbMutex mutex_;
+};
+
+}  // namespace egl
+}  // namespace glimp
+
+#endif  // GLIMP_EGL_SCOPED_EGL_LOCK_H_
diff --git a/src/glimp/egl/surface.cc b/src/glimp/egl/surface.cc
new file mode 100644
index 0000000..3dc7a64
--- /dev/null
+++ b/src/glimp/egl/surface.cc
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "glimp/egl/surface.h"
+
+#include "glimp/egl/error.h"
+#include "glimp/gles/context.h"
+#include "starboard/log.h"
+
+namespace glimp {
+namespace egl {
+
+Surface::Surface(nb::scoped_ptr<SurfaceImpl> surface_impl)
+    : surface_impl_(surface_impl.Pass()), is_bound_to_texture_(false) {}
+
+int Surface::GetWidth() const {
+  return surface_impl_->GetWidth();
+}
+
+int Surface::GetHeight() const {
+  return surface_impl_->GetHeight();
+}
+
+EGLint Surface::GetTextureFormat() const {
+  return EGL_TEXTURE_RGBA;
+}
+
+EGLint Surface::GetTextureTarget() const {
+  return EGL_TEXTURE_2D;
+}
+
+EGLBoolean Surface::QuerySurface(EGLint attribute, EGLint* value) {
+  switch (attribute) {
+    case EGL_HEIGHT: {
+      *value = GetHeight();
+      return true;
+    }
+
+    case EGL_WIDTH: {
+      *value = GetWidth();
+      return true;
+    }
+
+    case EGL_TEXTURE_FORMAT: {
+      // glimp only supports EGL_TEXTURE_RGBA.
+      *value = GetTextureFormat();
+      return true;
+    }
+
+    case EGL_TEXTURE_TARGET: {
+      // glimp only supports EGL_TEXTURE_2D.
+      *value = GetTextureTarget();
+    }
+
+    case EGL_CONFIG_ID:
+    case EGL_HORIZONTAL_RESOLUTION:
+    case EGL_LARGEST_PBUFFER:
+    case EGL_MIPMAP_LEVEL:
+    case EGL_MIPMAP_TEXTURE:
+    case EGL_MULTISAMPLE_RESOLVE:
+    case EGL_PIXEL_ASPECT_RATIO:
+    case EGL_RENDER_BUFFER:
+    case EGL_SWAP_BEHAVIOR:
+    case EGL_VERTICAL_RESOLUTION: {
+      SB_NOTIMPLEMENTED();
+    }  // Fall through to default on purpose.
+
+    default:
+      SetError(EGL_BAD_ATTRIBUTE);
+      return false;
+  }
+
+  return true;
+}
+
+EGLBoolean Surface::BindTexImage(EGLint buffer) {
+  if (buffer != EGL_BACK_BUFFER) {
+    SetError(EGL_BAD_MATCH);
+    return false;
+  }
+
+  if (is_bound_to_texture_) {
+    SetError(EGL_BAD_ACCESS);
+    return false;
+  }
+
+  if (GetTextureTarget() == EGL_NO_TEXTURE) {
+    SetError(EGL_BAD_MATCH);
+    return false;
+  }
+  if (GetTextureTarget() != EGL_TEXTURE_2D) {
+    SB_NOTIMPLEMENTED() << "glimp does not support binding anything other than "
+                           "EGL_TEXTURE_2D.";
+    SetError(EGL_BAD_MATCH);
+    return false;
+  }
+
+  // When this method is called, we should bind to the currently bound active
+  // texture in the current GL context.
+  //   https://www.khronos.org/registry/egl/sdk/docs/man/html/eglBindTexImage.xhtml
+  gles::Context* current_context = gles::Context::GetTLSCurrentContext();
+  if (current_context == NULL) {
+    SB_DLOG(WARNING)
+        << "No GL ES context current during call to eglBindTexImage().";
+    // This error is non-specified behavior, but seems reasonable.
+    SetError(EGL_BAD_CONTEXT);
+    return false;
+  }
+
+  is_bound_to_texture_ = current_context->BindTextureToEGLSurface(this);
+  return is_bound_to_texture_;
+}
+
+EGLBoolean Surface::ReleaseTexImage(EGLint buffer) {
+  if (buffer != EGL_BACK_BUFFER) {
+    SetError(EGL_BAD_MATCH);
+    return false;
+  }
+
+  if (!is_bound_to_texture_) {
+    // Nothing to do if the surface is not already bound.
+    return true;
+  }
+
+  gles::Context* current_context = gles::Context::GetTLSCurrentContext();
+  if (current_context == NULL) {
+    SB_DLOG(WARNING)
+        << "No GL ES context current during call to eglReleaseTexImage().";
+    // This error is non-specified behavior, but seems reasonable.
+    SetError(EGL_BAD_CONTEXT);
+    return false;
+  }
+
+  if (current_context->ReleaseTextureFromEGLSurface(this)) {
+    is_bound_to_texture_ = false;
+    return true;
+  } else {
+    return false;
+  }
+}
+
+namespace {
+bool AttributeKeyAndValueAreValid(int key, int value) {
+  switch (key) {
+    // First deal with the trivial keys where all values are valid.
+    case EGL_WIDTH:
+    case EGL_HEIGHT: {
+      return true;
+    }
+
+    case EGL_TEXTURE_TARGET: {
+      return value == EGL_TEXTURE_2D;
+    }
+
+    case EGL_TEXTURE_FORMAT: {
+      return value == EGL_TEXTURE_RGBA;
+    }
+  }
+
+  // If the switch statement didn't catch the key, this is an unknown
+  // key.
+  // TODO: glimp doesn't support all values yet, and will return false for keys
+  //       that it doesn't support.
+  return false;
+}
+}  // namespace
+
+bool ValidateSurfaceAttribList(const AttribMap& attribs) {
+  for (AttribMap::const_iterator iter = attribs.begin(); iter != attribs.end();
+       ++iter) {
+    if (!AttributeKeyAndValueAreValid(iter->first, iter->second)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+EGLSurface ToEGLSurface(Surface* surface) {
+  return reinterpret_cast<EGLSurface>(surface);
+}
+
+Surface* FromEGLSurface(EGLSurface surface) {
+  return reinterpret_cast<Surface*>(surface);
+}
+
+}  // namespace egl
+}  // namespace glimp
diff --git a/src/glimp/egl/surface.h b/src/glimp/egl/surface.h
new file mode 100644
index 0000000..dd26f2d
--- /dev/null
+++ b/src/glimp/egl/surface.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GLIMP_EGL_SURFACE_H_
+#define GLIMP_EGL_SURFACE_H_
+
+#include <EGL/egl.h>
+
+#include <map>
+
+#include "glimp/egl/attrib_map.h"
+#include "glimp/egl/surface_impl.h"
+#include "nb/scoped_ptr.h"
+
+namespace glimp {
+namespace egl {
+
+class Surface {
+ public:
+  explicit Surface(nb::scoped_ptr<SurfaceImpl> surface_impl);
+
+  int GetWidth() const;
+  int GetHeight() const;
+
+  EGLint GetTextureFormat() const;
+  EGLint GetTextureTarget() const;
+
+  EGLBoolean QuerySurface(EGLint attribute, EGLint* value);
+  EGLBoolean BindTexImage(EGLint buffer);
+  EGLBoolean ReleaseTexImage(EGLint buffer);
+
+  bool is_bound_to_texture() const { return is_bound_to_texture_; }
+
+  SurfaceImpl* impl() const { return surface_impl_.get(); }
+
+ private:
+  nb::scoped_ptr<SurfaceImpl> surface_impl_;
+
+  // True if this surface is currently bound to a GL ES texture via
+  // eglBindTexImage().
+  bool is_bound_to_texture_;
+};
+
+bool ValidateSurfaceAttribList(const AttribMap& attribs);
+
+EGLSurface ToEGLSurface(Surface* surface);
+Surface* FromEGLSurface(EGLSurface surface);
+
+}  // namespace egl
+}  // namespace glimp
+
+#endif  // GLIMP_EGL_SURFACE_H_
diff --git a/src/glimp/egl/surface_impl.h b/src/glimp/egl/surface_impl.h
new file mode 100644
index 0000000..ead72c4
--- /dev/null
+++ b/src/glimp/egl/surface_impl.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GLIMP_EGL_SURFACE_IMPL_H_
+#define GLIMP_EGL_SURFACE_IMPL_H_
+
+#include <EGL/egl.h>
+
+namespace glimp {
+namespace egl {
+
+class SurfaceImpl {
+ public:
+  virtual ~SurfaceImpl() {}
+
+  // Returns a description of the underlying surface.  This method will be
+  // referenced when functions like eglQuerySurface() are called.
+  //   https://www.khronos.org/registry/egl/sdk/docs/man/html/eglQuerySurface.xhtml
+  virtual int GetWidth() const = 0;
+  virtual int GetHeight() const = 0;
+
+ private:
+};
+
+}  // namespace egl
+}  // namespace glimp
+
+#endif  // GLIMP_EGL_SURFACE_IMPL_H_