OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "services/gfx/compositor/graph/scene_def.h" | |
6 | |
7 #include <ostream> | |
8 #include <sstream> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/logging.h" | |
12 #include "base/message_loop/message_loop.h" | |
13 #include "mojo/services/gfx/composition/cpp/logging.h" | |
14 #include "services/gfx/compositor/graph/snapshot.h" | |
15 #include "services/gfx/compositor/render/render_image.h" | |
16 #include "services/gfx/compositor/render/render_layer.h" | |
17 | |
18 namespace compositor { | |
19 | |
20 // TODO(jeffbrown): Determine and document a more appropriate size limit | |
21 // for transferred images as part of the image pipe abstraction instead. | |
22 static const int32_t kMaxTextureWidth = 65536; | |
23 static const int32_t kMaxTextureHeight = 65536; | |
24 | |
25 static void ReleaseMailboxTexture( | |
26 mojo::gfx::composition::MailboxTextureCallbackPtr callback) { | |
27 if (callback) | |
28 callback->OnMailboxTextureReleased(); | |
29 } | |
30 | |
31 SceneDef::SceneDef(mojo::gfx::composition::SceneTokenPtr scene_token, | |
32 const std::string& label) | |
33 : scene_token_(scene_token.Pass()), label_(label) { | |
34 DCHECK(scene_token_); | |
35 } | |
36 | |
37 SceneDef::~SceneDef() {} | |
38 | |
39 void SceneDef::EnqueueUpdate(mojo::gfx::composition::SceneUpdatePtr update) { | |
40 DCHECK(update); | |
41 pending_updates_.push_back(update.Pass()); | |
42 } | |
43 | |
44 void SceneDef::EnqueuePublish( | |
45 mojo::gfx::composition::SceneMetadataPtr metadata) { | |
46 DCHECK(metadata); | |
47 pending_publications_.emplace_back(new Publication(metadata.Pass())); | |
48 pending_updates_.swap(pending_publications_.back()->updates); | |
49 } | |
50 | |
51 SceneDef::Disposition SceneDef::Present( | |
52 int64_t presentation_time, | |
53 const SceneResolver& resolver, | |
54 const SceneUnavailableSender& unavailable_sender, | |
55 std::ostream& err) { | |
56 // Walk backwards through the pending publications to find the index | |
57 // just beyond the last one which is due to be presented at or before the | |
58 // presentation time. | |
59 size_t end = pending_publications_.size(); | |
60 for (;;) { | |
61 if (!end) | |
62 return Disposition::kUnchanged; | |
63 if (pending_publications_[end - 1]->is_due(presentation_time)) | |
64 break; // found last presentable publication | |
65 end--; | |
66 } | |
67 | |
68 // Prepare to apply all publications up to this point. | |
69 uint32_t version = pending_publications_[end - 1]->metadata->version; | |
70 if (version_ != version) { | |
71 version_ = version; | |
72 formatted_label_cache_.clear(); | |
73 } | |
74 Invalidate(); | |
75 | |
76 // Apply all updates sequentially. | |
77 for (size_t index = 0; index < end; ++index) { | |
78 for (auto& update : pending_publications_[index]->updates) { | |
79 if (!ApplyUpdate(update.Pass(), resolver, unavailable_sender, err)) | |
80 return Disposition::kFailed; | |
81 } | |
82 } | |
83 | |
84 // Dequeue the publications we processed. | |
85 pending_publications_.erase(pending_publications_.begin(), | |
86 pending_publications_.begin() + end); | |
87 | |
88 // Ensure the scene is in a valid state. | |
89 if (!Validate(err)) | |
90 return Disposition::kFailed; | |
91 return Disposition::kSucceeded; | |
92 } | |
93 | |
94 bool SceneDef::ApplyUpdate(mojo::gfx::composition::SceneUpdatePtr update, | |
95 const SceneResolver& resolver, | |
96 const SceneUnavailableSender& unavailable_sender, | |
97 std::ostream& err) { | |
98 DCHECK(update); | |
99 | |
100 // Update resources. | |
101 if (update->clear_resources) { | |
102 resources_.clear(); | |
103 } | |
104 for (auto it = update->resources.begin(); it != update->resources.end(); | |
105 ++it) { | |
106 uint32_t resource_id = it.GetKey(); | |
107 mojo::gfx::composition::ResourcePtr& resource_decl = it.GetValue(); | |
108 if (resource_decl) { | |
109 ResourceDef* resource = CreateResource(resource_id, resource_decl.Pass(), | |
110 resolver, unavailable_sender, err); | |
111 if (!resource) | |
112 return false; | |
113 resources_[resource_id].reset(resource); | |
114 } else { | |
115 resources_.erase(resource_id); | |
116 } | |
117 } | |
118 | |
119 // Update nodes. | |
120 if (update->clear_nodes) { | |
121 nodes_.clear(); | |
122 } | |
123 for (auto it = update->nodes.begin(); it != update->nodes.end(); ++it) { | |
124 uint32_t node_id = it.GetKey(); | |
125 mojo::gfx::composition::NodePtr& node_decl = it.GetValue(); | |
126 if (node_decl) { | |
127 NodeDef* node = CreateNode(node_id, node_decl.Pass(), err); | |
128 if (!node) | |
129 return false; | |
130 nodes_[node_id].reset(node); | |
131 } else { | |
132 nodes_.erase(node_id); | |
133 } | |
134 } | |
135 return true; | |
136 } | |
137 | |
138 bool SceneDef::Validate(std::ostream& err) { | |
139 // Validate all nodes. | |
140 // TODO(jeffbrown): Figure out how to do this incrementally if it gets | |
141 // too expensive to process all nodes each time. | |
142 root_node_ = nullptr; | |
143 for (auto& pair : nodes_) { | |
144 uint32_t node_id = pair.first; | |
145 NodeDef* node = pair.second.get(); | |
146 if (!node->Validate(this, err)) | |
147 return false; | |
148 if (node_id == mojo::gfx::composition::kSceneRootNodeId) | |
149 root_node_ = node; | |
150 } | |
151 return true; | |
152 } | |
153 | |
154 bool SceneDef::UnlinkReferencedScene( | |
155 SceneDef* scene, | |
156 const SceneUnavailableSender& unavailable_sender) { | |
157 DCHECK(scene); | |
158 | |
159 bool changed = false; | |
160 for (auto& pair : resources_) { | |
161 if (pair.second->type() == ResourceDef::Type::kScene) { | |
162 auto scene_resource = static_cast<SceneResourceDef*>(pair.second.get()); | |
163 if (scene_resource->referenced_scene() == scene) { | |
164 scene_resource->clear_referenced_scene(); | |
165 Invalidate(); | |
166 changed = true; | |
167 unavailable_sender.Run(pair.first); | |
abarth
2016/01/10 22:55:36
This really looks like a weak pointer with a callb
jeffbrown
2016/01/16 03:28:32
Perhaps, but I don't see a way to set a callback o
| |
168 } | |
169 } | |
170 } | |
171 return changed; | |
172 } | |
173 | |
174 bool SceneDef::Snapshot(SnapshotBuilder* snapshot_builder, | |
175 RenderLayerBuilder* layer_builder) { | |
176 DCHECK(snapshot_builder); | |
177 DCHECK(layer_builder); | |
178 | |
179 // Detect cycles. | |
180 if (visited_) { | |
181 if (snapshot_builder->block_log()) { | |
182 *snapshot_builder->block_log() | |
183 << "Scene blocked due to recursive cycle: " << FormattedLabel() | |
184 << std::endl; | |
185 } | |
186 return false; | |
187 } | |
188 | |
189 // Snapshot the contents of the scene. | |
190 visited_ = true; | |
191 bool success = SnapshotInner(snapshot_builder, layer_builder); | |
192 visited_ = false; | |
193 return success; | |
194 } | |
195 | |
196 bool SceneDef::SnapshotInner(SnapshotBuilder* snapshot_builder, | |
197 RenderLayerBuilder* layer_builder) { | |
198 // Note the dependency even if blocked. | |
199 snapshot_builder->AddSceneDependency(this); | |
200 | |
201 // Ensure we have a root node. | |
202 if (!root_node_) { | |
203 if (snapshot_builder->block_log()) { | |
204 *snapshot_builder->block_log() | |
205 << "Scene blocked due because it has no root node: " | |
206 << FormattedLabel() << std::endl; | |
207 } | |
208 return false; | |
209 } | |
210 | |
211 // Snapshot and draw the layer. | |
212 std::shared_ptr<RenderLayer> scene_layer = SnapshotLayer(snapshot_builder); | |
213 if (!scene_layer) | |
214 return false; | |
215 layer_builder->DrawLayer(scene_layer); | |
216 return true; | |
217 } | |
218 | |
219 std::shared_ptr<RenderLayer> SceneDef::SnapshotLayer( | |
220 SnapshotBuilder* snapshot_builder) { | |
221 if (cached_layer_) | |
222 return cached_layer_; | |
223 | |
224 RenderLayerBuilder scene_layer_builder; | |
225 scene_layer_builder.PushScene(scene_token_->value, version_); | |
226 if (!root_node_->Snapshot(snapshot_builder, &scene_layer_builder, this)) | |
227 return nullptr; | |
228 scene_layer_builder.PopScene(); | |
229 | |
230 // TODO(jeffbrown): Implement caching even when the scene has dependencies. | |
231 // There are some subtleties to be dealt with to ensure that caches | |
232 // are properly invalidated and that we don't accidentally cache layers which | |
233 // bake in decisions which counteract the intended cycle detection and | |
234 // avoidance behavior. Basically just need better bookkeeping. | |
abarth
2016/01/10 22:55:36
The DAG is making this harder for you. I'm not su
jeffbrown
2016/01/16 03:28:32
I think the DAG will be quite useful for cross-app
| |
235 std::shared_ptr<RenderLayer> scene_layer = scene_layer_builder.Build(); | |
236 if (!HasSceneResources()) | |
237 cached_layer_ = scene_layer; | |
238 return scene_layer; | |
239 } | |
240 | |
241 void SceneDef::Invalidate() { | |
242 cached_layer_.reset(); | |
243 } | |
244 | |
245 bool SceneDef::HasSceneResources() { | |
246 for (auto& pair : resources_) { | |
247 if (pair.second->type() == ResourceDef::Type::kScene) | |
248 return true; | |
249 } | |
250 return false; | |
251 } | |
252 | |
253 ResourceDef* SceneDef::CreateResource( | |
254 uint32_t resource_id, | |
255 mojo::gfx::composition::ResourcePtr resource_decl, | |
256 const SceneResolver& resolver, | |
257 const SceneUnavailableSender& unavailable_sender, | |
258 std::ostream& err) { | |
259 DCHECK(resource_decl); | |
260 | |
261 if (resource_decl->is_scene()) { | |
262 auto& scene_resource_decl = resource_decl->get_scene(); | |
263 DCHECK(scene_resource_decl->scene_token); | |
264 SceneDef* referenced_scene = | |
265 resolver.Run(scene_resource_decl->scene_token.get()); | |
266 if (!referenced_scene) { | |
267 unavailable_sender.Run(resource_id); | |
268 } | |
269 return new SceneResourceDef(referenced_scene); | |
270 } | |
271 | |
272 if (resource_decl->is_mailbox_texture()) { | |
273 auto& mailbox_texture_resource_decl = resource_decl->get_mailbox_texture(); | |
274 DCHECK(mailbox_texture_resource_decl->mailbox_name.size() == | |
275 GL_MAILBOX_SIZE_CHROMIUM); | |
276 DCHECK(mailbox_texture_resource_decl->size); | |
277 int32_t width = mailbox_texture_resource_decl->size->width; | |
278 int32_t height = mailbox_texture_resource_decl->size->height; | |
279 if (width < 1 || width > kMaxTextureWidth || height < 1 || | |
280 height > kMaxTextureHeight) { | |
281 err << "MailboxTexture resource has invalid size: " | |
282 << "resource_id=" << resource_id << ", width=" << width | |
283 << ", height=" << height; | |
284 return nullptr; | |
285 } | |
286 return new ImageResourceDef(RenderImage::FromMailboxTexture( | |
287 mailbox_texture_resource_decl->mailbox_name.data(), | |
288 mailbox_texture_resource_decl->sync_point, width, height, | |
289 base::MessageLoop::current()->task_runner(), | |
290 base::Bind( | |
291 &ReleaseMailboxTexture, | |
292 base::Passed(mailbox_texture_resource_decl->callback.Pass())))); | |
293 } | |
294 | |
295 err << "Unsupported resource type: resource_id=" << resource_id; | |
296 return nullptr; | |
297 } | |
298 | |
299 NodeDef* SceneDef::CreateNode(uint32_t node_id, | |
300 mojo::gfx::composition::NodePtr node_decl, | |
301 std::ostream& err) { | |
302 DCHECK(node_decl); | |
303 | |
304 NodeOp* op = nullptr; | |
305 if (node_decl->op) { | |
306 op = CreateNodeOp(node_id, node_decl->op.Pass(), err); | |
307 if (!op) | |
308 return nullptr; | |
309 } | |
310 | |
311 return new NodeDef(node_id, node_decl->content_transform.Pass(), | |
312 node_decl->content_clip.Pass(), node_decl->hit_id, | |
313 node_decl->combinator, node_decl->child_node_ids.storage(), | |
314 op); | |
315 } | |
316 | |
317 NodeOp* SceneDef::CreateNodeOp(uint32_t node_id, | |
318 mojo::gfx::composition::NodeOpPtr node_op_decl, | |
319 std::ostream& err) { | |
320 DCHECK(node_op_decl); | |
321 | |
322 if (node_op_decl->is_rect()) { | |
323 auto& rect_node_op_decl = node_op_decl->get_rect(); | |
324 DCHECK(rect_node_op_decl->content_rect); | |
325 DCHECK(rect_node_op_decl->color); | |
326 return new RectNodeOp(*rect_node_op_decl->content_rect, | |
327 *rect_node_op_decl->color); | |
328 } | |
329 | |
330 if (node_op_decl->is_image()) { | |
331 auto& image_node_op_decl = node_op_decl->get_image(); | |
332 DCHECK(image_node_op_decl->content_rect); | |
333 return new ImageNodeOp(*image_node_op_decl->content_rect, | |
334 image_node_op_decl->image_rect.Pass(), | |
335 image_node_op_decl->image_resource_id, | |
336 image_node_op_decl->blend.Pass()); | |
337 } | |
338 | |
339 if (node_op_decl->is_scene()) { | |
340 auto& scene_node_op_decl = node_op_decl->get_scene(); | |
341 return new SceneNodeOp(scene_node_op_decl->scene_resource_id, | |
342 scene_node_op_decl->scene_version); | |
343 } | |
344 | |
345 if (node_op_decl->is_layer()) { | |
346 auto& layer_node_op_decl = node_op_decl->get_layer(); | |
347 DCHECK(layer_node_op_decl->layer_size); | |
348 return new LayerNodeOp(*layer_node_op_decl->layer_size, | |
349 layer_node_op_decl->blend.Pass()); | |
350 } | |
351 | |
352 err << "Unsupported node op type: node_id=" << node_id | |
353 << ", node_op=" << node_op_decl; | |
354 return nullptr; | |
355 } | |
356 | |
357 NodeDef* SceneDef::FindNode(uint32_t node_id) { | |
358 auto it = nodes_.find(node_id); | |
359 return it != nodes_.end() ? it->second.get() : nullptr; | |
360 } | |
361 | |
362 ResourceDef* SceneDef::FindResource(uint32_t resource_id, | |
363 ResourceDef::Type resource_type) { | |
364 auto it = resources_.find(resource_id); | |
365 return it != resources_.end() && it->second->type() == resource_type | |
366 ? it->second.get() | |
367 : nullptr; | |
368 } | |
369 | |
370 std::string SceneDef::FormattedLabel() { | |
371 if (formatted_label_cache_.empty()) { | |
372 std::ostringstream s; | |
373 s << "<" << scene_token_->value; | |
374 if (!label_.empty()) | |
375 s << ":" << label_; | |
376 s << "/" << version_ << ">"; | |
377 formatted_label_cache_ = s.str(); | |
378 } | |
379 return formatted_label_cache_; | |
380 } | |
381 | |
382 SceneDef::Publication::Publication( | |
383 mojo::gfx::composition::SceneMetadataPtr metadata) | |
384 : metadata(metadata.Pass()) { | |
385 DCHECK(this->metadata); | |
386 } | |
387 | |
388 SceneDef::Publication::~Publication() {} | |
389 | |
390 } // namespace compositor | |
OLD | NEW |