mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-02 07:57:29 +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:
@@ -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])
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package gl
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGoString(t *testing.T) {
|
||||
tests := [][2]string{
|
||||
{"Hello\x00", "Hello"},
|
||||
{"\x00", ""},
|
||||
}
|
||||
for _, test := range tests {
|
||||
got := GoString([]byte(test[0]))
|
||||
if exp := test[1]; exp != got {
|
||||
t.Errorf("expected %q got %q", exp, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user