From d0e18bdb7924c71cdca8dd983711171d87ef28be Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 17 Jan 2022 23:12:32 -0500 Subject: glplanet, an OpenGL-based planetary renderer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glplanet draws Earth like it currently appears from space, putting nighttime areas in shadow and daytime areas in light. It’s modeled after Xplanet (http://xplanet.sourceforge.net/), but whereas Xplanet is entirely a CPU-resident program, glplanet draws using OpenGL. It’s thus much less resource-intensive, particularly when using high-resolution textures. --- src/egl.cc | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/egl.cc (limited to 'src/egl.cc') 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 +#include +#include + +#include +#include +#include +#include +#include + +#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 +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 MarshalAttributeList( + const absl::btree_map& attribs) { + std::vector 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 Display::GetConfigurations( + const std::vector>& filter_attributes, + absl::optional limit) const { + // Create the actual attribute list that we're going to pass to + // eglChooseConfig. + std::vector 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 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 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 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 -- cgit v1.2.3