| /* |
| * 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 |