summaryrefslogtreecommitdiff
path: root/src/egl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/egl.cc')
-rw-r--r--src/egl.cc198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/egl.cc b/src/egl.cc
new file mode 100644
index 0000000..c24815b
--- /dev/null
+++ b/src/egl.cc
@@ -0,0 +1,198 @@
+// Copyright 2021 Benjamin Barenblat
+//
+// 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
+//
+// https://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 "src/egl.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <X11/Xlib.h>
+
+#include <exception>
+#include <new>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+
+#include "src/undo_xlib_dot_h_namespace_pollution.h"
+//
+
+#include "src/util.h"
+#include "third_party/abseil/absl/container/btree_map.h"
+#include "third_party/abseil/absl/types/optional.h"
+
+namespace egl {
+
+namespace {
+
+// Converts an EGL error into a standard C++ exception.
+std::exception ExceptionFromErrorCode(EGLint error) noexcept {
+ DCHECK(error != EGL_SUCCESS);
+ switch (error) {
+ case EGL_NOT_INITIALIZED:
+ return std::logic_error("EGL: uninitialized");
+ case EGL_BAD_ACCESS:
+ return std::logic_error("EGL: bad access");
+ case EGL_BAD_ALLOC:
+ return std::bad_alloc();
+ case EGL_BAD_ATTRIBUTE:
+ return std::invalid_argument(
+ "EGL: unrecognized attribute or attribute value");
+ case EGL_BAD_CONTEXT:
+ return std::invalid_argument("EGL: invalid context");
+ case EGL_BAD_CONFIG:
+ return std::invalid_argument("EGL: invalid configuration");
+ case EGL_BAD_CURRENT_SURFACE:
+ return std::invalid_argument("EGL: current surface is no longer valid");
+ case EGL_BAD_DISPLAY:
+ return std::invalid_argument("EGL: invalid display");
+ case EGL_BAD_SURFACE:
+ return std::invalid_argument("EGL: invalid surface");
+ case EGL_BAD_MATCH:
+ return std::invalid_argument("EGL: inconsistent arguments");
+ case EGL_BAD_PARAMETER:
+ return std::invalid_argument("EGL: invalid argument");
+ case EGL_BAD_NATIVE_PIXMAP:
+ return std::invalid_argument("EGL: invalid native pixmap");
+ case EGL_BAD_NATIVE_WINDOW:
+ return std::invalid_argument("EGL: invalid native window");
+ case EGL_CONTEXT_LOST:
+ return std::runtime_error(
+ "EGL: context lost due to power management event");
+ default:
+ return std::runtime_error("EGL: error");
+ }
+}
+
+// Checks the specified result against `error`. If it is indeed `error`, gets
+// the associated EGL error and throws it as an exception.
+template <typename T>
+T SentinelCheckedCall(T error, T result) {
+ if (result == error) {
+ throw ExceptionFromErrorCode(eglGetError());
+ }
+ return result;
+}
+
+// A variant of SentinelCheckedCall that checks for `false` returns.
+void CheckedCall(bool result) { SentinelCheckedCall(false, result); }
+
+// Converts an egl::AttributeList into the format expected by EGL's C APIs.
+std::vector<EGLint> MarshalAttributeList(
+ const absl::btree_map<EGLint, EGLint>& attribs) {
+ std::vector<EGLint> r;
+ r.reserve(2 * attribs.size() + 1);
+ for (auto& [key, val] : attribs) {
+ r.push_back(key);
+ r.push_back(val);
+ }
+ r.push_back(EGL_NONE);
+ return r;
+}
+
+} // namespace
+
+int Configuration::Get(Attribute attribute) const {
+ EGLint r;
+ CheckedCall(eglGetConfigAttrib(display_, config_, attribute, &r));
+ return r;
+}
+
+void Surface::SwapBuffers() { CheckedCall(eglSwapBuffers(display_, surface_)); }
+
+Display::Display(::Display* xlib_display)
+ : display_(SentinelCheckedCall(
+ EGL_NO_DISPLAY,
+ eglGetPlatformDisplay(EGL_PLATFORM_X11_KHR, xlib_display,
+ /*attrib_list=*/nullptr))) {
+ EGLint major, minor;
+ CheckedCall(eglInitialize(display_, &major, &minor));
+}
+
+Display::~Display() noexcept {
+ // This should always succeed, since display_ is guaranteed valid.
+ DCHECK(eglTerminate(display_));
+}
+
+std::vector<Configuration> Display::GetConfigurations(
+ const std::vector<std::pair<Attribute, int>>& filter_attributes,
+ absl::optional<int> limit) const {
+ // Create the actual attribute list that we're going to pass to
+ // eglChooseConfig.
+ std::vector<EGLint> marshaled_attributes;
+ marshaled_attributes.reserve(2 * filter_attributes.size() + 1);
+ for (auto& [key, value] : filter_attributes) {
+ marshaled_attributes.push_back(key);
+ marshaled_attributes.push_back(value);
+ }
+ marshaled_attributes.push_back(EGL_NONE);
+
+ // Determine how many configs we want.
+ EGLint num_configs;
+ if (limit.has_value()) {
+ num_configs = *limit;
+ } else {
+ CheckedCall(eglChooseConfig(display_, marshaled_attributes.data(),
+ /*configs=*/nullptr,
+ /*config_size=*/0, &num_configs));
+ }
+
+ // Actually get the configs.
+ std::vector<EGLConfig> egl_configs(num_configs);
+ CheckedCall(eglChooseConfig(display_, marshaled_attributes.data(),
+ egl_configs.data(), egl_configs.size(),
+ &num_configs));
+
+ // Wrap the configs in Configuration objects.
+ std::vector<Configuration> r;
+ r.reserve(num_configs);
+ for (auto egl_config : egl_configs) {
+ r.push_back(Configuration(display_, egl_config));
+ }
+
+ return r;
+}
+
+Surface Display::CreateWindowSurface(const Configuration& config,
+ ::Window window) {
+ return Surface(
+ display_,
+ SentinelCheckedCall(EGL_NO_SURFACE, eglCreatePlatformWindowSurface(
+ display_, config.config_, &window,
+ /*attrib_list=*/nullptr)));
+}
+
+Context Display::CreateContext(const Configuration& config, Api api, int major,
+ int minor) {
+ absl::btree_map<EGLint, EGLint> attributes = {
+ {EGL_CONTEXT_MAJOR_VERSION, major}, {EGL_CONTEXT_MINOR_VERSION, minor}};
+#ifndef NDEBUG
+ attributes[EGL_CONTEXT_OPENGL_DEBUG] = EGL_TRUE;
+#endif
+
+ CheckedCall(eglBindAPI(FromEnum(api)));
+ return Context(
+ display_, SentinelCheckedCall(
+ EGL_NO_CONTEXT,
+ eglCreateContext(display_, config.config_,
+ /*share_context=*/EGL_NO_CONTEXT,
+ MarshalAttributeList(attributes).data())));
+}
+
+void BindContext(Surface& surface, Context& context) {
+ DCHECK(surface.display_ == context.display_);
+ CheckedCall(eglMakeCurrent(surface.display_, /*draw=*/surface.surface_,
+ /*read=*/surface.surface_, context.context_));
+}
+
+} // namespace egl