Skip to content

Commit

Permalink
gapis/gles: Fix FB requests stalling forever.
Browse files Browse the repository at this point in the history
If you request a frame at a command that aborts, the DCE will remove that command from the execution list. The `readFramebuffer` transform then never sees the CmdID it's waiting for, and the request is never completed.

Instead, change `readFramebuffer` to keep an ordered list of requests. If the next command is skipped, read the framebuffer at the next command instead.
  • Loading branch information
ben-clayton committed Aug 2, 2017
1 parent 38ea2bc commit 5a963da
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 24 deletions.
5 changes: 5 additions & 0 deletions gapis/api/cmd_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ func (id CmdID) Derived() CmdID {
return id | derivedBit
}

// IsReal returns true if the id is not derived nor CmdNoID.
func (id CmdID) IsReal() bool {
return id != CmdNoID && (id&derivedBit) == 0
}

const derivedBit = CmdID(1 << 62)

func (id CmdID) String() string {
Expand Down
68 changes: 46 additions & 22 deletions gapis/api/gles/read_framebuffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package gles
import (
"context"
"fmt"
"sort"

"github.com/google/gapid/core/context/keys"
"github.com/google/gapid/core/data/binary"
Expand All @@ -31,49 +32,72 @@ import (
"github.com/google/gapid/gapis/service"
)

type readFbTask struct {
at api.CmdID
work func(ctx context.Context, w transform.Writer)
}

type readFramebuffer struct {
injections map[api.CmdID][]func(context.Context, api.Cmd, transform.Writer)
tasks []readFbTask
tasksSorted bool
}

func newReadFramebuffer(ctx context.Context) *readFramebuffer {
return &readFramebuffer{
injections: make(map[api.CmdID][]func(context.Context, api.Cmd, transform.Writer)),
return &readFramebuffer{}
}

func (t *readFramebuffer) addTask(at api.CmdID, work func(context.Context, transform.Writer)) {
t.tasks = append(t.tasks, readFbTask{at, work})
t.tasksSorted = false
}

func (t *readFramebuffer) sortTasks() {
if !t.tasksSorted {
sort.Slice(t.tasks, func(i, j int) bool { return t.tasks[i].at < t.tasks[j].at })
t.tasksSorted = true
}
}

func (t *readFramebuffer) Transform(ctx context.Context, id api.CmdID, cmd api.Cmd, out transform.Writer) {
out.MutateAndWrite(ctx, id, cmd)
if r, ok := t.injections[id]; ok {
for _, injection := range r {
injection(ctx, cmd, out)
if id.IsReal() {
t.sortTasks()
for len(t.tasks) > 0 && t.tasks[0].at < id {
t.tasks[0].work(ctx, out)
t.tasks = t.tasks[1:]
}
delete(t.injections, id)
}
out.MutateAndWrite(ctx, id, cmd)
}

func (t *readFramebuffer) Flush(ctx context.Context, out transform.Writer) {}
func (t *readFramebuffer) Flush(ctx context.Context, out transform.Writer) {
t.sortTasks()
for _, task := range t.tasks {
task.work(ctx, out)
}
t.tasks = nil
}

func (t *readFramebuffer) Depth(id api.CmdID, res replay.Result) {
t.injections[id] = append(t.injections[id], func(ctx context.Context, cmd api.Cmd, out transform.Writer) {
func (t *readFramebuffer) depth(id api.CmdID, thread uint64, res replay.Result) {
t.addTask(id, func(ctx context.Context, out transform.Writer) {
s := out.State()
width, height, format, err := GetState(s).getFramebufferAttachmentInfo(cmd.Thread(), api.FramebufferAttachment_Depth)
width, height, format, err := GetState(s).getFramebufferAttachmentInfo(thread, api.FramebufferAttachment_Depth)
if err != nil {
log.W(ctx, "Failed to read framebuffer after cmd %v: %v", id, err)
res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()})
return
}

postColorData(ctx, s, int32(width), int32(height), format, out, id, cmd, res)
postColorData(ctx, s, int32(width), int32(height), format, out, id, thread, res)
})
}

func (t *readFramebuffer) Color(id api.CmdID, width, height, bufferIdx uint32, res replay.Result) {
t.injections[id] = append(t.injections[id], func(ctx context.Context, cmd api.Cmd, out transform.Writer) {
func (t *readFramebuffer) color(id api.CmdID, thread uint64, width, height, bufferIdx uint32, res replay.Result) {
t.addTask(id, func(ctx context.Context, out transform.Writer) {
s := out.State()
c := GetContext(s, cmd.Thread())
c := GetContext(s, thread)

attachment := api.FramebufferAttachment_Color0 + api.FramebufferAttachment(bufferIdx)
w, h, fmt, err := GetState(s).getFramebufferAttachmentInfo(cmd.Thread(), attachment)
w, h, fmt, err := GetState(s).getFramebufferAttachmentInfo(thread, attachment)
if err != nil {
log.W(ctx, "Failed to read framebuffer after cmd %v: %v", id, err)
res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()})
Expand All @@ -93,7 +117,7 @@ func (t *readFramebuffer) Color(id api.CmdID, width, height, bufferIdx uint32, r
)

dID := id.Derived()
cb := CommandBuilder{Thread: cmd.Thread()}
cb := CommandBuilder{Thread: thread}
t := newTweaker(out, dID, cb)
t.glBindFramebuffer_Read(ctx, c.Bound.DrawFramebuffer.GetID())

Expand All @@ -113,7 +137,7 @@ func (t *readFramebuffer) Color(id api.CmdID, width, height, bufferIdx uint32, r
}

if inW == outW && inH == outH {
postColorData(ctx, s, outW, outH, fmt, out, id, cmd, res)
postColorData(ctx, s, outW, outH, fmt, out, id, thread, res)
} else {
t.glScissor(ctx, 0, 0, GLsizei(inW), GLsizei(inH))
framebufferID := t.glGenFramebuffer(ctx)
Expand All @@ -128,7 +152,7 @@ func (t *readFramebuffer) Color(id api.CmdID, width, height, bufferIdx uint32, r
)
t.glBindFramebuffer_Read(ctx, framebufferID)

postColorData(ctx, s, outW, outH, fmt, out, id, cmd, res)
postColorData(ctx, s, outW, outH, fmt, out, id, thread, res)
}

t.revert(ctx)
Expand All @@ -141,7 +165,7 @@ func postColorData(ctx context.Context,
sizedFormat GLenum,
out transform.Writer,
id api.CmdID,
cmd api.Cmd,
thread uint64,
res replay.Result) {

unsizedFormat, ty := getUnsizedFormatAndType(sizedFormat)
Expand All @@ -152,7 +176,7 @@ func postColorData(ctx context.Context,
}

dID := id.Derived()
cb := CommandBuilder{Thread: cmd.Thread()}
cb := CommandBuilder{Thread: thread}
t := newTweaker(out, dID, cb)
t.setPackStorage(ctx, PixelStorageState{Alignment: 1}, 0)

Expand Down
5 changes: 3 additions & 2 deletions gapis/api/gles/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,15 @@ func (a API) Replay(
// TODO: Remove this and handle swap-buffers better.
deadCodeElimination.Request(req.after - 1)

thread := cmds[req.after].Thread()
switch req.attachment {
case api.FramebufferAttachment_Depth:
readFramebuffer.Depth(req.after, rr.Result)
readFramebuffer.depth(req.after, thread, rr.Result)
case api.FramebufferAttachment_Stencil:
return fmt.Errorf("Stencil buffer attachments are not currently supported")
default:
idx := uint32(req.attachment - api.FramebufferAttachment_Color0)
readFramebuffer.Color(req.after, req.width, req.height, idx, rr.Result)
readFramebuffer.color(req.after, thread, req.width, req.height, idx, rr.Result)
}

cfg := cfg.(drawConfig)
Expand Down

0 comments on commit 5a963da

Please sign in to comment.