Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compat fixes #1748

Merged
merged 2 commits into from
Mar 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gapis/api/gles/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ go_library(
srcs = [
"compat.go",
"compat_client.go",
"compat_buffers.go",
"context.go",
"custom_replay.go",
"datatypes.go",
Expand Down
92 changes: 28 additions & 64 deletions gapis/api/gles/compat.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,6 @@ func getFeatures(ctx context.Context, version string, ext extensions) (features,
return f, v, nil
}

type scratchBuffer struct {
size GLsizeiptr
id BufferId
}

type onCompatError func(context.Context, api.CmdID, api.Cmd, error)

func compat(ctx context.Context, device *device.Instance, onError onCompatError) (transform.Transformer, error) {
Expand All @@ -157,19 +152,9 @@ func compat(ctx context.Context, device *device.Instance, onError onCompatError)
}

contexts := map[*Context]features{}
bufferCompat := newBufferCompat(int(glDev.UniformBufferAlignment))
eglContextHandle := map[*Context]EGLContext{}

scratchBuffers := map[interface{}]scratchBuffer{}
nextBufferID := BufferId(0xffff0000)
newBuffer := func(i api.CmdID, cb CommandBuilder, out transform.Writer) BufferId {
s := out.State()
id := nextBufferID
tmp := s.AllocDataOrPanic(ctx, id)
out.MutateAndWrite(ctx, i.Derived(), cb.GlGenBuffers(1, tmp.Ptr()).AddWrite(tmp.Data()))
nextBufferID--
return id
}

nextTextureID := TextureId(0xffff0000)
newTexture := func(i api.CmdID, cb CommandBuilder, out transform.Writer) TextureId {
s := out.State()
Expand Down Expand Up @@ -359,57 +344,21 @@ func compat(ctx context.Context, device *device.Instance, onError onCompatError)
}
}

case *GlBindBufferRange:
misalignment := cmd.Offset % GLintptr(glDev.UniformBufferAlignment)
if cmd.Target == GLenum_GL_UNIFORM_BUFFER && misalignment != 0 {
// We have a glBindBufferRange() taking a uniform buffer with an
// illegal offset alignment.
// TODO: We don't handle the case where the buffer is kept bound
// while the buffer is updated. It's an unlikely issue, but
// something that may break us.
if !c.Objects.Buffers.Contains(cmd.Buffer) {
return // Don't know what buffer this is referring to.
}

// We need a scratch buffer to copy the buffer data to a correct
// alignment.
key := struct {
c *Context
Target GLenum
Index GLuint
}{c, cmd.Target, cmd.Index}

// Look for pre-existing buffer we can reuse.
buffer, ok := scratchBuffers[key]
if !ok {
buffer.id = newBuffer(dID, cb, out)
scratchBuffers[key] = buffer
}

// Bind the scratch buffer to GL_COPY_WRITE_BUFFER
origCopyWriteBuffer := c.Bound.CopyWriteBuffer
out.MutateAndWrite(ctx, dID, cb.GlBindBuffer(GLenum_GL_COPY_WRITE_BUFFER, buffer.id))

if buffer.size < cmd.Size {
// Resize the scratch buffer
out.MutateAndWrite(ctx, dID, cb.GlBufferData(GLenum_GL_COPY_WRITE_BUFFER, cmd.Size, memory.Nullptr, GLenum_GL_DYNAMIC_COPY))
buffer.size = cmd.Size
scratchBuffers[key] = buffer
}

// Copy out the misaligned data to the scratch buffer in the
// GL_COPY_WRITE_BUFFER binding.
out.MutateAndWrite(ctx, dID, cb.GlBindBuffer(cmd.Target, cmd.Buffer))
out.MutateAndWrite(ctx, dID, cb.GlCopyBufferSubData(cmd.Target, GLenum_GL_COPY_WRITE_BUFFER, cmd.Offset, 0, cmd.Size))
case *GlBufferData:
bufferCompat.modifyBufferData(ctx, out, cb, c, id, cmd.Target, func() { out.MutateAndWrite(ctx, id, cmd) })
return

// We can now bind the range with correct alignment.
out.MutateAndWrite(ctx, id, cb.GlBindBufferRange(cmd.Target, cmd.Index, buffer.id, 0, cmd.Size))
case *GlBufferSubData:
bufferCompat.modifyBufferData(ctx, out, cb, c, id, cmd.Target, func() { out.MutateAndWrite(ctx, id, cmd) })
return

// Restore old GL_COPY_WRITE_BUFFER binding.
out.MutateAndWrite(ctx, dID, cb.GlBindBuffer(GLenum_GL_COPY_WRITE_BUFFER, origCopyWriteBuffer.GetID()))
case *GlCopyBufferSubData:
bufferCompat.modifyBufferData(ctx, out, cb, c, id, cmd.WriteTarget, func() { out.MutateAndWrite(ctx, id, cmd) })
return

return
}
case *GlBindBufferRange:
bufferCompat.bindBufferRange(ctx, out, cb, c, id, cmd)
return

case *GlDisableVertexAttribArray:
if c.Bound.VertexArray.VertexAttributeArrays.Get(cmd.Location).Enabled == GLboolean_GL_FALSE {
Expand Down Expand Up @@ -509,6 +458,21 @@ func compat(ctx context.Context, device *device.Instance, onError onCompatError)
}
return

case *GlDrawBuffers:
// Currently the default framebuffer for replay is single-buffered
// and so we need to transform any usage of GL_BACK to GL_FRONT.
cmd.Extras().Observations().ApplyReads(s.Memory.ApplicationPool())
bufs := cmd.Bufs.Slice(0, uint64(cmd.N), s.MemoryLayout).MustRead(ctx, cmd, s, nil)
for i, buf := range bufs {
if buf == GLenum_GL_BACK {
bufs[i] = GLenum_GL_FRONT
}
}
tmp := s.AllocDataOrPanic(ctx, bufs)
out.MutateAndWrite(ctx, id, cb.GlDrawBuffers(cmd.N, tmp.Ptr()).AddRead(tmp.Data()))
tmp.Free()
return

case *GlDrawArrays:
if target.vertexArrayObjects == required {
if clientVAsBound(c, clientVAs) {
Expand Down
190 changes: 190 additions & 0 deletions gapis/api/gles/compat_buffers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// 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 gles

import (
"context"

"github.com/google/gapid/gapis/api"
"github.com/google/gapid/gapis/api/transform"
"github.com/google/gapid/gapis/memory"
)

// bufferCompat provides compatibility transformations for GL buffers.
type bufferCompat struct {
// uniformBufferAlignment is the target's minimum alignment for uniform
// buffers.
uniformBufferAlignment int
// unaligned is a map of compat generated aligned buffer IDs to their
// original buffer.
unaligned map[BufferId]*Buffer
// scratch holds the temporary buffers created by the bufferCompat.
scratch map[scratchBufferKey]scratchBuffer
// nextBufferID is the buffer identifer to use for the next created scratch
// buffer.
nextBufferID BufferId
}

func newBufferCompat(uniformBufferAlignment int) *bufferCompat {
return &bufferCompat{
uniformBufferAlignment: uniformBufferAlignment,
unaligned: map[BufferId]*Buffer{},
scratch: map[scratchBufferKey]scratchBuffer{},
nextBufferID: BufferId(0xffff0000),
}
}

// scratchBufferKey is the key to the bufferCompat's scratch-buffer map.
type scratchBufferKey struct {
c *Context // The current GL context.
Target GLenum // The buffer target.
Index GLuint // The buffer binding index.
}

// scratchBufferKey is the value to the bufferCompat's scratch-buffer map.
type scratchBuffer struct {
size GLsizeiptr // Size of the buffer.
id BufferId // Identifier of the buffer.
}

// modifyBufferData deals with the complexities of copying unaligned buffer data
// to their aligned copies and should be called when ever a buffer is to be
// modified. modify is called to apply the buffer modification.
func (m *bufferCompat) modifyBufferData(ctx context.Context, out transform.Writer, cb CommandBuilder, c *Context, id api.CmdID, target GLenum, modify func()) {
id = id.Derived()
s := out.State()

// Get the target buffer.
buf, err := subGetBoundBuffer(ctx, nil, api.CmdNoID, nil, s, GetState(s), cb.Thread, nil, target)
if buf == nil || err != nil {
// Unknown buffer
modify()
return
}

// Lookup the original (unaligned) buffer.
unaligned, ok := m.unaligned[buf.ID]
if !ok {
// Buffer was not unaligned
modify()
return
}

// Walk the current bindings looking for those referencing the aligned
// buffer.
type binding struct {
index GLuint
offset GLintptr
size GLsizeiptr
}

rebind := []binding{}
for i, b := range c.Bound.UniformBuffers.Range() {
if b.Binding == nil || m.unaligned[b.Binding.ID] != unaligned {
continue
}

rebind = append(rebind, binding{
index: GLuint(i),
offset: b.Start,
size: b.Size,
})
}

if len(rebind) == 0 {
// No unaligned buffers require copying.
modify()
return
}

// Bind the original unaligned buffer.
out.MutateAndWrite(ctx, id, cb.GlBindBuffer(target, unaligned.ID))

// Apply the modification.
modify()

// Rebind all the unaligned bindings.
for _, r := range rebind {
cmd := cb.GlBindBufferRange(target, r.index, buf.ID, r.offset, r.size)
m.bindBufferRange(ctx, out, cb, c, id, cmd)
}
}

// bindBufferRange provides compatibiliy for glBindBufferRange by handling
// buffers that do not meet their minimum alignment on the target device.
// If a buffer is unaligned, then a new buffer is created and the data range is
// copied to this new buffer, and the new buffer is bound.
func (m *bufferCompat) bindBufferRange(ctx context.Context, out transform.Writer, cb CommandBuilder, c *Context, id api.CmdID, cmd *GlBindBufferRange) {
misalignment := cmd.Offset % GLintptr(m.uniformBufferAlignment)

if cmd.Target != GLenum_GL_UNIFORM_BUFFER || misalignment == 0 {
out.MutateAndWrite(ctx, id, cmd)
return
}

dID := id.Derived()

// We have a glBindBufferRange() taking a uniform buffer with an illegal
// offset alignment.

orig := c.Objects.Buffers.Get(cmd.Buffer)
if orig == nil {
return // Don't know what buffer this is referring to.
}

// We need a scratch buffer to copy the buffer data to a correct
// alignment.
scratchKey := scratchBufferKey{c, cmd.Target, cmd.Index}

// Look for pre-existing buffer we can reuse.
buffer, ok := m.scratch[scratchKey]
if !ok {
buffer.id = m.newBuffer(ctx, dID, cb, out)
m.scratch[scratchKey] = buffer
}

// Bind the scratch buffer to GL_COPY_WRITE_BUFFER
origCopyWriteBuffer := c.Bound.CopyWriteBuffer
out.MutateAndWrite(ctx, dID, cb.GlBindBuffer(GLenum_GL_COPY_WRITE_BUFFER, buffer.id))

if buffer.size < cmd.Size {
// Resize the scratch buffer
out.MutateAndWrite(ctx, dID, cb.GlBufferData(GLenum_GL_COPY_WRITE_BUFFER, cmd.Size, memory.Nullptr, GLenum_GL_DYNAMIC_COPY))
buffer.size = cmd.Size
m.scratch[scratchKey] = buffer
}

// Copy out the unaligned data to the scratch buffer in the
// GL_COPY_WRITE_BUFFER binding.
out.MutateAndWrite(ctx, dID, cb.GlBindBuffer(cmd.Target, cmd.Buffer))
out.MutateAndWrite(ctx, dID, cb.GlCopyBufferSubData(cmd.Target, GLenum_GL_COPY_WRITE_BUFFER, cmd.Offset, 0, cmd.Size))

// We can now bind the range with correct alignment.
out.MutateAndWrite(ctx, id, cb.GlBindBufferRange(cmd.Target, cmd.Index, buffer.id, 0, cmd.Size))

// Restore old GL_COPY_WRITE_BUFFER binding.
out.MutateAndWrite(ctx, dID, cb.GlBindBuffer(GLenum_GL_COPY_WRITE_BUFFER, origCopyWriteBuffer.GetID()))

m.unaligned[buffer.id] = orig
}

func (m *bufferCompat) newBuffer(ctx context.Context, id api.CmdID, cb CommandBuilder, out transform.Writer) BufferId {
s := out.State()
bufID := m.nextBufferID
tmp := s.AllocDataOrPanic(ctx, bufID)
out.MutateAndWrite(ctx, id, cb.GlGenBuffers(1, tmp.Ptr()).AddWrite(tmp.Data()))
m.nextBufferID--
return bufID
}