mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-01-23 02:34:42 +00:00
Clean SDL/OpenGL functions
This commit is contained in:
parent
8754a5edfa
commit
58a19affcb
3 changed files with 135 additions and 203 deletions
|
|
@ -9,24 +9,6 @@ import (
|
|||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/graphics/gl"
|
||||
)
|
||||
|
||||
type (
|
||||
offscreenSetup struct {
|
||||
tex uint32
|
||||
fbo uint32
|
||||
rbo uint32
|
||||
|
||||
width int32
|
||||
height int32
|
||||
|
||||
pixType uint32
|
||||
pixFormat uint32
|
||||
|
||||
hasDepth bool
|
||||
hasStencil bool
|
||||
}
|
||||
PixelFormat int
|
||||
)
|
||||
|
||||
type Context int
|
||||
|
||||
const (
|
||||
|
|
@ -37,11 +19,12 @@ const (
|
|||
CtxOpenGlEs3
|
||||
CtxOpenGlEsVersion
|
||||
CtxVulkan
|
||||
|
||||
CtxUnknown = math.MaxInt32 - 1
|
||||
CtxDummy = math.MaxInt32
|
||||
)
|
||||
|
||||
type PixelFormat int
|
||||
|
||||
const (
|
||||
UnsignedShort5551 PixelFormat = iota
|
||||
UnsignedShort565
|
||||
|
|
@ -49,99 +32,91 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
opt = offscreenSetup{}
|
||||
buf = make([]byte, 1024*1024)
|
||||
fbo, tex, rbo uint32
|
||||
hasDepth bool
|
||||
pixType, pixFormat uint32
|
||||
buf []byte
|
||||
bufPtr unsafe.Pointer
|
||||
)
|
||||
|
||||
func initContext(getProcAddr func(name string) unsafe.Pointer) {
|
||||
if err := gl.InitWithProcAddrFunc(getProcAddr); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gl.PixelStorei(gl.PackAlignment, 1)
|
||||
}
|
||||
|
||||
func initFramebuffer(w int, h int, hasDepth bool, hasStencil bool) error {
|
||||
opt.width = int32(w)
|
||||
opt.height = int32(h)
|
||||
opt.hasDepth = hasDepth
|
||||
opt.hasStencil = hasStencil
|
||||
|
||||
// texture init
|
||||
gl.GenTextures(1, &opt.tex)
|
||||
gl.BindTexture(gl.Texture2d, opt.tex)
|
||||
func initFramebuffer(width, height int, depth, stencil bool) error {
|
||||
w, h := int32(width), int32(height)
|
||||
hasDepth = depth
|
||||
|
||||
gl.GenTextures(1, &tex)
|
||||
gl.BindTexture(gl.Texture2d, tex)
|
||||
gl.TexParameteri(gl.Texture2d, gl.TextureMinFilter, gl.NEAREST)
|
||||
gl.TexParameteri(gl.Texture2d, gl.TextureMagFilter, gl.NEAREST)
|
||||
|
||||
gl.TexImage2D(gl.Texture2d, 0, gl.RGBA8, opt.width, opt.height, 0, opt.pixType, opt.pixFormat, nil)
|
||||
gl.TexImage2D(gl.Texture2d, 0, gl.RGBA8, w, h, 0, pixType, pixFormat, nil)
|
||||
gl.BindTexture(gl.Texture2d, 0)
|
||||
|
||||
// framebuffer init
|
||||
gl.GenFramebuffers(1, &opt.fbo)
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, opt.fbo)
|
||||
gl.GenFramebuffers(1, &fbo)
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, fbo)
|
||||
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.ColorAttachment0, gl.Texture2d, tex, 0)
|
||||
|
||||
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.ColorAttachment0, gl.Texture2d, opt.tex, 0)
|
||||
|
||||
// more buffers init
|
||||
if opt.hasDepth {
|
||||
gl.GenRenderbuffers(1, &opt.rbo)
|
||||
gl.BindRenderbuffer(gl.RENDERBUFFER, opt.rbo)
|
||||
if opt.hasStencil {
|
||||
gl.RenderbufferStorage(gl.RENDERBUFFER, gl.Depth24Stencil8, opt.width, opt.height)
|
||||
gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DepthStencilAttachment, gl.RENDERBUFFER, opt.rbo)
|
||||
} else {
|
||||
gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DepthComponent24, opt.width, opt.height)
|
||||
gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DepthAttachment, gl.RENDERBUFFER, opt.rbo)
|
||||
if depth {
|
||||
gl.GenRenderbuffers(1, &rbo)
|
||||
gl.BindRenderbuffer(gl.RENDERBUFFER, rbo)
|
||||
format, attachment := uint32(gl.DepthComponent24), uint32(gl.DepthAttachment)
|
||||
if stencil {
|
||||
format, attachment = gl.Depth24Stencil8, gl.DepthStencilAttachment
|
||||
}
|
||||
gl.RenderbufferStorage(gl.RENDERBUFFER, format, w, h)
|
||||
gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, rbo)
|
||||
gl.BindRenderbuffer(gl.RENDERBUFFER, 0)
|
||||
}
|
||||
|
||||
if status := gl.CheckFramebufferStatus(gl.FRAMEBUFFER); status != gl.FramebufferComplete {
|
||||
return fmt.Errorf("invalid framebuffer (0x%X)", status)
|
||||
return fmt.Errorf("framebuffer incomplete: 0x%X", status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func destroyFramebuffer() {
|
||||
if opt.hasDepth {
|
||||
gl.DeleteRenderbuffers(1, &opt.rbo)
|
||||
if hasDepth {
|
||||
gl.DeleteRenderbuffers(1, &rbo)
|
||||
}
|
||||
gl.DeleteFramebuffers(1, &opt.fbo)
|
||||
gl.DeleteTextures(1, &opt.tex)
|
||||
gl.DeleteFramebuffers(1, &fbo)
|
||||
gl.DeleteTextures(1, &tex)
|
||||
}
|
||||
|
||||
func ReadFramebuffer(bytes, w, h uint) []byte {
|
||||
data := buf[:bytes:bytes]
|
||||
gl.PixelStorei(gl.PackAlignment, 1)
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, opt.fbo)
|
||||
gl.ReadPixels(0, 0, int32(w), int32(h), opt.pixType, opt.pixFormat, unsafe.Pointer(&data[0]))
|
||||
return data
|
||||
func ReadFramebuffer(size, w, h uint) []byte {
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, fbo)
|
||||
gl.ReadPixels(0, 0, int32(w), int32(h), pixType, pixFormat, bufPtr)
|
||||
return buf[:size]
|
||||
}
|
||||
|
||||
func getFbo() uint32 { return opt.fbo }
|
||||
|
||||
func SetBuffer(size int) { buf = make([]byte, size) }
|
||||
func SetBuffer(size int) {
|
||||
buf = make([]byte, size)
|
||||
bufPtr = unsafe.Pointer(&buf[0])
|
||||
}
|
||||
|
||||
func SetPixelFormat(format PixelFormat) error {
|
||||
switch format {
|
||||
case UnsignedShort5551:
|
||||
opt.pixFormat = gl.UnsignedShort5551
|
||||
opt.pixType = gl.BGRA
|
||||
pixFormat, pixType = gl.UnsignedShort5551, gl.BGRA
|
||||
case UnsignedShort565:
|
||||
opt.pixFormat = gl.UnsignedShort565
|
||||
opt.pixType = gl.RGB
|
||||
pixFormat, pixType = gl.UnsignedShort565, gl.RGB
|
||||
case UnsignedInt8888Rev:
|
||||
opt.pixFormat = gl.UnsignedInt8888Rev
|
||||
opt.pixType = gl.BGRA
|
||||
pixFormat, pixType = gl.UnsignedInt8888Rev, gl.BGRA
|
||||
default:
|
||||
return errors.New("unknown pixel format")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetGLVersionInfo() string { return get(gl.VERSION) }
|
||||
func GetGLVendorInfo() string { return get(gl.VENDOR) }
|
||||
func GetGLRendererInfo() string { return get(gl.RENDERER) }
|
||||
func GetGLSLInfo() string { return get(gl.ShadingLanguageVersion) }
|
||||
func GetGLError() uint32 { return gl.GetError() }
|
||||
func GLInfo() (version, vendor, renderer, glsl string) {
|
||||
return gl.GoStr(gl.GetString(gl.VERSION)),
|
||||
gl.GoStr(gl.GetString(gl.VENDOR)),
|
||||
gl.GoStr(gl.GetString(gl.RENDERER)),
|
||||
gl.GoStr(gl.GetString(gl.ShadingLanguageVersion))
|
||||
}
|
||||
|
||||
func get(name uint32) string { return gl.GoStr(gl.GetString(name)) }
|
||||
func GlFbo() uint32 { return fbo }
|
||||
|
|
|
|||
|
|
@ -4,21 +4,17 @@ import (
|
|||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/giongto35/cloud-game/v3/pkg/logger"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/thread"
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
|
||||
type SDL struct {
|
||||
glWCtx sdl.GLContext
|
||||
w *sdl.Window
|
||||
log *logger.Logger
|
||||
w *sdl.Window
|
||||
ctx sdl.GLContext
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Ctx Context
|
||||
W int
|
||||
H int
|
||||
W, H int
|
||||
GLAutoContext bool
|
||||
GLVersionMajor uint
|
||||
GLVersionMinor uint
|
||||
|
|
@ -26,123 +22,79 @@ type Config struct {
|
|||
GLHasStencil bool
|
||||
}
|
||||
|
||||
// NewSDLContext initializes SDL/OpenGL context.
|
||||
// Uses main thread lock (see thread/mainthread).
|
||||
func NewSDLContext(cfg Config, log *logger.Logger) (*SDL, error) {
|
||||
log.Debug().Msg("[SDL/OpenGL] initialization...")
|
||||
|
||||
func NewSDLContext(cfg Config) (*SDL, error) {
|
||||
if err := sdl.Init(sdl.INIT_VIDEO); err != nil {
|
||||
return nil, fmt.Errorf("SDL initialization fail: %w", err)
|
||||
return nil, fmt.Errorf("sdl: %w", err)
|
||||
}
|
||||
|
||||
display := SDL{log: log}
|
||||
|
||||
if cfg.GLAutoContext {
|
||||
log.Debug().Msgf("[OpenGL] CONTEXT_AUTO (type: %v v%v.%v)", cfg.Ctx, cfg.GLVersionMajor, cfg.GLVersionMinor)
|
||||
} else {
|
||||
switch cfg.Ctx {
|
||||
case CtxOpenGlCore:
|
||||
display.setAttribute(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_CORE)
|
||||
log.Debug().Msgf("[OpenGL] CONTEXT_PROFILE_CORE")
|
||||
case CtxOpenGlEs2:
|
||||
display.setAttribute(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_ES)
|
||||
display.setAttribute(sdl.GL_CONTEXT_MAJOR_VERSION, 3)
|
||||
display.setAttribute(sdl.GL_CONTEXT_MINOR_VERSION, 0)
|
||||
log.Debug().Msgf("[OpenGL] CONTEXT_PROFILE_ES 3.0")
|
||||
case CtxOpenGl:
|
||||
if cfg.GLVersionMajor >= 3 {
|
||||
display.setAttribute(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_COMPATIBILITY)
|
||||
}
|
||||
log.Debug().Msgf("[OpenGL] CONTEXT_PROFILE_COMPATIBILITY")
|
||||
default:
|
||||
log.Error().Msgf("[OpenGL] Unsupported hw context: %v", cfg.Ctx)
|
||||
if !cfg.GLAutoContext {
|
||||
if err := setGLAttrs(cfg.Ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
// In OSX 10.14+ window creation and context creation must happen in the main thread
|
||||
thread.Main(func() { display.w, display.glWCtx, err = createWindow() })
|
||||
w, err := sdl.CreateWindow("cloud-retro", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, 1, 1, sdl.WINDOW_OPENGL|sdl.WINDOW_HIDDEN)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("window fail: %w", err)
|
||||
return nil, fmt.Errorf("window: %w", err)
|
||||
}
|
||||
|
||||
if err := display.BindContext(); err != nil {
|
||||
return nil, fmt.Errorf("bind context fail: %w", err)
|
||||
ctx, err := w.GLCreateContext()
|
||||
if err != nil {
|
||||
err1 := w.Destroy()
|
||||
return nil, fmt.Errorf("gl context: %w, destroy err: %w", err, err1)
|
||||
}
|
||||
|
||||
if err = w.GLMakeCurrent(ctx); err != nil {
|
||||
return nil, fmt.Errorf("gl bind: %w", err)
|
||||
}
|
||||
|
||||
initContext(sdl.GLGetProcAddress)
|
||||
if err := initFramebuffer(cfg.W, cfg.H, cfg.GLHasDepth, cfg.GLHasStencil); err != nil {
|
||||
return nil, fmt.Errorf("OpenGL initialization fail: %w", err)
|
||||
|
||||
if err = initFramebuffer(cfg.W, cfg.H, cfg.GLHasDepth, cfg.GLHasStencil); err != nil {
|
||||
return nil, fmt.Errorf("fbo: %w", err)
|
||||
}
|
||||
return &display, nil
|
||||
|
||||
return &SDL{w: w, ctx: ctx}, nil
|
||||
}
|
||||
|
||||
// TryInit check weather SDL context can be created on the system.
|
||||
func setGLAttrs(ctx Context) error {
|
||||
set := sdl.GLSetAttribute
|
||||
switch ctx {
|
||||
case CtxOpenGlCore:
|
||||
return set(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_CORE)
|
||||
case CtxOpenGlEs2:
|
||||
for _, a := range [][2]int{
|
||||
{sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_ES},
|
||||
{sdl.GL_CONTEXT_MAJOR_VERSION, 3},
|
||||
{sdl.GL_CONTEXT_MINOR_VERSION, 0},
|
||||
} {
|
||||
if err := set(sdl.GLattr(a[0]), a[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case CtxOpenGl:
|
||||
return set(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_COMPATIBILITY)
|
||||
default:
|
||||
return fmt.Errorf("unsupported gl context: %v", ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SDL) Deinit() error {
|
||||
destroyFramebuffer()
|
||||
sdl.GLDeleteContext(s.ctx)
|
||||
err := s.w.Destroy()
|
||||
sdl.Quit()
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SDL) BindContext() error { return s.w.GLMakeCurrent(s.ctx) }
|
||||
func GlProcAddress(proc string) unsafe.Pointer { return sdl.GLGetProcAddress(proc) }
|
||||
|
||||
func TryInit() error {
|
||||
if err := sdl.Init(sdl.INIT_VIDEO); err != nil {
|
||||
return fmt.Errorf("SDL init fail: %w", err)
|
||||
}
|
||||
sdl.Quit()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deinit destroys SDL/OpenGL context.
|
||||
// Uses main thread lock (see thread/mainthread).
|
||||
func (s *SDL) Deinit() error {
|
||||
s.log.Debug().Msg("[SDL/OpenGL] shutdown...")
|
||||
destroyFramebuffer()
|
||||
var err error
|
||||
// In OSX 10.14+ window deletion must happen in the main thread
|
||||
thread.Main(func() {
|
||||
err = s.destroyWindow()
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("[SDL/OpenGL] deinit fail: %w", err)
|
||||
}
|
||||
sdl.Quit()
|
||||
s.log.Debug().Msgf("[SDL/OpenGL] shutdown codes:(%v, %v)", sdl.GetError(), GetGLError())
|
||||
return nil
|
||||
}
|
||||
|
||||
// createWindow creates a fake SDL window just for OpenGL initialization purposes.
|
||||
func createWindow() (*sdl.Window, sdl.GLContext, error) {
|
||||
w, err := sdl.CreateWindow(
|
||||
"CloudRetro dummy window",
|
||||
sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED,
|
||||
1, 1,
|
||||
sdl.WINDOW_OPENGL|sdl.WINDOW_HIDDEN,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("window creation fail: %w", err)
|
||||
}
|
||||
glWCtx, err := w.GLCreateContext()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("window OpenGL context fail: %w", err)
|
||||
}
|
||||
return w, glWCtx, nil
|
||||
}
|
||||
|
||||
// destroyWindow destroys previously created SDL window.
|
||||
func (s *SDL) destroyWindow() error {
|
||||
if err := s.BindContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
sdl.GLDeleteContext(s.glWCtx)
|
||||
if err := s.w.Destroy(); err != nil {
|
||||
return fmt.Errorf("window destroy fail: %w", err)
|
||||
}
|
||||
sdl.Quit()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BindContext explicitly binds context to current thread.
|
||||
func (s *SDL) BindContext() error { return s.w.GLMakeCurrent(s.glWCtx) }
|
||||
|
||||
// setAttribute tries to set a GL attribute or prints error.
|
||||
func (s *SDL) setAttribute(attr sdl.GLattr, value int) {
|
||||
if err := sdl.GLSetAttribute(attr, value); err != nil {
|
||||
s.log.Error().Err(err).Msg("[SDL] attribute")
|
||||
}
|
||||
}
|
||||
|
||||
func GetGlFbo() uint32 { return getFbo() }
|
||||
|
||||
func GetGlProcAddress(proc string) unsafe.Pointer { return sdl.GLGetProcAddress(proc) }
|
||||
|
|
|
|||
|
|
@ -486,10 +486,11 @@ func setRotation(rot uint) {
|
|||
func printOpenGLDriverInfo() {
|
||||
var openGLInfo strings.Builder
|
||||
openGLInfo.Grow(128)
|
||||
openGLInfo.WriteString(fmt.Sprintf("\n[OpenGL] Version: %v\n", graphics.GetGLVersionInfo()))
|
||||
openGLInfo.WriteString(fmt.Sprintf("[OpenGL] Vendor: %v\n", graphics.GetGLVendorInfo()))
|
||||
openGLInfo.WriteString(fmt.Sprintf("[OpenGL] Renderer: %v\n", graphics.GetGLRendererInfo()))
|
||||
openGLInfo.WriteString(fmt.Sprintf("[OpenGL] GLSL Version: %v", graphics.GetGLSLInfo()))
|
||||
version, vendor, renderrer, glsl := graphics.GLInfo()
|
||||
openGLInfo.WriteString(fmt.Sprintf("\n[OpenGL] Version: %v\n", version))
|
||||
openGLInfo.WriteString(fmt.Sprintf("[OpenGL] Vendor: %v\n", vendor))
|
||||
openGLInfo.WriteString(fmt.Sprintf("[OpenGL] Renderer: %v\n", renderrer))
|
||||
openGLInfo.WriteString(fmt.Sprintf("[OpenGL] GLSL Version: %v", glsl))
|
||||
Nan0.log.Debug().Msg(openGLInfo.String())
|
||||
}
|
||||
|
||||
|
|
@ -711,11 +712,11 @@ func coreLog(level C.enum_retro_log_level, msg *C.char) {
|
|||
}
|
||||
|
||||
//export coreGetCurrentFramebuffer
|
||||
func coreGetCurrentFramebuffer() C.uintptr_t { return (C.uintptr_t)(graphics.GetGlFbo()) }
|
||||
func coreGetCurrentFramebuffer() C.uintptr_t { return (C.uintptr_t)(graphics.GlFbo()) }
|
||||
|
||||
//export coreGetProcAddress
|
||||
func coreGetProcAddress(sym *C.char) C.retro_proc_address_t {
|
||||
return (C.retro_proc_address_t)(graphics.GetGlProcAddress(C.GoString(sym)))
|
||||
return (C.retro_proc_address_t)(graphics.GlProcAddress(C.GoString(sym)))
|
||||
}
|
||||
|
||||
//export coreEnvironment
|
||||
|
|
@ -857,20 +858,22 @@ func initVideo() {
|
|||
context = graphics.CtxUnknown
|
||||
}
|
||||
|
||||
sdl, err := graphics.NewSDLContext(graphics.Config{
|
||||
Ctx: context,
|
||||
W: int(Nan0.sys.av.geometry.max_width),
|
||||
H: int(Nan0.sys.av.geometry.max_height),
|
||||
GLAutoContext: Nan0.Video.gl.autoCtx,
|
||||
GLVersionMajor: uint(Nan0.Video.hw.version_major),
|
||||
GLVersionMinor: uint(Nan0.Video.hw.version_minor),
|
||||
GLHasDepth: bool(Nan0.Video.hw.depth),
|
||||
GLHasStencil: bool(Nan0.Video.hw.stencil),
|
||||
}, Nan0.log)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
Nan0.sdlCtx = sdl
|
||||
thread.Main(func() {
|
||||
var err error
|
||||
Nan0.sdlCtx, err = graphics.NewSDLContext(graphics.Config{
|
||||
Ctx: context,
|
||||
W: int(Nan0.sys.av.geometry.max_width),
|
||||
H: int(Nan0.sys.av.geometry.max_height),
|
||||
GLAutoContext: Nan0.Video.gl.autoCtx,
|
||||
GLVersionMajor: uint(Nan0.Video.hw.version_major),
|
||||
GLVersionMinor: uint(Nan0.Video.hw.version_minor),
|
||||
GLHasDepth: bool(Nan0.Video.hw.depth),
|
||||
GLHasStencil: bool(Nan0.Video.hw.stencil),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
|
||||
if Nan0.log.GetLevel() < logger.InfoLevel {
|
||||
printOpenGLDriverInfo()
|
||||
|
|
@ -882,9 +885,11 @@ func deinitVideo() {
|
|||
if !Nan0.hackSkipHwContextDestroy {
|
||||
C.bridge_context_reset(Nan0.Video.hw.context_destroy)
|
||||
}
|
||||
if err := Nan0.sdlCtx.Deinit(); err != nil {
|
||||
Nan0.log.Error().Err(err).Msg("deinit fail")
|
||||
}
|
||||
thread.Main(func() {
|
||||
if err := Nan0.sdlCtx.Deinit(); err != nil {
|
||||
Nan0.log.Error().Err(err).Msg("deinit fail")
|
||||
}
|
||||
})
|
||||
Nan0.Video.gl.enabled = false
|
||||
Nan0.Video.gl.autoCtx = false
|
||||
Nan0.hackSkipHwContextDestroy = false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue