Commit c44ad7df authored by Michael Spang's avatar Michael Spang Committed by Commit Bot

ozone: demo: Simplify ozone demo & make skia demo standalone

Ozone demo is intended to be the simplest possible program that uses the
ozone API to create a window and render to it. Porters use this to see if
their platform implements the API correctly without having to build or
run the entire browser.

Ozone demo recently started using skia infrastructure for rendering via
GL. However, this arguably makes the demo less useful to porters because
it is additional complexity that is not relevant to implementing the
platform window or rendering surface.

This splits the skia demo into a new executable and brings back the
"simple" renderer that just clears the screen to a solid color.

Bug: none
Test: ozone_demo, skia_demo
Change-Id: I8570d43949d68dcf87175c53057c7c72cf45ea6e
Reviewed-on: https://chromium-review.googlesource.com/1028579Reviewed-by: default avatarAlex Sakhartchouk <alexst@chromium.org>
Commit-Queue: Michael Spang <spang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#553721}
parent 3a4c3ed2
......@@ -5,42 +5,78 @@
group("demo") {
deps = [
":ozone_demo",
":skia_demo",
]
}
executable("ozone_demo") {
source_set("ozone_demo_lib") {
sources = [
"demo_window.cc",
"demo_window.h",
"ozone_demo.cc",
"renderer.h",
"renderer_base.cc",
"renderer_base.h",
"renderer_factory.cc",
"renderer_factory.h",
"skia_renderer.cc",
"skia_renderer.h",
"software_renderer.cc",
"software_renderer.h",
"surfaceless_skia_renderer.cc",
"surfaceless_skia_renderer.h",
"window_manager.cc",
"window_manager.h",
]
deps = [
"//base",
"//build/config:exe_and_shlib_deps",
"//components/tracing:startup_tracing",
"//skia",
"//ui/display/types",
"//ui/events",
"//ui/events:dom_keycode_converter",
"//ui/events/ozone:events_ozone_layout",
"//ui/gfx/geometry",
"//ui/ozone",
"//ui/platform_window",
]
}
executable("ozone_demo") {
sources = [
"gl_renderer.cc",
"gl_renderer.h",
"ozone_demo.cc",
"simple_renderer_factory.cc",
"simple_renderer_factory.h",
"software_renderer.cc",
"software_renderer.h",
"surfaceless_gl_renderer.cc",
"surfaceless_gl_renderer.h",
]
deps = [
":ozone_demo_lib",
"//build/config:exe_and_shlib_deps",
"//components/tracing:startup_tracing",
"//ui/events/ozone:events_ozone_layout",
"//ui/gl",
"//ui/gl/init",
"//ui/ozone",
]
}
executable("skia_demo") {
sources = [
"skia/skia_demo.cc",
"skia/skia_gl_renderer.cc",
"skia/skia_gl_renderer.h",
"skia/skia_renderer_factory.cc",
"skia/skia_renderer_factory.h",
"skia/skia_surfaceless_gl_renderer.cc",
"skia/skia_surfaceless_gl_renderer.h",
]
deps = [
":ozone_demo_lib",
"//build/config:exe_and_shlib_deps",
"//components/tracing:startup_tracing",
"//skia",
"//ui/events/ozone:events_ozone_layout",
"//ui/gl",
"//ui/gl/init",
"//ui/ozone",
"//ui/platform_window",
]
}
// Copyright 2014 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 "ui/ozone/demo/gl_renderer.h"
#include "base/location.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/init/gl_factory.h"
namespace ui {
GlRenderer::GlRenderer(gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size)
: RendererBase(widget, size), surface_(surface), weak_ptr_factory_(this) {}
GlRenderer::~GlRenderer() {}
bool GlRenderer::Initialize() {
context_ = gl::init::CreateGLContext(nullptr, surface_.get(),
gl::GLContextAttribs());
if (!context_.get()) {
LOG(ERROR) << "Failed to create GL context";
return false;
}
surface_->Resize(size_, 1.f, gl::GLSurface::ColorSpace::UNSPECIFIED, true);
if (!context_->MakeCurrent(surface_.get())) {
LOG(ERROR) << "Failed to make GL context current";
return false;
}
// Schedule the initial render.
PostRenderFrameTask(gfx::SwapResult::SWAP_ACK);
return true;
}
void GlRenderer::RenderFrame() {
TRACE_EVENT0("ozone", "GlRenderer::RenderFrame");
float fraction = NextFraction();
context_->MakeCurrent(surface_.get());
glViewport(0, 0, size_.width(), size_.height());
glClearColor(1.f - fraction, fraction, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (surface_->SupportsAsyncSwap()) {
surface_->SwapBuffersAsync(base::Bind(&GlRenderer::PostRenderFrameTask,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&GlRenderer::OnPresentation,
weak_ptr_factory_.GetWeakPtr()));
} else {
PostRenderFrameTask(surface_->SwapBuffers(base::Bind(
&GlRenderer::OnPresentation, weak_ptr_factory_.GetWeakPtr())));
}
}
void GlRenderer::PostRenderFrameTask(gfx::SwapResult result) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&GlRenderer::RenderFrame, weak_ptr_factory_.GetWeakPtr()));
}
void GlRenderer::OnPresentation(const gfx::PresentationFeedback& feedback) {
DCHECK(surface_->SupportsPresentationCallback());
LOG_IF(ERROR, feedback.timestamp.is_null()) << "Last frame is discarded!";
}
} // namespace ui
// Copyright 2018 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.
#ifndef UI_OZONE_DEMO_GL_RENDERER_H_
#define UI_OZONE_DEMO_GL_RENDERER_H_
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "ui/gfx/swap_result.h"
#include "ui/ozone/demo/renderer_base.h"
namespace gfx {
struct PresentationFeedback;
} // namespace gfx
namespace gl {
class GLContext;
class GLSurface;
} // namespace gl
namespace ui {
class GlRenderer : public RendererBase {
public:
GlRenderer(gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size);
~GlRenderer() override;
// Renderer:
bool Initialize() override;
protected:
virtual void RenderFrame();
virtual void PostRenderFrameTask(gfx::SwapResult result);
scoped_refptr<gl::GLSurface> surface_;
scoped_refptr<gl::GLContext> context_;
private:
void OnPresentation(const gfx::PresentationFeedback& feedback);
base::WeakPtrFactory<GlRenderer> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(GlRenderer);
};
} // namespace ui
#endif // UI_OZONE_DEMO_GL_RENDERER_H_
......@@ -16,6 +16,7 @@
#include "components/tracing/common/tracing_switches.h"
#include "ui/events/ozone/layout/keyboard_layout_engine.h"
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
#include "ui/ozone/demo/simple_renderer_factory.h"
#include "ui/ozone/demo/window_manager.h"
#include "ui/ozone/public/ozone_platform.h"
......@@ -40,11 +41,9 @@ int main(int argc, char** argv) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(kHelp)) {
std::cout <<
"Usage:\n\n"
" --enable-drm-atomic Use the atomic KMS API\n"
" --disable-gpu Force software rendering\n"
" --disable-surfaceless Don't use surfaceless EGL\n"
" --window-size=WIDTHxHEIGHT Specify window size\n"
" --use-ddl Use SkDeferredDisplayList\n"
" --partial-primary-plane "
"Use smaller than fullscreen primary plane\n"
" --enable-overlay Use an overlay plane\n"
......@@ -78,7 +77,8 @@ int main(int argc, char** argv) {
base::RunLoop run_loop;
ui::WindowManager window_manager(run_loop.QuitClosure());
ui::WindowManager window_manager(
std::make_unique<ui::SimpleRendererFactory>(), run_loop.QuitClosure());
run_loop.Run();
......
......@@ -4,72 +4,8 @@
#include "ui/ozone/demo/renderer_factory.h"
#include <memory>
#include "base/command_line.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/init/gl_factory.h"
#include "ui/ozone/demo/skia_renderer.h"
#include "ui/ozone/demo/software_renderer.h"
#include "ui/ozone/demo/surfaceless_skia_renderer.h"
#include "ui/ozone/public/ozone_platform.h"
namespace ui {
namespace {
const char kDisableSurfaceless[] = "disable-surfaceless";
const char kDisableGpu[] = "disable-gpu";
scoped_refptr<gl::GLSurface> CreateGLSurface(gfx::AcceleratedWidget widget) {
scoped_refptr<gl::GLSurface> surface;
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(kDisableSurfaceless))
surface = gl::init::CreateSurfacelessViewGLSurface(widget);
if (!surface)
surface = gl::init::CreateViewGLSurface(widget);
return surface;
}
} // namespace
RendererFactory::RendererFactory() {}
RendererFactory::~RendererFactory() {}
bool RendererFactory::Initialize() {
OzonePlatform::InitParams params;
params.single_process = true;
OzonePlatform::InitializeForGPU(params);
OzonePlatform::GetInstance()->AfterSandboxEntry();
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (!command_line->HasSwitch(kDisableGpu) && gl::init::InitializeGLOneOff() &&
gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get())) {
type_ = SKIA;
} else {
type_ = SOFTWARE;
}
return true;
}
std::unique_ptr<Renderer> RendererFactory::CreateRenderer(
gfx::AcceleratedWidget widget,
const gfx::Size& size) {
switch (type_) {
case SKIA: {
scoped_refptr<gl::GLSurface> surface = CreateGLSurface(widget);
if (!surface)
LOG(FATAL) << "Failed to create GL surface";
if (surface->IsSurfaceless()) {
return std::make_unique<SurfacelessSkiaRenderer>(widget, surface, size);
}
return std::make_unique<SkiaRenderer>(widget, surface, size);
}
case SOFTWARE:
return std::make_unique<SoftwareRenderer>(widget, size);
}
return nullptr;
}
} // namespace ui
......@@ -7,7 +7,6 @@
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/ozone/public/ozone_gpu_test_helper.h"
namespace ui {
......@@ -15,25 +14,13 @@ class Renderer;
class RendererFactory {
public:
enum RendererType {
SKIA,
SOFTWARE,
};
virtual ~RendererFactory();
RendererFactory();
~RendererFactory();
virtual bool Initialize() = 0;
bool Initialize();
std::unique_ptr<Renderer> CreateRenderer(gfx::AcceleratedWidget widget,
const gfx::Size& size);
private:
RendererType type_ = SOFTWARE;
// Helper for applications that do GL on main thread.
OzoneGpuTestHelper gpu_helper_;
DISALLOW_COPY_AND_ASSIGN(RendererFactory);
virtual std::unique_ptr<Renderer> CreateRenderer(
gfx::AcceleratedWidget widget,
const gfx::Size& size) = 0;
};
} // namespace ui
......
// Copyright 2018 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 "ui/ozone/demo/simple_renderer_factory.h"
#include <memory>
#include "base/command_line.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/init/gl_factory.h"
#include "ui/ozone/demo/gl_renderer.h"
#include "ui/ozone/demo/software_renderer.h"
#include "ui/ozone/demo/surfaceless_gl_renderer.h"
#include "ui/ozone/public/ozone_platform.h"
namespace ui {
namespace {
const char kDisableSurfaceless[] = "disable-surfaceless";
const char kDisableGpu[] = "disable-gpu";
scoped_refptr<gl::GLSurface> CreateGLSurface(gfx::AcceleratedWidget widget) {
scoped_refptr<gl::GLSurface> surface;
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(kDisableSurfaceless))
surface = gl::init::CreateSurfacelessViewGLSurface(widget);
if (!surface)
surface = gl::init::CreateViewGLSurface(widget);
return surface;
}
} // namespace
SimpleRendererFactory::SimpleRendererFactory() {}
SimpleRendererFactory::~SimpleRendererFactory() {}
bool SimpleRendererFactory::Initialize() {
OzonePlatform::InitParams params;
params.single_process = true;
OzonePlatform::InitializeForGPU(params);
OzonePlatform::GetInstance()->AfterSandboxEntry();
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (!command_line->HasSwitch(kDisableGpu) && gl::init::InitializeGLOneOff() &&
gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get())) {
type_ = GL;
} else {
type_ = SOFTWARE;
}
return true;
}
std::unique_ptr<Renderer> SimpleRendererFactory::CreateRenderer(
gfx::AcceleratedWidget widget,
const gfx::Size& size) {
switch (type_) {
case GL: {
scoped_refptr<gl::GLSurface> surface = CreateGLSurface(widget);
if (!surface)
LOG(FATAL) << "Failed to create GL surface";
if (surface->IsSurfaceless()) {
return std::make_unique<SurfacelessGlRenderer>(widget, surface, size);
}
return std::make_unique<GlRenderer>(widget, surface, size);
}
case SOFTWARE:
return std::make_unique<SoftwareRenderer>(widget, size);
}
return nullptr;
}
} // namespace ui
// Copyright 2018 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.
#ifndef UI_OZONE_DEMO_SIMPLE_RENDERER_FACTORY_H_
#define UI_OZONE_DEMO_SIMPLE_RENDERER_FACTORY_H_
#include "ui/ozone/demo/renderer_factory.h"
#include "ui/ozone/public/ozone_gpu_test_helper.h"
namespace ui {
class SimpleRendererFactory : public RendererFactory {
public:
enum RendererType {
GL,
SOFTWARE,
};
SimpleRendererFactory();
~SimpleRendererFactory() override;
bool Initialize() override;
std::unique_ptr<Renderer> CreateRenderer(gfx::AcceleratedWidget widget,
const gfx::Size& size) override;
private:
RendererType type_ = SOFTWARE;
// Helper for applications that do GL on main thread.
OzoneGpuTestHelper gpu_helper_;
DISALLOW_COPY_AND_ASSIGN(SimpleRendererFactory);
};
} // namespace ui
#endif // UI_OZONE_DEMO_SIMPLE_RENDERER_FACTORY_H_
// Copyright 2014 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 <iostream>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/debug/stack_trace.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/task_scheduler/task_scheduler.h"
#include "base/trace_event/trace_event.h"
#include "components/tracing/common/trace_to_console.h"
#include "components/tracing/common/tracing_switches.h"
#include "ui/events/ozone/layout/keyboard_layout_engine.h"
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
#include "ui/ozone/demo/skia/skia_renderer_factory.h"
#include "ui/ozone/demo/window_manager.h"
#include "ui/ozone/public/ozone_platform.h"
int main(int argc, char** argv) {
base::CommandLine::Init(argc, argv);
base::AtExitManager exit_manager;
base::debug::EnableInProcessStackDumping();
// Initialize logging so we can enable VLOG messages.
logging::LoggingSettings settings;
logging::InitLogging(settings);
// Initialize tracing.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kTraceToConsole)) {
base::trace_event::TraceConfig trace_config =
tracing::GetConfigForTraceToConsole();
base::trace_event::TraceLog::GetInstance()->SetEnabled(
trace_config, base::trace_event::TraceLog::RECORDING_MODE);
}
// Build UI thread message loop. This is used by platform
// implementations for event polling & running background tasks.
base::MessageLoopForUI message_loop;
base::TaskScheduler::CreateAndStartWithDefaultParams("SkiaDemo");
ui::OzonePlatform::InitParams params;
params.single_process = true;
ui::OzonePlatform::InitializeForUI(params);
ui::KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()
->SetCurrentLayoutByName("us");
base::RunLoop run_loop;
ui::WindowManager window_manager(std::make_unique<ui::SkiaRendererFactory>(),
run_loop.QuitClosure());
run_loop.Run();
return 0;
}
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/demo/skia_renderer.h"
#include "ui/ozone/demo/skia/skia_gl_renderer.h"
#include "base/command_line.h"
#include "base/location.h"
......@@ -33,21 +33,21 @@ const char kUseDDL[] = "use-ddl";
} // namespace
SkiaRenderer::SkiaRenderer(gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size)
SkiaGlRenderer::SkiaGlRenderer(gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size)
: RendererBase(widget, size),
gl_surface_(surface),
use_ddl_(base::CommandLine::ForCurrentProcess()->HasSwitch(kUseDDL)),
condition_variable_(&lock_),
weak_ptr_factory_(this) {}
SkiaRenderer::~SkiaRenderer() {
SkiaGlRenderer::~SkiaGlRenderer() {
if (use_ddl_)
StopDDLRenderThread();
}
bool SkiaRenderer::Initialize() {
bool SkiaGlRenderer::Initialize() {
gl_context_ = gl::init::CreateGLContext(nullptr, gl_surface_.get(),
gl::GLContextAttribs());
if (!gl_context_.get()) {
......@@ -75,8 +75,8 @@ bool SkiaRenderer::Initialize() {
return true;
}
void SkiaRenderer::RenderFrame() {
TRACE_EVENT0("ozone", "SkiaRenderer::RenderFrame");
void SkiaGlRenderer::RenderFrame() {
TRACE_EVENT0("ozone", "SkiaGlRenderer::RenderFrame");
// LegacyFontHost will get LCD text and skia figures out what type to use.
SkSurfaceProps surface_props =
......@@ -106,24 +106,24 @@ void SkiaRenderer::RenderFrame() {
if (gl_surface_->SupportsAsyncSwap()) {
gl_surface_->SwapBuffersAsync(
base::BindRepeating(&SkiaRenderer::PostRenderFrameTask,
base::BindRepeating(&SkiaGlRenderer::PostRenderFrameTask,
weak_ptr_factory_.GetWeakPtr()),
base::BindRepeating(&SkiaRenderer::OnPresentation,
base::BindRepeating(&SkiaGlRenderer::OnPresentation,
weak_ptr_factory_.GetWeakPtr()));
} else {
PostRenderFrameTask(gl_surface_->SwapBuffers(base::BindRepeating(
&SkiaRenderer::OnPresentation, weak_ptr_factory_.GetWeakPtr())));
&SkiaGlRenderer::OnPresentation, weak_ptr_factory_.GetWeakPtr())));
}
}
void SkiaRenderer::PostRenderFrameTask(gfx::SwapResult result) {
void SkiaGlRenderer::PostRenderFrameTask(gfx::SwapResult result) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindRepeating(&SkiaRenderer::RenderFrame,
FROM_HERE, base::BindRepeating(&SkiaGlRenderer::RenderFrame,
weak_ptr_factory_.GetWeakPtr()));
}
void SkiaRenderer::Draw(SkCanvas* canvas, float fraction) {
TRACE_EVENT0("ozone", "SkiaRenderer::Draw");
void SkiaGlRenderer::Draw(SkCanvas* canvas, float fraction) {
TRACE_EVENT0("ozone", "SkiaGlRenderer::Draw");
// Clear background
canvas->clear(SkColorSetARGB(255, 255 * fraction, 255 * (1 - fraction), 0));
......@@ -172,7 +172,7 @@ void SkiaRenderer::Draw(SkCanvas* canvas, float fraction) {
canvas->restore();
}
void SkiaRenderer::StartDDLRenderThreadIfNecessary(SkSurface* sk_surface) {
void SkiaGlRenderer::StartDDLRenderThreadIfNecessary(SkSurface* sk_surface) {
DCHECK(use_ddl_);
if (ddl_render_thread_)
return;
......@@ -186,7 +186,7 @@ void SkiaRenderer::StartDDLRenderThreadIfNecessary(SkSurface* sk_surface) {
ddl_render_thread_->Start();
}
void SkiaRenderer::StopDDLRenderThread() {
void SkiaGlRenderer::StopDDLRenderThread() {
if (!ddl_render_thread_)
return;
{
......@@ -200,7 +200,7 @@ void SkiaRenderer::StopDDLRenderThread() {
ddls_.pop();
}
std::unique_ptr<SkDeferredDisplayList> SkiaRenderer::GetDDL() {
std::unique_ptr<SkDeferredDisplayList> SkiaGlRenderer::GetDDL() {
base::AutoLock auto_lock_(lock_);
DCHECK(surface_charaterization_.isValid());
// Wait until DDL is generated by DDL render thread.
......@@ -212,7 +212,7 @@ std::unique_ptr<SkDeferredDisplayList> SkiaRenderer::GetDDL() {
return ddl;
}
void SkiaRenderer::Run() {
void SkiaGlRenderer::Run() {
base::AutoLock auto_lock(lock_);
while (true) {
// Wait until ddls_ is consumed or surface_charaterization_ is reset.
......@@ -235,7 +235,7 @@ void SkiaRenderer::Run() {
}
}
void SkiaRenderer::OnPresentation(const gfx::PresentationFeedback& feedback) {
void SkiaGlRenderer::OnPresentation(const gfx::PresentationFeedback& feedback) {
DCHECK(gl_surface_->SupportsPresentationCallback());
LOG_IF(ERROR, feedback.timestamp.is_null()) << "Last frame is discarded!";
}
......
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_OZONE_DEMO_SKIA_RENDERER_H_
#define UI_OZONE_DEMO_SKIA_RENDERER_H_
#ifndef UI_OZONE_DEMO_SKIA_SKIA_GL_RENDERER_H_
#define UI_OZONE_DEMO_SKIA_SKIA_GL_RENDERER_H_
#include "base/containers/queue.h"
#include "base/macros.h"
......@@ -29,13 +29,13 @@ class GLSurface;
namespace ui {
class SkiaRenderer : public RendererBase,
public base::DelegateSimpleThread::Delegate {
class SkiaGlRenderer : public RendererBase,
public base::DelegateSimpleThread::Delegate {
public:
SkiaRenderer(gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size);
~SkiaRenderer() override;
SkiaGlRenderer(gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size);
~SkiaGlRenderer() override;
// Renderer:
bool Initialize() override;
......@@ -76,11 +76,11 @@ class SkiaRenderer : public RendererBase,
SkSurfaceCharacterization surface_charaterization_;
base::queue<std::unique_ptr<SkDeferredDisplayList>> ddls_;
base::WeakPtrFactory<SkiaRenderer> weak_ptr_factory_;
base::WeakPtrFactory<SkiaGlRenderer> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(SkiaRenderer);
DISALLOW_COPY_AND_ASSIGN(SkiaGlRenderer);
};
} // namespace ui
#endif // UI_OZONE_DEMO_SKIA_RENDERER_H_
#endif // UI_OZONE_DEMO_SKIA_SKIA_GL_RENDERER_H_
// Copyright 2018 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 "ui/ozone/demo/skia/skia_renderer_factory.h"
#include <memory>
#include "base/command_line.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/init/gl_factory.h"
#include "ui/ozone/demo/skia/skia_gl_renderer.h"
#include "ui/ozone/demo/skia/skia_surfaceless_gl_renderer.h"
#include "ui/ozone/public/ozone_platform.h"
namespace ui {
namespace {
const char kDisableSurfaceless[] = "disable-surfaceless";
scoped_refptr<gl::GLSurface> CreateGLSurface(gfx::AcceleratedWidget widget) {
scoped_refptr<gl::GLSurface> surface;
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(kDisableSurfaceless))
surface = gl::init::CreateSurfacelessViewGLSurface(widget);
if (!surface)
surface = gl::init::CreateViewGLSurface(widget);
return surface;
}
} // namespace
SkiaRendererFactory::SkiaRendererFactory() {}
SkiaRendererFactory::~SkiaRendererFactory() {}
bool SkiaRendererFactory::Initialize() {
OzonePlatform::InitParams params;
params.single_process = true;
OzonePlatform::InitializeForGPU(params);
OzonePlatform::GetInstance()->AfterSandboxEntry();
if (!gl::init::InitializeGLOneOff() ||
!gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get())) {
LOG(FATAL) << "Failed to initialize GL";
}
return true;
}
std::unique_ptr<Renderer> SkiaRendererFactory::CreateRenderer(
gfx::AcceleratedWidget widget,
const gfx::Size& size) {
scoped_refptr<gl::GLSurface> surface = CreateGLSurface(widget);
if (!surface)
LOG(FATAL) << "Failed to create GL surface";
if (surface->IsSurfaceless()) {
return std::make_unique<SurfacelessSkiaGlRenderer>(widget, surface, size);
}
return std::make_unique<SkiaGlRenderer>(widget, surface, size);
}
} // namespace ui
// Copyright 2018 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.
#ifndef UI_OZONE_DEMO_SKIA_SKIA_RENDERER_FACTORY_H_
#define UI_OZONE_DEMO_SKIA_SKIA_RENDERER_FACTORY_H_
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/ozone/demo/renderer_factory.h"
#include "ui/ozone/public/ozone_gpu_test_helper.h"
namespace ui {
class Renderer;
class SkiaRendererFactory : public RendererFactory {
public:
enum RendererType {
SKIA,
SOFTWARE,
};
SkiaRendererFactory();
~SkiaRendererFactory() override;
bool Initialize() override;
std::unique_ptr<Renderer> CreateRenderer(gfx::AcceleratedWidget widget,
const gfx::Size& size) override;
private:
// Helper for applications that do GL on main thread.
OzoneGpuTestHelper gpu_helper_;
DISALLOW_COPY_AND_ASSIGN(SkiaRendererFactory);
};
} // namespace ui
#endif // UI_OZONE_DEMO_SKIA_SKIA_RENDERER_FACTORY_H_
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/demo/surfaceless_skia_renderer.h"
#include "ui/ozone/demo/skia/skia_surfaceless_gl_renderer.h"
#include <stddef.h>
......@@ -69,7 +69,7 @@ OverlaySurfaceCandidate MakeOverlayCandidate(int z_order,
} // namespace
class SurfacelessSkiaRenderer::BufferWrapper {
class SurfacelessSkiaGlRenderer::BufferWrapper {
public:
BufferWrapper();
~BufferWrapper();
......@@ -93,16 +93,16 @@ class SurfacelessSkiaRenderer::BufferWrapper {
sk_sp<SkSurface> sk_surface_;
};
SurfacelessSkiaRenderer::BufferWrapper::BufferWrapper() = default;
SurfacelessSkiaGlRenderer::BufferWrapper::BufferWrapper() = default;
SurfacelessSkiaRenderer::BufferWrapper::~BufferWrapper() {
SurfacelessSkiaGlRenderer::BufferWrapper::~BufferWrapper() {
if (gl_tex_) {
image_->ReleaseTexImage(GL_TEXTURE_2D);
glDeleteTextures(1, &gl_tex_);
}
}
bool SurfacelessSkiaRenderer::BufferWrapper::Initialize(
bool SurfacelessSkiaGlRenderer::BufferWrapper::Initialize(
GrContext* gr_context,
gfx::AcceleratedWidget widget,
const gfx::Size& size) {
......@@ -144,24 +144,24 @@ bool SurfacelessSkiaRenderer::BufferWrapper::Initialize(
return true;
}
SurfacelessSkiaRenderer::SurfacelessSkiaRenderer(
SurfacelessSkiaGlRenderer::SurfacelessSkiaGlRenderer(
gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size)
: SkiaRenderer(widget, surface, size),
: SkiaGlRenderer(widget, surface, size),
overlay_checker_(ui::OzonePlatform::GetInstance()
->GetOverlayManager()
->CreateOverlayCandidates(widget)),
weak_ptr_factory_(this) {}
SurfacelessSkiaRenderer::~SurfacelessSkiaRenderer() {
SurfacelessSkiaGlRenderer::~SurfacelessSkiaGlRenderer() {
// Need to make current when deleting the framebuffer resources allocated in
// the buffers.
gl_context_->MakeCurrent(gl_surface_.get());
}
bool SurfacelessSkiaRenderer::Initialize() {
if (!SkiaRenderer::Initialize())
bool SurfacelessSkiaGlRenderer::Initialize() {
if (!SkiaGlRenderer::Initialize())
return false;
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
......@@ -193,8 +193,8 @@ bool SurfacelessSkiaRenderer::Initialize() {
return true;
}
void SurfacelessSkiaRenderer::RenderFrame() {
TRACE_EVENT0("ozone", "SurfacelessSkiaRenderer::RenderFrame");
void SurfacelessSkiaGlRenderer::RenderFrame() {
TRACE_EVENT0("ozone", "SurfacelessSkiaGlRenderer::RenderFrame");
float fraction = CurrentFraction();
gfx::Rect overlay_rect;
......@@ -257,12 +257,12 @@ void SurfacelessSkiaRenderer::RenderFrame() {
back_buffer_ ^= 1;
gl_surface_->SwapBuffersAsync(
base::BindRepeating(&SurfacelessSkiaRenderer::PostRenderFrameTask,
base::BindRepeating(&SurfacelessSkiaGlRenderer::PostRenderFrameTask,
weak_ptr_factory_.GetWeakPtr()),
base::DoNothing());
}
void SurfacelessSkiaRenderer::PostRenderFrameTask(gfx::SwapResult result) {
void SurfacelessSkiaGlRenderer::PostRenderFrameTask(gfx::SwapResult result) {
switch (result) {
case gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS:
for (size_t i = 0; i < arraysize(buffers_); ++i) {
......@@ -273,7 +273,7 @@ void SurfacelessSkiaRenderer::PostRenderFrameTask(gfx::SwapResult result) {
}
FALLTHROUGH; // We want to render a new frame anyways.
case gfx::SwapResult::SWAP_ACK:
SkiaRenderer::PostRenderFrameTask(result);
SkiaGlRenderer::PostRenderFrameTask(result);
break;
case gfx::SwapResult::SWAP_FAILED:
LOG(FATAL) << "Failed to swap buffers";
......
......@@ -2,30 +2,30 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_OZONE_DEMO_SURFACELESS_SKIA_RENDERER_H_
#define UI_OZONE_DEMO_SURFACELESS_SKIA_RENDERER_H_
#ifndef UI_OZONE_DEMO_SKIA_SKIA_SURFACELESS_GL_RENDERER_H_
#define UI_OZONE_DEMO_SKIA_SKIA_SURFACELESS_GL_RENDERER_H_
#include <memory>
#include "base/macros.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/ozone/demo/skia_renderer.h"
#include "ui/ozone/demo/skia/skia_gl_renderer.h"
namespace ui {
class OverlayCandidatesOzone;
class SurfacelessSkiaRenderer : public SkiaRenderer {
class SurfacelessSkiaGlRenderer : public SkiaGlRenderer {
public:
SurfacelessSkiaRenderer(gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size);
~SurfacelessSkiaRenderer() override;
SurfacelessSkiaGlRenderer(gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size);
~SurfacelessSkiaGlRenderer() override;
// Renderer:
bool Initialize() override;
private:
// SkiaRenderer:
// SkiaGlRenderer:
void RenderFrame() override;
void PostRenderFrameTask(gfx::SwapResult result) override;
......@@ -41,11 +41,11 @@ class SurfacelessSkiaRenderer : public SkiaRenderer {
int back_buffer_ = 0;
base::WeakPtrFactory<SurfacelessSkiaRenderer> weak_ptr_factory_;
base::WeakPtrFactory<SurfacelessSkiaGlRenderer> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(SurfacelessSkiaRenderer);
DISALLOW_COPY_AND_ASSIGN(SurfacelessSkiaGlRenderer);
};
} // namespace ui
#endif // UI_OZONE_DEMO_SURFACELESS_SKIA_RENDERER_H_
#endif // UI_OZONE_DEMO_SKIA_SKIA_SURFACELESS_GL_RENDERER_H_
// Copyright 2014 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 "ui/ozone/demo/surfaceless_gl_renderer.h"
#include <stddef.h>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/trace_event/trace_event.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_image.h"
#include "ui/gl/gl_image_native_pixmap.h"
#include "ui/gl/gl_surface.h"
#include "ui/ozone/public/overlay_candidates_ozone.h"
#include "ui/ozone/public/overlay_manager_ozone.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/surface_factory_ozone.h"
namespace ui {
namespace {
OverlaySurfaceCandidate MakeOverlayCandidate(int z_order,
gfx::Rect bounds_rect,
gfx::RectF crop_rect) {
// The overlay checking interface is designed to satisfy the needs of CC which
// will be producing RectF target rectangles. But we use the bounds produced
// in RenderFrame for GLSurface::ScheduleOverlayPlane.
gfx::RectF display_rect(bounds_rect.x(), bounds_rect.y(), bounds_rect.width(),
bounds_rect.height());
OverlaySurfaceCandidate overlay_candidate;
// Use default display format since this should be compatible with most
// devices.
overlay_candidate.format = display::DisplaySnapshot::PrimaryFormat();
// The bounds rectangle of the candidate overlay buffer.
overlay_candidate.buffer_size = bounds_rect.size();
// The same rectangle in floating point coordinates.
overlay_candidate.display_rect = display_rect;
// Show the entire buffer by setting the crop to a unity square.
overlay_candidate.crop_rect = gfx::RectF(0, 0, 1, 1);
// The demo overlay instance is always ontop and not clipped. Clipped quads
// cannot be placed in overlays.
overlay_candidate.is_clipped = false;
return overlay_candidate;
}
} // namespace
SurfacelessGlRenderer::BufferWrapper::BufferWrapper() {}
SurfacelessGlRenderer::BufferWrapper::~BufferWrapper() {
if (gl_fb_)
glDeleteFramebuffersEXT(1, &gl_fb_);
if (gl_tex_) {
image_->ReleaseTexImage(GL_TEXTURE_2D);
glDeleteTextures(1, &gl_tex_);
}
}
bool SurfacelessGlRenderer::BufferWrapper::Initialize(
gfx::AcceleratedWidget widget,
const gfx::Size& size) {
glGenFramebuffersEXT(1, &gl_fb_);
glGenTextures(1, &gl_tex_);
gfx::BufferFormat format = display::DisplaySnapshot::PrimaryFormat();
scoped_refptr<gfx::NativePixmap> pixmap =
OzonePlatform::GetInstance()
->GetSurfaceFactoryOzone()
->CreateNativePixmap(widget, size, format, gfx::BufferUsage::SCANOUT);
scoped_refptr<gl::GLImageNativePixmap> image(
new gl::GLImageNativePixmap(size, GL_BGRA_EXT));
if (!image->Initialize(pixmap.get(), format)) {
LOG(ERROR) << "Failed to create GLImage";
return false;
}
image_ = image;
glBindFramebufferEXT(GL_FRAMEBUFFER, gl_fb_);
glBindTexture(GL_TEXTURE_2D, gl_tex_);
image_->BindTexImage(GL_TEXTURE_2D);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
gl_tex_, 0);
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LOG(ERROR) << "Failed to create framebuffer "
<< glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
return false;
}
widget_ = widget;
size_ = size;
return true;
}
void SurfacelessGlRenderer::BufferWrapper::BindFramebuffer() {
glBindFramebufferEXT(GL_FRAMEBUFFER, gl_fb_);
}
SurfacelessGlRenderer::SurfacelessGlRenderer(
gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size)
: GlRenderer(widget, surface, size),
overlay_checker_(ui::OzonePlatform::GetInstance()
->GetOverlayManager()
->CreateOverlayCandidates(widget)),
weak_ptr_factory_(this) {}
SurfacelessGlRenderer::~SurfacelessGlRenderer() {
// Need to make current when deleting the framebuffer resources allocated in
// the buffers.
context_->MakeCurrent(surface_.get());
}
bool SurfacelessGlRenderer::Initialize() {
if (!GlRenderer::Initialize())
return false;
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch("partial-primary-plane"))
primary_plane_rect_ = gfx::Rect(200, 200, 800, 800);
else
primary_plane_rect_ = gfx::Rect(size_);
for (size_t i = 0; i < arraysize(buffers_); ++i) {
buffers_[i].reset(new BufferWrapper());
if (!buffers_[i]->Initialize(widget_, primary_plane_rect_.size()))
return false;
}
if (command_line->HasSwitch("enable-overlay")) {
gfx::Size overlay_size = gfx::Size(size_.width() / 8, size_.height() / 8);
for (size_t i = 0; i < arraysize(overlay_buffer_); ++i) {
overlay_buffer_[i].reset(new BufferWrapper());
overlay_buffer_[i]->Initialize(gfx::kNullAcceleratedWidget, overlay_size);
glViewport(0, 0, overlay_size.width(), overlay_size.height());
glClearColor(i, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
}
disable_primary_plane_ = command_line->HasSwitch("disable-primary-plane");
PostRenderFrameTask(gfx::SwapResult::SWAP_ACK);
return true;
}
void SurfacelessGlRenderer::RenderFrame() {
TRACE_EVENT0("ozone", "SurfacelessGlRenderer::RenderFrame");
float fraction = NextFraction();
gfx::Rect overlay_rect;
OverlayCandidatesOzone::OverlaySurfaceCandidateList overlay_list;
if (!disable_primary_plane_) {
overlay_list.push_back(
MakeOverlayCandidate(1, gfx::Rect(size_), gfx::RectF(0, 0, 1, 1)));
// We know at least the primary plane can be scanned out.
overlay_list.back().overlay_handled = true;
}
if (overlay_buffer_[0]) {
overlay_rect = gfx::Rect(overlay_buffer_[0]->size());
float steps_num = 5.0f;
float stepped_fraction =
std::floor((fraction + 0.5f / steps_num) * steps_num) / steps_num;
gfx::Vector2d offset(
stepped_fraction * (size_.width() - overlay_rect.width()),
(size_.height() - overlay_rect.height()) / 2);
overlay_rect += offset;
overlay_list.push_back(
MakeOverlayCandidate(1, overlay_rect, gfx::RectF(0, 0, 1, 1)));
}
// The actual validation for a specific overlay configuration is done
// asynchronously and then cached inside overlay_checker_ once a reply
// is sent back.
// This means that the first few frames we call this method for a specific
// overlay_list, all the overlays but the primary plane, that we explicitly
// marked as handled, will be rejected even if they might be handled at a
// later time.
overlay_checker_->CheckOverlaySupport(&overlay_list);
context_->MakeCurrent(surface_.get());
buffers_[back_buffer_]->BindFramebuffer();
glViewport(0, 0, size_.width(), size_.height());
glClearColor(1 - fraction, 0.0, fraction, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (!disable_primary_plane_) {
CHECK(overlay_list.front().overlay_handled);
surface_->ScheduleOverlayPlane(
0, gfx::OVERLAY_TRANSFORM_NONE, buffers_[back_buffer_]->image(),
primary_plane_rect_, gfx::RectF(0, 0, 1, 1), false);
}
if (overlay_buffer_[0] && overlay_list.back().overlay_handled) {
surface_->ScheduleOverlayPlane(1, gfx::OVERLAY_TRANSFORM_NONE,
overlay_buffer_[back_buffer_]->image(),
overlay_rect, gfx::RectF(0, 0, 1, 1), false);
}
back_buffer_ ^= 1;
surface_->SwapBuffersAsync(
base::Bind(&SurfacelessGlRenderer::PostRenderFrameTask,
weak_ptr_factory_.GetWeakPtr()),
base::Bind([](const gfx::PresentationFeedback&) {}));
}
void SurfacelessGlRenderer::PostRenderFrameTask(gfx::SwapResult result) {
switch (result) {
case gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS:
for (size_t i = 0; i < arraysize(buffers_); ++i) {
buffers_[i].reset(new BufferWrapper());
if (!buffers_[i]->Initialize(widget_, primary_plane_rect_.size()))
LOG(FATAL) << "Failed to recreate buffer";
}
FALLTHROUGH; // We want to render a new frame anyways.
case gfx::SwapResult::SWAP_ACK:
GlRenderer::PostRenderFrameTask(result);
break;
case gfx::SwapResult::SWAP_FAILED:
LOG(FATAL) << "Failed to swap buffers";
break;
}
}
} // namespace ui
// Copyright 2018 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.
#ifndef UI_OZONE_DEMO_SURFACELESS_GL_RENDERER_H_
#define UI_OZONE_DEMO_SURFACELESS_GL_RENDERER_H_
#include <memory>
#include "base/macros.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/ozone/demo/gl_renderer.h"
namespace gl {
class GLImage;
}
namespace ui {
class OverlayCandidatesOzone;
class SurfacelessGlRenderer : public GlRenderer {
public:
SurfacelessGlRenderer(gfx::AcceleratedWidget widget,
const scoped_refptr<gl::GLSurface>& surface,
const gfx::Size& size);
~SurfacelessGlRenderer() override;
// Renderer:
bool Initialize() override;
private:
// GlRenderer:
void RenderFrame() override;
void PostRenderFrameTask(gfx::SwapResult result) override;
class BufferWrapper {
public:
BufferWrapper();
~BufferWrapper();
gl::GLImage* image() const { return image_.get(); }
bool Initialize(gfx::AcceleratedWidget widget, const gfx::Size& size);
void BindFramebuffer();
const gfx::Size size() const { return size_; }
private:
gfx::AcceleratedWidget widget_ = gfx::kNullAcceleratedWidget;
gfx::Size size_;
scoped_refptr<gl::GLImage> image_;
unsigned int gl_fb_ = 0;
unsigned int gl_tex_ = 0;
};
std::unique_ptr<BufferWrapper> buffers_[2];
std::unique_ptr<BufferWrapper> overlay_buffer_[2];
bool disable_primary_plane_ = false;
gfx::Rect primary_plane_rect_;
std::unique_ptr<OverlayCandidatesOzone> overlay_checker_;
int back_buffer_ = 0;
base::WeakPtrFactory<SurfacelessGlRenderer> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(SurfacelessGlRenderer);
};
} // namespace ui
#endif // UI_OZONE_DEMO_SURFACELESS_GL_RENDERER_H_
......@@ -21,11 +21,13 @@ const char kWindowSize[] = "window-size";
} // namespace
WindowManager::WindowManager(base::OnceClosure quit_closure)
WindowManager::WindowManager(std::unique_ptr<RendererFactory> renderer_factory,
base::OnceClosure quit_closure)
: delegate_(
ui::OzonePlatform::GetInstance()->CreateNativeDisplayDelegate()),
quit_closure_(std::move(quit_closure)) {
if (!renderer_factory_.Initialize())
quit_closure_(std::move(quit_closure)),
renderer_factory_(std::move(renderer_factory)) {
if (!renderer_factory_->Initialize())
LOG(FATAL) << "Failed to initialize renderer factory";
if (delegate_) {
......@@ -41,7 +43,7 @@ WindowManager::WindowManager(base::OnceClosure quit_closure)
.c_str(),
"%dx%d", &width, &height);
DemoWindow* window = new DemoWindow(this, &renderer_factory_,
DemoWindow* window = new DemoWindow(this, renderer_factory_.get(),
gfx::Rect(gfx::Size(width, height)));
window->Start();
}
......@@ -101,7 +103,7 @@ void WindowManager::OnDisplaysAquired(
void WindowManager::OnDisplayConfigured(const gfx::Rect& bounds, bool success) {
if (success) {
std::unique_ptr<DemoWindow> window(
new DemoWindow(this, &renderer_factory_, bounds));
new DemoWindow(this, renderer_factory_.get(), bounds));
window->Start();
windows_.push_back(std::move(window));
} else {
......
......@@ -25,7 +25,8 @@ class DemoWindow;
class WindowManager : public display::NativeDisplayObserver {
public:
explicit WindowManager(base::OnceClosure quit_closure);
explicit WindowManager(std::unique_ptr<RendererFactory> renderer_factory,
base::OnceClosure quit_closure);
~WindowManager() override;
void Quit();
......@@ -44,7 +45,7 @@ class WindowManager : public display::NativeDisplayObserver {
std::unique_ptr<display::NativeDisplayDelegate> delegate_;
base::OnceClosure quit_closure_;
RendererFactory renderer_factory_;
std::unique_ptr<RendererFactory> renderer_factory_;
std::vector<std::unique_ptr<DemoWindow>> windows_;
// Flags used to keep track of the current state of display configuration.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment