mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
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:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user