gl_ios.go raw
1 // SPDX-License-Identifier: Unlicense OR MIT
2
3 // +build darwin,ios
4
5 package wm
6
7 /*
8 #include <CoreFoundation/CoreFoundation.h>
9 #include <OpenGLES/ES2/gl.h>
10 #include <OpenGLES/ES2/glext.h>
11
12 __attribute__ ((visibility ("hidden"))) int gio_renderbufferStorage(CFTypeRef ctx, CFTypeRef layer, GLenum buffer);
13 __attribute__ ((visibility ("hidden"))) int gio_presentRenderbuffer(CFTypeRef ctx, GLenum buffer);
14 __attribute__ ((visibility ("hidden"))) int gio_makeCurrent(CFTypeRef ctx);
15 __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createContext(void);
16 __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLLayer(void);
17 */
18 import "C"
19
20 import (
21 "errors"
22 "fmt"
23
24 "github.com/p9c/p9/pkg/gel/gio/gpu"
25 "github.com/p9c/p9/pkg/gel/gio/internal/gl"
26 )
27
28 type context struct {
29 owner *window
30 c *gl.Functions
31 ctx C.CFTypeRef
32 layer C.CFTypeRef
33 init bool
34 frameBuffer gl.Framebuffer
35 colorBuffer, depthBuffer gl.Renderbuffer
36 }
37
38 func init() {
39 layerFactory = func() uintptr {
40 return uintptr(C.gio_createGLLayer())
41 }
42 }
43
44 func newContext(w *window) (*context, error) {
45 ctx := C.gio_createContext()
46 if ctx == 0 {
47 return nil, fmt.Errorf("failed to create EAGLContext")
48 }
49 c := &context{
50 ctx: ctx,
51 owner: w,
52 layer: C.CFTypeRef(w.contextLayer()),
53 c: new(gl.Functions),
54 }
55 return c, nil
56 }
57
58 func (c *context) API() gpu.API {
59 return gpu.OpenGL{}
60 }
61
62 func (c *context) Release() {
63 if c.ctx == 0 {
64 return
65 }
66 C.gio_renderbufferStorage(c.ctx, 0, C.GLenum(gl.RENDERBUFFER))
67 c.c.DeleteFramebuffer(c.frameBuffer)
68 c.c.DeleteRenderbuffer(c.colorBuffer)
69 c.c.DeleteRenderbuffer(c.depthBuffer)
70 C.gio_makeCurrent(0)
71 C.CFRelease(c.ctx)
72 c.ctx = 0
73 }
74
75 func (c *context) Present() error {
76 if c.layer == 0 {
77 panic("context is not active")
78 }
79 // Discard depth buffer as recommended in
80 // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html
81 c.c.BindFramebuffer(gl.FRAMEBUFFER, c.frameBuffer)
82 c.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT)
83 c.c.BindRenderbuffer(gl.RENDERBUFFER, c.colorBuffer)
84 if C.gio_presentRenderbuffer(c.ctx, C.GLenum(gl.RENDERBUFFER)) == 0 {
85 return errors.New("presentRenderBuffer failed")
86 }
87 return nil
88 }
89
90 func (c *context) Lock() {}
91
92 func (c *context) Unlock() {}
93
94 func (c *context) MakeCurrent() error {
95 if C.gio_makeCurrent(c.ctx) == 0 {
96 C.CFRelease(c.ctx)
97 c.ctx = 0
98 return errors.New("[EAGLContext setCurrentContext] failed")
99 }
100 if !c.init {
101 c.init = true
102 c.frameBuffer = c.c.CreateFramebuffer()
103 c.colorBuffer = c.c.CreateRenderbuffer()
104 c.depthBuffer = c.c.CreateRenderbuffer()
105 }
106 if !c.owner.isVisible() {
107 // Make sure any in-flight GL commands are complete.
108 c.c.Finish()
109 return nil
110 }
111 currentRB := gl.Renderbuffer{uint(c.c.GetInteger(gl.RENDERBUFFER_BINDING))}
112 c.c.BindRenderbuffer(gl.RENDERBUFFER, c.colorBuffer)
113 if C.gio_renderbufferStorage(c.ctx, c.layer, C.GLenum(gl.RENDERBUFFER)) == 0 {
114 return errors.New("renderbufferStorage failed")
115 }
116 w := c.c.GetRenderbufferParameteri(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)
117 h := c.c.GetRenderbufferParameteri(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT)
118 c.c.BindRenderbuffer(gl.RENDERBUFFER, c.depthBuffer)
119 c.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h)
120 c.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB)
121 c.c.BindFramebuffer(gl.FRAMEBUFFER, c.frameBuffer)
122 c.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, c.colorBuffer)
123 c.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, c.depthBuffer)
124 if st := c.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
125 return fmt.Errorf("framebuffer incomplete, status: %#x\n", st)
126 }
127 return nil
128 }
129
130 func (w *window) NewContext() (Context, error) {
131 return newContext(w)
132 }
133