| // Copyright (C) 2015 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 gles |
| |
| import ( |
| "android.googlesource.com/platform/tools/gpu/atom" |
| "android.googlesource.com/platform/tools/gpu/atom/transform" |
| "android.googlesource.com/platform/tools/gpu/config" |
| "android.googlesource.com/platform/tools/gpu/database" |
| "android.googlesource.com/platform/tools/gpu/gfxapi" |
| "android.googlesource.com/platform/tools/gpu/log" |
| "android.googlesource.com/platform/tools/gpu/memory" |
| "android.googlesource.com/platform/tools/gpu/replay" |
| "android.googlesource.com/platform/tools/gpu/service" |
| "android.googlesource.com/platform/tools/gpu/service/path" |
| ) |
| |
| var ( |
| // Interface compliance tests |
| _ = replay.QueryColorBuffer(api{}) |
| _ = replay.QueryDepthBuffer(api{}) |
| _ = replay.QueryCallDurations(api{}) |
| ) |
| |
| // drawConfig is a replay Config used by colorBufferRequest and |
| // depthBufferRequests. |
| type drawConfig struct { |
| wireframe bool |
| } |
| |
| // uniqueConfig returns a replay.Config that is guaranteed to be unique. |
| // Any requests made with a Config returned from uniqueConfig will not be |
| // batched with any other request. |
| func uniqueConfig() replay.Config { |
| return &struct{}{} |
| } |
| |
| // colorBufferRequest requests a postback of the framebuffer's color attachment. |
| type colorBufferRequest struct { |
| after atom.ID |
| width, height uint32 |
| out chan replay.Image |
| } |
| |
| // colorBufferRequest requests a postback of the framebuffer's depth attachment. |
| type depthBufferRequest struct { |
| after atom.ID |
| out chan replay.Image |
| } |
| |
| // timeCallsRequest requests a postback of atom timing information. |
| type timeCallsRequest struct { |
| out chan replay.CallTiming |
| flags service.TimingFlags |
| } |
| |
| func (a api) Replay( |
| ctx replay.Context, |
| cfg replay.Config, |
| requests []replay.Request, |
| device *service.Device, |
| atoms atom.List, |
| out atom.Writer, |
| db database.Database, |
| logger log.Logger) error { |
| |
| transforms := atom.Transforms{} |
| |
| // Terminate after all atoms of interest. |
| earlyTerminator := &transform.EarlyTerminator{} |
| |
| // Skip unnecessary draw calls. |
| skipDrawCalls := &transform.SkipDrawCalls{} |
| |
| // Injector of new atoms. |
| injector := &transform.Injector{} |
| |
| profiling := false |
| |
| for _, req := range requests { |
| switch req := req.(type) { |
| case colorBufferRequest: |
| earlyTerminator.Add(req.after) |
| skipDrawCalls.Draw(req.after) |
| injector.Inject(req.after, readFramebufferColor(req.width, req.height, req.out)) |
| |
| case depthBufferRequest: |
| earlyTerminator.Add(req.after) |
| skipDrawCalls.Draw(req.after) |
| injector.Inject(req.after, readFramebufferDepth(req.out)) |
| |
| case timeCallsRequest: |
| profiling = true |
| transforms.Add(timingInfo(req.flags, req.out, device, db, logger)) |
| } |
| } |
| |
| if !profiling { |
| // Not profiling. Add optimisation transforms. |
| transforms.Add(earlyTerminator, skipDrawCalls) |
| } |
| |
| transforms.Add( |
| injector, |
| remapAttributes()) |
| |
| // Device-dependent transforms. |
| transforms.Add( |
| decompressTextures(device, &path.Capture{ID: ctx.Capture}, db, logger), |
| precisionStrip(device, db, logger), |
| halfFloatOESToHalfFloatARB(device)) |
| |
| if c, ok := cfg.(drawConfig); ok && c.wireframe { |
| transforms.Add(wireframe(db, logger)) |
| } |
| |
| // Cleanup |
| transforms.Add(&destroyResourcesAtEOS{ |
| state: gfxapi.NewState(), |
| db: db, |
| logger: logger, |
| }) |
| |
| if config.DebugReplay { |
| log.Infof(logger, "Replaying %d atoms using transform chain:", len(atoms.Atoms)) |
| for i, t := range transforms { |
| log.Infof(logger, "(%d) %#v", i, t) |
| } |
| } |
| |
| transforms.Transform(atoms, out) |
| |
| return nil |
| } |
| |
| func (a api) QueryColorBuffer(ctx *replay.Context, mgr *replay.Manager, after atom.ID, width, height uint32, wireframe bool) <-chan replay.Image { |
| out := make(chan replay.Image, 1) |
| c := drawConfig{wireframe: wireframe} |
| r := colorBufferRequest{after: after, width: width, height: height, out: out} |
| if err := mgr.Replay(ctx, c, r, a); err != nil { |
| out <- replay.Image{Error: err} |
| } |
| return out |
| } |
| |
| func (a api) QueryDepthBuffer(ctx *replay.Context, mgr *replay.Manager, after atom.ID) <-chan replay.Image { |
| out := make(chan replay.Image, 1) |
| c := drawConfig{} |
| r := depthBufferRequest{after: after, out: out} |
| if err := mgr.Replay(ctx, c, r, a); err != nil { |
| out <- replay.Image{Error: err} |
| } |
| return out |
| } |
| |
| func (a api) QueryCallDurations(ctx *replay.Context, mgr *replay.Manager, flags service.TimingFlags) <-chan replay.CallTiming { |
| out := make(chan replay.CallTiming, 1) |
| c := uniqueConfig() |
| r := timeCallsRequest{flags: flags, out: out} |
| if err := mgr.Replay(ctx, c, r, a); err != nil { |
| out <- replay.CallTiming{Error: err} |
| } |
| return out |
| } |
| |
| // halfFloatOESToHalfFloatARB returns a transform that converts all vertex streams |
| // declared of type GL_HALF_FLOAT_OES to GL_HALF_FLOAT_ARB, if unsupported by the target device. |
| func halfFloatOESToHalfFloatARB(device *service.Device) atom.Transformer { |
| if v, err := ParseVersion(device.Version); err == nil { |
| if v.IsES && device.HasExtension("GL_OES_vertex_half_float") { |
| return nil |
| } |
| } |
| // TODO: fallback to full GL_FLOAT unpacking if GL_ARB_half_float_vertex isn't supported. |
| return atom.Transform("HalfFloatOESToHalfFloatARB", func(id atom.ID, a atom.Atom, out atom.Writer) { |
| if cmd, ok := a.(*GlVertexAttribPointer); ok && |
| cmd.Type == GLenum_GL_HALF_FLOAT_OES { |
| out.Write(id, &GlVertexAttribPointer{ |
| Location: cmd.Location, |
| Size: cmd.Size, |
| Type: GLenum_GL_HALF_FLOAT_ARB, |
| Normalized: cmd.Normalized, |
| Stride: cmd.Stride, |
| Data: cmd.Data, |
| }) |
| } else { |
| out.Write(id, a) |
| } |
| }) |
| } |
| |
| // destroyResourcesAtEOS is a transform that destroys all textures, |
| // framebuffers, buffers, shaders, programs and vertex-arrays that were not |
| // destroyed by EOS. |
| type destroyResourcesAtEOS struct { |
| state *gfxapi.State |
| db database.Database |
| logger log.Logger |
| } |
| |
| func (t *destroyResourcesAtEOS) Transform(id atom.ID, a atom.Atom, out atom.Writer) { |
| a.Mutate(t.state, t.db, t.logger) |
| out.Write(id, a) |
| } |
| |
| func (t *destroyResourcesAtEOS) Flush(out atom.Writer) { |
| id := atom.NoID |
| c := getContext(t.state) |
| if c == nil { |
| return |
| } |
| |
| a, d, l := t.state.Architecture, t.db, t.logger |
| |
| // Delete all Renderbuffers. |
| renderbuffers := make([]RenderbufferId, 0, len(c.Instances.Renderbuffers)-3) |
| for renderbufferId := range c.Instances.Renderbuffers { |
| // Skip virtual renderbuffers: backbuffer_color(-1), backbuffer_depth(-2), backbuffer_stencil(-3). |
| if renderbufferId < 0xf0000000 { |
| renderbuffers = append(renderbuffers, renderbufferId) |
| } |
| } |
| if len(renderbuffers) > 0 { |
| out.Write(id, |
| NewGlDeleteRenderbuffers(int32(len(renderbuffers)), memory.Tmp). |
| AddRead(atom.Data(a, d, l, memory.Tmp, renderbuffers))) |
| } |
| |
| // Delete all Textures. |
| textures := make([]TextureId, 0, len(c.Instances.Textures)) |
| for textureId := range c.Instances.Textures { |
| textures = append(textures, textureId) |
| } |
| if len(textures) > 0 { |
| out.Write(id, |
| NewGlDeleteTextures(int32(len(textures)), memory.Tmp). |
| AddRead(atom.Data(a, d, l, memory.Tmp, textures))) |
| } |
| |
| // Delete all Framebuffers. |
| framebuffers := make([]FramebufferId, 0, len(c.Instances.Framebuffers)) |
| for framebufferId := range c.Instances.Framebuffers { |
| framebuffers = append(framebuffers, framebufferId) |
| } |
| if len(framebuffers) > 0 { |
| out.Write(id, |
| NewGlDeleteFramebuffers(int32(len(framebuffers)), memory.Tmp). |
| AddRead(atom.Data(a, d, l, memory.Tmp, framebuffers))) |
| } |
| |
| // Delete all Buffers. |
| buffers := make([]BufferId, 0, len(c.Instances.Buffers)) |
| for bufferId := range c.Instances.Buffers { |
| buffers = append(buffers, bufferId) |
| } |
| if len(buffers) > 0 { |
| out.Write(id, |
| NewGlDeleteBuffers(int32(len(buffers)), memory.Tmp). |
| AddRead(atom.Data(a, d, l, memory.Tmp, buffers))) |
| } |
| |
| // Delete all VertexArrays. |
| vertexArrays := make([]VertexArrayId, 0, len(c.Instances.VertexArrays)) |
| for vertexArrayId := range c.Instances.VertexArrays { |
| vertexArrays = append(vertexArrays, vertexArrayId) |
| } |
| if len(vertexArrays) > 0 { |
| out.Write(id, |
| NewGlDeleteVertexArraysOES(int32(len(vertexArrays)), memory.Tmp). |
| AddRead(atom.Data(a, d, l, memory.Tmp, vertexArrays))) |
| } |
| |
| // Delete all Shaders. |
| for shaderId := range c.Instances.Shaders { |
| out.Write(id, NewGlDeleteShader(shaderId)) |
| } |
| |
| // Delete all Programs. |
| for programId := range c.Instances.Programs { |
| out.Write(id, NewGlDeleteProgram(programId)) |
| } |
| |
| // Delete all Queries. |
| queries := make([]QueryId, 0, len(c.Instances.Queries)) |
| for queryId := range c.Instances.Queries { |
| queries = append(queries, queryId) |
| } |
| if len(queries) > 0 { |
| out.Write(id, |
| NewGlDeleteQueries(int32(len(queries)), memory.Tmp). |
| AddRead(atom.Data(a, d, l, memory.Tmp, queries))) |
| } |
| } |