blob: afd2afaaf4fbbbf8cdaa7418128257a29ef5c8d5 [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/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"
)
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
mask service.TimingMask
}
func (a api) ReplayTransforms(
ctx replay.Context,
config replay.Config,
requests []replay.Request,
postback replay.Postback,
device *service.Device,
db database.Database,
logger log.Logger) atom.Transforms {
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,
postback(func(data interface{}, err error) {
if err == nil {
req.out <- replay.Image{Data: data.([]byte)}
} else {
req.out <- replay.Image{Error: err}
}
}), readFramebufferColor{
width: req.width,
height: req.height,
},
)
case depthBufferRequest:
earlyTerminator.Add(req.after)
skipDrawCalls.Draw(req.after)
injector.Inject(
req.after,
postback(func(data interface{}, err error) {
if err == nil {
req.out <- replay.Image{Data: data.([]byte)}
} else {
req.out <- replay.Image{Error: err}
}
}), readFramebufferDepth{
database: db,
},
)
case timeCallsRequest:
profiling = true
transforms.Add(&timingInfoTransform{
postback: postback,
out: req.out,
perCommand: (req.mask & service.TimingMaskTimingPerCommand) != 0,
perDrawCall: (req.mask & service.TimingMaskTimingPerDrawCall) != 0,
perFrame: (req.mask & service.TimingMaskTimingPerFrame) != 0,
timerStartId: make(map[uint8]atom.ID),
})
}
}
if !profiling {
// Not profiling. Add optimisation transforms.
transforms.Add(earlyTerminator, skipDrawCalls)
}
transforms.Add(injector)
// TODO: These features should be testing capabilites returned by extensions.
if device.RequiresShaderPatching {
transforms.Add(
precisionStrip(db, logger),
halfFloatOESToHalfFloatARB(),
decompressTextures(ctx.CaptureID, db, logger),
)
}
if c, ok := config.(drawConfig); ok && c.wireframe {
transforms.Add(wireframe(db, logger))
}
// Cleanup
transforms.Add(&destroyResourcesAtEOS{
state: gfxapi.NewState(),
db: db,
logger: logger,
})
return transforms
}
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, mask service.TimingMask) <-chan replay.CallTiming {
out := make(chan replay.CallTiming, 1)
c := uniqueConfig()
r := timeCallsRequest{mask: mask, 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.
func halfFloatOESToHalfFloatARB() atom.Transformer {
// https://www.opengl.org/registry/specs/ARB/half_float_pixel.txt
const GL_HALF_FLOAT_ARB = 0x140B
return atom.Transform("HalfFloatOESToHalfFloatARB", func(id atom.ID, a atom.Atom, out atom.Writer) {
if cmd, ok := a.(*GlVertexAttribPointer); ok &&
cmd.Type == VertexAttribType_GL_HALF_FLOAT_OES {
out.Write(id, &GlVertexAttribPointer{
Location: cmd.Location,
Size: cmd.Size,
Type: 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 := []RenderbufferId{}
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.Base).
AddRead(atom.Data(a, d, l, memory.Tmp.Base, renderbuffers)))
}
// Delete all Textures.
textures := []TextureId{}
for textureId := range c.Instances.Textures {
textures = append(textures, textureId)
}
if len(textures) > 0 {
out.Write(id,
NewGlDeleteTextures(int32(len(textures)), memory.Tmp.Base).
AddRead(atom.Data(a, d, l, memory.Tmp.Base, textures)))
}
// Delete all Framebuffers.
framebuffers := []FramebufferId{}
for framebufferId := range c.Instances.Framebuffers {
framebuffers = append(framebuffers, framebufferId)
}
if len(framebuffers) > 0 {
out.Write(id,
NewGlDeleteFramebuffers(int32(len(framebuffers)), memory.Tmp.Base).
AddRead(atom.Data(a, d, l, memory.Tmp.Base, framebuffers)))
}
// Delete all Buffers.
buffers := []BufferId{}
for bufferId := range c.Instances.Buffers {
buffers = append(buffers, bufferId)
}
if len(buffers) > 0 {
out.Write(id,
NewGlDeleteBuffers(int32(len(buffers)), memory.Tmp.Base).
AddRead(atom.Data(a, d, l, memory.Tmp.Base, buffers)))
}
// Delete all VertexArrays.
vertexArrays := []VertexArrayId{}
for vertexArrayId := range c.Instances.VertexArrays {
vertexArrays = append(vertexArrays, vertexArrayId)
}
if len(vertexArrays) > 0 {
out.Write(id,
NewGlDeleteVertexArraysOES(int32(len(vertexArrays)), memory.Tmp.Base).
AddRead(atom.Data(a, d, l, memory.Tmp.Base, 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 SyncObjects. TODO: Uncomment when added to API file.
// for syncObjectId := range c.Instances.SyncObjects {
// out.Write(id, NewGlDeleteSync(syncObjectId))
// }
}