blob: 1f04c7ad0b24b56d3a2441341f0484900b70cd37 [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. 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 "starboard/configuration.h"
#if SB_API_VERSION >= 12 || SB_HAS(GLES2)
#include "cobalt/renderer/rasterizer/egl/hardware_rasterizer.h"
#include <memory>
#include "base/threading/thread_checker.h"
#include "base/trace_event/trace_event.h"
#include "cobalt/render_tree/filter_node.h"
#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
#include "cobalt/renderer/backend/egl/graphics_system.h"
#include "cobalt/renderer/backend/egl/texture.h"
#include "cobalt/renderer/backend/egl/utils.h"
#include "cobalt/renderer/egl_and_gles.h"
#include "cobalt/renderer/rasterizer/egl/draw_object_manager.h"
#include "cobalt/renderer/rasterizer/egl/graphics_state.h"
#include "cobalt/renderer/rasterizer/egl/offscreen_target_manager.h"
#include "cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h"
#include "cobalt/renderer/rasterizer/egl/shader_program_manager.h"
#include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
#include "cobalt/renderer/rasterizer/skia/gl_format_conversions.h"
#include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrContext.h"
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace egl {
class HardwareRasterizer::Impl {
public:
Impl(backend::GraphicsContext* graphics_context, int skia_atlas_width,
int skia_atlas_height, int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int offscreen_target_cache_size_in_bytes,
bool purge_skia_font_caches_on_destruction,
bool force_deterministic_rendering);
~Impl();
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
const Options& options);
void SubmitToFallbackRasterizer(
const scoped_refptr<render_tree::Node>& render_tree,
SkCanvas* fallback_render_target, const math::Matrix3F& transform,
const math::RectF& scissor, float opacity, uint32_t rasterize_flags);
render_tree::ResourceProvider* GetResourceProvider() {
return fallback_rasterizer_->GetResourceProvider();
}
int64_t GetFallbackRasterizeCount() { return fallback_rasterize_count_; }
void ResetGraphicsStateCache() {
graphics_state_->SetDirty();
GetFallbackContext()->resetContext();
}
void MakeCurrent() { graphics_context_->MakeCurrent(); }
void ReleaseContext() { graphics_context_->ReleaseCurrentContext(); }
private:
GrContext* GetFallbackContext() {
return fallback_rasterizer_->GetGrContext();
}
void ResetFallbackContextDuringFrame();
void FlushFallbackOffscreenDraws();
void RasterizeTree(const scoped_refptr<render_tree::Node>& render_tree,
backend::RenderTargetEGL* render_target,
const math::Rect& content_rect, bool clear_first);
sk_sp<SkSurface> CreateFallbackSurface(
bool force_deterministic_rendering,
const backend::RenderTarget* render_target);
std::unique_ptr<skia::HardwareRasterizer> fallback_rasterizer_;
std::unique_ptr<GraphicsState> graphics_state_;
std::unique_ptr<ShaderProgramManager> shader_program_manager_;
std::unique_ptr<OffscreenTargetManager> offscreen_target_manager_;
int64_t fallback_rasterize_count_;
backend::GraphicsContextEGL* graphics_context_;
THREAD_CHECKER(thread_checker_);
};
HardwareRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
int skia_atlas_width, int skia_atlas_height,
int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int offscreen_target_cache_size_in_bytes,
bool purge_skia_font_caches_on_destruction,
bool force_deterministic_rendering)
: fallback_rasterizer_(new skia::HardwareRasterizer(
graphics_context, skia_atlas_width, skia_atlas_height,
skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes,
purge_skia_font_caches_on_destruction,
force_deterministic_rendering)),
fallback_rasterize_count_(0),
graphics_context_(
base::polymorphic_downcast<backend::GraphicsContextEGL*>(
graphics_context)) {
backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_);
graphics_state_.reset(new GraphicsState());
shader_program_manager_.reset(new ShaderProgramManager());
offscreen_target_manager_.reset(new OffscreenTargetManager(
graphics_context_,
base::Bind(&HardwareRasterizer::Impl::CreateFallbackSurface,
base::Unretained(this), force_deterministic_rendering),
offscreen_target_cache_size_in_bytes));
if (force_deterministic_rendering) {
offscreen_target_manager_->SetCacheErrorThreshold(0.0f);
}
}
HardwareRasterizer::Impl::~Impl() {
backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_);
GL_CALL(glFinish());
offscreen_target_manager_.reset();
shader_program_manager_.reset();
graphics_state_.reset();
}
void HardwareRasterizer::Impl::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
const Options& options) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
backend::RenderTargetEGL* render_target_egl =
base::polymorphic_downcast<backend::RenderTargetEGL*>(
render_target.get());
// Skip rendering if we lost the surface. This can happen just before suspend
// on Android, so now we're just waiting for the suspend to clean up.
if (render_target_egl->is_surface_bad()) {
return;
}
backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_, render_target_egl);
// Update only the dirty pixels if the render target contents are preserved
// between frames.
bool clear_first = options.flags & Rasterizer::kSubmitFlags_Clear;
math::Rect content_rect(render_target->GetSize());
if (!clear_first && options.dirty &&
render_target_egl->ContentWasPreservedAfterSwap()) {
content_rect = *options.dirty;
}
offscreen_target_manager_->Update(render_target->GetSize());
RasterizeTree(render_tree, render_target_egl, content_rect, clear_first);
graphics_context_->SwapBuffers(render_target_egl);
// Reset the fallback context in case it is used between frames (e.g.
// to initialize images).
GetFallbackContext()->resetContext();
}
void HardwareRasterizer::Impl::SubmitToFallbackRasterizer(
const scoped_refptr<render_tree::Node>& render_tree,
SkCanvas* fallback_render_target, const math::Matrix3F& transform,
const math::RectF& scissor, float opacity, uint32_t rasterize_flags) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
TRACE_EVENT0("cobalt::renderer", "SubmitToFallbackRasterizer");
if (!scissor.IsExpressibleAsRect()) {
DLOG(WARNING) << "Invalid scissor of " << scissor.ToString()
<< " passed into SubmitToFallbackRasterizer.";
return;
}
// Use skia to rasterize to the allocated offscreen target.
fallback_render_target->save();
fallback_render_target->clipRect(SkRect::MakeXYWH(
scissor.x(), scissor.y(), scissor.width(), scissor.height()));
fallback_render_target->concat(skia::CobaltMatrixToSkia(transform));
if ((rasterize_flags & RenderTreeNodeVisitor::kFallbackShouldClear) != 0) {
fallback_render_target->clear(SK_ColorTRANSPARENT);
}
if (opacity < 1.0f) {
scoped_refptr<render_tree::Node> opacity_node = new render_tree::FilterNode(
render_tree::OpacityFilter(opacity), render_tree);
fallback_rasterizer_->SubmitOffscreen(opacity_node, fallback_render_target);
} else {
fallback_rasterizer_->SubmitOffscreen(render_tree, fallback_render_target);
}
if ((rasterize_flags & RenderTreeNodeVisitor::kFallbackShouldFlush) != 0) {
fallback_render_target->flush();
}
fallback_render_target->restore();
}
void HardwareRasterizer::Impl::FlushFallbackOffscreenDraws() {
TRACE_EVENT0("cobalt::renderer", "Skia Flush");
offscreen_target_manager_->Flush();
}
void HardwareRasterizer::Impl::ResetFallbackContextDuringFrame() {
// Perform a minimal reset of the fallback context. Only need to invalidate
// states that this rasterizer pollutes.
uint32_t untouched_states =
kMSAAEnable_GrGLBackendState | kStencil_GrGLBackendState |
kPixelStore_GrGLBackendState | kFixedFunction_GrGLBackendState |
kPathRendering_GrGLBackendState;
GetFallbackContext()->resetContext(~untouched_states & kAll_GrBackendState);
}
void HardwareRasterizer::Impl::RasterizeTree(
const scoped_refptr<render_tree::Node>& render_tree,
backend::RenderTargetEGL* render_target, const math::Rect& content_rect,
bool clear_first) {
DrawObjectManager draw_object_manager(
base::Bind(&HardwareRasterizer::Impl::ResetFallbackContextDuringFrame,
base::Unretained(this)),
base::Bind(&HardwareRasterizer::Impl::FlushFallbackOffscreenDraws,
base::Unretained(this)));
RenderTreeNodeVisitor visitor(
graphics_state_.get(), &draw_object_manager,
offscreen_target_manager_.get(),
base::Bind(&HardwareRasterizer::Impl::SubmitToFallbackRasterizer,
base::Unretained(this)),
fallback_rasterizer_->GetCachedCanvas(render_target), render_target,
content_rect);
// Traverse the render tree to populate the draw object manager.
{
TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
render_tree->Accept(&visitor);
}
fallback_rasterize_count_ += visitor.GetFallbackRasterizeCount();
graphics_state_->BeginFrame();
// Rasterize to offscreen targets using skia.
{
TRACE_EVENT0("cobalt::renderer", "OffscreenRasterize");
// Ensure the skia context is fully reset.
GetFallbackContext()->resetContext();
draw_object_manager.ExecuteOffscreenRasterize(
graphics_state_.get(), shader_program_manager_.get());
}
// Clear the dirty region of the render target.
graphics_state_->BindFramebuffer(render_target);
graphics_state_->Viewport(0, 0, render_target->GetSize().width(),
render_target->GetSize().height());
graphics_state_->Scissor(content_rect.x(), content_rect.y(),
content_rect.width(), content_rect.height());
if (clear_first) {
graphics_state_->Clear();
}
{
TRACE_EVENT0("cobalt::renderer", "OnscreenRasterize");
draw_object_manager.ExecuteOnscreenRasterize(graphics_state_.get(),
shader_program_manager_.get());
}
graphics_context_->ResetCurrentSurface();
graphics_state_->EndFrame();
}
sk_sp<SkSurface> HardwareRasterizer::Impl::CreateFallbackSurface(
bool force_deterministic_rendering,
const backend::RenderTarget* render_target) {
// Wrap the given render target in a new skia surface.
GrGLFramebufferInfo info;
info.fFBOID = render_target->GetPlatformHandle();
info.fFormat = skia::ConvertBaseGLFormatToSizedInternalFormat(GL_RGBA);
GrBackendRenderTarget skia_render_target(render_target->GetSize().width(),
render_target->GetSize().height(), 0,
0, info);
uint32_t flags = 0;
if (!force_deterministic_rendering) {
// Distance field fonts are known to result in non-deterministic graphical,
// since the output depends on the size of the glyph that enters the atlas
// first (which would get re-used for similarly but unequal sized
// subsequent glyphs).
flags = SkSurfaceProps::kUseDistanceFieldFonts_Flag;
}
SkSurfaceProps skia_surface_props(flags,
SkSurfaceProps::kLegacyFontHost_InitType);
return SkSurface::MakeFromBackendRenderTarget(
GetFallbackContext(), skia_render_target, kBottomLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, nullptr, &skia_surface_props);
}
HardwareRasterizer::HardwareRasterizer(
backend::GraphicsContext* graphics_context, int skia_atlas_width,
int skia_atlas_height, int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int offscreen_target_cache_size_in_bytes,
bool purge_skia_font_caches_on_destruction,
bool force_deterministic_rendering)
: impl_(new Impl(graphics_context, skia_atlas_width, skia_atlas_height,
skia_cache_size_in_bytes,
scratch_surface_cache_size_in_bytes,
offscreen_target_cache_size_in_bytes,
purge_skia_font_caches_on_destruction,
force_deterministic_rendering)) {}
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", "HardwareRasterizer::Submit");
impl_->Submit(render_tree, render_target, options);
}
render_tree::ResourceProvider* HardwareRasterizer::GetResourceProvider() {
return impl_->GetResourceProvider();
}
int64_t HardwareRasterizer::GetFallbackRasterizeCount() {
return impl_->GetFallbackRasterizeCount();
}
void HardwareRasterizer::ResetGraphicsStateCache() {
impl_->ResetGraphicsStateCache();
}
void HardwareRasterizer::MakeCurrent() { return impl_->MakeCurrent(); }
void HardwareRasterizer::ReleaseContext() { return impl_->ReleaseContext(); }
} // namespace egl
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt
#endif // SB_API_VERSION >= 12 || SB_HAS(GLES2)