blob: 5fe239e336ea7992b1c3de11462f51f86689c9f3 [file] [log] [blame]
// 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)))
}
}