blob: eb2127433938ac1752ebf14e168c53e46ef62f8c [file] [log] [blame]
// 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"
"os"
"path/filepath"
"time"
"android.googlesource.com/platform/tools/gpu/atexit"
"android.googlesource.com/platform/tools/gpu/log"
"android.googlesource.com/platform/tools/gpu/service"
"android.googlesource.com/platform/tools/gpu/service/path"
"github.com/google/gxui"
"github.com/google/gxui/drivers/gl"
"github.com/google/gxui/math"
"github.com/google/gxui/themes/dark"
)
type Config struct {
DataPath string
Gapis string
GXUIDebug bool
InitialCapture string
ReplayDevice string
}
type app struct {
Config
}
func createPanels(appCtx *ApplicationContext, window gxui.Window) gxui.Control {
theme, driver := appCtx.theme, appCtx.theme.Driver()
vSplitter := theme.CreateSplitterLayout()
vSplitter.SetOrientation(gxui.Vertical)
{
holder := theme.CreatePanelHolder()
holder.AddPanel(CreateFramesPanel(appCtx), "Frames")
holder.AddPanel(CreateProfilerPanel(appCtx), "Profile")
vSplitter.AddChild(holder)
}
{
hSplitter := theme.CreateSplitterLayout()
hSplitter.SetOrientation(gxui.Horizontal)
{
holder := theme.CreatePanelHolder()
holder.AddPanel(CreateCommandsPanel(appCtx), "Commands")
hSplitter.AddChild(holder)
}
{
holder := theme.CreatePanelHolder()
holder.AddPanel(CreateColorBufferPanel(appCtx), "Color")
holder.AddPanel(CreateDepthBufferPanel(appCtx), "Depth")
holder.AddPanel(theme.CreateLinearLayout(), "Stencil")
hSplitter.AddChild(holder)
hSplitter.SetChildWeight(holder, 2.0)
}
vSplitter.AddChild(hSplitter)
vSplitter.SetChildWeight(hSplitter, 2.0)
}
{
hSplitter := theme.CreateSplitterLayout()
hSplitter.SetOrientation(gxui.Horizontal)
{
holder := theme.CreatePanelHolder()
holder.AddPanel(CreateStatePanel(appCtx), "State")
if appCtx.GXUIDebug {
holder.AddPanel(CreateGxuiDebug(appCtx, window, driver), "GXUI debug")
}
hSplitter.AddChild(holder)
}
{
holder := theme.CreatePanelHolder()
holder.AddPanel(CreateReportPanel(appCtx), "Report")
holder.AddPanel(CreateMemoryPanel(appCtx), "Memory")
holder.AddPanel(CreateImageViewerPanel(appCtx), "Image")
holder.AddPanel(CreateDocsPanel(appCtx), "Docs")
holder.AddPanel(CreateLogPanel(appCtx), "Log")
hSplitter.AddChild(holder)
}
vSplitter.AddChild(hSplitter)
}
return vSplitter
}
type capture struct {
path *path.Capture
info *service.Capture
}
type captureAdapter struct {
gxui.AdapterBase
items []capture
}
func (a *captureAdapter) Count() int {
return len(a.items)
}
func (a *captureAdapter) ItemAt(index int) gxui.AdapterItem {
return a.items[index].path
}
func (a *captureAdapter) ItemIndex(item gxui.AdapterItem) int {
p := item.(*path.Capture)
for i := range a.items {
if path.Equal(a.items[i].path, p) {
return i
}
}
return -1
}
func (a *captureAdapter) Create(theme gxui.Theme, index int) gxui.Control {
l := theme.CreateLabel()
l.SetText(a.items[index].info.Name)
return l
}
func (a *captureAdapter) Size(gxui.Theme) math.Size {
return math.Size{W: 250, H: 18}
}
func createCaptureList(appCtx *ApplicationContext) gxui.DropDownList {
adapter := &captureAdapter{}
list := appCtx.theme.CreateDropDownList()
list.SetBubbleOverlay(appCtx.dropDownOverlay)
list.SetAdapter(adapter)
list.OnSelectionChanged(func(item gxui.AdapterItem) {
appCtx.events.Select(item.(*path.Capture))
})
list.OnAttach(func() {
go func() {
for list.Attached() { // While the list control is visible
if captures, err := appCtx.rpc.GetCaptures(); err == nil {
appCtx.Run(func() {
adapter.items = captures
adapter.DataChanged()
})
}
time.Sleep(10 * time.Second)
}
}()
})
return list
}
type device struct {
path *path.Device
info *service.Device
}
type deviceAdapter struct {
gxui.AdapterBase
items []device
}
func (a *deviceAdapter) Count() int {
return len(a.items)
}
func (a *deviceAdapter) ItemAt(index int) gxui.AdapterItem {
return a.items[index].path
}
func (a *deviceAdapter) ItemIndex(item gxui.AdapterItem) int {
p := item.(*path.Device)
for i := range a.items {
if path.Equal(a.items[i].path, p) {
return i
}
}
return -1
}
func (a *deviceAdapter) Create(theme gxui.Theme, index int) gxui.Control {
l := theme.CreateLabel()
l.SetText(a.items[index].info.Name)
return l
}
func (a *deviceAdapter) Size(gxui.Theme) math.Size {
return math.Size{W: 250, H: 18}
}
func (d *device) String() string { return d.info.Name }
func createDeviceList(appCtx *ApplicationContext) gxui.DropDownList {
adapter := &deviceAdapter{}
list := appCtx.theme.CreateDropDownList()
list.SetBubbleOverlay(appCtx.dropDownOverlay)
list.SetAdapter(adapter)
list.OnSelectionChanged(func(item gxui.AdapterItem) {
appCtx.events.Select(item.(*path.Device))
})
list.OnAttach(func() {
go func() {
for list.Attached() { // While the list control is visible
if devices, err := appCtx.rpc.GetDevices(); err == nil {
appCtx.Run(func() {
adapter.items = devices
adapter.DataChanged()
if list.Selected() == nil && len(devices) > 0 {
list.Select(devices[0].path)
}
})
}
time.Sleep(10 * time.Second)
}
}()
})
return list
}
func createToolbar(appCtx *ApplicationContext) gxui.Control {
theme := appCtx.theme
takeCapture := theme.CreateButton()
takeCapture.SetText("Take capture")
takeCapture.OnClick(func(gxui.MouseEvent) {
CreateTakeCaptureDialog(appCtx)
})
captureLabel := theme.CreateLabel()
captureLabel.SetText("Capture: ")
captureList := createCaptureList(appCtx)
deviceLabel := theme.CreateLabel()
deviceLabel.SetText("Device: ")
deviceList := createDeviceList(appCtx)
layout := theme.CreateLinearLayout()
layout.SetDirection(gxui.LeftToRight)
layout.AddChild(takeCapture)
layout.AddChild(captureLabel)
layout.AddChild(captureList)
layout.AddChild(deviceLabel)
layout.AddChild(deviceList)
return layout
}
func createStatusBar(appCtx *ApplicationContext) gxui.Control {
theme := appCtx.theme
label := theme.CreateLabel()
appCtx.events.OnSelect(func(p path.Path) {
label.SetText(fmt.Sprintf("Selected: %v", p.Path()))
})
return label
}
type EnableDebugger interface {
EnableDebug(bool)
}
func (a app) main(driver gxui.Driver) {
if a.GXUIDebug {
if d, ok := driver.(EnableDebugger); ok {
d.EnableDebug(true)
}
}
theme := dark.CreateTheme(driver)
appCtx, err := CreateApplicationContext(theme, a.Config)
if err != nil {
fmt.Printf("Could not create application context: %v\n", err)
os.Exit(1)
}
// Create the log file
logPath, _ := filepath.Abs(filepath.Join(a.DataPath, "..", "logs", "client.log"))
fmt.Printf("Client log file created at: %s\n", logPath)
logFile, err := log.File(logPath)
if err != nil {
panic(err)
}
atexit.Register(func() { log.Close(logFile) }, time.Second)
appCtx.logger.Add(logFile)
window := theme.CreateWindow(800, 600, "Main")
layout := theme.CreateLinearLayout()
layout.SetDirection(gxui.TopToBottom)
layout.AddChild(createToolbar(appCtx))
layout.AddChild(createPanels(appCtx, window))
status := theme.CreateLinearLayout()
status.SetDirection(gxui.BottomToTop)
status.AddChild(createStatusBar(appCtx))
status.AddChild(layout)
window.OnClose(driver.Terminate)
window.AddChild(status)
window.AddChild(appCtx.dropDownOverlay)
window.AddChild(appCtx.toolTipOverlay)
if appCtx.InitialCapture != "" {
ImportCapture(appCtx, appCtx.InitialCapture, appCtx.logger)
}
appCtx.events.OnSelect(func(p path.Path) {
log.Infof(appCtx.logger, "Select '%v'", p)
})
}
func Run(config Config) {
app := app{Config: config}
gl.StartDriver(app.main)
}