gpu,gpu/gl: introduce Backend

A recent change made the OpenGL functions an interface of the functions
required for the implementation of GPU, a renderer for Gio operations.
That allowed for running Gio on external systems where OpenGL is
available.

However, to allow for non-OpenGL flavored backends such as Vulkan,
Metal and Direct3D, this change introduces Backend for the high-level
operations required by GPU. This change also adds a concrete backend
to package gl.

Type Backend is a first cut heavily based on OpenGL. Future changes will add
more backends, where the Backend interface quite possibly will need refinement.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2020-02-09 17:03:57 +01:00
parent 9602337b45
commit 3ae5a37c24
16 changed files with 984 additions and 483 deletions
+7 -1
View File
@@ -51,7 +51,13 @@ func NewWindow(width, height int) (*Window, error) {
ctx.Release()
return err
}
gpu, err := gpu.New(f)
backend, err := gl.NewBackend(f)
if err != nil {
fbo.Release()
ctx.Release()
return err
}
gpu, err := gpu.New(backend)
if err != nil {
fbo.Release()
ctx.Release()
+2 -2
View File
@@ -11,7 +11,7 @@ import (
syscall "golang.org/x/sys/windows"
"gioui.org/app/internal/glimpl"
"gioui.org/gpu/gl"
gunsafe "gioui.org/internal/unsafe"
)
type (
@@ -157,7 +157,7 @@ func eglTerminate(disp _EGLDisplay) bool {
func eglQueryString(disp _EGLDisplay, name _EGLint) string {
r, _, _ := _eglQueryString.Call(uintptr(disp), uintptr(name))
return gl.GoString(gl.SliceOf(r))
return gunsafe.GoString(gunsafe.SliceOf(r))
}
// issue34474KeepAlive calls runtime.KeepAlive as a
+2 -1
View File
@@ -11,6 +11,7 @@ import (
"golang.org/x/sys/windows"
"gioui.org/gpu/gl"
gunsafe "gioui.org/internal/unsafe"
)
var (
@@ -290,7 +291,7 @@ func (c *Functions) GetShaderInfoLog(s gl.Shader) string {
}
func (c *Functions) GetString(pname gl.Enum) string {
s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0)
return gl.GoString(gl.SliceOf(s))
return gunsafe.GoString(gunsafe.SliceOf(s))
}
func (c *Functions) GetUniformLocation(p gl.Program, name string) gl.Uniform {
cname := cString(name)
+7 -1
View File
@@ -8,6 +8,7 @@ import (
"gioui.org/app/internal/window"
"gioui.org/gpu"
"gioui.org/gpu/gl"
"gioui.org/op"
)
@@ -66,7 +67,12 @@ func (l *renderLoop) renderLoop(glctx window.Context) error {
initErr <- err
return
}
g, err := gpu.New(glctx.Functions())
ctx, err := gl.NewBackend(glctx.Functions())
if err != nil {
initErr <- err
return
}
g, err := gpu.New(ctx)
if err != nil {
initErr <- err
return
+156
View File
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: Unlicense OR MIT
package gpu
import (
"image"
"time"
)
// Backend represents the abstraction of underlying GPU
// APIs such as OpenGL, Direct3D useful for rendering Gio
// operations.
type Backend interface {
BeginFrame()
EndFrame()
Caps() Caps
NewTimer() Timer
// IsContinuousTime reports whether all timer measurements
// are valid at the point of call.
IsTimeContinuous() bool
NewTexture(minFilter, magFilter TextureFilter) Texture
DefaultFramebuffer() Framebuffer
NilTexture() Texture
NewFramebuffer() Framebuffer
NewBuffer(typ BufferType) Buffer
NewProgram(vertexShader, fragmentShader string, attribMap []string) (Program, error)
SetupVertexArray(slot int, size int, dataType DataType, stride, offset int)
DepthFunc(f DepthFunc)
ClearColor(r, g, b, a float32)
ClearDepth(d float32)
Clear(buffers BufferAttachments)
Viewport(x, y, width, height int)
DrawArrays(mode DrawMode, off, count int)
DrawElements(mode DrawMode, off, count int)
SetBlend(enable bool)
SetDepthTest(enable bool)
DepthMask(mask bool)
BlendFunc(sfactor, dfactor BlendFactor)
}
type BlendFactor uint8
type DrawMode uint8
type BufferAttachments uint
type TextureFilter uint8
type TextureFormat uint8
type BufferType uint8
type BufferUsage uint8
type DataType uint8
type DepthFunc uint8
type Features uint
type Caps struct {
Features Features
MaxTextureSize int
}
type Program interface {
Bind()
Release()
UniformFor(uniform string) Uniform
Uniform1i(u Uniform, v int)
Uniform1f(u Uniform, v float32)
Uniform2f(u Uniform, v0, v1 float32)
Uniform4f(u Uniform, v0, v1, v2, v3 float32)
}
type Uniform interface{}
type Buffer interface {
Bind()
Upload(usage BufferUsage, data []byte)
Release()
}
type Framebuffer interface {
Bind()
BindTexture(t Texture)
Invalidate()
Release()
IsComplete() error
}
type Timer interface {
Begin()
End()
Duration() (time.Duration, bool)
Release()
}
type Texture interface {
Upload(img *image.RGBA)
Release()
Bind(unit int)
Resize(format TextureFormat, width, height int)
}
const (
BufferAttachmentColor BufferAttachments = 1 << iota
BufferAttachmentDepth
)
const (
DepthFuncGreater DepthFunc = iota
)
const (
DataTypeFloat DataType = iota
DataTypeShort
)
const (
BufferUsageStaticDraw BufferUsage = iota
)
const (
BufferTypeIndices BufferType = iota
BufferTypeData
)
const (
TextureFormatSRGB TextureFormat = iota
TextureFormatFloat
)
const (
FilterNearest TextureFilter = iota
FilterLinear
)
const (
FeatureTimers Features = iota
)
const (
DrawModeTriangleStrip DrawMode = iota
DrawModeTriangles
)
const (
BlendFactorOne BlendFactor = iota
BlendFactorOneMinusSrcAlpha
BlendFactorZero
BlendFactorDstColor
)
func (f Features) Has(feats Features) bool {
return f&feats == feats
}
+8 -8
View File
@@ -43,11 +43,11 @@ func (r *resourceCache) put(key interface{}, val resource) {
r.newRes[key] = val
}
func (r *resourceCache) frame(ctx *context) {
func (r *resourceCache) frame() {
for k, v := range r.res {
if _, exists := r.newRes[k]; !exists {
delete(r.res, k)
v.release(ctx)
v.release()
}
}
for k, v := range r.newRes {
@@ -56,9 +56,9 @@ func (r *resourceCache) frame(ctx *context) {
}
}
func (r *resourceCache) release(ctx *context) {
func (r *resourceCache) release() {
for _, v := range r.newRes {
v.release(ctx)
v.release()
}
r.newRes = nil
r.res = nil
@@ -87,11 +87,11 @@ func (r *opCache) put(key ops.Key, val resource) {
r.newRes[key] = val
}
func (r *opCache) frame(ctx *context) {
func (r *opCache) frame() {
for k, v := range r.res {
if _, exists := r.newRes[k]; !exists {
delete(r.res, k)
v.release(ctx)
v.release()
}
}
for k, v := range r.newRes {
@@ -100,9 +100,9 @@ func (r *opCache) frame(ctx *context) {
}
}
func (r *opCache) release(ctx *context) {
func (r *opCache) release() {
for _, v := range r.newRes {
v.release(ctx)
v.release()
}
r.newRes = nil
r.res = nil
-128
View File
@@ -1,128 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
package gpu
import (
"errors"
"strings"
"gioui.org/gpu/gl"
)
type context struct {
caps caps
gl.Functions
}
type caps struct {
EXT_disjoint_timer_query bool
// floatTriple holds the settings for floating point
// textures.
floatTriple textureTriple
// Single channel alpha textures.
alphaTriple textureTriple
srgbaTriple textureTriple
}
// textureTriple holds the type settings for
// a TexImage2D call.
type textureTriple struct {
internalFormat int
format gl.Enum
typ gl.Enum
}
func newContext(glctx gl.Functions) (*context, error) {
ctx := &context{
Functions: glctx,
}
exts := strings.Split(ctx.GetString(gl.EXTENSIONS), " ")
glVer := ctx.GetString(gl.VERSION)
ver, err := gl.ParseGLVersion(glVer)
if err != nil {
return nil, err
}
floatTriple, err := floatTripleFor(ctx, ver, exts)
if err != nil {
return nil, err
}
srgbaTriple, err := srgbaTripleFor(ver, exts)
if err != nil {
return nil, err
}
hasTimers := hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query")
ctx.caps = caps{
EXT_disjoint_timer_query: hasTimers,
floatTriple: floatTriple,
alphaTriple: alphaTripleFor(ver),
srgbaTriple: srgbaTriple,
}
return ctx, nil
}
// floatTripleFor determines the best texture triple for floating point FBOs.
func floatTripleFor(ctx *context, ver [2]int, exts []string) (textureTriple, error) {
var triples []textureTriple
if ver[0] >= 3 {
triples = append(triples, textureTriple{gl.R16F, gl.Enum(gl.RED), gl.Enum(gl.HALF_FLOAT)})
}
if hasExtension(exts, "GL_OES_texture_half_float") && hasExtension(exts, "GL_EXT_color_buffer_half_float") {
// Try single channel.
triples = append(triples, textureTriple{gl.LUMINANCE, gl.Enum(gl.LUMINANCE), gl.Enum(gl.HALF_FLOAT_OES)})
// Fallback to 4 channels.
triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.HALF_FLOAT_OES)})
}
if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") {
triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.FLOAT)})
}
tex := ctx.CreateTexture()
defer ctx.DeleteTexture(tex)
ctx.BindTexture(gl.TEXTURE_2D, tex)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
fbo := ctx.CreateFramebuffer()
defer ctx.DeleteFramebuffer(fbo)
defFBO := gl.Framebuffer(ctx.GetBinding(gl.FRAMEBUFFER_BINDING))
ctx.BindFramebuffer(gl.FRAMEBUFFER, fbo)
defer ctx.BindFramebuffer(gl.FRAMEBUFFER, defFBO)
for _, tt := range triples {
const size = 256
ctx.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ, nil)
ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)
if st := ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); st == gl.FRAMEBUFFER_COMPLETE {
return tt, nil
}
}
return textureTriple{}, errors.New("floating point fbos not supported")
}
func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) {
switch {
case ver[0] >= 3:
return textureTriple{gl.SRGB8_ALPHA8, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)}, nil
case hasExtension(exts, "GL_EXT_sRGB"):
return textureTriple{gl.SRGB_ALPHA_EXT, gl.Enum(gl.SRGB_ALPHA_EXT), gl.Enum(gl.UNSIGNED_BYTE)}, nil
default:
return textureTriple{}, errors.New("no sRGB texture formats found")
}
}
func alphaTripleFor(ver [2]int) textureTriple {
intf, f := gl.R8, gl.Enum(gl.RED)
if ver[0] < 3 {
// R8, RED not supported on OpenGL ES 2.0.
intf, f = gl.LUMINANCE, gl.Enum(gl.LUMINANCE)
}
return textureTriple{intf, f, gl.UNSIGNED_BYTE}
}
func hasExtension(exts []string, ext string) bool {
for _, e := range exts {
if ext == e {
return true
}
}
return false
}
+514
View File
@@ -0,0 +1,514 @@
// SPDX-License-Identifier: Unlicense OR MIT
package gl
import (
"errors"
"fmt"
"image"
"strings"
"time"
"gioui.org/gpu"
)
// Backend implements gpu.Backend.
type Backend struct {
funcs Functions
defFBO *gpuFramebuffer
state glstate
feats gpu.Caps
// floatTriple holds the settings for floating point
// textures.
floatTriple textureTriple
// Single channel alpha textures.
alphaTriple textureTriple
srgbaTriple textureTriple
}
// State tracking.
type glstate struct {
// nattr is the current number of enabled vertex arrays.
nattr int
prog *gpuProgram
texUnits [2]*gpuTexture
}
type gpuTimer struct {
funcs Functions
obj Query
}
type gpuTexture struct {
backend *Backend
obj Texture
}
type gpuFramebuffer struct {
funcs Functions
obj Framebuffer
}
type gpuBuffer struct {
funcs Functions
obj Buffer
typ Enum
}
type gpuProgram struct {
backend *Backend
obj Program
nattr int
}
// textureTriple holds the type settings for
// a TexImage2D call.
type textureTriple struct {
internalFormat int
format Enum
typ Enum
}
func NewBackend(f Functions) (*Backend, error) {
exts := strings.Split(f.GetString(EXTENSIONS), " ")
glVer := f.GetString(VERSION)
ver, err := parseGLVersion(glVer)
if err != nil {
return nil, err
}
floatTriple, err := floatTripleFor(f, ver, exts)
if err != nil {
return nil, err
}
srgbaTriple, err := srgbaTripleFor(ver, exts)
if err != nil {
return nil, err
}
defFBO := Framebuffer(f.GetBinding(FRAMEBUFFER_BINDING))
b := &Backend{
defFBO: &gpuFramebuffer{funcs: f, obj: defFBO},
funcs: f,
floatTriple: floatTriple,
alphaTriple: alphaTripleFor(ver),
srgbaTriple: srgbaTriple,
}
if hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") {
b.feats.Features |= gpu.FeatureTimers
}
b.feats.MaxTextureSize = f.GetInteger(MAX_TEXTURE_SIZE)
return b, nil
}
func (b *Backend) BeginFrame() {
// Assume GL state is reset.
b.state = glstate{}
}
func (b *Backend) EndFrame() {
b.funcs.ActiveTexture(TEXTURE0)
}
func (b *Backend) Caps() gpu.Caps {
return b.feats
}
func (b *Backend) NewTimer() gpu.Timer {
return &gpuTimer{
funcs: b.funcs,
obj: b.funcs.CreateQuery(),
}
}
func (b *Backend) IsTimeContinuous() bool {
return b.funcs.GetInteger(GPU_DISJOINT_EXT) == FALSE
}
func (b *Backend) NewFramebuffer() gpu.Framebuffer {
fb := b.funcs.CreateFramebuffer()
return &gpuFramebuffer{funcs: b.funcs, obj: fb}
}
func (b *Backend) NilTexture() gpu.Texture {
return &gpuTexture{backend: b}
}
func (b *Backend) DefaultFramebuffer() gpu.Framebuffer {
return b.defFBO
}
func (b *Backend) NewTexture(minFilter, magFilter gpu.TextureFilter) gpu.Texture {
tex := &gpuTexture{backend: b, obj: b.funcs.CreateTexture()}
tex.Bind(0)
b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, toTexFilter(magFilter))
b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, toTexFilter(minFilter))
b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE)
b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE)
return tex
}
func (b *Backend) NewBuffer(typ gpu.BufferType) gpu.Buffer {
obj := b.funcs.CreateBuffer()
var gltyp Enum
switch typ {
case gpu.BufferTypeData:
gltyp = ARRAY_BUFFER
case gpu.BufferTypeIndices:
gltyp = ELEMENT_ARRAY_BUFFER
default:
panic("unsupported buffer type")
}
return &gpuBuffer{funcs: b.funcs, obj: obj, typ: gltyp}
}
func (b *Backend) bindTexture(unit int, t *gpuTexture) {
if b.state.texUnits[unit] != t {
b.funcs.ActiveTexture(TEXTURE0 + Enum(unit))
b.funcs.BindTexture(TEXTURE_2D, t.obj)
b.state.texUnits[unit] = t
}
}
func (b *Backend) useProgram(p *gpuProgram) {
if b.state.prog != p {
p.backend.funcs.UseProgram(p.obj)
b.state.prog = p
}
}
func (b *Backend) enableVertexArrays(n int) {
// Enable needed arrays.
for i := b.state.nattr; i < n; i++ {
b.funcs.EnableVertexAttribArray(Attrib(i))
}
// Disable extra arrays.
for i := n; i < b.state.nattr; i++ {
b.funcs.DisableVertexAttribArray(Attrib(i))
}
b.state.nattr = n
}
func (b *Backend) SetDepthTest(enable bool) {
if enable {
b.funcs.Enable(DEPTH_TEST)
} else {
b.funcs.Disable(DEPTH_TEST)
}
}
func (b *Backend) BlendFunc(sfactor, dfactor gpu.BlendFactor) {
b.funcs.BlendFunc(toGLBlendFactor(sfactor), toGLBlendFactor(dfactor))
}
func toGLBlendFactor(f gpu.BlendFactor) Enum {
switch f {
case gpu.BlendFactorOne:
return ONE
case gpu.BlendFactorOneMinusSrcAlpha:
return ONE_MINUS_SRC_ALPHA
case gpu.BlendFactorZero:
return ZERO
case gpu.BlendFactorDstColor:
return DST_COLOR
default:
panic("unsupported blend factor")
}
}
func (b *Backend) DepthMask(mask bool) {
b.funcs.DepthMask(mask)
}
func (b *Backend) SetBlend(enable bool) {
if enable {
b.funcs.Enable(BLEND)
} else {
b.funcs.Disable(BLEND)
}
}
func (b *Backend) DrawElements(mode gpu.DrawMode, off, count int) {
b.funcs.DrawElements(toGLDrawMode(mode), count, UNSIGNED_SHORT, off)
}
func (b *Backend) DrawArrays(mode gpu.DrawMode, off, count int) {
b.funcs.DrawArrays(toGLDrawMode(mode), off, count)
}
func toGLDrawMode(mode gpu.DrawMode) Enum {
switch mode {
case gpu.DrawModeTriangleStrip:
return TRIANGLE_STRIP
case gpu.DrawModeTriangles:
return TRIANGLES
default:
panic("unsupported draw mode")
}
}
func (b *Backend) Viewport(x, y, width, height int) {
b.funcs.Viewport(x, y, width, height)
}
func (b *Backend) Clear(attachments gpu.BufferAttachments) {
var mask Enum
if attachments&gpu.BufferAttachmentColor != 0 {
mask |= COLOR_BUFFER_BIT
}
if attachments&gpu.BufferAttachmentDepth != 0 {
mask |= DEPTH_BUFFER_BIT
}
b.funcs.Clear(mask)
}
func (b *Backend) ClearDepth(d float32) {
b.funcs.ClearDepthf(d)
}
func (b *Backend) ClearColor(colR, colG, colB, colA float32) {
b.funcs.ClearColor(colR, colG, colB, colA)
}
func (b *Backend) DepthFunc(f gpu.DepthFunc) {
var glfunc Enum
switch f {
case gpu.DepthFuncGreater:
glfunc = GREATER
default:
panic("unsupported depth func")
}
b.funcs.DepthFunc(glfunc)
}
func (b *Backend) NewProgram(vssrc, fssrc string, attr []string) (gpu.Program, error) {
p, err := createProgram(b.funcs, vssrc, fssrc, attr)
if err != nil {
return nil, err
}
return &gpuProgram{backend: b, obj: p, nattr: len(attr)}, nil
}
func (p *gpuProgram) Uniform1i(u gpu.Uniform, v int) {
p.Bind()
p.backend.funcs.Uniform1i(u.(Uniform), v)
}
func (p *gpuProgram) Uniform1f(u gpu.Uniform, v0 float32) {
p.Bind()
p.backend.funcs.Uniform1f(u.(Uniform), v0)
}
func (p *gpuProgram) Uniform2f(u gpu.Uniform, v0, v1 float32) {
p.Bind()
p.backend.funcs.Uniform2f(u.(Uniform), v0, v1)
}
func (p *gpuProgram) Uniform4f(u gpu.Uniform, v0, v1, v2, v3 float32) {
p.Bind()
p.backend.funcs.Uniform4f(u.(Uniform), v0, v1, v2, v3)
}
func (p *gpuProgram) Bind() {
p.backend.useProgram(p)
p.backend.enableVertexArrays(p.nattr)
}
func (p *gpuProgram) UniformFor(uniform string) gpu.Uniform {
f := p.backend.funcs
return getUniformLocation(f, p.obj, uniform)
}
func (b *Backend) SetupVertexArray(slot int, size int, dataType gpu.DataType, stride, offset int) {
var gltyp Enum
switch dataType {
case gpu.DataTypeFloat:
gltyp = FLOAT
case gpu.DataTypeShort:
gltyp = SHORT
default:
panic("unsupported data type")
}
b.funcs.VertexAttribPointer(Attrib(slot), size, gltyp, false, stride, offset)
}
func (p *gpuProgram) Release() {
p.backend.funcs.DeleteProgram(p.obj)
}
func (b *gpuBuffer) Release() {
b.funcs.DeleteBuffer(b.obj)
}
func (b *gpuBuffer) Bind() {
b.funcs.BindBuffer(b.typ, b.obj)
}
func (b *gpuBuffer) Upload(usage gpu.BufferUsage, data []byte) {
b.Bind()
var glusage Enum
switch usage {
case gpu.BufferUsageStaticDraw:
glusage = STATIC_DRAW
default:
panic("unsupported buffer usage")
}
b.funcs.BufferData(b.typ, data, glusage)
}
func (f *gpuFramebuffer) IsComplete() error {
if st := f.funcs.CheckFramebufferStatus(FRAMEBUFFER); st != FRAMEBUFFER_COMPLETE {
return fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, f.funcs.GetError())
}
return nil
}
func (f *gpuFramebuffer) Bind() {
f.funcs.BindFramebuffer(FRAMEBUFFER, f.obj)
}
func (f *gpuFramebuffer) Invalidate() {
f.Bind()
f.funcs.InvalidateFramebuffer(FRAMEBUFFER, COLOR_ATTACHMENT0)
}
func (f *gpuFramebuffer) Release() {
f.funcs.DeleteFramebuffer(f.obj)
}
func (f *gpuFramebuffer) BindTexture(t gpu.Texture) {
gltex := t.(*gpuTexture)
f.Bind()
f.funcs.FramebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, gltex.obj, 0)
}
func toTexFilter(f gpu.TextureFilter) int {
switch f {
case gpu.FilterNearest:
return NEAREST
case gpu.FilterLinear:
return LINEAR
default:
panic("unsupported texture filter")
}
}
func (t *gpuTexture) Bind(unit int) {
t.backend.bindTexture(unit, t)
}
func (t *gpuTexture) Release() {
t.backend.funcs.DeleteTexture(t.obj)
}
func (t *gpuTexture) Resize(format gpu.TextureFormat, width, height int) {
t.Bind(0)
tt := t.backend.floatTriple
t.backend.funcs.TexImage2D(TEXTURE_2D, 0, tt.internalFormat, width, height, tt.format, tt.typ, nil)
}
func (t *gpuTexture) Upload(img *image.RGBA) {
t.Bind(0)
var pixels []byte
b := img.Bounds()
w, h := b.Dx(), b.Dy()
if img.Stride != w*4 {
panic("unsupported stride")
}
start := (b.Min.X + b.Min.Y*w) * 4
end := (b.Max.X + (b.Max.Y-1)*w) * 4
pixels = img.Pix[start:end]
tt := t.backend.srgbaTriple
t.backend.funcs.TexImage2D(TEXTURE_2D, 0, tt.internalFormat, w, h, tt.format, tt.typ, pixels)
}
func (t *gpuTimer) Begin() {
t.funcs.BeginQuery(TIME_ELAPSED_EXT, t.obj)
}
func (t *gpuTimer) End() {
t.funcs.EndQuery(TIME_ELAPSED_EXT)
}
func (t *gpuTimer) ready() bool {
return t.funcs.GetQueryObjectuiv(t.obj, QUERY_RESULT_AVAILABLE) == TRUE
}
func (t *gpuTimer) Release() {
t.funcs.DeleteQuery(t.obj)
}
func (t *gpuTimer) Duration() (time.Duration, bool) {
if !t.ready() {
return 0, false
}
nanos := t.funcs.GetQueryObjectuiv(t.obj, QUERY_RESULT)
return time.Duration(nanos), true
}
// floatTripleFor determines the best texture triple for floating point FBOs.
func floatTripleFor(f Functions, ver [2]int, exts []string) (textureTriple, error) {
var triples []textureTriple
if ver[0] >= 3 {
triples = append(triples, textureTriple{R16F, Enum(RED), Enum(HALF_FLOAT)})
}
if hasExtension(exts, "GL_OES_texture_half_float") && hasExtension(exts, "GL_EXT_color_buffer_half_float") {
// Try single channel.
triples = append(triples, textureTriple{LUMINANCE, Enum(LUMINANCE), Enum(HALF_FLOAT_OES)})
// Fallback to 4 channels.
triples = append(triples, textureTriple{RGBA, Enum(RGBA), Enum(HALF_FLOAT_OES)})
}
if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") {
triples = append(triples, textureTriple{RGBA, Enum(RGBA), Enum(FLOAT)})
}
tex := f.CreateTexture()
defer f.DeleteTexture(tex)
f.BindTexture(TEXTURE_2D, tex)
f.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE)
f.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE)
f.TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST)
f.TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST)
fbo := f.CreateFramebuffer()
defer f.DeleteFramebuffer(fbo)
defFBO := Framebuffer(f.GetBinding(FRAMEBUFFER_BINDING))
f.BindFramebuffer(FRAMEBUFFER, fbo)
defer f.BindFramebuffer(FRAMEBUFFER, defFBO)
for _, tt := range triples {
const size = 256
f.TexImage2D(TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ, nil)
f.FramebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, tex, 0)
if st := f.CheckFramebufferStatus(FRAMEBUFFER); st == FRAMEBUFFER_COMPLETE {
return tt, nil
}
}
return textureTriple{}, errors.New("floating point fbos not supported")
}
func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) {
switch {
case ver[0] >= 3:
return textureTriple{SRGB8_ALPHA8, Enum(RGBA), Enum(UNSIGNED_BYTE)}, nil
case hasExtension(exts, "GL_EXT_sRGB"):
return textureTriple{SRGB_ALPHA_EXT, Enum(SRGB_ALPHA_EXT), Enum(UNSIGNED_BYTE)}, nil
default:
return textureTriple{}, errors.New("no sRGB texture formats found")
}
}
func alphaTripleFor(ver [2]int) textureTriple {
intf, f := R8, Enum(RED)
if ver[0] < 3 {
// R8, RED not supported on OpenGL ES 2.0.
intf, f = LUMINANCE, Enum(LUMINANCE)
}
return textureTriple{intf, f, UNSIGNED_BYTE}
}
func hasExtension(exts []string, ext string) bool {
for _, e := range exts {
if ext == e {
return true
}
}
return false
}
+2
View File
@@ -21,6 +21,7 @@ const (
DST_COLOR = 0x306
ELEMENT_ARRAY_BUFFER = 0x8893
EXTENSIONS = 0x1f03
FALSE = 0
FLOAT = 0x1406
FRAGMENT_SHADER = 0x8b30
FRAMEBUFFER = 0x8d40
@@ -69,6 +70,7 @@ const (
TEXTURE1 = 0x84c1
TRIANGLE_STRIP = 0x5
TRIANGLES = 0x4
TRUE = 1
UNPACK_ALIGNMENT = 0xcf5
UNSIGNED_BYTE = 0x1401
UNSIGNED_SHORT = 0x1403
+6 -4
View File
@@ -6,6 +6,8 @@ import (
"fmt"
"runtime"
"strings"
"gioui.org/internal/unsafe"
)
// SRGBFBO implements an intermediate sRGB FBO
@@ -26,7 +28,7 @@ type SRGBFBO struct {
func NewSRGBFBO(f Functions) (*SRGBFBO, error) {
var es3 bool
glVer := f.GetString(VERSION)
ver, err := ParseGLVersion(glVer)
ver, err := parseGLVersion(glVer)
if err != nil {
return nil, err
}
@@ -55,17 +57,17 @@ func NewSRGBFBO(f Functions) (*SRGBFBO, error) {
func (s *SRGBFBO) Blit() {
if !s.blitted {
prog, err := CreateProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"})
prog, err := createProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"})
if err != nil {
panic(err)
}
s.prog = prog
s.c.UseProgram(prog)
s.c.Uniform1i(GetUniformLocation(s.c, prog, "tex"), 0)
s.c.Uniform1i(getUniformLocation(s.c, prog, "tex"), 0)
s.quad = s.c.CreateBuffer()
s.c.BindBuffer(ARRAY_BUFFER, s.quad)
s.c.BufferData(ARRAY_BUFFER,
BytesView([]float32{
unsafe.BytesView([]float32{
-1, +1, 0, 1,
+1, +1, 1, 1,
-1, -1, 0, 0,
+3 -42
View File
@@ -5,12 +5,10 @@ package gl
import (
"errors"
"fmt"
"reflect"
"strings"
"unsafe"
)
func CreateProgram(ctx Functions, vsSrc, fsSrc string, attribs []string) (Program, error) {
func createProgram(ctx Functions, vsSrc, fsSrc string, attribs []string) (Program, error) {
vs, err := createShader(ctx, VERTEX_SHADER, vsSrc)
if err != nil {
return Program{}, err
@@ -39,7 +37,7 @@ func CreateProgram(ctx Functions, vsSrc, fsSrc string, attribs []string) (Progra
return prog, nil
}
func GetUniformLocation(ctx Functions, prog Program, name string) Uniform {
func getUniformLocation(ctx Functions, prog Program, name string) Uniform {
loc := ctx.GetUniformLocation(prog, name)
if !loc.Valid() {
panic(fmt.Errorf("uniform %s not found", name))
@@ -62,19 +60,7 @@ func createShader(ctx Functions, typ Enum, src string) (Shader, error) {
return sh, nil
}
// BytesView returns a byte slice view of a slice.
func BytesView(s interface{}) []byte {
v := reflect.ValueOf(s)
first := v.Index(0)
sz := int(first.Type().Size())
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(first.UnsafeAddr())))),
Len: v.Len() * sz,
Cap: v.Cap() * sz,
}))
}
func ParseGLVersion(glVer string) ([2]int, error) {
func parseGLVersion(glVer string) ([2]int, error) {
var ver [2]int
if _, err := fmt.Sscanf(glVer, "OpenGL ES %d.%d", &ver[0], &ver[1]); err == nil {
return ver, nil
@@ -87,28 +73,3 @@ func ParseGLVersion(glVer string) ([2]int, error) {
}
return ver, fmt.Errorf("failed to parse OpenGL ES version (%s)", glVer)
}
func SliceOf(s uintptr) []byte {
if s == 0 {
return nil
}
sh := reflect.SliceHeader{
Data: s,
Len: 1 << 30,
Cap: 1 << 30,
}
return *(*[]byte)(unsafe.Pointer(&sh))
}
// GoString convert a NUL-terminated C string
// to a Go string.
func GoString(s []byte) string {
i := 0
for {
if s[i] == 0 {
break
}
i++
}
return string(s[:i])
}
+103 -143
View File
@@ -18,10 +18,10 @@ import (
"unsafe"
"gioui.org/f32"
"gioui.org/gpu/gl"
"gioui.org/internal/opconst"
"gioui.org/internal/ops"
"gioui.org/internal/path"
gunsafe "gioui.org/internal/unsafe"
"gioui.org/op"
"gioui.org/op/paint"
)
@@ -35,12 +35,12 @@ type GPU struct {
frameStart time.Time
zopsTimer, stencilTimer, coverTimer, cleanupTimer *timer
drawOps drawOps
ctx *context
ctx Backend
renderer *renderer
}
type renderer struct {
ctx *context
ctx Backend
blitter *blitter
pather *pather
packer packer
@@ -189,25 +189,25 @@ func decodePaintOp(data []byte) paint.PaintOp {
type clipType uint8
type resource interface {
release(ctx *context)
release()
}
type texture struct {
src *image.RGBA
id gl.Texture
tex Texture
}
type blitter struct {
ctx *context
ctx Backend
viewport image.Point
prog [2]gl.Program
prog [2]Program
vars [2]struct {
z gl.Uniform
uScale, uOffset gl.Uniform
uUVScale, uUVOffset gl.Uniform
uColor gl.Uniform
z Uniform
uScale, uOffset Uniform
uUVScale, uUVOffset Uniform
uColor Uniform
}
quadVerts gl.Buffer
quadVerts Buffer
}
type materialType uint8
@@ -224,12 +224,15 @@ const (
)
var (
blitAttribs = []string{"pos", "uv"}
attribPos gl.Attrib = 0
attribUV gl.Attrib = 1
blitAttribs = []string{"pos", "uv"}
)
func New(ctx gl.Functions) (*GPU, error) {
const (
attribPos = 0
attribUV = 1
)
func New(ctx Backend) (*GPU, error) {
g := &GPU{
pathCache: newOpCache(),
cache: newResourceCache(),
@@ -240,11 +243,7 @@ func New(ctx gl.Functions) (*GPU, error) {
return g, nil
}
func (g *GPU) init(glctx gl.Functions) error {
ctx, err := newContext(glctx)
if err != nil {
return err
}
func (g *GPU) init(ctx Backend) error {
g.ctx = ctx
g.renderer = newRenderer(ctx)
return nil
@@ -252,8 +251,8 @@ func (g *GPU) init(glctx gl.Functions) error {
func (g *GPU) Release() {
g.renderer.release()
g.pathCache.release(g.ctx)
g.cache.release(g.ctx)
g.pathCache.release()
g.cache.release()
if g.timers != nil {
g.timers.release()
}
@@ -265,7 +264,7 @@ func (g *GPU) Collect(viewport image.Point, frameOps *op.Ops) {
g.drawOps.reset(g.cache, viewport)
g.drawOps.collect(g.cache, frameOps, viewport)
g.frameStart = time.Now()
if g.drawOps.profile && g.timers == nil && g.ctx.caps.EXT_disjoint_timer_query {
if g.drawOps.profile && g.timers == nil && g.ctx.Caps().Features.Has(FeatureTimers) {
g.timers = newTimers(g.ctx)
g.zopsTimer = g.timers.newTimer()
g.stencilTimer = g.timers.newTimer()
@@ -282,6 +281,8 @@ func (g *GPU) Collect(viewport image.Point, frameOps *op.Ops) {
}
func (g *GPU) BeginFrame() {
g.ctx.BeginFrame()
defer g.ctx.EndFrame()
viewport := g.renderer.blitter.viewport
for _, img := range g.drawOps.imageOps {
expandPathOp(img.path, img.clip)
@@ -289,15 +290,15 @@ func (g *GPU) BeginFrame() {
if g.drawOps.profile {
g.zopsTimer.begin()
}
g.ctx.DepthFunc(gl.GREATER)
g.ctx.DepthFunc(DepthFuncGreater)
g.ctx.ClearColor(g.drawOps.clearColor[0], g.drawOps.clearColor[1], g.drawOps.clearColor[2], 1.0)
g.ctx.ClearDepthf(0.0)
g.ctx.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
g.ctx.ClearDepth(0.0)
g.ctx.Clear(BufferAttachmentColor | BufferAttachmentDepth)
g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
g.renderer.drawZOps(g.drawOps.zimageOps)
g.zopsTimer.end()
g.stencilTimer.begin()
g.ctx.Enable(gl.BLEND)
g.ctx.SetBlend(true)
g.renderer.packStencils(&g.drawOps.pathOps)
g.renderer.stencilClips(g.pathCache, g.drawOps.pathOps)
g.renderer.packIntersections(g.drawOps.imageOps)
@@ -306,15 +307,15 @@ func (g *GPU) BeginFrame() {
g.coverTimer.begin()
g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
g.renderer.drawOps(g.drawOps.imageOps)
g.ctx.Disable(gl.BLEND)
g.ctx.SetBlend(false)
g.renderer.pather.stenciler.invalidateFBO()
g.coverTimer.end()
}
func (g *GPU) EndFrame() {
g.cleanupTimer.begin()
g.cache.frame(g.ctx)
g.pathCache.frame(g.ctx)
g.cache.frame()
g.pathCache.frame()
g.cleanupTimer.end()
if g.drawOps.profile && g.timers.ready() {
zt, st, covt, cleant := g.zopsTimer.Elapsed, g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed
@@ -331,29 +332,28 @@ func (g *GPU) Profile() string {
return g.profile
}
func (r *renderer) texHandle(t *texture) gl.Texture {
if t.id.Valid() {
return t.id
func (r *renderer) texHandle(t *texture) Texture {
if t.tex != nil {
return t.tex
}
t.id = createTexture(r.ctx)
r.ctx.BindTexture(gl.TEXTURE_2D, t.id)
r.uploadTexture(t.src)
return t.id
t.tex = r.ctx.NewTexture(FilterLinear, FilterLinear)
t.tex.Upload(t.src)
return t.tex
}
func (t *texture) release(ctx *context) {
if t.id.Valid() {
ctx.DeleteTexture(t.id)
func (t *texture) release() {
if t.tex != nil {
t.tex.Release()
}
}
func newRenderer(ctx *context) *renderer {
func newRenderer(ctx Backend) *renderer {
r := &renderer{
ctx: ctx,
blitter: newBlitter(ctx),
pather: newPather(ctx),
}
r.packer.maxDim = ctx.GetInteger(gl.MAX_TEXTURE_SIZE)
r.packer.maxDim = ctx.Caps().MaxTextureSize
r.intersections.maxDim = r.packer.maxDim
return r
}
@@ -363,53 +363,50 @@ func (r *renderer) release() {
r.blitter.release()
}
func newBlitter(ctx *context) *blitter {
func newBlitter(ctx Backend) *blitter {
prog, err := createColorPrograms(ctx, blitVSrc, blitFSrc)
if err != nil {
panic(err)
}
quadVerts := ctx.CreateBuffer()
ctx.BindBuffer(gl.ARRAY_BUFFER, quadVerts)
ctx.BufferData(gl.ARRAY_BUFFER,
gl.BytesView([]float32{
quadVerts := ctx.NewBuffer(BufferTypeData)
quadVerts.Upload(BufferUsageStaticDraw,
gunsafe.BytesView([]float32{
-1, +1, 0, 0,
+1, +1, 1, 0,
-1, -1, 0, 1,
+1, -1, 1, 1,
}),
gl.STATIC_DRAW)
}))
b := &blitter{
ctx: ctx,
prog: prog,
quadVerts: quadVerts,
}
for i, prog := range prog {
ctx.UseProgram(prog)
switch materialType(i) {
case materialTexture:
uTex := gl.GetUniformLocation(ctx.Functions, prog, "tex")
ctx.Uniform1i(uTex, 0)
b.vars[i].uUVScale = gl.GetUniformLocation(ctx.Functions, prog, "uvScale")
b.vars[i].uUVOffset = gl.GetUniformLocation(ctx.Functions, prog, "uvOffset")
uTex := prog.UniformFor("tex")
prog.Uniform1i(uTex, 0)
b.vars[i].uUVScale = prog.UniformFor("uvScale")
b.vars[i].uUVOffset = prog.UniformFor("uvOffset")
case materialColor:
b.vars[i].uColor = gl.GetUniformLocation(ctx.Functions, prog, "color")
b.vars[i].uColor = prog.UniformFor("color")
}
b.vars[i].z = gl.GetUniformLocation(ctx.Functions, prog, "z")
b.vars[i].uScale = gl.GetUniformLocation(ctx.Functions, prog, "scale")
b.vars[i].uOffset = gl.GetUniformLocation(ctx.Functions, prog, "offset")
b.vars[i].z = prog.UniformFor("z")
b.vars[i].uScale = prog.UniformFor("scale")
b.vars[i].uOffset = prog.UniformFor("offset")
}
return b
}
func (b *blitter) release() {
b.ctx.DeleteBuffer(b.quadVerts)
b.quadVerts.Release()
for _, p := range b.prog {
b.ctx.DeleteProgram(p)
p.Release()
}
}
func createColorPrograms(ctx *context, vsSrc, fsSrc string) ([2]gl.Program, error) {
var prog [2]gl.Program
func createColorPrograms(ctx Backend, vsSrc, fsSrc string) ([2]Program, error) {
var prog [2]Program
frep := strings.NewReplacer(
"HEADER", `
uniform sampler2D tex;
@@ -418,7 +415,7 @@ uniform sampler2D tex;
)
fsSrcTex := frep.Replace(fsSrc)
var err error
prog[materialTexture], err = gl.CreateProgram(ctx.Functions, vsSrc, fsSrcTex, blitAttribs)
prog[materialTexture], err = ctx.NewProgram(vsSrc, fsSrcTex, blitAttribs)
if err != nil {
return prog, err
}
@@ -429,9 +426,9 @@ uniform vec4 color;
"GET_COLOR", `color`,
)
fsSrcCol := frep.Replace(fsSrc)
prog[materialColor], err = gl.CreateProgram(ctx.Functions, vsSrc, fsSrcCol, blitAttribs)
prog[materialColor], err = ctx.NewProgram(vsSrc, fsSrcCol, blitAttribs)
if err != nil {
ctx.DeleteProgram(prog[materialTexture])
prog[materialTexture].Release()
return prog, err
}
return prog, nil
@@ -447,8 +444,8 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
if fbo != p.place.Idx {
fbo = p.place.Idx
f := r.pather.stenciler.cover(fbo)
bindFramebuffer(r.ctx, f.fbo)
r.ctx.Clear(gl.COLOR_BUFFER_BIT)
bindFramebuffer(f.fbo)
r.ctx.Clear(BufferAttachmentColor)
}
data, _ := pathCache.get(p.pathKey)
r.pather.stencilPath(p.clip, p.off, p.place.Pos, data.(*pathData))
@@ -462,11 +459,9 @@ func (r *renderer) intersect(ops []imageOp) {
}
fbo := -1
r.pather.stenciler.beginIntersect(r.intersections.sizes)
r.ctx.BindBuffer(gl.ARRAY_BUFFER, r.blitter.quadVerts)
r.ctx.VertexAttribPointer(attribPos, 2, gl.FLOAT, false, 4*4, 0)
r.ctx.VertexAttribPointer(attribUV, 2, gl.FLOAT, false, 4*4, 4*2)
r.ctx.EnableVertexAttribArray(attribPos)
r.ctx.EnableVertexAttribArray(attribUV)
r.blitter.quadVerts.Bind()
r.ctx.SetupVertexArray(attribPos, 2, DataTypeFloat, 4*4, 0)
r.ctx.SetupVertexArray(attribUV, 2, DataTypeFloat, 4*4, 4*2)
for _, img := range ops {
if img.clipType != clipTypeIntersection {
continue
@@ -474,14 +469,12 @@ func (r *renderer) intersect(ops []imageOp) {
if fbo != img.place.Idx {
fbo = img.place.Idx
f := r.pather.stenciler.intersections.fbos[fbo]
bindFramebuffer(r.ctx, f.fbo)
r.ctx.Clear(gl.COLOR_BUFFER_BIT)
bindFramebuffer(f.fbo)
r.ctx.Clear(BufferAttachmentColor)
}
r.ctx.Viewport(img.place.Pos.X, img.place.Pos.Y, img.clip.Dx(), img.clip.Dy())
r.intersectPath(img.path, img.clip)
}
r.ctx.DisableVertexAttribArray(attribPos)
r.ctx.DisableVertexAttribArray(attribUV)
r.pather.stenciler.endIntersect()
}
@@ -498,11 +491,11 @@ func (r *renderer) intersectPath(p *pathOp, clip image.Rectangle) {
Max: o.Add(clip.Size()),
}
fbo := r.pather.stenciler.cover(p.place.Idx)
r.ctx.BindTexture(gl.TEXTURE_2D, fbo.tex)
fbo.tex.Bind(0)
coverScale, coverOff := texSpaceTransform(toRectF(uv), fbo.size)
r.ctx.Uniform2f(r.pather.stenciler.uIntersectUVScale, coverScale.X, coverScale.Y)
r.ctx.Uniform2f(r.pather.stenciler.uIntersectUVOffset, coverOff.X, coverOff.Y)
r.ctx.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
r.pather.stenciler.iprog.Uniform2f(r.pather.stenciler.uIntersectUVScale, coverScale.X, coverScale.Y)
r.pather.stenciler.iprog.Uniform2f(r.pather.stenciler.uIntersectUVOffset, coverOff.X, coverOff.Y)
r.ctx.DrawArrays(DrawModeTriangleStrip, 0, 4)
}
func (r *renderer) packIntersections(ops []imageOp) {
@@ -778,44 +771,38 @@ func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f3
}
func (r *renderer) drawZOps(ops []imageOp) {
r.ctx.Enable(gl.DEPTH_TEST)
r.ctx.BindBuffer(gl.ARRAY_BUFFER, r.blitter.quadVerts)
r.ctx.VertexAttribPointer(attribPos, 2, gl.FLOAT, false, 4*4, 0)
r.ctx.VertexAttribPointer(attribUV, 2, gl.FLOAT, false, 4*4, 4*2)
r.ctx.EnableVertexAttribArray(attribPos)
r.ctx.EnableVertexAttribArray(attribUV)
r.ctx.SetDepthTest(true)
r.blitter.quadVerts.Bind()
r.ctx.SetupVertexArray(attribPos, 2, DataTypeFloat, 4*4, 0)
r.ctx.SetupVertexArray(attribUV, 2, DataTypeFloat, 4*4, 4*2)
// Render front to back.
for i := len(ops) - 1; i >= 0; i-- {
img := ops[i]
m := img.material
switch m.material {
case materialTexture:
r.ctx.BindTexture(gl.TEXTURE_2D, r.texHandle(m.texture))
r.texHandle(m.texture).Bind(0)
}
drc := img.clip
scale, off := clipSpaceTransform(drc, r.blitter.viewport)
r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvScale, m.uvOffset)
}
r.ctx.DisableVertexAttribArray(attribPos)
r.ctx.DisableVertexAttribArray(attribUV)
r.ctx.Disable(gl.DEPTH_TEST)
r.ctx.SetDepthTest(false)
}
func (r *renderer) drawOps(ops []imageOp) {
r.ctx.Enable(gl.DEPTH_TEST)
r.ctx.SetDepthTest(true)
r.ctx.DepthMask(false)
r.ctx.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
r.ctx.BindBuffer(gl.ARRAY_BUFFER, r.blitter.quadVerts)
r.ctx.VertexAttribPointer(attribPos, 2, gl.FLOAT, false, 4*4, 0)
r.ctx.VertexAttribPointer(attribUV, 2, gl.FLOAT, false, 4*4, 4*2)
r.ctx.EnableVertexAttribArray(attribPos)
r.ctx.EnableVertexAttribArray(attribUV)
var coverTex gl.Texture
r.ctx.BlendFunc(BlendFactorOne, BlendFactorOneMinusSrcAlpha)
r.blitter.quadVerts.Bind()
r.ctx.SetupVertexArray(attribPos, 2, DataTypeFloat, 4*4, 0)
r.ctx.SetupVertexArray(attribUV, 2, DataTypeFloat, 4*4, 4*2)
var coverTex Texture
for _, img := range ops {
m := img.material
switch m.material {
case materialTexture:
r.ctx.BindTexture(gl.TEXTURE_2D, r.texHandle(m.texture))
r.texHandle(m.texture).Bind(0)
}
drc := img.clip
scale, off := clipSpaceTransform(drc, r.blitter.viewport)
@@ -829,11 +816,9 @@ func (r *renderer) drawOps(ops []imageOp) {
case clipTypeIntersection:
fbo = r.pather.stenciler.intersections.fbos[img.place.Idx]
}
if !coverTex.Equal(fbo.tex) {
if coverTex != fbo.tex {
coverTex = fbo.tex
r.ctx.ActiveTexture(gl.TEXTURE1)
r.ctx.BindTexture(gl.TEXTURE_2D, coverTex)
r.ctx.ActiveTexture(gl.TEXTURE0)
coverTex.Bind(1)
}
uv := image.Rectangle{
Min: img.place.Pos,
@@ -842,24 +827,8 @@ func (r *renderer) drawOps(ops []imageOp) {
coverScale, coverOff := texSpaceTransform(toRectF(uv), fbo.size)
r.pather.cover(img.z, m.material, m.color, scale, off, m.uvScale, m.uvOffset, coverScale, coverOff)
}
r.ctx.DisableVertexAttribArray(attribPos)
r.ctx.DisableVertexAttribArray(attribUV)
r.ctx.DepthMask(true)
r.ctx.Disable(gl.DEPTH_TEST)
}
func (r *renderer) uploadTexture(img *image.RGBA) {
var pixels []byte
b := img.Bounds()
w, h := b.Dx(), b.Dy()
if img.Stride != w*4 {
panic("unsupported stride")
}
start := (b.Min.X + b.Min.Y*w) * 4
end := (b.Max.X + (b.Max.Y-1)*w) * 4
pixels = img.Pix[start:end]
tt := r.ctx.caps.srgbaTriple
r.ctx.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, w, h, tt.format, tt.typ, pixels)
r.ctx.SetDepthTest(false)
}
func gamma(r, g, b, a uint32) [4]float32 {
@@ -879,18 +848,19 @@ func gamma(r, g, b, a uint32) [4]float32 {
}
func (b *blitter) blit(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff f32.Point) {
b.ctx.UseProgram(b.prog[mat])
p := b.prog[mat]
p.Bind()
switch mat {
case materialColor:
b.ctx.Uniform4f(b.vars[mat].uColor, col[0], col[1], col[2], col[3])
p.Uniform4f(b.vars[mat].uColor, col[0], col[1], col[2], col[3])
case materialTexture:
b.ctx.Uniform2f(b.vars[mat].uUVScale, uvScale.X, uvScale.Y)
b.ctx.Uniform2f(b.vars[mat].uUVOffset, uvOff.X, uvOff.Y)
p.Uniform2f(b.vars[mat].uUVScale, uvScale.X, uvScale.Y)
p.Uniform2f(b.vars[mat].uUVOffset, uvOff.X, uvOff.Y)
}
b.ctx.Uniform1f(b.vars[mat].z, z)
b.ctx.Uniform2f(b.vars[mat].uScale, scale.X, scale.Y)
b.ctx.Uniform2f(b.vars[mat].uOffset, off.X, off.Y)
b.ctx.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
p.Uniform1f(b.vars[mat].z, z)
p.Uniform2f(b.vars[mat].uScale, scale.X, scale.Y)
p.Uniform2f(b.vars[mat].uOffset, off.X, off.Y)
b.ctx.DrawArrays(DrawModeTriangleStrip, 0, 4)
}
// texSpaceTransform return the scale and offset that transforms the given subimage
@@ -925,23 +895,13 @@ func clipSpaceTransform(r image.Rectangle, viewport image.Point) (f32.Point, f32
return scale, offset
}
func bindFramebuffer(ctx *context, fbo gl.Framebuffer) {
ctx.BindFramebuffer(gl.FRAMEBUFFER, fbo)
if st := ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
panic(fmt.Errorf("AA FBO not complete; status = 0x%x, err = %d", st, ctx.GetError()))
func bindFramebuffer(fbo Framebuffer) {
fbo.Bind()
if err := fbo.IsComplete(); err != nil {
panic(fmt.Errorf("AA FBO not complete: %v", err))
}
}
func createTexture(ctx *context) gl.Texture {
tex := ctx.CreateTexture()
ctx.BindTexture(gl.TEXTURE_2D, tex)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
return tex
}
// Fill in maximal Y coordinates of the NW and NE corners.
func fillMaxY(verts []byte) {
contour := 0
+106 -132
View File
@@ -10,12 +10,12 @@ import (
"unsafe"
"gioui.org/f32"
"gioui.org/gpu/gl"
"gioui.org/internal/path"
gunsafe "gioui.org/internal/unsafe"
)
type pather struct {
ctx *context
ctx Backend
viewport image.Point
@@ -24,30 +24,30 @@ type pather struct {
}
type coverer struct {
ctx *context
prog [2]gl.Program
ctx Backend
prog [2]Program
vars [2]struct {
z gl.Uniform
uScale, uOffset gl.Uniform
uUVScale, uUVOffset gl.Uniform
uCoverUVScale, uCoverUVOffset gl.Uniform
uColor gl.Uniform
z Uniform
uScale, uOffset Uniform
uUVScale, uUVOffset Uniform
uCoverUVScale, uCoverUVOffset Uniform
uColor Uniform
}
}
type stenciler struct {
ctx *context
defFBO gl.Framebuffer
ctx Backend
defFBO Framebuffer
indexBufQuads int
prog gl.Program
iprog gl.Program
prog Program
iprog Program
fbos fboSet
intersections fboSet
uScale, uOffset gl.Uniform
uPathOffset gl.Uniform
uIntersectUVOffset gl.Uniform
uIntersectUVScale gl.Uniform
indexBuf gl.Buffer
uScale, uOffset Uniform
uPathOffset Uniform
uIntersectUVOffset Uniform
uIntersectUVScale Uniform
indexBuf Buffer
}
type fboSet struct {
@@ -56,27 +56,29 @@ type fboSet struct {
type stencilFBO struct {
size image.Point
fbo gl.Framebuffer
tex gl.Texture
fbo Framebuffer
tex Texture
}
type pathData struct {
ncurves int
data gl.Buffer
data Buffer
}
var (
pathAttribs = []string{"corner", "maxy", "from", "ctrl", "to"}
attribPathCorner gl.Attrib = 0
attribPathMaxY gl.Attrib = 1
attribPathFrom gl.Attrib = 2
attribPathCtrl gl.Attrib = 3
attribPathTo gl.Attrib = 4
pathAttribs = []string{"corner", "maxy", "from", "ctrl", "to"}
intersectAttribs = []string{"pos", "uv"}
)
func newPather(ctx *context) *pather {
const (
attribPathCorner = 0
attribPathMaxY = 1
attribPathFrom = 2
attribPathCtrl = 3
attribPathTo = 4
)
func newPather(ctx Backend) *pather {
return &pather{
ctx: ctx,
stenciler: newStenciler(ctx),
@@ -84,7 +86,7 @@ func newPather(ctx *context) *pather {
}
}
func newCoverer(ctx *context) *coverer {
func newCoverer(ctx Backend) *coverer {
prog, err := createColorPrograms(ctx, coverVSrc, coverFSrc)
if err != nil {
panic(err)
@@ -94,68 +96,58 @@ func newCoverer(ctx *context) *coverer {
prog: prog,
}
for i, prog := range prog {
ctx.UseProgram(prog)
switch materialType(i) {
case materialTexture:
uTex := gl.GetUniformLocation(ctx.Functions, prog, "tex")
ctx.Uniform1i(uTex, 0)
c.vars[i].uUVScale = gl.GetUniformLocation(ctx.Functions, prog, "uvScale")
c.vars[i].uUVOffset = gl.GetUniformLocation(ctx.Functions, prog, "uvOffset")
uTex := prog.UniformFor("tex")
prog.Uniform1i(uTex, 0)
c.vars[i].uUVScale = prog.UniformFor("uvScale")
c.vars[i].uUVOffset = prog.UniformFor("uvOffset")
case materialColor:
c.vars[i].uColor = gl.GetUniformLocation(ctx.Functions, prog, "color")
c.vars[i].uColor = prog.UniformFor("color")
}
uCover := gl.GetUniformLocation(ctx.Functions, prog, "cover")
ctx.Uniform1i(uCover, 1)
c.vars[i].z = gl.GetUniformLocation(ctx.Functions, prog, "z")
c.vars[i].uScale = gl.GetUniformLocation(ctx.Functions, prog, "scale")
c.vars[i].uOffset = gl.GetUniformLocation(ctx.Functions, prog, "offset")
c.vars[i].uCoverUVScale = gl.GetUniformLocation(ctx.Functions, prog, "uvCoverScale")
c.vars[i].uCoverUVOffset = gl.GetUniformLocation(ctx.Functions, prog, "uvCoverOffset")
uCover := prog.UniformFor("cover")
prog.Uniform1i(uCover, 1)
c.vars[i].z = prog.UniformFor("z")
c.vars[i].uScale = prog.UniformFor("scale")
c.vars[i].uOffset = prog.UniformFor("offset")
c.vars[i].uCoverUVScale = prog.UniformFor("uvCoverScale")
c.vars[i].uCoverUVOffset = prog.UniformFor("uvCoverOffset")
}
return c
}
func newStenciler(ctx *context) *stenciler {
defFBO := gl.Framebuffer(ctx.GetBinding(gl.FRAMEBUFFER_BINDING))
prog, err := gl.CreateProgram(ctx.Functions, stencilVSrc, stencilFSrc, pathAttribs)
func newStenciler(ctx Backend) *stenciler {
defFBO := ctx.DefaultFramebuffer()
prog, err := ctx.NewProgram(stencilVSrc, stencilFSrc, pathAttribs)
if err != nil {
panic(err)
}
ctx.UseProgram(prog)
iprog, err := gl.CreateProgram(ctx.Functions, intersectVSrc, intersectFSrc, intersectAttribs)
iprog, err := ctx.NewProgram(intersectVSrc, intersectFSrc, intersectAttribs)
if err != nil {
panic(err)
}
coverLoc := gl.GetUniformLocation(ctx.Functions, iprog, "cover")
ctx.UseProgram(iprog)
ctx.Uniform1i(coverLoc, 0)
coverLoc := iprog.UniformFor("cover")
iprog.Uniform1i(coverLoc, 0)
return &stenciler{
ctx: ctx,
defFBO: defFBO,
prog: prog,
iprog: iprog,
uScale: gl.GetUniformLocation(ctx.Functions, prog, "scale"),
uOffset: gl.GetUniformLocation(ctx.Functions, prog, "offset"),
uPathOffset: gl.GetUniformLocation(ctx.Functions, prog, "pathOffset"),
uIntersectUVScale: gl.GetUniformLocation(ctx.Functions, iprog, "uvScale"),
uIntersectUVOffset: gl.GetUniformLocation(ctx.Functions, iprog, "uvOffset"),
indexBuf: ctx.CreateBuffer(),
uScale: prog.UniformFor("scale"),
uOffset: prog.UniformFor("offset"),
uPathOffset: prog.UniformFor("pathOffset"),
uIntersectUVScale: iprog.UniformFor("uvScale"),
uIntersectUVOffset: iprog.UniformFor("uvOffset"),
indexBuf: ctx.NewBuffer(BufferTypeIndices),
}
}
func (s *fboSet) resize(ctx *context, sizes []image.Point) {
func (s *fboSet) resize(ctx Backend, sizes []image.Point) {
// Add fbos.
for i := len(s.fbos); i < len(sizes); i++ {
tex := ctx.CreateTexture()
ctx.BindTexture(gl.TEXTURE_2D, tex)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
fbo := ctx.CreateFramebuffer()
s.fbos = append(s.fbos, stencilFBO{
fbo: fbo,
tex: tex,
fbo: ctx.NewFramebuffer(),
tex: ctx.NewTexture(FilterNearest, FilterNearest),
})
}
// Resize fbos.
@@ -168,37 +160,33 @@ func (s *fboSet) resize(ctx *context, sizes []image.Point) {
resize = resize || waste > 1.2
if resize {
f.size = sz
ctx.BindTexture(gl.TEXTURE_2D, f.tex)
tt := ctx.caps.floatTriple
ctx.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, sz.X, sz.Y, tt.format, tt.typ, nil)
ctx.BindFramebuffer(gl.FRAMEBUFFER, f.fbo)
ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, f.tex, 0)
f.tex.Resize(TextureFormatFloat, sz.X, sz.Y)
f.fbo.BindTexture(f.tex)
}
}
// Delete extra fbos.
s.delete(ctx, len(sizes))
}
func (s *fboSet) invalidate(ctx *context) {
func (s *fboSet) invalidate(ctx Backend) {
for _, f := range s.fbos {
ctx.BindFramebuffer(gl.FRAMEBUFFER, f.fbo)
ctx.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
f.fbo.Invalidate()
}
}
func (s *fboSet) delete(ctx *context, idx int) {
func (s *fboSet) delete(ctx Backend, idx int) {
for i := idx; i < len(s.fbos); i++ {
f := s.fbos[i]
ctx.DeleteFramebuffer(f.fbo)
ctx.DeleteTexture(f.tex)
f.fbo.Release()
f.tex.Release()
}
s.fbos = s.fbos[:idx]
}
func (s *stenciler) release() {
s.fbos.delete(s.ctx, 0)
s.ctx.DeleteProgram(s.prog)
s.ctx.DeleteBuffer(s.indexBuf)
s.prog.Release()
s.indexBuf.Release()
}
func (p *pather) release() {
@@ -208,22 +196,21 @@ func (p *pather) release() {
func (c *coverer) release() {
for _, p := range c.prog {
c.ctx.DeleteProgram(p)
p.Release()
}
}
func buildPath(ctx *context, p []byte) *pathData {
buf := ctx.CreateBuffer()
ctx.BindBuffer(gl.ARRAY_BUFFER, buf)
ctx.BufferData(gl.ARRAY_BUFFER, p, gl.STATIC_DRAW)
func buildPath(ctx Backend, p []byte) *pathData {
buf := ctx.NewBuffer(BufferTypeData)
buf.Upload(BufferUsageStaticDraw, p)
return &pathData{
ncurves: len(p) / path.VertStride,
data: buf,
}
}
func (p *pathData) release(ctx *context) {
ctx.DeleteBuffer(p.data)
func (p *pathData) release() {
p.data.Release()
}
func (p *pather) begin(sizes []image.Point) {
@@ -239,26 +226,24 @@ func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.
}
func (s *stenciler) beginIntersect(sizes []image.Point) {
s.ctx.ActiveTexture(gl.TEXTURE1)
s.ctx.BindTexture(gl.TEXTURE_2D, gl.Texture{})
s.ctx.ActiveTexture(gl.TEXTURE0)
s.ctx.BlendFunc(gl.DST_COLOR, gl.ZERO)
s.ctx.NilTexture().Bind(1)
s.ctx.BlendFunc(BlendFactorDstColor, BlendFactorZero)
// 8 bit coverage is enough, but OpenGL ES only supports single channel
// floating point formats. Replace with GL_RGB+GL_UNSIGNED_BYTE if
// no floating point support is available.
s.intersections.resize(s.ctx, sizes)
s.ctx.ClearColor(1.0, 0.0, 0.0, 0.0)
s.ctx.UseProgram(s.iprog)
s.iprog.Bind()
}
func (s *stenciler) endIntersect() {
s.ctx.BindFramebuffer(gl.FRAMEBUFFER, s.defFBO)
s.defFBO.Bind()
}
func (s *stenciler) invalidateFBO() {
s.intersections.invalidate(s.ctx)
s.fbos.invalidate(s.ctx)
s.ctx.BindFramebuffer(gl.FRAMEBUFFER, s.defFBO)
s.defFBO.Bind()
}
func (s *stenciler) cover(idx int) stencilFBO {
@@ -266,31 +251,24 @@ func (s *stenciler) cover(idx int) stencilFBO {
}
func (s *stenciler) begin(sizes []image.Point) {
s.ctx.ActiveTexture(gl.TEXTURE1)
s.ctx.BindTexture(gl.TEXTURE_2D, gl.Texture{})
s.ctx.ActiveTexture(gl.TEXTURE0)
s.ctx.BlendFunc(gl.ONE, gl.ONE)
s.ctx.NilTexture().Bind(1)
s.ctx.BlendFunc(BlendFactorOne, BlendFactorOne)
s.fbos.resize(s.ctx, sizes)
s.ctx.ClearColor(0.0, 0.0, 0.0, 0.0)
s.ctx.UseProgram(s.prog)
s.ctx.EnableVertexAttribArray(attribPathCorner)
s.ctx.EnableVertexAttribArray(attribPathMaxY)
s.ctx.EnableVertexAttribArray(attribPathFrom)
s.ctx.EnableVertexAttribArray(attribPathCtrl)
s.ctx.EnableVertexAttribArray(attribPathTo)
s.ctx.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, s.indexBuf)
s.prog.Bind()
s.indexBuf.Bind()
}
func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data *pathData) {
s.ctx.BindBuffer(gl.ARRAY_BUFFER, data.data)
data.data.Bind()
s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy())
// Transform UI coordinates to OpenGL coordinates.
texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y}
s.ctx.Uniform2f(s.uScale, scale.X, scale.Y)
s.ctx.Uniform2f(s.uOffset, orig.X, orig.Y)
s.ctx.Uniform2f(s.uPathOffset, offset.X, offset.Y)
s.prog.Uniform2f(s.uScale, scale.X, scale.Y)
s.prog.Uniform2f(s.uOffset, orig.X, orig.Y)
s.prog.Uniform2f(s.uPathOffset, offset.X, offset.Y)
// Draw in batches that fit in uint16 indices.
start := 0
nquads := data.ncurves / 4
@@ -311,27 +289,22 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima
indices[i*6+4] = i*4 + 1
indices[i*6+5] = i*4 + 3
}
s.ctx.BufferData(gl.ELEMENT_ARRAY_BUFFER, gl.BytesView(indices), gl.STATIC_DRAW)
s.indexBuf.Upload(BufferUsageStaticDraw, gunsafe.BytesView(indices))
s.indexBufQuads = batch
}
off := path.VertStride * start * 4
s.ctx.VertexAttribPointer(attribPathCorner, 2, gl.SHORT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).CornerX)))
s.ctx.VertexAttribPointer(attribPathMaxY, 1, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).MaxY)))
s.ctx.VertexAttribPointer(attribPathFrom, 2, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).FromX)))
s.ctx.VertexAttribPointer(attribPathCtrl, 2, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).CtrlX)))
s.ctx.VertexAttribPointer(attribPathTo, 2, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).ToX)))
s.ctx.DrawElements(gl.TRIANGLES, batch*6, gl.UNSIGNED_SHORT, 0)
s.ctx.SetupVertexArray(attribPathCorner, 2, DataTypeShort, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).CornerX)))
s.ctx.SetupVertexArray(attribPathMaxY, 1, DataTypeFloat, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).MaxY)))
s.ctx.SetupVertexArray(attribPathFrom, 2, DataTypeFloat, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).FromX)))
s.ctx.SetupVertexArray(attribPathCtrl, 2, DataTypeFloat, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).CtrlX)))
s.ctx.SetupVertexArray(attribPathTo, 2, DataTypeFloat, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).ToX)))
s.ctx.DrawElements(DrawModeTriangles, 0, batch*6)
start += batch
}
}
func (s *stenciler) end() {
s.ctx.DisableVertexAttribArray(attribPathCorner)
s.ctx.DisableVertexAttribArray(attribPathMaxY)
s.ctx.DisableVertexAttribArray(attribPathFrom)
s.ctx.DisableVertexAttribArray(attribPathCtrl)
s.ctx.DisableVertexAttribArray(attribPathTo)
s.ctx.BindFramebuffer(gl.FRAMEBUFFER, s.defFBO)
s.defFBO.Bind()
}
func (p *pather) cover(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
@@ -339,20 +312,21 @@ func (p *pather) cover(z float32, mat materialType, col [4]float32, scale, off,
}
func (c *coverer) cover(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
c.ctx.UseProgram(c.prog[mat])
p := c.prog[mat]
p.Bind()
switch mat {
case materialColor:
c.ctx.Uniform4f(c.vars[mat].uColor, col[0], col[1], col[2], col[3])
p.Uniform4f(c.vars[mat].uColor, col[0], col[1], col[2], col[3])
case materialTexture:
c.ctx.Uniform2f(c.vars[mat].uUVScale, uvScale.X, uvScale.Y)
c.ctx.Uniform2f(c.vars[mat].uUVOffset, uvOff.X, uvOff.Y)
p.Uniform2f(c.vars[mat].uUVScale, uvScale.X, uvScale.Y)
p.Uniform2f(c.vars[mat].uUVOffset, uvOff.X, uvOff.Y)
}
c.ctx.Uniform1f(c.vars[mat].z, z)
c.ctx.Uniform2f(c.vars[mat].uScale, scale.X, scale.Y)
c.ctx.Uniform2f(c.vars[mat].uOffset, off.X, off.Y)
c.ctx.Uniform2f(c.vars[mat].uCoverUVScale, coverScale.X, coverScale.Y)
c.ctx.Uniform2f(c.vars[mat].uCoverUVOffset, coverOff.X, coverOff.Y)
c.ctx.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
p.Uniform1f(c.vars[mat].z, z)
p.Uniform2f(c.vars[mat].uScale, scale.X, scale.Y)
p.Uniform2f(c.vars[mat].uOffset, off.X, off.Y)
p.Uniform2f(c.vars[mat].uCoverUVScale, coverScale.X, coverScale.Y)
p.Uniform2f(c.vars[mat].uCoverUVOffset, coverOff.X, coverOff.Y)
c.ctx.DrawArrays(DrawModeTriangleStrip, 0, 4)
}
const stencilVSrc = `
+19 -20
View File
@@ -4,19 +4,17 @@ package gpu
import (
"time"
"gioui.org/gpu/gl"
)
type timers struct {
ctx *context
timers []*timer
backend Backend
timers []*timer
}
type timer struct {
Elapsed time.Duration
ctx *context
obj gl.Query
backend Backend
timer Timer
state timerState
}
@@ -28,9 +26,9 @@ const (
timerWaiting
)
func newTimers(ctx *context) *timers {
func newTimers(b Backend) *timers {
return &timers{
ctx: ctx,
backend: b,
}
}
@@ -39,8 +37,8 @@ func (t *timers) newTimer() *timer {
return nil
}
tt := &timer{
ctx: t.ctx,
obj: t.ctx.CreateQuery(),
backend: t.backend,
timer: t.backend.NewTimer(),
}
t.timers = append(t.timers, tt)
return tt
@@ -50,7 +48,7 @@ func (t *timer) begin() {
if t == nil || t.state != timerIdle {
return
}
t.ctx.BeginQuery(gl.TIME_ELAPSED_EXT, t.obj)
t.timer.Begin()
t.state = timerRunning
}
@@ -58,7 +56,7 @@ func (t *timer) end() {
if t == nil || t.state != timerRunning {
return
}
t.ctx.EndQuery(gl.TIME_ELAPSED_EXT)
t.timer.End()
t.state = timerWaiting
}
@@ -67,19 +65,20 @@ func (t *timers) ready() bool {
return false
}
for _, tt := range t.timers {
if tt.state != timerWaiting {
switch tt.state {
case timerIdle:
continue
case timerRunning:
return false
}
if t.ctx.GetQueryObjectuiv(tt.obj, gl.QUERY_RESULT_AVAILABLE) == 0 {
d, ok := tt.timer.Duration()
if !ok {
return false
}
}
for _, tt := range t.timers {
tt.state = timerIdle
nanos := t.ctx.GetQueryObjectuiv(tt.obj, gl.QUERY_RESULT)
tt.Elapsed = time.Duration(nanos)
tt.Elapsed = d
}
return t.ctx.GetInteger(gl.GPU_DISJOINT_EXT) == 0
return t.backend.IsTimeContinuous()
}
func (t *timers) release() {
@@ -87,7 +86,7 @@ func (t *timers) release() {
return
}
for _, tt := range t.timers {
t.ctx.DeleteQuery(tt.obj)
tt.timer.Release()
}
t.timers = nil
}
+46
View File
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: Unlicense OR MIT
package unsafe
import (
"reflect"
"unsafe"
)
// BytesView returns a byte slice view of a slice.
func BytesView(s interface{}) []byte {
v := reflect.ValueOf(s)
first := v.Index(0)
sz := int(first.Type().Size())
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(first.UnsafeAddr())))),
Len: v.Len() * sz,
Cap: v.Cap() * sz,
}))
}
// SliceOf returns a slice from a (native) pointer.
func SliceOf(s uintptr) []byte {
if s == 0 {
return nil
}
sh := reflect.SliceHeader{
Data: s,
Len: 1 << 30,
Cap: 1 << 30,
}
return *(*[]byte)(unsafe.Pointer(&sh))
}
// GoString convert a NUL-terminated C string
// to a Go string.
func GoString(s []byte) string {
i := 0
for {
if s[i] == 0 {
break
}
i++
}
return string(s[:i])
}
@@ -1,4 +1,6 @@
package gl
// SPDX-License-Identifier: Unlicense OR MIT
package unsafe
import (
"testing"