blob: 5f60ffcddb11b7c90918045157c6cf42067ceaf4 [file] [log] [blame]
// Copyright 2017 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 COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_COPIER_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_COPIER_H_
#include <stdint.h>
#include <array>
#include <memory>
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/unguessable_token.h"
#include "components/viz/common/gl_helper.h"
#include "components/viz/service/viz_service_export.h"
namespace gfx {
class ColorSpace;
class Rect;
class Size;
} // namespace gfx
namespace viz {
class ContextProvider;
class CopyOutputRequest;
class TextureDeleter;
// Helper class for GLRenderer that executes CopyOutputRequests using GL, and
// manages the caching of resources needed to ensure efficient video
// performance.
//
// GLRenderer calls CopyFromTextureOrFramebuffer() to execute a
// CopyOutputRequest. GLRendererCopier will examine the request and determine
// the minimal amount of work needed to satisfy all the requirements of the
// request.
//
// In many cases, interim GL objects (textures, framebuffers, etc.) must be
// created as part of a multi-step process. When considering video performance
// (i.e., a series of CopyOutputRequests from the same "source"), these interim
// objects must be cached to prevent a significant performance penalty on some
// GPU/drivers. GLRendererCopier manages such a cache and automatically frees
// the objects when it detects that a stream of CopyOutputRequests from a given
// "source" has ended.
class VIZ_SERVICE_EXPORT GLRendererCopier {
public:
// A callback that calls GLRenderer::MoveFromDrawToWindowSpace().
using ComputeWindowRectCallback =
base::RepeatingCallback<gfx::Rect(const gfx::Rect&)>;
// |texture_deleter| must outlive this instance.
GLRendererCopier(scoped_refptr<ContextProvider> context_provider,
TextureDeleter* texture_deleter,
ComputeWindowRectCallback window_rect_callback);
~GLRendererCopier();
// Executes the |request|, copying from the currently-bound framebuffer of the
// given |internal_format|. |output_rect| is the RenderPass's output Rect in
// draw space, and is used to translate and clip the result selection Rect.
// |framebuffer_texture| and |framebuffer_texture_size| are optional: When
// non-zero, the texture might be used as the source, to avoid making an extra
// copy of the framebuffer. |flipped_source| is true (common case) if the
// framebuffer content is vertically flipped. |color_space| specifies the
// color space of the pixels in the framebuffer.
//
// This implementation may change a wide variety of GL state, such as texture
// and framebuffer bindings, shader programs, and related attributes; and so
// the caller must not make any assumptions about the state of the GL context
// after this call.
void CopyFromTextureOrFramebuffer(std::unique_ptr<CopyOutputRequest> request,
const gfx::Rect& output_rect,
GLenum internal_format,
GLuint framebuffer_texture,
const gfx::Size& framebuffer_texture_size,
bool flipped_source,
const gfx::ColorSpace& color_space);
// Checks whether cached resources should be freed because recent copy
// activity is no longer using them. This should be called after a frame has
// finished drawing (after all copy requests have been executed).
void FreeUnusedCachedResources();
private:
friend class GLRendererCopierTest;
// The collection of resources that might be cached over multiple copy
// requests from the same source.
//
// TODO(crbug.com/781986): Post-impl clean-up to make this mechanism simpler.
struct VIZ_SERVICE_EXPORT CacheEntry {
uint32_t purge_count_at_last_use = 0;
std::array<GLuint, 8> object_names;
std::unique_ptr<GLHelper::ScalerInterface> scaler;
std::unique_ptr<I420Converter> i420_converter;
// Index in |object_names| of the various objects that might be cached.
static constexpr int kFramebufferCopyTexture = 0;
static constexpr int kResultTexture = 1;
static constexpr int kYPlaneTexture = 2;
static constexpr int kUPlaneTexture = 3;
static constexpr int kVPlaneTexture = 4;
static constexpr int kReadbackFramebuffer = 5;
static constexpr int kReadbackFramebufferU = 6;
static constexpr int kReadbackFramebufferV = 7;
CacheEntry();
CacheEntry(CacheEntry&&);
CacheEntry& operator=(CacheEntry&&);
~CacheEntry();
private:
DISALLOW_COPY_AND_ASSIGN(CacheEntry);
};
// Creates a texture and renders a transformed copy of the currently-bound
// framebuffer, according to the |request| parameters. This includes scaling.
// The result texture's content is always Y-flipped. The caller owns the
// returned texture.
GLuint RenderResultTexture(const CopyOutputRequest& request,
const gfx::Rect& framebuffer_copy_rect,
GLenum internal_format,
GLuint framebuffer_texture,
const gfx::Size& framebuffer_texture_size,
bool flipped_source,
const gfx::Rect& result_rect);
// Processes the next phase of the copy request by starting readback of the
// |copy_rect| from the given |source_texture| into a pixel transfer buffer.
// The source texture is assumed to be Y-flipped. This method does NOT take
// ownership of the |source_texture|.
void StartReadbackFromTexture(std::unique_ptr<CopyOutputRequest> request,
GLuint source_texture,
const gfx::Rect& copy_rect,
const gfx::Rect& result_rect,
const gfx::ColorSpace& color_space);
// Processes the next phase of the copy request by starting readback of the
// |copy_rect| from the currently-bound framebuffer into a pixel transfer
// buffer. The framebuffer content is assumed to be Y-flipped. This method
// kicks-off an asynchronous glReadPixels() workflow.
void StartReadbackFromFramebuffer(std::unique_ptr<CopyOutputRequest> request,
const gfx::Rect& copy_rect,
const gfx::Rect& result_rect,
const gfx::ColorSpace& color_space);
// Completes a copy request by packaging-up and sending the given
// |result_texture| in a mailbox. This method takes ownership of
// |result_texture|.
void SendTextureResult(std::unique_ptr<CopyOutputRequest> request,
GLuint result_texture,
const gfx::Rect& result_rect,
const gfx::ColorSpace& color_space);
// Processes the next phase of an I420_PLANES copy request by using the
// I420Converter to planarize the given |source_texture| and then starting
// readback of the planes via a pixel transfer buffer. The source texture is
// assumed to be Y-flipped. This method does NOT take ownership of the
// |source_texture|.
void StartI420ReadbackFromTexture(std::unique_ptr<CopyOutputRequest> request,
GLuint source_texture,
const gfx::Size& source_texture_size,
const gfx::Rect& copy_rect,
const gfx::Rect& result_rect,
const gfx::ColorSpace& color_space);
// Returns the GL object names found in the cache, or creates new ones
// on-demand. The caller takes ownership of the objects.
void TakeCachedObjectsOrCreate(const base::UnguessableToken& for_source,
int first,
int count,
GLuint* names);
// Stashes GL object names into the cache, or deletes the objects if they
// should not be cached.
void CacheObjectsOrDelete(const base::UnguessableToken& for_source,
int first,
int count,
const GLuint* names);
// Returns a cached scaler for the given request, or creates one on-demand.
// The scaler can be configured for source textures that may or may not be
// Y-flipped, but its results will always be Y-flipped.
std::unique_ptr<GLHelper::ScalerInterface> TakeCachedScalerOrCreate(
const CopyOutputRequest& for_request,
bool flipped_source);
// Stashes a scaler into the cache, or deletes it if it should not be cached.
void CacheScalerOrDelete(const base::UnguessableToken& for_source,
std::unique_ptr<GLHelper::ScalerInterface> scaler);
// Returns a cached I420 converter for the given source, or creates one
// on-demand.
std::unique_ptr<I420Converter> TakeCachedI420ConverterOrCreate(
const base::UnguessableToken& for_source);
// Stashes an I420 converter into the cache, or deletes it if it should not be
// cached.
void CacheI420ConverterOrDelete(
const base::UnguessableToken& for_source,
std::unique_ptr<I420Converter> i420_converter);
// Frees any objects currently stashed in the given CacheEntry.
void FreeCachedResources(CacheEntry* entry);
// Queries the GL implementation to determine which is the more performance-
// optimal supported readback format: GL_RGBA or GL_BGRA_EXT, and memoizes the
// result for all future calls.
//
// Precondition: The GL context has a complete, bound framebuffer ready for
// readback.
GLenum GetOptimalReadbackFormat();
// Injected dependencies.
const scoped_refptr<ContextProvider> context_provider_;
TextureDeleter* const texture_deleter_;
const ComputeWindowRectCallback window_rect_callback_;
// Provides comprehensive, quality and efficient scaling and other utilities.
GLHelper helper_;
// This increments by one for every call to FreeUnusedCachedResources(). It
// is meant to determine when cached resources should be freed because they
// are unlikely to see further use.
uint32_t purge_counter_ = 0;
// A cache of resources recently used in the execution of a stream of copy
// requests from the same source. Since this reflects the number of active
// video captures, it is expected to almost always be zero or one entry in
// size.
base::flat_map<base::UnguessableToken, CacheEntry> cache_;
// This specifies whether the GPU+driver combination executes readback more
// efficiently using GL_RGBA or GL_BGRA_EXT format. This starts out as
// GL_NONE, which means "unknown," and will be determined at the time the
// first readback request is made.
GLenum optimal_readback_format_ = static_cast<GLenum>(GL_NONE);
// Purge cache entries that have not been used after this many calls to
// FreeUnusedCachedResources(). The choice of 60 is arbitrary, but on most
// platforms means that a somewhat-to-fully active compositor will cause
// things to be auto-purged after approx. 1-2 seconds of not being used.
static constexpr int kKeepalivePeriod = 60;
DISALLOW_COPY_AND_ASSIGN(GLRendererCopier);
};
} // namespace viz
#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_COPIER_H_