Replace SDL2 with lightweight RGFW-like GL/Window helper lib

This commit is contained in:
sergystepanov 2026-06-29 23:24:09 +03:00
parent 728b2a49f0
commit dfb78c458d
10 changed files with 850 additions and 744 deletions

View file

@ -1,9 +1,12 @@
ARG BUILD_PATH=/tmp/cloud-game
ARG VERSION=master
ARG GO_VERSION=1.26.4
ARG GSTREAMER_VERSION=1.29.2
# gstreamer minimal build
FROM ubuntu:resolute AS gst-builder
ARG GST_VERSION=1.28.4
ARG GSTREAMER_VERSION
ENV DEBIAN_FRONTEND=noninteractive
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
@ -16,7 +19,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
WORKDIR /gst
RUN git clone --single-branch --depth 1 --branch ${GST_VERSION} \
RUN git clone --single-branch --depth 1 --branch ${GSTREAMER_VERSION} \
https://gitlab.freedesktop.org/gstreamer/gstreamer.git .
# fix meson build error
@ -29,6 +32,8 @@ RUN meson setup builddir \
-Doptimization=3 \
-Db_lto=true \
-Dauto_features=disabled \
-Dextra-checks=disabled \
-Dbenchmarks=disabled \
-Dtools=disabled \
-Dgstreamer:tools=disabled \
-Dbase=enabled \
@ -62,8 +67,8 @@ RUN meson setup builddir \
# base build stage
FROM ubuntu:resolute AS build0
ENV DEBIAN_FRONTEND=noninteractive
ARG GO=1.26.4
ARG GO_DIST=go${GO}.linux-amd64.tar.gz
ARG GO_VERSION
ARG GO_DIST=go${GO_VERSION}.linux-amd64.tar.gz
ADD https://go.dev/dl/$GO_DIST ./
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
@ -107,9 +112,10 @@ COPY --from=gst-builder /usr/local /usr/local
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get -q update && apt-get -q install --no-install-recommends -y \
build-essential \
libsdl2-dev \
pkg-config \
build-essential \
libx11-dev \
libgl1-mesa-dev \
libglib2.0-dev && \
rm -rf /var/lib/apt/lists/*

7
go.mod
View file

@ -12,14 +12,13 @@ require (
github.com/klauspost/cpuid/v2 v2.3.0
github.com/knadh/koanf/maps v0.1.2
github.com/knadh/koanf/v2 v2.3.5
github.com/minio/minio-go/v7 v7.2.0
github.com/minio/minio-go/v7 v7.2.1
github.com/pion/ice/v4 v4.2.7
github.com/pion/interceptor v0.1.45
github.com/pion/logging v0.2.4
github.com/pion/webrtc/v4 v4.2.15
github.com/pion/webrtc/v4 v4.2.16
github.com/rs/xid v1.6.0
github.com/rs/zerolog v1.35.1
github.com/veandco/go-sdl2 v0.4.40
golang.org/x/crypto v0.53.0
golang.org/x/image v0.38.0
gopkg.in/yaml.v3 v3.0.1
@ -47,7 +46,7 @@ require (
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.16 // indirect
github.com/pion/rtp v1.10.2 // indirect
github.com/pion/sctp v1.10.1 // indirect
github.com/pion/sctp v1.10.3 // indirect
github.com/pion/sdp/v3 v3.0.19 // indirect
github.com/pion/srtp/v3 v3.0.12 // indirect
github.com/pion/stun/v3 v3.1.6 // indirect

14
go.sum
View file

@ -48,8 +48,8 @@ github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.2.0 h1:RCJM0R1XOsRs+A3x3UCaf3ZYbByDaLjFeAi+YCQEPhs=
github.com/minio/minio-go/v7 v7.2.0/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0=
github.com/minio/minio-go/v7 v7.2.1 h1:PfBfwvKB/MmqyN8Vb1G9voWisaM9OrLv+WwOvMwS9Dw=
github.com/minio/minio-go/v7 v7.2.1/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
@ -74,8 +74,8 @@ github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo=
github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo=
github.com/pion/rtp v1.10.2 h1:l+f6tTDcAH6xwepaAoW791ddhuYsJlqRATOzirO04Mo=
github.com/pion/rtp v1.10.2/go.mod h1:Au8fc6cEByy8RLTwKTQTEeQqDB/SJDxwL4mZuxYA5Pk=
github.com/pion/sctp v1.10.1 h1:IDwqRiteu1gqPnqYysDLxVTwIOMFR0zvwOS6cjhzX7k=
github.com/pion/sctp v1.10.1/go.mod h1:N20Dq6LY+JvJDAh9VVh1JELngb2rQ8dPgds5yBWiPgw=
github.com/pion/sctp v1.10.3 h1:1gBtLMA9lmwNuJkZSZJCdD5/Hz4yJs+7dAqi6ZY97QI=
github.com/pion/sctp v1.10.3/go.mod h1:7KFmTwLcoYgJs/Z+99nJvsWL0qDpuyloSI0RbAqlrz0=
github.com/pion/sdp/v3 v3.0.19 h1:1VMKs3gIkTQV5M3hNKfTAPrDXSNrYtOlmOD8+mSZUGQ=
github.com/pion/sdp/v3 v3.0.19/go.mod h1:dE5WOSlzXrtiE/iuZqe9n+AcEbOjtAd3k5m5NtlV/qU=
github.com/pion/srtp/v3 v3.0.12 h1:U7V17bckl7sI4mb3sepiojByDuBY0wNCqQE+6IlQBbc=
@ -88,8 +88,8 @@ github.com/pion/transport/v4 v4.0.2 h1:ifYlPqNwsy6aKQ9y8yzxXlHae5431ZrH2avkD/Rn6
github.com/pion/transport/v4 v4.0.2/go.mod h1:06hFI+jCFcok2X2MekVufNZ/uzNZXivGBPfviSVcjgM=
github.com/pion/turn/v5 v5.0.10 h1:mOMZjudflXpte5OsCnXztpUKwNXcpXIAzMBnq9TXOSQ=
github.com/pion/turn/v5 v5.0.10/go.mod h1:u3XjBqy2Z4+NhCUpDoOSsNuQDrPLvKStlCGWk6sTQ1E=
github.com/pion/webrtc/v4 v4.2.15 h1:Ir/MauNFCfg+kgyBYPQLiGdVWFlzEcLxqtuzAkYkky0=
github.com/pion/webrtc/v4 v4.2.15/go.mod h1:CPTcyLfIzC4scOkQ4UY4pj6WvbUGhcNLIpK28cP5h6M=
github.com/pion/webrtc/v4 v4.2.16 h1:oK1GAg0TWJtZWYB8J/BgTgGWPoV2148gQWocH12vr3Q=
github.com/pion/webrtc/v4 v4.2.16/go.mod h1:y4HjLAkX90LH+C/qPqGOUgz8RA8CbDj3Iar3d+2hdKQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
@ -113,8 +113,6 @@ github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G
github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ=
github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY=
github.com/veandco/go-sdl2 v0.4.40 h1:fZv6wC3zz1Xt167P09gazawnpa0KY5LM7JAvKpX9d/U=
github.com/veandco/go-sdl2 v0.4.40/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=

View file

@ -522,7 +522,7 @@ func (f *Frontend) Load() error {
}
func (f *Frontend) IsSupported() error {
return graphics.TryInit()
return graphics.RGFWTryInit()
}
func (f *Frontend) autosave(periodSec int) {

View file

@ -1,311 +0,0 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

View file

@ -1,274 +0,0 @@
package gl
// Custom OpenGL bindings
// Based on https://github.com/go-gl/gl/tree/master/v2.1/gl
/*
#cgo egl,windows LDFLAGS: -lEGL
#cgo egl,darwin LDFLAGS: -lEGL
#cgo !gles2,darwin LDFLAGS: -framework OpenGL
#cgo gles2,darwin LDFLAGS: -lGLESv2
#cgo !gles2,windows LDFLAGS: -lopengl32
#cgo gles2,windows LDFLAGS: -lGLESv2
#cgo !egl,linux !egl,freebsd !egl,openbsd pkg-config: gl
#cgo egl,linux egl,freebsd egl,openbsd pkg-config: egl
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
#endif
#ifndef APIENTRY
#define APIENTRY
#endif
#ifndef APIENTRYP
#define APIENTRYP APIENTRY*
#endif
#ifndef GLAPI
#define GLAPI extern
#endif
#include <KHR/khrplatform.h>
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
typedef khronos_int8_t GLbyte;
typedef khronos_uint8_t GLubyte;
typedef khronos_int16_t GLshort;
typedef khronos_uint16_t GLushort;
typedef int GLint;
typedef unsigned int GLuint;
typedef khronos_int32_t GLclampx;
typedef int GLsizei;
typedef khronos_float_t GLfloat;
typedef khronos_float_t GLclampf;
typedef double GLdouble;
typedef double GLclampd;
typedef void *GLeglClientBufferEXT;
typedef void *GLeglImageOES;
typedef char GLchar;
typedef char GLcharARB;
#ifdef __APPLE__
typedef void *GLhandleARB;
#else
typedef unsigned int GLhandleARB;
#endif
typedef const GLubyte *(APIENTRYP GPGETSTRING)(GLenum name);
typedef GLenum (APIENTRYP GPCHECKFRAMEBUFFERSTATUS)(GLenum target);
typedef GLenum (APIENTRYP GPGETERROR)();
typedef void (APIENTRYP GPBINDFRAMEBUFFER)(GLenum target, GLuint framebuffer);
typedef void (APIENTRYP GPBINDRENDERBUFFER)(GLenum target, GLuint renderbuffer);
typedef void (APIENTRYP GPBINDTEXTURE)(GLenum target, GLuint texture);
typedef void (APIENTRYP GPDELETEFRAMEBUFFERS)(GLsizei n, const GLuint *framebuffers);
typedef void (APIENTRYP GPDELETERENDERBUFFERS)(GLsizei n, const GLuint *renderbuffers);
typedef void (APIENTRYP GPDELETETEXTURES)(GLsizei n, const GLuint* textures);
typedef void (APIENTRYP GPFRAMEBUFFERRENDERBUFFER)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
typedef void (APIENTRYP GPFRAMEBUFFERTEXTURE2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
typedef void (APIENTRYP GPGENFRAMEBUFFERS)(GLsizei n, GLuint *framebuffers);
typedef void (APIENTRYP GPGENRENDERBUFFERS)(GLsizei n, GLuint *renderbuffers);
typedef void (APIENTRYP GPGENTEXTURES)(GLsizei n, GLuint *textures);
typedef void (APIENTRYP GPREADPIXELS)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
typedef void (APIENTRYP GPRENDERBUFFERSTORAGE)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
typedef void (APIENTRYP GPTEXIMAGE2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
typedef void (APIENTRYP GPTEXPARAMETERI)(GLenum target, GLenum pname, GLint param);
typedef void (APIENTRYP GPPIXELSTOREI)(GLenum pname, GLint param);
static const GLubyte *getString(GPGETSTRING ptr, GLenum name) { return (*ptr)(name); }
static GLenum getError(GPGETERROR ptr) { return (*ptr)(); }
static void bindTexture(GPBINDTEXTURE ptr, GLenum target, GLuint texture) { (*ptr)(target, texture); }
static void bindFramebuffer(GPBINDFRAMEBUFFER ptr, GLenum target, GLuint framebuffer) { (*ptr)(target, framebuffer); }
static void bindRenderbuffer(GPBINDRENDERBUFFER ptr, GLenum target, GLuint buf) { (*ptr)(target, buf); }
static void texParameteri(GPTEXPARAMETERI ptr, GLenum target, GLenum pname, GLint param) {
(*ptr)(target, pname, param);
}
static void texImage2D(GPTEXIMAGE2D ptr, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {
(*ptr)(target, level, internalformat, width, height, border, format, type, pixels);
}
static void genFramebuffers(GPGENFRAMEBUFFERS ptr, GLsizei n, GLuint *framebuffers) { (*ptr)(n, framebuffers); }
static void genTextures(GPGENTEXTURES ptr, GLsizei n, GLuint *textures) { (*ptr)(n, textures); }
static void framebufferTexture2D(GPFRAMEBUFFERTEXTURE2D ptr, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {
(*ptr)(target, attachment, textarget, texture, level);
}
static void genRenderbuffers(GPGENRENDERBUFFERS ptr, GLsizei n, GLuint *renderbuffers) { (*ptr)(n, renderbuffers); }
static void renderbufferStorage(GPRENDERBUFFERSTORAGE ptr, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
(*ptr)(target, internalformat, width, height);
}
static void framebufferRenderbuffer(GPFRAMEBUFFERRENDERBUFFER ptr, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {
(*ptr)(target, attachment, renderbuffertarget, renderbuffer);
}
static GLenum checkFramebufferStatus(GPCHECKFRAMEBUFFERSTATUS ptr, GLenum target) { return (*ptr)(target); }
static void deleteRenderbuffers(GPDELETERENDERBUFFERS ptr, GLsizei n, const GLuint *renderbuffers) {
(*ptr)(n, renderbuffers);
}
static void deleteFramebuffers(GPDELETEFRAMEBUFFERS ptr, GLsizei n, const GLuint *framebuffers) {
(*ptr)(n, framebuffers);
}
static void deleteTextures(GPDELETETEXTURES ptr, GLsizei n, const GLuint *textures) { (*ptr)(n, textures); }
static void readPixels(GPREADPIXELS ptr, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) {
(*ptr)(x, y, width, height, format, type, pixels);
}
static void pixelStorei(GPPIXELSTOREI ptr, GLenum pname, GLint param) { (*ptr)(pname, param); }
*/
import "C"
import (
"errors"
"unsafe"
)
const (
VENDOR = 0x1F00
VERSION = 0x1F02
RENDERER = 0x1F01
ShadingLanguageVersion = 0x8B8C
Texture2d = 0x0DE1
RENDERBUFFER = 0x8D41
FRAMEBUFFER = 0x8D40
TextureMinFilter = 0x2801
TextureMagFilter = 0x2800
NEAREST = 0x2600
RGBA8 = 0x8058
BGRA = 0x80E1
RGB = 0x1907
ColorAttachment0 = 0x8CE0
Depth24Stencil8 = 0x88F0
DepthStencilAttachment = 0x821A
DepthComponent24 = 0x81A6
DepthAttachment = 0x8D00
FramebufferComplete = 0x8CD5
UnsignedShort5551 = 0x8034
UnsignedShort565 = 0x8363
UnsignedInt8888Rev = 0x8367
PackAlignment = 0x0D05
)
var (
gpGetString C.GPGETSTRING
gpGenTextures C.GPGENTEXTURES
gpGetError C.GPGETERROR
gpBindTexture C.GPBINDTEXTURE
gpBindFramebuffer C.GPBINDFRAMEBUFFER
gpTexParameteri C.GPTEXPARAMETERI
gpTexImage2D C.GPTEXIMAGE2D
gpGenFramebuffers C.GPGENFRAMEBUFFERS
gpFramebufferTexture2D C.GPFRAMEBUFFERTEXTURE2D
gpGenRenderbuffers C.GPGENRENDERBUFFERS
gpBindRenderbuffer C.GPBINDRENDERBUFFER
gpRenderbufferStorage C.GPRENDERBUFFERSTORAGE
gpFramebufferRenderbuffer C.GPFRAMEBUFFERRENDERBUFFER
gpCheckFramebufferStatus C.GPCHECKFRAMEBUFFERSTATUS
gpDeleteRenderbuffers C.GPDELETERENDERBUFFERS
gpDeleteFramebuffers C.GPDELETEFRAMEBUFFERS
gpDeleteTextures C.GPDELETETEXTURES
gpReadPixels C.GPREADPIXELS
gpPixelStorei C.GPPIXELSTOREI
)
func InitWithProcAddrFunc(getProcAddr func(name string) unsafe.Pointer) error {
if gpGetString = (C.GPGETSTRING)(getProcAddr("glGetString")); gpGetString == nil {
return errors.New("glGetString")
}
if gpGenTextures = (C.GPGENTEXTURES)(getProcAddr("glGenTextures")); gpGenTextures == nil {
return errors.New("glGenTextures")
}
if gpGetError = (C.GPGETERROR)(getProcAddr("glGetError")); gpGetError == nil {
return errors.New("glGetError")
}
if gpBindTexture = (C.GPBINDTEXTURE)(getProcAddr("glBindTexture")); gpBindTexture == nil {
return errors.New("glBindTexture")
}
if gpBindFramebuffer = (C.GPBINDFRAMEBUFFER)(getProcAddr("glBindFramebuffer")); gpBindFramebuffer == nil {
return errors.New("glBindFramebuffer")
}
if gpTexParameteri = (C.GPTEXPARAMETERI)(getProcAddr("glTexParameteri")); gpTexParameteri == nil {
return errors.New("glTexParameteri")
}
if gpTexImage2D = (C.GPTEXIMAGE2D)(getProcAddr("glTexImage2D")); gpTexImage2D == nil {
return errors.New("glTexImage2D")
}
gpGenFramebuffers = (C.GPGENFRAMEBUFFERS)(getProcAddr("glGenFramebuffers"))
gpFramebufferTexture2D = (C.GPFRAMEBUFFERTEXTURE2D)(getProcAddr("glFramebufferTexture2D"))
gpGenRenderbuffers = (C.GPGENRENDERBUFFERS)(getProcAddr("glGenRenderbuffers"))
gpBindRenderbuffer = (C.GPBINDRENDERBUFFER)(getProcAddr("glBindRenderbuffer"))
gpRenderbufferStorage = (C.GPRENDERBUFFERSTORAGE)(getProcAddr("glRenderbufferStorage"))
gpFramebufferRenderbuffer = (C.GPFRAMEBUFFERRENDERBUFFER)(getProcAddr("glFramebufferRenderbuffer"))
gpCheckFramebufferStatus = (C.GPCHECKFRAMEBUFFERSTATUS)(getProcAddr("glCheckFramebufferStatus"))
gpDeleteRenderbuffers = (C.GPDELETERENDERBUFFERS)(getProcAddr("glDeleteRenderbuffers"))
gpDeleteFramebuffers = (C.GPDELETEFRAMEBUFFERS)(getProcAddr("glDeleteFramebuffers"))
if gpDeleteTextures = (C.GPDELETETEXTURES)(getProcAddr("glDeleteTextures")); gpDeleteTextures == nil {
return errors.New("glDeleteTextures")
}
gpReadPixels = (C.GPREADPIXELS)(getProcAddr("glReadPixels"))
if gpReadPixels == nil {
return errors.New("glReadPixels")
}
if gpPixelStorei = (C.GPPIXELSTOREI)(getProcAddr("glPixelStorei")); gpPixelStorei == nil {
return errors.New("glPixelStorei")
}
return nil
}
func GetString(name uint32) *uint8 { return (*uint8)(C.getString(gpGetString, (C.GLenum)(name))) }
func GenTextures(n int32, textures *uint32) {
C.genTextures(gpGenTextures, (C.GLsizei)(n), (*C.GLuint)(unsafe.Pointer(textures)))
}
func BindTexture(target uint32, texture uint32) {
C.bindTexture(gpBindTexture, (C.GLenum)(target), (C.GLuint)(texture))
}
func BindFramebuffer(target uint32, framebuffer uint32) {
C.bindFramebuffer(gpBindFramebuffer, (C.GLenum)(target), (C.GLuint)(framebuffer))
}
func TexParameteri(target uint32, name uint32, param int32) {
C.texParameteri(gpTexParameteri, (C.GLenum)(target), (C.GLenum)(name), (C.GLint)(param))
}
func TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, border int32, format uint32, xtype uint32, pixels unsafe.Pointer) {
C.texImage2D(gpTexImage2D, (C.GLenum)(target), (C.GLint)(level), (C.GLint)(internalformat), (C.GLsizei)(width), (C.GLsizei)(height), (C.GLint)(border), (C.GLenum)(format), (C.GLenum)(xtype), pixels)
}
func GenFramebuffers(n int32, framebuffers *uint32) {
C.genFramebuffers(gpGenFramebuffers, (C.GLsizei)(n), (*C.GLuint)(unsafe.Pointer(framebuffers)))
}
func FramebufferTexture2D(target uint32, attachment uint32, texTarget uint32, texture uint32, level int32) {
C.framebufferTexture2D(gpFramebufferTexture2D, (C.GLenum)(target), (C.GLenum)(attachment), (C.GLenum)(texTarget), (C.GLuint)(texture), (C.GLint)(level))
}
func GenRenderbuffers(n int32, renderbuffers *uint32) {
C.genRenderbuffers(gpGenRenderbuffers, (C.GLsizei)(n), (*C.GLuint)(unsafe.Pointer(renderbuffers)))
}
func BindRenderbuffer(target uint32, renderbuffer uint32) {
C.bindRenderbuffer(gpBindRenderbuffer, (C.GLenum)(target), (C.GLuint)(renderbuffer))
}
func RenderbufferStorage(target uint32, internalformat uint32, width int32, height int32) {
C.renderbufferStorage(gpRenderbufferStorage, (C.GLenum)(target), (C.GLenum)(internalformat), (C.GLsizei)(width), (C.GLsizei)(height))
}
func FramebufferRenderbuffer(target uint32, attachment uint32, renderbufferTarget uint32, renderbuffer uint32) {
C.framebufferRenderbuffer(gpFramebufferRenderbuffer, (C.GLenum)(target), (C.GLenum)(attachment), (C.GLenum)(renderbufferTarget), (C.GLuint)(renderbuffer))
}
func CheckFramebufferStatus(target uint32) uint32 {
return (uint32)(C.checkFramebufferStatus(gpCheckFramebufferStatus, (C.GLenum)(target)))
}
func DeleteRenderbuffers(n int32, renderbuffers *uint32) {
C.deleteRenderbuffers(gpDeleteRenderbuffers, (C.GLsizei)(n), (*C.GLuint)(unsafe.Pointer(renderbuffers)))
}
func DeleteFramebuffers(n int32, framebuffers *uint32) {
C.deleteFramebuffers(gpDeleteFramebuffers, (C.GLsizei)(n), (*C.GLuint)(unsafe.Pointer(framebuffers)))
}
func DeleteTextures(n int32, textures *uint32) {
C.deleteTextures(gpDeleteTextures, (C.GLsizei)(n), (*C.GLuint)(unsafe.Pointer(textures)))
}
func ReadPixels(x int32, y int32, width int32, height int32, format uint32, xtype uint32, pixels unsafe.Pointer) {
C.readPixels(gpReadPixels, (C.GLint)(x), (C.GLint)(y), (C.GLsizei)(width), (C.GLsizei)(height), (C.GLenum)(format), (C.GLenum)(xtype), pixels)
}
func PixelStorei(pname uint32, param int32) {
C.pixelStorei(gpPixelStorei, (C.GLenum)(pname), (C.GLint)(param))
}
func GetError() uint32 { return (uint32)(C.getError(gpGetError)) }
func GoStr(str *uint8) string { return C.GoString((*C.char)(unsafe.Pointer(str))) }

View file

@ -1,14 +1,147 @@
package graphics
/*
#cgo windows LDFLAGS: -lopengl32
#cgo linux LDFLAGS: -lGL
#cgo darwin LDFLAGS: -framework OpenGL
#include <stdint.h>
#ifndef APIENTRY
#define APIENTRY
#endif
#ifndef APIENTRYP
#define APIENTRYP APIENTRY*
#endif
typedef unsigned int GLenum;
typedef int GLint;
typedef unsigned int GLuint;
typedef int GLsizei;
typedef unsigned char GLubyte;
typedef const GLubyte *(APIENTRYP GPGETSTRING)(GLenum name);
typedef GLenum (APIENTRYP GPCHECKFRAMEBUFFERSTATUS)(GLenum target);
typedef GLenum (APIENTRYP GPGETERROR)();
typedef void (APIENTRYP GPBINDFRAMEBUFFER)(GLenum target, GLuint framebuffer);
typedef void (APIENTRYP GPBINDRENDERBUFFER)(GLenum target, GLuint renderbuffer);
typedef void (APIENTRYP GPBINDTEXTURE)(GLenum target, GLuint texture);
typedef void (APIENTRYP GPDELETEFRAMEBUFFERS)(GLsizei n, const GLuint *framebuffers);
typedef void (APIENTRYP GPDELETERENDERBUFFERS)(GLsizei n, const GLuint *renderbuffers);
typedef void (APIENTRYP GPDELETETEXTURES)(GLsizei n, const GLuint* textures);
typedef void (APIENTRYP GPFRAMEBUFFERRENDERBUFFER)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
typedef void (APIENTRYP GPFRAMEBUFFERTEXTURE2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
typedef void (APIENTRYP GPGENFRAMEBUFFERS)(GLsizei n, GLuint *fb);
typedef void (APIENTRYP GPGENRENDERBUFFERS)(GLsizei n, GLuint *rb);
typedef void (APIENTRYP GPGENTEXTURES)(GLsizei n, GLuint *tex);
typedef void (APIENTRYP GPREADPIXELS)(GLint x, GLint y, GLsizei w, GLsizei h, GLenum f, GLenum t, void *p);
typedef void (APIENTRYP GPRENDERBUFFERSTORAGE)(GLenum t, GLenum i, GLsizei w, GLsizei h);
typedef void (APIENTRYP GPTEXIMAGE2D)(GLenum t, GLint l, GLint i, GLsizei w, GLsizei h, GLint b, GLenum f, GLenum x, const void *px);
typedef void (APIENTRYP GPTEXPARAMETERI)(GLenum t, GLenum n, GLint p);
typedef void (APIENTRYP GPPIXELSTOREI)(GLenum n, GLint p);
// Generic C wrappers — one per signature pattern, called from Go via function pointers.
static const GLubyte *gl_get_string(GPGETSTRING f, GLenum a) { return f(a); }
static GLenum gl_get_error(GPGETERROR f) { return f(); }
static GLenum gl_check_fb_status(GPCHECKFRAMEBUFFERSTATUS f, GLenum a) { return f(a); }
static void gl_1u32(GPGENTEXTURES f, GLsizei a, GLuint *b) { f(a, b); }
static void gl_bind_tex(GPBINDTEXTURE f, GLenum a, GLuint b) { f(a, b); }
static void gl_bind_fb(GPBINDFRAMEBUFFER f, GLenum a, GLuint b) { f(a, b); }
static void gl_bind_rb(GPBINDRENDERBUFFER f, GLenum a, GLuint b) { f(a, b); }
static void gl_tex_param(GPTEXPARAMETERI f, GLenum a, GLenum b, GLint c) { f(a, b, c); }
static void gl_tex_image(GPTEXIMAGE2D f, GLenum a, GLint b, GLint c, GLsizei d, GLsizei e, GLint g, GLenum h, GLenum i, const void *j) { f(a, b, c, d, e, g, h, i, j); }
static void gl_fb_tex2d(GPFRAMEBUFFERTEXTURE2D f, GLenum a, GLenum b, GLenum c, GLuint d, GLint e) { f(a, b, c, d, e); }
static void gl_fb_rb(GPFRAMEBUFFERRENDERBUFFER f, GLenum a, GLenum b, GLenum c, GLuint d) { f(a, b, c, d); }
static void gl_rb_storage(GPRENDERBUFFERSTORAGE f, GLenum a, GLenum b, GLsizei c, GLsizei d) { f(a, b, c, d); }
static void gl_delete_fb(GPDELETEFRAMEBUFFERS f, GLsizei a, const GLuint *b) { f(a, b); }
static void gl_delete_rb(GPDELETERENDERBUFFERS f, GLsizei a, const GLuint *b) { f(a, b); }
static void gl_delete_tex(GPDELETETEXTURES f, GLsizei a, const GLuint *b) { f(a, b); }
static void gl_read_pixels(GPREADPIXELS f, GLint a, GLint b, GLsizei c, GLsizei d, GLenum e, GLenum g, void *h) { f(a, b, c, d, e, g, h); }
static void gl_pixel_storei(GPPIXELSTOREI f, GLenum a, GLint b) { f(a, b); }
*/
import "C"
import (
"errors"
"fmt"
"math"
"unsafe"
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/graphics/gl"
)
const (
gl_VENDOR = 0x1F00
gl_VERSION = 0x1F02
gl_RENDERER = 0x1F01
gl_SHADING_LANG_VERSION = 0x8B8C
gl_TEXTURE_2D = 0x0DE1
gl_RENDERBUFFER = 0x8D41
gl_FRAMEBUFFER = 0x8D40
gl_TEXTURE_MIN_FILTER = 0x2801
gl_TEXTURE_MAG_FILTER = 0x2800
gl_NEAREST = 0x2600
gl_RGBA = 0x1908
gl_RGB = 0x1907
gl_BGRA = 0x80E1
gl_RGBA8 = 0x8058
gl_COLOR_ATTACHMENT0 = 0x8CE0
gl_DEPTH_ATTACHMENT = 0x8D00
gl_DEPTH_STENCIL_ATTACH = 0x821A
gl_DEPTH_COMPONENT24 = 0x81A6
gl_DEPTH24_STENCIL8 = 0x88F0
gl_FRAMEBUFFER_COMPLETE = 0x8CD5
gl_PACK_ALIGNMENT = 0x0D05
)
// ---- GL function pointers ----
var (
gpGetString,
gpGetError,
gpBindTexture,
gpBindFramebuffer,
gpBindRenderbuffer,
gpTexParameteri,
gpTexImage2D,
gpGenTextures,
gpGenFramebuffers,
gpGenRenderbuffers,
gpFramebufferTexture2D,
gpFramebufferRenderbuffer,
gpRenderbufferStorage,
gpCheckFramebufferStatus,
gpDeleteFramebuffers,
gpDeleteRenderbuffers,
gpDeleteTextures,
gpReadPixels,
gpPixelStorei unsafe.Pointer
)
type glFunc struct {
ptr *unsafe.Pointer
name string
}
var glFuncs = [...]glFunc{
{&gpGetString, "glGetString"},
{&gpGetError, "glGetError"},
{&gpBindTexture, "glBindTexture"},
{&gpBindFramebuffer, "glBindFramebuffer"},
{&gpBindRenderbuffer, "glBindRenderbuffer"},
{&gpTexParameteri, "glTexParameteri"},
{&gpTexImage2D, "glTexImage2D"},
{&gpGenTextures, "glGenTextures"},
{&gpGenFramebuffers, "glGenFramebuffers"},
{&gpGenRenderbuffers, "glGenRenderbuffers"},
{&gpFramebufferTexture2D, "glFramebufferTexture2D"},
{&gpFramebufferRenderbuffer, "glFramebufferRenderbuffer"},
{&gpRenderbufferStorage, "glRenderbufferStorage"},
{&gpCheckFramebufferStatus, "glCheckFramebufferStatus"},
{&gpDeleteFramebuffers, "glDeleteFramebuffers"},
{&gpDeleteRenderbuffers, "glDeleteRenderbuffers"},
{&gpDeleteTextures, "glDeleteTextures"},
{&gpReadPixels, "glReadPixels"},
{&gpPixelStorei, "glPixelStorei"},
}
type Context int
const (
@ -31,6 +164,84 @@ const (
UnsignedInt8888Rev
)
type Config struct {
Ctx Context
W, H int
GLAutoContext bool
GLVersionMajor uint
GLVersionMinor uint
GLHasDepth bool
GLHasStencil bool
}
func initProcAddr(getProcAddr func(name string) unsafe.Pointer) error {
for _, f := range glFuncs {
p := getProcAddr(f.name)
if p == nil {
return errors.New(f.name)
}
*f.ptr = p
}
return nil
}
func goStr(s *uint8) string { return C.GoString((*C.char)(unsafe.Pointer(s))) }
func getString(name uint32) *uint8 {
return (*uint8)(C.gl_get_string((C.GPGETSTRING)(gpGetString), C.GLenum(name)))
}
func genTextures(n int32, textures *uint32) {
C.gl_1u32((C.GPGENTEXTURES)(gpGenTextures), C.GLsizei(n), (*C.GLuint)(unsafe.Pointer(textures)))
}
func bindTexture(target, texture uint32) {
C.gl_bind_tex((C.GPBINDTEXTURE)(gpBindTexture), C.GLenum(target), C.GLuint(texture))
}
func bindFramebuffer(target, framebuffer uint32) {
C.gl_bind_fb((C.GPBINDFRAMEBUFFER)(gpBindFramebuffer), C.GLenum(target), C.GLuint(framebuffer))
}
func bindRenderbuffer(target, renderbuffer uint32) {
C.gl_bind_rb((C.GPBINDRENDERBUFFER)(gpBindRenderbuffer), C.GLenum(target), C.GLuint(renderbuffer))
}
func texParameteri(target, pname uint32, param int32) {
C.gl_tex_param((C.GPTEXPARAMETERI)(gpTexParameteri), C.GLenum(target), C.GLenum(pname), C.GLint(param))
}
func texImage2D(target uint32, level, internalformat, width, height, border int32, format, xtype uint32, pixels unsafe.Pointer) {
C.gl_tex_image((C.GPTEXIMAGE2D)(gpTexImage2D), C.GLenum(target), C.GLint(level), C.GLint(internalformat), C.GLsizei(width), C.GLsizei(height), C.GLint(border), C.GLenum(format), C.GLenum(xtype), pixels)
}
func genFramebuffers(n int32, framebuffers *uint32) {
C.gl_1u32((C.GPGENTEXTURES)(gpGenFramebuffers), C.GLsizei(n), (*C.GLuint)(unsafe.Pointer(framebuffers)))
}
func framebufferTexture2D(target, attachment, texTarget, texture uint32, level int32) {
C.gl_fb_tex2d((C.GPFRAMEBUFFERTEXTURE2D)(gpFramebufferTexture2D), C.GLenum(target), C.GLenum(attachment), C.GLenum(texTarget), C.GLuint(texture), C.GLint(level))
}
func genRenderbuffers(n int32, renderbuffers *uint32) {
C.gl_1u32((C.GPGENTEXTURES)(gpGenRenderbuffers), C.GLsizei(n), (*C.GLuint)(unsafe.Pointer(renderbuffers)))
}
func renderbufferStorage(target, internalformat uint32, width, height int32) {
C.gl_rb_storage((C.GPRENDERBUFFERSTORAGE)(gpRenderbufferStorage), C.GLenum(target), C.GLenum(internalformat), C.GLsizei(width), C.GLsizei(height))
}
func framebufferRenderbuffer(target, attachment, rbtarget, rb uint32) {
C.gl_fb_rb((C.GPFRAMEBUFFERRENDERBUFFER)(gpFramebufferRenderbuffer), C.GLenum(target), C.GLenum(attachment), C.GLenum(rbtarget), C.GLuint(rb))
}
func checkFramebufferStatus(target uint32) uint32 {
return uint32(C.gl_check_fb_status((C.GPCHECKFRAMEBUFFERSTATUS)(gpCheckFramebufferStatus), C.GLenum(target)))
}
func deleteRenderbuffers(n int32, renderbuffers *uint32) {
C.gl_delete_rb((C.GPDELETERENDERBUFFERS)(gpDeleteRenderbuffers), C.GLsizei(n), (*C.GLuint)(unsafe.Pointer(renderbuffers)))
}
func deleteFramebuffers(n int32, framebuffers *uint32) {
C.gl_delete_fb((C.GPDELETEFRAMEBUFFERS)(gpDeleteFramebuffers), C.GLsizei(n), (*C.GLuint)(unsafe.Pointer(framebuffers)))
}
func deleteTextures(n int32, textures *uint32) {
C.gl_delete_tex((C.GPDELETETEXTURES)(gpDeleteTextures), C.GLsizei(n), (*C.GLuint)(unsafe.Pointer(textures)))
}
func readPixels(x, y, width, height int32, format, xtype uint32, pixels unsafe.Pointer) {
C.gl_read_pixels((C.GPREADPIXELS)(gpReadPixels), C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(xtype), pixels)
}
func pixelStorei(pname uint32, param int32) {
C.gl_pixel_storei((C.GPPIXELSTOREI)(gpPixelStorei), C.GLenum(pname), C.GLint(param))
}
var (
fbo, tex, rbo uint32
hasDepth bool
@ -40,40 +251,40 @@ var (
)
func initContext(getProcAddr func(name string) unsafe.Pointer) {
if err := gl.InitWithProcAddrFunc(getProcAddr); err != nil {
if err := initProcAddr(getProcAddr); err != nil {
panic(err)
}
gl.PixelStorei(gl.PackAlignment, 1)
pixelStorei(gl_PACK_ALIGNMENT, 1)
}
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, w, h, 0, pixType, pixFormat, nil)
gl.BindTexture(gl.Texture2d, 0)
genTextures(1, &tex)
bindTexture(gl_TEXTURE_2D, tex)
texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_NEAREST)
texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_NEAREST)
texImage2D(gl_TEXTURE_2D, 0, gl_RGBA8, w, h, 0, pixType, pixFormat, nil)
bindTexture(gl_TEXTURE_2D, 0)
gl.GenFramebuffers(1, &fbo)
gl.BindFramebuffer(gl.FRAMEBUFFER, fbo)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.ColorAttachment0, gl.Texture2d, tex, 0)
genFramebuffers(1, &fbo)
bindFramebuffer(gl_FRAMEBUFFER, fbo)
framebufferTexture2D(gl_FRAMEBUFFER, gl_COLOR_ATTACHMENT0, gl_TEXTURE_2D, tex, 0)
if depth {
gl.GenRenderbuffers(1, &rbo)
gl.BindRenderbuffer(gl.RENDERBUFFER, rbo)
format, attachment := uint32(gl.DepthComponent24), uint32(gl.DepthAttachment)
genRenderbuffers(1, &rbo)
bindRenderbuffer(gl_RENDERBUFFER, rbo)
format, attachment := uint32(gl_DEPTH_COMPONENT24), uint32(gl_DEPTH_ATTACHMENT)
if stencil {
format, attachment = gl.Depth24Stencil8, gl.DepthStencilAttachment
format, attachment = gl_DEPTH24_STENCIL8, gl_DEPTH_STENCIL_ATTACH
}
gl.RenderbufferStorage(gl.RENDERBUFFER, format, w, h)
gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, rbo)
gl.BindRenderbuffer(gl.RENDERBUFFER, 0)
renderbufferStorage(gl_RENDERBUFFER, format, w, h)
framebufferRenderbuffer(gl_FRAMEBUFFER, attachment, gl_RENDERBUFFER, rbo)
bindRenderbuffer(gl_RENDERBUFFER, 0)
}
if status := gl.CheckFramebufferStatus(gl.FRAMEBUFFER); status != gl.FramebufferComplete {
if status := checkFramebufferStatus(gl_FRAMEBUFFER); status != gl_FRAMEBUFFER_COMPLETE {
return fmt.Errorf("framebuffer incomplete: 0x%X", status)
}
return nil
@ -81,15 +292,17 @@ func initFramebuffer(width, height int, depth, stencil bool) error {
func destroyFramebuffer() {
if hasDepth {
gl.DeleteRenderbuffers(1, &rbo)
deleteRenderbuffers(1, &rbo)
}
gl.DeleteFramebuffers(1, &fbo)
gl.DeleteTextures(1, &tex)
deleteFramebuffers(1, &fbo)
deleteTextures(1, &tex)
}
// ReadFramebuffer reads the current FBO color attachment into the internal buffer.
// Call SetBuffer first to size the buffer (w * h * 4).
func ReadFramebuffer(size, w, h uint) []byte {
gl.BindFramebuffer(gl.FRAMEBUFFER, fbo)
gl.ReadPixels(0, 0, int32(w), int32(h), pixType, pixFormat, bufPtr)
bindFramebuffer(gl_FRAMEBUFFER, fbo)
readPixels(0, 0, int32(w), int32(h), pixType, pixFormat, bufPtr)
return buf[:size]
}
@ -101,11 +314,11 @@ func SetBuffer(size int) {
func SetPixelFormat(format PixelFormat) error {
switch format {
case UnsignedShort5551:
pixFormat, pixType = gl.UnsignedShort5551, gl.BGRA
pixFormat, pixType = 0x8034, gl_BGRA // GL_UNSIGNED_SHORT_5_5_5_1
case UnsignedShort565:
pixFormat, pixType = gl.UnsignedShort565, gl.RGB
pixFormat, pixType = 0x8363, gl_RGB // GL_UNSIGNED_SHORT_5_6_5
case UnsignedInt8888Rev:
pixFormat, pixType = gl.UnsignedInt8888Rev, gl.BGRA
pixFormat, pixType = 0x8367, gl_BGRA // GL_UNSIGNED_INT_8_8_8_8_REV
default:
return errors.New("unknown pixel format")
}
@ -113,10 +326,10 @@ func SetPixelFormat(format PixelFormat) error {
}
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))
return goStr(getString(gl_VERSION)),
goStr(getString(gl_VENDOR)),
goStr(getString(gl_RENDERER)),
goStr(getString(gl_SHADING_LANG_VERSION))
}
func GlFbo() uint32 { return fbo }

View file

@ -0,0 +1,575 @@
package graphics
/*
#cgo windows LDFLAGS: -lgdi32 -lopengl32
#cgo linux LDFLAGS: -lX11 -lGL
#cgo darwin LDFLAGS: -framework OpenGL
// ==========================================================================
// rgfw_nano — minimal window + GL context for cloud-game
// Derived from RGFW 2.0.0-dev (libpng license)
// Copyright (C) 2022-26 Riley Mabb (@ColleagueRiley)
// Altered: stripped to ~540 lines from ~16,400
// Original: https://github.com/ColleagueRiley/RGFW
// ==========================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ---- platform detection ----
#if defined(_WIN32)
#define RGFW_WINDOWS
#include <windows.h>
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#define RGFW_X11
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/glx.h>
#elif defined(__APPLE__)
#define RGFW_MACOS
#include <dlfcn.h>
#include <OpenGL/OpenGL.h>
#include <OpenGL/CGLTypes.h>
#endif
// ---- basic types ----
typedef unsigned char u8;
typedef signed char i8;
typedef unsigned short u16;
typedef signed short i16;
typedef unsigned int u32;
typedef signed int i32;
typedef unsigned long long u64;
typedef signed long long i64;
typedef u8 RGFW_bool;
#define RGFW_TRUE 1
#define RGFW_FALSE 0
#define RGFW_ALLOC malloc
#define RGFW_FREE free
// ---- window flags ----
typedef u32 RGFW_windowFlags;
#define RGFW_windowHide (1u << 9)
// ---- GL types ----
typedef void (*RGFW_proc)(void);
typedef struct RGFW_glContext {
#ifdef RGFW_WINDOWS
HGLRC ctx;
#elif defined(RGFW_MACOS)
void* ctx;
#else
GLXContext ctx;
#endif
} RGFW_glContext;
typedef enum { RGFW_glCore = 0, RGFW_glForwardCompatibility, RGFW_glCompatibility, RGFW_glES } RGFW_glProfile;
typedef enum { RGFW_glAccelerated = 0, RGFW_glSoftware } RGFW_glRenderer;
typedef enum { RGFW_glReleaseNone = 0 } RGFW_glReleaseBehavior;
typedef struct RGFW_glHints {
i32 stencil, samples, stereo, auxBuffers, doubleBuffer;
i32 red, green, blue, alpha, depth;
i32 accumRed, accumGreen, accumBlue, accumAlpha;
RGFW_bool sRGB, robustness, debug, noError;
RGFW_glReleaseBehavior releaseBehavior;
RGFW_glProfile profile;
i32 major, minor;
RGFW_glContext* share;
RGFW_glRenderer renderer;
} RGFW_glHints;
typedef u32 RGFW_gfxContextType;
#define RGFW_gfxNativeOpenGL (1u << 0)
#define RGFW_gfxOwnedByRGFW (1u << 2)
// ---- window struct ----
#ifdef RGFW_WINDOWS
typedef struct RGFW_window_src {
HWND window;
HDC hdc;
RGFW_glContext* ctx_native;
RGFW_gfxContextType gfxType;
} RGFW_window_src;
#elif defined(RGFW_MACOS)
typedef struct RGFW_window_src {
void* window;
void* view;
RGFW_glContext* ctx_native;
RGFW_gfxContextType gfxType;
} RGFW_window_src;
#else
typedef struct RGFW_window_src {
Window window;
Display* display;
Colormap cmap;
GLXFBConfig fbconfig;
RGFW_glContext* ctx_native;
RGFW_gfxContextType gfxType;
} RGFW_window_src;
#endif
typedef struct RGFW_window {
RGFW_window_src src;
i32 x, y, w, h;
} RGFW_window;
// ---- global hints ----
static RGFW_glHints RGFW_nano_hints = {0,0,0,0,1, 8,8,8,8,24, 0,0,0,0, 0,0,0,0, RGFW_glReleaseNone, RGFW_glCore, 1,0, NULL, RGFW_glAccelerated};
static void RGFW_resetGlobalHints_OpenGL(void) {
RGFW_glHints d = {0,0,0,0,1, 8,8,8,8,24, 0,0,0,0, 0,0,0,0, RGFW_glReleaseNone, RGFW_glCore, 1,0, NULL, RGFW_glAccelerated};
RGFW_nano_hints = d;
}
static void RGFW_setGlobalHints_OpenGL(RGFW_glHints* h) { RGFW_nano_hints = *h; }
static RGFW_glHints* RGFW_getGlobalHints_OpenGL(void) { return &RGFW_nano_hints; }
// ---- API prototypes ----
static i32 RGFW_init(const char* name, u8 flags);
static void RGFW_deinit(void);
static RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags);
static void RGFW_window_close(RGFW_window* win);
static void RGFW_window_setName(RGFW_window* win, const char* name);
static void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win);
static RGFW_proc RGFW_getProcAddress_OpenGL(const char* name);
static RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints);
static RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints);
// ==========================================================================
// Platform implementations
// ==========================================================================
#ifdef RGFW_WINDOWS
// -------------------------------------------------------------------
// Windows (Win32 + WGL)
// -------------------------------------------------------------------
static LRESULT CALLBACK RGFW_nano_WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
return DefWindowProcW(hwnd, msg, wp, lp);
}
static i32 RGFW_init(const char* name, u8 flags) { (void)name; (void)flags; return 0; }
static void RGFW_deinit(void) {}
static RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags) {
(void)x; (void)y;
RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window));
if (!win) return NULL;
memset(win, 0, sizeof(*win));
win->w = w; win->h = h;
HINSTANCE hi = GetModuleHandleW(NULL);
WNDCLASSW wc = {0};
wc.lpfnWndProc = RGFW_nano_WndProc;
wc.hInstance = hi;
wc.lpszClassName = L"rgfw_nano_wc";
RegisterClassW(&wc);
DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
RECT rect = {0, 0, (LONG)w, (LONG)h};
AdjustWindowRect(&rect, style, FALSE);
win->src.window = CreateWindowExW(0, L"rgfw_nano_wc", L"",
style, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
NULL, NULL, hi, NULL);
if (!win->src.window) { RGFW_FREE(win); return NULL; }
win->src.hdc = GetDC(win->src.window);
return win;
}
static void RGFW_window_close(RGFW_window* win) {
if (!win) return;
if (win->src.ctx_native && (win->src.gfxType & RGFW_gfxOwnedByRGFW))
RGFW_FREE(win->src.ctx_native);
if (win->src.hdc) { ReleaseDC(win->src.window, win->src.hdc); win->src.hdc = NULL; }
if (win->src.window) { DestroyWindow(win->src.window); win->src.window = NULL; }
RGFW_FREE(win);
}
static void RGFW_window_setName(RGFW_window* win, const char* name) { (void)win; (void)name; }
static void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) {
if (win && win->src.hdc && win->src.ctx_native && win->src.ctx_native->ctx)
wglMakeCurrent(win->src.hdc, win->src.ctx_native->ctx);
}
static RGFW_proc RGFW_getProcAddress_OpenGL(const char* name) {
static HMODULE glModule;
if (!glModule) glModule = LoadLibraryA("opengl32.dll");
if (glModule) { RGFW_proc p = (RGFW_proc)GetProcAddress(glModule, name); if (p) return p; }
return (RGFW_proc)wglGetProcAddress(name);
}
static RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
(void)hints;
if (!win || !win->src.hdc || !ctx) return RGFW_FALSE;
PIXELFORMATDESCRIPTOR pfd = {0};
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_GENERIC_FORMAT;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
int pf = ChoosePixelFormat(win->src.hdc, &pfd);
if (!pf || !SetPixelFormat(win->src.hdc, pf, &pfd)) return RGFW_FALSE;
ctx->ctx = wglCreateContext(win->src.hdc);
if (!ctx->ctx) return RGFW_FALSE;
win->src.ctx_native = ctx;
win->src.gfxType = RGFW_gfxNativeOpenGL;
return RGFW_TRUE;
}
static RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints) {
RGFW_glContext* ctx = (RGFW_glContext*)RGFW_ALLOC(sizeof(RGFW_glContext));
if (!ctx) return NULL;
if (!RGFW_window_createContextPtr_OpenGL(win, ctx, hints)) { RGFW_FREE(ctx); return NULL; }
win->src.gfxType |= RGFW_gfxOwnedByRGFW;
return ctx;
}
// -------------------------------------------------------------------
// macOS (CGL, offscreen)
// -------------------------------------------------------------------
#elif defined(RGFW_MACOS)
static i32 RGFW_init(const char* name, u8 flags) { (void)name; (void)flags; return 0; }
static void RGFW_deinit(void) {}
static RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags) {
(void)x; (void)y; (void)flags; (void)name;
RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window));
if (!win) return NULL;
memset(win, 0, sizeof(*win));
win->w = w; win->h = h;
return win;
}
static void RGFW_window_close(RGFW_window* win) {
if (!win) return;
if (win->src.ctx_native && (win->src.gfxType & RGFW_gfxNativeOpenGL)) {
if (win->src.ctx_native->ctx) CGLDestroyContext((CGLContextObj)win->src.ctx_native->ctx);
if (win->src.gfxType & RGFW_gfxOwnedByRGFW) RGFW_FREE(win->src.ctx_native);
}
RGFW_FREE(win);
}
static void RGFW_window_setName(RGFW_window* win, const char* name) { (void)win; (void)name; }
static void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) {
if (win && win->src.ctx_native && win->src.ctx_native->ctx)
CGLSetCurrentContext((CGLContextObj)win->src.ctx_native->ctx);
}
static RGFW_proc RGFW_getProcAddress_OpenGL(const char* name) {
static void* gl_handle;
if (!gl_handle) gl_handle = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY);
if (gl_handle) { void* sym = dlsym(gl_handle, name); if (sym) return (RGFW_proc)sym; }
return NULL;
}
static RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
(void)win;
if (!ctx) return RGFW_FALSE;
CGLPixelFormatAttribute attrs[32];
int n = 0;
attrs[n++] = kCGLPFAOpenGLProfile;
attrs[n++] = (CGLPixelFormatAttribute)kCGLOGLPVersion_Legacy;
if (hints && hints->renderer == RGFW_glAccelerated) attrs[n++] = kCGLPFAAccelerated;
attrs[n++] = kCGLPFAColorSize; attrs[n++] = 32;
attrs[n++] = kCGLPFADepthSize; attrs[n++] = hints ? hints->depth : 24;
attrs[n++] = kCGLPFAStencilSize; attrs[n++] = hints ? hints->stencil : 8;
if (hints && hints->doubleBuffer) attrs[n++] = kCGLPFADoubleBuffer;
attrs[n++] = 0;
CGLPixelFormatObj pix = NULL; GLint npix = 0;
if (CGLChoosePixelFormat(attrs, &pix, &npix) != kCGLNoError || !pix) return RGFW_FALSE;
CGLError err = CGLCreateContext(pix, NULL, (CGLContextObj*)&ctx->ctx);
CGLDestroyPixelFormat(pix);
if (err != kCGLNoError || !ctx->ctx) return RGFW_FALSE;
if (CGLSetCurrentContext((CGLContextObj)ctx->ctx) != kCGLNoError) {
CGLDestroyContext((CGLContextObj)ctx->ctx); ctx->ctx = NULL; return RGFW_FALSE;
}
win->src.ctx_native = ctx;
win->src.gfxType = RGFW_gfxNativeOpenGL;
return RGFW_TRUE;
}
static RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints) {
RGFW_glContext* ctx = (RGFW_glContext*)RGFW_ALLOC(sizeof(RGFW_glContext));
if (!ctx) return NULL;
if (!RGFW_window_createContextPtr_OpenGL(win, ctx, hints)) { RGFW_FREE(ctx); return NULL; }
win->src.gfxType |= RGFW_gfxOwnedByRGFW;
return ctx;
}
// -------------------------------------------------------------------
// X11 (GLX)
// -------------------------------------------------------------------
#else
static Display* RGFW_nano_display = NULL;
static Atom RGFW_nano_WM_DELETE_WINDOW;
static i32 RGFW_init(const char* name, u8 flags) {
(void)name; (void)flags;
RGFW_nano_display = XOpenDisplay(NULL);
if (!RGFW_nano_display) return -1;
RGFW_nano_WM_DELETE_WINDOW = XInternAtom(RGFW_nano_display, "WM_DELETE_WINDOW", False);
return 0;
}
static void RGFW_deinit(void) {
if (RGFW_nano_display) { XCloseDisplay(RGFW_nano_display); RGFW_nano_display = NULL; }
}
static RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags) {
(void)x; (void)y; (void)flags;
if (!RGFW_nano_display) return NULL;
RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window));
if (!win) return NULL;
memset(win, 0, sizeof(*win));
win->w = w; win->h = h;
win->src.display = RGFW_nano_display;
int scr = DefaultScreen(RGFW_nano_display);
Window root = DefaultRootWindow(RGFW_nano_display);
XSetWindowAttributes swa;
swa.event_mask = StructureNotifyMask | ExposureMask;
swa.colormap = XCreateColormap(RGFW_nano_display, root, DefaultVisual(RGFW_nano_display, scr), AllocNone);
win->src.cmap = swa.colormap;
win->src.window = XCreateWindow(RGFW_nano_display, root, 0, 0, w, h, 0,
DefaultDepth(RGFW_nano_display, scr), InputOutput,
DefaultVisual(RGFW_nano_display, scr), CWEventMask | CWColormap, &swa);
if (!win->src.window) { RGFW_FREE(win); return NULL; }
XSetWMProtocols(RGFW_nano_display, win->src.window, &RGFW_nano_WM_DELETE_WINDOW, 1);
RGFW_window_setName(win, name);
XMapWindow(RGFW_nano_display, win->src.window);
XFlush(RGFW_nano_display);
return win;
}
static void RGFW_window_close(RGFW_window* win) {
if (!win) return;
if (win->src.ctx_native && (win->src.gfxType & RGFW_gfxNativeOpenGL)) {
glXMakeCurrent(win->src.display, None, NULL);
if (win->src.ctx_native->ctx) glXDestroyContext(win->src.display, win->src.ctx_native->ctx);
if (win->src.gfxType & RGFW_gfxOwnedByRGFW) RGFW_FREE(win->src.ctx_native);
}
if (win->src.window) { XDestroyWindow(win->src.display, win->src.window); win->src.window = 0; }
if (win->src.cmap) { XFreeColormap(win->src.display, win->src.cmap); win->src.cmap = 0; }
RGFW_FREE(win);
}
static void RGFW_window_setName(RGFW_window* win, const char* name) {
if (win && win->src.display && win->src.window) XStoreName(win->src.display, win->src.window, name);
}
static void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) {
if (win && win->src.display && win->src.ctx_native && win->src.ctx_native->ctx)
glXMakeCurrent(win->src.display, win->src.window, win->src.ctx_native->ctx);
}
static RGFW_proc RGFW_getProcAddress_OpenGL(const char* name) {
return (RGFW_proc)glXGetProcAddress((const GLubyte*)name);
}
static int RGFW_nano_glx_attribs(int* attribs, int max, RGFW_glHints* hints) {
int n = 0;
(void)max;
attribs[n++] = GLX_RENDER_TYPE; attribs[n++] = GLX_RGBA_BIT;
attribs[n++] = GLX_DRAWABLE_TYPE; attribs[n++] = GLX_WINDOW_BIT;
attribs[n++] = GLX_DOUBLEBUFFER; attribs[n++] = hints->doubleBuffer ? True : False;
attribs[n++] = GLX_RED_SIZE; attribs[n++] = hints->red;
attribs[n++] = GLX_GREEN_SIZE; attribs[n++] = hints->green;
attribs[n++] = GLX_BLUE_SIZE; attribs[n++] = hints->blue;
attribs[n++] = GLX_ALPHA_SIZE; attribs[n++] = hints->alpha;
attribs[n++] = GLX_DEPTH_SIZE; attribs[n++] = hints->depth;
attribs[n++] = GLX_STENCIL_SIZE; attribs[n++] = hints->stencil;
attribs[n++] = None;
return n;
}
static RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
if (!win || !win->src.display || !ctx) return RGFW_FALSE;
int attribs[64];
RGFW_nano_glx_attribs(attribs, 64, hints);
int fbcount;
GLXFBConfig* fbcs = glXChooseFBConfig(win->src.display, DefaultScreen(win->src.display), attribs, &fbcount);
if (!fbcs || fbcount == 0) return RGFW_FALSE;
win->src.fbconfig = fbcs[0];
XFree(fbcs);
GLXContext share = (hints && hints->share) ? hints->share->ctx : NULL;
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
glXCreateContextAttribsARBProc glXCreateContextAttribsARB =
(glXCreateContextAttribsARBProc)glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");
if (glXCreateContextAttribsARB && hints && (hints->major > 0 || hints->minor > 0)) {
int ctx_attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, hints->major,
GLX_CONTEXT_MINOR_VERSION_ARB, hints->minor,
GLX_CONTEXT_PROFILE_MASK_ARB,
hints->profile == RGFW_glCore ? GLX_CONTEXT_CORE_PROFILE_BIT_ARB :
hints->profile == RGFW_glCompatibility ? GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :
GLX_CONTEXT_ES_PROFILE_BIT_EXT,
None
};
ctx->ctx = glXCreateContextAttribsARB(win->src.display, win->src.fbconfig, share, True, ctx_attribs);
}
if (!ctx->ctx) {
ctx->ctx = glXCreateNewContext(win->src.display, win->src.fbconfig, GLX_RGBA_TYPE, share, True);
}
if (!ctx->ctx) return RGFW_FALSE;
win->src.ctx_native = ctx;
win->src.gfxType = RGFW_gfxNativeOpenGL;
return RGFW_TRUE;
}
static RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints) {
RGFW_glContext* ctx = (RGFW_glContext*)RGFW_ALLOC(sizeof(RGFW_glContext));
if (!ctx) return NULL;
if (!RGFW_window_createContextPtr_OpenGL(win, ctx, hints)) { RGFW_FREE(ctx); return NULL; }
win->src.gfxType |= RGFW_gfxOwnedByRGFW;
return ctx;
}
#endif
*/
import "C"
import (
"fmt"
"unsafe"
)
// RGFW is a graphics context backed by the inlined rgfw_nano C code.
type RGFW struct {
win *C.RGFW_window
inited bool
}
// NewRGFWContext creates a hidden window with an OpenGL context and an FBO.
func NewRGFWContext(cfg Config) (*RGFW, error) {
name := C.CString("cloud-retro")
defer C.free(unsafe.Pointer(name))
if ret := C.RGFW_init(name, 0); ret < 0 {
return nil, fmt.Errorf("rgfw init failed: %d", int(ret))
}
if !cfg.GLAutoContext {
var hints C.RGFW_glHints
C.RGFW_resetGlobalHints_OpenGL()
hints.major = C.i32(cfg.GLVersionMajor)
hints.minor = C.i32(cfg.GLVersionMinor)
if cfg.GLHasDepth {
hints.depth = 24
}
if cfg.GLHasStencil {
hints.stencil = 8
}
hints.doubleBuffer = 1
hints.renderer = C.RGFW_glSoftware
switch cfg.Ctx {
case CtxOpenGlCore:
hints.profile = C.RGFW_glCore
case CtxOpenGlEs2, CtxOpenGlEs3, CtxOpenGlEsVersion:
hints.profile = C.RGFW_glES
hints.major = 3
hints.minor = 0
case CtxOpenGl:
hints.profile = C.RGFW_glCompatibility
default:
hints.profile = C.RGFW_glCompatibility
}
C.RGFW_setGlobalHints_OpenGL(&hints)
}
wname := C.CString("cloud-retro")
defer C.free(unsafe.Pointer(wname))
win := C.RGFW_createWindow(wname, 0, 0, 1, 1, C.RGFW_windowHide)
if win == nil {
C.RGFW_deinit()
return nil, fmt.Errorf("rgfw window creation failed")
}
if C.RGFW_window_createContext_OpenGL(win, C.RGFW_getGlobalHints_OpenGL()) == nil {
C.RGFW_window_close(win)
C.RGFW_deinit()
return nil, fmt.Errorf("rgfw GL context creation failed")
}
C.RGFW_window_makeCurrentContext_OpenGL(win)
initContext(func(name string) unsafe.Pointer {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return unsafe.Pointer(C.RGFW_getProcAddress_OpenGL(cname))
})
if err := initFramebuffer(cfg.W, cfg.H, cfg.GLHasDepth, cfg.GLHasStencil); err != nil {
C.RGFW_window_close(win)
C.RGFW_deinit()
return nil, fmt.Errorf("rgfw fbo: %w", err)
}
return &RGFW{win: win, inited: true}, nil
}
func (r *RGFW) Deinit() error {
if !r.inited {
return nil
}
r.inited = false
destroyFramebuffer()
C.RGFW_window_close(r.win)
r.win = nil
C.RGFW_deinit()
return nil
}
func (r *RGFW) BindContext() error {
if !r.inited {
return fmt.Errorf("rgfw: context not initialized")
}
C.RGFW_window_makeCurrentContext_OpenGL(r.win)
return nil
}
func RGFWGlProcAddress(proc string) unsafe.Pointer {
cname := C.CString(proc)
defer C.free(unsafe.Pointer(cname))
return unsafe.Pointer(C.RGFW_getProcAddress_OpenGL(cname))
}
func RGFWTryInit() error {
className := C.CString("cloud-retro-test")
defer C.free(unsafe.Pointer(className))
if ret := C.RGFW_init(className, 0); ret < 0 {
return fmt.Errorf("rgfw init failed: %d", int(ret))
}
C.RGFW_deinit()
return nil
}

View file

@ -1,100 +0,0 @@
package graphics
import (
"fmt"
"unsafe"
"github.com/veandco/go-sdl2/sdl"
)
type SDL struct {
w *sdl.Window
ctx sdl.GLContext
}
type Config struct {
Ctx Context
W, H int
GLAutoContext bool
GLVersionMajor uint
GLVersionMinor uint
GLHasDepth bool
GLHasStencil bool
}
func NewSDLContext(cfg Config) (*SDL, error) {
if err := sdl.Init(sdl.INIT_VIDEO); err != nil {
return nil, fmt.Errorf("sdl: %w", err)
}
if !cfg.GLAutoContext {
if err := setGLAttrs(cfg.Ctx); err != nil {
return nil, err
}
}
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: %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("fbo: %w", err)
}
return &SDL{w: w, ctx: ctx}, nil
}
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 err
}
sdl.Quit()
return nil
}

View file

@ -68,7 +68,7 @@ type Nanoarch struct {
}
vfr bool
Aspect bool
sdlCtx *graphics.SDL
glCtx *graphics.RGFW
hackSkipHwContextDestroy bool
hackSkipSameThreadSave bool
limiter func(func())
@ -373,7 +373,7 @@ func (n *Nanoarch) Shutdown() {
thread.Main(func() {
// running inside a go routine, lock the thread to make sure the OpenGL context stays current
runtime.LockOSThread()
if err := n.sdlCtx.BindContext(); err != nil {
if err := n.glCtx.BindContext(); err != nil {
n.log.Error().Err(err).Msg("ctx switch fail")
}
})
@ -421,7 +421,7 @@ func (n *Nanoarch) Run() {
} else {
if n.Video.gl.enabled {
runtime.LockOSThread()
if err := n.sdlCtx.BindContext(); err != nil {
if err := n.glCtx.BindContext(); err != nil {
n.log.Error().Err(err).Msg("ctx bind fail")
}
}
@ -432,7 +432,7 @@ func (n *Nanoarch) Run() {
}
}
func (n *Nanoarch) IsSupported() error { return graphics.TryInit() }
func (n *Nanoarch) IsSupported() error { return graphics.RGFWTryInit() }
func (n *Nanoarch) IsGL() bool { return n.Video.gl.enabled }
func (n *Nanoarch) IsStopped() bool { return n.Stopped.Load() }
func (n *Nanoarch) InputRetropad(port int, data []byte) { n.retropad.SetInput(port, data) }
@ -744,7 +744,7 @@ func coreGetCurrentFramebuffer() C.uintptr_t { return (C.uintptr_t)(graphics.GlF
//export coreGetProcAddress
func coreGetProcAddress(sym *C.char) C.retro_proc_address_t {
return (C.retro_proc_address_t)(graphics.GlProcAddress(C.GoString(sym)))
return (C.retro_proc_address_t)(graphics.RGFWGlProcAddress(C.GoString(sym)))
}
//export coreEnvironment
@ -903,7 +903,7 @@ func initVideo() {
thread.Main(func() {
var err error
Nan0.sdlCtx, err = graphics.NewSDLContext(graphics.Config{
Nan0.glCtx, err = graphics.NewRGFWContext(graphics.Config{
Ctx: context,
W: int(Nan0.sys.av.geometry.max_width),
H: int(Nan0.sys.av.geometry.max_height),
@ -929,7 +929,7 @@ func deinitVideo() {
C.bridge_context_reset(Nan0.Video.hw.context_destroy)
}
thread.Main(func() {
if err := Nan0.sdlCtx.Deinit(); err != nil {
if err := Nan0.glCtx.Deinit(); err != nil {
Nan0.log.Error().Err(err).Msg("deinit fail")
}
})