blob: a5fbd1b85e9e1d7c7bd1156b9a0ceb707dcd8f02 [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/blitter/surface_cache_delegate.h"
#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/blitter/cobalt_blitter_conversions.h"
#include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
#include "starboard/blitter.h"
#if SB_HAS(BLITTER)
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace blitter {
SurfaceCacheDelegate::SurfaceCacheDelegate(SbBlitterDevice device,
SbBlitterContext context)
: device_(device),
context_(context)
#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
{
}
#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* blitter_surface =
base::polymorphic_downcast<CachedSurface*>(surface);
if (!SbBlitterIsSurfaceValid(blitter_surface->surface())) {
return;
}
// First, post-apply our scale to our current transform.
Transform apply_transform(render_state_->transform);
apply_transform.ApplyScale(blitter_surface->output_post_scale());
math::RectF output_rectf =
apply_transform.TransformRect(blitter_surface->output_bounds());
// We can simulate a "pre-multiply" by translation by offsetting the final
// output rectangle by the pre-translate, effectively resulting in the
// translation being applied last, as intended.
output_rectf.Offset(blitter_surface->output_pre_translate());
SbBlitterRect output_blitter_rect = RectFToBlitterRect(output_rectf);
#if defined(ENABLE_DEBUG_CONSOLE)
if (toggle_cache_highlights_) {
SbBlitterSetColor(context_, SbBlitterColorFromRGBA(255, 128, 128, 128));
SbBlitterSetBlending(context_, true);
SbBlitterFillRect(context_, output_blitter_rect);
} else // NOLINT(readability/braces)
#endif
{
// Render the cached surface to the output render target.
SbBlitterSetBlending(context_, true);
SbBlitterSetModulateBlitsWithColor(context_, false);
SbBlitterBlitRectToRect(
context_, blitter_surface->surface(),
SbBlitterMakeRect(0, 0, blitter_surface->output_bounds().width(),
blitter_surface->output_bounds().height()),
output_blitter_rect);
}
}
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 scale = render_state_->transform.scale();
math::Vector2dF inv_scale(scale.x() < 1.0f ? 1.0f / scale.x() : 1.0f,
scale.y() < 1.0f ? 1.0f / scale.y() : 1.0f);
Transform inv_scale_transform(render_state_->transform);
inv_scale_transform.ApplyScale(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, inv_scale_transform.ToMatrix(),
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(*render_state_, coord_mapping,
kSbBlitterInvalidSurface);
return;
}
// Now create a surface and set it up as the new render target for our client
// to target.
SbBlitterSurface surface = SbBlitterCreateRenderTargetSurface(
device_, coord_mapping.output_bounds.width(),
coord_mapping.output_bounds.height(), kSbBlitterSurfaceFormatRGBA8);
CHECK(SbBlitterIsSurfaceValid(surface));
// Setup our offscreen surface as the render target for use while recording.
SbBlitterRenderTarget offscreen_render_target =
SbBlitterGetRenderTargetFromSurface(surface);
CHECK(SbBlitterIsRenderTargetValid(offscreen_render_target));
recording_data_.emplace(*render_state_, coord_mapping, surface);
// Set our new render state to the recording render state.
render_state_ = set_render_state_function_.Run(RenderState(
offscreen_render_target, Transform(coord_mapping.sub_render_transform),
BoundsStack(context_, math::Rect(coord_mapping.output_bounds.size()))));
SbBlitterSetRenderTarget(context_, render_state_->render_target);
render_state_->bounds_stack.UpdateContext();
// Clear the draw area to RGBA(0, 0, 0, 0) for a fresh scratch surface before
// returning.
SbBlitterSetColor(context_, SbBlitterColorFromRGBA(0, 0, 0, 0));
SbBlitterSetBlending(context_, false);
SbBlitterFillRect(context_,
SbBlitterMakeRect(0, 0, coord_mapping.output_bounds.width(),
coord_mapping.output_bounds.height()));
}
common::SurfaceCache::CachedSurface* SurfaceCacheDelegate::EndRecording() {
TRACE_EVENT0("cobalt::renderer", "SurfaceCacheDelegate::EndRecording()");
DCHECK(recording_data_);
// Restore our original render state from before we started recording.
render_state_ =
set_render_state_function_.Run(recording_data_->original_render_state);
SbBlitterSetRenderTarget(context_, render_state_->render_target);
render_state_->bounds_stack.UpdateContext();
// 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) {
math::Size size(
static_cast<int>(
local_size.width() * render_state_->transform.scale().x() + 0.5f),
static_cast<int>(
local_size.height() * render_state_->transform.scale().y() + 0.5f));
return size;
}
math::Size SurfaceCacheDelegate::MaximumSurfaceSize() {
// The Blitter API does not have methods for determining the maximum surface
// size.
return math::Size(2048, 2048);
}
} // namespace blitter
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt
#endif // #if SB_HAS(BLITTER)