Compare commits

...
61 Commits
Author SHA1 Message Date
Jean-André Santoni 71dc28f4a5 Remove extensions and specify shader version 2023-03-15 17:28:51 +07:00
Jean-André Santoni 16cd399681 Try gl 4.1 core 2023-03-15 17:19:10 +07:00
Jean-André Santoni 3edf65f9fd Merge remote-tracking branch 'origin/master' into kivutar/gl 2022-08-05 11:53:07 +02:00
Jean-André Santoni e01cc6bb86 Merge remote-tracking branch 'origin/master' into kivutar/gl 2022-02-10 21:25:25 +01:00
Jean-André Santoni 3e084bf7f1 Merge remote-tracking branch 'origin/master' into kivutar/gl 2021-12-14 11:05:13 +01:00
Jean-André Santoni 2bcbb1d24c Merge remote-tracking branch 'origin/master' into kivutar/gl 2021-11-24 13:12:40 +07:00
Jean-André Santoni 7d48bb840b Merge remote-tracking branch 'origin/master' into kivutar/gl 2021-11-14 10:12:38 +07:00
Jean-André Santoni cb8649a129 Forgot to use program before get uniform 2021-10-22 20:47:24 +07:00
Jean-André Santoni d74969f50e Fix 2021-10-22 20:09:45 +07:00
Jean-André Santoni a95767339e Merge remote-tracking branch 'origin/master' into kivutar/gl 2021-10-22 19:47:59 +07:00
Jean-André Santoni 346dbd8a33 Merge remote-tracking branch 'origin/master' into kivutar/gl 2021-09-30 14:24:57 +07:00
Jean-André Santoni c95a3a7a14 Merge branch 'master' into kivutar/gl 2021-05-14 19:59:41 +07:00
Jean-André Santoni 8647e6f893 Merge branch 'master' into kivutar/gl 2021-05-14 11:09:32 +07:00
Jean-André Santoni a43843bab1 Fix 2021-04-17 15:40:33 +07:00
Jean-André Santoni 6097d396df Merge branch 'master' into kivutar/gl 2021-04-17 14:56:26 +07:00
Jean-André Santoni e4c5845b57 Merge branch 'master' into kivutar/gl 2021-01-28 10:57:07 +07:00
Jean-André Santoni da0092ef53 Fixes 2021-01-05 22:38:01 +07:00
Jean-André Santoni d8afc6031a Merge remote-tracking branch 'origin/master' into kivutar/gl 2021-01-05 22:33:41 +07:00
Jean-André Santoni 1e0c4a0966 Uncomment gl.Enable(gl.TEXTURE_2D) 2020-07-27 00:10:20 +07:00
Jean-André Santoni e01a76b08d Merge remote-tracking branch 'origin/master' into kivutar/gl 2020-07-26 23:35:51 +07:00
Jean-André Santoni 73d70cb1f9 Implement libretro.EnvironmentGetPrefferedHWRenderer 2020-03-01 17:37:04 +07:00
Jean-André Santoni 8b8d49eef4 Merge remote-tracking branch 'origin/master' into kivutar/gl 2020-03-01 17:24:24 +07:00
Jean-André Santoni 008a70c851 Fix FBO on OSX (#313)
* Fix FBO on OSX

* Remove useless code

* Use GL v2.1 for fonts drawing

* Fix fonts on OSX with GL 2.1

* Rename

* Same thing for GenVertexArrays

* Make fl0w work on OSX
2020-02-29 15:40:18 +07:00
Jean-André Santoni 1d8f8e8fac Merge remote-tracking branch 'origin/master' into kivutar/gl 2020-02-29 12:25:12 +07:00
Jean-André Santoni 1146cb6f99 Merge remote-tracking branch 'origin/master' into kivutar/gl 2020-02-26 21:17:16 +07:00
Jean-André Santoni dbfbe3ace5 Temporarily fix tests 2020-02-24 19:25:47 +07:00
Jean-André Santoni e35cba220c Fix shader live update 2020-02-24 18:56:29 +07:00
Jean-André Santoni f32af42ba9 Call gl.TexSubImage2D after binding the texture 2020-02-24 18:42:12 +07:00
Higor Eurípedes 827ae5461d Fix sharp-bilinear and zfast-crt filters (#311)
TextureSize uniform was receiving the input size instead of the actual texture
size.
2020-02-24 04:23:24 +07:00
Jean-André Santoni 40396a4b86 Merge remote-tracking branch 'origin/master' into kivutar/gl 2020-02-07 10:09:37 +07:00
Higor Eurípedes ff9a3c8f4b OpenGL core support improvements (#308)
* Fix sample batch callback and OpenGL core support

* Fix texture/framebuffer allocation and clipping

The libretro api requires the framebuffers to be at least as large as
max_width/max_height.

* Fix context destruction

Fixes Flycast
2020-02-07 09:50:07 +07:00
Jean-André Santoni b8144699f6 Unbind vertex array 2019-11-24 21:02:46 +07:00
Jean-André Santoni 42d43d63c3 Active texture fixes 2019-11-24 20:56:12 +07:00
Jean-André Santoni 9c5c3f5962 Small fix 2019-11-24 20:52:40 +07:00
Jean-André Santoni 2dd9150510 Various rendering improvements 2019-11-24 20:35:25 +07:00
Jean-André Santoni d127589d50 Fix an important memory bug 2019-11-22 12:05:28 +07:00
Jean-André Santoni 05adbe4f6a Debug 2019-11-22 11:11:09 +07:00
Jean-André Santoni ab13ef0ebc Fix compat with non GL cores 2019-11-22 10:56:19 +07:00
Jean-André Santoni 8f51dd3b2d Merge remote-tracking branch 'origin/master' into kivutar/gl 2019-11-22 10:45:16 +07:00
Jean-André Santoni 4618fb63bf Merge remote-tracking branch 'origin/master' into kivutar/gl 2019-10-31 11:51:50 +07:00
Jean-André Santoni 2d55e97431 Lint fix 2019-10-27 12:39:57 +07:00
Jean-André Santoni c7030756e3 Set c.get_current_framebuffer and c.get_proc_address properly 2019-10-27 12:31:28 +07:00
Jean-André Santoni 614c5c2ed9 Merge remote-tracking branch 'origin/master' into kivutar/gl 2019-10-27 11:26:52 +07:00
Jean-André Santoni 466c2ef696 Cast C.retro_hw_context_reset_t 2019-10-27 11:21:17 +07:00
Jean-André Santoni 4914b16606 Fix gl.Clear 2019-10-26 17:20:20 +07:00
Jean-André Santoni 341ad35519 Fix framebuffer creation 2019-10-26 17:14:40 +07:00
Jean-André Santoni 933c86b852 Update dependency for GetProcAddress 2019-10-08 10:55:36 +07:00
Jean-André Santoni 681dfbc6ab Merge branch 'master' into kivutar/gl 2019-10-08 09:51:48 +07:00
Jean-André Santoni f241ef4995 List hardware contexts in the libretro binding 2019-03-12 23:58:12 +07:00
Jean-André Santoni 6e4b4b634e Remove useless code 2019-03-12 21:32:28 +07:00
Jean-André Santoni 87175ca350 Call vid.InitFramebuffer and Core.HWRenderCallback.ContextReset during LoadGame 2019-03-12 19:58:20 +07:00
Jean-André Santoni 7eef38cf2e Comment 2019-03-12 19:00:31 +07:00
Jean-André Santoni dc56ebfff9 Merge branch 'master' into kivutar/gl 2019-03-12 18:52:09 +07:00
Jean-André Santoni d3fd407e77 Move InitFramebuffer and call HWRenderCallback.ContextReset 2019-02-22 16:32:59 +07:00
Jean-André Santoni a7cf8d1f99 Implement more of InitFramebuffer 2019-02-22 16:27:24 +07:00
Jean-André Santoni a7da8f7cb9 Fix display of non gl cores 2019-02-22 16:18:06 +07:00
Jean-André Santoni 967b0da04f Merge remote-tracking branch 'origin/master' into kivutar/gl 2019-02-22 15:55:55 +07:00
Jean-André Santoni c6c0c9c819 Try to implement ProcAddress 2018-12-30 13:03:25 +07:00
Jean-André Santoni d41ab5d9d7 TODO 2018-12-30 12:38:54 +07:00
Jean-André Santoni 7e794694f5 Start implementing InitFramebuffer 2018-12-30 12:33:24 +07:00
Jean-André Santoni d861e3d47d Improve the libretro binding to support GL cores 2018-12-30 11:46:35 +07:00
18 changed files with 423 additions and 73 deletions

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@ ARCH ?= x86_64
VERSION ?= dev
BUNDLENAME = $(APP)-$(OS)-$(ARCH)-$(VERSION)
CORES = atari800 bluemsx swanstation fbneo fceumm gambatte gearsystem genesis_plus_gx handy lutro mednafen_ngp mednafen_pce mednafen_pce_fast mednafen_pcfx mednafen_psx mednafen_saturn mednafen_supergrafx mednafen_vb mednafen_wswan mgba melonds np2kai o2em pcsx_rearmed picodrive pokemini prosystem snes9x stella2014 vecx virtualjaguar
CORES = ppsspp dolphin atari800 bluemsx swanstation fbneo fceumm gambatte gearsystem genesis_plus_gx handy lutro mednafen_ngp mednafen_pce mednafen_pce_fast mednafen_pcfx mednafen_psx mednafen_saturn mednafen_supergrafx mednafen_vb mednafen_wswan mgba melonds np2kai o2em pcsx_rearmed picodrive pokemini prosystem snes9x stella2014 vecx virtualjaguar
ifeq ($(ARCH), arm)
CORES := $(filter-out swanstation,$(CORES))
+5
View File
@@ -201,6 +201,11 @@ func LoadGame(gamePath string) error {
log.Println("[Core]: Game loaded: " + gamePath)
savefiles.LoadSRAM()
if state.Core.HWRenderCallback != nil {
vid.InitFramebuffer()
state.Core.HWRenderCallback.ContextReset()
}
return nil
}
+10
View File
@@ -143,6 +143,13 @@ func environment(cmd uint32, data unsafe.Pointer) bool {
state.Core.SetFrameTimeCallback(data)
case libretro.EnvironmentSetAudioCallback:
state.Core.SetAudioCallback(data)
case libretro.EnvironmentSetHWRender:
state.Core.HWRenderCallback = libretro.SetHWRenderCallback(
data,
vid.CurrentFramebuffer,
vid.ProcAddress)
log.Println("[Env]: HWContextType:", state.Core.HWRenderCallback.HWContextType)
return true
case libretro.EnvironmentGetCanDupe:
libretro.SetBool(data, true)
case libretro.EnvironmentSetPixelFormat:
@@ -151,6 +158,9 @@ func environment(cmd uint32, data unsafe.Pointer) bool {
return environmentGetSystemDirectory(data)
case libretro.EnvironmentGetSaveDirectory:
return environmentGetSaveDirectory(data)
case libretro.EnvironmentGetPrefferedHWRender:
log.Println("[Env]: EnvironmentGetPrefferedHWRenderer: 1")
libretro.SetUint(data, uint(libretro.HWContextOpenGL))
case libretro.EnvironmentShutdown:
vid.SetShouldClose(true)
case libretro.EnvironmentGetCoreOptionsVersion:
+1
View File
@@ -9,6 +9,7 @@ require (
github.com/fatih/structs v1.1.0
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec
github.com/go-gl/mathgl v1.0.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/golang/snappy v0.0.4 // indirect
github.com/klauspost/compress v1.13.6 // indirect
+3
View File
@@ -21,6 +21,8 @@ github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8I
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec h1:3FLiRYO6PlQFDpUU7OEFlWgjGD1jnBIVSJ5SYRWk+9c=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/mathgl v1.0.0 h1:t9DznWJlXxxjeeKLIdovCOVJQk/GzDEL7h/h+Ro2B68=
github.com/go-gl/mathgl v1.0.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -83,6 +85,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
+18
View File
@@ -22,6 +22,14 @@ void bridge_retro_frame_time_callback(retro_frame_time_callback_t f, retro_usec_
f(usec);
}
void bridge_retro_hw_context_reset(retro_hw_context_reset_t f) {
f();
}
void bridge_retro_hw_context_destroy(retro_hw_context_reset_t f) {
f();
}
void bridge_retro_audio_callback(retro_audio_callback_t f) {
f();
}
@@ -168,5 +176,15 @@ int64_t coreGetTimeUsec_cgo() {
return coreGetTimeUsec();
}
uintptr_t coreGetCurrentFramebuffer_cgo() {
uintptr_t coreGetCurrentFramebuffer();
return coreGetCurrentFramebuffer();
}
uintptr_t coreGetProcAddress_cgo(const char *sym) {
uintptr_t coreGetProcAddress(const char *sym);
return coreGetProcAddress(sym);
}
*/
import "C"
+100 -16
View File
@@ -33,6 +33,8 @@ void bridge_retro_unload_game(void *f);
void bridge_retro_run(void *f);
void bridge_retro_reset(void *f);
void bridge_retro_frame_time_callback(retro_frame_time_callback_t f, retro_usec_t usec);
void bridge_retro_hw_context_reset(retro_hw_context_reset_t f);
void bridge_retro_hw_context_destroy(retro_hw_context_reset_t f);
void bridge_retro_audio_callback(retro_audio_callback_t f);
void bridge_retro_audio_set_state(retro_audio_set_state_callback_t f, bool state);
size_t bridge_retro_get_memory_size(void *f, unsigned id);
@@ -51,6 +53,8 @@ size_t coreAudioSampleBatch_cgo(const int16_t *data, size_t frames);
int16_t coreInputState_cgo(unsigned port, unsigned device, unsigned index, unsigned id);
void coreLog_cgo(enum retro_log_level level, const char *msg);
int64_t coreGetTimeUsec_cgo();
uintptr_t coreGetCurrentFramebuffer_cgo();
uintptr_t coreGetProcAddress_cgo(const char *sym);
*/
import "C"
import (
@@ -216,6 +220,20 @@ type FrameTimeCallback struct {
Reference int64
}
// HWRenderCallback sets an interface to let a libretro core render with
// hardware acceleration.
type HWRenderCallback struct {
HWContextType uint32
ContextReset func()
Depth bool
Stencil bool
BottomLeftOrigin bool
VersionMajor, VersionMinor uint
CacheContext bool
ContextDestroy func()
DebugContext bool
}
// AudioCallback stores the audio callback itself and the SetState callback
type AudioCallback struct {
Callback func()
@@ -409,26 +427,48 @@ const (
MemoryVideoRAM = uint32(C.RETRO_MEMORY_VIDEO_RAM)
)
// Hardware contexts
const (
HWContextNone = uint32(C.RETRO_HW_CONTEXT_NONE)
HWContextOpenGL = uint32(C.RETRO_HW_CONTEXT_OPENGL)
HWContextOpenGLES2 = uint32(C.RETRO_HW_CONTEXT_OPENGLES2)
HWContextOpenGLCore = uint32(C.RETRO_HW_CONTEXT_OPENGL_CORE)
HWContextOpenGLES3 = uint32(C.RETRO_HW_CONTEXT_OPENGLES3)
HWContextOpenGLESVersion = uint32(C.RETRO_HW_CONTEXT_OPENGLES_VERSION)
HWContextVulkan = uint32(C.RETRO_HW_CONTEXT_VULKAN)
HWContextDummy = uint32(C.RETRO_HW_CONTEXT_DUMMY)
)
// Pass this to retro_video_refresh_t if rendering to hardware.
// Passing NULL to retro_video_refresh_t is still a frame dupe as normal.
var (
HWFrameBufferValid = unsafe.Pointer(C.RETRO_HW_FRAME_BUFFER_VALID)
)
type (
environmentFunc func(uint32, unsafe.Pointer) bool
videoRefreshFunc func(unsafe.Pointer, int32, int32, int32)
audioSampleFunc func(int16, int16)
audioSampleBatchFunc func([]byte, int32) int32
inputPollFunc func()
inputStateFunc func(uint, uint32, uint, uint) int16
logFunc func(uint32, string)
getTimeUsecFunc func() int64
environmentFunc func(uint32, unsafe.Pointer) bool
videoRefreshFunc func(unsafe.Pointer, int32, int32, int32)
audioSampleFunc func(int16, int16)
audioSampleBatchFunc func([]byte, int32) int32
inputPollFunc func()
inputStateFunc func(uint, uint32, uint, uint) int16
logFunc func(uint32, string)
getTimeUsecFunc func() int64
getCurrentFramebufferFunc func() uintptr
getProcAddressFunc func(string) uintptr
)
var (
environment environmentFunc
videoRefresh videoRefreshFunc
audioSample audioSampleFunc
audioSampleBatch audioSampleBatchFunc
inputPoll inputPollFunc
inputState inputStateFunc
log logFunc
getTimeUsec getTimeUsecFunc
environment environmentFunc
videoRefresh videoRefreshFunc
audioSample audioSampleFunc
audioSampleBatch audioSampleBatchFunc
inputPoll inputPollFunc
inputState inputStateFunc
log logFunc
getTimeUsec getTimeUsecFunc
getCurrentFramebuffer getCurrentFramebufferFunc
getProcAddress getProcAddressFunc
)
// Load dynamically loads a libretro core at the given path and returns a Core instance
@@ -490,6 +530,8 @@ func (core *Core) Deinit() {
inputState = nil
log = nil
getTimeUsec = nil
getCurrentFramebuffer = nil
getProcAddress = nil
}
// Run runs the game for one video frame.
@@ -721,6 +763,16 @@ func coreGetTimeUsec() C.uint64_t {
return C.uint64_t(getTimeUsec())
}
//export coreGetCurrentFramebuffer
func coreGetCurrentFramebuffer() C.uintptr_t {
return C.uintptr_t(getCurrentFramebuffer())
}
//export coreGetProcAddress
func coreGetProcAddress(sym *C.char) C.uintptr_t {
return C.uintptr_t(getProcAddress(C.GoString(sym)))
}
// SetData is a setter for the data of a GameInfo type
func (gi *GameInfo) SetData(bytes []byte) {
cstr := C.CString(string(bytes))
@@ -883,6 +935,38 @@ func (core *Core) SetFrameTimeCallback(data unsafe.Pointer) {
core.FrameTimeCallback = ftc
}
// SetHWRenderCallback is an environment callback helper to set the HWRenderCallback
func SetHWRenderCallback(
data unsafe.Pointer,
currentFramebuffer getCurrentFramebufferFunc,
procAddress getProcAddressFunc,
) *HWRenderCallback {
c := (*C.struct_retro_hw_render_callback)(data)
hwrc := HWRenderCallback{}
hwrc.HWContextType = uint32(c.context_type)
getCurrentFramebuffer = currentFramebuffer
getProcAddress = procAddress
c.get_current_framebuffer = (C.retro_hw_get_current_framebuffer_t)(C.coreGetCurrentFramebuffer_cgo)
c.get_proc_address = (C.retro_hw_get_proc_address_t)(C.coreGetProcAddress_cgo)
hwrc.ContextReset = func() {
C.bridge_retro_hw_context_reset((C.retro_hw_context_reset_t)(c.context_reset))
}
hwrc.Depth = bool(c.depth)
hwrc.Stencil = bool(c.stencil)
hwrc.BottomLeftOrigin = bool(c.bottom_left_origin)
hwrc.VersionMajor = uint(c.version_major)
hwrc.VersionMinor = uint(c.version_minor)
hwrc.CacheContext = bool(c.cache_context)
hwrc.ContextDestroy = func() {
C.bridge_retro_hw_context_destroy((C.retro_hw_context_reset_t)(c.context_destroy))
}
hwrc.DebugContext = bool(c.debug_context)
return &hwrc
}
// SetAudioCallback is an environment callback helper to set the AudioCallback
func (core *Core) SetAudioCallback(data unsafe.Pointer) {
c := *(*C.struct_retro_audio_callback)(data)
+1
View File
@@ -31,6 +31,7 @@ type Core struct {
AudioCallback *AudioCallback
FrameTimeCallback *FrameTimeCallback
DiskControlCallback *DiskControlCallback
HWRenderCallback *HWRenderCallback
MemoryMap []MemoryDescriptor
}
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"path/filepath"
"strings"
"github.com/go-gl/gl/v2.1/gl"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/libretro/ludo/settings"
"github.com/libretro/ludo/video"
)
+3 -1
View File
@@ -19,8 +19,10 @@ COMPAT_ATTRIBUTE vec2 vertTexCoord;
COMPAT_VARYING vec2 fragTexCoord;
uniform mat4 MVP;
void main() {
fragTexCoord = vertTexCoord;
gl_Position = vec4(vert, 0.0, 1.0);
gl_Position = vec4(vert, 0.0, 1.0) * MVP;
}
` + "\x00"
+20 -24
View File
@@ -8,7 +8,7 @@ import (
"io/ioutil"
"os"
"github.com/go-gl/gl/v2.1/gl"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
@@ -174,22 +174,14 @@ func LoadTrueTypeFont(program uint32, r io.Reader, scale int32, low, high rune,
f.fontChar = append(f.fontChar, char)
}
// Generate texture
gl.GenTextures(1, &f.textureID)
gl.BindTexture(gl.TEXTURE_2D, f.textureID)
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(rgba.Rect.Dx()), int32(rgba.Rect.Dy()), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(rgba.Pix))
gl.GenerateMipmap(gl.TEXTURE_2D)
gl.BindTexture(gl.TEXTURE_2D, 0)
textureUniform := gl.GetUniformLocation(f.program, gl.Str("Texture\x00"))
gl.Uniform1i(textureUniform, 0)
// Configure VAO/VBO for texture quads
genVertexArrays(1, &f.vao)
gl.GenBuffers(1, &f.vbo)
bindVertexArray(f.vao)
gl.GenBuffers(1, &f.vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, f.vbo)
vertAttrib := uint32(gl.GetAttribLocation(f.program, gl.Str("vert\x00")))
@@ -200,6 +192,16 @@ func LoadTrueTypeFont(program uint32, r io.Reader, scale int32, low, high rune,
gl.EnableVertexAttribArray(texCoordAttrib)
gl.VertexAttribPointerWithOffset(texCoordAttrib, 2, gl.FLOAT, false, 4*4, 2*4)
// Generate texture
gl.ActiveTexture(gl.TEXTURE0)
gl.GenTextures(1, &f.textureID)
gl.BindTexture(gl.TEXTURE_2D, f.textureID)
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(rgba.Rect.Dx()), int32(rgba.Rect.Dy()), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(rgba.Pix))
gl.GenerateMipmap(gl.TEXTURE_2D)
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
bindVertexArray(0)
@@ -224,8 +226,7 @@ func LoadFont(file string, scale int32, windowWidth int, windowHeight int) (*Fon
gl.UseProgram(program)
// Set screen resolution
resUniform := gl.GetUniformLocation(program, gl.Str("resolution\x00"))
gl.Uniform2f(resUniform, float32(windowWidth), float32(windowHeight))
gl.Uniform2f(gl.GetUniformLocation(program, gl.Str("resolution\x00")), float32(windowWidth), float32(windowHeight))
return LoadTrueTypeFont(program, fd, scale, 32, 256, LeftToRight)
}
@@ -253,15 +254,6 @@ func (f *Font) Printf(x, y float32, scale float32, fs string, argv ...interface{
lowChar := rune(32)
// Setup blending mode
gl.Enable(gl.BLEND)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
// Activate corresponding render state
gl.UseProgram(f.program)
// Set text color
gl.Uniform4f(gl.GetUniformLocation(f.program, gl.Str("textColor\x00")), f.color.R, f.color.G, f.color.B, f.color.A)
var coords []point
// Iterate through all characters in string
@@ -302,6 +294,10 @@ func (f *Font) Printf(x, y float32, scale float32, fs string, argv ...interface{
x += float32((ch.advance >> 6)) * scale // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
}
gl.UseProgram(f.program)
gl.Uniform4f(gl.GetUniformLocation(f.program, gl.Str("color\x00")), f.color.R, f.color.G, f.color.B, f.color.A)
gl.Enable(gl.BLEND)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
bindVertexArray(f.vao)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, f.textureID)
+6 -5
View File
@@ -6,7 +6,7 @@ var fontFragmentShader = `
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#define COMPAT_FRAGCOLOR FragColor
out vec4 FragColor;
out vec4 COMPAT_FRAGCOLOR;
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
@@ -14,12 +14,13 @@ out vec4 FragColor;
#define COMPAT_FRAGCOLOR gl_FragColor
#endif
uniform sampler2D tex;
uniform vec4 textColor;
uniform sampler2D Texture;
uniform vec4 color;
COMPAT_VARYING vec2 fragTexCoord;
void main() {
vec4 sampled = vec4(1.0, 1.0, 1.0, COMPAT_TEXTURE(tex, fragTexCoord).r);
COMPAT_FRAGCOLOR = min(textColor, vec4(1.0, 1.0, 1.0, 1.0)) * sampled;
vec4 sampled = vec4(1.0, 1.0, 1.0, COMPAT_TEXTURE(Texture, fragTexCoord).r);
COMPAT_FRAGCOLOR = min(color, vec4(1.0, 1.0, 1.0, 1.0)) * sampled;
}
` + "\x00"
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"image/draw"
"os"
"github.com/go-gl/gl/v2.1/gl"
"github.com/go-gl/gl/v4.1-core/gl"
)
// Color is an RGBA type that we use in the menu
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"path/filepath"
"github.com/disintegration/imaging"
"github.com/go-gl/gl/v2.1/gl"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/libretro/ludo/settings"
"github.com/libretro/ludo/state"
+15 -1
View File
@@ -4,7 +4,8 @@ import (
"fmt"
"strings"
"github.com/go-gl/gl/v2.1/gl"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/go-gl/mathgl/mgl32"
)
func newProgram(vertexShaderSource, fragmentShaderSource string) (uint32, error) {
@@ -39,12 +40,25 @@ func newProgram(vertexShaderSource, fragmentShaderSource string) (uint32, error)
gl.DeleteShader(vertexShader)
gl.DeleteShader(fragmentShader)
// Only the core rendering code uses MVPs so we set it to identity
// to avoid issues with other parts of the program
gl.UseProgram(program)
mvp := gl.GetUniformLocation(program, gl.Str("MVP\x00"))
if mvp != -1 {
IdentityMatrix := mgl32.Ident4()
gl.UniformMatrix4fv(mvp, 1, false, &IdentityMatrix[0])
}
gl.UseProgram(0)
return program, nil
}
func compileShader(source string, shaderType uint32) (uint32, error) {
shader := gl.CreateShader(shaderType)
source = "#version 410\n" + source
csources, free := gl.Strs(source)
gl.ShaderSource(shader, 1, csources, nil)
free()
+103 -18
View File
@@ -4,12 +4,14 @@
package video
import (
"fmt"
"log"
"path/filepath"
"unsafe"
"github.com/go-gl/gl/v2.1/gl"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/go-gl/glfw/v3.3/glfw"
"github.com/go-gl/mathgl/mgl32"
"github.com/libretro/ludo/libretro"
"github.com/libretro/ludo/settings"
"github.com/libretro/ludo/state"
@@ -33,6 +35,10 @@ type Video struct {
vao uint32
vbo uint32
texID uint32
fboID uint32
rboID uint32
identityMat mgl32.Mat4 // just a cache
orthoMat mgl32.Mat4
pitch int32 // pitch set by the refresh callback
pixFmt uint32 // format set by the environment callback
@@ -45,6 +51,7 @@ type Video struct {
// Init instanciates the video package
func Init(fullscreen bool) *Video {
vid := &Video{}
vid.identityMat = mgl32.Ident4()
vid.Configure(fullscreen)
return vid
}
@@ -52,6 +59,14 @@ func Init(fullscreen bool) *Video {
// Reconfigure destroys and recreates the window with new attributes
func (video *Video) Reconfigure(fullscreen bool) {
if video.Window != nil {
// This is the expected frontend behavior and Flycast requires this
// for fullscreen toggling to work, but ppsspp breaks. OTOH, ppsspp
// breaks in those situations even if we don't call context_destroy
// so ignore it.
hw := state.Core.HWRenderCallback
if state.CoreRunning && hw != nil && hw.ContextDestroy != nil {
state.Core.HWRenderCallback.ContextDestroy()
}
video.Window.Destroy()
}
video.Configure(fullscreen)
@@ -97,6 +112,11 @@ func (video *Video) Configure(fullscreen bool) {
height = 240 * 2
}
glfw.WindowHint(glfw.ContextVersionMajor, 4)
glfw.WindowHint(glfw.ContextVersionMinor, 1)
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
var err error
video.Window, err = glfw.CreateWindow(width, height, "Ludo", m, nil)
if err != nil {
@@ -115,6 +135,12 @@ func (video *Video) Configure(fullscreen bool) {
panic(err)
}
version := gl.GoStr(gl.GetString(gl.VERSION))
fmt.Println("OpenGL version", version)
shversion := gl.GoStr(gl.GetString(gl.SHADING_LANGUAGE_VERSION))
fmt.Println("OpenGL shading version", shversion)
fbw, fbh := video.Window.GetFramebufferSize()
// LoadFont (fontfile, font scale, window width, window height)
@@ -167,6 +193,7 @@ func (video *Video) Configure(fullscreen bool) {
video.UpdateFilter(settings.Current.VideoFilter)
gl.UseProgram(video.program)
textureUniform := gl.GetUniformLocation(video.program, gl.Str("Texture\x00"))
gl.Uniform1i(textureUniform, 0)
@@ -193,20 +220,27 @@ func (video *Video) Configure(fullscreen bool) {
video.bpp = 2
}
gl.GenTextures(1, &video.texID)
gl.ActiveTexture(gl.TEXTURE0)
if video.texID == 0 && state.Verbose {
log.Println("[Video]: Failed to create the vid texture")
if video.Geom.MaxWidth == 0 || video.Geom.MaxHeight == 0 {
video.Geom.MaxWidth = video.Geom.BaseWidth
video.Geom.MaxHeight = video.Geom.BaseHeight
}
gl.GenTextures(1, &video.texID)
if video.texID == 0 && state.Verbose {
log.Fatalln("[Video]: Failed to create the vid texture")
}
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, video.texID)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, int32(video.Geom.MaxWidth), int32(video.Geom.MaxHeight), 0, video.pixType, video.pixFmt, nil)
video.UpdateFilter(settings.Current.VideoFilter)
video.coreRatioViewport(fbw, fbh)
video.coreRatioViewport(fbw, fbh, video.Geom.BaseWidth, video.Geom.BaseHeight)
if e := gl.GetError(); e != gl.NO_ERROR {
bindVertexArray(0)
for e := gl.GetError(); e != gl.NO_ERROR; e = gl.NO_ERROR {
log.Printf("[Video] OpenGL error: %d\n", e)
}
}
@@ -219,6 +253,7 @@ func (video *Video) Configure(fullscreen bool) {
// CRT: zfast-crt
// LCD: zfast-lcd
func (video *Video) UpdateFilter(filter string) {
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, video.texID)
switch filter {
case "Smooth":
@@ -247,8 +282,9 @@ func (video *Video) UpdateFilter(filter string) {
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
gl.UseProgram(video.program)
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("TextureSize\x00")), float32(video.width), float32(video.height))
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("TextureSize\x00")), float32(video.Geom.MaxWidth), float32(video.Geom.MaxHeight))
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("InputSize\x00")), float32(video.width), float32(video.height))
gl.UseProgram(0)
}
// SetPixelFormat is a callback passed to the libretro implementation.
@@ -298,7 +334,7 @@ func (video *Video) ResetRot() {
// coreRatioViewport configures the vertex array to display the game at the center of the window
// while preserving the original ascpect ratio of the game or core
func (video *Video) coreRatioViewport(fbWidth int, fbHeight int) (x, y, w, h float32) {
func (video *Video) coreRatioViewport(fbWidth, fbHeight, clipWidth, clipHeight int) (x, y, w, h float32) {
// Scale the content to fit in the viewport.
fbw := float32(fbWidth)
fbh := float32(fbHeight)
@@ -321,6 +357,12 @@ func (video *Video) coreRatioViewport(fbWidth int, fbHeight int) (x, y, w, h flo
y = (fbh - h) / 2
va := video.vertexArray(x, y, w, h, 1.0)
va[3] = float32(clipHeight) / float32(video.Geom.MaxHeight)
va[10] = float32(clipWidth) / float32(video.Geom.MaxWidth)
va[11] = va[3]
va[14] = va[10]
va = rotateUV(va, video.rot)
gl.BindBuffer(gl.ARRAY_BUFFER, video.vbo)
gl.BufferData(gl.ARRAY_BUFFER, len(va)*4, gl.Ptr(va), gl.STATIC_DRAW)
@@ -336,36 +378,64 @@ func (video *Video) ResizeViewport() {
// Render the current frame
func (video *Video) Render() {
// Render directly to the screen
bindBackbuffer()
// We can't trust the core to leave the OpenGL in the same state as
// before retro_run() was called so we restore some state manually.
gl.Disable(gl.DEPTH_TEST)
gl.Disable(gl.CULL_FACE)
gl.Disable(gl.DITHER)
gl.Disable(gl.STENCIL_TEST)
gl.Disable(gl.BLEND)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
gl.BlendEquation(gl.FUNC_ADD)
gl.Enable(gl.TEXTURE_2D)
video.ResizeViewport()
if !state.CoreRunning {
gl.ClearColor(1, 1, 1, 1)
gl.Clear(gl.COLOR_BUFFER_BIT)
return
}
gl.ClearColor(0, 0, 0, 1)
gl.Clear(gl.COLOR_BUFFER_BIT)
// Early return to not render the first frame of a newly loaded game with the
// previous game pitch. A sane pitch must be set by video.Refresh first.
if video.pitch == 0 {
if state.Core.HWRenderCallback == nil && video.pitch == 0 {
return
}
fbw, fbh := video.Window.GetFramebufferSize()
_, _, w, h := video.coreRatioViewport(fbw, fbh)
_, _, w, h := video.coreRatioViewport(fbw, fbh, int(video.width), int(video.height))
gl.UseProgram(video.program)
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("OutputSize\x00")), w, h)
bindVertexArray(video.vao)
if state.Core.HWRenderCallback != nil {
gl.UniformMatrix4fv(gl.GetUniformLocation(video.program, gl.Str("MVP\x00")), 1, false, &video.orthoMat[0])
}
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, video.texID)
gl.BindBuffer(gl.ARRAY_BUFFER, video.vbo)
bindVertexArray(video.vao)
gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
bindVertexArray(0)
// Reset MVP to identity to avoid menu issues
gl.UniformMatrix4fv(gl.GetUniformLocation(video.program, gl.Str("MVP\x00")), 1, false, &video.identityMat[0])
gl.UseProgram(0)
}
// Refresh the texture framebuffer
func (video *Video) Refresh(data unsafe.Pointer, width int32, height int32, pitch int32) {
bindBackbuffer()
video.width = width
video.height = height
video.pitch = pitch
@@ -374,13 +444,28 @@ func (video *Video) Refresh(data unsafe.Pointer, width int32, height int32, pitc
gl.PixelStorei(gl.UNPACK_ROW_LENGTH, video.pitch/video.bpp)
gl.UseProgram(video.program)
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("TextureSize\x00")), float32(width), float32(height))
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, video.texID)
if data != nil && data != libretro.HWFrameBufferValid {
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, int32(video.Geom.MaxWidth), int32(video.Geom.MaxHeight), 0, video.pixType, video.pixFmt, data)
}
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("TextureSize\x00")), float32(video.Geom.MaxWidth), float32(video.Geom.MaxHeight))
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("InputSize\x00")), float32(width), float32(height))
if data == nil {
return
}
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, video.pixType, video.pixFmt, data)
gl.UseProgram(0)
}
// CurrentFramebuffer returns the current FBO ID
func (video *Video) CurrentFramebuffer() uintptr {
return uintptr(video.fboID)
}
// ProcAddress returns the address of the proc from GLFW
func (video *Video) ProcAddress(procName string) uintptr {
return uintptr(glfw.GetProcAddress(procName))
}
// SetRotation rotates the game image as requested by the core
+68 -3
View File
@@ -1,15 +1,80 @@
//go:build darwin
// +build darwin
package video
import (
"github.com/go-gl/gl/v2.1/gl"
"log"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/go-gl/mathgl/mgl32"
"github.com/libretro/ludo/state"
)
// InitFramebuffer initializes and configures the video frame buffer based on
// informations from the HWRenderCallback of the libretro core.
func (video *Video) InitFramebuffer() {
width := int32(video.Geom.MaxWidth)
height := int32(video.Geom.MaxHeight)
log.Printf("[Video]: Initializing HW render (%v x %v).\n", width, height)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, video.texID)
gl.TexStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height)
gl.GenFramebuffers(1, &video.fboID)
gl.BindFramebuffer(gl.FRAMEBUFFER, video.fboID)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, video.texID, 0)
hw := state.Core.HWRenderCallback
if hw.Depth {
gl.GenRenderbuffers(1, &video.rboID)
gl.BindRenderbuffer(gl.RENDERBUFFER, video.rboID)
format := gl.DEPTH_COMPONENT16
if hw.Stencil {
format = gl.DEPTH24_STENCIL8
}
gl.RenderbufferStorage(gl.RENDERBUFFER, uint32(format), width, height)
gl.BindRenderbuffer(gl.RENDERBUFFER, 0)
if hw.Stencil {
gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, video.rboID)
} else {
gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, video.rboID)
}
}
// Default origin is top left
video.orthoMat = mgl32.Ortho2D(-1, 1, -1, 1)
if hw.BottomLeftOrigin {
video.orthoMat = mgl32.Ortho2D(-1, 1, 1, -1)
}
if st := gl.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
log.Fatalf("[Video] Framebuffer is not complete. Error: %v\n", st)
}
bindBackbuffer()
gl.ClearColor(0, 0, 0, 1)
if hw.Depth && hw.Stencil {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
} else if hw.Depth {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
} else {
gl.Clear(gl.COLOR_BUFFER_BIT)
}
}
func bindBackbuffer() {
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
}
func genVertexArrays(n int32, arrays *uint32) {
gl.GenVertexArraysAPPLE(n, arrays)
gl.GenVertexArrays(n, arrays)
}
func bindVertexArray(array uint32) {
gl.BindVertexArrayAPPLE(array)
gl.BindVertexArray(array)
}
+66 -1
View File
@@ -1,11 +1,76 @@
//go:build !darwin
// +build !darwin
package video
import (
"github.com/go-gl/gl/v2.1/gl"
"log"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/go-gl/mathgl/mgl32"
"github.com/libretro/ludo/state"
)
// InitFramebuffer initializes and configures the video frame buffer based on
// informations from the HWRenderCallback of the libretro core.
func (video *Video) InitFramebuffer() {
width := int32(video.Geom.MaxWidth)
height := int32(video.Geom.MaxHeight)
log.Printf("[Video]: Initializing HW render (%v x %v).\n", width, height)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, video.texID)
gl.TexStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height)
gl.GenFramebuffers(1, &video.fboID)
gl.BindFramebuffer(gl.FRAMEBUFFER, video.fboID)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, video.texID, 0)
hw := state.Core.HWRenderCallback
if hw.Depth {
gl.GenRenderbuffers(1, &video.rboID)
gl.BindRenderbuffer(gl.RENDERBUFFER, video.rboID)
format := gl.DEPTH_COMPONENT16
if hw.Stencil {
format = gl.DEPTH24_STENCIL8
}
gl.RenderbufferStorage(gl.RENDERBUFFER, uint32(format), width, height)
gl.BindRenderbuffer(gl.RENDERBUFFER, 0)
if hw.Stencil {
gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, video.rboID)
} else {
gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, video.rboID)
}
}
// Default origin is top left
video.orthoMat = mgl32.Ortho2D(-1, 1, -1, 1)
if hw.BottomLeftOrigin {
video.orthoMat = mgl32.Ortho2D(-1, 1, 1, -1)
}
if st := gl.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
log.Fatalf("[Video] Framebuffer is not complete. Error: %v\n", st)
}
bindBackbuffer()
gl.ClearColor(0, 0, 0, 1)
if hw.Depth && hw.Stencil {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
} else if hw.Depth {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
} else {
gl.Clear(gl.COLOR_BUFFER_BIT)
}
}
func bindBackbuffer() {
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
}
func genVertexArrays(n int32, arrays *uint32) {
gl.GenVertexArrays(n, arrays)
}