blob: 2ea8e6e573731bed8500c0ec67492f96429c287f [file] [log] [blame]
/*
* Copyright 2014 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/hardware_rasterizer.h"
#include <algorithm>
#include "base/debug/trace_event.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
#include "cobalt/renderer/backend/egl/graphics_system.h"
#include "cobalt/renderer/frame_rate_throttler.h"
#include "cobalt/renderer/rasterizer/common/surface_cache.h"
#include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
#include "cobalt/renderer/rasterizer/skia/hardware_resource_provider.h"
#include "cobalt/renderer/rasterizer/skia/render_tree_node_visitor.h"
#include "cobalt/renderer/rasterizer/skia/scratch_surface_cache.h"
#include "cobalt/renderer/rasterizer/skia/surface_cache_delegate.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "third_party/skia/include/gpu/GrTexture.h"
#include "third_party/skia/include/gpu/SkGrPixelRef.h"
#include "third_party/skia/src/gpu/SkGpuDevice.h"
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace skia {
class HardwareRasterizer::Impl {
public:
Impl(backend::GraphicsContext* graphics_context, int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int surface_cache_size_in_bytes);
~Impl();
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
const Options& options);
render_tree::ResourceProvider* GetResourceProvider();
private:
class CachedScratchSurfaceHolder
: public RenderTreeNodeVisitor::ScratchSurface {
public:
CachedScratchSurfaceHolder(ScratchSurfaceCache* cache,
const math::Size& size)
: cached_scratch_surface_(cache, size) {}
SkSurface* GetSurface() OVERRIDE {
return cached_scratch_surface_.GetSurface();
}
private:
CachedScratchSurface cached_scratch_surface_;
};
SkSurface* CreateSkSurface(const math::Size& size);
scoped_ptr<RenderTreeNodeVisitor::ScratchSurface> CreateScratchSurface(
const math::Size& size);
void ResetSkiaState();
base::ThreadChecker thread_checker_;
backend::GraphicsContextEGL* graphics_context_;
scoped_ptr<render_tree::ResourceProvider> resource_provider_;
SkAutoTUnref<GrContext> gr_context_;
SkAutoTUnref<SkSurface> sk_output_surface_;
base::optional<ScratchSurfaceCache> scratch_surface_cache_;
base::optional<SurfaceCacheDelegate> surface_cache_delegate_;
base::optional<common::SurfaceCache> surface_cache_;
FrameRateThrottler frame_rate_throttler_;
};
namespace {
SkSurfaceProps GetRenderTargetSurfaceProps() {
return SkSurfaceProps(SkSurfaceProps::kUseDistanceFieldFonts_Flag,
SkSurfaceProps::kLegacyFontHost_InitType);
}
SkSurface* CreateSkiaRenderTargetSurface(GrRenderTarget* render_target) {
SkSurfaceProps surface_props = GetRenderTargetSurfaceProps();
return SkSurface::NewRenderTargetDirect(render_target, &surface_props);
}
// Takes meta-data from a Cobalt RenderTarget object and uses it to fill out
// a Skia backend render target descriptor. Additionally, it also references
// the actual render target object as well so that Skia can then recover
// the Cobalt render target object.
GrBackendRenderTargetDesc CobaltRenderTargetToSkiaBackendRenderTargetDesc(
cobalt::renderer::backend::RenderTarget* cobalt_render_target) {
const math::Size& size = cobalt_render_target->GetSize();
GrBackendRenderTargetDesc skia_desc;
skia_desc.fWidth = size.width();
skia_desc.fHeight = size.height();
skia_desc.fConfig = kRGBA_8888_GrPixelConfig;
skia_desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
skia_desc.fSampleCnt = 0;
skia_desc.fStencilBits = 0;
skia_desc.fRenderTargetHandle =
static_cast<GrBackendObject>(cobalt_render_target->GetPlatformHandle());
return skia_desc;
}
} // namespace
HardwareRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int surface_cache_size_in_bytes)
: graphics_context_(
base::polymorphic_downcast<backend::GraphicsContextEGL*>(
graphics_context)) {
TRACE_EVENT0("cobalt::renderer", "HardwareRasterizer::Impl::Impl()");
DLOG(INFO) << "skia_cache_size_in_bytes: " << skia_cache_size_in_bytes;
DLOG(INFO) << "scratch_surface_cache_size_in_bytes: "
<< scratch_surface_cache_size_in_bytes;
DLOG(INFO) << "surface_cache_size_in_bytes: " << surface_cache_size_in_bytes;
graphics_context_->MakeCurrent();
// Create a GrContext object that wraps the passed in Cobalt GraphicsContext
// object.
gr_context_.reset(GrContext::Create(
kCobalt_GrBackend,
reinterpret_cast<GrBackendContext>(graphics_context_)));
DCHECK(gr_context_);
// The GrContext manages a resource cache internally using GrResourceCache
// which by default caches 96MB of resources. This is used for helping with
// rendering shadow effects, gradient effects, and software rendered paths.
// As we have our own cache for most resources, set it to a much smaller value
// so Skia doesn't use too much GPU memory.
const int kSkiaCacheMaxResources = 128;
gr_context_->setResourceCacheLimits(kSkiaCacheMaxResources,
skia_cache_size_in_bytes);
base::Callback<SkSurface*(const math::Size&)> create_sk_surface_function =
base::Bind(&HardwareRasterizer::Impl::CreateSkSurface,
base::Unretained(this));
scratch_surface_cache_.emplace(create_sk_surface_function,
scratch_surface_cache_size_in_bytes);
// Setup a resource provider for resources to be used with a hardware
// accelerated Skia rasterizer.
resource_provider_.reset(
new HardwareResourceProvider(graphics_context_, gr_context_));
graphics_context_->ReleaseCurrentContext();
int max_surface_size = std::max(gr_context_->getMaxRenderTargetSize(),
gr_context_->getMaxTextureSize());
DLOG(INFO) << "Max renderer surface size: " << max_surface_size;
if (surface_cache_size_in_bytes > 0) {
surface_cache_delegate_.emplace(
create_sk_surface_function,
math::Size(max_surface_size, max_surface_size));
surface_cache_.emplace(&surface_cache_delegate_.value(),
surface_cache_size_in_bytes);
}
}
HardwareRasterizer::Impl::~Impl() {
graphics_context_->MakeCurrent();
sk_output_surface_.reset(NULL);
surface_cache_ = base::nullopt;
surface_cache_delegate_ = base::nullopt;
scratch_surface_cache_ = base::nullopt;
gr_context_.reset(NULL);
graphics_context_->ReleaseCurrentContext();
}
void HardwareRasterizer::Impl::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
const Options& options) {
DCHECK(thread_checker_.CalledOnValidThread());
backend::RenderTargetEGL* render_target_egl =
base::polymorphic_downcast<backend::RenderTargetEGL*>(
render_target.get());
backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_, render_target_egl);
// First reset the graphics context state for the pending render tree
// draw calls, in case we have modified state in between.
gr_context_->resetContext();
// Update our surface cache to do per-frame calculations such as deciding
// which render tree nodes are candidates for caching in this upcoming
// frame.
if (surface_cache_) {
surface_cache_->Frame();
}
if (!sk_output_surface_) {
// Setup a Skia render target that wraps the passed in Cobalt render target.
SkAutoTUnref<GrRenderTarget> skia_render_target(
gr_context_->wrapBackendRenderTarget(
CobaltRenderTargetToSkiaBackendRenderTargetDesc(
render_target.get())));
// Create an SkSurface from the render target so that we can acquire a
// SkCanvas object from it in Submit().
sk_output_surface_.reset(CreateSkiaRenderTargetSurface(skia_render_target));
}
// Get a SkCanvas that outputs to our hardware render target.
SkCanvas* canvas = sk_output_surface_->getCanvas();
canvas->save();
if (options.flags & Rasterizer::kSubmitFlags_Clear) {
canvas->clear(SkColorSetARGB(0, 0, 0, 0));
} else if (options.dirty) {
// Only a portion of the display is dirty. Reuse the previous frame
// if possible.
if (render_target_egl->IsContentPreservedOnSwap() &&
render_target_egl->swap_count() >= 3) {
canvas->clipRect(CobaltRectFToSkiaRect(*options.dirty));
}
}
{
TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
// Rasterize the passed in render tree to our hardware render target.
RenderTreeNodeVisitor::CreateScratchSurfaceFunction
create_scratch_surface_function =
base::Bind(&HardwareRasterizer::Impl::CreateScratchSurface,
base::Unretained(this));
RenderTreeNodeVisitor visitor(
canvas, &create_scratch_surface_function,
base::Bind(&HardwareRasterizer::Impl::ResetSkiaState,
base::Unretained(this)),
surface_cache_delegate_ ? &surface_cache_delegate_.value() : NULL,
surface_cache_ ? &surface_cache_.value() : NULL);
render_tree->Accept(&visitor);
}
{
TRACE_EVENT0("cobalt::renderer", "Skia Flush");
canvas->flush();
}
frame_rate_throttler_.EndInterval();
graphics_context_->SwapBuffers(render_target_egl);
frame_rate_throttler_.BeginInterval();
canvas->restore();
}
render_tree::ResourceProvider* HardwareRasterizer::Impl::GetResourceProvider() {
return resource_provider_.get();
}
SkSurface* HardwareRasterizer::Impl::CreateSkSurface(const math::Size& size) {
TRACE_EVENT2("cobalt::renderer", "HardwareRasterizer::CreateSkSurface()",
"width", size.width(), "height", size.height());
// Create a texture of the specified size. Then convert it to a render
// target and then convert that to a SkSurface which we return.
GrTextureDesc target_surface_desc;
target_surface_desc.fFlags =
kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
target_surface_desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
target_surface_desc.fWidth = size.width();
target_surface_desc.fHeight = size.height();
target_surface_desc.fConfig = kRGBA_8888_GrPixelConfig;
target_surface_desc.fSampleCnt = 0;
SkAutoTUnref<GrTexture> skia_texture(
gr_context_->createUncachedTexture(target_surface_desc, NULL, 0));
if (!skia_texture) {
// If we failed at creating a texture, try again using a different texture
// format.
target_surface_desc.fConfig = kBGRA_8888_GrPixelConfig;
skia_texture.reset(
gr_context_->createUncachedTexture(target_surface_desc, NULL, 0));
}
if (!skia_texture) {
return NULL;
}
GrRenderTarget* skia_render_target = skia_texture->asRenderTarget();
DCHECK(skia_render_target);
SkSurfaceProps surface_props = GetRenderTargetSurfaceProps();
return SkSurface::NewRenderTargetDirect(skia_render_target, &surface_props);
}
scoped_ptr<RenderTreeNodeVisitor::ScratchSurface>
HardwareRasterizer::Impl::CreateScratchSurface(const math::Size& size) {
TRACE_EVENT2("cobalt::renderer", "HardwareRasterizer::CreateScratchImage()",
"width", size.width(), "height", size.height());
scoped_ptr<CachedScratchSurfaceHolder> scratch_surface(
new CachedScratchSurfaceHolder(&scratch_surface_cache_.value(), size));
if (scratch_surface->GetSurface()) {
return scratch_surface.PassAs<RenderTreeNodeVisitor::ScratchSurface>();
} else {
return scoped_ptr<RenderTreeNodeVisitor::ScratchSurface>();
}
}
void HardwareRasterizer::Impl::ResetSkiaState() { gr_context_->resetContext(); }
HardwareRasterizer::HardwareRasterizer(
backend::GraphicsContext* graphics_context, int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes, int surface_cache_size_in_bytes)
: impl_(new Impl(graphics_context, skia_cache_size_in_bytes,
scratch_surface_cache_size_in_bytes,
surface_cache_size_in_bytes)) {}
HardwareRasterizer::~HardwareRasterizer() {}
void HardwareRasterizer::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
const Options& options) {
TRACE_EVENT0("cobalt::renderer", "Rasterizer::Submit()");
TRACE_EVENT0("cobalt::renderer", "HardwareRasterizer::Submit()");
impl_->Submit(render_tree, render_target, options);
}
render_tree::ResourceProvider* HardwareRasterizer::GetResourceProvider() {
return impl_->GetResourceProvider();
}
} // namespace skia
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt