| /* |
| * Copyright 2015 The Cobalt Authors. 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 <utility> |
| #include <vector> |
| |
| #include "glimp/egl/config.h" |
| #include "glimp/egl/error.h" |
| #include "starboard/common/log.h" |
| #include "starboard/event.h" |
| |
| namespace glimp { |
| namespace egl { |
| |
| const int64_t kSubmitDoneDelay = 1000000 / 60; // 1/60 seconds, in microseconds |
| |
| // Don't repeat the submitDone callback during suspension |
| // until specified by eglTerminate. |
| bool Display::repeat_submit_done_during_suspend = false; |
| |
| namespace { |
| void ScheduleSubmitDoneCallback(void* context) { |
| if (Display::repeat_submit_done_during_suspend) { |
| SB_DCHECK(true) << "Suspend mode is disabled. ScheduleSubmitDoneCallback " |
| "shouldn't be triggered."; |
| DisplayImpl::CallSubmitDone(); |
| Display::RepeatSubmitDoneDuringSuspend(); |
| } |
| } |
| } // namespace |
| |
| void Display::RepeatSubmitDoneDuringSuspend() { |
| static SbEventId submit_done_repeating_callback_event = kSbEventIdInvalid; |
| if (Display::repeat_submit_done_during_suspend) { |
| submit_done_repeating_callback_event = |
| SbEventSchedule(&ScheduleSubmitDoneCallback, NULL, kSubmitDoneDelay); |
| } else { |
| if (submit_done_repeating_callback_event != kSbEventIdInvalid) { |
| SbEventCancel(submit_done_repeating_callback_event); |
| } |
| } |
| } |
| |
| Display::Display(std::unique_ptr<DisplayImpl> display_impl) |
| : impl_(std::move(display_impl)) {} |
| |
| 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; |
| } |
| |
| std::unique_ptr<SurfaceImpl> surface_impl = impl_->CreateWindowSurface( |
| reinterpret_cast<Config*>(config), win, attribs); |
| if (!surface_impl) { |
| return EGL_NO_SURFACE; |
| } |
| |
| Surface* surface = new Surface(std::move(surface_impl)); |
| 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; |
| } |
| |
| std::unique_ptr<SurfaceImpl> surface_impl = |
| impl_->CreatePbufferSurface(reinterpret_cast<Config*>(config), attribs); |
| if (!surface_impl) { |
| return EGL_NO_SURFACE; |
| } |
| |
| Surface* surface = new Surface(std::move(surface_impl)); |
| 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); |
| } |
| |
| std::unique_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(std::move(context_impl), 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; |
| } |
| |
| bool Display::SwapInterval(EGLint interval) { |
| return impl_->SetSwapInterval(interval); |
| } |
| |
| } // namespace egl |
| } // namespace glimp |