forked from joejulian/gio
7d84e419c9
Both the OpenGL and the Direct3D API are stateful and gpu.GPU renders to the render target current when Frame is called. Modern GPU API such as Metal don't have a concept of a current render target, and the target even changes each frame. Add RenderTarget and add an explicit target argument to GPU.Frame as well as the underlying driver.Device.BeginFrame. Signed-off-by: Elias Naur <mail@eliasnaur.com>
152 lines
4.2 KiB
Go
152 lines
4.2 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
//go:build darwin && ios
|
|
// +build darwin,ios
|
|
|
|
package wm
|
|
|
|
/*
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
#include <OpenGLES/ES2/gl.h>
|
|
#include <OpenGLES/ES2/glext.h>
|
|
|
|
__attribute__ ((visibility ("hidden"))) int gio_renderbufferStorage(CFTypeRef ctx, CFTypeRef layer, GLenum buffer);
|
|
__attribute__ ((visibility ("hidden"))) int gio_presentRenderbuffer(CFTypeRef ctx, GLenum buffer);
|
|
__attribute__ ((visibility ("hidden"))) int gio_makeCurrent(CFTypeRef ctx);
|
|
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createContext(void);
|
|
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLLayer(void);
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"gioui.org/gpu"
|
|
"gioui.org/internal/gl"
|
|
)
|
|
|
|
type context struct {
|
|
owner *window
|
|
c *gl.Functions
|
|
ctx C.CFTypeRef
|
|
layer C.CFTypeRef
|
|
init bool
|
|
frameBuffer gl.Framebuffer
|
|
colorBuffer, depthBuffer gl.Renderbuffer
|
|
}
|
|
|
|
func init() {
|
|
layerFactory = func() uintptr {
|
|
return uintptr(C.gio_createGLLayer())
|
|
}
|
|
}
|
|
|
|
func newContext(w *window) (*context, error) {
|
|
ctx := C.gio_createContext()
|
|
if ctx == 0 {
|
|
return nil, fmt.Errorf("failed to create EAGLContext")
|
|
}
|
|
api := contextAPI()
|
|
f, err := gl.NewFunctions(api.Context, api.ES)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c := &context{
|
|
ctx: ctx,
|
|
owner: w,
|
|
layer: C.CFTypeRef(w.contextLayer()),
|
|
c: f,
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func contextAPI() gpu.OpenGL {
|
|
return gpu.OpenGL{}
|
|
}
|
|
|
|
func (c *context) RenderTarget() gpu.RenderTarget {
|
|
return gpu.OpenGLRenderTarget(c.frameBuffer)
|
|
}
|
|
|
|
func (c *context) API() gpu.API {
|
|
return contextAPI()
|
|
}
|
|
|
|
func (c *context) Release() {
|
|
if c.ctx == 0 {
|
|
return
|
|
}
|
|
C.gio_renderbufferStorage(c.ctx, 0, C.GLenum(gl.RENDERBUFFER))
|
|
c.c.DeleteFramebuffer(c.frameBuffer)
|
|
c.c.DeleteRenderbuffer(c.colorBuffer)
|
|
c.c.DeleteRenderbuffer(c.depthBuffer)
|
|
C.gio_makeCurrent(0)
|
|
C.CFRelease(c.ctx)
|
|
c.ctx = 0
|
|
}
|
|
|
|
func (c *context) Present() error {
|
|
if c.layer == 0 {
|
|
panic("context is not active")
|
|
}
|
|
// Discard depth buffer as recommended in
|
|
// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html
|
|
c.c.BindFramebuffer(gl.FRAMEBUFFER, c.frameBuffer)
|
|
c.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT)
|
|
c.c.BindRenderbuffer(gl.RENDERBUFFER, c.colorBuffer)
|
|
if C.gio_presentRenderbuffer(c.ctx, C.GLenum(gl.RENDERBUFFER)) == 0 {
|
|
return errors.New("presentRenderBuffer failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *context) Lock() error {
|
|
if C.gio_makeCurrent(c.ctx) == 0 {
|
|
return errors.New("[EAGLContext setCurrentContext] failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *context) Unlock() {
|
|
C.gio_makeCurrent(0)
|
|
}
|
|
|
|
func (c *context) Refresh() error {
|
|
if C.gio_makeCurrent(c.ctx) == 0 {
|
|
return errors.New("[EAGLContext setCurrentContext] failed")
|
|
}
|
|
if !c.init {
|
|
c.init = true
|
|
c.frameBuffer = c.c.CreateFramebuffer()
|
|
c.colorBuffer = c.c.CreateRenderbuffer()
|
|
c.depthBuffer = c.c.CreateRenderbuffer()
|
|
}
|
|
if !c.owner.isVisible() {
|
|
// Make sure any in-flight GL commands are complete.
|
|
c.c.Finish()
|
|
return nil
|
|
}
|
|
currentRB := gl.Renderbuffer{uint(c.c.GetInteger(gl.RENDERBUFFER_BINDING))}
|
|
c.c.BindRenderbuffer(gl.RENDERBUFFER, c.colorBuffer)
|
|
if C.gio_renderbufferStorage(c.ctx, c.layer, C.GLenum(gl.RENDERBUFFER)) == 0 {
|
|
return errors.New("renderbufferStorage failed")
|
|
}
|
|
w := c.c.GetRenderbufferParameteri(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)
|
|
h := c.c.GetRenderbufferParameteri(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT)
|
|
c.c.BindRenderbuffer(gl.RENDERBUFFER, c.depthBuffer)
|
|
c.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h)
|
|
c.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB)
|
|
c.c.BindFramebuffer(gl.FRAMEBUFFER, c.frameBuffer)
|
|
c.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, c.colorBuffer)
|
|
c.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, c.depthBuffer)
|
|
if st := c.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
|
|
return fmt.Errorf("framebuffer incomplete, status: %#x\n", st)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *window) NewContext() (Context, error) {
|
|
return newContext(w)
|
|
}
|