| // Copyright (C) 2015 The Android Open Source Project |
| // |
| // 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. |
| |
| package client |
| |
| import ( |
| "fmt" |
| |
| "android.googlesource.com/platform/tools/gpu/atom" |
| "android.googlesource.com/platform/tools/gpu/task" |
| "github.com/google/gxui" |
| "github.com/google/gxui/math" |
| ) |
| |
| type hierarchyItem struct { |
| rng atom.Range |
| } |
| |
| // hierarchyTreeNode is a gxui.TreeNode representing a range of atoms. |
| // It has the item of type hierarchyItem. |
| type hierarchyTreeNode struct { |
| ctx *commandAdapterCtx |
| group atom.Group |
| } |
| |
| func (n hierarchyTreeNode) Count() int { |
| return int(n.group.Count()) |
| } |
| |
| func (n hierarchyTreeNode) NodeAt(index int) gxui.TreeNode { |
| if id, subgroup := n.group.Index(uint64(index)); subgroup != nil { |
| return hierarchyTreeNode{ |
| ctx: n.ctx, |
| group: *subgroup, |
| } |
| } else { |
| return atomTreeNode{ |
| ctx: n.ctx, |
| item: atomItem{atomID: id}, |
| } |
| } |
| } |
| |
| func (n hierarchyTreeNode) Item() gxui.AdapterItem { |
| return hierarchyItem{rng: n.group.Range} |
| } |
| |
| func (n hierarchyTreeNode) ItemIndex(item gxui.AdapterItem) int { |
| var id atom.ID |
| |
| switch i := item.(type) { |
| case hierarchyItem: |
| id = i.rng.Start |
| case atomItem: |
| id = i.atomID |
| case observationItem: |
| id = i.atomID |
| default: |
| panic(fmt.Errorf("Unknown item type %T", i)) |
| } |
| |
| return int(n.group.IndexOf(id)) |
| } |
| |
| func (n hierarchyTreeNode) Create(theme gxui.Theme) gxui.Control { |
| layout := theme.CreateLinearLayout() |
| layout.SetDirection(gxui.LeftToRight) |
| |
| img := theme.CreateImage() |
| img.SetExplicitSize(math.Size{W: kCommandAdapterItemHeight, H: kCommandAdapterItemHeight}) |
| img.SetAspectMode(gxui.AspectCorrectLetterbox) |
| layout.AddChild(img) |
| |
| if ns, ok := n.ctx.timings.RangeDuration(n.group.Range); ok { |
| timeLbl := theme.CreateLabel() |
| milliseconds := float64(ns) / 1000000. |
| timeLbl.SetText(fmt.Sprintf("%6.3f ms ", milliseconds)) |
| switch { |
| case milliseconds >= 1.0: |
| timeLbl.SetColor(gxui.ColorFromHex(0xFFFC19 + 0xFF<<24)) |
| case milliseconds >= 5.0: |
| timeLbl.SetColor(gxui.ColorFromHex(0xD21212 + 0xFF<<24)) |
| } |
| layout.AddChild(timeLbl) |
| } |
| |
| atomID := n.group.Range.Last() |
| appCtx := n.ctx.appCtx |
| |
| t := task.New() |
| update := func() { |
| t.Cancel() |
| if n.ctx.device != nil { |
| if flags := n.ctx.atoms[atomID].Flags(); flags.IsDrawCall() || flags.IsEndOfFrame() { |
| after := n.ctx.capture.Atoms().Index(uint64(atomID)) |
| t.Run(updateThumbnail{appCtx, n.ctx.device, after, img}) |
| } |
| } |
| } |
| |
| // TODO: When trees can be updated, this should listen for capture / device changes. |
| img.OnAttach(update) |
| img.OnDetach(t.Cancel) |
| |
| label := theme.CreateLabel() |
| label.SetText(n.group.Name) |
| layout.AddChild(label) |
| |
| return layout |
| } |