// 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