blob: 7662e3cc3b577cc0f8f30e3f53cc2304211895c8 [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"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/renderer/backend/egl/utils.h"
#include "cobalt/renderer/rasterizer/egl/draw_callback.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::AddOnscreenDraw(scoped_ptr<DrawObject> draw_object,
BlendType blend_type, base::TypeId draw_type,
const backend::RenderTarget* render_target,
const math::RectF& draw_bounds) {
onscreen_draws_.push_back(DrawInfo(
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) {
// Put all draws using kBlendExternal into their own draw list since they
// use an external rasterizer which tracks its own state.
auto* draw_list = &offscreen_draws_;
if (blend_type == kBlendExternal) {
draw_list = &external_offscreen_draws_;
// Only the DrawCallback type should use kBlendExternal. All other draw
// object types use the native rasterizer.
DCHECK(base::polymorphic_downcast<DrawCallback*>(draw_object.get()));
}
draw_list->push_back(DrawInfo(
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(std::vector<DrawInfo>* 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::ExecuteOffscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
SortOffscreenDraws(&external_offscreen_draws_);
// Process draws handled by an external rasterizer.
{
TRACE_EVENT0("cobalt::renderer", "OffscreenExternalRasterizer");
for (auto draw = external_offscreen_draws_.begin();
draw != external_offscreen_draws_.end(); ++draw) {
draw->draw_object->ExecuteRasterize(graphics_state, program_manager);
}
if (!flush_external_offscreen_draws_.is_null()) {
flush_external_offscreen_draws_.Run();
}
}
SortOffscreenDraws(&offscreen_draws_);
SortOnscreenDraws(&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(offscreen_draws_, graphics_state, program_manager);
}
}
void DrawObjectManager::ExecuteUpdateVertexBuffer(
GraphicsState* graphics_state, ShaderProgramManager* program_manager) {
for (auto draw = offscreen_draws_.begin(); draw != offscreen_draws_.end();
++draw) {
draw->draw_object->ExecuteUpdateVertexBuffer(
graphics_state, program_manager);
}
for (auto draw = onscreen_draws_.begin(); draw != onscreen_draws_.end();
++draw) {
draw->draw_object->ExecuteUpdateVertexBuffer(
graphics_state, program_manager);
}
graphics_state->UpdateVertexData();
}
void DrawObjectManager::ExecuteOnscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
Rasterize(onscreen_draws_, graphics_state, program_manager);
}
void DrawObjectManager::Rasterize(const std::vector<DrawInfo>& 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 (auto draw = draw_list.begin(); draw != draw_list.end(); ++draw) {
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(std::vector<DrawInfo>* draw_list) {
TRACE_EVENT0("cobalt::renderer", "SortOffscreenDraws");
// Sort offscreen draws to minimize GPU state changes.
for (auto iter = draw_list->begin(); iter != draw_list->end(); ++iter) {
for (auto current_draw = iter; current_draw != draw_list->begin();
std::swap(*current_draw, *(current_draw - 1)), current_draw--) {
auto prev_draw = current_draw - 1;
// Unlike onscreen draws, offscreen draws should be grouped by
// render target.
if (prev_draw->render_target > current_draw->render_target) {
continue;
} else if (prev_draw->render_target < current_draw->render_target) {
break;
}
if (prev_draw->draw_bounds.Intersects(current_draw->draw_bounds)) {
break;
}
if (prev_draw->draw_type > current_draw->draw_type) {
continue;
} else if (prev_draw->draw_type < current_draw->draw_type) {
break;
}
if (prev_draw->blend_type <= current_draw->blend_type) {
break;
}
}
}
}
void DrawObjectManager::SortOnscreenDraws(std::vector<DrawInfo>* draw_list) {
TRACE_EVENT0("cobalt::renderer", "SortOnscreenDraws");
// Sort onscreen draws to minimize GPU state changes.
for (auto iter = draw_list->begin(); iter != draw_list->end(); ++iter) {
for (auto current_draw = iter; current_draw != draw_list->begin();
std::swap(*current_draw, *(current_draw - 1)), current_draw--) {
auto prev_draw = current_draw - 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 != current_draw->render_target) {
break;
}
if (prev_draw->draw_bounds.Intersects(current_draw->draw_bounds)) {
break;
}
// Group native vs. non-native draws together.
bool next_uses_same_rasterizer = current_draw + 1 != draw_list->end() &&
((current_draw + 1)->blend_type == kBlendExternal) ==
(current_draw->blend_type == kBlendExternal);
bool prev_uses_same_rasterizer =
(prev_draw->blend_type == kBlendExternal) ==
(current_draw->blend_type == kBlendExternal);
if (!next_uses_same_rasterizer && !prev_uses_same_rasterizer) {
continue;
}
if (next_uses_same_rasterizer && !prev_uses_same_rasterizer) {
break;
}
if (prev_draw->draw_type > current_draw->draw_type) {
continue;
} else if (prev_draw->draw_type < current_draw->draw_type) {
break;
}
if (prev_draw->blend_type <= current_draw->blend_type) {
break;
}
}
}
}
} // namespace egl
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt