Skip to content

[rcore][win32] Adding native win32 backend #4869

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

marler8997
Copy link
Contributor

A native Win32 backend for your consideration. The main benefits are a smaller disk/memory footprint for raylib applications and less unexpected behavior. Here's some numbers to compare:

Executable File Size of core_basic_window

      |     ReleaseFast    |   ReleaseSmall   |      Debug       |
------|--------------------|------------------|------------------|
GLFW  |  1,138 KB  (1.00x) |   794 KB (1.00x) | 1,479 KB (1.00x) | 
RGFW  |    981 KB  (0.86x) |   633 KB (0.78x) | 1,282 KB (0.86x) |
WIN32 |    951 KB  (0.83x) |   611 KB (0.77x) | 1,254 KB (0.85x) |

Memory Footprint (process memory size) of core_basic_window

      |   ReleaseFast   | ReleaseSmall  |     Debug     |
------|-----------------|---------------|---------------|
GLFW  |     53.4 MB     |    53.5 MB    |     53.4 MB   |
RGFW  |     53.6 MB     |    53.5 MB    |     53.5 MB   |
WIN32 |     53.2 MB     |    53.1 MB    |     53.1 MB   |

The implementation attempts to keep the actual state of the window and the raylib flags in sync. This is enforced before/after every window message which is overly paranoid but probably good for a first pass. The Win32 platform is full of gotchas and the benefit of using an abstraction like GLFW/RGFW are they have had a long time to fix all of them. However, I've made an effort to handle alot of them here.

The backend is not complete but it currently works with most examples.

@raysan5 raysan5 changed the title native win32 backend [rcore][win32] Adding native win32 backend Mar 31, 2025
zet23t pushed a commit to zet23t/raylib that referenced this pull request Apr 1, 2025
I believe it makes sense to only do this when there are no known
touch points. I am not even sure if this should be done at all.

See raysan5#4869 for more information.
@raysan5
Copy link
Owner

raysan5 commented Apr 2, 2025

@marler8997 Thanks for this new backend! Looks great! I want to test and review more carefully before merging.

I don't understand OpenGL well enough to know the difference between
SwapBuffers and wglSwapLayerBuffers but the former seems to double
my FPS (from 2000 to about 4000 in core_vr_simulator).
Instead of calling BeginPaint/EndPaint in WM_PAINT which signals to the
OS that our window content is updated, now when we encounter the WM_PAINT
message instead we return back to the raylib application which will
trigger it to render a new frame. We've replaced the call to BeginPaint
and EndPaint with a call to ValidateRect in SwapBuffers, which, means
we're now correctly telling the OS when our window content is actually
up-to-date.

Note that this doesn't fix the window content not being updated during
a window resize/move beacuse thos have their own message loop which
doesn't return early when it's time to paint.
@raysan5
Copy link
Owner

raysan5 commented Apr 18, 2025

@marler8997 I'm testing the new backend and it fails when tryin to compile with gcc:

gcc -c rcore.c -Iexternal/glfw/include -std=c99 -Wall -DPLATFORM_DESKTOP_WIN32 -DGRAPHICS_API_OPENGL_33 -Wno-unused-parameter
Process started (PID=18108) >>>
In file included from rcore.c:557:
platforms/rcore_desktop_win32.c: In function 'DecoratedFromStyle':
platforms/rcore_desktop_win32.c:132:10: warning: unused variable 'is_off' [-Wunused-variable]
  132 |     bool is_off;
      |          ^~~~~~
platforms/rcore_desktop_win32.c: In function 'IsWindows10Version1703OrGreaterWin32':
platforms/rcore_desktop_win32.c:483:27: error: passing argument 1 of 'Verify' from incompatible pointer type [-Wincompatible-pointer-types]
  483 |     return 0 == (*Verify)(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, cond);
      |                           ^~~~~
      |                           |
      |                           OSVERSIONINFOEX * {aka OSVERSIONINFOEXA *}
platforms/rcore_desktop_win32.c:483:27: note: expected 'RTL_OSVERSIONINFOEXW *' but argument is of type 'OSVERSIONINFOEX *' {aka 'OSVERSIONINFOEXA *'}
platforms/rcore_desktop_win32.c: In function 'GetCursorName':
platforms/rcore_desktop_win32.c:700:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  700 |     case MOUSE_CURSOR_DEFAULT      : return IDC_ARROW;
      |                                             ^~~~~~~~~
platforms/rcore_desktop_win32.c:701:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  701 |     case MOUSE_CURSOR_ARROW        : return IDC_ARROW;
      |                                             ^~~~~~~~~
platforms/rcore_desktop_win32.c:702:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  702 |     case MOUSE_CURSOR_IBEAM        : return IDC_IBEAM;
      |                                             ^~~~~~~~~
platforms/rcore_desktop_win32.c:703:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  703 |     case MOUSE_CURSOR_CROSSHAIR    : return IDC_CROSS;
      |                                             ^~~~~~~~~
platforms/rcore_desktop_win32.c:704:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  704 |     case MOUSE_CURSOR_POINTING_HAND: return IDC_HAND;
      |                                             ^~~~~~~~
platforms/rcore_desktop_win32.c:705:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  705 |     case MOUSE_CURSOR_RESIZE_EW    : return IDC_SIZEWE;
      |                                             ^~~~~~~~~~
platforms/rcore_desktop_win32.c:706:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  706 |     case MOUSE_CURSOR_RESIZE_NS    : return IDC_SIZENS;
      |                                             ^~~~~~~~~~
platforms/rcore_desktop_win32.c:707:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  707 |     case MOUSE_CURSOR_RESIZE_NWSE  : return IDC_SIZENWSE;
      |                                             ^~~~~~~~~~~~
platforms/rcore_desktop_win32.c:708:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  708 |     case MOUSE_CURSOR_RESIZE_NESW  : return IDC_SIZENESW;
      |                                             ^~~~~~~~~~~~
platforms/rcore_desktop_win32.c:709:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  709 |     case MOUSE_CURSOR_RESIZE_ALL   : return IDC_SIZEALL;
      |                                             ^~~~~~~~~~~
platforms/rcore_desktop_win32.c:710:45: error: returning 'CHAR *' {aka 'char *'} from a function with incompatible return type 'LPCWSTR' {aka 'const short unsigned int *'} [-Wincompatible-pointer-types]
  710 |     case MOUSE_CURSOR_NOT_ALLOWED  : return IDC_NO;
      |                                             ^~~~~~
platforms/rcore_desktop_win32.c: In function 'ShowCursor':
platforms/rcore_desktop_win32.c:1346:33: error: passing argument 2 of 'LoadCursorW' from incompatible pointer type [-Wincompatible-pointer-types]
 1346 |     SetCursor(LoadCursorW(NULL, IDC_ARROW));
      |                                 ^~~~~~~~~
      |                                 |
      |                                 CHAR * {aka char *}
In file included from C:/raylib/w64devkit/x86_64-w64-mingw32/include/windows.h:72,
                 from platforms/rcore_desktop_win32.c:56:
C:/raylib/w64devkit/x86_64-w64-mingw32/include/winuser.h:4114:69: note: expected 'LPCWSTR' {aka 'const short unsigned int *'} but argument is of type 'CHAR *' {aka 'char *'}
 4114 |   WINUSERAPI HCURSOR WINAPI LoadCursorW(HINSTANCE hInstance,LPCWSTR lpCursorName);
      |                                                             ~~~~~~~~^~~~~~~~~~~~
platforms/rcore_desktop_win32.c: In function 'WndProc2':
platforms/rcore_desktop_win32.c:1798:80: error: passing argument 2 of 'LoadCursorW' from incompatible pointer type [-Wincompatible-pointer-types]
 1798 |             SetCursor(CORE.Input.Mouse.cursorHidden ? NULL : LoadCursorW(NULL, IDC_ARROW));
      |                                                                                ^~~~~~~~~
      |                                                                                |
      |                                                                                CHAR * {aka char *}
C:/raylib/w64devkit/x86_64-w64-mingw32/include/winuser.h:4114:69: note: expected 'LPCWSTR' {aka 'const short unsigned int *'} but argument is of type 'CHAR *' {aka 'char *'}
 4114 |   WINUSERAPI HCURSOR WINAPI LoadCursorW(HINSTANCE hInstance,LPCWSTR lpCursorName);
      |                                                             ~~~~~~~~^~~~~~~~~~~~
platforms/rcore_desktop_win32.c: In function 'InitPlatform':
platforms/rcore_desktop_win32.c:1941:39: error: passing argument 2 of 'LoadCursorW' from incompatible pointer type [-Wincompatible-pointer-types]
 1941 |         c.hCursor = LoadCursorW(NULL, IDC_ARROW);
      |                                       ^~~~~~~~~
      |                                       |
      |                                       CHAR * {aka char *}
C:/raylib/w64devkit/x86_64-w64-mingw32/include/winuser.h:4114:69: note: expected 'LPCWSTR' {aka 'const short unsigned int *'} but argument is of type 'CHAR *' {aka 'char *'}
 4114 |   WINUSERAPI HCURSOR WINAPI LoadCursorW(HINSTANCE hInstance,LPCWSTR lpCursorName);
      |                                                             ~~~~~~~~^~~~~~~~~~~~
rcore.c: At top level:
rcore.c:3604:6: warning: 'SetupFramebuffer' defined but not used [-Wunused-function]
 3604 | void SetupFramebuffer(int width, int height)
      |      ^~~~~~~~~~~~~~~~
platforms/rcore_desktop_win32.c:716:13: warning: 'GetMonitorDpi' defined but not used [-Wunused-function]
  716 | static UINT GetMonitorDpi(HMONITOR monitor)
      |             ^~~~~~~~~~~~~
<<< Process finished (PID=18108). (Exit code 1)

Also noted that file does not follow the raylib code conventions, it should be reviewed.

@marler8997
Copy link
Contributor Author

Ok I added a few commits to fix the errors compiling with GCC and updated the style to match raylib conventions. With this I can build on Windows with the w64devkit via make PLATFORM=PLATFORM_DESKTOP_WIN32 GRAPHICS=GRAPHICS_API_OPENGL_33

Added some more missing spaces after conditional statements.  Also
made unsupported MSAA_4X an assert instead of an abort and also
removed dpi-aware cases for older OS's.  More changes would be needed
to support those OS versions, namely, removing the dependency
on shcore.
@raysan5
Copy link
Owner

raysan5 commented Apr 20, 2025

@marler8997 Reviewed it again, why does it require the -DUNICODE flag and the -lshcore library? GLFW did not required it, afaik.

When compiling I get an empty window, I guess it could be related to DPI:

image

Also, it does not seem to consider the OpenGL 3.3 requested version.

EDIT: Compiled with Notepad++ script and w64devkit included in raylib Windows installer.

@marler8997
Copy link
Contributor Author

marler8997 commented Apr 20, 2025

The -DUNICODE flag isn't necessary as far as I know, it helps prevent us from accidently using old legacy wrapper functions but it can be removed if you prefer. I pushed a commit that should allow you to compile using the w64devkit without this flag.

shcore is required for proper DPI support on Windows 8 and newer, so to support both older and newer Windows versions (Windows 7 and older), GLFW loads it at runtime with LoadLibrary/GetProc. If raylib support extends to Windows 7, then we should do the same, does it?

Also yes I can confirm, the backend currently doesn't do anything with the requested OpenGL version.

After making the change to allow me to compile with w64devkit, building with a modified notepad++ script and the w64devkit, I was not able to reproduce the blank screen issue. Here's the two scripts I used to compile the core basic window example:

echo > Choose compile options
echo -------------------------------
echo Set desired OpenGL API version: 1.1, 2.1, 3.3, 4.3
SET GRAPHIC_API=GRAPHICS_API_OPENGL_33
echo
echo > Setup required Environment
echo -------------------------------------
SET RAYLIB_PATH=C:\git\raylib
SET COMPILER_PATH=C:\w64devkit\bin
ENV_SET PATH=$(COMPILER_PATH)
SET CC=gcc
SET AR=ar
SET CFLAGS=-std=c99 -Wall -DPLATFORM_DESKTOP_WIN32 -D$(GRAPHIC_API) -Wno-unused-parameter
SET RELEASE_PATH=$(COMPILER_PATH)\..\x86_64-w64-mingw32
cd $(RAYLIB_PATH)\src
echo
echo > Clean latest build
echo -----------------------
cmd /c del /F *.o
cmd /c del /F libraylib.a
echo
echo > Compile raylib modules
echo ------------------------------
$(CC) --version
$(CC) -c rcore.c -Iexternal/glfw/include $(CFLAGS)
$(CC) -c rglfw.c $(CFLAGS)
$(CC) -c rshapes.c $(CFLAGS)
$(CC) -c rtextures.c $(CFLAGS)
$(CC) -c rtext.c $(CFLAGS)
$(CC) -c rmodels.c $(CFLAGS)
$(CC) -c raudio.c $(CFLAGS)
$(CC) -c utils.c $(CFLAGS)
echo
echo > Generate raylib library
echo ------------------------------
$(AR) rcs libraylib.a rcore.o rglfw.o rshapes.o rtextures.o rtext.o rmodels.o raudio.o utils.o
echo
echo > Install raylib library
echo --------------------------
cmd /c copy raylib.h $(RELEASE_PATH)\include /Y
cmd /c copy rlgl.h $(RELEASE_PATH)\include /Y
cmd /c copy libraylib.a $(RELEASE_PATH)\lib /Y
echo
echo > Reset Environment
echo --------------------------
ENV_UNSET PATH
echo > Setup required Environment
echo -------------------------------------
SET RAYLIB_PATH=C:\git\raylib
SET COMPILER_PATH=C:\w64devkit\bin
ENV_SET PATH=$(COMPILER_PATH)
SET CC=gcc
cd $(CURRENT_DIRECTORY)
echo
echo > Saving Current File
echo -------------------------
npp_save
echo
echo > Compile simple C file
echo ----------------------------
$(CC) -o $(NAME_PART).exe $(FILE_NAME) -Wall -Wextra -std=c99 -lraylib -lopengl32 -lgdi32 -lshcore -lwinmm
echo
echo > Reset Environment
echo --------------------------
ENV_UNSET PATH

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants