blob: 686094e4076674119e28472f001d8637fc7841ca [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 (
"fmt"
"math/rand"
"android.googlesource.com/platform/tools/gpu/atom"
"android.googlesource.com/platform/tools/gpu/binary"
"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/replay/builder"
"android.googlesource.com/platform/tools/gpu/replay/value"
)
func readFramebufferDepth(out chan replay.Image) atom.Atom {
return replay.Custom(func(i atom.ID, s *gfxapi.State, d database.Database, l log.Logger, b *builder.Builder) error {
arch := s.Architecture
c := getContext(s)
colorW, colorH, err := getState(s).getFramebufferAttachmentSize(gfxapi.FramebufferAttachmentColor)
if err != nil {
return err
}
depthW, depthH, err := getState(s).getFramebufferAttachmentSize(gfxapi.FramebufferAttachmentDepth)
if err != nil {
return err
}
const (
uTextureLocation UniformLocation = 0
aScreenCoordsLocation AttributeLocation = 0
vertexShaderSource string = `
precision highp float;
attribute vec2 aScreenCoords;
varying vec2 vTexCoords;
void main() {
vTexCoords = aScreenCoords / 2. + vec2(0.5, 0.5);
gl_Position = vec4(aScreenCoords.xy, 0., 1.);
}`
fragmentShaderSource string = `
precision highp float;
uniform sampler2D uTexture;
varying vec2 vTexCoords;
vec4 float2rgba(float f) {
vec4 v = fract(f * vec4(1., 255., 65025., 16581375.));
return v - vec4(v.yzw, 0.) / 255.;
}
void main() {
float sample = texture2D(uTexture, vTexCoords).r;
gl_FragColor = float2rgba(sample);
}`
)
var (
origProgramID = c.BoundProgram
origRenderbufferID = c.BoundRenderbuffers[GLenum_GL_RENDERBUFFER]
origReadFramebufferID = c.BoundFramebuffers[GLenum_GL_READ_FRAMEBUFFER]
origDrawFramebufferID = c.BoundFramebuffers[GLenum_GL_DRAW_FRAMEBUFFER]
origTextureID = c.TextureUnits[c.ActiveTextureUnit][GLenum_GL_TEXTURE_2D]
origArrayBufferID = c.BoundBuffers[GLenum_GL_ARRAY_BUFFER]
origElementArrayBufferID = c.BoundBuffers[GLenum_GL_ELEMENT_ARRAY_BUFFER]
origActiveTextureUnit = int32(c.ActiveTextureUnit - GLenum_GL_TEXTURE0)
inW = int32(depthW)
inH = int32(depthH)
outW = int32(colorW)
outH = int32(colorH)
)
// Generate new unused object IDs.
renderbufferID := RenderbufferId(newUnusedID(func(x uint32) bool { _, ok := c.Instances.Renderbuffers[RenderbufferId(x)]; return ok }))
framebufferID := FramebufferId(newUnusedID(func(x uint32) bool { _, ok := c.Instances.Framebuffers[FramebufferId(x)]; return ok }))
textureID := TextureId(newUnusedID(func(x uint32) bool { _, ok := c.Instances.Textures[TextureId(x)]; return ok }))
programID := ProgramId(newUnusedID(func(x uint32) bool { _, ok := c.Instances.Programs[ProgramId(x)]; return ok }))
vertexShaderID := ShaderId(newUnusedID(func(x uint32) bool { _, ok := c.Instances.Shaders[ShaderId(x)]; return ok }))
fragmentShaderID := ShaderId(newUnusedID(func(x uint32) bool {
_, ok := c.Instances.Shaders[ShaderId(x)]
return ok || ShaderId(x) == vertexShaderID
}))
// 2D vertices positions for a full screen 2D triangle strip.
positions := []float32{-1., -1., 1., -1., -1., 1., 1., 1.}
// Temporarily change rasterizing/blending state and enable VAP 0.
undoList := []atom.Atom{}
for _, cap := range []GLenum{
GLenum_GL_BLEND,
GLenum_GL_CULL_FACE,
GLenum_GL_DEPTH_TEST,
GLenum_GL_SCISSOR_TEST,
GLenum_GL_STENCIL_TEST,
} {
capability := cap
if c.Capabilities[capability] {
NewGlDisable(capability).Replay(i, s, d, l, b)
undoList = append(undoList, NewGlEnable(capability))
}
}
if !c.VertexAttributeArrays[aScreenCoordsLocation].Enabled {
NewGlEnableVertexAttribArray(aScreenCoordsLocation).Replay(i, s, d, l, b)
undoList = append(undoList, NewGlDisableVertexAttribArray(aScreenCoordsLocation))
}
replayEach(i, s, d, l, b,
// Setup new framebuffer/renderbuffer.
NewGlGenFramebuffers(1, memory.Tmp).
AddRead(atom.Data(arch, d, l, memory.Tmp, framebufferID)),
NewGlBindFramebuffer(GLenum_GL_DRAW_FRAMEBUFFER, framebufferID),
NewGlGenRenderbuffers(1, memory.Tmp).
AddRead(atom.Data(arch, d, l, memory.Tmp, renderbufferID)),
NewGlBindRenderbuffer(GLenum_GL_RENDERBUFFER, renderbufferID),
NewGlRenderbufferStorage(GLenum_GL_RENDERBUFFER, GLenum_GL_RGBA8, GLsizei(outW), GLsizei(outH)),
NewGlFramebufferRenderbuffer(GLenum_GL_DRAW_FRAMEBUFFER, GLenum_GL_COLOR_ATTACHMENT0, GLenum_GL_RENDERBUFFER, renderbufferID),
// Setup depth texture.
NewGlGenTextures(1, memory.Tmp).
AddRead(atom.Data(arch, d, l, memory.Tmp, textureID)),
NewGlBindTexture(GLenum_GL_TEXTURE_2D, textureID),
NewGlTexImage2D(GLenum_GL_TEXTURE_2D, 0, GLint(GLenum_GL_DEPTH24_STENCIL8), GLsizei(outW), GLsizei(outH), 0, GLenum_GL_DEPTH_STENCIL, GLenum_GL_UNSIGNED_INT_24_8, memory.Nullptr),
NewGlTexParameteri(GLenum_GL_TEXTURE_2D, GLenum_GL_TEXTURE_MIN_FILTER, GLint(GLenum_GL_NEAREST)),
NewGlTexParameteri(GLenum_GL_TEXTURE_2D, GLenum_GL_TEXTURE_MAG_FILTER, GLint(GLenum_GL_NEAREST)),
NewGlTexParameteri(GLenum_GL_TEXTURE_2D, GLenum_GL_TEXTURE_WRAP_S, GLint(GLenum_GL_CLAMP_TO_EDGE)),
NewGlTexParameteri(GLenum_GL_TEXTURE_2D, GLenum_GL_TEXTURE_WRAP_T, GLint(GLenum_GL_CLAMP_TO_EDGE)),
// Blit depth attachment.
NewGlFramebufferTexture2D(GLenum_GL_DRAW_FRAMEBUFFER, GLenum_GL_DEPTH_ATTACHMENT, GLenum_GL_TEXTURE_2D, textureID, 0),
NewGlBlitFramebuffer(0, 0, GLint(inW), GLint(inH), 0, 0, GLint(outW), GLint(outH), GLbitfield_GL_DEPTH_BUFFER_BIT, GLenum_GL_NEAREST),
NewGlFramebufferTexture2D(GLenum_GL_DRAW_FRAMEBUFFER, GLenum_GL_DEPTH_ATTACHMENT, GLenum_GL_TEXTURE_2D, TextureId(0), 0),
// Bind new framebuffer.
NewGlBindFramebuffer(GLenum_GL_READ_FRAMEBUFFER, framebufferID),
// Render depth texture to framebuffer color attachment.
NewGlClear(GLbitfield_GL_COLOR_BUFFER_BIT),
)
// Create the shader program
replayEach(i, s, d, l, b,
NewProgram(arch, d, l, vertexShaderID, fragmentShaderID, programID, vertexShaderSource, fragmentShaderSource)...)
replayEach(i, s, d, l, b,
NewGlBindAttribLocation(programID, aScreenCoordsLocation, "aScreenCoords"),
NewGlLinkProgram(programID),
NewGlUseProgram(programID),
NewGlBindTexture(GLenum_GL_TEXTURE_2D, textureID),
NewGlGetUniformLocation(programID, "uTexture", uTextureLocation),
NewGlUniform1i(uTextureLocation, GLint(origActiveTextureUnit)),
NewGlBindBuffer(GLenum_GL_ARRAY_BUFFER, 0),
NewGlBindBuffer(GLenum_GL_ELEMENT_ARRAY_BUFFER, 0),
NewGlVertexAttribPointer(aScreenCoordsLocation, 2, GLenum_GL_FLOAT, false, 0, memory.Tmp),
NewGlDrawArrays(GLenum_GL_TRIANGLE_STRIP, 0, 4).
AddRead(atom.Data(arch, d, l, memory.Tmp, positions)),
)
postColorData(i, s, d, l, b, outW, outH, out)
// Restore conditionally changed state.
replayEach(i, s, d, l, b, undoList...)
replayEach(i, s, d, l, b,
// Restore buffer/vertexAttrib state.
NewGlBindBuffer(GLenum_GL_ELEMENT_ARRAY_BUFFER, origElementArrayBufferID),
NewGlBindBuffer(GLenum_GL_ARRAY_BUFFER, origArrayBufferID),
// Note: we're not restoring the original VertexAttribPointer as we may re-enter an inconsistent state, which would abort the current replay batch.
// NewGlVertexAttribPointer(aScreenCoordsLocation, origVertexAttrib.Size, origVertexAttrib.Type, origVertexAttrib.Normalized, origVertexAttrib.Stride, VertexPointer(origVertexAttrib.Data)),
// Restore texture state.
NewGlBindTexture(GLenum_GL_TEXTURE_2D, origTextureID),
NewGlDeleteTextures(1, memory.Tmp).
AddRead(atom.Data(arch, d, l, memory.Tmp, textureID)),
// Restore framebuffer/renderbuffer state.
NewGlBindRenderbuffer(GLenum_GL_RENDERBUFFER, origRenderbufferID),
NewGlBindFramebuffer(GLenum_GL_READ_FRAMEBUFFER, origReadFramebufferID),
NewGlBindFramebuffer(GLenum_GL_DRAW_FRAMEBUFFER, origDrawFramebufferID),
NewGlDeleteRenderbuffers(1, memory.Tmp).
AddRead(atom.Data(arch, d, l, memory.Tmp, renderbufferID)),
NewGlDeleteFramebuffers(1, memory.Tmp).
AddRead(atom.Data(arch, d, l, memory.Tmp, framebufferID)),
// Restore program state.
NewGlUseProgram(origProgramID),
NewGlDeleteProgram(programID),
NewGlDeleteShader(vertexShaderID),
NewGlDeleteShader(fragmentShaderID),
)
return nil
})
}
func readFramebufferColor(width, height uint32, out chan replay.Image) atom.Atom {
return replay.Custom(func(i atom.ID, s *gfxapi.State, d database.Database, l log.Logger, b *builder.Builder) error {
arch := s.Architecture
c := getContext(s)
colorW, colorH, err := getState(s).getFramebufferAttachmentSize(gfxapi.FramebufferAttachmentColor)
if err != nil {
return err
}
var (
origRenderbufferID = c.BoundRenderbuffers[GLenum_GL_RENDERBUFFER]
origReadFramebufferID = c.BoundFramebuffers[GLenum_GL_READ_FRAMEBUFFER]
origDrawFramebufferID = c.BoundFramebuffers[GLenum_GL_DRAW_FRAMEBUFFER]
inW = int32(colorW)
inH = int32(colorH)
outW = int32(width)
outH = int32(height)
)
// Generate new unused object IDs.
renderbufferID := RenderbufferId(newUnusedID(func(x uint32) bool { _, ok := c.Instances.Renderbuffers[RenderbufferId(x)]; return ok }))
framebufferID := FramebufferId(newUnusedID(func(x uint32) bool { _, ok := c.Instances.Framebuffers[FramebufferId(x)]; return ok }))
if inW == outW && inH == outH {
postColorData(i, s, d, l, b, outW, outH, out)
} else {
ctx := getContext(s)
origScissor := ctx.Rasterizing.Scissor
replayEach(i, s, d, l, b,
NewGlScissor(0, 0, GLsizei(colorW), GLsizei(colorH)),
NewGlGenFramebuffers(1, memory.Tmp).
AddRead(atom.Data(arch, d, l, memory.Tmp, framebufferID)),
NewGlBindFramebuffer(GLenum_GL_DRAW_FRAMEBUFFER, framebufferID),
NewGlGenRenderbuffers(1, memory.Tmp).
AddRead(atom.Data(arch, d, l, memory.Tmp, renderbufferID)),
NewGlBindRenderbuffer(GLenum_GL_RENDERBUFFER, renderbufferID),
NewGlRenderbufferStorage(GLenum_GL_RENDERBUFFER, GLenum_GL_RGBA8, GLsizei(outW), GLsizei(outH)),
NewGlFramebufferRenderbuffer(GLenum_GL_DRAW_FRAMEBUFFER, GLenum_GL_COLOR_ATTACHMENT0, GLenum_GL_RENDERBUFFER, renderbufferID),
NewGlBlitFramebuffer(0, 0, GLint(inW), GLint(inH), 0, 0, GLint(outW), GLint(outH), GLbitfield_GL_COLOR_BUFFER_BIT, GLenum_GL_LINEAR),
NewGlBindFramebuffer(GLenum_GL_READ_FRAMEBUFFER, framebufferID),
)
postColorData(i, s, d, l, b, outW, outH, out)
replayEach(i, s, d, l, b,
NewGlBindRenderbuffer(GLenum_GL_RENDERBUFFER, origRenderbufferID),
NewGlBindFramebuffer(GLenum_GL_READ_FRAMEBUFFER, origReadFramebufferID),
NewGlBindFramebuffer(GLenum_GL_DRAW_FRAMEBUFFER, origDrawFramebufferID),
NewGlDeleteRenderbuffers(1, memory.Tmp).
AddRead(atom.Data(arch, d, l, memory.Tmp, renderbufferID)),
NewGlDeleteFramebuffers(1, memory.Tmp).
AddRead(atom.Data(arch, d, l, memory.Tmp, framebufferID)),
NewGlScissor(origScissor.X, origScissor.Y, origScissor.Width, origScissor.Height),
)
}
return nil
})
}
func postColorData(i atom.ID, s *gfxapi.State, d database.Database, l log.Logger, b *builder.Builder, width, height int32, img chan<- replay.Image) {
ctx := getContext(s)
origPackAlignment := ctx.PixelStorage[GLenum_GL_PACK_ALIGNMENT]
if origPackAlignment != 1 {
NewGlPixelStorei(GLenum_GL_PACK_ALIGNMENT, 1).Replay(i, s, d, l, b)
defer NewGlPixelStorei(GLenum_GL_PACK_ALIGNMENT, origPackAlignment).Replay(i, s, d, l, b)
}
if origPackBuffer, ok := ctx.BoundBuffers[GLenum_GL_PIXEL_PACK_BUFFER]; ok && origPackBuffer != 0 {
NewGlBindBuffer(GLenum_GL_PIXEL_PACK_BUFFER, 0).Replay(i, s, d, l, b)
defer NewGlBindBuffer(GLenum_GL_PIXEL_PACK_BUFFER, origPackBuffer).Replay(i, s, d, l, b)
}
imageSize := uint64(width * height * 4)
NewGlReadPixels(0, 0, GLsizei(width), GLsizei(height), GLenum_GL_RGBA, GLenum_GL_UNSIGNED_BYTE, memory.Tmp).Replay(i, s, d, l, b)
b.Post(value.RemappedPointer(memory.Tmp.Address), imageSize, func(d binary.Decoder, err error) error {
var data []byte
if err == nil {
data = make([]byte, imageSize)
err = d.Data(data)
}
if err != nil {
err = fmt.Errorf("Could not read framebuffer data (expected length %d bytes): %v", imageSize, err)
data = nil
}
img <- replay.Image{Data: data, Error: err}
return err
})
}
func replayEach(i atom.ID, s *gfxapi.State, d database.Database, l log.Logger, b *builder.Builder, atoms ...atom.Atom) {
for _, a := range atoms {
if r, ok := a.(replay.Replayer); ok {
r.Replay(i, s, d, l, b)
}
}
}
func newUnusedID(existenceTest func(uint32) bool) uint32 {
for {
x := rand.Uint32()
if !existenceTest(x) {
return x
}
}
}