backend can now reuse gl contexts, updated examples and docs

This commit is contained in:
Thomas Friedel 2019-03-20 11:48:52 +01:00
parent 22c3c25d8e
commit c98bc6c23d
13 changed files with 276 additions and 185 deletions

View file

@ -13,10 +13,9 @@ const alphaTexSize = 2048
var zeroes [alphaTexSize]byte
type GoGLBackend struct {
x, y, w, h int
fx, fy, fw, fh float64
// GLContext is a context that contains all the
// shaders and buffers necessary for rendering
type GLContext struct {
buf uint32
shadowBuf uint32
alphaTex uint32
@ -43,158 +42,144 @@ type GoGLBackend struct {
ptsBuf []float32
glChan chan func()
activateFn func()
disableTextureRenderTarget func()
}
type offscreenBuffer struct {
tex uint32
w int
h int
renderStencilBuf uint32
frameBuf uint32
alpha bool
}
// NewGLContext creates all the necessary GL resources,
// like shaders and buffers
func NewGLContext() (*GLContext, error) {
ctx := &GLContext{
ptsBuf: make([]float32, 0, 4096),
glChan: make(chan func()),
}
func New(x, y, w, h int) (*GoGLBackend, error) {
err := gl.Init()
if err != nil {
return nil, err
}
b := &GoGLBackend{
w: w,
h: h,
fw: float64(w),
fh: float64(h),
ptsBuf: make([]float32, 0, 4096),
glChan: make(chan func()),
}
gl.GetError() // clear error state
err = loadShader(solidVS, solidFS, &b.sr.shaderProgram)
err = loadShader(solidVS, solidFS, &ctx.sr.shaderProgram)
if err != nil {
return nil, err
}
b.sr.shaderProgram.mustLoadLocations(&b.sr)
ctx.sr.shaderProgram.mustLoadLocations(&ctx.sr)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(linearGradientVS, linearGradientFS, &b.lgr.shaderProgram)
err = loadShader(linearGradientVS, linearGradientFS, &ctx.lgr.shaderProgram)
if err != nil {
return nil, err
}
b.lgr.shaderProgram.mustLoadLocations(&b.lgr)
ctx.lgr.shaderProgram.mustLoadLocations(&ctx.lgr)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(radialGradientVS, radialGradientFS, &b.rgr.shaderProgram)
err = loadShader(radialGradientVS, radialGradientFS, &ctx.rgr.shaderProgram)
if err != nil {
return nil, err
}
b.rgr.shaderProgram.mustLoadLocations(&b.rgr)
ctx.rgr.shaderProgram.mustLoadLocations(&ctx.rgr)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(imagePatternVS, imagePatternFS, &b.ipr.shaderProgram)
err = loadShader(imagePatternVS, imagePatternFS, &ctx.ipr.shaderProgram)
if err != nil {
return nil, err
}
b.ipr.shaderProgram.mustLoadLocations(&b.ipr)
ctx.ipr.shaderProgram.mustLoadLocations(&ctx.ipr)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(solidAlphaVS, solidAlphaFS, &b.sar.shaderProgram)
err = loadShader(solidAlphaVS, solidAlphaFS, &ctx.sar.shaderProgram)
if err != nil {
return nil, err
}
b.sar.shaderProgram.mustLoadLocations(&b.sar)
ctx.sar.shaderProgram.mustLoadLocations(&ctx.sar)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(linearGradientAlphaVS, linearGradientFS, &b.lgar.shaderProgram)
err = loadShader(linearGradientAlphaVS, linearGradientFS, &ctx.lgar.shaderProgram)
if err != nil {
return nil, err
}
b.lgar.shaderProgram.mustLoadLocations(&b.lgar)
ctx.lgar.shaderProgram.mustLoadLocations(&ctx.lgar)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(radialGradientAlphaVS, radialGradientAlphaFS, &b.rgar.shaderProgram)
err = loadShader(radialGradientAlphaVS, radialGradientAlphaFS, &ctx.rgar.shaderProgram)
if err != nil {
return nil, err
}
b.rgar.shaderProgram.mustLoadLocations(&b.rgar)
ctx.rgar.shaderProgram.mustLoadLocations(&ctx.rgar)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(imagePatternAlphaVS, imagePatternAlphaFS, &b.ipar.shaderProgram)
err = loadShader(imagePatternAlphaVS, imagePatternAlphaFS, &ctx.ipar.shaderProgram)
if err != nil {
return nil, err
}
b.ipar.shaderProgram.mustLoadLocations(&b.ipar)
ctx.ipar.shaderProgram.mustLoadLocations(&ctx.ipar)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(imageVS, imageFS, &b.ir.shaderProgram)
err = loadShader(imageVS, imageFS, &ctx.ir.shaderProgram)
if err != nil {
return nil, err
}
b.ir.shaderProgram.mustLoadLocations(&b.ir)
ctx.ir.shaderProgram.mustLoadLocations(&ctx.ir)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(gaussian15VS, gaussian15FS, &b.gauss15r.shaderProgram)
err = loadShader(gaussian15VS, gaussian15FS, &ctx.gauss15r.shaderProgram)
if err != nil {
return nil, err
}
b.gauss15r.shaderProgram.mustLoadLocations(&b.gauss15r)
ctx.gauss15r.shaderProgram.mustLoadLocations(&ctx.gauss15r)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(gaussian63VS, gaussian63FS, &b.gauss63r.shaderProgram)
err = loadShader(gaussian63VS, gaussian63FS, &ctx.gauss63r.shaderProgram)
if err != nil {
return nil, err
}
b.gauss63r.shaderProgram.mustLoadLocations(&b.gauss63r)
ctx.gauss63r.shaderProgram.mustLoadLocations(&ctx.gauss63r)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(gaussian127VS, gaussian127FS, &b.gauss127r.shaderProgram)
err = loadShader(gaussian127VS, gaussian127FS, &ctx.gauss127r.shaderProgram)
if err != nil {
return nil, err
}
b.gauss127r.shaderProgram.mustLoadLocations(&b.gauss127r)
ctx.gauss127r.shaderProgram.mustLoadLocations(&ctx.gauss127r)
if err = glError(); err != nil {
return nil, err
}
gl.GenBuffers(1, &b.buf)
gl.GenBuffers(1, &ctx.buf)
if err = glError(); err != nil {
return nil, err
}
gl.GenBuffers(1, &b.shadowBuf)
gl.GenBuffers(1, &ctx.shadowBuf)
if err = glError(); err != nil {
return nil, err
}
gl.ActiveTexture(gl.TEXTURE0)
gl.GenTextures(1, &b.alphaTex)
gl.BindTexture(gl.TEXTURE_2D, b.alphaTex)
gl.GenTextures(1, &ctx.alphaTex)
gl.BindTexture(gl.TEXTURE_2D, ctx.alphaTex)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
@ -211,6 +196,51 @@ func New(x, y, w, h int) (*GoGLBackend, error) {
gl.Disable(gl.SCISSOR_TEST)
return ctx, nil
}
// GoGLBackend is a canvas backend using Go-GL
type GoGLBackend struct {
x, y, w, h int
fx, fy, fw, fh float64
*GLContext
activateFn func()
disableTextureRenderTarget func()
}
type offscreenBuffer struct {
tex uint32
w int
h int
renderStencilBuf uint32
frameBuf uint32
alpha bool
}
// New returns a new canvas backend. x, y, w, h define the target
// rectangle in the window. ctx is a GLContext created with
// NewGLContext, but can be nil for a default one. It makes sense
// to pass one in when using for example an onscreen and an
// offscreen backend using the same GL context.
func New(x, y, w, h int, ctx *GLContext) (*GoGLBackend, error) {
if ctx == nil {
var err error
ctx, err = NewGLContext()
if err != nil {
return nil, err
}
}
b := &GoGLBackend{
w: w,
h: h,
fw: float64(w),
fh: float64(h),
GLContext: ctx,
}
b.activateFn = func() {
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
gl.Viewport(int32(b.x), int32(b.y), int32(b.w), int32(b.h))
@ -222,6 +252,8 @@ func New(x, y, w, h int) (*GoGLBackend, error) {
return b, nil
}
// GoGLBackendOffscreen is a canvas backend using an offscreen
// texture
type GoGLBackendOffscreen struct {
GoGLBackend
@ -229,8 +261,13 @@ type GoGLBackendOffscreen struct {
offscrImg Image
}
func NewOffscreen(w, h int, alpha bool) (*GoGLBackendOffscreen, error) {
b, err := New(0, 0, w, h)
// NewOffscreen returns a new offscreen canvas backend. w, h define
// the size of the offscreen texture. ctx is a GLContext created
// with NewGLContext, but can be nil for a default one. It makes
// sense to pass one in when using for example an onscreen and an
// offscreen backend using the same GL context.
func NewOffscreen(w, h int, alpha bool, ctx *GLContext) (*GoGLBackendOffscreen, error) {
b, err := New(0, 0, w, h, ctx)
if err != nil {
return nil, err
}
@ -267,14 +304,16 @@ func (b *GoGLBackend) SetBounds(x, y, w, h int) {
}
}
// SetBounds updates the size of the offscreen texture
func (b *GoGLBackendOffscreen) SetBounds(w, h int) {
// SetSize updates the size of the offscreen texture
func (b *GoGLBackendOffscreen) SetSize(w, h int) {
b.GoGLBackend.SetBounds(0, 0, w, h)
b.enableTextureRenderTarget(&b.offscrBuf)
b.offscrImg.w = b.offscrBuf.w
b.offscrImg.h = b.offscrBuf.h
}
// Size returns the size of the window or offscreen
// texture
func (b *GoGLBackend) Size() (int, int) {
return b.w, b.h
}
@ -308,21 +347,31 @@ func (b *GoGLBackend) runGLQueue() {
}
}
// Delete deletes the offscreen texture. After calling this
// the backend can no longer be used
func (b *GoGLBackendOffscreen) Delete() {
gl.DeleteTextures(1, &b.offscrBuf.tex)
gl.DeleteFramebuffers(1, &b.offscrBuf.frameBuf)
gl.DeleteRenderbuffers(1, &b.offscrBuf.renderStencilBuf)
}
// CanUseAsImage returns true if the given backend can be
// directly used by this backend to avoid a conversion.
// Used internally
func (b *GoGLBackend) CanUseAsImage(b2 backendbase.Backend) bool {
_, ok := b2.(*GoGLBackendOffscreen)
return ok
}
// AsImage returns nil, since this backend cannot be directly
// used as an image. Used internally
func (b *GoGLBackend) AsImage() backendbase.Image {
return nil
}
// AsImage returns an implementation of the Image interface
// that can be used to render this offscreen texture
// directly. Used internally
func (b *GoGLBackendOffscreen) AsImage() backendbase.Image {
return &b.offscrImg
}
@ -506,8 +555,8 @@ func (b *GoGLBackend) enableTextureRenderTarget(offscr *offscreenBuffer) {
type vec [2]float64
func (v1 vec) sub(v2 vec) vec {
return vec{v1[0] - v2[0], v1[1] - v2[1]}
func (v vec) sub(v2 vec) vec {
return vec{v[0] - v2[0], v[1] - v2[1]}
}
func (v vec) len() float64 {

View file

@ -248,16 +248,16 @@ func rewrite(filename, src string) (string, string) {
}
func rewriteMain(src string) string {
src = strings.Replace(src, "type GLContext struct {\n",
"type GLContext struct {\n\tglctx gl.Context\n\n", 1)
src = strings.Replace(src, "ctx := &GLContext{\n",
"ctx := &GLContext{\n\t\tglctx: glctx,\n\n", 1)
src = strings.Replace(src, "\tb.glctx.GetError() // clear error state\n",
"\tb := &XMobileBackend{GLContext: ctx}\n\n\tb.glctx.GetError() // clear error state\n\n", 1)
src = strings.Replace(src, "type XMobileBackend struct {\n",
"type XMobileBackend struct {\n\tglctx gl.Context\n\n", 1)
src = strings.Replace(src, "func New(x, y, w, h int) (*XMobileBackend, error) {",
"func New(glctx gl.Context, x, y, w, h int) (*XMobileBackend, error) {", 1)
src = strings.Replace(src, "func NewOffscreen(w, h int, alpha bool) (*XMobileBackendOffscreen, error)",
"func NewOffscreen(glctx gl.Context, w, h int, alpha bool) (*XMobileBackendOffscreen, error)", 1)
src = rewriteCalls(src, "New", func(params []string) string {
return "New(glctx, " + strings.Join(params, ", ") + ")"
})
"type XMobileBackend struct {\n", 1)
src = strings.Replace(src, "func NewGLContext() (*GLContext, error) {",
"func NewGLContext(glctx gl.Context) (*GLContext, error) {", 1)
src = strings.Replace(src,
` err := gl.Init()
@ -268,8 +268,37 @@ func rewriteMain(src string) string {
`, ` var err error
`, 1)
src = strings.Replace(src, "\tb := &XMobileBackend{\n",
"\tb := &XMobileBackend{\n\t\tglctx: glctx,\n\n", 1)
src = strings.Replace(src,
`// New returns a new canvas backend. x, y, w, h define the target
// rectangle in the window. ctx is a GLContext created with
// NewGLContext, but can be nil for a default one. It makes sense
// to pass one in when using for example an onscreen and an
// offscreen backend using the same GL context.
`, `// New returns a new canvas backend. x, y, w, h define the target
// rectangle in the window. ctx is a GLContext created with
// NewGLContext
`, 1)
src = strings.Replace(src,
`// NewOffscreen returns a new offscreen canvas backend. w, h define
// the size of the offscreen texture. ctx is a GLContext created
// with NewGLContext, but can be nil for a default one. It makes
// sense to pass one in when using for example an onscreen and an
// offscreen backend using the same GL context.
`, `// NewOffscreen returns a new offscreen canvas backend. w, h define
// the size of the offscreen texture. ctx is a GLContext created
// with NewGLContext
`, 1)
src = strings.Replace(src,
` if ctx == nil {
var err error
ctx, err = NewGLContext()
if err != nil {
return nil, err
}
}
`, "", 1)
src = strings.Replace(src,
` buf uint32

View file

@ -27,15 +27,13 @@ type gradient struct {
b *XMobileBackend
tex gl.Texture
loaded bool
deleted bool
opaque bool
}
func (b *XMobileBackend) LoadLinearGradient(data backendbase.Gradient) backendbase.LinearGradient {
b.activate()
lg := &LinearGradient{
gradient: gradient{b: b, opaque: true},
gradient: gradient{b: b},
}
lg.tex = b.glctx.CreateTexture()
b.glctx.ActiveTexture(gl.TEXTURE0)
@ -57,7 +55,7 @@ func (b *XMobileBackend) LoadRadialGradient(data backendbase.Gradient) backendba
b.activate()
rg := &RadialGradient{
gradient: gradient{b: b, opaque: true},
gradient: gradient{b: b},
}
rg.tex = b.glctx.CreateTexture()
b.glctx.ActiveTexture(gl.TEXTURE0)
@ -81,12 +79,8 @@ func (g *gradient) Delete() {
g.b.activate()
b.glctx.DeleteTexture(g.tex)
g.deleted = true
}
func (g *gradient) IsDeleted() bool { return g.deleted }
func (g *gradient) IsOpaque() bool { return g.opaque }
func (lg *LinearGradient) Replace(data backendbase.Gradient) { lg.load(data) }
func (rg *RadialGradient) Replace(data backendbase.Gradient) { rg.load(data) }

View file

@ -15,8 +15,6 @@ type Image struct {
b *XMobileBackend
w, h int
tex gl.Texture
deleted bool
opaque bool
flip bool
}
@ -38,11 +36,9 @@ func (b *XMobileBackend) LoadImage(src image.Image) (backendbase.Image, error) {
img.b = b
runtime.SetFinalizer(img, func(img *Image) {
if !img.deleted {
b.glChan <- func() {
b.glctx.DeleteTexture(img.tex)
}
}
})
return img, nil
@ -74,19 +70,7 @@ func loadImage(b *XMobileBackend, src image.Image, tex gl.Texture) (*Image, erro
}
func loadImageRGBA(b *XMobileBackend, src *image.RGBA, tex gl.Texture) (*Image, error) {
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy(), opaque: true}
checkOpaque:
for y := 0; y < img.h; y++ {
off := src.PixOffset(0, y) + 3
for x := 0; x < img.w; x++ {
if src.Pix[off] < 255 {
img.opaque = false
break checkOpaque
}
off += 4
}
}
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy()}
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
@ -147,7 +131,7 @@ func loadImageGray(b *XMobileBackend, src *image.Gray, tex gl.Texture) (*Image,
}
func loadImageConverted(b *XMobileBackend, src image.Image, tex gl.Texture) (*Image, error) {
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy(), opaque: true}
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy()}
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
@ -161,9 +145,6 @@ func loadImageConverted(b *XMobileBackend, src image.Image, tex gl.Texture) (*Im
ir, ig, ib, ia := src.At(x, y).RGBA()
r, g, b, a := uint8(ir>>8), uint8(ig>>8), uint8(ib>>8), uint8(ia>>8)
data = append(data, r, g, b, a)
if a < 255 {
img.opaque = false
}
}
}
b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, img.w, img.h, gl.RGBA, gl.UNSIGNED_BYTE, data[0:])
@ -193,13 +174,8 @@ func (img *Image) Delete() {
img.b.activate()
b.glctx.DeleteTexture(img.tex)
img.deleted = true
}
// IsDeleted returns true if the Delete function has been
// called on this image
func (img *Image) IsDeleted() bool { return img.deleted }
// Replace replaces the image with the new one
func (img *Image) Replace(src image.Image) error {
b := img.b
@ -216,10 +192,6 @@ func (img *Image) Replace(src image.Image) error {
return nil
}
// IsOpaque returns true if all pixels in the image
// have a full alpha value
func (img *Image) IsOpaque() bool { return img.opaque }
func (b *XMobileBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64, pts [4][2]float64, alpha float64) {
b.activate()

View file

@ -15,12 +15,11 @@ const alphaTexSize = 2048
var zeroes [alphaTexSize]byte
type XMobileBackend struct {
// GLContext is a context that contains all the
// shaders and buffers necessary for rendering
type GLContext struct {
glctx gl.Context
x, y, w, h int
fx, fy, fw, fh float64
buf gl.Buffer
shadowBuf gl.Buffer
alphaTex gl.Texture
@ -47,157 +46,145 @@ type XMobileBackend struct {
ptsBuf []float32
glChan chan func()
activateFn func()
disableTextureRenderTarget func()
}
type offscreenBuffer struct {
tex gl.Texture
w int
h int
renderStencilBuf gl.Renderbuffer
frameBuf gl.Framebuffer
alpha bool
}
func New(glctx gl.Context, x, y, w, h int) (*XMobileBackend, error) {
var err error
b := &XMobileBackend{
// NewGLContext creates all the necessary GL resources,
// like shaders and buffers
func NewGLContext(glctx gl.Context) (*GLContext, error) {
ctx := &GLContext{
glctx: glctx,
w: w,
h: h,
fw: float64(w),
fh: float64(h),
ptsBuf: make([]float32, 0, 4096),
glChan: make(chan func()),
}
var err error
b := &XMobileBackend{GLContext: ctx}
b.glctx.GetError() // clear error state
err = loadShader(b, solidVS, solidFS, &b.sr.shaderProgram)
err = loadShader(b, solidVS, solidFS, &ctx.sr.shaderProgram)
if err != nil {
return nil, err
}
b.sr.shaderProgram.mustLoadLocations(&b.sr)
ctx.sr.shaderProgram.mustLoadLocations(&ctx.sr)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, linearGradientVS, linearGradientFS, &b.lgr.shaderProgram)
err = loadShader(b, linearGradientVS, linearGradientFS, &ctx.lgr.shaderProgram)
if err != nil {
return nil, err
}
b.lgr.shaderProgram.mustLoadLocations(&b.lgr)
ctx.lgr.shaderProgram.mustLoadLocations(&ctx.lgr)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, radialGradientVS, radialGradientFS, &b.rgr.shaderProgram)
err = loadShader(b, radialGradientVS, radialGradientFS, &ctx.rgr.shaderProgram)
if err != nil {
return nil, err
}
b.rgr.shaderProgram.mustLoadLocations(&b.rgr)
ctx.rgr.shaderProgram.mustLoadLocations(&ctx.rgr)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, imagePatternVS, imagePatternFS, &b.ipr.shaderProgram)
err = loadShader(b, imagePatternVS, imagePatternFS, &ctx.ipr.shaderProgram)
if err != nil {
return nil, err
}
b.ipr.shaderProgram.mustLoadLocations(&b.ipr)
ctx.ipr.shaderProgram.mustLoadLocations(&ctx.ipr)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, solidAlphaVS, solidAlphaFS, &b.sar.shaderProgram)
err = loadShader(b, solidAlphaVS, solidAlphaFS, &ctx.sar.shaderProgram)
if err != nil {
return nil, err
}
b.sar.shaderProgram.mustLoadLocations(&b.sar)
ctx.sar.shaderProgram.mustLoadLocations(&ctx.sar)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, linearGradientAlphaVS, linearGradientFS, &b.lgar.shaderProgram)
err = loadShader(b, linearGradientAlphaVS, linearGradientFS, &ctx.lgar.shaderProgram)
if err != nil {
return nil, err
}
b.lgar.shaderProgram.mustLoadLocations(&b.lgar)
ctx.lgar.shaderProgram.mustLoadLocations(&ctx.lgar)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, radialGradientAlphaVS, radialGradientAlphaFS, &b.rgar.shaderProgram)
err = loadShader(b, radialGradientAlphaVS, radialGradientAlphaFS, &ctx.rgar.shaderProgram)
if err != nil {
return nil, err
}
b.rgar.shaderProgram.mustLoadLocations(&b.rgar)
ctx.rgar.shaderProgram.mustLoadLocations(&ctx.rgar)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, imagePatternAlphaVS, imagePatternAlphaFS, &b.ipar.shaderProgram)
err = loadShader(b, imagePatternAlphaVS, imagePatternAlphaFS, &ctx.ipar.shaderProgram)
if err != nil {
return nil, err
}
b.ipar.shaderProgram.mustLoadLocations(&b.ipar)
ctx.ipar.shaderProgram.mustLoadLocations(&ctx.ipar)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, imageVS, imageFS, &b.ir.shaderProgram)
err = loadShader(b, imageVS, imageFS, &ctx.ir.shaderProgram)
if err != nil {
return nil, err
}
b.ir.shaderProgram.mustLoadLocations(&b.ir)
ctx.ir.shaderProgram.mustLoadLocations(&ctx.ir)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, gaussian15VS, gaussian15FS, &b.gauss15r.shaderProgram)
err = loadShader(b, gaussian15VS, gaussian15FS, &ctx.gauss15r.shaderProgram)
if err != nil {
return nil, err
}
b.gauss15r.shaderProgram.mustLoadLocations(&b.gauss15r)
ctx.gauss15r.shaderProgram.mustLoadLocations(&ctx.gauss15r)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, gaussian63VS, gaussian63FS, &b.gauss63r.shaderProgram)
err = loadShader(b, gaussian63VS, gaussian63FS, &ctx.gauss63r.shaderProgram)
if err != nil {
return nil, err
}
b.gauss63r.shaderProgram.mustLoadLocations(&b.gauss63r)
ctx.gauss63r.shaderProgram.mustLoadLocations(&ctx.gauss63r)
if err = glError(b); err != nil {
return nil, err
}
err = loadShader(b, gaussian127VS, gaussian127FS, &b.gauss127r.shaderProgram)
err = loadShader(b, gaussian127VS, gaussian127FS, &ctx.gauss127r.shaderProgram)
if err != nil {
return nil, err
}
b.gauss127r.shaderProgram.mustLoadLocations(&b.gauss127r)
ctx.gauss127r.shaderProgram.mustLoadLocations(&ctx.gauss127r)
if err = glError(b); err != nil {
return nil, err
}
b.buf = b.glctx.CreateBuffer()
ctx.buf = b.glctx.CreateBuffer()
if err = glError(b); err != nil {
return nil, err
}
b.shadowBuf = b.glctx.CreateBuffer()
ctx.shadowBuf = b.glctx.CreateBuffer()
if err = glError(b); err != nil {
return nil, err
}
b.glctx.ActiveTexture(gl.TEXTURE0)
b.alphaTex = b.glctx.CreateTexture()
b.glctx.BindTexture(gl.TEXTURE_2D, b.alphaTex)
ctx.alphaTex = b.glctx.CreateTexture()
b.glctx.BindTexture(gl.TEXTURE_2D, ctx.alphaTex)
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
@ -214,6 +201,41 @@ func New(glctx gl.Context, x, y, w, h int) (*XMobileBackend, error) {
b.glctx.Disable(gl.SCISSOR_TEST)
return ctx, nil
}
// XMobileBackend is a canvas backend using Go-GL
type XMobileBackend struct {
x, y, w, h int
fx, fy, fw, fh float64
*GLContext
activateFn func()
disableTextureRenderTarget func()
}
type offscreenBuffer struct {
tex gl.Texture
w int
h int
renderStencilBuf gl.Renderbuffer
frameBuf gl.Framebuffer
alpha bool
}
// New returns a new canvas backend. x, y, w, h define the target
// rectangle in the window. ctx is a GLContext created with
// NewGLContext
func New(x, y, w, h int, ctx *GLContext) (*XMobileBackend, error) {
b := &XMobileBackend{
w: w,
h: h,
fw: float64(w),
fh: float64(h),
GLContext: ctx,
}
b.activateFn = func() {
b.glctx.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{Value: 0})
b.glctx.Viewport(b.x, b.y, b.w, b.h)
@ -225,6 +247,8 @@ func New(glctx gl.Context, x, y, w, h int) (*XMobileBackend, error) {
return b, nil
}
// XMobileBackendOffscreen is a canvas backend using an offscreen
// texture
type XMobileBackendOffscreen struct {
XMobileBackend
@ -232,8 +256,11 @@ type XMobileBackendOffscreen struct {
offscrImg Image
}
func NewOffscreen(glctx gl.Context, w, h int, alpha bool) (*XMobileBackendOffscreen, error) {
b, err := New(glctx, 0, 0, w, h)
// NewOffscreen returns a new offscreen canvas backend. w, h define
// the size of the offscreen texture. ctx is a GLContext created
// with NewGLContext
func NewOffscreen(w, h int, alpha bool, ctx *GLContext) (*XMobileBackendOffscreen, error) {
b, err := New(0, 0, w, h, ctx)
if err != nil {
return nil, err
}
@ -270,14 +297,16 @@ func (b *XMobileBackend) SetBounds(x, y, w, h int) {
}
}
// SetBounds updates the size of the offscreen texture
func (b *XMobileBackendOffscreen) SetBounds(w, h int) {
// SetSize updates the size of the offscreen texture
func (b *XMobileBackendOffscreen) SetSize(w, h int) {
b.XMobileBackend.SetBounds(0, 0, w, h)
b.enableTextureRenderTarget(&b.offscrBuf)
b.offscrImg.w = b.offscrBuf.w
b.offscrImg.h = b.offscrBuf.h
}
// Size returns the size of the window or offscreen
// texture
func (b *XMobileBackend) Size() (int, int) {
return b.w, b.h
}
@ -311,21 +340,31 @@ func (b *XMobileBackend) runGLQueue() {
}
}
// Delete deletes the offscreen texture. After calling this
// the backend can no longer be used
func (b *XMobileBackendOffscreen) Delete() {
b.glctx.DeleteTexture(b.offscrBuf.tex)
b.glctx.DeleteFramebuffer(b.offscrBuf.frameBuf)
b.glctx.DeleteRenderbuffer(b.offscrBuf.renderStencilBuf)
}
// CanUseAsImage returns true if the given backend can be
// directly used by this backend to avoid a conversion.
// Used internally
func (b *XMobileBackend) CanUseAsImage(b2 backendbase.Backend) bool {
_, ok := b2.(*XMobileBackendOffscreen)
return ok
}
// AsImage returns nil, since this backend cannot be directly
// used as an image. Used internally
func (b *XMobileBackend) AsImage() backendbase.Image {
return nil
}
// AsImage returns an implementation of the Image interface
// that can be used to render this offscreen texture
// directly. Used internally
func (b *XMobileBackendOffscreen) AsImage() backendbase.Image {
return &b.offscrImg
}
@ -509,8 +548,8 @@ func (b *XMobileBackend) enableTextureRenderTarget(offscr *offscreenBuffer) {
type vec [2]float64
func (v1 vec) sub(v2 vec) vec {
return vec{v1[0] - v2[0], v1[1] - v2[1]}
func (v vec) sub(v2 vec) vec {
return vec{v[0] - v2[0], v[1] - v2[1]}
}
func (v vec) len() float64 {

View file

@ -18,7 +18,7 @@ func OnSurfaceCreated() {
}
func OnSurfaceChanged(w, h int) {
backend, err := goglbackend.New(0, 0, w, h)
backend, err := goglbackend.New(0, 0, w, h, nil)
if err != nil {
panic(err)
}

View file

@ -42,7 +42,7 @@ func main() {
gl.Enable(gl.MULTISAMPLE)
// load GL backend
backend, err := goglbackend.New(0, 0, 0, 0)
backend, err := goglbackend.New(0, 0, 0, 0, nil)
if err != nil {
log.Fatalf("Error loading canvas GL assets: %v", err)
}

View file

@ -29,11 +29,15 @@ func main() {
case lifecycle.CrossOn:
var err error
glctx = e.DrawContext.(gl.Context)
cvb, err = xmobilebackend.NewOffscreen(glctx, 0, 0, false)
ctx, err := xmobilebackend.NewGLContext(glctx)
if err != nil {
log.Fatal(err)
}
cvb, err = xmobilebackend.NewOffscreen(0, 0, false, ctx)
if err != nil {
log.Fatalln(err)
}
painterb, err = xmobilebackend.New(glctx, 0, 0, 0, 0)
painterb, err = xmobilebackend.New(0, 0, 0, 0, ctx)
if err != nil {
log.Fatalln(err)
}
@ -51,7 +55,7 @@ func main() {
fw, fh := float64(w), float64(h)
color := math.Sin(float64(time.Now().UnixNano())*0.000000002)*0.3 + 0.7
cvb.SetBounds(w, h)
cvb.SetSize(w, h)
cv.SetFillStyle(color*0.2, color*0.2, color*0.8)
cv.FillRect(fw*0.25, fh*0.25, fw*0.5, fh*0.5)

View file

@ -15,7 +15,7 @@ func TouchEvent(typ string, x, y int) {
}
func LoadGL(w, h int) {
backend, err := goglbackend.New(0, 0, w, h)
backend, err := goglbackend.New(0, 0, w, h, nil)
if err != nil {
panic(err)
}

View file

@ -63,7 +63,7 @@ func main() {
gl.Enable(gl.MULTISAMPLE)
// load GL backend
backend, err := goglbackend.New(0, 0, 0, 0)
backend, err := goglbackend.New(0, 0, 0, 0, nil)
if err != nil {
log.Fatalf("Error loading canvas GL assets: %v", err)
}

View file

@ -19,7 +19,11 @@ func main() {
gldriver.Main(func(s screen.Screen) {
glw := glwidget.NewGL(draw)
sheet = widget.NewSheet(glw)
backend, err := xmobilebackend.New(glw.Ctx, 0, 0, 600, 600)
ctx, err := xmobilebackend.NewGLContext(glw.Ctx)
if err != nil {
log.Fatal(err)
}
backend, err := xmobilebackend.New(0, 0, 600, 600, ctx)
if err != nil {
log.Fatal(err)
}

View file

@ -67,7 +67,7 @@ func CreateWindow(w, h int, title string) (*Window, *canvas.Canvas, error) {
gl.Enable(gl.MULTISAMPLE)
// load canvas GL backend
backend, err := goglbackend.New(0, 0, w, h)
backend, err := goglbackend.New(0, 0, w, h, nil)
if err != nil {
return nil, nil, fmt.Errorf("Error loading GoGL backend: %v", err)
}

View file

@ -88,7 +88,7 @@ func CreateWindow(w, h int, title string) (*Window, *canvas.Canvas, error) {
}
// load canvas GL backend
backend, err := goglbackend.New(0, 0, w, h)
backend, err := goglbackend.New(0, 0, w, h, nil)
if err != nil {
return nil, nil, fmt.Errorf("Error loading GoGL backend: %v", err)
}