blob: 341fc277ee5286b78a4324217f539cdccca555f2 [file] [log] [blame]
// Copyright (C) 2016 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 builder
import (
"fmt"
"sort"
"android.googlesource.com/platform/tools/gpu/framework/binary"
"android.googlesource.com/platform/tools/gpu/framework/log"
"android.googlesource.com/platform/tools/gpu/gapid/atom"
"android.googlesource.com/platform/tools/gpu/gapid/database"
"android.googlesource.com/platform/tools/gpu/gapid/gfxapi"
"android.googlesource.com/platform/tools/gpu/gapid/gfxapi/state"
"android.googlesource.com/platform/tools/gpu/gapid/image"
"android.googlesource.com/platform/tools/gpu/gapid/messages"
"android.googlesource.com/platform/tools/gpu/gapid/replay"
"android.googlesource.com/platform/tools/gpu/gapid/service"
"android.googlesource.com/platform/tools/gpu/gapid/service/path"
)
// GetFramebufferAttachment is a lazy that builds the *image.Info for a given
// framebuffer attachment.
type GetFramebufferAttachment struct {
binary.Generate
Device *path.Device
After *path.Atom
Attachment gfxapi.FramebufferAttachment
Settings service.RenderSettings
}
// BuildLazy returns the *service.ImageInfo resulting from the given
// GetFramebufferAttachment request.
func (r *GetFramebufferAttachment) BuildLazy(ctx log.Context, c interface{}, d database.Database) (interface{}, error) {
fbInfo, err := getFramebufferAttachmentInfo(ctx, r.After, r.Attachment, d)
if err != nil {
return nil, err
}
width, height := uniformScale(fbInfo.width, fbInfo.height, r.Settings.MaxWidth, r.Settings.MaxHeight)
data, err := database.Store(ctx, &queryFramebufferAttachment{
Device: r.Device,
After: r.After,
Width: width,
Height: height,
Attachment: r.Attachment,
WireframeMode: r.Settings.WireframeMode,
}, d)
if err != nil {
return nil, err
}
format := fbInfo.format
return &image.Info{
Width: width,
Height: height,
Format: format,
Data: &path.Blob{ID: data},
}, nil
}
type queryFramebufferAttachment struct {
binary.Generate
Device *path.Device
After *path.Atom
Width uint32
Height uint32
Attachment gfxapi.FramebufferAttachment
WireframeMode service.WireframeMode
}
// BuildLazy returns the []byte data for the given GetFramebufferAttachment
// request.
func (r *queryFramebufferAttachment) BuildLazy(ctx log.Context, c interface{}, d database.Database) (interface{}, error) {
mgr := c.(*Proxy).ReplayManager
intent := replay.Intent{
Device: r.Device.ID,
Capture: r.After.Atoms.Capture.ID,
}
after, err := ResolveAtom(ctx, r.After, d)
if err != nil {
return nil, err
}
api := after.API()
if api == nil {
return nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}
}
query, ok := api.(replay.QueryFramebufferAttachment)
if !ok {
ctx.Error().Logf("API %s does not implement QueryFramebufferAttachment", api.Name())
return nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}
}
wireframeMode := replay.WireframeMode_None
switch r.WireframeMode {
case service.WireframeMode_None:
case service.WireframeMode_All:
wireframeMode = replay.WireframeMode_All
case service.WireframeMode_Overlay:
wireframeMode = replay.WireframeMode_Overlay
default:
return nil, &service.ErrInvalidArgument{Reason: messages.ErrInvalidEnumValue(wireframeMode, "WireframeMode")}
}
res, err := query.QueryFramebufferAttachment(ctx, intent, mgr, atom.ID(r.After.Index), r.Width, r.Height, r.Attachment, wireframeMode)
if err != nil {
if _, ok := err.(*service.ErrDataUnavailable); ok {
return nil, err
}
return nil, ctx.WrapError(err, "Couldn't get framebuffer attachment")
}
return res.Data, nil
}
// getFramebufferAttachmentInfo returns the framebuffer dimensions and format
// after a given atom in the given capture, atom and attachment.
// The first call to getFramebufferInfo for a given capture/context
// will trigger a computation for all atoms of this capture, which will be
// cached to the database for subsequent calls, regardless of the given atom.
func getFramebufferAttachmentInfo(ctx log.Context, after *path.Atom, att gfxapi.FramebufferAttachment, d database.Database) (framebufferAttachmentInfo, error) {
obj, err := database.Build(ctx, &getFramebufferChangesLazy{capture: after.Atoms.Capture}, d)
if err != nil {
return framebufferAttachmentInfo{}, err
}
changes := obj.(*framebufferChanges)
info, err := changes.attachments[att].after(ctx, after.Index)
if err != nil {
return framebufferAttachmentInfo{}, err
}
if !info.valid {
return framebufferAttachmentInfo{}, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}
}
return info, nil
}
// framebufferChanges describes the list of attachment changes over the span of
// the entire capture.
type framebufferChanges struct {
binary.Generate
attachments []framebufferAttachmentChanges
}
// framebufferAttachmentChanges describes the list of changes to a single
// attachment over the span of the entire capture.
type framebufferAttachmentChanges struct {
binary.Generate
changes []framebufferAttachmentInfo
}
// framebufferAttachmentInfo describes the dimensions and format of a
// framebuffer attachment.
type framebufferAttachmentInfo struct {
binary.Generate
after uint64 // index of the last atom to change the attachment.
width uint32
height uint32
format image.Format
valid bool
}
func (c framebufferAttachmentChanges) after(ctx log.Context, i uint64) (framebufferAttachmentInfo, error) {
idx := sort.Search(len(c.changes), func(x int) bool { return c.changes[x].after > i }) - 1
if idx < 0 {
ctx.Warning().Logf("No dimension records found after atom %d. FB dimension records = %d", i, len(c.changes))
return framebufferAttachmentInfo{}, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}
}
return c.changes[idx], nil
}
func (c framebufferAttachmentChanges) last() framebufferAttachmentInfo {
if count := len(c.changes); count > 0 {
return c.changes[count-1]
}
return framebufferAttachmentInfo{}
}
var allFramebufferAttachments = []gfxapi.FramebufferAttachment{
gfxapi.FramebufferAttachment_Depth,
gfxapi.FramebufferAttachment_Stencil,
gfxapi.FramebufferAttachment_Color0,
gfxapi.FramebufferAttachment_Color1,
gfxapi.FramebufferAttachment_Color2,
gfxapi.FramebufferAttachment_Color3,
}
// getFramebufferInfoLazy is a lazy that builds a *framebufferChanges
type getFramebufferChangesLazy struct {
binary.Generate
capture *path.Capture
}
func (r *getFramebufferChangesLazy) BuildLazy(ctx log.Context, c interface{}, d database.Database) (interface{}, error) {
ctx = service.WithCaptureAndDatabase(ctx, r.capture, d)
var id atom.ID
defer func() {
if err := recover(); err != nil {
panic(fmt.Errorf("Panic at atom %d: %v", id, err))
}
}()
atoms, err := ResolveAtoms(ctx, r.capture.Atoms(), d)
if err != nil {
return nil, err
}
out := &framebufferChanges{
// TODO: Remove hardcoded upper limit
attachments: make([]framebufferAttachmentChanges, gfxapi.FramebufferAttachment_Color3+1),
}
s := state.New(ctx)
for i, a := range atoms {
id = atom.ID(i)
a.Mutate(ctx, s, d, nil /* no builder, just mutate */)
api := a.API()
for _, att := range allFramebufferAttachments {
info := framebufferAttachmentInfo{after: uint64(i)}
if api != nil {
if w, h, f, err := api.GetFramebufferAttachmentInfo(s, att); err == nil {
info.width, info.height, info.format, info.valid = w, h, f, true
}
}
if last := out.attachments[att].last(); last != info {
attachment := out.attachments[att]
attachment.changes = append(attachment.changes, info)
out.attachments[att] = attachment
}
}
}
return out, nil
}