blob: 30159b67acac2bef6dd39d89b58f115e211c5abc [file] [log] [blame]
// Copyright 2015 Google Inc. 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 "cobalt/renderer/backend/egl/graphics_system.h"
#include "base/debug/leak_annotations.h"
#if defined(ENABLE_GLIMP_TRACING)
#include "base/debug/trace_event.h"
#endif
#include "base/optional.h"
#if defined(GLES3_SUPPORTED)
#include "cobalt/renderer/backend/egl/texture_data_pbo.h"
#else
#include "cobalt/renderer/backend/egl/texture_data_cpu.h"
#endif
#include "cobalt/renderer/backend/egl/display.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
#include "cobalt/renderer/backend/egl/texture.h"
#include "cobalt/renderer/backend/egl/utils.h"
#if defined(ENABLE_GLIMP_TRACING)
#include "glimp/tracing/tracing.h"
#endif
namespace cobalt {
namespace renderer {
namespace backend {
#if defined(ENABLE_GLIMP_TRACING)
// Hookup glimp tracing to Chromium's base trace_event tracing.
class GlimpToBaseTraceEventBridge : public glimp::TraceEventImpl {
public:
void BeginTrace(const char* name) override {
TRACE_EVENT_BEGIN0("glimp", name);
}
void EndTrace(const char* name) override { TRACE_EVENT_END0("glimp", name); }
};
GlimpToBaseTraceEventBridge s_glimp_to_base_trace_event_bridge;
#endif // #if defined(ENABLE_GLIMP_TRACING)
namespace {
struct ChooseConfigResult {
ChooseConfigResult() : window_surface(EGL_NO_SURFACE) {}
EGLConfig config;
// This will be EGL_NO_SURFACE if no window was provided to find a config to
// match with.
EGLSurface window_surface;
};
// Returns a config that matches the |attribute_list|. Optionally,
// |system_window| can also be provided in which case the config returned will
// also be guaranteed to match with it, and a EGLSurface will be returned
// for that window.
base::optional<ChooseConfigResult> ChooseConfig(
EGLDisplay display, EGLint* attribute_list,
system_window::SystemWindow* system_window) {
if (!system_window) {
// If there is no system window provided then this task is much easier,
// just return the first config that matches the provided attributes.
EGLint num_configs;
EGLConfig config;
eglChooseConfig(display, attribute_list, &config, 1, &num_configs);
DCHECK_GE(1, num_configs);
if (num_configs == 1) {
ChooseConfigResult results;
results.config = config;
return results;
} else {
LOG(ERROR) << "Could not find a EGLConfig compatible with the specified "
<< "attributes.";
return base::nullopt;
}
}
// Retrieve *all* configs that match the attribute list, and for each of them
// test to see if we can successfully call eglCreateWindowSurface() with them.
// Return the first config that succeeds the above test.
EGLint num_configs = 0;
eglChooseConfig(display, attribute_list, NULL, 0, &num_configs);
CHECK_EQ(EGL_SUCCESS, eglGetError());
CHECK_LT(0, num_configs);
scoped_array<EGLConfig> configs(new EGLConfig[num_configs]);
eglChooseConfig(display, attribute_list, configs.get(), num_configs,
&num_configs);
EGLNativeWindowType native_window =
(EGLNativeWindowType)(system_window->GetWindowHandle());
for (EGLint i = 0; i < num_configs; ++i) {
EGLSurface surface =
eglCreateWindowSurface(display, configs[i], native_window, NULL);
if (EGL_SUCCESS == eglGetError()) {
// We did it, we found a config that allows us to successfully create
// a surface. Return the config along with the created surface.
ChooseConfigResult result;
result.config = configs[i];
result.window_surface = surface;
return result;
}
}
// We could not find a config with the provided window, return a failure.
LOG(ERROR) << "Could not find a EGLConfig compatible with the specified "
<< "EGLNativeWindowType object and attributes.";
return base::nullopt;
}
} // namespace
GraphicsSystemEGL::GraphicsSystemEGL(
system_window::SystemWindow* system_window) {
#if defined(ENABLE_GLIMP_TRACING)
// If glimp tracing is enabled, hook up glimp trace calls to Chromium's
// base trace_event calls.
glimp::SetTraceEventImplementation(&s_glimp_to_base_trace_event_bridge);
#endif // #if defined(ENABLE_GLIMP_TRACING)
display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
CHECK_NE(EGL_NO_DISPLAY, display_);
CHECK_EQ(EGL_SUCCESS, eglGetError());
{
// Despite eglTerminate() being used in the destructor, the current
// mesa egl drivers still leak memory.
ANNOTATE_SCOPED_MEMORY_LEAK;
EGL_CALL(eglInitialize(display_, NULL, NULL));
}
// Setup our configuration to support RGBA and compatibility with PBuffer
// objects (for offscreen rendering).
EGLint attribute_list[] = {
EGL_SURFACE_TYPE, // this must be first
EGL_WINDOW_BIT | EGL_PBUFFER_BIT
#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
| EGL_SWAP_BEHAVIOR_PRESERVED_BIT
#endif // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
8,
EGL_RENDERABLE_TYPE,
#if defined(GLES3_SUPPORTED)
EGL_OPENGL_ES3_BIT,
#else
EGL_OPENGL_ES2_BIT,
#endif // #if defined(GLES3_SUPPORTED)
EGL_NONE
};
base::optional<ChooseConfigResult> choose_config_results =
ChooseConfig(display_, attribute_list, system_window);
#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
// Try to allow preservation of the frame contents between swap calls --
// this will allow rendering of only parts of the frame that have changed.
DCHECK_EQ(EGL_SURFACE_TYPE, attribute_list[0]);
EGLint& surface_type_value = attribute_list[1];
// Swap buffer preservation may not be supported. If we failed to find a
// config with the EGL_SWAP_BEHAVIOR_PRESERVED_BIT attribute, try again
// without it.
if (!choose_config_results) {
surface_type_value &= ~EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
choose_config_results =
ChooseConfig(display_, attribute_list, system_window);
}
#endif // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
DCHECK(choose_config_results);
config_ = choose_config_results->config;
system_window_ = system_window;
window_surface_ = choose_config_results->window_surface;
#if defined(GLES3_SUPPORTED)
resource_context_.emplace(display_, config_);
#endif
}
GraphicsSystemEGL::~GraphicsSystemEGL() {
resource_context_ = base::nullopt;
if (window_surface_ != EGL_NO_SURFACE) {
eglDestroySurface(display_, window_surface_);
}
eglTerminate(display_);
}
scoped_ptr<Display> GraphicsSystemEGL::CreateDisplay(
system_window::SystemWindow* system_window) {
EGLSurface surface;
if (system_window == system_window_ && window_surface_ != EGL_NO_SURFACE) {
// Hand-off our precreated window into the display being created.
surface = window_surface_;
window_surface_ = EGL_NO_SURFACE;
} else {
EGLNativeWindowType native_window =
(EGLNativeWindowType)(system_window->GetWindowHandle());
surface = eglCreateWindowSurface(display_, config_, native_window, NULL);
CHECK_EQ(EGL_SUCCESS, eglGetError());
}
return scoped_ptr<Display>(new DisplayEGL(display_, surface));
}
scoped_ptr<GraphicsContext> GraphicsSystemEGL::CreateGraphicsContext() {
// If GLES3 is supported, we will make use of PBOs to allocate buffers for
// texture data and populate them on separate threads. In order to access
// that data from graphics contexts created through this method, we must
// enable sharing between them and the resource context, which is why we
// must pass it in here.
#if defined(GLES3_SUPPORTED)
ResourceContext* resource_context = &(resource_context_.value());
#else
ResourceContext* resource_context = NULL;
#endif
return scoped_ptr<GraphicsContext>(
new GraphicsContextEGL(this, display_, config_, resource_context));
}
scoped_ptr<TextureDataEGL> GraphicsSystemEGL::AllocateTextureData(
const math::Size& size, GLenum format) {
#if defined(GLES3_SUPPORTED)
scoped_ptr<TextureDataEGL> texture_data(
new TextureDataPBO(&(resource_context_.value()), size, format));
#else
scoped_ptr<TextureDataEGL> texture_data(new TextureDataCPU(size, format));
#endif
if (texture_data->CreationError()) {
return scoped_ptr<TextureDataEGL>();
} else {
return texture_data.Pass();
}
}
scoped_ptr<RawTextureMemoryEGL> GraphicsSystemEGL::AllocateRawTextureMemory(
size_t size_in_bytes, size_t alignment) {
#if defined(GLES3_SUPPORTED)
return scoped_ptr<RawTextureMemoryEGL>(new RawTextureMemoryPBO(
&(resource_context_.value()), size_in_bytes, alignment));
#else
return scoped_ptr<RawTextureMemoryEGL>(
new RawTextureMemoryCPU(size_in_bytes, alignment));
#endif
}
} // namespace backend
} // namespace renderer
} // namespace cobalt