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