blob: 41bb49741f8b53ab15e737839aba25dcf82f3c8a [file] [log] [blame]
// Copyright 2019 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 "starboard/blitter.h"
#include <EGL/egl.h>
#include <sanitizer/lsan_interface.h>
#include <memory>
#include "starboard/common/log.h"
#include "starboard/common/optional.h"
#include "starboard/shared/blittergles/blitter_context.h"
#include "starboard/shared/blittergles/blitter_internal.h"
#include "starboard/shared/blittergles/blitter_surface.h"
#include "starboard/shared/blittergles/color_shader_program.h"
#include "starboard/shared/gles/gl_call.h"
#include "starboard/shared/x11/application_x11.h"
#include "starboard/window.h"
namespace {
// This function will search for an EGLConfig that enables a successful
// eglCreateWindowSurface(). If there's no existing window, create a best-guess
// config.
starboard::optional<EGLConfig> GetEGLConfig(EGLDisplay display) {
SbWindow sample_window =
starboard::shared::x11::ApplicationX11::Get()->GetFirstWindow();
EGLint num_configs = 0;
EGLConfig config;
if (sample_window == kSbWindowInvalid) {
EGL_CALL(eglChooseConfig(
display, starboard::shared::blittergles::kConfigAttributeList, &config,
1, &num_configs));
if (num_configs == 1) {
return config;
}
} else {
EGL_CALL(eglChooseConfig(
display, starboard::shared::blittergles::kConfigAttributeList, NULL, 0,
&num_configs));
if (num_configs <= 0) {
SB_DLOG(ERROR) << ": Found no matching configs.";
return starboard::nullopt;
}
std::unique_ptr<EGLConfig[]> configs(new EGLConfig[num_configs]);
eglChooseConfig(display,
starboard::shared::blittergles::kConfigAttributeList,
configs.get(), num_configs, &num_configs);
if (eglGetError() != EGL_SUCCESS) {
SB_DLOG(ERROR) << ": Error getting matching EGLConfigs.";
return starboard::nullopt;
}
// Find first config that allows eglCreateWindowSurface().
EGLSurface surface;
EGLNativeWindowType native_window =
(EGLNativeWindowType)SbWindowGetPlatformHandle(sample_window);
for (int config_number = 0; config_number < num_configs; ++config_number) {
config = configs[config_number];
surface = eglCreateWindowSurface(display, config, native_window, NULL);
if (eglGetError() == EGL_SUCCESS) {
EGL_CALL(eglDestroySurface(display, surface));
return config;
}
}
}
SB_DLOG(ERROR) << ": Couldn't find a compatible config.";
return starboard::nullopt;
}
// When using Xvfb, the selected drivers will leak memory on the 1st call to
// glDrawArrays(). We get that draw out of the way with a dummy draw here.
void DummyDraw(SbBlitterContext context) {
SbBlitterSurface dummy_surface = SbBlitterCreateRenderTargetSurface(
context->device, 1, 1, kSbBlitterSurfaceFormatRGBA8);
SbBlitterSetRenderTarget(context, dummy_surface->render_target);
SbBlitterContextPrivate::ScopedCurrentContext scoped_current_context(context);
const starboard::shared::blittergles::ColorShaderProgram&
color_shader_program = context->GetColorShaderProgram();
color_shader_program.DummyDraw(dummy_surface->render_target);
context->current_render_target = kSbBlitterInvalidRenderTarget;
context->scissor = SbBlitterMakeRect(0, 0, 0, 0);
SbBlitterDestroySurface(dummy_surface);
}
#if defined ADDRESS_SANITIZER
class ScopedLeakSanitizerDisabler {
public:
ScopedLeakSanitizerDisabler() { __lsan_disable(); }
~ScopedLeakSanitizerDisabler() { __lsan_enable(); }
};
#define ANNOTATE_SCOPED_MEMORY_LEAK \
ScopedLeakSanitizerDisabler leak_sanitizer_disabler; static_cast<void>(0)
#else
#define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0)
#endif
} // namespace
SbBlitterDevice SbBlitterCreateDefaultDevice() {
starboard::shared::blittergles::SbBlitterDeviceRegistry* device_registry =
starboard::shared::blittergles::GetBlitterDeviceRegistry();
starboard::ScopedLock lock(device_registry->mutex);
if (device_registry->default_device) {
SB_DLOG(ERROR) << ": Default device has already been created.";
return kSbBlitterInvalidDevice;
}
std::unique_ptr<SbBlitterDevicePrivate> device(new SbBlitterDevicePrivate());
device->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (device->display == EGL_NO_DISPLAY) {
SB_DLOG(ERROR) << ": Failed to get EGL display connection.";
return kSbBlitterInvalidDevice;
}
{
// Despite eglTerminate() being used in SbBlitterDestroyDevice(), the
// current mesa egl drivers still leak memory.
ANNOTATE_SCOPED_MEMORY_LEAK;
if (!eglInitialize(device->display, NULL, NULL)) {
SB_DLOG(ERROR) << ": Failed to initialize device.";
return kSbBlitterInvalidDevice;
}
}
starboard::optional<EGLConfig> config = GetEGLConfig(device->display);
if (!config) {
return kSbBlitterInvalidDevice;
}
device->config = *config;
device_registry->default_device = device.release();
std::unique_ptr<SbBlitterContextPrivate> context(
new SbBlitterContextPrivate(device_registry->default_device));
if (!context->IsValid()) {
return kSbBlitterInvalidDevice;
}
starboard::shared::blittergles::SbBlitterContextRegistry* context_registry =
starboard::shared::blittergles::GetBlitterContextRegistry();
starboard::ScopedLock context_lock(context_registry->mutex);
context_registry->context = context.release();
DummyDraw(context_registry->context);
return device_registry->default_device;
}