| Index: content/common/gpu/media/rendering_helper.cc
|
| diff --git a/content/common/gpu/media/rendering_helper.cc b/content/common/gpu/media/rendering_helper.cc
|
| deleted file mode 100644
|
| index cabe3fbbf82b333a3768c88a0c020ab262d1f6ea..0000000000000000000000000000000000000000
|
| --- a/content/common/gpu/media/rendering_helper.cc
|
| +++ /dev/null
|
| @@ -1,905 +0,0 @@
|
| -// Copyright 2013 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 "content/common/gpu/media/rendering_helper.h"
|
| -
|
| -#include <string.h>
|
| -
|
| -#include <algorithm>
|
| -#include <memory>
|
| -#include <numeric>
|
| -#include <vector>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/callback_helpers.h"
|
| -#include "base/command_line.h"
|
| -#include "base/mac/scoped_nsautorelease_pool.h"
|
| -#include "base/macros.h"
|
| -#include "base/message_loop/message_loop.h"
|
| -#include "base/run_loop.h"
|
| -#include "base/strings/stringize_macros.h"
|
| -#include "base/synchronization/waitable_event.h"
|
| -#include "base/time/time.h"
|
| -#include "build/build_config.h"
|
| -#include "ui/gl/gl_context.h"
|
| -#include "ui/gl/gl_implementation.h"
|
| -#include "ui/gl/gl_surface.h"
|
| -
|
| -#if defined(OS_WIN)
|
| -#include <windows.h>
|
| -#endif
|
| -
|
| -#if defined(USE_X11)
|
| -#include "ui/gfx/x/x11_types.h"
|
| -#endif
|
| -
|
| -#if defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11)
|
| -#include "ui/gl/gl_surface_glx.h"
|
| -#define GL_VARIANT_GLX 1
|
| -#else
|
| -#include "ui/gl/gl_surface_egl.h"
|
| -#define GL_VARIANT_EGL 1
|
| -#endif
|
| -
|
| -#if defined(USE_OZONE)
|
| -#if defined(OS_CHROMEOS)
|
| -#include "ui/display/chromeos/display_configurator.h"
|
| -#include "ui/display/types/native_display_delegate.h"
|
| -#endif // defined(OS_CHROMEOS)
|
| -#include "ui/ozone/public/ozone_platform.h"
|
| -#include "ui/platform_window/platform_window.h"
|
| -#include "ui/platform_window/platform_window_delegate.h"
|
| -#endif // defined(USE_OZONE)
|
| -
|
| -// Helper for Shader creation.
|
| -static void CreateShader(GLuint program,
|
| - GLenum type,
|
| - const char* source,
|
| - int size) {
|
| - GLuint shader = glCreateShader(type);
|
| - glShaderSource(shader, 1, &source, &size);
|
| - glCompileShader(shader);
|
| - int result = GL_FALSE;
|
| - glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
|
| - if (!result) {
|
| - char log[4096];
|
| - glGetShaderInfoLog(shader, arraysize(log), NULL, log);
|
| - LOG(FATAL) << log;
|
| - }
|
| - glAttachShader(program, shader);
|
| - glDeleteShader(shader);
|
| - CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
|
| -}
|
| -
|
| -namespace content {
|
| -namespace {
|
| -
|
| -void WaitForSwapAck(const base::Closure& callback, gfx::SwapResult result) {
|
| - callback.Run();
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -#if defined(USE_OZONE)
|
| -
|
| -class DisplayConfiguratorObserver : public ui::DisplayConfigurator::Observer {
|
| - public:
|
| - explicit DisplayConfiguratorObserver(base::RunLoop* loop) : loop_(loop) {}
|
| - ~DisplayConfiguratorObserver() override {}
|
| -
|
| - private:
|
| - // ui::DisplayConfigurator::Observer overrides:
|
| - void OnDisplayModeChanged(
|
| - const ui::DisplayConfigurator::DisplayStateList& outputs) override {
|
| - if (!loop_)
|
| - return;
|
| - loop_->Quit();
|
| - loop_ = nullptr;
|
| - }
|
| - void OnDisplayModeChangeFailed(
|
| - const ui::DisplayConfigurator::DisplayStateList& outputs,
|
| - ui::MultipleDisplayState failed_new_state) override {
|
| - LOG(FATAL) << "Could not configure display";
|
| - }
|
| -
|
| - base::RunLoop* loop_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(DisplayConfiguratorObserver);
|
| -};
|
| -
|
| -class RenderingHelper::StubOzoneDelegate : public ui::PlatformWindowDelegate {
|
| - public:
|
| - StubOzoneDelegate() : accelerated_widget_(gfx::kNullAcceleratedWidget) {
|
| - platform_window_ = ui::OzonePlatform::GetInstance()->CreatePlatformWindow(
|
| - this, gfx::Rect());
|
| - }
|
| - ~StubOzoneDelegate() override {}
|
| -
|
| - void OnBoundsChanged(const gfx::Rect& new_bounds) override {}
|
| -
|
| - void OnDamageRect(const gfx::Rect& damaged_region) override {}
|
| -
|
| - void DispatchEvent(ui::Event* event) override {}
|
| -
|
| - void OnCloseRequest() override {}
|
| - void OnClosed() override {}
|
| -
|
| - void OnWindowStateChanged(ui::PlatformWindowState new_state) override {}
|
| -
|
| - void OnLostCapture() override {};
|
| -
|
| - void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget,
|
| - float device_pixel_ratio) override {
|
| - accelerated_widget_ = widget;
|
| - }
|
| -
|
| - void OnAcceleratedWidgetDestroyed() override {
|
| - NOTREACHED();
|
| - }
|
| -
|
| - void OnActivationChanged(bool active) override {};
|
| -
|
| - gfx::AcceleratedWidget accelerated_widget() const {
|
| - return accelerated_widget_;
|
| - }
|
| -
|
| - gfx::Size GetSize() { return platform_window_->GetBounds().size(); }
|
| -
|
| - ui::PlatformWindow* platform_window() const { return platform_window_.get(); }
|
| -
|
| - private:
|
| - std::unique_ptr<ui::PlatformWindow> platform_window_;
|
| - gfx::AcceleratedWidget accelerated_widget_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(StubOzoneDelegate);
|
| -};
|
| -
|
| -#endif // defined(USE_OZONE)
|
| -
|
| -RenderingHelperParams::RenderingHelperParams()
|
| - : rendering_fps(0), warm_up_iterations(0), render_as_thumbnails(false) {
|
| -}
|
| -
|
| -RenderingHelperParams::RenderingHelperParams(
|
| - const RenderingHelperParams& other) = default;
|
| -
|
| -RenderingHelperParams::~RenderingHelperParams() {}
|
| -
|
| -VideoFrameTexture::VideoFrameTexture(uint32_t texture_target,
|
| - uint32_t texture_id,
|
| - const base::Closure& no_longer_needed_cb)
|
| - : texture_target_(texture_target),
|
| - texture_id_(texture_id),
|
| - no_longer_needed_cb_(no_longer_needed_cb) {
|
| - DCHECK(!no_longer_needed_cb_.is_null());
|
| -}
|
| -
|
| -VideoFrameTexture::~VideoFrameTexture() {
|
| - base::ResetAndReturn(&no_longer_needed_cb_).Run();
|
| -}
|
| -
|
| -RenderingHelper::RenderedVideo::RenderedVideo()
|
| - : is_flushing(false), frames_to_drop(0) {
|
| -}
|
| -
|
| -RenderingHelper::RenderedVideo::RenderedVideo(const RenderedVideo& other) =
|
| - default;
|
| -
|
| -RenderingHelper::RenderedVideo::~RenderedVideo() {
|
| -}
|
| -
|
| -// static
|
| -void RenderingHelper::InitializeOneOff(base::WaitableEvent* done) {
|
| - base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
|
| -#if GL_VARIANT_GLX
|
| - cmd_line->AppendSwitchASCII(switches::kUseGL,
|
| - gfx::kGLImplementationDesktopName);
|
| -#else
|
| - cmd_line->AppendSwitchASCII(switches::kUseGL, gfx::kGLImplementationEGLName);
|
| -#endif
|
| -
|
| - if (!gfx::GLSurface::InitializeOneOff())
|
| - LOG(FATAL) << "Could not initialize GL";
|
| - done->Signal();
|
| -}
|
| -
|
| -RenderingHelper::RenderingHelper() : ignore_vsync_(false) {
|
| - window_ = gfx::kNullAcceleratedWidget;
|
| - Clear();
|
| -}
|
| -
|
| -RenderingHelper::~RenderingHelper() {
|
| - CHECK_EQ(videos_.size(), 0U) << "Must call UnInitialize before dtor.";
|
| - Clear();
|
| -}
|
| -
|
| -void RenderingHelper::Setup() {
|
| -#if defined(OS_WIN)
|
| - window_ = CreateWindowEx(0,
|
| - L"Static",
|
| - L"VideoDecodeAcceleratorTest",
|
| - WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
| - 0,
|
| - 0,
|
| - GetSystemMetrics(SM_CXSCREEN),
|
| - GetSystemMetrics(SM_CYSCREEN),
|
| - NULL,
|
| - NULL,
|
| - NULL,
|
| - NULL);
|
| -#elif defined(USE_X11)
|
| - Display* display = gfx::GetXDisplay();
|
| - Screen* screen = DefaultScreenOfDisplay(display);
|
| -
|
| - CHECK(display);
|
| -
|
| - XSetWindowAttributes window_attributes;
|
| - memset(&window_attributes, 0, sizeof(window_attributes));
|
| - window_attributes.background_pixel =
|
| - BlackPixel(display, DefaultScreen(display));
|
| - window_attributes.override_redirect = true;
|
| - int depth = DefaultDepth(display, DefaultScreen(display));
|
| -
|
| - window_ = XCreateWindow(display,
|
| - DefaultRootWindow(display),
|
| - 0,
|
| - 0,
|
| - XWidthOfScreen(screen),
|
| - XHeightOfScreen(screen),
|
| - 0 /* border width */,
|
| - depth,
|
| - CopyFromParent /* class */,
|
| - CopyFromParent /* visual */,
|
| - (CWBackPixel | CWOverrideRedirect),
|
| - &window_attributes);
|
| - XStoreName(display, window_, "VideoDecodeAcceleratorTest");
|
| - XSelectInput(display, window_, ExposureMask);
|
| - XMapWindow(display, window_);
|
| -#elif defined(USE_OZONE)
|
| - base::MessageLoop::ScopedNestableTaskAllower nest_loop(
|
| - base::MessageLoop::current());
|
| - base::RunLoop wait_window_resize;
|
| -
|
| - platform_window_delegate_.reset(new RenderingHelper::StubOzoneDelegate());
|
| - window_ = platform_window_delegate_->accelerated_widget();
|
| - gfx::Size window_size(800, 600);
|
| - // Ignore the vsync provider by default. On ChromeOS this will be set
|
| - // accordingly based on the display configuration.
|
| - ignore_vsync_ = true;
|
| -#if defined(OS_CHROMEOS)
|
| - // We hold onto the main loop here to wait for the DisplayController
|
| - // to give us the size of the display so we can create a window of
|
| - // the same size.
|
| - base::RunLoop wait_display_setup;
|
| - DisplayConfiguratorObserver display_setup_observer(&wait_display_setup);
|
| - display_configurator_.reset(new ui::DisplayConfigurator());
|
| - display_configurator_->SetDelegateForTesting(0);
|
| - display_configurator_->AddObserver(&display_setup_observer);
|
| - display_configurator_->Init(
|
| - ui::OzonePlatform::GetInstance()->CreateNativeDisplayDelegate(),
|
| - true);
|
| - display_configurator_->ForceInitialConfigure(0);
|
| - // Make sure all the display configuration is applied.
|
| - wait_display_setup.Run();
|
| - display_configurator_->RemoveObserver(&display_setup_observer);
|
| -
|
| - gfx::Size framebuffer_size = display_configurator_->framebuffer_size();
|
| - if (!framebuffer_size.IsEmpty()) {
|
| - window_size = framebuffer_size;
|
| - ignore_vsync_ = false;
|
| - }
|
| -#endif
|
| - if (ignore_vsync_)
|
| - DVLOG(1) << "Ignoring vsync provider";
|
| -
|
| - platform_window_delegate_->platform_window()->SetBounds(
|
| - gfx::Rect(window_size));
|
| -
|
| - // On Ozone/DRI, platform windows are associated with the physical
|
| - // outputs. Association is achieved by matching the bounds of the
|
| - // window with the origin & modeset of the display output. Until a
|
| - // window is associated with a display output, we cannot get vsync
|
| - // events, because there is no hardware to get events from. Here we
|
| - // wait for the window to resized and therefore associated with
|
| - // display output to be sure that we will get such events.
|
| - wait_window_resize.RunUntilIdle();
|
| -#else
|
| -#error unknown platform
|
| -#endif
|
| - CHECK(window_ != gfx::kNullAcceleratedWidget);
|
| -}
|
| -
|
| -void RenderingHelper::TearDown() {
|
| -#if defined(OS_WIN)
|
| - if (window_)
|
| - DestroyWindow(window_);
|
| -#elif defined(USE_X11)
|
| - // Destroy resources acquired in Initialize, in reverse-acquisition order.
|
| - if (window_) {
|
| - CHECK(XUnmapWindow(gfx::GetXDisplay(), window_));
|
| - CHECK(XDestroyWindow(gfx::GetXDisplay(), window_));
|
| - }
|
| -#elif defined(USE_OZONE)
|
| - platform_window_delegate_.reset();
|
| -#if defined(OS_CHROMEOS)
|
| - display_configurator_->PrepareForExit();
|
| - display_configurator_.reset();
|
| -#endif
|
| -#endif
|
| - window_ = gfx::kNullAcceleratedWidget;
|
| -}
|
| -
|
| -void RenderingHelper::Initialize(const RenderingHelperParams& params,
|
| - base::WaitableEvent* done) {
|
| - // Use videos_.size() != 0 as a proxy for the class having already been
|
| - // Initialize()'d, and UnInitialize() before continuing.
|
| - if (videos_.size()) {
|
| - base::WaitableEvent done(false, false);
|
| - UnInitialize(&done);
|
| - done.Wait();
|
| - }
|
| -
|
| - render_task_.Reset(
|
| - base::Bind(&RenderingHelper::RenderContent, base::Unretained(this)));
|
| -
|
| - frame_duration_ = params.rendering_fps > 0
|
| - ? base::TimeDelta::FromSeconds(1) / params.rendering_fps
|
| - : base::TimeDelta();
|
| -
|
| - render_as_thumbnails_ = params.render_as_thumbnails;
|
| - message_loop_ = base::MessageLoop::current();
|
| -
|
| - gl_surface_ = gfx::GLSurface::CreateViewGLSurface(window_);
|
| -#if defined(USE_OZONE)
|
| - gl_surface_->Resize(platform_window_delegate_->GetSize(), 1.f, true);
|
| -#endif // defined(USE_OZONE)
|
| - screen_size_ = gl_surface_->GetSize();
|
| -
|
| - gl_context_ = gfx::GLContext::CreateGLContext(
|
| - NULL, gl_surface_.get(), gfx::PreferIntegratedGpu);
|
| - CHECK(gl_context_->MakeCurrent(gl_surface_.get()));
|
| -
|
| - CHECK_GT(params.window_sizes.size(), 0U);
|
| - videos_.resize(params.window_sizes.size());
|
| - LayoutRenderingAreas(params.window_sizes);
|
| -
|
| - if (render_as_thumbnails_) {
|
| - CHECK_EQ(videos_.size(), 1U);
|
| -
|
| - GLint max_texture_size;
|
| - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
|
| - CHECK_GE(max_texture_size, params.thumbnails_page_size.width());
|
| - CHECK_GE(max_texture_size, params.thumbnails_page_size.height());
|
| -
|
| - thumbnails_fbo_size_ = params.thumbnails_page_size;
|
| - thumbnail_size_ = params.thumbnail_size;
|
| -
|
| - glGenFramebuffersEXT(1, &thumbnails_fbo_id_);
|
| - glGenTextures(1, &thumbnails_texture_id_);
|
| - glBindTexture(GL_TEXTURE_2D, thumbnails_texture_id_);
|
| - glTexImage2D(GL_TEXTURE_2D,
|
| - 0,
|
| - GL_RGB,
|
| - thumbnails_fbo_size_.width(),
|
| - thumbnails_fbo_size_.height(),
|
| - 0,
|
| - GL_RGB,
|
| - GL_UNSIGNED_SHORT_5_6_5,
|
| - NULL);
|
| - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
| - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
| - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
| - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
| - glBindTexture(GL_TEXTURE_2D, 0);
|
| -
|
| - glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_);
|
| - glFramebufferTexture2DEXT(GL_FRAMEBUFFER,
|
| - GL_COLOR_ATTACHMENT0,
|
| - GL_TEXTURE_2D,
|
| - thumbnails_texture_id_,
|
| - 0);
|
| -
|
| - GLenum fb_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
|
| - CHECK(fb_status == GL_FRAMEBUFFER_COMPLETE) << fb_status;
|
| - glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
| - glClear(GL_COLOR_BUFFER_BIT);
|
| - glBindFramebufferEXT(GL_FRAMEBUFFER,
|
| - gl_surface_->GetBackingFrameBufferObject());
|
| - }
|
| -
|
| - // These vertices and texture coords. map (0,0) in the texture to the
|
| - // bottom left of the viewport. Since we get the video frames with the
|
| - // the top left at (0,0) we need to flip the texture y coordinate
|
| - // in the vertex shader for this to be rendered the right way up.
|
| - // In the case of thumbnail rendering we use the same vertex shader
|
| - // to render the FBO the screen, where we do not want this flipping.
|
| - static const float kVertices[] =
|
| - { -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f, -1.f, };
|
| - static const float kTextureCoords[] = { 0, 1, 0, 0, 1, 1, 1, 0, };
|
| - static const char kVertexShader[] = STRINGIZE(
|
| - varying vec2 interp_tc;
|
| - attribute vec4 in_pos;
|
| - attribute vec2 in_tc;
|
| - uniform bool tex_flip;
|
| - void main() {
|
| - if (tex_flip)
|
| - interp_tc = vec2(in_tc.x, 1.0 - in_tc.y);
|
| - else
|
| - interp_tc = in_tc;
|
| - gl_Position = in_pos;
|
| - });
|
| -
|
| -#if GL_VARIANT_EGL
|
| - static const char kFragmentShader[] =
|
| - "#extension GL_OES_EGL_image_external : enable\n"
|
| - "precision mediump float;\n"
|
| - "varying vec2 interp_tc;\n"
|
| - "uniform sampler2D tex;\n"
|
| - "#ifdef GL_OES_EGL_image_external\n"
|
| - "uniform samplerExternalOES tex_external;\n"
|
| - "#endif\n"
|
| - "void main() {\n"
|
| - " vec4 color = texture2D(tex, interp_tc);\n"
|
| - "#ifdef GL_OES_EGL_image_external\n"
|
| - " color += texture2D(tex_external, interp_tc);\n"
|
| - "#endif\n"
|
| - " gl_FragColor = color;\n"
|
| - "}\n";
|
| -#else
|
| - static const char kFragmentShader[] = STRINGIZE(
|
| - varying vec2 interp_tc;
|
| - uniform sampler2D tex;
|
| - void main() {
|
| - gl_FragColor = texture2D(tex, interp_tc);
|
| - });
|
| -#endif
|
| - program_ = glCreateProgram();
|
| - CreateShader(
|
| - program_, GL_VERTEX_SHADER, kVertexShader, arraysize(kVertexShader));
|
| - CreateShader(program_,
|
| - GL_FRAGMENT_SHADER,
|
| - kFragmentShader,
|
| - arraysize(kFragmentShader));
|
| - glLinkProgram(program_);
|
| - int result = GL_FALSE;
|
| - glGetProgramiv(program_, GL_LINK_STATUS, &result);
|
| - if (!result) {
|
| - char log[4096];
|
| - glGetShaderInfoLog(program_, arraysize(log), NULL, log);
|
| - LOG(FATAL) << log;
|
| - }
|
| - glUseProgram(program_);
|
| - glDeleteProgram(program_);
|
| -
|
| - glUniform1i(glGetUniformLocation(program_, "tex_flip"), 0);
|
| - glUniform1i(glGetUniformLocation(program_, "tex"), 0);
|
| - GLint tex_external = glGetUniformLocation(program_, "tex_external");
|
| - if (tex_external != -1) {
|
| - glUniform1i(tex_external, 1);
|
| - }
|
| - int pos_location = glGetAttribLocation(program_, "in_pos");
|
| - glEnableVertexAttribArray(pos_location);
|
| - glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices);
|
| - int tc_location = glGetAttribLocation(program_, "in_tc");
|
| - glEnableVertexAttribArray(tc_location);
|
| - glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, kTextureCoords);
|
| -
|
| - if (!frame_duration_.is_zero()) {
|
| - int warm_up_iterations = params.warm_up_iterations;
|
| -#if defined(USE_OZONE)
|
| - // On Ozone the VSyncProvider can't provide a vsync interval until
|
| - // we render at least a frame, so we warm up with at least one
|
| - // frame.
|
| - // On top of this, we want to make sure all the buffers backing
|
| - // the GL surface are cleared, otherwise we can see the previous
|
| - // test's last frames, so we set warm up iterations to 2, to clear
|
| - // the front and back buffers.
|
| - warm_up_iterations = std::max(2, warm_up_iterations);
|
| -#endif
|
| - WarmUpRendering(warm_up_iterations);
|
| - }
|
| -
|
| - // It's safe to use Unretained here since |rendering_thread_| will be stopped
|
| - // in VideoDecodeAcceleratorTest.TearDown(), while the |rendering_helper_| is
|
| - // a member of that class. (See video_decode_accelerator_unittest.cc.)
|
| - gfx::VSyncProvider* vsync_provider = gl_surface_->GetVSyncProvider();
|
| -
|
| - // VSync providers rely on the underlying CRTC to get the timing. In headless
|
| - // mode the surface isn't associated with a CRTC so the vsync provider can not
|
| - // get the timing, meaning it will not call UpdateVsyncParameters() ever.
|
| - if (!ignore_vsync_ && vsync_provider && !frame_duration_.is_zero()) {
|
| - vsync_provider->GetVSyncParameters(base::Bind(
|
| - &RenderingHelper::UpdateVSyncParameters, base::Unretained(this), done));
|
| - } else {
|
| - done->Signal();
|
| - }
|
| -}
|
| -
|
| -// The rendering for the first few frames is slow (e.g., 100ms on Peach Pit).
|
| -// This affects the numbers measured in the performance test. We try to render
|
| -// several frames here to warm up the rendering.
|
| -void RenderingHelper::WarmUpRendering(int warm_up_iterations) {
|
| - unsigned int texture_id;
|
| - std::unique_ptr<GLubyte[]> emptyData(
|
| - new GLubyte[screen_size_.GetArea() * 2]());
|
| - glGenTextures(1, &texture_id);
|
| - glBindTexture(GL_TEXTURE_2D, texture_id);
|
| - glTexImage2D(GL_TEXTURE_2D,
|
| - 0,
|
| - GL_RGB,
|
| - screen_size_.width(),
|
| - screen_size_.height(),
|
| - 0,
|
| - GL_RGB,
|
| - GL_UNSIGNED_SHORT_5_6_5,
|
| - emptyData.get());
|
| - for (int i = 0; i < warm_up_iterations; ++i) {
|
| - RenderTexture(GL_TEXTURE_2D, texture_id);
|
| -
|
| - // Need to allow nestable tasks since WarmUpRendering() is called from
|
| - // within another task on the renderer thread.
|
| - base::MessageLoop::ScopedNestableTaskAllower allow(
|
| - base::MessageLoop::current());
|
| - base::RunLoop wait_for_swap_ack;
|
| - gl_surface_->SwapBuffersAsync(
|
| - base::Bind(&WaitForSwapAck, wait_for_swap_ack.QuitClosure()));
|
| - wait_for_swap_ack.Run();
|
| - }
|
| - glDeleteTextures(1, &texture_id);
|
| -}
|
| -
|
| -void RenderingHelper::UnInitialize(base::WaitableEvent* done) {
|
| - CHECK_EQ(base::MessageLoop::current(), message_loop_);
|
| -
|
| - render_task_.Cancel();
|
| -
|
| - if (render_as_thumbnails_) {
|
| - glDeleteTextures(1, &thumbnails_texture_id_);
|
| - glDeleteFramebuffersEXT(1, &thumbnails_fbo_id_);
|
| - }
|
| -
|
| - gl_context_->ReleaseCurrent(gl_surface_.get());
|
| - gl_context_ = NULL;
|
| - gl_surface_ = NULL;
|
| -
|
| - Clear();
|
| - done->Signal();
|
| -}
|
| -
|
| -void RenderingHelper::CreateTexture(uint32_t texture_target,
|
| - uint32_t* texture_id,
|
| - const gfx::Size& size,
|
| - base::WaitableEvent* done) {
|
| - if (base::MessageLoop::current() != message_loop_) {
|
| - message_loop_->PostTask(FROM_HERE,
|
| - base::Bind(&RenderingHelper::CreateTexture,
|
| - base::Unretained(this),
|
| - texture_target,
|
| - texture_id,
|
| - size,
|
| - done));
|
| - return;
|
| - }
|
| - glGenTextures(1, texture_id);
|
| - glBindTexture(texture_target, *texture_id);
|
| - if (texture_target == GL_TEXTURE_2D) {
|
| - glTexImage2D(GL_TEXTURE_2D,
|
| - 0,
|
| - GL_RGBA,
|
| - size.width(),
|
| - size.height(),
|
| - 0,
|
| - GL_RGBA,
|
| - GL_UNSIGNED_BYTE,
|
| - NULL);
|
| - }
|
| - glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
| - glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
| - // OpenGLES2.0.25 section 3.8.2 requires CLAMP_TO_EDGE for NPOT textures.
|
| - glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
| - glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
| - CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
|
| - done->Signal();
|
| -}
|
| -
|
| -// Helper function to set GL viewport.
|
| -static inline void GLSetViewPort(const gfx::Rect& area) {
|
| - glViewport(area.x(), area.y(), area.width(), area.height());
|
| - glScissor(area.x(), area.y(), area.width(), area.height());
|
| -}
|
| -
|
| -void RenderingHelper::RenderThumbnail(uint32_t texture_target,
|
| - uint32_t texture_id) {
|
| - CHECK_EQ(base::MessageLoop::current(), message_loop_);
|
| - const int width = thumbnail_size_.width();
|
| - const int height = thumbnail_size_.height();
|
| - const int thumbnails_in_row = thumbnails_fbo_size_.width() / width;
|
| - const int thumbnails_in_column = thumbnails_fbo_size_.height() / height;
|
| - const int row = (frame_count_ / thumbnails_in_row) % thumbnails_in_column;
|
| - const int col = frame_count_ % thumbnails_in_row;
|
| -
|
| - gfx::Rect area(col * width, row * height, width, height);
|
| -
|
| - glUniform1i(glGetUniformLocation(program_, "tex_flip"), 0);
|
| - glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_);
|
| - GLSetViewPort(area);
|
| - RenderTexture(texture_target, texture_id);
|
| - glBindFramebufferEXT(GL_FRAMEBUFFER,
|
| - gl_surface_->GetBackingFrameBufferObject());
|
| -
|
| - // Need to flush the GL commands before we return the tnumbnail texture to
|
| - // the decoder.
|
| - glFlush();
|
| - ++frame_count_;
|
| -}
|
| -
|
| -void RenderingHelper::QueueVideoFrame(
|
| - size_t window_id,
|
| - scoped_refptr<VideoFrameTexture> video_frame) {
|
| - CHECK_EQ(base::MessageLoop::current(), message_loop_);
|
| - RenderedVideo* video = &videos_[window_id];
|
| - DCHECK(!video->is_flushing);
|
| -
|
| - video->pending_frames.push(video_frame);
|
| -
|
| - if (video->frames_to_drop > 0 && video->pending_frames.size() > 1) {
|
| - --video->frames_to_drop;
|
| - video->pending_frames.pop();
|
| - }
|
| -
|
| - // Schedules the first RenderContent() if need.
|
| - if (scheduled_render_time_.is_null()) {
|
| - scheduled_render_time_ = base::TimeTicks::Now();
|
| - message_loop_->PostTask(FROM_HERE, render_task_.callback());
|
| - }
|
| -}
|
| -
|
| -void RenderingHelper::RenderTexture(uint32_t texture_target,
|
| - uint32_t texture_id) {
|
| - // The ExternalOES sampler is bound to GL_TEXTURE1 and the Texture2D sampler
|
| - // is bound to GL_TEXTURE0.
|
| - if (texture_target == GL_TEXTURE_2D) {
|
| - glActiveTexture(GL_TEXTURE0 + 0);
|
| - } else if (texture_target == GL_TEXTURE_EXTERNAL_OES) {
|
| - glActiveTexture(GL_TEXTURE0 + 1);
|
| - }
|
| - glBindTexture(texture_target, texture_id);
|
| - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
| - glBindTexture(texture_target, 0);
|
| - CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
|
| -}
|
| -
|
| -void RenderingHelper::DeleteTexture(uint32_t texture_id) {
|
| - CHECK_EQ(base::MessageLoop::current(), message_loop_);
|
| - glDeleteTextures(1, &texture_id);
|
| - CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
|
| -}
|
| -
|
| -gfx::GLContext* RenderingHelper::GetGLContext() {
|
| - return gl_context_.get();
|
| -}
|
| -
|
| -void* RenderingHelper::GetGLDisplay() {
|
| - return gl_surface_->GetDisplay();
|
| -}
|
| -
|
| -void RenderingHelper::Clear() {
|
| - videos_.clear();
|
| - message_loop_ = NULL;
|
| - gl_context_ = NULL;
|
| - gl_surface_ = NULL;
|
| -
|
| - render_as_thumbnails_ = false;
|
| - frame_count_ = 0;
|
| - thumbnails_fbo_id_ = 0;
|
| - thumbnails_texture_id_ = 0;
|
| -}
|
| -
|
| -void RenderingHelper::GetThumbnailsAsRGB(std::vector<unsigned char>* rgb,
|
| - bool* alpha_solid,
|
| - base::WaitableEvent* done) {
|
| - CHECK(render_as_thumbnails_);
|
| -
|
| - const size_t num_pixels = thumbnails_fbo_size_.GetArea();
|
| - std::vector<unsigned char> rgba;
|
| - rgba.resize(num_pixels * 4);
|
| - glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_);
|
| - glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
| - // We can only count on GL_RGBA/GL_UNSIGNED_BYTE support.
|
| - glReadPixels(0,
|
| - 0,
|
| - thumbnails_fbo_size_.width(),
|
| - thumbnails_fbo_size_.height(),
|
| - GL_RGBA,
|
| - GL_UNSIGNED_BYTE,
|
| - &rgba[0]);
|
| - glBindFramebufferEXT(GL_FRAMEBUFFER,
|
| - gl_surface_->GetBackingFrameBufferObject());
|
| - rgb->resize(num_pixels * 3);
|
| - // Drop the alpha channel, but check as we go that it is all 0xff.
|
| - bool solid = true;
|
| - unsigned char* rgb_ptr = &((*rgb)[0]);
|
| - unsigned char* rgba_ptr = &rgba[0];
|
| - for (size_t i = 0; i < num_pixels; ++i) {
|
| - *rgb_ptr++ = *rgba_ptr++;
|
| - *rgb_ptr++ = *rgba_ptr++;
|
| - *rgb_ptr++ = *rgba_ptr++;
|
| - solid = solid && (*rgba_ptr == 0xff);
|
| - rgba_ptr++;
|
| - }
|
| - *alpha_solid = solid;
|
| -
|
| - done->Signal();
|
| -}
|
| -
|
| -void RenderingHelper::Flush(size_t window_id) {
|
| - videos_[window_id].is_flushing = true;
|
| -}
|
| -
|
| -void RenderingHelper::RenderContent() {
|
| - CHECK_EQ(base::MessageLoop::current(), message_loop_);
|
| -
|
| - // Update the VSync params.
|
| - //
|
| - // It's safe to use Unretained here since |rendering_thread_| will be stopped
|
| - // in VideoDecodeAcceleratorTest.TearDown(), while the |rendering_helper_| is
|
| - // a member of that class. (See video_decode_accelerator_unittest.cc.)
|
| - gfx::VSyncProvider* vsync_provider = gl_surface_->GetVSyncProvider();
|
| - if (vsync_provider && !ignore_vsync_) {
|
| - vsync_provider->GetVSyncParameters(base::Bind(
|
| - &RenderingHelper::UpdateVSyncParameters, base::Unretained(this),
|
| - static_cast<base::WaitableEvent*>(NULL)));
|
| - }
|
| -
|
| - int tex_flip = 1;
|
| -#if defined(USE_OZONE)
|
| - // Ozone surfaceless renders flipped from normal GL, so there's no need to
|
| - // do an extra flip.
|
| - tex_flip = 0;
|
| -#endif // defined(USE_OZONE)
|
| - glUniform1i(glGetUniformLocation(program_, "tex_flip"), tex_flip);
|
| -
|
| - // Frames that will be returned to the client (via the no_longer_needed_cb)
|
| - // after this vector falls out of scope at the end of this method. We need
|
| - // to keep references to them until after SwapBuffers() call below.
|
| - std::vector<scoped_refptr<VideoFrameTexture> > frames_to_be_returned;
|
| - bool need_swap_buffer = false;
|
| - if (render_as_thumbnails_) {
|
| - // In render_as_thumbnails_ mode, we render the FBO content on the
|
| - // screen instead of the decoded textures.
|
| - GLSetViewPort(videos_[0].render_area);
|
| - RenderTexture(GL_TEXTURE_2D, thumbnails_texture_id_);
|
| - need_swap_buffer = true;
|
| - } else {
|
| - for (RenderedVideo& video : videos_) {
|
| - if (video.pending_frames.empty())
|
| - continue;
|
| - need_swap_buffer = true;
|
| - scoped_refptr<VideoFrameTexture> frame = video.pending_frames.front();
|
| - GLSetViewPort(video.render_area);
|
| - RenderTexture(frame->texture_target(), frame->texture_id());
|
| -
|
| - if (video.pending_frames.size() > 1 || video.is_flushing) {
|
| - frames_to_be_returned.push_back(video.pending_frames.front());
|
| - video.pending_frames.pop();
|
| - } else {
|
| - ++video.frames_to_drop;
|
| - }
|
| - }
|
| - }
|
| -
|
| - base::Closure schedule_frame = base::Bind(
|
| - &RenderingHelper::ScheduleNextRenderContent, base::Unretained(this));
|
| - if (!need_swap_buffer) {
|
| - schedule_frame.Run();
|
| - return;
|
| - }
|
| -
|
| - gl_surface_->SwapBuffersAsync(
|
| - base::Bind(&WaitForSwapAck, schedule_frame));
|
| -}
|
| -
|
| -// Helper function for the LayoutRenderingAreas(). The |lengths| are the
|
| -// heights(widths) of the rows(columns). It scales the elements in
|
| -// |lengths| proportionally so that the sum of them equal to |total_length|.
|
| -// It also outputs the coordinates of the rows(columns) to |offsets|.
|
| -static void ScaleAndCalculateOffsets(std::vector<int>* lengths,
|
| - std::vector<int>* offsets,
|
| - int total_length) {
|
| - int sum = std::accumulate(lengths->begin(), lengths->end(), 0);
|
| - for (size_t i = 0; i < lengths->size(); ++i) {
|
| - lengths->at(i) = lengths->at(i) * total_length / sum;
|
| - offsets->at(i) = (i == 0) ? 0 : offsets->at(i - 1) + lengths->at(i - 1);
|
| - }
|
| -}
|
| -
|
| -void RenderingHelper::LayoutRenderingAreas(
|
| - const std::vector<gfx::Size>& window_sizes) {
|
| - // Find the number of colums and rows.
|
| - // The smallest n * n or n * (n + 1) > number of windows.
|
| - size_t cols = sqrt(videos_.size() - 1) + 1;
|
| - size_t rows = (videos_.size() + cols - 1) / cols;
|
| -
|
| - // Find the widths and heights of the grid.
|
| - std::vector<int> widths(cols);
|
| - std::vector<int> heights(rows);
|
| - std::vector<int> offset_x(cols);
|
| - std::vector<int> offset_y(rows);
|
| -
|
| - for (size_t i = 0; i < window_sizes.size(); ++i) {
|
| - const gfx::Size& size = window_sizes[i];
|
| - widths[i % cols] = std::max(widths[i % cols], size.width());
|
| - heights[i / cols] = std::max(heights[i / cols], size.height());
|
| - }
|
| -
|
| - ScaleAndCalculateOffsets(&widths, &offset_x, screen_size_.width());
|
| - ScaleAndCalculateOffsets(&heights, &offset_y, screen_size_.height());
|
| -
|
| - // Put each render_area_ in the center of each cell.
|
| - for (size_t i = 0; i < window_sizes.size(); ++i) {
|
| - const gfx::Size& size = window_sizes[i];
|
| - float scale =
|
| - std::min(static_cast<float>(widths[i % cols]) / size.width(),
|
| - static_cast<float>(heights[i / cols]) / size.height());
|
| -
|
| - // Don't scale up the texture.
|
| - scale = std::min(1.0f, scale);
|
| -
|
| - size_t w = scale * size.width();
|
| - size_t h = scale * size.height();
|
| - size_t x = offset_x[i % cols] + (widths[i % cols] - w) / 2;
|
| - size_t y = offset_y[i / cols] + (heights[i / cols] - h) / 2;
|
| - videos_[i].render_area = gfx::Rect(x, y, w, h);
|
| - }
|
| -}
|
| -
|
| -void RenderingHelper::UpdateVSyncParameters(base::WaitableEvent* done,
|
| - const base::TimeTicks timebase,
|
| - const base::TimeDelta interval) {
|
| - vsync_timebase_ = timebase;
|
| - vsync_interval_ = interval;
|
| -
|
| - if (done)
|
| - done->Signal();
|
| -}
|
| -
|
| -void RenderingHelper::DropOneFrameForAllVideos() {
|
| - for (RenderedVideo& video : videos_) {
|
| - if (video.pending_frames.empty())
|
| - continue;
|
| -
|
| - if (video.pending_frames.size() > 1 || video.is_flushing) {
|
| - video.pending_frames.pop();
|
| - } else {
|
| - ++video.frames_to_drop;
|
| - }
|
| - }
|
| -}
|
| -
|
| -void RenderingHelper::ScheduleNextRenderContent() {
|
| - scheduled_render_time_ += frame_duration_;
|
| - base::TimeTicks now = base::TimeTicks::Now();
|
| - base::TimeTicks target;
|
| -
|
| - if (vsync_interval_.is_zero()) {
|
| - target = std::max(now, scheduled_render_time_);
|
| - } else {
|
| - // Schedules the next RenderContent() at latest VSYNC before the
|
| - // |scheduled_render_time_|.
|
| - target = std::max(now + vsync_interval_, scheduled_render_time_);
|
| -
|
| - int64_t intervals = (target - vsync_timebase_) / vsync_interval_;
|
| - target = vsync_timebase_ + intervals * vsync_interval_;
|
| - }
|
| -
|
| - // When the rendering falls behind, drops frames.
|
| - while (scheduled_render_time_ < target) {
|
| - scheduled_render_time_ += frame_duration_;
|
| - DropOneFrameForAllVideos();
|
| - }
|
| -
|
| - message_loop_->PostDelayedTask(
|
| - FROM_HERE, render_task_.callback(), target - now);
|
| -}
|
| -} // namespace content
|
|
|