| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gpu/gles2_conform_support/egl/display.h" |
| |
| #include "base/stl_util.h" |
| #include "build/build_config.h" |
| #include "gpu/gles2_conform_support/egl/config.h" |
| #include "gpu/gles2_conform_support/egl/context.h" |
| #include "gpu/gles2_conform_support/egl/surface.h" |
| #include "gpu/gles2_conform_support/egl/thread_state.h" |
| #include "ui/gl/init/gl_factory.h" |
| |
| namespace egl { |
| |
| Display::Display() |
| : is_initialized_(false), |
| next_create_window_surface_creates_pbuffer_(false), |
| window_surface_pbuffer_width_(0), |
| window_surface_pbuffer_height_(0) { |
| } |
| |
| Display::~Display() { |
| surfaces_.clear(); |
| contexts_.clear(); |
| } |
| void Display::SetNextCreateWindowSurfaceCreatesPBuffer(EGLint width, |
| EGLint height) { |
| next_create_window_surface_creates_pbuffer_ = true; |
| window_surface_pbuffer_width_ = width; |
| window_surface_pbuffer_height_ = height; |
| } |
| |
| EGLBoolean Display::Initialize(ThreadState* ts, EGLint* major, EGLint* minor) { |
| base::AutoLock auto_lock(lock_); |
| is_initialized_ = true; |
| |
| if (major) |
| *major = 1; |
| if (minor) |
| *minor = 4; |
| return ts->ReturnSuccess(EGL_TRUE); |
| } |
| |
| EGLBoolean Display::Terminate(ThreadState* ts) { |
| base::AutoLock auto_lock(lock_); |
| is_initialized_ = false; |
| surfaces_.clear(); |
| for (const auto& context : contexts_) |
| context->MarkDestroyed(); |
| contexts_.clear(); |
| return ts->ReturnSuccess(EGL_TRUE); |
| } |
| |
| const char* Display::QueryString(ThreadState* ts, EGLint name) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError<const char*>(EGL_NOT_INITIALIZED, nullptr); |
| switch (name) { |
| case EGL_CLIENT_APIS: |
| return ts->ReturnSuccess("OpenGL_ES"); |
| case EGL_EXTENSIONS: |
| return ts->ReturnSuccess(""); |
| case EGL_VENDOR: |
| return ts->ReturnSuccess("Google Inc."); |
| case EGL_VERSION: |
| return ts->ReturnSuccess("1.4"); |
| default: |
| return ts->ReturnError<const char*>(EGL_BAD_PARAMETER, nullptr); |
| } |
| } |
| |
| EGLBoolean Display::ChooseConfig(ThreadState* ts, |
| const EGLint* attrib_list, |
| EGLConfig* configs, |
| EGLint config_size, |
| EGLint* num_config) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError(EGL_NOT_INITIALIZED, EGL_FALSE); |
| if (num_config == nullptr) |
| return ts->ReturnError(EGL_BAD_PARAMETER, EGL_FALSE); |
| if (!Config::ValidateAttributeList(attrib_list)) |
| return ts->ReturnError(EGL_BAD_ATTRIBUTE, EGL_FALSE); |
| InitializeConfigsIfNeeded(); |
| if (!configs) |
| config_size = 0; |
| *num_config = 0; |
| for (size_t i = 0; i < base::size(configs_); ++i) { |
| if (configs_[i]->Matches(attrib_list)) { |
| if (*num_config < config_size) { |
| configs[*num_config] = configs_[i].get(); |
| } |
| ++*num_config; |
| } |
| } |
| return ts->ReturnSuccess(EGL_TRUE); |
| } |
| |
| EGLBoolean Display::GetConfigs(ThreadState* ts, |
| EGLConfig* configs, |
| EGLint config_size, |
| EGLint* num_config) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError(EGL_NOT_INITIALIZED, EGL_FALSE); |
| if (num_config == nullptr) |
| return ts->ReturnError(EGL_BAD_PARAMETER, EGL_FALSE); |
| InitializeConfigsIfNeeded(); |
| if (!configs) |
| config_size = 0; |
| *num_config = base::size(configs_); |
| size_t count = |
| std::min(base::size(configs_), static_cast<size_t>(config_size)); |
| for (size_t i = 0; i < count; ++i) |
| configs[i] = configs_[i].get(); |
| return ts->ReturnSuccess(EGL_TRUE); |
| } |
| |
| bool Display::IsValidNativeWindow(EGLNativeWindowType win) { |
| #if defined OS_WIN |
| return ::IsWindow(win) != FALSE; |
| #else |
| // TODO(alokp): Validate window handle. |
| return true; |
| #endif // OS_WIN |
| } |
| |
| EGLBoolean Display::GetConfigAttrib(ThreadState* ts, |
| EGLConfig cfg, |
| EGLint attribute, |
| EGLint* value) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError(EGL_NOT_INITIALIZED, EGL_FALSE); |
| const egl::Config* config = GetConfig(cfg); |
| if (!config) |
| return ts->ReturnError(EGL_BAD_CONFIG, EGL_FALSE); |
| if (!config->GetAttrib(attribute, value)) |
| return ts->ReturnError(EGL_BAD_ATTRIBUTE, EGL_FALSE); |
| return ts->ReturnSuccess(EGL_TRUE); |
| } |
| |
| EGLSurface Display::CreatePbufferSurface(ThreadState* ts, |
| EGLConfig cfg, |
| const EGLint* attrib_list) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError(EGL_NOT_INITIALIZED, EGL_NO_SURFACE); |
| const egl::Config* config = GetConfig(cfg); |
| if (!config) |
| return ts->ReturnError(EGL_BAD_CONFIG, EGL_NO_SURFACE); |
| EGLint value = EGL_NONE; |
| config->GetAttrib(EGL_SURFACE_TYPE, &value); |
| if ((value & EGL_PBUFFER_BIT) == 0) |
| return ts->ReturnError(EGL_BAD_MATCH, EGL_NO_SURFACE); |
| if (!egl::Surface::ValidatePbufferAttributeList(attrib_list)) |
| return ts->ReturnError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); |
| |
| int width = 1; |
| int height = 1; |
| if (attrib_list) { |
| for (const int32_t* attr = attrib_list; attr[0] != EGL_NONE; attr += 2) { |
| switch (attr[0]) { |
| case EGL_WIDTH: |
| width = attr[1]; |
| break; |
| case EGL_HEIGHT: |
| height = attr[1]; |
| break; |
| } |
| } |
| } |
| return DoCreatePbufferSurface(ts, config, width, height); |
| } |
| |
| EGLSurface Display::DoCreatePbufferSurface(ThreadState* ts, |
| const Config* config, |
| EGLint width, |
| EGLint height) { |
| lock_.AssertAcquired(); |
| scoped_refptr<gl::GLSurface> gl_surface; |
| gl_surface = gl::init::CreateOffscreenGLSurface(gfx::Size(width, height)); |
| if (!gl_surface) |
| return ts->ReturnError(EGL_BAD_ALLOC, nullptr); |
| surfaces_.emplace_back(new Surface(gl_surface.get(), config)); |
| return ts->ReturnSuccess<EGLSurface>(surfaces_.back().get()); |
| } |
| |
| EGLSurface Display::CreateWindowSurface(ThreadState* ts, |
| EGLConfig cfg, |
| EGLNativeWindowType win, |
| const EGLint* attrib_list) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError(EGL_NOT_INITIALIZED, EGL_NO_SURFACE); |
| const egl::Config* config = GetConfig(cfg); |
| if (!config) |
| return ts->ReturnError(EGL_BAD_CONFIG, EGL_NO_SURFACE); |
| EGLint value = EGL_NONE; |
| config->GetAttrib(EGL_SURFACE_TYPE, &value); |
| if ((value & EGL_WINDOW_BIT) == 0) |
| return ts->ReturnError(EGL_BAD_CONFIG, EGL_NO_SURFACE); |
| if (!next_create_window_surface_creates_pbuffer_ && !IsValidNativeWindow(win)) |
| return ts->ReturnError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); |
| if (!Surface::ValidateWindowAttributeList(attrib_list)) |
| return ts->ReturnError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); |
| if (next_create_window_surface_creates_pbuffer_) { |
| EGLSurface result = DoCreatePbufferSurface(ts, config, |
| window_surface_pbuffer_width_, |
| window_surface_pbuffer_height_); |
| next_create_window_surface_creates_pbuffer_ = false; |
| window_surface_pbuffer_width_ = 0; |
| window_surface_pbuffer_height_ = 0; |
| return result; |
| } |
| scoped_refptr<gl::GLSurface> gl_surface; |
| #if defined(OS_MACOSX) |
| gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget; |
| #else |
| gfx::AcceleratedWidget widget = static_cast<gfx::AcceleratedWidget>(win); |
| #endif |
| gl_surface = gl::init::CreateViewGLSurface(widget); |
| if (!gl_surface) |
| return ts->ReturnError(EGL_BAD_ALLOC, EGL_NO_SURFACE); |
| surfaces_.emplace_back(new Surface(gl_surface.get(), config)); |
| return ts->ReturnSuccess(surfaces_.back().get()); |
| } |
| |
| EGLBoolean Display::DestroySurface(ThreadState* ts, EGLSurface sfe) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError(EGL_NOT_INITIALIZED, EGL_FALSE); |
| auto it = std::find(surfaces_.begin(), surfaces_.end(), sfe); |
| if (it == surfaces_.end()) |
| return ts->ReturnError(EGL_BAD_SURFACE, EGL_FALSE); |
| surfaces_.erase(it); |
| return ts->ReturnSuccess(EGL_TRUE); |
| } |
| |
| EGLBoolean Display::ReleaseCurrent(ThreadState* ts) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnSuccess(EGL_TRUE); |
| ThreadState::AutoCurrentContextRestore accr(ts); |
| if (ts->current_context()) { |
| Context::MakeCurrent(ts->current_context(), ts->current_surface(), nullptr, |
| nullptr); |
| accr.SetCurrent(nullptr, nullptr); |
| } |
| return ts->ReturnSuccess(EGL_TRUE); |
| } |
| |
| EGLBoolean Display::MakeCurrent(ThreadState* ts, |
| EGLSurface draw, |
| EGLSurface read, |
| EGLSurface ctx) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError(EGL_NOT_INITIALIZED, EGL_FALSE); |
| ThreadState::AutoCurrentContextRestore accr(ts); |
| // Client might have called use because it changed some other gl binding |
| // global state. For example, the client might have called eglMakeCurrent on |
| // the same EGL as what command buffer uses. The client probably knows that |
| // this invalidates the internal state of command buffer, too. So reset the |
| // current context with accr in any case, regardless whether context or |
| // surface pointer changes. |
| Surface* new_surface = GetSurface(draw); |
| if (!new_surface) |
| return ts->ReturnError(EGL_BAD_SURFACE, EGL_FALSE); |
| new_surface = GetSurface(read); |
| if (!new_surface) |
| return ts->ReturnError(EGL_BAD_SURFACE, EGL_FALSE); |
| egl::Context* new_context = GetContext(ctx); |
| if (!new_context) |
| return ts->ReturnError(EGL_BAD_CONTEXT, EGL_FALSE); |
| if (draw != read) |
| return ts->ReturnError(EGL_BAD_MATCH, EGL_FALSE); |
| |
| Surface* current_surface = ts->current_surface(); |
| Context* current_context = ts->current_context(); |
| |
| if (current_context != new_context && |
| new_context->is_current_in_some_thread()) |
| return ts->ReturnError(EGL_BAD_ACCESS, EGL_FALSE); |
| |
| if (current_surface != new_surface && |
| new_surface->is_current_in_some_thread()) |
| return ts->ReturnError(EGL_BAD_ACCESS, EGL_FALSE); |
| |
| if (!Context::MakeCurrent(current_context, |
| current_context ? current_surface : nullptr, |
| new_context, new_context ? new_surface : nullptr)) |
| return ts->ReturnError(EGL_BAD_MATCH, EGL_FALSE); |
| |
| accr.SetCurrent(new_surface, new_context); |
| return ts->ReturnSuccess(EGL_TRUE); |
| } |
| |
| EGLBoolean Display::SwapBuffers(ThreadState* ts, EGLSurface sfe) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError(EGL_NOT_INITIALIZED, EGL_FALSE); |
| egl::Surface* surface = GetSurface(sfe); |
| if (!surface) |
| return ts->ReturnError(EGL_BAD_SURFACE, EGL_FALSE); |
| if (ts->current_surface() != surface) |
| return ts->ReturnError(EGL_BAD_SURFACE, EGL_FALSE); |
| if (!ts->current_context()->SwapBuffers(surface)) |
| ts->ReturnError(EGL_CONTEXT_LOST, EGL_FALSE); |
| return ts->ReturnSuccess(EGL_TRUE); |
| } |
| |
| EGLContext Display::CreateContext(ThreadState* ts, |
| EGLConfig cfg, |
| EGLContext share_ctx, |
| const EGLint* attrib_list) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError(EGL_NOT_INITIALIZED, EGL_NO_CONTEXT); |
| if (share_ctx != EGL_NO_CONTEXT) { |
| egl::Context* share_context = GetContext(share_ctx); |
| if (!share_context) |
| return ts->ReturnError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT); |
| // TODO(alokp): Add support for shared contexts. |
| return ts->ReturnError(EGL_BAD_MATCH, EGL_NO_CONTEXT); |
| } |
| |
| if (!egl::Context::ValidateAttributeList(attrib_list)) |
| return ts->ReturnError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT); |
| const egl::Config* config = GetConfig(cfg); |
| if (!config) |
| return ts->ReturnError(EGL_BAD_CONFIG, EGL_NO_CONTEXT); |
| scoped_refptr<Context> context(new Context(this, config)); |
| if (!context) |
| return ts->ReturnError(EGL_BAD_ALLOC, EGL_NO_CONTEXT); |
| contexts_.emplace_back(context.get()); |
| return ts->ReturnSuccess<EGLContext>(context.get()); |
| } |
| |
| EGLBoolean Display::DestroyContext(ThreadState* ts, EGLContext ctx) { |
| base::AutoLock auto_lock(lock_); |
| if (!is_initialized_) |
| return ts->ReturnError(EGL_NOT_INITIALIZED, EGL_FALSE); |
| auto it = std::find(contexts_.begin(), contexts_.end(), ctx); |
| if (it == contexts_.end()) |
| return ts->ReturnError(EGL_BAD_CONTEXT, EGL_FALSE); |
| (*it)->MarkDestroyed(); |
| contexts_.erase(it); |
| return ts->ReturnSuccess(EGL_TRUE); |
| } |
| |
| uint64_t Display::GenerateFenceSyncRelease() { |
| base::AutoLock auto_lock(lock_); |
| return next_fence_sync_release_++; |
| } |
| |
| void Display::InitializeConfigsIfNeeded() { |
| lock_.AssertAcquired(); |
| if (!configs_[0]) { |
| // The interface offers separate configs for window and pbuffer. |
| // This way we can record the client intention at context creation time. |
| // The GL implementation (gl::GLContext and gl::GLSurface) needs this |
| // distinction when creating a context. |
| configs_[0].reset(new Config(EGL_WINDOW_BIT)); |
| configs_[1].reset(new Config(EGL_PBUFFER_BIT)); |
| } |
| } |
| |
| const Config* Display::GetConfig(EGLConfig cfg) { |
| lock_.AssertAcquired(); |
| for (const auto& config : configs_) { |
| if (config.get() == cfg) |
| return config.get(); |
| } |
| return nullptr; |
| } |
| |
| Surface* Display::GetSurface(EGLSurface surface) { |
| lock_.AssertAcquired(); |
| auto it = std::find(surfaces_.begin(), surfaces_.end(), surface); |
| if (it == surfaces_.end()) |
| return nullptr; |
| return it->get(); |
| } |
| |
| Context* Display::GetContext(EGLContext context) { |
| lock_.AssertAcquired(); |
| auto it = std::find(contexts_.begin(), contexts_.end(), context); |
| if (it == contexts_.end()) |
| return nullptr; |
| return it->get(); |
| } |
| |
| } // namespace egl |