blob: 3f8fdf20b39d4427eb2ce862c71e46691e5cea56 [file] [log] [blame]
// Copyright 2016 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/rasterizer/skia/surface_cache_delegate.h"
#include <algorithm>
#include <string>
#include "base/debug/trace_event.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/math/transform_2d.h"
#include "cobalt/math/vector2d_f.h"
#include "cobalt/math/vector3d_f.h"
#include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace skia {
SurfaceCacheDelegate::SurfaceCacheDelegate(
const CreateSkSurfaceFunction& create_sk_surface_function,
const math::Size& max_surface_size)
: create_sk_surface_function_(create_sk_surface_function),
max_surface_size_(max_surface_size),
draw_state_(NULL)
#if defined(ENABLE_DEBUG_CONSOLE)
,
toggle_cache_highlights_(false),
toggle_cache_highlights_command_handler_(
"toggle_cache_highlights",
base::Bind(&SurfaceCacheDelegate::OnToggleCacheHighlights,
base::Unretained(this)),
"Toggles cache highlights showing render tree nodes that are cached.",
"Will replace render tree node images with translucent red "
"rectangles so that cached nodes can be visualized on screen.")
#endif
{
}
void SurfaceCacheDelegate::UpdateCanvasScale() {
math::Matrix3F total_matrix =
SkiaMatrixToCobalt(draw_state_->render_target->getTotalMatrix());
math::Vector3dF column_0(total_matrix.column(0));
math::Vector3dF column_1(total_matrix.column(1));
scale_ = math::Vector2dF(column_0.Length(), column_1.Length());
}
#if defined(ENABLE_DEBUG_CONSOLE)
void SurfaceCacheDelegate::OnToggleCacheHighlights(const std::string& message) {
toggle_cache_highlights_ = !toggle_cache_highlights_;
}
#endif
void SurfaceCacheDelegate::ApplySurface(
common::SurfaceCache::CachedSurface* surface) {
TRACE_EVENT0("cobalt::renderer", "SurfaceCacheDelegate::ApplySurface()");
CachedSurface* skia_surface =
base::polymorphic_downcast<CachedSurface*>(surface);
if (!skia_surface->image()) {
return;
}
draw_state_->render_target->save();
SkMatrix total_matrix = draw_state_->render_target->getTotalMatrix();
// We "preScale()" the "post scale" because skia uses "pre" to mean that the
// transform will be applied before the other transforms, whereas
// common::OffscreenRenderCoordinateMapping() uses "post" to mean that the
// transform matrix should be post-multiplied by the existing matrix.
total_matrix.preScale(skia_surface->output_post_scale().x(),
skia_surface->output_post_scale().y());
total_matrix.postTranslate(skia_surface->output_pre_translate().x(),
skia_surface->output_pre_translate().y());
draw_state_->render_target->setMatrix(total_matrix);
SkPaint paint;
paint.setFilterLevel(SkPaint::kLow_FilterLevel);
if (draw_state_->opacity < 1.0f) {
paint.setAlpha(draw_state_->opacity * 255);
}
SkRect dest_rect = SkRect::MakeXYWH(skia_surface->output_bounds().x(),
skia_surface->output_bounds().y(),
skia_surface->output_bounds().width(),
skia_surface->output_bounds().height());
SkRect source_rect = SkRect::MakeWH(skia_surface->output_bounds().width(),
skia_surface->output_bounds().height());
#if defined(ENABLE_DEBUG_CONSOLE)
if (toggle_cache_highlights_) {
paint.setARGB(128, 255, 128, 128);
draw_state_->render_target->drawRect(dest_rect, paint);
} else // NOLINT(readability/braces)
#endif
{
draw_state_->render_target->drawImageRect(skia_surface->image(),
&source_rect, dest_rect, &paint);
}
draw_state_->render_target->restore();
}
void SurfaceCacheDelegate::StartRecording(const math::RectF& local_bounds) {
TRACE_EVENT0("cobalt::renderer", "SurfaceCacheDelegate::StartRecording()");
DCHECK(!recording_data_);
// If the scale is less than one, render to the offscreen surface as if the
// scale is 1, since the scale may be animating and this way we do not need
// to rerender the subtree each frame of the animation. If the scale is
// larger than 1, we cache it as is and if it is animating, performance will
// suffer.
math::Vector2dF inv_scale(scale_.x() < 1.0f ? 1.0f / scale_.x() : 1.0f,
scale_.y() < 1.0f ? 1.0f / scale_.y() : 1.0f);
math::Matrix3F total_matrix =
SkiaMatrixToCobalt(draw_state_->render_target->getTotalMatrix()) *
math::ScaleMatrix(inv_scale);
// Figure where we should render to the offscreen surface and then how to map
// that later to the onscreen surface when applying the cached surface. We
// ignore the viewport so that we cache all of partially on-screen surfaces.
common::OffscreenRenderCoordinateMapping coord_mapping =
common::GetOffscreenRenderCoordinateMapping(local_bounds, total_matrix,
base::optional<math::Rect>());
// If the output has an area of 0 then there is nothing to cache. This should
// not be common.
if (coord_mapping.output_bounds.size().GetArea() == 0) {
recording_data_.emplace(*draw_state_, coord_mapping,
static_cast<SkSurface*>(NULL));
return;
}
#if !defined(NDEBUG)
// Ensure that the surface we ultimately render to is not larger than what
// we are reporting with GetRenderSize().
math::Size render_size = GetRenderSize(local_bounds.size());
DCHECK_LE(coord_mapping.output_bounds.width(), render_size.width());
DCHECK_LE(coord_mapping.output_bounds.height(), render_size.height());
#endif
// Now create a SkSurface and set it up as the new canvas for our client to
// target.
SkSurface* surface = create_sk_surface_function_.Run(
math::Size(coord_mapping.output_bounds.width(),
coord_mapping.output_bounds.height()));
if (!surface) {
LOG(WARNING) << "Could not create a " << coord_mapping.output_bounds.width()
<< "x" << coord_mapping.output_bounds.height() << ".";
NOTREACHED();
}
recording_data_.emplace(*draw_state_, coord_mapping, surface);
SkCanvas* offscreen_canvas = surface->getCanvas();
draw_state_->render_target = offscreen_canvas;
draw_state_->opacity = 1.0f;
// Clear the draw area to RGBA(0, 0, 0, 0) for a fresh scratch surface before
// returning.
draw_state_->render_target->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode);
draw_state_->render_target->setMatrix(
CobaltMatrixToSkia(coord_mapping.sub_render_transform));
}
common::SurfaceCache::CachedSurface* SurfaceCacheDelegate::EndRecording() {
TRACE_EVENT0("cobalt::renderer", "SurfaceCacheDelegate::EndRecording()");
DCHECK(recording_data_);
*draw_state_ = recording_data_->original_draw_state;
// Save the results as a CachedSurface and return them.
CachedSurface* cached_surface =
new CachedSurface(recording_data_->surface,
recording_data_->coord_mapping.output_post_scale,
recording_data_->coord_mapping.output_pre_translate,
recording_data_->coord_mapping.output_bounds);
recording_data_ = base::nullopt;
return cached_surface;
}
void SurfaceCacheDelegate::ReleaseSurface(
common::SurfaceCache::CachedSurface* surface) {
TRACE_EVENT0("cobalt::renderer", "SurfaceCacheDelegate::ReleaseSurface()");
delete surface;
}
math::Size SurfaceCacheDelegate::GetRenderSize(const math::SizeF& local_size) {
// All surfaces scaled down would get rendered at their unscaled size, while
// surfaces scaled up will be rendered at their scaled size. This is so that
// we can keep in the cache items that have animating scale, as long as the
// scale is less than 1 (which is common). The cost is that we may use more
// memory than necessary for these down-scaled surfaces.
math::Vector2dF scale(std::max(1.0f, scale_.x()), std::max(1.0f, scale_.y()));
// Add 2.0f to account for any rounding out that may occur.
math::Size size(static_cast<int>(local_size.width() * scale.x() + 2.0f),
static_cast<int>(local_size.height() * scale.y() + 2.0f));
return size;
}
math::Size SurfaceCacheDelegate::MaximumSurfaceSize() {
return max_surface_size_;
}
} // namespace skia
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt