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