Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenGL ES on device replay #2001

Merged
merged 2 commits into from
Jun 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions gapis/api/gles/compat.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,10 @@ func compat(ctx context.Context, device *device.Instance, onError onCompatError)
return

case *GlShaderSource:
if version.IsES { // No compat required.
out.MutateAndWrite(ctx, id, cmd)
return
}
// Apply the state mutation of the unmodified glShaderSource
// command.
// This is so we can grab the source string from the Shader object.
Expand All @@ -412,6 +416,10 @@ func compat(ctx context.Context, device *device.Instance, onError onCompatError)
return

case *GlCompileShader:
if version.IsES { // No compat required.
out.MutateAndWrite(ctx, id, cmd)
return
}
shader := c.Objects().Shaders().Get(cmd.Shader())
src := ""

Expand Down
37 changes: 22 additions & 15 deletions gapis/api/gles/find_issues.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,20 @@ import (
// the slice out. Once the last issue is sent (if any) all the chans in out are
// closed.
type findIssues struct {
state *api.GlobalState
device *device.Instance
issues []replay.Issue
res []replay.Result
lastGlError GLenum
state *api.GlobalState
device *device.Instance
targetVersion *Version
issues []replay.Issue
res []replay.Result
lastGlError GLenum
}

func newFindIssues(ctx context.Context, c *capture.Capture, device *device.Instance) *findIssues {
targetVersion, _ := ParseVersion(device.Configuration.Drivers.OpenGL.Version)
transform := &findIssues{
state: c.NewState(ctx),
device: device,
state: c.NewState(ctx),
device: device,
targetVersion: targetVersion,
}
transform.state.OnError = func(err interface{}) {
if glenum, ok := err.(GLenum); ok {
Expand Down Expand Up @@ -166,15 +169,19 @@ func (t *findIssues) Transform(ctx context.Context, id api.CmdID, cmd api.Cmd, o
t.onIssue(cmd, id, service.Severity_ErrorLevel, err)
return
}
opts := shadertools.ConvertOptions{
ShaderType: st,
CheckAfterChanges: true,
Disassemble: true,
TargetGLSLVersion: 430,
}

if _, err := shadertools.ConvertGlsl(shader.Source(), &opts); err != nil {
t.onIssue(cmd, id, service.Severity_ErrorLevel, err)
if t.targetVersion.IsES {
// Check we are able to convert this GLES shader to desktop GL.
opts := shadertools.ConvertOptions{
ShaderType: st,
CheckAfterChanges: true,
Disassemble: true,
TargetGLSLVersion: 430,
}

if _, err := shadertools.ConvertGlsl(shader.Source(), &opts); err != nil {
t.onIssue(cmd, id, service.Severity_ErrorLevel, err)
}
}

const buflen = 8192
Expand Down
74 changes: 65 additions & 9 deletions gapis/api/gles/read_framebuffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/google/gapid/core/data/binary"
"github.com/google/gapid/core/image"
"github.com/google/gapid/core/log"
"github.com/google/gapid/core/os/device"
"github.com/google/gapid/core/stream"
"github.com/google/gapid/gapis/api"
"github.com/google/gapid/gapis/api/transform"
Expand All @@ -32,10 +33,14 @@ import (
"github.com/google/gapid/gapis/service"
)

type readFramebuffer struct{ transform.Tasks }
type readFramebuffer struct {
transform.Tasks
targetVersion *Version
}

func newReadFramebuffer(ctx context.Context) *readFramebuffer {
return &readFramebuffer{}
func newReadFramebuffer(ctx context.Context, device *device.Instance) *readFramebuffer {
targetVersion, _ := ParseVersion(device.Configuration.Drivers.OpenGL.Version)
return &readFramebuffer{targetVersion: targetVersion}
}

func getBoundFramebufferID(thread uint64, s *api.GlobalState) (FramebufferId, error) {
Expand All @@ -56,7 +61,7 @@ func (t *readFramebuffer) depth(
res replay.Result) {

t.Add(id, func(ctx context.Context, out transform.Writer) {
postFBData(ctx, id, thread, 0, 0, fb, GLenum_GL_DEPTH_ATTACHMENT, out, res)
postFBData(ctx, id, thread, 0, 0, fb, GLenum_GL_DEPTH_ATTACHMENT, t.targetVersion, out, res)
})
}

Expand All @@ -70,7 +75,7 @@ func (t *readFramebuffer) color(

t.Add(id, func(ctx context.Context, out transform.Writer) {
attachment := GLenum_GL_COLOR_ATTACHMENT0 + GLenum(bufferIdx)
postFBData(ctx, id, thread, width, height, fb, attachment, out, res)
postFBData(ctx, id, thread, width, height, fb, attachment, t.targetVersion, out, res)
})
}

Expand All @@ -80,6 +85,7 @@ func postFBData(ctx context.Context,
width, height uint32,
fb FramebufferId,
attachment GLenum,
version *Version,
out transform.Writer,
res replay.Result) {

Expand Down Expand Up @@ -180,15 +186,18 @@ func postFBData(ctx context.Context,
}

if hasColor {
// 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.
if c.Bound().DrawFramebuffer() == c.Objects().Default().Framebuffer() {
out.MutateAndWrite(ctx, dID, cb.Custom(func(ctx context.Context, s *api.GlobalState, b *builder.Builder) error {
// TODO: We assume here that the default framebuffer is
// single-buffered. Once we support double-buffering we
// need to decide whether to read from GL_FRONT or GL_BACK.
cb.GlReadBuffer(GLenum_GL_FRONT).Call(ctx, s, b)
buf := GLenum_GL_BACK
if !version.IsES {
// OpenGL expects GL_FRONT for single-buffered
// configurations. Note this is not a legal value for GLES.
buf = GLenum_GL_FRONT
}
cb.GlReadBuffer(buf).Call(ctx, s, b)
return nil
}))
} else {
Expand Down Expand Up @@ -229,6 +238,21 @@ func postFBData(ctx context.Context,
t.glBindFramebuffer_Read(ctx, framebufferID)
}

if u, t := getReadPixelsFormat(version, unsizedFormat, ty); unsizedFormat != u || ty != t {
// glReadPixels() cannot be called with the natural unsized-format and
// type of the framebuffer. Instead, fetch the framebuffer in a format
// that can be read, and then convert the result to the expected format.
f, err := getImageFormat(u, t)
if err != nil {
res(nil, err)
return
}
res = res.Transform(func(in interface{}) (interface{}, error) {
return in.(*image.Data).Convert(imgFmt)
})
imgFmt, unsizedFormat, ty = f, u, t
}

t.setPackStorage(ctx, NewPixelStorageState(s.Arena,
0, // ImageHeight
0, // SkipImages
Expand Down Expand Up @@ -279,6 +303,38 @@ func postFBData(ctx context.Context,
out.MutateAndWrite(ctx, dID, cb.GlGetError(0)) // Check for errors.
}

// getReadPixelsFormat returns a unsized-format and type that is compatible with
// glReadPixels() for the given framebuffer unsized-format and type.
// See the GLES spec: 4.3.2 Reading Pixels
func getReadPixelsFormat(version *Version, uf GLenum, ty GLenum) (GLenum, GLenum) {
if version.IsES {
switch uf {
case GLenum_GL_RED_INTEGER, GLenum_GL_RG_INTEGER, GLenum_GL_RGB_INTEGER, GLenum_GL_RGBA_INTEGER:
uf = GLenum_GL_RGBA_INTEGER
default:
uf = GLenum_GL_RGBA
}
switch ty {
case GLenum_GL_UNSIGNED_BYTE,
GLenum_GL_UNSIGNED_SHORT_4_4_4_4,
GLenum_GL_UNSIGNED_SHORT_5_5_5_1,
GLenum_GL_UNSIGNED_SHORT_5_6_5:
ty = GLenum_GL_UNSIGNED_BYTE
case GLenum_GL_UNSIGNED_INT,
GLenum_GL_UNSIGNED_INT_10F_11F_11F_REV,
GLenum_GL_UNSIGNED_INT_2_10_10_10_REV,
GLenum_GL_UNSIGNED_INT_5_9_9_9_REV,
GLenum_GL_UNSIGNED_SHORT:
ty = GLenum_GL_UNSIGNED_INT
case GLenum_GL_BYTE, GLenum_GL_INT, GLenum_GL_SHORT:
ty = GLenum_GL_INT
default:
ty = GLenum_GL_FLOAT
}
}
return uf, ty
}

func mutateAndWriteEach(ctx context.Context, out transform.Writer, id api.CmdID, cmds ...api.Cmd) {
for _, cmd := range cmds {
out.MutateAndWrite(ctx, id, cmd)
Expand Down
21 changes: 15 additions & 6 deletions gapis/api/gles/read_texture.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/google/gapid/core/image"
"github.com/google/gapid/core/log"
"github.com/google/gapid/core/os/device"
"github.com/google/gapid/gapis/api"
"github.com/google/gapid/gapis/api/transform"
"github.com/google/gapid/gapis/replay"
Expand All @@ -30,7 +31,15 @@ type textureRequest struct {
data *ReadGPUTextureDataResolveable
}

type readTexture struct{ transform.Tasks }
type readTexture struct {
transform.Tasks
targetVersion *Version
}

func newReadTexture(ctx context.Context, device *device.Instance) *readTexture {
targetVersion, _ := ParseVersion(device.Configuration.Drivers.OpenGL.Version)
return &readTexture{targetVersion: targetVersion}
}

func (t *readTexture) add(ctx context.Context, r *ReadGPUTextureDataResolveable, res replay.Result) {
id := api.CmdID(r.After)
Expand Down Expand Up @@ -69,11 +78,11 @@ func (t *readTexture) add(ctx context.Context, r *ReadGPUTextureDataResolveable,
return
}

t := newTweaker(out, dID, cb)
defer t.revert(ctx)
tw := newTweaker(out, dID, cb)
defer tw.revert(ctx)

framebufferID := t.glGenFramebuffer(ctx)
t.glBindFramebuffer_Draw(ctx, framebufferID)
framebufferID := tw.glGenFramebuffer(ctx)
tw.glBindFramebuffer_Draw(ctx, framebufferID)

streamFmt, err := getUncompressedStreamFormat(getUnsizedFormatAndType(layer.SizedFormat()))
if err != nil {
Expand Down Expand Up @@ -126,7 +135,7 @@ func (t *readTexture) add(ctx context.Context, r *ReadGPUTextureDataResolveable,
return in.(*image.Data).Convert(f)
})

postFBData(ctx, dID, r.Thread, uint32(layer.Width()), uint32(layer.Height()), framebufferID, attachment, out, res)
postFBData(ctx, dID, r.Thread, uint32(layer.Width()), uint32(layer.Height()), framebufferID, attachment, t.targetVersion, out, res)
})
}

Expand Down
20 changes: 14 additions & 6 deletions gapis/api/gles/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,22 @@ type framebufferRequest struct {

// GetReplayPriority returns a uint32 representing the preference for
// replaying this trace on the given device.
// A lower number represents a higher priority, and Zero represents
// A lower number represents a higher priority, and zero represents
// an inability for the trace to be replayed on the given device.
func (a API) GetReplayPriority(ctx context.Context, i *device.Instance, h *capture.Header) uint32 {
if i.GetConfiguration().GetOS().GetKind() != device.Android {
return 1
v, err := ParseVersion(i.GetConfiguration().GetDrivers().GetOpenGL().GetVersion())
if err != nil {
return 0 // Can't figure out what we're dealing with.
}

return 2
switch {
case v.AtLeastES(3, 0):
return 2 // GLES 3.0+ on-device replay is WIP.
case v.IsES:
return 0 // Can't replay on this version of an ES device.
default:
return 1 // Desktop GL can be used with heavy use of compat.
}
}

func (a API) Replay(
Expand Down Expand Up @@ -138,15 +146,15 @@ func (a API) Replay(

case textureRequest:
if rt == nil {
rt = &readTexture{}
rt = newReadTexture(ctx, device)
}
after := api.CmdID(req.data.After)
deadCodeElimination.Request(after)
rt.add(ctx, req.data, rr.Result)

case framebufferRequest:
if rf == nil {
rf = &readFramebuffer{}
rf = newReadFramebuffer(ctx, device)
}
deadCodeElimination.Request(req.after)

Expand Down