From 15a31727ffc5e26594202f9f4c439c0739c793ee Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 13 Sep 2017 12:28:47 +0100 Subject: [PATCH 1/5] gapis: Move the sequence grouper to cmdgrouper package. --- gapis/extensions/unity/state_reset_grouper.go | 469 +++++++----------- gapis/extensions/unity/unity.go | 2 +- gapis/resolve/cmdgrouper/CMakeFiles.cmake | 1 + gapis/resolve/cmdgrouper/sequence.go | 111 +++++ 4 files changed, 300 insertions(+), 283 deletions(-) create mode 100644 gapis/resolve/cmdgrouper/sequence.go diff --git a/gapis/extensions/unity/state_reset_grouper.go b/gapis/extensions/unity/state_reset_grouper.go index b47a021155..83dbc94740 100644 --- a/gapis/extensions/unity/state_reset_grouper.go +++ b/gapis/extensions/unity/state_reset_grouper.go @@ -15,318 +15,223 @@ package unity import ( - "context" - "github.com/google/gapid/gapis/api" "github.com/google/gapid/gapis/api/gles" "github.com/google/gapid/gapis/resolve/cmdgrouper" ) -var _ cmdgrouper.Grouper = &stateResetGrouper{} - -type stateResetGrouper struct { - rule int - passed bool - prev api.Cmd - start api.CmdID - groups []cmdgrouper.Group -} - -func (g *stateResetGrouper) flush(id api.CmdID) { - if g.rule == len(stateResetRules) { - g.groups = append(g.groups, cmdgrouper.Group{ - Start: g.start, - End: id, - Name: "Unity state reset", - }) - g.rule, g.passed, g.start = 0, false, id - } -} - -// Process considers the command for inclusion in the group. -func (g *stateResetGrouper) Process(ctx context.Context, id api.CmdID, cmd api.Cmd, s *api.GlobalState) { - prev := g.prev - g.prev = cmd - - for { - g.flush(id) - rule := stateResetRules[g.rule] - passed := rule.pred(cmd, prev) - optional := (rule.flags & optional) != 0 - repeats := (rule.flags & repeats) != 0 - - switch { - case passed && repeats: - g.passed = true - return - case passed && !repeats: - g.rule++ - g.passed = false - return - // --- below failed --- - case optional, g.passed: - g.rule++ - g.passed = false - continue - default: // failed sequence - g.rule, g.passed, g.start = 0, false, id+1 - return +func newStateResetGrouper() cmdgrouper.Grouper { + eglGetCurrentContext := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + _, ok := cmd.(*gles.EglGetCurrentContext) + return ok } } -} - -// Build returns the groups built and resets the state of the grouper. -func (g *stateResetGrouper) Build(end api.CmdID) []cmdgrouper.Group { - g.flush(end) - out := g.groups - g.groups = nil - return out -} - -type sequenceRule struct { - pred func(cmd, prev api.Cmd) bool - flags ruleFlags -} - -type ruleFlags int - -const ( - repeats ruleFlags = 1 - optional ruleFlags = 2 -) - -var stateResetRules = []sequenceRule{ - {eglGetCurrentContextRule(), 0}, // eglGetCurrentContext() - {glDisableRule(gles.GLenum_GL_DEPTH_TEST), 0}, // glDisable(GL_DEPTH_TEST) - {glDisableRule(gles.GLenum_GL_BLEND), 0}, // glDisable(GL_BLEND) - {glDisableRule(gles.GLenum_GL_SAMPLE_ALPHA_TO_COVERAGE), 0}, // glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE) - {glDisableRule(gles.GLenum_GL_STENCIL_TEST), 0}, // glDisable(GL_STENCIL_TEST) - {glDisableRule(gles.GLenum_GL_POLYGON_OFFSET_FILL), 0}, // glDisable(GL_POLYGON_OFFSET_FILL) - {glDisableRule(gles.GLenum_GL_SCISSOR_TEST), 0}, // glDisable(GL_SCISSOR_TEST) - {glDisableRule(gles.GLenum_GL_FRAMEBUFFER_SRGB_EXT), optional}, // glDisable(GL_FRAMEBUFFER_SRGB_EXT) - {glEnableRule(gles.GLenum_GL_DITHER), 0}, // glEnable(GL_DITHER) - {glDepthFuncRule(gles.GLenum_GL_NEVER), optional}, // glDepthFunc(GL_NEVER) - {glDepthMaskRule(0), 0}, // glDepthMask(0) - {glEnableRule(gles.GLenum_GL_DEPTH_TEST), optional}, // glEnable(GL_DEPTH_TEST) - {glDepthFuncRule(gles.GLenum_GL_ALWAYS), optional}, // glDepthFunc(GL_ALWAYS) - {glColorMaskRule(1, 1, 1, 1), 0}, // glColorMask(1, 1, 1, 1) - {glBlendFuncSeparateRule(gles.GLenum_GL_ONE, gles.GLenum_GL_ZERO, gles.GLenum_GL_ONE, gles.GLenum_GL_ZERO), 0}, // glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO) - {glBlendEquationSeparateRule(gles.GLenum_GL_FUNC_ADD, gles.GLenum_GL_FUNC_ADD), 0}, // glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD) - {glStencilFuncSeparateRule(gles.GLenum_GL_FRONT, gles.GLenum_GL_ALWAYS, 0, 255), 0}, // glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 255) - {glStencilOpSeparateRule(gles.GLenum_GL_FRONT, gles.GLenum_GL_KEEP, gles.GLenum_GL_KEEP, gles.GLenum_GL_KEEP), 0}, // glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_KEEP) - {glStencilFuncSeparateRule(gles.GLenum_GL_BACK, gles.GLenum_GL_ALWAYS, 0, 255), 0}, // glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 255) - {glStencilOpSeparateRule(gles.GLenum_GL_BACK, gles.GLenum_GL_KEEP, gles.GLenum_GL_KEEP, gles.GLenum_GL_KEEP), 0}, // glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP) - {glStencilMaskRule(255), 0}, // glStencilMask(255) - {glCullFaceRule(gles.GLenum_GL_BACK), 0}, // glCullFace(GL_BACK) - {glEnableRule(gles.GLenum_GL_CULL_FACE), 0}, // glEnable(GL_CULL_FACE) - {glFrontFaceRule(gles.GLenum_GL_CW), 0}, // glFrontFace(orientation: GL_CW) - {glBindSamplerRule(), repeats}, // glBindSampler(0..N, 0) - {glBindBufferRule(gles.GLenum_GL_ARRAY_BUFFER), 0}, // glBindBuffer(GL_ARRAY_BUFFER, 0) - {glBindBufferRule(gles.GLenum_GL_ELEMENT_ARRAY_BUFFER), 0}, // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) - {glBindBufferRule(gles.GLenum_GL_DRAW_INDIRECT_BUFFER), 0}, // glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0) - {glBindBufferRule(gles.GLenum_GL_COPY_READ_BUFFER), 0}, // glBindBuffer(GL_COPY_READ_BUFFER, 0) - {glBindBufferRule(gles.GLenum_GL_COPY_WRITE_BUFFER), 0}, // glBindBuffer(GL_COPY_WRITE_BUFFER, 0) - {glBindBufferBaseRule(gles.GLenum_GL_UNIFORM_BUFFER), repeats}, // glBindBufferBase(GL_UNIFORM_BUFFER, 0..N, 0) - {glBindBufferBaseRule(gles.GLenum_GL_TRANSFORM_FEEDBACK_BUFFER), 0}, // glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0) - {glBindBufferBaseRule(gles.GLenum_GL_SHADER_STORAGE_BUFFER), repeats}, // glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0..N, 0) - {glBindBufferBaseRule(gles.GLenum_GL_ATOMIC_COUNTER_BUFFER), repeats}, // glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0..N, 0) - {glBindBufferRule(gles.GLenum_GL_DISPATCH_INDIRECT_BUFFER), 0}, // glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0) - {glUseProgramRule(), 0}, // glUseProgram(0) - {glActiveTextureOrBindTextureRule(), repeats}, // glActiveTexture(GL_TEXTURE31 .. 0), glBindTexture(GL_TEXTURE_2D, 0) - {glPixelStoreiRule(gles.GLenum_GL_UNPACK_ROW_LENGTH), 0}, // glPixelStorei(GL_UNPACK_ROW_LENGTH, 0) - {glPixelStoreiRule(gles.GLenum_GL_PACK_ALIGNMENT), 0}, // glPixelStorei(GL_PACK_ALIGNMENT, 1) - {glPixelStoreiRule(gles.GLenum_GL_UNPACK_ALIGNMENT), 0}, // glPixelStorei(GL_UNPACK_ALIGNMENT, 1) - {glBindFramebufferRule(gles.GLenum_GL_DRAW_FRAMEBUFFER), 0}, // glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0) - {glBindFramebufferRule(gles.GLenum_GL_READ_FRAMEBUFFER), 0}, // glBindFramebuffer(GL_READ_FRAMEBUFFER, 0) - {glIsVertexArrayRuleOrGenVertexArraysRule(), repeats}, // glIsVertexArray(array: 1) → 0, glGenVertexArrays(count: 1, arrays: 0xcab3ff84) - {glBindVertexArrayRule(), 0}, // glBindVertexArray(array: 2) - {glDisableVertexAttribArrayRule(), repeats}, // glDisableVertexAttribArray(0 .. N) - {glFrontFaceRule(gles.GLenum_GL_CW), optional}, // glFrontFace(orientation: GL_CW) -} - -func eglGetCurrentContextRule() func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - _, ok := cmd.(*gles.EglGetCurrentContext) - return ok - } -} - -func glDisableRule(capability gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlDisable) - return ok && c.Capability == capability - } -} - -func glEnableRule(capability gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlEnable) - return ok && c.Capability == capability + glDisable := func(capability gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlDisable) + return ok && c.Capability == capability + } } -} - -func glFrontFaceRule(orientation gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlFrontFace) - return ok && c.Orientation == orientation + glEnable := func(capability gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlEnable) + return ok && c.Capability == capability + } } -} - -func glDepthFuncRule(function gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlDepthFunc) - return ok && c.Function == function + glFrontFace := func(orientation gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlFrontFace) + return ok && c.Orientation == orientation + } } -} - -func glColorMaskRule(r, g, b, a gles.GLboolean) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlColorMask) - return ok && c.Red == r && c.Green == g && c.Blue == b && c.Alpha == a + glDepthFunc := func(function gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlDepthFunc) + return ok && c.Function == function + } } -} - -func glBlendFuncSeparateRule(srcFactorRGB, dstFactorRGB, srcFactorA, dstFactorA gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlBlendFuncSeparate) - return ok && - c.SrcFactorRgb == srcFactorRGB && - c.DstFactorRgb == dstFactorRGB && - c.SrcFactorAlpha == srcFactorA && - c.DstFactorAlpha == dstFactorA + glColorMask := func(r, g, b, a gles.GLboolean) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlColorMask) + return ok && c.Red == r && c.Green == g && c.Blue == b && c.Alpha == a + } } -} - -func glBlendEquationSeparateRule(rgb, alpha gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlBlendEquationSeparate) - return ok && c.Rgb == rgb && c.Alpha == alpha + glBlendFuncSeparate := func(srcFactorRGB, dstFactorRGB, srcFactorA, dstFactorA gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlBlendFuncSeparate) + return ok && + c.SrcFactorRgb == srcFactorRGB && + c.DstFactorRgb == dstFactorRGB && + c.SrcFactorAlpha == srcFactorA && + c.DstFactorAlpha == dstFactorA + } } -} - -func glStencilFuncSeparateRule(face, function gles.GLenum, referenceValue gles.GLint, mask gles.GLuint) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlStencilFuncSeparate) - return ok && - c.Face == face && - c.Function == function && - c.ReferenceValue == referenceValue && - c.Mask == mask + glBlendEquationSeparate := func(rgb, alpha gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlBlendEquationSeparate) + return ok && c.Rgb == rgb && c.Alpha == alpha + } } -} - -func glStencilOpSeparateRule(face, stencilFail, stencilPassDepthFail, stencilPassDepthPass gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlStencilOpSeparate) - return ok && - c.Face == face && - c.StencilFail == stencilFail && - c.StencilPassDepthFail == stencilPassDepthFail && - c.StencilPassDepthPass == stencilPassDepthPass + glStencilFuncSeparate := func(face, function gles.GLenum, referenceValue gles.GLint, mask gles.GLuint) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlStencilFuncSeparate) + return ok && + c.Face == face && + c.Function == function && + c.ReferenceValue == referenceValue && + c.Mask == mask + } } -} - -func glStencilMaskRule(mask gles.GLuint) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlStencilMask) - return ok && c.Mask == mask + glStencilOpSeparate := func(face, stencilFail, stencilPassDepthFail, stencilPassDepthPass gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlStencilOpSeparate) + return ok && + c.Face == face && + c.StencilFail == stencilFail && + c.StencilPassDepthFail == stencilPassDepthFail && + c.StencilPassDepthPass == stencilPassDepthPass + } } -} - -func glCullFaceRule(mode gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlCullFace) - return ok && c.Mode == mode + glStencilMask := func(mask gles.GLuint) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlStencilMask) + return ok && c.Mask == mask + } } -} - -func glDepthMaskRule(enabled gles.GLboolean) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlDepthMask) - return ok && c.Enabled == enabled + glCullFace := func(mode gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlCullFace) + return ok && c.Mode == mode + } } -} - -func glBindSamplerRule() func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlBindSampler) - p, _ := prev.(*gles.GlBindSampler) - return ok && c.Sampler == 0 && (p == nil || c.Index == p.Index+1) + glDepthMask := func(enabled gles.GLboolean) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlDepthMask) + return ok && c.Enabled == enabled + } } -} - -func glBindBufferRule(target gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlBindBuffer) - return ok && c.Buffer == 0 && c.Target == target + glBindSampler := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlBindSampler) + p, _ := prev.(*gles.GlBindSampler) + return ok && c.Sampler == 0 && (p == nil || c.Index == p.Index+1) + } } -} - -func glBindBufferBaseRule(target gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlBindBufferBase) - p, _ := prev.(*gles.GlBindBufferBase) - return ok && c.Buffer == 0 && c.Target == target && - (p == nil || c.Target != p.Target || c.Index == p.Index+1) + glBindBuffer := func(target gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlBindBuffer) + return ok && c.Buffer == 0 && c.Target == target + } } -} - -func glUseProgramRule() func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlUseProgram) - return ok && c.Program == 0 + glBindBufferBase := func(target gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlBindBufferBase) + p, _ := prev.(*gles.GlBindBufferBase) + return ok && c.Buffer == 0 && c.Target == target && + (p == nil || c.Target != p.Target || c.Index == p.Index+1) + } } -} - -func glActiveTextureOrBindTextureRule() func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - if _, ok := cmd.(*gles.GlActiveTexture); ok { - return true + glUseProgram := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlUseProgram) + return ok && c.Program == 0 } - if cmd, ok := cmd.(*gles.GlBindTexture); ok && cmd.Texture == 0 { - return true + } + glActiveTextureOrBindTexture := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + if _, ok := cmd.(*gles.GlActiveTexture); ok { + return true + } + if cmd, ok := cmd.(*gles.GlBindTexture); ok && cmd.Texture == 0 { + return true + } + return false } - return false } -} - -func glPixelStoreiRule(param gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlPixelStorei) - return ok && c.Parameter == param + glPixelStorei := func(param gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlPixelStorei) + return ok && c.Parameter == param + } } -} - -func glBindFramebufferRule(target gles.GLenum) func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlBindFramebuffer) - return ok && c.Target == target + glBindFramebuffer := func(target gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlBindFramebuffer) + return ok && c.Target == target + } } -} - -func glIsVertexArrayRuleOrGenVertexArraysRule() func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - if _, ok := cmd.(*gles.GlIsVertexArray); ok { - return true + glIsVertexArrayRuleOrGenVertexArrays := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + if _, ok := cmd.(*gles.GlIsVertexArray); ok { + return true + } + if _, ok := cmd.(*gles.GlGenVertexArrays); ok { + return true + } + return false } - if _, ok := cmd.(*gles.GlGenVertexArrays); ok { - return true + } + glBindVertexArray := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + _, ok := cmd.(*gles.GlBindVertexArray) + return ok } - return false } -} - -func glBindVertexArrayRule() func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - _, ok := cmd.(*gles.GlBindVertexArray) - return ok + glDisableVertexAttribArray := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlDisableVertexAttribArray) + p, _ := prev.(*gles.GlDisableVertexAttribArray) + return ok && ((p == nil && c.Location == 0) || (p != nil && c.Location == p.Location+1)) + } } -} -func glDisableVertexAttribArrayRule() func(cmd, prev api.Cmd) bool { - return func(cmd, prev api.Cmd) bool { - c, ok := cmd.(*gles.GlDisableVertexAttribArray) - p, _ := prev.(*gles.GlDisableVertexAttribArray) - return ok && ((p == nil && c.Location == 0) || (p != nil && c.Location == p.Location+1)) - } + return cmdgrouper.Sequence("Unity state reset", + cmdgrouper.Rule{Pred: eglGetCurrentContext()}, // eglGetCurrentContext() + cmdgrouper.Rule{Pred: glDisable(gles.GLenum_GL_DEPTH_TEST)}, // glDisable(GL_DEPTH_TEST) + cmdgrouper.Rule{Pred: glDisable(gles.GLenum_GL_BLEND)}, // glDisable(GL_BLEND) + cmdgrouper.Rule{Pred: glDisable(gles.GLenum_GL_SAMPLE_ALPHA_TO_COVERAGE)}, // glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE) + cmdgrouper.Rule{Pred: glDisable(gles.GLenum_GL_STENCIL_TEST)}, // glDisable(GL_STENCIL_TEST) + cmdgrouper.Rule{Pred: glDisable(gles.GLenum_GL_POLYGON_OFFSET_FILL)}, // glDisable(GL_POLYGON_OFFSET_FILL) + cmdgrouper.Rule{Pred: glDisable(gles.GLenum_GL_SCISSOR_TEST)}, // glDisable(GL_SCISSOR_TEST) + cmdgrouper.Rule{Pred: glDisable(gles.GLenum_GL_FRAMEBUFFER_SRGB_EXT), Optional: true}, // glDisable(GL_FRAMEBUFFER_SRGB_EXT) + cmdgrouper.Rule{Pred: glEnable(gles.GLenum_GL_DITHER)}, // glEnable(GL_DITHER) + cmdgrouper.Rule{Pred: glDepthFunc(gles.GLenum_GL_NEVER), Optional: true}, // glDepthFunc(GL_NEVER) + cmdgrouper.Rule{Pred: glDepthMask(0)}, // glDepthMask(0) + cmdgrouper.Rule{Pred: glEnable(gles.GLenum_GL_DEPTH_TEST), Optional: true}, // glEnable(GL_DEPTH_TEST) + cmdgrouper.Rule{Pred: glDepthFunc(gles.GLenum_GL_ALWAYS), Optional: true}, // glDepthFunc(GL_ALWAYS) + cmdgrouper.Rule{Pred: glColorMask(1, 1, 1, 1)}, // glColorMask(1, 1, 1, 1) + cmdgrouper.Rule{Pred: glBlendFuncSeparate(gles.GLenum_GL_ONE, gles.GLenum_GL_ZERO, gles.GLenum_GL_ONE, gles.GLenum_GL_ZERO)}, // glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO) + cmdgrouper.Rule{Pred: glBlendEquationSeparate(gles.GLenum_GL_FUNC_ADD, gles.GLenum_GL_FUNC_ADD)}, // glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD) + cmdgrouper.Rule{Pred: glStencilFuncSeparate(gles.GLenum_GL_FRONT, gles.GLenum_GL_ALWAYS, 0, 255)}, // glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 255) + cmdgrouper.Rule{Pred: glStencilOpSeparate(gles.GLenum_GL_FRONT, gles.GLenum_GL_KEEP, gles.GLenum_GL_KEEP, gles.GLenum_GL_KEEP)}, // glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_KEEP) + cmdgrouper.Rule{Pred: glStencilFuncSeparate(gles.GLenum_GL_BACK, gles.GLenum_GL_ALWAYS, 0, 255)}, // glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 255) + cmdgrouper.Rule{Pred: glStencilOpSeparate(gles.GLenum_GL_BACK, gles.GLenum_GL_KEEP, gles.GLenum_GL_KEEP, gles.GLenum_GL_KEEP)}, // glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP) + cmdgrouper.Rule{Pred: glStencilMask(255)}, // glStencilMask(255) + cmdgrouper.Rule{Pred: glCullFace(gles.GLenum_GL_BACK)}, // glCullFace(GL_BACK) + cmdgrouper.Rule{Pred: glEnable(gles.GLenum_GL_CULL_FACE)}, // glEnable(GL_CULL_FACE) + cmdgrouper.Rule{Pred: glFrontFace(gles.GLenum_GL_CW)}, // glFrontFace(orientation: GL_CW) + cmdgrouper.Rule{Pred: glBindSampler(), Repeats: true}, // glBindSampler(0..N, 0) + cmdgrouper.Rule{Pred: glBindBuffer(gles.GLenum_GL_ARRAY_BUFFER)}, // glBindBuffer(GL_ARRAY_BUFFER, 0) + cmdgrouper.Rule{Pred: glBindBuffer(gles.GLenum_GL_ELEMENT_ARRAY_BUFFER)}, // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) + cmdgrouper.Rule{Pred: glBindBuffer(gles.GLenum_GL_DRAW_INDIRECT_BUFFER)}, // glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0) + cmdgrouper.Rule{Pred: glBindBuffer(gles.GLenum_GL_COPY_READ_BUFFER)}, // glBindBuffer(GL_COPY_READ_BUFFER, 0) + cmdgrouper.Rule{Pred: glBindBuffer(gles.GLenum_GL_COPY_WRITE_BUFFER)}, // glBindBuffer(GL_COPY_WRITE_BUFFER, 0) + cmdgrouper.Rule{Pred: glBindBufferBase(gles.GLenum_GL_UNIFORM_BUFFER), Repeats: true}, // glBindBufferBase(GL_UNIFORM_BUFFER, 0..N, 0) + cmdgrouper.Rule{Pred: glBindBufferBase(gles.GLenum_GL_TRANSFORM_FEEDBACK_BUFFER)}, // glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0) + cmdgrouper.Rule{Pred: glBindBufferBase(gles.GLenum_GL_SHADER_STORAGE_BUFFER), Repeats: true}, // glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0..N, 0) + cmdgrouper.Rule{Pred: glBindBufferBase(gles.GLenum_GL_ATOMIC_COUNTER_BUFFER), Repeats: true}, // glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0..N, 0) + cmdgrouper.Rule{Pred: glBindBuffer(gles.GLenum_GL_DISPATCH_INDIRECT_BUFFER)}, // glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0) + cmdgrouper.Rule{Pred: glUseProgram()}, // glUseProgram(0) + cmdgrouper.Rule{Pred: glActiveTextureOrBindTexture(), Repeats: true}, // glActiveTexture(GL_TEXTURE31 .. 0), glBindTexture(GL_TEXTURE_2D, 0) + cmdgrouper.Rule{Pred: glPixelStorei(gles.GLenum_GL_UNPACK_ROW_LENGTH)}, // glPixelStorei(GL_UNPACK_ROW_LENGTH, 0) + cmdgrouper.Rule{Pred: glPixelStorei(gles.GLenum_GL_PACK_ALIGNMENT)}, // glPixelStorei(GL_PACK_ALIGNMENT, 1) + cmdgrouper.Rule{Pred: glPixelStorei(gles.GLenum_GL_UNPACK_ALIGNMENT)}, // glPixelStorei(GL_UNPACK_ALIGNMENT, 1) + cmdgrouper.Rule{Pred: glBindFramebuffer(gles.GLenum_GL_DRAW_FRAMEBUFFER)}, // glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0) + cmdgrouper.Rule{Pred: glBindFramebuffer(gles.GLenum_GL_READ_FRAMEBUFFER)}, // glBindFramebuffer(GL_READ_FRAMEBUFFER, 0) + cmdgrouper.Rule{Pred: glIsVertexArrayRuleOrGenVertexArrays(), Repeats: true}, // glIsVertexArray(array: 1) → 0, glGenVertexArrays(count: 1, arrays: 0xcab3ff84) + cmdgrouper.Rule{Pred: glBindVertexArray()}, // glBindVertexArray(array: 2) + cmdgrouper.Rule{Pred: glDisableVertexAttribArray(), Repeats: true}, // glDisableVertexAttribArray(0 .. N) + cmdgrouper.Rule{Pred: glFrontFace(gles.GLenum_GL_CW), Optional: true}, // glFrontFace(orientation: GL_CW) + ) } diff --git a/gapis/extensions/unity/unity.go b/gapis/extensions/unity/unity.go index ddeca6966a..5a5fcb2584 100644 --- a/gapis/extensions/unity/unity.go +++ b/gapis/extensions/unity/unity.go @@ -25,7 +25,7 @@ func init() { Name: "Unity", CmdGroupers: func() []cmdgrouper.Grouper { return []cmdgrouper.Grouper{ - &stateResetGrouper{}, + newStateResetGrouper(), } }, }) diff --git a/gapis/resolve/cmdgrouper/CMakeFiles.cmake b/gapis/resolve/cmdgrouper/CMakeFiles.cmake index be5c8e5420..984d6c5f98 100644 --- a/gapis/resolve/cmdgrouper/CMakeFiles.cmake +++ b/gapis/resolve/cmdgrouper/CMakeFiles.cmake @@ -19,6 +19,7 @@ set(files cmdgrouper.go + sequence.go ) set(dirs ) diff --git a/gapis/resolve/cmdgrouper/sequence.go b/gapis/resolve/cmdgrouper/sequence.go new file mode 100644 index 0000000000..4fbbcc250d --- /dev/null +++ b/gapis/resolve/cmdgrouper/sequence.go @@ -0,0 +1,111 @@ +// Copyright (C) 2017 Google Inc. +// +// 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 cmdgrouper + +import ( + "context" + + "github.com/google/gapid/core/log" + "github.com/google/gapid/gapis/api" +) + +// Rule is a single rule in a sequence grouper. +type Rule struct { + // Pred returns true if the rule passes. + Pred func(cmd, prev api.Cmd) bool + // Repeats is true if the rule should repeat until it no longer passes. + // The rule has to pass at least once for the sequence to complete. + Repeats bool + // Optional is true if the rule can be skipped and still have the sequence + // complete. + Optional bool +} + +// Sequence returns a Grouper that groups commands that match a sequence of +// rules. +func Sequence(name string, rules ...Rule) Grouper { + return &sequence{name: name, rules: rules} +} + +var _ Grouper = &sequence{} + +type sequence struct { + name string + rules []Rule + rule int + passed bool + prev api.Cmd + start api.CmdID + groups []Group +} + +func (g *sequence) flush(id api.CmdID) { + if g.rule == len(g.rules) { + g.groups = append(g.groups, Group{ + Start: g.start, + End: id, + Name: g.name, + }) + g.rule, g.passed, g.start = 0, false, id + } +} + +// Process considers the command for inclusion in the group. +func (g *sequence) Process(ctx context.Context, id api.CmdID, cmd api.Cmd, s *api.GlobalState) { + const debug = false + + prev := g.prev + g.prev = cmd + if g.start == api.CmdNoID { + g.start = id + } + + for { + g.flush(id) + rule := g.rules[g.rule] + passed := rule.Pred(cmd, prev) + optional := rule.Optional + repeats := rule.Repeats + + switch { + case passed && repeats: + g.passed = true + return + case passed && !repeats: + g.rule++ + g.passed = false + return + // --- below failed --- + case optional, g.passed: + g.rule++ + g.passed = false + continue + default: // failed sequence + if debug && g.rule > 1 { + log.W(ctx, "%v: %v failed at rule %v. cmd: %v", id, g.name, g.rule, cmd) + } + g.rule, g.passed, g.start = 0, false, api.CmdNoID + return + } + } +} + +// Build returns the groups built and resets the state of the grouper. +func (g *sequence) Build(end api.CmdID) []Group { + g.flush(end) + out := g.groups + g.groups = nil + return out +} From 05f2454d6d21b39c33f710401f5439cd7e64ced5 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 13 Sep 2017 12:29:06 +0100 Subject: [PATCH 2/5] gapis/api: Improve AddGroup error messages. --- gapis/api/cmd_id_group.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gapis/api/cmd_id_group.go b/gapis/api/cmd_id_group.go index ba21bf2bab..bec51d329a 100644 --- a/gapis/api/cmd_id_group.go +++ b/gapis/api/cmd_id_group.go @@ -337,9 +337,9 @@ func (g *CmdIDGroup) AddGroup(start, end CmdID, name string) (*CmdIDGroup, error // New group fits entirely within an existing group. Add as subgroup. out, err = first.AddGroup(start, end, name) case sIn && start != first.Range.Start: - return nil, fmt.Errorf("New group '%s' overlaps with existing group '%s'", name, first) + return nil, fmt.Errorf("New group '%v' %v overlaps with existing group '%v'", name, r, first) case eIn && end != last.Range.End: - return nil, fmt.Errorf("New group '%s' overlaps with existing group '%s'", name, last) + return nil, fmt.Errorf("New group '%v' %v overlaps with existing group '%v'", name, r, last) default: // New group completely wraps one or more existing groups. Add the // existing group(s) as subgroups to the new group, and add to the list. From af18138d30e9966adf455595305a7daac41bd4da Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 13 Sep 2017 12:29:53 +0100 Subject: [PATCH 3/5] gapis/resolve: Move resolve.InternalContext to api.ContextInfo. The only reason to have it as a proto is for when we want to store resolvables to disk. We're a long way off from that, so just make it a go struct. Simplifies usage. --- gapis/api/context.go | 23 ++++- gapis/api/gles/gles.go | 4 +- gapis/resolve/command_tree.go | 7 +- gapis/resolve/contexts.go | 106 ++++++++++++++-------- gapis/resolve/filter.go | 7 +- gapis/resolve/resolvables.proto | 7 -- gapis/resolve/service.go | 16 +++- gapis/service/path/path.go | 9 +- test/integration/replay/gles/gles_test.go | 2 +- 9 files changed, 121 insertions(+), 60 deletions(-) diff --git a/gapis/api/context.go b/gapis/api/context.go index f2446b37b4..fa59e921d8 100644 --- a/gapis/api/context.go +++ b/gapis/api/context.go @@ -14,7 +14,12 @@ package api -import "github.com/google/gapid/core/data/id" +import ( + "reflect" + + "github.com/google/gapid/core/data/id" + "github.com/google/gapid/gapis/service/path" +) // ContextID is the unique identifier for a context. type ContextID id.ID @@ -23,9 +28,19 @@ type ContextID id.ID type Context interface { APIObject - // Name returns the display-name of the context. - Name() string - // ID returns the context's unique identifier ID() ContextID } + +// ContextInfo is describes a Context. +// Unlike Context, ContextInfo describes the context at no particular point in +// the trace. +type ContextInfo struct { + Path *path.Context + ID ContextID + API ID + NumCommandsByType map[reflect.Type]int + Name string + Priority int + UserData map[interface{}]interface{} +} diff --git a/gapis/api/gles/gles.go b/gapis/api/gles/gles.go index 4ad5236423..a48451e143 100644 --- a/gapis/api/gles/gles.go +++ b/gapis/api/gles/gles.go @@ -45,12 +45,12 @@ func (s *State) Root(ctx context.Context, p *path.State) (path.Node, error) { if p.Context == nil || !p.Context.IsValid() { return p, nil } - c, err := resolve.Context(ctx, p.After.Capture.Context(p.Context)) + c, err := resolve.Context(ctx, p.After.Capture.Context(p.Context.ID())) if err != nil { return nil, err } for thread, context := range s.Contexts { - if c.ID() == context.ID() { + if c.ID == context.ID() { return s.contextRoot(p.After, thread), nil } } diff --git a/gapis/resolve/command_tree.go b/gapis/resolve/command_tree.go index 7a5f7edda8..38b1be80fd 100644 --- a/gapis/resolve/command_tree.go +++ b/gapis/resolve/command_tree.go @@ -190,11 +190,16 @@ func (r *CommandTreeResolvable) Resolve(ctx context.Context) (interface{}, error if p.IncludeNoContextGroups { noContextID = api.ContextID{} } + ctxs, err := ContextsByID(ctx, p.Capture.Contexts()) + if err != nil { + return nil, err + } groupers = append(groupers, cmdgrouper.Run( func(cmd api.Cmd, s *api.GlobalState) (interface{}, string) { if api := cmd.API(); api != nil { if context := api.Context(s, cmd.Thread()); context != nil { - return context.ID(), context.Name() + id := context.ID() + return id, ctxs[id].Name } } return noContextID, "No context" diff --git a/gapis/resolve/contexts.go b/gapis/resolve/contexts.go index 37a9799b46..41f15d2fbc 100644 --- a/gapis/resolve/contexts.go +++ b/gapis/resolve/contexts.go @@ -16,6 +16,8 @@ package resolve import ( "context" + "fmt" + "reflect" "sort" "github.com/google/gapid/core/data/id" @@ -28,24 +30,43 @@ import ( ) // Contexts resolves the list of contexts belonging to a capture. -func Contexts(ctx context.Context, p *path.Contexts) (*service.Contexts, error) { +func Contexts(ctx context.Context, p *path.Contexts) ([]*api.ContextInfo, error) { obj, err := database.Build(ctx, &ContextListResolvable{p.Capture}) if err != nil { return nil, err } - return obj.(*service.Contexts), nil + return obj.([]*api.ContextInfo), nil +} + +// ContextsByID resolves the list of contexts belonging to a capture. +func ContextsByID(ctx context.Context, p *path.Contexts) (map[api.ContextID]*api.ContextInfo, error) { + ctxs, err := Contexts(ctx, p) + if err != nil { + return nil, err + } + out := map[api.ContextID]*api.ContextInfo{} + for _, c := range ctxs { + out[c.ID] = c + } + return out, nil } // Context resolves the single context. -func Context(ctx context.Context, p *path.Context) (*InternalContext, error) { - boxed, err := database.Resolve(ctx, p.Id.ID()) +func Context(ctx context.Context, p *path.Context) (*api.ContextInfo, error) { + contexts, err := Contexts(ctx, p.Capture.Contexts()) if err != nil { - return nil, &service.ErrInvalidPath{ - Reason: messages.ErrContextDoesNotExist(p.Id), - Path: p.Path(), + return nil, err + } + id := api.ContextID(p.Id.ID()) + for _, c := range contexts { + if c.ID == id { + return c, nil } } - return boxed.(*InternalContext), nil + return nil, &service.ErrInvalidPath{ + Reason: messages.ErrContextDoesNotExist(p.Id), + Path: p.Path(), + } } // Importance is the interface implemeneted by commands that provide an @@ -54,6 +75,11 @@ type Importance interface { Importance() int } +// Named is the interface implemented by context that have a name. +type Named interface { + Name() string +} + // Resolve implements the database.Resolver interface. func (r *ContextListResolvable) Resolve(ctx context.Context) (interface{}, error) { ctx = capture.Put(ctx, r.Capture) @@ -63,8 +89,14 @@ func (r *ContextListResolvable) Resolve(ctx context.Context) (interface{}, error return nil, err } - priorities := map[api.ContextID]int{} - contexts := []api.Context{} + type ctxInfo struct { + ctx api.Context + cnts map[reflect.Type]int + pri int + } + + seen := map[api.ContextID]int{} + contexts := []*ctxInfo{} s := c.NewState() err = api.ForeachCmd(ctx, c.Commands, func(ctx context.Context, i api.CmdID, cmd api.Cmd) error { @@ -81,14 +113,21 @@ func (r *ContextListResolvable) Resolve(ctx context.Context) (interface{}, error } id := context.ID() - p, ok := priorities[id] + idx, ok := seen[id] if !ok { - priorities[id] = p - contexts = append(contexts, context) + idx = len(contexts) + seen[id] = idx + contexts = append(contexts, &ctxInfo{ + ctx: context, + cnts: map[reflect.Type]int{}, + }) } + + c := contexts[idx] + cmdTy := reflect.TypeOf(cmd) + c.cnts[cmdTy] = c.cnts[cmdTy] + 1 if i, ok := cmd.(Importance); ok { - p += i.Importance() - priorities[id] = p + c.pri += i.Importance() } return nil }) @@ -97,32 +136,25 @@ func (r *ContextListResolvable) Resolve(ctx context.Context) (interface{}, error } sort.Slice(contexts, func(i, j int) bool { - return priorities[contexts[i].ID()] < priorities[contexts[j].ID()] + return contexts[i].pri < contexts[j].pri }) - out := &service.Contexts{ - List: make([]*path.Context, len(contexts)), - } - + out := make([]*api.ContextInfo, len(contexts)) for i, c := range contexts { - api := c.API() - ctxID := c.ID() - id, err := database.Store(ctx, &InternalContext{ - Id: ctxID[:], - Api: &path.API{Id: path.NewID(id.ID(api.ID()))}, - Name: c.Name(), - Priority: uint32(i), - }) - if err != nil { - return nil, err + name := fmt.Sprintf("Context %v", i) + if n, ok := c.ctx.(Named); ok { + name = n.Name() + } + out[i] = &api.ContextInfo{ + Path: r.Capture.Context(id.ID(c.ctx.ID())), + ID: c.ctx.ID(), + API: c.ctx.API().ID(), + NumCommandsByType: c.cnts, + Name: name, + Priority: i, + UserData: map[interface{}]interface{}{}, } - out.List[i] = r.Capture.Context(path.NewID(id)) } - return out, nil -} -func (i *InternalContext) ID() api.ContextID { - var out api.ContextID - copy(out[:], i.Id) - return out + return out, nil } diff --git a/gapis/resolve/filter.go b/gapis/resolve/filter.go index f60d45ce25..f72e101f7e 100644 --- a/gapis/resolve/filter.go +++ b/gapis/resolve/filter.go @@ -30,16 +30,15 @@ func buildFilter(ctx context.Context, p *path.Capture, f *path.CommandFilter, sd return !sd.Hidden.Contains(id) }, } - if c := f.GetContext(); c.IsValid() { - c, err := Context(ctx, p.Context(c)) + if f := f.GetContext(); f.IsValid() { + c, err := Context(ctx, p.Context(f.ID())) if err != nil { return nil, err } - ctxID := c.ID() filters = append(filters, func(id api.CmdID, cmd api.Cmd, s *api.GlobalState) bool { if api := cmd.API(); api != nil { if ctx := api.Context(s, cmd.Thread()); ctx != nil { - return ctx.ID() == ctxID + return ctx.ID() == c.ID } } return false diff --git a/gapis/resolve/resolvables.proto b/gapis/resolve/resolvables.proto index e5f6e21a55..f24424c72f 100644 --- a/gapis/resolve/resolvables.proto +++ b/gapis/resolve/resolvables.proto @@ -25,13 +25,6 @@ message ContextListResolvable { path.Capture capture = 1; } -message InternalContext { - bytes id = 1; - path.API api = 2; - string name = 3; - uint32 priority = 4; -} - message CommandTreeResolvable { path.CommandTree path = 1; } diff --git a/gapis/resolve/service.go b/gapis/resolve/service.go index 1eec847b30..222bfcd2f4 100644 --- a/gapis/resolve/service.go +++ b/gapis/resolve/service.go @@ -15,17 +15,29 @@ package resolve import ( + "github.com/google/gapid/core/data/id" "github.com/google/gapid/gapis/api" "github.com/google/gapid/gapis/service" "github.com/google/gapid/gapis/service/box" + "github.com/google/gapid/gapis/service/path" ) func internalToService(v interface{}) (interface{}, error) { switch v := v.(type) { case api.Cmd: return api.CmdToService(v) - case *InternalContext: - return &service.Context{Name: v.Name, Api: v.Api, Priority: v.Priority}, nil + case []*api.ContextInfo: + out := &service.Contexts{List: make([]*path.Context, len(v))} + for i, c := range v { + out.List[i] = c.Path + } + return out, nil + case *api.ContextInfo: + return &service.Context{ + Name: v.Name, + Api: path.NewAPI(id.ID(v.API)), + Priority: uint32(v.Priority), + }, nil default: return v, nil } diff --git a/gapis/service/path/path.go b/gapis/service/path/path.go index 77e603c04f..60b54229f1 100644 --- a/gapis/service/path/path.go +++ b/gapis/service/path/path.go @@ -442,6 +442,11 @@ func FindCapture(n Node) *Capture { return nil } +// NewAPI returns a new Api path node with the given ID. +func NewAPI(id id.ID) *API { + return &API{Id: NewID(id)} +} + // NewCapture returns a new Capture path node with the given ID. func NewCapture(id id.ID) *Capture { return &Capture{Id: NewID(id)} @@ -564,8 +569,8 @@ func (n *Capture) Command(i uint64, subidx ...uint64) *Command { } // Context returns the path node to the a context with the given ID. -func (n *Capture) Context(id *ID) *Context { - return &Context{Capture: n, Id: id} +func (n *Capture) Context(id id.ID) *Context { + return &Context{Capture: n, Id: NewID(id)} } // Thread returns the path node to the thread with the given ID. diff --git a/test/integration/replay/gles/gles_test.go b/test/integration/replay/gles/gles_test.go index 00026ce916..33cd4cc0b5 100644 --- a/test/integration/replay/gles/gles_test.go +++ b/test/integration/replay/gles/gles_test.go @@ -616,7 +616,7 @@ func TestMultiContextCapture(t *testing.T) { contexts, err := resolve.Contexts(ctx, capture.Contexts()) assert.With(ctx).ThatError(err).Succeeded() - assert.With(ctx).That(len(contexts.List)).Equals(3) + assert.With(ctx).That(len(contexts)).Equals(3) } func TestTraceWithIssues(t *testing.T) { From 22298ede6a87fa5714ea259ffb6c70aaee4da29a Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 13 Sep 2017 17:18:06 +0100 Subject: [PATCH 4/5] Add gvr extension to group reproduction contexts sensibly. --- gapis/api/gvr/CMakeFiles.cmake | 1 + gapis/api/gvr/extension.go | 185 ++++++++++++++++++++++++++++++++ gapis/extensions/extensions.go | 15 ++- gapis/extensions/unity/unity.go | 5 +- gapis/resolve/command_tree.go | 2 +- gapis/resolve/contexts.go | 7 ++ gapis/resolve/events.go | 15 +++ 7 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 gapis/api/gvr/extension.go diff --git a/gapis/api/gvr/CMakeFiles.cmake b/gapis/api/gvr/CMakeFiles.cmake index f791f02b84..d462d1fff8 100644 --- a/gapis/api/gvr/CMakeFiles.cmake +++ b/gapis/api/gvr/CMakeFiles.cmake @@ -22,6 +22,7 @@ set(files constant_sets.go convert.go enum.go + extension.go framebindings.go gvr.api gvr.go diff --git a/gapis/api/gvr/extension.go b/gapis/api/gvr/extension.go new file mode 100644 index 0000000000..41a180623b --- /dev/null +++ b/gapis/api/gvr/extension.go @@ -0,0 +1,185 @@ +// Copyright (C) 2017 Google Inc. +// +// 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 gvr + +import ( + "context" + "reflect" + + "github.com/google/gapid/gapis/api" + "github.com/google/gapid/gapis/api/gles" + "github.com/google/gapid/gapis/extensions" + "github.com/google/gapid/gapis/resolve" + "github.com/google/gapid/gapis/resolve/cmdgrouper" + "github.com/google/gapid/gapis/service" + "github.com/google/gapid/gapis/service/path" +) + +func init() { + extensions.Register(extensions.Extension{ + Name: "GVR", + AdjustContexts: adjustContexts, + CmdGroupers: newReprojectionGroupers, + Events: newReprojectionEvents, + }) +} + +type contextUsage int + +const ( + rendererCtx = contextUsage(iota) + reprojectionCtx +) + +func adjustContexts(ctx context.Context, ctxs []*api.ContextInfo) { + // Look for the renderer context. + tyGvrFrameSubmit := reflect.TypeOf(&Gvr_frame_submit{}) + if c := findContextByCommand(ctxs, tyGvrFrameSubmit); c != nil { + c.UserData[rendererCtx] = true + c.Name = "Main context (" + c.Name + ")" + } + + // Look for the reprojection context. + tyGlFlush := reflect.TypeOf(&gles.GlFlush{}) + if c := findContextByCommand(ctxs, tyGlFlush); c != nil { + c.UserData[reprojectionCtx] = true + c.Name = "Reprojection context" + } +} + +func findContextByCommand(ctxs []*api.ContextInfo, ty reflect.Type) *api.ContextInfo { + highest, best := 0, (*api.ContextInfo)(nil) + for _, c := range ctxs { + if count := c.NumCommandsByType[ty]; count > highest { + highest, best = count, c + } + } + return best +} + +func isReprojectionContext(ctx context.Context, p *path.Context) bool { + // Only group if we're looking at the reprojection thread. + if p == nil { + return false + } + c, err := resolve.Context(ctx, p) + if c == nil || err != nil { + return false + } + _, ok := c.UserData[reprojectionCtx] + return ok +} + +func newReprojectionGroupers(ctx context.Context, p *path.CommandTree) []cmdgrouper.Grouper { + if !isReprojectionContext(ctx, p.Capture.Context(p.GetFilter().GetContext().ID())) { + return nil + } + glFenceSync := func(cond gles.GLenum) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlFenceSync) + return ok && c.Condition == cond + } + } + glEndTilingQCOM := func(cond gles.GLbitfield) func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + c, ok := cmd.(*gles.GlEndTilingQCOM) + return ok && c.PreserveMask == cond + } + } + eglDestroySyncKHR := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + _, ok := cmd.(*gles.EglDestroySyncKHR) + return ok + } + } + glClientWaitSync := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + _, ok := cmd.(*gles.GlClientWaitSync) + return ok + } + } + glDeleteSync := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + _, ok := cmd.(*gles.GlDeleteSync) + return ok + } + } + glDrawElements := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + _, ok := cmd.(*gles.GlDrawElements) + return ok + } + } + notGlDrawElements := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + _, ok := cmd.(*gles.GlDrawElements) + return !ok + } + } + notGlFlush := func() func(cmd, prev api.Cmd) bool { + return func(cmd, prev api.Cmd) bool { + _, ok := cmd.(*gles.GlFlush) + return !ok + } + } + return []cmdgrouper.Grouper{ + cmdgrouper.Sequence("Left eye", + cmdgrouper.Rule{Pred: eglDestroySyncKHR()}, + cmdgrouper.Rule{Pred: glClientWaitSync()}, + cmdgrouper.Rule{Pred: glDeleteSync()}, + cmdgrouper.Rule{Pred: notGlDrawElements(), Repeats: true}, + cmdgrouper.Rule{Pred: glDrawElements()}, + ), + cmdgrouper.Sequence("Right eye", + cmdgrouper.Rule{Pred: glFenceSync(gles.GLenum_GL_SYNC_GPU_COMMANDS_COMPLETE)}, + cmdgrouper.Rule{Pred: glEndTilingQCOM(1), Optional: true}, + cmdgrouper.Rule{Pred: glClientWaitSync()}, + cmdgrouper.Rule{Pred: glDeleteSync()}, + cmdgrouper.Rule{Pred: notGlDrawElements(), Repeats: true}, + cmdgrouper.Rule{Pred: glDrawElements()}, + cmdgrouper.Rule{Pred: notGlFlush(), Repeats: true}, + ), + } +} + +func newReprojectionEvents(ctx context.Context, p *path.Events) extensions.EventProvider { + if !isReprojectionContext(ctx, p.Capture.Context(p.GetFilter().GetContext().ID())) { + return nil + } + + var pending []service.EventKind + return func(ctx context.Context, id api.CmdID, cmd api.Cmd, s *api.GlobalState) []*service.Event { + events := []*service.Event{} + for _, kind := range pending { + events = append(events, &service.Event{ + Kind: kind, + Command: p.Capture.Command(uint64(id)), + }) + } + pending = nil + if _, ok := cmd.(*gles.GlFlush); ok { + if p.LastInFrame { + events = append(events, &service.Event{ + Kind: service.EventKind_LastInFrame, + Command: p.Capture.Command(uint64(id)), + }) + } + if p.FirstInFrame { + pending = append(pending, service.EventKind_FirstInFrame) + } + } + return events + } +} diff --git a/gapis/extensions/extensions.go b/gapis/extensions/extensions.go index 9bae77a00f..6a8e70728f 100644 --- a/gapis/extensions/extensions.go +++ b/gapis/extensions/extensions.go @@ -19,9 +19,13 @@ package extensions import ( + "context" "sync" + "github.com/google/gapid/gapis/api" "github.com/google/gapid/gapis/resolve/cmdgrouper" + "github.com/google/gapid/gapis/service" + "github.com/google/gapid/gapis/service/path" ) var ( @@ -29,13 +33,22 @@ var ( mutex sync.Mutex ) +// EventProvider is a function that produces events for the given command and +// state. +type EventProvider func(ctx context.Context, id api.CmdID, cmd api.Cmd, s *api.GlobalState) []*service.Event + // Extension is a GAPIS extension. // It should be registered at application initialization with Register. type Extension struct { // Name of the extension. Name string + // AdjustContexts lets the extension rename or reprioritize the list of + // contexts. + AdjustContexts func(context.Context, []*api.ContextInfo) // Custom command groupers. - CmdGroupers func() []cmdgrouper.Grouper + CmdGroupers func(ctx context.Context, p *path.CommandTree) []cmdgrouper.Grouper + // Custom events provider. + Events func(ctx context.Context, p *path.Events) EventProvider } // Register registers the extension e. diff --git a/gapis/extensions/unity/unity.go b/gapis/extensions/unity/unity.go index 5a5fcb2584..ceebdd68b2 100644 --- a/gapis/extensions/unity/unity.go +++ b/gapis/extensions/unity/unity.go @@ -16,14 +16,17 @@ package unity import ( + "context" + "github.com/google/gapid/gapis/extensions" "github.com/google/gapid/gapis/resolve/cmdgrouper" + "github.com/google/gapid/gapis/service/path" ) func init() { extensions.Register(extensions.Extension{ Name: "Unity", - CmdGroupers: func() []cmdgrouper.Grouper { + CmdGroupers: func(ctx context.Context, p *path.CommandTree) []cmdgrouper.Grouper { return []cmdgrouper.Grouper{ newStateResetGrouper(), } diff --git a/gapis/resolve/command_tree.go b/gapis/resolve/command_tree.go index 38b1be80fd..0ee5427b0c 100644 --- a/gapis/resolve/command_tree.go +++ b/gapis/resolve/command_tree.go @@ -220,7 +220,7 @@ func (r *CommandTreeResolvable) Resolve(ctx context.Context) (interface{}, error // Add any extension groupers for _, e := range extensions.Get() { - groupers = append(groupers, e.CmdGroupers()...) + groupers = append(groupers, e.CmdGroupers(ctx, p)...) } // Walk the list of unfiltered commands to build the groups. diff --git a/gapis/resolve/contexts.go b/gapis/resolve/contexts.go index 41f15d2fbc..0c62126a2d 100644 --- a/gapis/resolve/contexts.go +++ b/gapis/resolve/contexts.go @@ -24,6 +24,7 @@ import ( "github.com/google/gapid/gapis/api" "github.com/google/gapid/gapis/capture" "github.com/google/gapid/gapis/database" + "github.com/google/gapid/gapis/extensions" "github.com/google/gapid/gapis/messages" "github.com/google/gapid/gapis/service" "github.com/google/gapid/gapis/service/path" @@ -156,5 +157,11 @@ func (r *ContextListResolvable) Resolve(ctx context.Context) (interface{}, error } } + for _, e := range extensions.Get() { + if e.AdjustContexts != nil { + e.AdjustContexts(ctx, out) + } + } + return out, nil } diff --git a/gapis/resolve/events.go b/gapis/resolve/events.go index ec28d4022a..c3b90cd414 100644 --- a/gapis/resolve/events.go +++ b/gapis/resolve/events.go @@ -19,6 +19,7 @@ import ( "github.com/google/gapid/gapis/api" "github.com/google/gapid/gapis/capture" + "github.com/google/gapid/gapis/extensions" "github.com/google/gapid/gapis/service" "github.com/google/gapid/gapis/service/path" ) @@ -40,6 +41,16 @@ func Events(ctx context.Context, p *path.Events) (*service.Events, error) { return nil, err } + // Add any extension events + eps := []extensions.EventProvider{} + for _, e := range extensions.Get() { + if e.Events != nil { + if ep := e.Events(ctx, p); ep != nil { + eps = append(eps, ep) + } + } + } + events := []*service.Event{} s := c.NewState() @@ -138,6 +149,10 @@ func Events(ctx context.Context, p *path.Events) (*service.Events, error) { }) } + for _, ep := range eps { + events = append(events, ep(ctx, id, cmd, s)...) + } + lastCmd = id return nil }) From 610671e968e4a566f0cf396aef77c27a5a85ee12 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 13 Sep 2017 18:00:41 +0100 Subject: [PATCH 5/5] gvr: Don't put extra Draw groups inside the reprojection context. --- gapis/api/gvr/extension.go | 23 +++++++++++++++++++---- gapis/resolve/cmdgrouper/cmdgrouper.go | 11 ++++++----- gapis/resolve/command_tree.go | 23 ++++++++++++++++------- gapis/resolve/thumbnail.go | 2 +- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/gapis/api/gvr/extension.go b/gapis/api/gvr/extension.go index 41a180623b..cdf609f33d 100644 --- a/gapis/api/gvr/extension.go +++ b/gapis/api/gvr/extension.go @@ -135,14 +135,14 @@ func newReprojectionGroupers(ctx context.Context, p *path.CommandTree) []cmdgrou } } return []cmdgrouper.Grouper{ - cmdgrouper.Sequence("Left eye", + noSubFrameEventGrouper{cmdgrouper.Sequence("Left eye", cmdgrouper.Rule{Pred: eglDestroySyncKHR()}, cmdgrouper.Rule{Pred: glClientWaitSync()}, cmdgrouper.Rule{Pred: glDeleteSync()}, cmdgrouper.Rule{Pred: notGlDrawElements(), Repeats: true}, cmdgrouper.Rule{Pred: glDrawElements()}, - ), - cmdgrouper.Sequence("Right eye", + )}, + noSubFrameEventGrouper{cmdgrouper.Sequence("Right eye", cmdgrouper.Rule{Pred: glFenceSync(gles.GLenum_GL_SYNC_GPU_COMMANDS_COMPLETE)}, cmdgrouper.Rule{Pred: glEndTilingQCOM(1), Optional: true}, cmdgrouper.Rule{Pred: glClientWaitSync()}, @@ -150,10 +150,25 @@ func newReprojectionGroupers(ctx context.Context, p *path.CommandTree) []cmdgrou cmdgrouper.Rule{Pred: notGlDrawElements(), Repeats: true}, cmdgrouper.Rule{Pred: glDrawElements()}, cmdgrouper.Rule{Pred: notGlFlush(), Repeats: true}, - ), + )}, } } +type noSubFrameEventGrouper struct { + cmdgrouper.Grouper +} + +func (n noSubFrameEventGrouper) Build(end api.CmdID) []cmdgrouper.Group { + out := n.Grouper.Build(end) + for i := range out { + out[i].UserData = &resolve.CmdGroupData{ + Thumbnail: api.CmdNoID, + NoFrameEventGroups: true, + } + } + return out +} + func newReprojectionEvents(ctx context.Context, p *path.Events) extensions.EventProvider { if !isReprojectionContext(ctx, p.Capture.Context(p.GetFilter().GetContext().ID())) { return nil diff --git a/gapis/resolve/cmdgrouper/cmdgrouper.go b/gapis/resolve/cmdgrouper/cmdgrouper.go index ea13029a6b..57eeb20874 100644 --- a/gapis/resolve/cmdgrouper/cmdgrouper.go +++ b/gapis/resolve/cmdgrouper/cmdgrouper.go @@ -24,9 +24,10 @@ import ( // Group is the product of a Grouper. type Group struct { - Start api.CmdID - End api.CmdID - Name string + Start api.CmdID + End api.CmdID + Name string + UserData interface{} } // Grouper is the interface implemented by types that build groups. @@ -60,7 +61,7 @@ func (g *run) Process(ctx context.Context, id api.CmdID, cmd api.Cmd, s *api.Glo val, name := g.f(cmd, s) if val != g.current { if g.current != nil { - g.out = append(g.out, Group{g.start, id, g.name}) + g.out = append(g.out, Group{g.start, id, g.name, nil}) } g.start = id } @@ -69,7 +70,7 @@ func (g *run) Process(ctx context.Context, id api.CmdID, cmd api.Cmd, s *api.Glo func (g *run) Build(end api.CmdID) []Group { if g.current != nil && g.start != end { - g.out = append(g.out, Group{g.start, end, g.name}) + g.out = append(g.out, Group{g.start, end, g.name, nil}) } out := g.out g.out, g.start, g.current, g.name = nil, 0, nil, "" diff --git a/gapis/resolve/command_tree.go b/gapis/resolve/command_tree.go index 0ee5427b0c..c17858c309 100644 --- a/gapis/resolve/command_tree.go +++ b/gapis/resolve/command_tree.go @@ -30,8 +30,12 @@ import ( "github.com/google/gapid/gapis/service/path" ) -type CommandTreeNodeUserData struct { +// CmdGroupData is the additional metadata assigned to api.CmdIDGroups UserData +// field. +type CmdGroupData struct { Thumbnail api.CmdID + // If true, then children frame event groups should not be added to this group. + NoFrameEventGroups bool } // CommandTree resolves the specified command tree path. @@ -245,7 +249,9 @@ func (r *CommandTreeResolvable) Resolve(ctx context.Context) (interface{}, error } for _, g := range groupers { for _, l := range g.Build(api.CmdID(len(c.Commands))) { - out.root.AddGroup(l.Start, l.End, l.Name) + if group, err := out.root.AddGroup(l.Start, l.End, l.Name); err == nil { + group.UserData = l.UserData + } } } @@ -298,7 +304,6 @@ func (r *CommandTreeResolvable) Resolve(ctx context.Context) (interface{}, error markers := snc.SubCommandMarkerGroups.Value(append([]uint64{uint64(id)}, x[0:len(x)-1]...)).([]*api.CmdIDGroup) r.AddSubCmdMarkerGroups(x[0:len(x)-1], markers) } - // r.Insert([]uint64{uint64(id)}, append([]uint64{}, x...)) r.Insert(append([]uint64{}, x...)) } return nil @@ -349,6 +354,10 @@ func addFrameEventGroups( break } + if data, ok := group.UserData.(*CmdGroupData); ok && data.NoFrameEventGroups { + continue + } + // Start with group of size 1 and grow it backward as long as nothing gets in the way. start := i for start >= group.Bounds().Start+1 && group.Spans.IndexOf(start-1) == -1 { @@ -400,7 +409,7 @@ func addFrameGroups(ctx context.Context, events *service.Events, p *path.Command group, _ := t.root.AddGroup(frameStart, frameEnd+1, fmt.Sprintf("Frame %v", frameCount)) if group != nil { - group.UserData = &CommandTreeNodeUserData{Thumbnail: i} + group.UserData = &CmdGroupData{Thumbnail: i} } } } @@ -410,15 +419,15 @@ func addFrameGroups(ctx context.Context, events *service.Events, p *path.Command } func setThumbnails(ctx context.Context, g *api.CmdIDGroup, drawOrClearCmds api.Spans) { - data, _ := g.UserData.(*CommandTreeNodeUserData) + data, _ := g.UserData.(*CmdGroupData) if data == nil { - data = &CommandTreeNodeUserData{Thumbnail: api.CmdNoID} + data = &CmdGroupData{Thumbnail: api.CmdNoID} g.UserData = data } if data.Thumbnail == api.CmdNoID { if s, c := interval.Intersect(drawOrClearCmds, g.Bounds().Span()); c > 0 { thumbnail := drawOrClearCmds[s+c-1].Bounds().Start - g.UserData = &CommandTreeNodeUserData{Thumbnail: thumbnail} + data.Thumbnail = thumbnail } } diff --git a/gapis/resolve/thumbnail.go b/gapis/resolve/thumbnail.go index 7a0ed2dbfa..d7acda2791 100644 --- a/gapis/resolve/thumbnail.go +++ b/gapis/resolve/thumbnail.go @@ -84,7 +84,7 @@ func CommandTreeNodeThumbnail(ctx context.Context, w, h uint32, f *image.Format, switch item := cmdTree.index(p.Indices).(type) { case api.CmdIDGroup: thumbnail := item.Range.Last() - if userData, ok := item.UserData.(*CommandTreeNodeUserData); ok { + if userData, ok := item.UserData.(*CmdGroupData); ok { if userData.Thumbnail != api.CmdNoID { thumbnail = userData.Thumbnail }