blob: d12c4bc481c05f42ab96cb17c8d52266d5d61944 [file] [log] [blame]
// Copyright 2017 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/egl/draw_object_manager.h"
#include <algorithm>
#include "base/debug/trace_event.h"
#include "base/logging.h"
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace egl {
DrawObjectManager::DrawObjectManager(
const base::Closure& reset_external_rasterizer,
const base::Closure& flush_external_offscreen_draws)
: reset_external_rasterizer_(reset_external_rasterizer),
flush_external_offscreen_draws_(flush_external_offscreen_draws),
current_draw_id_(0) {}
uint32_t DrawObjectManager::AddBatchedExternalDraw(
scoped_ptr<DrawObject> draw_object, base::TypeId draw_type,
const backend::RenderTarget* render_target,
const math::RectF& draw_bounds) {
external_offscreen_draws_.emplace_back(draw_object.Pass(), draw_type,
kBlendExternal, render_target, draw_bounds, ++current_draw_id_);
return current_draw_id_;
}
uint32_t DrawObjectManager::AddOnscreenDraw(scoped_ptr<DrawObject> draw_object,
BlendType blend_type, base::TypeId draw_type,
const backend::RenderTarget* render_target,
const math::RectF& draw_bounds) {
// See if this draw object can be merged with the last one.
if (!onscreen_draws_.empty()) {
DrawInfo& last_draw = onscreen_draws_.back();
if (last_draw.render_target == render_target &&
last_draw.draw_type == draw_type &&
last_draw.blend_type == blend_type &&
last_draw.draw_object->TryMerge(draw_object.get())) {
last_draw.draw_bounds.Union(draw_bounds);
last_draw.draw_id = ++current_draw_id_;
return current_draw_id_;
}
}
onscreen_draws_.emplace_back(draw_object.Pass(), draw_type, blend_type,
render_target, draw_bounds, ++current_draw_id_);
return current_draw_id_;
}
uint32_t DrawObjectManager::AddOffscreenDraw(scoped_ptr<DrawObject> draw_object,
BlendType blend_type, base::TypeId draw_type,
const backend::RenderTarget* render_target,
const math::RectF& draw_bounds) {
// See if this draw object can be merged with the last one.
if (!offscreen_draws_.empty()) {
DrawInfo& last_draw = offscreen_draws_.back();
if (last_draw.render_target == render_target &&
last_draw.draw_type == draw_type &&
last_draw.blend_type == blend_type &&
last_draw.draw_object->TryMerge(draw_object.get())) {
last_draw.draw_bounds.Union(draw_bounds);
last_draw.draw_id = ++current_draw_id_;
return current_draw_id_;
}
}
offscreen_draws_.emplace_back(draw_object.Pass(), draw_type, blend_type,
render_target, draw_bounds, ++current_draw_id_);
return current_draw_id_;
}
void DrawObjectManager::RemoveDraws(uint32_t last_valid_draw_id) {
TRACE_EVENT0("cobalt::renderer", "RemoveDraws");
RemoveDraws(&onscreen_draws_, last_valid_draw_id);
RemoveDraws(&offscreen_draws_, last_valid_draw_id);
RemoveDraws(&external_offscreen_draws_, last_valid_draw_id);
}
void DrawObjectManager::RemoveDraws(DrawList* draw_list,
uint32_t last_valid_draw_id) {
// Objects in the draw list should have ascending draw IDs at this point.
auto iter = draw_list->end();
for (; iter != draw_list->begin(); --iter) {
if ((iter - 1)->draw_id <= last_valid_draw_id) {
break;
}
}
if (iter != draw_list->end()) {
draw_list->erase(iter, draw_list->end());
}
}
void DrawObjectManager::AddRenderTargetDependency(
const backend::RenderTarget* draw_target,
const backend::RenderTarget* required_target) {
// Check for circular dependencies.
DCHECK(draw_target != required_target);
// There should be very few unique render target dependencies, so just keep
// them in a vector for simplicity.
for (auto dep = draw_dependencies_.begin();; ++dep) {
if (dep == draw_dependencies_.end()) {
draw_dependencies_.emplace_back(draw_target, required_target);
auto count = dependency_count_.find(draw_target->GetSerialNumber());
if (count != dependency_count_.end()) {
count->second += 1;
} else {
dependency_count_.emplace(draw_target->GetSerialNumber(), 1);
}
break;
}
if (dep->draw_target == draw_target &&
dep->required_target == required_target) {
return;
}
}
// Propagate dependencies upward so that targets which depend on
// |draw_target| will also depend on |required_target|.
size_t count = draw_dependencies_.size();
for (size_t index = 0; index < count; ++index) {
if (draw_dependencies_[index].required_target == draw_target) {
AddRenderTargetDependency(draw_dependencies_[index].draw_target,
required_target);
}
}
}
void DrawObjectManager::ExecuteOffscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
SortOffscreenDraws(external_offscreen_draws_,
&sorted_external_offscreen_draws_);
// Process draws handled by an external rasterizer.
{
TRACE_EVENT0("cobalt::renderer", "OffscreenExternalRasterizer");
for (const DrawInfo* draw : sorted_external_offscreen_draws_) {
// Batched external draws should not have any dependencies.
DCHECK_EQ(draw->dependencies, 0);
draw->draw_object->ExecuteRasterize(graphics_state, program_manager);
}
if (!flush_external_offscreen_draws_.is_null()) {
flush_external_offscreen_draws_.Run();
}
}
SortOffscreenDraws(offscreen_draws_, &sorted_offscreen_draws_);
SortOnscreenDraws(onscreen_draws_, &sorted_onscreen_draws_);
// Update the vertex buffer for all draws.
{
TRACE_EVENT0("cobalt::renderer", "UpdateVertexBuffer");
ExecuteUpdateVertexBuffer(graphics_state, program_manager);
}
// Process the native offscreen draws.
{
TRACE_EVENT0("cobalt::renderer", "OffscreenNativeRasterizer");
Rasterize(sorted_offscreen_draws_, graphics_state, program_manager);
}
}
void DrawObjectManager::ExecuteUpdateVertexBuffer(
GraphicsState* graphics_state, ShaderProgramManager* program_manager) {
for (const DrawInfo* draw : sorted_offscreen_draws_) {
draw->draw_object->ExecuteUpdateVertexBuffer(
graphics_state, program_manager);
}
for (const DrawInfo* draw : sorted_onscreen_draws_) {
draw->draw_object->ExecuteUpdateVertexBuffer(
graphics_state, program_manager);
}
graphics_state->UpdateVertexBuffers();
}
void DrawObjectManager::ExecuteOnscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
Rasterize(sorted_onscreen_draws_, graphics_state, program_manager);
}
void DrawObjectManager::Rasterize(const SortedDrawList& sorted_draw_list,
GraphicsState* graphics_state, ShaderProgramManager* program_manager) {
const backend::RenderTarget* current_target = nullptr;
bool using_native_rasterizer = true;
// Starting from an unknown state.
graphics_state->SetDirty();
for (const DrawInfo* draw : sorted_draw_list) {
bool draw_uses_native_rasterizer = draw->blend_type != kBlendExternal;
if (draw_uses_native_rasterizer) {
if (!using_native_rasterizer) {
// Transitioning from external to native rasterizer. Set the native
// rasterizer's state as dirty.
graphics_state->SetDirty();
}
if (draw->render_target != current_target) {
current_target = draw->render_target;
graphics_state->BindFramebuffer(current_target);
graphics_state->Viewport(0, 0,
current_target->GetSize().width(),
current_target->GetSize().height());
}
if (draw->blend_type == kBlendNone) {
graphics_state->DisableBlend();
} else if (draw->blend_type == kBlendSrcAlpha) {
graphics_state->EnableBlend();
} else {
NOTREACHED() << "Unsupported blend type";
}
} else {
if (using_native_rasterizer) {
// Transitioning from native to external rasterizer. Set the external
// rasterizer's state as dirty.
if (!reset_external_rasterizer_.is_null()) {
reset_external_rasterizer_.Run();
}
}
}
draw->draw_object->ExecuteRasterize(graphics_state, program_manager);
using_native_rasterizer = draw_uses_native_rasterizer;
}
}
void DrawObjectManager::SortOffscreenDraws(const DrawList& draw_list,
SortedDrawList* sorted_draw_list) {
TRACE_EVENT0("cobalt::renderer", "SortOffscreenDraws");
// Sort offscreen draws to minimize GPU state changes.
sorted_draw_list->reserve(draw_list.size());
for (size_t draw_pos = 0; draw_pos < draw_list.size(); ++draw_pos) {
auto* draw = &draw_list[draw_pos];
bool draw_uses_native_rasterizer = draw->blend_type != kBlendExternal;
bool next_uses_native_rasterizer = draw_pos + 1 < draw_list.size() &&
draw_list[draw_pos + 1].blend_type != kBlendExternal;
auto dependencies =
dependency_count_.find(draw->render_target->GetSerialNumber());
draw->dependencies =
dependencies != dependency_count_.end() ? dependencies->second : 0;
// Find an appropriate sort position for the current draw.
auto sort_pos = draw_pos;
for (; sort_pos > 0; --sort_pos) {
auto* prev_draw = sorted_draw_list->at(sort_pos - 1);
// Unlike onscreen draws, offscreen draws should be grouped by render
// target. Ensure that render targets with fewer dependencies are first
// in the draw list.
if (prev_draw->dependencies > draw->dependencies) {
continue;
} else if (prev_draw->dependencies < draw->dependencies) {
break;
}
if (prev_draw->render_target > draw->render_target) {
continue;
} else if (prev_draw->render_target < draw->render_target) {
break;
}
// The rest of the sorting logic is the same between onscreen and
// offscreen draws.
if (prev_draw->draw_bounds.Intersects(draw->draw_bounds)) {
break;
}
// Group native vs. non-native draws together.
bool prev_uses_native_rasterizer =
prev_draw->blend_type != kBlendExternal;
if (draw_uses_native_rasterizer != next_uses_native_rasterizer &&
draw_uses_native_rasterizer != prev_uses_native_rasterizer) {
next_uses_native_rasterizer = prev_uses_native_rasterizer;
continue;
}
if (draw_uses_native_rasterizer == next_uses_native_rasterizer &&
draw_uses_native_rasterizer != prev_uses_native_rasterizer) {
break;
}
next_uses_native_rasterizer = prev_uses_native_rasterizer;
if (prev_draw->draw_type > draw->draw_type) {
continue;
} else if (prev_draw->draw_type < draw->draw_type) {
break;
}
if (prev_draw->blend_type <= draw->blend_type) {
break;
}
}
sorted_draw_list->insert(sorted_draw_list->begin() + sort_pos, draw);
}
}
void DrawObjectManager::SortOnscreenDraws(const DrawList& draw_list,
SortedDrawList* sorted_draw_list) {
TRACE_EVENT0("cobalt::renderer", "SortOnscreenDraws");
// Sort onscreen draws to minimize GPU state changes.
sorted_draw_list->reserve(draw_list.size());
for (size_t draw_pos = 0; draw_pos < draw_list.size(); ++draw_pos) {
auto* draw = &draw_list[draw_pos];
bool draw_uses_native_rasterizer = draw->blend_type != kBlendExternal;
bool next_uses_native_rasterizer = draw_pos + 1 < draw_list.size() &&
draw_list[draw_pos + 1].blend_type != kBlendExternal;
// Find an appropriate sort position for the current draw.
auto sort_pos = draw_pos;
for (; sort_pos > 0; --sort_pos) {
auto* prev_draw = sorted_draw_list->at(sort_pos - 1);
// Do not sort across different render targets since their contents may
// be generated just before consumed by a subsequent draw.
if (prev_draw->render_target != draw->render_target) {
break;
}
// The rest of the sorting logic is the same between onscreen and
// offscreen draws.
if (prev_draw->draw_bounds.Intersects(draw->draw_bounds)) {
break;
}
// Group native vs. non-native draws together.
bool prev_uses_native_rasterizer =
prev_draw->blend_type != kBlendExternal;
if (draw_uses_native_rasterizer != next_uses_native_rasterizer &&
draw_uses_native_rasterizer != prev_uses_native_rasterizer) {
next_uses_native_rasterizer = prev_uses_native_rasterizer;
continue;
}
if (draw_uses_native_rasterizer == next_uses_native_rasterizer &&
draw_uses_native_rasterizer != prev_uses_native_rasterizer) {
break;
}
next_uses_native_rasterizer = prev_uses_native_rasterizer;
if (prev_draw->draw_type > draw->draw_type) {
continue;
} else if (prev_draw->draw_type < draw->draw_type) {
break;
}
if (prev_draw->blend_type <= draw->blend_type) {
break;
}
}
sorted_draw_list->insert(sorted_draw_list->begin() + sort_pos, draw);
}
}
} // namespace egl
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt