From 0201216bb12cf131c0be22766267a6b3fa9f26e7 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 11 Oct 2017 00:28:54 +0100 Subject: [PATCH] gles: Rework read_texture.go and read_framebuffer.go to handle MS. Unify the framebuffer read-back logic for color and depth maps. Add support for multisample resolving. Use this logic for texture read-backs too. --- gapis/api/gles/gles.go | 8 +- gapis/api/gles/read_framebuffer.go | 181 +++++++++++++++++------------ gapis/api/gles/read_texture.go | 71 ++++++----- gapis/api/gles/state.go | 85 ++++++++++---- 4 files changed, 212 insertions(+), 133 deletions(-) diff --git a/gapis/api/gles/gles.go b/gapis/api/gles/gles.go index d93fbf36ff..013c9fcf7e 100644 --- a/gapis/api/gles/gles.go +++ b/gapis/api/gles/gles.go @@ -159,16 +159,16 @@ func GetFramebufferAttachmentInfoByID( fb = c.Bound.DrawFramebuffer.GetID() } - w, h, sizedFormat, err := s.getFramebufferAttachmentInfo(thread, fb, attachment) - if sizedFormat == 0 { + fbai, err := s.getFramebufferAttachmentInfo(thread, fb, attachment) + if fbai.format == 0 { return 0, 0, 0, nil, fmt.Errorf("No format set") } if err != nil { return 0, 0, 0, nil, err } - fmt, ty := getUnsizedFormatAndType(sizedFormat) + fmt, ty := getUnsizedFormatAndType(fbai.format) f, err := getImageFormat(fmt, ty) - return w, h, 0, f, err + return fbai.width, fbai.height, 0, f, err } // Context returns the active context for the given state and thread. diff --git a/gapis/api/gles/read_framebuffer.go b/gapis/api/gles/read_framebuffer.go index 67d8ea6f90..ff97a0de83 100644 --- a/gapis/api/gles/read_framebuffer.go +++ b/gapis/api/gles/read_framebuffer.go @@ -55,29 +55,7 @@ func (t *readFramebuffer) depth( res replay.Result) { t.Add(id, func(ctx context.Context, out transform.Writer) { - s := out.State() - - if fb == 0 { - var err error - if fb, err = getBoundFramebufferID(thread, s); err != nil { - log.W(ctx, "Could not read framebuffer after cmd %v: err", err) - res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}) - return - } - } - - width, height, format, err := GetState(s).getFramebufferAttachmentInfo(thread, fb, 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 - } - - t := newTweaker(out, id.Derived(), CommandBuilder{Thread: thread}) - defer t.revert(ctx) - t.glBindFramebuffer_Read(ctx, fb) - - postColorData(ctx, s, int32(width), int32(height), format, out, id, thread, res) + postFBData(ctx, id, thread, 0, 0, fb, api.FramebufferAttachment_Depth, out, res) }) } @@ -90,44 +68,81 @@ func (t *readFramebuffer) color( res replay.Result) { t.Add(id, func(ctx context.Context, out transform.Writer) { - s := out.State() - c := GetContext(s, thread) - - if fb == 0 { - var err error - if fb, err = getBoundFramebufferID(thread, s); err != nil { - log.W(ctx, "Could not read framebuffer after cmd %v: err", err) - res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}) - return - } - } - attachment := api.FramebufferAttachment_Color0 + api.FramebufferAttachment(bufferIdx) - w, h, fmt, err := GetState(s).getFramebufferAttachmentInfo(thread, fb, attachment) - if err != nil { - log.W(ctx, "Failed to read framebuffer after cmd %v: %v", id, err) - res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}) - return - } - if fmt == 0 { - log.W(ctx, "Failed to read framebuffer after cmd %v: no image format", id) + postFBData(ctx, id, thread, width, height, fb, attachment, out, res) + }) +} + +func postFBData(ctx context.Context, + id api.CmdID, + thread uint64, + width, height uint32, + fb FramebufferId, + attachment api.FramebufferAttachment, + out transform.Writer, + res replay.Result) { + + s := out.State() + c := GetContext(s, thread) + + if fb == 0 { + var err error + if fb, err = getBoundFramebufferID(thread, s); err != nil { + log.W(ctx, "Could not read framebuffer after cmd %v: err", err) res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}) return } + } - var ( - inW = int32(w) - inH = int32(h) - outW = int32(width) - outH = int32(height) - ) + fbai, err := GetState(s).getFramebufferAttachmentInfo(thread, fb, attachment) + if err != nil { + log.W(ctx, "Failed to read framebuffer after cmd %v: %v", id, err) + res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}) + return + } + if fbai.format == 0 { + log.W(ctx, "Failed to read framebuffer after cmd %v: no image format", id) + res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}) + return + } + glAtt, err := attachmentToEnum(attachment) + if err != nil { + log.W(ctx, "Failed to get attachment as GLenum: %v", err) + res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()}) + return + } - dID := id.Derived() - cb := CommandBuilder{Thread: thread} - t := newTweaker(out, dID, cb) - defer t.revert(ctx) - t.glBindFramebuffer_Read(ctx, fb) + var ( + inW = int32(fbai.width) + inH = int32(fbai.height) + outW = int32(width) + outH = int32(height) + ) + if outW == 0 { + outW = inW + } + if outH == 0 { + outH = inH + } + + dID := id.Derived() + cb := CommandBuilder{Thread: thread} + t := newTweaker(out, dID, cb) + defer t.revert(ctx) + t.glBindFramebuffer_Read(ctx, fb) + + var bufferBits GLbitfield + switch { + case attachment.IsColor(): + bufferBits = GLbitfield_GL_COLOR_BUFFER_BIT + case attachment.IsDepth(): + bufferBits = GLbitfield_GL_DEPTH_BUFFER_BIT + case attachment.IsStencil(): + bufferBits = GLbitfield_GL_STENCIL_BUFFER_BIT + } + + if attachment.IsColor() { // TODO: These glReadBuffer calls need to be changed for on-device // replay. Note that glReadBuffer was only introduced in // OpenGL ES 3.0, and that GL_FRONT is not a legal enum value. @@ -140,32 +155,48 @@ func (t *readFramebuffer) color( return nil })) } else { - t.glReadBuffer(ctx, GLenum_GL_COLOR_ATTACHMENT0+GLenum(bufferIdx)) + t.glReadBuffer(ctx, glAtt) } + } - if inW == outW && inH == outH { - postColorData(ctx, s, outW, outH, fmt, out, id, thread, res) - } else { - t.glScissor(ctx, 0, 0, GLsizei(inW), GLsizei(inH)) - framebufferID := t.glGenFramebuffer(ctx) - t.glBindFramebuffer_Draw(ctx, framebufferID) - renderbufferID := t.glGenRenderbuffer(ctx) - t.glBindRenderbuffer(ctx, renderbufferID) - - mutateAndWriteEach(ctx, out, dID, - cb.GlRenderbufferStorage(GLenum_GL_RENDERBUFFER, fmt, GLsizei(outW), GLsizei(outH)), - cb.GlFramebufferRenderbuffer(GLenum_GL_DRAW_FRAMEBUFFER, GLenum_GL_COLOR_ATTACHMENT0, GLenum_GL_RENDERBUFFER, renderbufferID), - cb.GlBlitFramebuffer(0, 0, GLint(inW), GLint(inH), 0, 0, GLint(outW), GLint(outH), GLbitfield_GL_COLOR_BUFFER_BIT, GLenum_GL_LINEAR), - ) - t.glBindFramebuffer_Read(ctx, framebufferID) - - postColorData(ctx, s, outW, outH, fmt, out, id, thread, res) - } + if fbai.multisampled { + // Resolve + t.glScissor(ctx, 0, 0, GLsizei(inW), GLsizei(inH)) + framebufferID := t.glGenFramebuffer(ctx) + t.glBindFramebuffer_Draw(ctx, framebufferID) + renderbufferID := t.glGenRenderbuffer(ctx) + t.glBindRenderbuffer(ctx, renderbufferID) + + mutateAndWriteEach(ctx, out, dID, + cb.GlRenderbufferStorage(GLenum_GL_RENDERBUFFER, fbai.format, GLsizei(inW), GLsizei(inH)), + cb.GlFramebufferRenderbuffer(GLenum_GL_DRAW_FRAMEBUFFER, glAtt, GLenum_GL_RENDERBUFFER, renderbufferID), + cb.GlBlitFramebuffer(0, 0, GLint(inW), GLint(inH), 0, 0, GLint(inW), GLint(inH), bufferBits, GLenum_GL_NEAREST), + ) - }) + t.glBindFramebuffer_Read(ctx, framebufferID) + } + + if attachment.IsColor() && (inW != outW || inH != outH) { + // Resize + t.glScissor(ctx, 0, 0, GLsizei(inW), GLsizei(inH)) + framebufferID := t.glGenFramebuffer(ctx) + t.glBindFramebuffer_Draw(ctx, framebufferID) + renderbufferID := t.glGenRenderbuffer(ctx) + t.glBindRenderbuffer(ctx, renderbufferID) + + mutateAndWriteEach(ctx, out, dID, + cb.GlRenderbufferStorage(GLenum_GL_RENDERBUFFER, fbai.format, GLsizei(outW), GLsizei(outH)), + cb.GlFramebufferRenderbuffer(GLenum_GL_DRAW_FRAMEBUFFER, glAtt, GLenum_GL_RENDERBUFFER, renderbufferID), + cb.GlBlitFramebuffer(0, 0, GLint(inW), GLint(inH), 0, 0, GLint(outW), GLint(outH), bufferBits, GLenum_GL_LINEAR), + ) + t.glBindFramebuffer_Read(ctx, framebufferID) + } + + postColorData(ctx, s, outW, outH, fbai.format, out, id, thread, res) } -func postColorData(ctx context.Context, +func postColorData( + ctx context.Context, s *api.GlobalState, width, height int32, sizedFormat GLenum, diff --git a/gapis/api/gles/read_texture.go b/gapis/api/gles/read_texture.go index a5da0f3d87..fe7ce1353d 100644 --- a/gapis/api/gles/read_texture.go +++ b/gapis/api/gles/read_texture.go @@ -18,12 +18,11 @@ import ( "context" "fmt" - "github.com/google/gapid/core/data/binary" + "github.com/google/gapid/core/image" + "github.com/google/gapid/core/log" "github.com/google/gapid/gapis/api" "github.com/google/gapid/gapis/api/transform" "github.com/google/gapid/gapis/replay" - "github.com/google/gapid/gapis/replay/builder" - "github.com/google/gapid/gapis/replay/value" "github.com/google/gapid/gapis/service" ) @@ -49,11 +48,23 @@ func (t *readTexture) add(ctx context.Context, r *ReadGPUTextureDataResolveable, tex, ok := c.Objects.Shared.Textures[TextureId(r.Texture)] if !ok { - panic(fmt.Errorf("Attempting to read from a texture that does not exist.\nResolvable: %+v\nTexture: %+v", r, tex)) + err := fmt.Errorf("Attempting to read from a texture that does not exist.\n"+ + "Resolvable: %+v"+ + "Texture: %+v", r, tex) + log.W(ctx, "%v", err) + res(nil, err) + return } - layer := tex.Levels[GLint(r.Level)].Layers[GLint(r.Layer)] + lvl := tex.Levels[GLint(r.Level)] + layer := lvl.Layers[GLint(r.Layer)] if layer == nil { - panic(fmt.Errorf("Attempting to read from a texture layer that does not exist.\nResolvable: %+v\nTexture: %+v", r, tex)) + err := fmt.Errorf("Attempting to read from a texture (Level: %v/%v, Layer: %v/%v) that does not exist.\n"+ + "Resolvable: %+v\n"+ + "Texture: %+v", + r.Level, len(tex.Levels), r.Layer, len(lvl.Layers), r, tex) + log.W(ctx, "%v", err) + res(nil, err) + return } size := uint64(f.Size(int(layer.Width), int(layer.Height), 1)) @@ -63,31 +74,33 @@ func (t *readTexture) add(ctx context.Context, r *ReadGPUTextureDataResolveable, t := newTweaker(out, dID, cb) defer t.revert(ctx) - t.setPackStorage(ctx, PixelStorageState{Alignment: 1}, 0) - t.glBindTexture(ctx, tex) + framebufferID := t.glGenFramebuffer(ctx) + t.glBindFramebuffer_Draw(ctx, framebufferID) - target := tex.Kind - if tex.Kind == GLenum_GL_TEXTURE_CUBE_MAP { - target = GLenum_GL_TEXTURE_CUBE_MAP_POSITIVE_X + GLenum(r.Layer) + streamFmt, err := getUncompressedStreamFormat(getUnsizedFormatAndType(layer.SizedFormat)) + if err != nil { + res(nil, err) + return } - out.MutateAndWrite(ctx, dID, cb.GlGetTexImage(target, GLint(r.Level), GLenum(r.DataFormat), GLenum(r.DataType), tmp.Ptr())) - out.MutateAndWrite(ctx, dID, cb.Custom(func(ctx context.Context, s *api.GlobalState, b *builder.Builder) error { - b.Post(value.ObservedPointer(tmp.Address()), size, func(r binary.Reader, err error) error { - data := make([]byte, size) - if err == nil { - r.Data(data) - err = r.Error() - } - if err == nil { - res(data, nil) - } else { - res(nil, err) - } - return err - }) - return nil - })) + var glAtt GLenum + var apiAtt api.FramebufferAttachment + switch { + case streamFmt.HasColorComponent(): + glAtt, apiAtt = GLenum_GL_COLOR_ATTACHMENT0, api.FramebufferAttachment_Color0 + case streamFmt.HasDepthComponent(): + glAtt, apiAtt = GLenum_GL_DEPTH_ATTACHMENT, api.FramebufferAttachment_Depth + default: + res(nil, fmt.Errorf("Unsupported texture format %v", streamFmt)) + return + } + + if r.Layer == 0 { + out.MutateAndWrite(ctx, dID, cb.GlFramebufferTexture(GLenum_GL_DRAW_FRAMEBUFFER, glAtt, tex.ID, GLint(r.Level))) + } else { + out.MutateAndWrite(ctx, dID, cb.GlFramebufferTextureLayer(GLenum_GL_DRAW_FRAMEBUFFER, glAtt, tex.ID, GLint(r.Level), GLint(r.Layer))) + } + postFBData(ctx, dID, r.Thread, uint32(layer.Width), uint32(layer.Height), framebufferID, apiAtt, out, res) }) } @@ -104,5 +117,5 @@ func (r *ReadGPUTextureDataResolveable) Resolve(ctx context.Context) (interface{ if err != nil { return nil, err } - return res.([]byte), nil + return res.(*image.Data).Bytes, nil } diff --git a/gapis/api/gles/state.go b/gapis/api/gles/state.go index ef2fc36492..105d51b805 100644 --- a/gapis/api/gles/state.go +++ b/gapis/api/gles/state.go @@ -20,55 +20,90 @@ import ( "github.com/google/gapid/gapis/api" ) +// fbai is the result getFramebufferAttachmentInfo. +type fbai struct { + width uint32 + height uint32 + format GLenum // sized + multisampled bool +} + +func attachmentToEnum(a api.FramebufferAttachment) (GLenum, error) { + switch a { + case api.FramebufferAttachment_Color0: + return GLenum_GL_COLOR_ATTACHMENT0, nil + case api.FramebufferAttachment_Color1: + return GLenum_GL_COLOR_ATTACHMENT1, nil + case api.FramebufferAttachment_Color2: + return GLenum_GL_COLOR_ATTACHMENT2, nil + case api.FramebufferAttachment_Color3: + return GLenum_GL_COLOR_ATTACHMENT3, nil + case api.FramebufferAttachment_Depth: + return GLenum_GL_DEPTH_ATTACHMENT, nil + case api.FramebufferAttachment_Stencil: + return GLenum_GL_STENCIL_ATTACHMENT, nil + default: + return 0, fmt.Errorf("Framebuffer attachment %v unsupported by gles", a) + } +} + +func (f *Framebuffer) getAttachment(a api.FramebufferAttachment) (FramebufferAttachment, error) { + switch a { + case api.FramebufferAttachment_Color0: + return f.ColorAttachments[0], nil + case api.FramebufferAttachment_Color1: + return f.ColorAttachments[1], nil + case api.FramebufferAttachment_Color2: + return f.ColorAttachments[2], nil + case api.FramebufferAttachment_Color3: + return f.ColorAttachments[3], nil + case api.FramebufferAttachment_Depth: + return f.DepthAttachment, nil + case api.FramebufferAttachment_Stencil: + return f.StencilAttachment, nil + default: + return FramebufferAttachment{}, fmt.Errorf("Framebuffer attachment %v unsupported by gles", a) + } +} + // TODO: When gfx api macros produce functions instead of inlining, move this logic // to the gles.api file. -func (s *State) getFramebufferAttachmentInfo(thread uint64, fb FramebufferId, att api.FramebufferAttachment) (width, height uint32, sizedFormat GLenum, err error) { +func (s *State) getFramebufferAttachmentInfo(thread uint64, fb FramebufferId, att api.FramebufferAttachment) (fbai, error) { c := s.GetContext(thread) if c == nil { - return 0, 0, 0, fmt.Errorf("No context bound") + return fbai{}, fmt.Errorf("No context bound") } if !c.Info.Initialized { - return 0, 0, 0, fmt.Errorf("Context not initialized") + return fbai{}, fmt.Errorf("Context not initialized") } framebuffer, ok := c.Objects.Framebuffers[fb] if !ok { - return 0, 0, 0, fmt.Errorf("Invalid framebuffer %v", fb) + return fbai{}, fmt.Errorf("Invalid framebuffer %v", fb) } - var a FramebufferAttachment - switch att { - case api.FramebufferAttachment_Color0: - a = framebuffer.ColorAttachments[0] - case api.FramebufferAttachment_Color1: - a = framebuffer.ColorAttachments[1] - case api.FramebufferAttachment_Color2: - a = framebuffer.ColorAttachments[2] - case api.FramebufferAttachment_Color3: - a = framebuffer.ColorAttachments[3] - case api.FramebufferAttachment_Depth: - a = framebuffer.DepthAttachment - case api.FramebufferAttachment_Stencil: - a = framebuffer.StencilAttachment - default: - return 0, 0, 0, fmt.Errorf("Framebuffer attachment %v unsupported by gles", att) + a, err := framebuffer.getAttachment(att) + if err != nil { + return fbai{}, err } switch a.Type { case GLenum_GL_NONE: - return 0, 0, 0, fmt.Errorf("%s is not bound", att) + return fbai{}, fmt.Errorf("%s is not bound", att) case GLenum_GL_TEXTURE: t := a.Texture l := t.Levels[a.TextureLevel].Layers[a.TextureLayer] if l == nil { - return 0, 0, 0, fmt.Errorf("Texture %v does not have Level[%v].Layer[%v]", + return fbai{}, fmt.Errorf("Texture %v does not have Level[%v].Layer[%v]", t.ID, a.TextureLevel, a.TextureLayer) } - return uint32(l.Width), uint32(l.Height), l.SizedFormat, nil + multisampled := l.Samples > 0 + return fbai{uint32(l.Width), uint32(l.Height), l.SizedFormat, multisampled}, nil case GLenum_GL_RENDERBUFFER: r := a.Renderbuffer - return uint32(r.Width), uint32(r.Height), r.InternalFormat, nil + multisampled := r.Samples > 0 + return fbai{uint32(r.Width), uint32(r.Height), r.InternalFormat, multisampled}, nil default: - return 0, 0, 0, fmt.Errorf("Unknown framebuffer attachment type %T", a.Type) + return fbai{}, fmt.Errorf("Unknown framebuffer attachment type %T", a.Type) } }