16 Commits

Author SHA1 Message Date
Ray
1fc0b4955f REVIEWED: rl_gputex.h, added some info for future improvements 2025-07-26 13:36:03 +02:00
Ray
eb7f8912f8 Minor format tweaks 2025-07-26 12:50:29 +02:00
Ray
8343aed4f6 Avoid fatal errors on OBJ model loading 2025-07-26 12:50:12 +02:00
Ray
c9f9219fa6 REVIEWED: DRM cache buffers support #4988 2025-07-26 12:49:49 +02:00
Ray
5c680739bd Removed platform-specific flag from config.h #4988 2025-07-26 12:49:03 +02:00
Ray
86d9afc10c Merge pull request #4988 from rob-bits/master
[rcore][drm] Replace DRM swap buffer implementation with asynchronous page-flipping and triple framebuffer caching
2025-07-26 12:14:21 +02:00
Ray
715e174d13 Merge pull request #5062 from danilwhale/feat/get-proc-address
[rlgl] Add rlGetProcAddress
2025-07-26 12:07:43 +02:00
Ray
7262be85fd Minor format tweaks 2025-07-26 12:06:24 +02:00
Ray
4da399141a Merge pull request #5063 from kariem2k/fix_cmake_sdl3
Fixed: CMake support for SDL3
2025-07-26 11:49:05 +02:00
Ray
d7893141f3 REVIEWED: rl_gputex.h library to be usable standalone 2025-07-26 11:44:36 +02:00
32960af1dc Fixed:
Added CMake support for SDL3.
Now supports including SDL2 or SDL3 as a subdirectory within the project. The system will first check for SDL3, then SDL2. If neither is found, it will attempt find_package(SDL3), followed by find_package(SDL2). If all these checks fail, the process will terminate with an error.
2025-07-24 09:48:52 +01:00
d6a897e551 [rlgl]: Add rlGetProcAddress 2025-07-23 21:41:45 +03:00
8388160c32 - fixing SUPPORT_DRM_CACHE define check at the end of InitPlatform() 2025-07-15 13:58:23 +02:00
5b182139ae - implementing Raylib coding convention 2025-07-07 17:55:32 +02:00
de62be0ec5 - created complier flag SUPPORT_DRM_CACHE, to enable triple buffered DRM caching 2025-07-07 17:28:23 +02:00
060bd787b1 Refactor: Replace DRM swap buffer implementation with asynchronous page-flipping and framebuffer caching
The original implementation created/destroyed framebuffers (FBs) per-frame, leading to kernel overhead and screen tearing. This commit replaces it with a different approach using:
- Asynchronous `drmModePageFlip()` with vblank sync
- Framebuffer caching to reduce repeated FB creation/removal operations
- Proper resource management through BO callbacks and buffer release synchronization
- Added error handling for busy displays, cache overflows, and flip failures
- Event-driven cleanup via page_flip_handler to prevent GPU/scanout conflicts

Co-authored-by: rob-bits
2025-06-05 15:50:07 +02:00
10 changed files with 1016 additions and 159 deletions

View File

@ -101,10 +101,37 @@ elseif ("${PLATFORM}" MATCHES "DRM")
set(LIBS_PRIVATE ${GLESV2} ${EGL} ${DRM} ${GBM} atomic pthread m dl)
elseif ("${PLATFORM}" MATCHES "SDL")
find_package(SDL2 REQUIRED)
set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL")
set(LIBS_PRIVATE SDL2::SDL2)
# First, check if SDL is included as a subdirectory
if(TARGET SDL3::SDL3)
message(STATUS "Using SDL3 from subdirectory")
set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL")
set(LIBS_PRIVATE SDL3::SDL3)
add_compile_definitions(USING_SDL3_PROJECT)
elseif(TARGET SDL2::SDL2)
message(STATUS "Using SDL2 from subdirectory")
set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL")
set(LIBS_PRIVATE SDL2::SDL2)
add_compile_definitions(USING_SDL2_PROJECT)
else()
# No SDL added via add_subdirectory(), try find_package()
message(STATUS "No SDL target from subdirectory, searching via find_package()...")
# First try SDL3
find_package(SDL3 QUIET)
if(SDL3_FOUND)
message(STATUS "Found SDL3 via find_package()")
set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL")
set(LIBS_PRIVATE SDL3::SDL3)
add_compile_definitions(USING_SDL3_PACKAGE)
else()
# Fallback to SDL2
find_package(SDL2 REQUIRED)
message(STATUS "Found SDL2 via find_package()")
set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL")
set(LIBS_PRIVATE SDL2::SDL2)
add_compile_definitions(USING_SDL2_PACKAGE)
endif()
endif()
endif ()
if (NOT ${OPENGL_VERSION} MATCHES "OFF")

File diff suppressed because it is too large Load Diff

View File

@ -1862,8 +1862,8 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
// WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1
// to work properly with our implementation (IsKeyDown/IsKeyUp checks)
if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0;
else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1;
else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
else if (action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1;
else if (action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
// WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys
if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) ||

View File

@ -52,13 +52,27 @@
#ifndef SDL_ENABLE_OLD_NAMES
#define SDL_ENABLE_OLD_NAMES // Just in case we're on SDL3, we need some in-between compatibily
#endif
#include "SDL.h" // SDL base library (window/rendered, input, timing... functionality)
// SDL base library (window/rendered, input, timing... functionality)
#ifdef USING_SDL3_PROJECT
#include "SDL3/SDL.h"
#elif USING_SDL2_PROJECT
#include "SDL2/SDL.h"
#else
#include "SDL.h"
#endif
#if defined(GRAPHICS_API_OPENGL_ES2)
// It seems it does not need to be included to work
//#include "SDL_opengles2.h"
#else
#include "SDL_opengl.h" // SDL OpenGL functionality (if required, instead of internal renderer)
// SDL OpenGL functionality (if required, instead of internal renderer)
#ifdef USING_SDL3_PROJECT
#include "SDL3/SDL_opengl.h"
#elif USING_SDL2_PROJECT
#include "SDL2/SDL_opengl.h"
#else
#include "SDL_opengl.h"
#endif
#endif
//----------------------------------------------------------------------------------
@ -238,8 +252,7 @@ static const int CursorsLUT[] = {
//SDL_SYSTEM_CURSOR_WAITARROW, // No equivalent implemented on MouseCursor enum on raylib.h
};
// SDL3 Migration Layer made to avoid `ifdefs` inside functions when we can.
// SDL3 Migration Layer made to avoid 'ifdefs' inside functions when we can.
#if defined(PLATFORM_DESKTOP_SDL3)
// SDL3 Migration:
@ -290,13 +303,13 @@ int SDL_GetNumVideoDisplays(void)
int monitorCount = 0;
SDL_DisplayID *displays = SDL_GetDisplays(&monitorCount);
// Safe because If `mem` is NULL, SDL_free does nothing
// Safe because If 'mem' is NULL, SDL_free does nothing
SDL_free(displays);
return monitorCount;
}
// SLD3 Migration: To emulate SDL2 this function should return `SDL_DISABLE` or `SDL_ENABLE`
// SLD3 Migration: To emulate SDL2 this function should return 'SDL_DISABLE' or 'SDL_ENABLE'
// representing the *processing state* of the event before this function makes any changes to it
Uint8 SDL_EventState(Uint32 type, int state)
{
@ -567,7 +580,7 @@ void SetWindowState(unsigned int flags)
if (flags & FLAG_WINDOW_UNFOCUSED)
{
// NOTE: To be able to implement this part it seems that we should
// do it ourselves, via `Windows.h`, `X11/Xlib.h` or even `Cocoa.h`
// do it ourselves, via 'windows.h', 'X11/Xlib.h' or even 'Cocoa.h'
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL");
}
if (flags & FLAG_WINDOW_TOPMOST)
@ -1155,13 +1168,13 @@ Image GetClipboardImage(void)
image = LoadImageFromMemory(imageExtensions[i], fileData, dataSize);
if (IsImageValid(image))
{
TRACELOG(LOG_INFO, "Clipboard image: Got image from clipboard as a `%s` successfully", imageExtensions[i]);
TRACELOG(LOG_INFO, "Clipboard: Got image from clipboard successfully: %s", imageExtensions[i]);
return image;
}
}
}
if (!IsImageValid(image)) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data. Error: %s", SDL_GetError());
if (!IsImageValid(image)) TRACELOG(LOG_WARNING, "Clipboard: Couldn't get clipboard data. ERROR: %s", SDL_GetError());
#endif
return image;

View File

@ -48,11 +48,11 @@
*
**********************************************************************************************/
#include <fcntl.h> // POSIX file control definitions - open(), creat(), fcntl()
#include <unistd.h> // POSIX standard function definitions - read(), close(), STDIN_FILENO
#include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
#include <pthread.h> // POSIX threads management (inputs reading)
#include <dirent.h> // POSIX directory browsing
#include <fcntl.h> // POSIX file control definitions - open(), creat(), fcntl()
#include <unistd.h> // POSIX standard function definitions - read(), close(), STDIN_FILENO
#include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
#include <pthread.h> // POSIX threads management (inputs reading)
#include <dirent.h> // POSIX directory browsing
#include <sys/ioctl.h> // Required for: ioctl() - UNIX System call for device-specific input/output operations
#include <linux/kd.h> // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
@ -71,6 +71,14 @@
#include "EGL/egl.h" // Native platform windowing system interface
#include "EGL/eglext.h" // EGL extensions
// NOTE: DRM cache enables triple buffered DRM caching
#if defined(SUPPORT_DRM_CACHE)
#include <poll.h> // Required for: drmHandleEvent() poll
#include <errno.h> // Required for: EBUSY, EAGAIN
#define MAX_DRM_CACHED_BUFFERS 3
#endif // SUPPORT_DRM_CACHE
#ifndef EGL_OPENGL_ES3_BIT
#define EGL_OPENGL_ES3_BIT 0x40
#endif
@ -78,18 +86,16 @@
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
#define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event<N> number
#define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event<N> number
#define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events
#define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events
// So actually the biggest key is KEY_CNT but we only really map the keys up to
// KEY_ALS_TOGGLE
// Actually biggest key is KEY_CNT but we only really map the keys up to KEY_ALS_TOGGLE
#define KEYMAP_SIZE KEY_ALS_TOGGLE
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef struct {
// Display data
int fd; // File descriptor for /dev/dri/...
@ -129,6 +135,18 @@ typedef struct {
int gamepadCount; // The number of gamepads registered
} PlatformData;
#if defined(SUPPORT_DRM_CACHE)
typedef struct {
struct gbm_bo *bo; // Graphics buffer object
uint32_t fbId; // DRM framebuffer ID
} FramebufferCache;
static FramebufferCache fbCache[MAX_DRM_CACHED_BUFFERS] = { 0 };
static volatile int fbCacheCount = 0;
static volatile bool pendingFlip = false;
static bool crtcSet = false;
#endif // SUPPORT_DRM_CACHE
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
@ -551,6 +569,212 @@ void DisableCursor(void)
CORE.Input.Mouse.cursorHidden = true;
}
#if defined(SUPPORT_DRM_CACHE)
// Destroy cached framebuffer callback, set by gbm_bo_set_user_data()
static void DestroyFrameBufferCallback(struct gbm_bo *bo, void *data)
{
uint32_t fbId = (uintptr_t)data;
// Remove from cache
for (int i = 0; i < fbCacheCount; i++)
{
if (fbCache[i].bo == bo)
{
TRACELOG(LOG_INFO, "DISPLAY: DRM: Framebuffer removed [%u]", (uintptr_t)fbId);
drmModeRmFB(platform.fd, fbCache[i].fbId); // Release DRM FB
// Shift remaining entries
for (int j = i; j < fbCacheCount - 1; j++) fbCache[j] = fbCache[j + 1];
fbCacheCount--;
break;
}
}
}
// Create or retrieve cached DRM FB for BO
static uint32_t GetOrCreateFbForBo(struct gbm_bo *bo)
{
// Try to find existing cache entry
for (int i = 0; i < fbCacheCount; i++)
{
if (fbCache[i].bo == bo) return fbCache[i].fbId;
}
// Create new entry if cache not full
if (fbCacheCount >= MAX_DRM_CACHED_BUFFERS) return 0; // FB cache full
uint32_t handle = gbm_bo_get_handle(bo).u32;
uint32_t stride = gbm_bo_get_stride(bo);
uint32_t width = gbm_bo_get_width(bo);
uint32_t height = gbm_bo_get_height(bo);
uint32_t fbId = 0;
if (drmModeAddFB(platform.fd, width, height, 24, 32, stride, handle, &fbId)) return 0;
// Store in cache
fbCache[fbCacheCount] = (FramebufferCache){ .bo = bo, .fbId = fbId };
fbCacheCount++;
// Set destroy callback to auto-cleanup
gbm_bo_set_user_data(bo, (void *)(uintptr_t)fbId, DestroyFrameBufferCallback);
TRACELOG(LOG_INFO, "DISPLAY: DRM: Added new buffer object [%u]" , (uintptr_t)fbId);
return fbId;
}
// Renders a blank frame to allocate initial buffers
// TODO: WARNING: Platform layers do not include OpenGL code!
void RenderBlankFrame()
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(platform.device, platform.surface);
glFinish(); // Ensure the buffer is processed
}
// Initialize with first buffer only
int InitSwapScreenBuffer()
{
if (!platform.gbmSurface || (platform.fd < 0))
{
TRACELOG(LOG_ERROR, "DISPLAY: DRM: Swap buffers can not be initialized");
return -1;
}
// Render a blank frame to allocate buffers
RenderBlankFrame();
// Get first buffer
struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface);
if (!bo)
{
TRACELOG(LOG_ERROR, "DISPLAY: DRM: Failed to lock initial swap buffer");
return -1;
}
// Create FB for first buffer
uint32_t fbId = GetOrCreateFbForBo(bo);
if (!fbId)
{
gbm_surface_release_buffer(platform.gbmSurface, bo);
return -1;
}
// Initial CRTC setup
if (drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fbId, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]))
{
TRACELOG(LOG_ERROR, "DISPLAY: DRM: Failed to initialize CRTC setup. ERROR: %s", strerror(errno));
gbm_surface_release_buffer(platform.gbmSurface, bo);
return -1;
}
// Keep first buffer locked until flipped
platform.prevBO = bo;
crtcSet = true;
return 0;
}
// Static page flip handler
// NOTE: Called once the drmModePageFlip() finished from the drmHandleEvent() context
static void PageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data)
{
// Unused inputs
(void)fd;
(void)frame;
(void)sec;
(void)usec;
pendingFlip = false;
struct gbm_bo *boToRelease = (struct gbm_bo *)data;
// Buffers are released after the flip completes (via page_flip_handler), ensuring they're no longer in use
// Prevents the GPU from writing to a buffer being scanned out
if (boToRelease) gbm_surface_release_buffer(platform.gbmSurface, boToRelease);
}
// Swap implementation with proper caching
void SwapScreenBuffer()
{
if (!crtcSet || !platform.gbmSurface) return;
static int loopCnt = 0;
static int errCnt[5] = {0};
loopCnt++;
// Call this only, if pendingFlip is not set
eglSwapBuffers(platform.device, platform.surface);
// Process pending events non-blocking
drmEventContext evctx = {
.version = DRM_EVENT_CONTEXT_VERSION,
.page_flip_handler = PageFlipHandler
};
struct pollfd pfd = { .fd = platform.fd, .events = POLLIN };
// Polling for event for 0ms
while (poll(&pfd, 1, 0) > 0) drmHandleEvent(platform.fd, &evctx);
// Skip if previous flip pending
if (pendingFlip)
{
errCnt[0]++; // Skip frame: flip pending
return;
}
// Get new front buffer
struct gbm_bo *nextBO = gbm_surface_lock_front_buffer(platform.gbmSurface);
if (!nextBO) // Failed to lock front buffer
{
errCnt[1]++;
return;
}
// Get FB ID (creates new one if needed)
uint32_t fbId = GetOrCreateFbForBo(nextBO);
if (!fbId)
{
gbm_surface_release_buffer(platform.gbmSurface, nextBO);
errCnt[2]++;
return;
}
// Attempt page flip
// NOTE: rmModePageFlip() schedules a buffer-flip for the next vblank and then notifies us about it.
// It takes a CRTC-id, fb-id and an arbitrary data-pointer and then schedules the page-flip.
// This is fully asynchronous and when the page-flip happens, the DRM-fd will become readable and we can call drmHandleEvent().
// This will read all vblank/page-flip events and call our modeset_page_flip_event() callback with the data-pointer that we passed to drmModePageFlip().
// We simply call modeset_draw_dev() then so the next frame is rendered... returns immediately.
if (drmModePageFlip(platform.fd, platform.crtc->crtc_id, fbId, DRM_MODE_PAGE_FLIP_EVENT, platform.prevBO))
{
if (errno == EBUSY) errCnt[3]++; // Display busy - skip flip
else errCnt[4]++; // Page flip failed
gbm_surface_release_buffer(platform.gbmSurface, nextBO);
return;
}
// Success: update state
pendingFlip = true;
platform.prevBO = nextBO;
/*
// Some benchmarking code
if (loopCnt >= 600)
{
TRACELOG(LOG_INFO, "DRM: Error counters: %d, %d, %d, %d, %d, %d", errCnt[0], errCnt[1], errCnt[2], errCnt[3], errCnt[4], loopCnt);
for (int i = 0; i < 5; i++) errCnt[i] = 0;
loopCnt = 0;
}
*/
}
#else // !SUPPORT_DRM_CACHE
// Swap back buffer with front buffer (screen drawing)
void SwapScreenBuffer(void)
{
@ -580,6 +804,7 @@ void SwapScreenBuffer(void)
platform.prevBO = bo;
}
#endif // SUPPORT_DRM_CACHE
//----------------------------------------------------------------------------------
// Module Functions Definition: Misc
@ -1083,9 +1308,21 @@ int InitPlatform(void)
CORE.Storage.basePath = GetWorkingDirectory();
//----------------------------------------------------------------------------
#if defined(SUPPORT_DRM_CACHE)
if (InitSwapScreenBuffer() == 0)
{
TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully");
return 0;
}
else
{
TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized failed");
return -1;
}
#else // !SUPPORT_DRM_CACHE
TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully");
return 0;
#endif
}
// Close platform

View File

@ -1547,7 +1547,8 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
music.looping = true; // Looping enabled by default
musicLoaded = true;
}
else {
else
{
drwav_uninit(ctxWav);
RL_FREE(ctxWav);
}

View File

@ -539,7 +539,7 @@ const char *TextFormat(const char *text, ...); // Formatting of tex
#pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG")
#endif
// Not needed because `rtexture.c` will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined
// Not needed because 'rtexture.c' will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined
// #if !defined(STBI_REQUIRED)
// #pragma message ("WARNING: "STBI_REQUIRED is not defined, that means we can't load images from clipbard"
// #endif

View File

@ -710,6 +710,7 @@ RLAPI void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha,
RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states)
RLAPI void rlglClose(void); // De-initialize rlgl (buffers, shaders, textures)
RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function required)
RLAPI void *rlGetProcAddress(const char *procName); // Get OpenGL procedure address
RLAPI int rlGetVersion(void); // Get current OpenGL version
RLAPI void rlSetFramebufferWidth(int width); // Set current framebuffer width
RLAPI int rlGetFramebufferWidth(void); // Get default framebuffer width
@ -1041,10 +1042,15 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
// Types and Structures Definition
//----------------------------------------------------------------------------------
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions loader signature (same as GLADloadproc)
typedef struct rlglData {
rlRenderBatch *currentBatch; // Current render batch
rlRenderBatch defaultBatch; // Default internal render batch
rlglLoadProc loader; // OpenGL function loader
struct {
int vertexCounter; // Current active render batch vertex counter (generic, used for all batches)
float texcoordx, texcoordy; // Current active texture coordinate (added on glVertex*())
@ -1114,8 +1120,6 @@ typedef struct rlglData {
} ExtSupported; // Extensions supported flags
} rlglData;
typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions loader signature (same as GLADloadproc)
#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
//----------------------------------------------------------------------------------
@ -2567,6 +2571,8 @@ void rlLoadExtensions(void *loader)
TRACELOG(RL_LOG_INFO, " > Version: %s", glGetString(GL_VERSION));
TRACELOG(RL_LOG_INFO, " > GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
RLGL.loader = (rlglLoadProc)loader;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
// NOTE: Anisotropy levels capability is an extension
#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
@ -2625,6 +2631,11 @@ void rlLoadExtensions(void *loader)
#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
}
// Get OpenGL procedure address
void *rlGetProcAddress(const char *procName) {
return RLGL.loader(procName);
}
// Get current OpenGL version
int rlGetVersion(void)
{

View File

@ -4292,7 +4292,7 @@ static Model LoadOBJ(const char *fileName)
if (fileText == NULL)
{
TRACELOG(LOG_ERROR, "MODEL: [%s] Unable to read obj file", fileName);
TRACELOG(LOG_WARNING, "MODEL: [%s] Unable to read obj file", fileName);
return model;
}
@ -4308,7 +4308,7 @@ static Model LoadOBJ(const char *fileName)
if (ret != TINYOBJ_SUCCESS)
{
TRACELOG(LOG_ERROR, "MODEL: Unable to read obj data %s", fileName);
TRACELOG(LOG_WARNING, "MODEL: Unable to read obj data %s", fileName);
return model;
}

View File

@ -168,23 +168,14 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#define RL_GPUTEX_MALLOC RL_MALLOC
#define RL_GPUTEX_CALLOC RL_CALLOC
#define RL_GPUTEX_REALLOC RL_REALLOC
#define RL_GPUTEX_FREE RL_FREE
#define RL_GPUTEX_WARN(...) TRACELOG(LOG_WARNING, "IMAGE: " __VA_ARGS__)
#define RL_GPUTEX_SHOW_WARN_INFO
// FIXME: probably, we should NOT export public functions from rl_gputex
// but this is how it always worked... so let's keep it this way
#define RLGPUTEXAPI RLAPI
#define RL_GPUTEX_MALLOC RL_MALLOC
#define RL_GPUTEX_FREE RL_FREE
#define RL_GPUTEX_LOG(...) TRACELOG(LOG_WARNING, "IMAGE: " __VA_ARGS__)
#define RL_GPUTEX_SHOW_LOG_INFO
#define RL_GPUTEX_IMPLEMENTATION
#include "external/rl_gputex.h" // Required for: rl_load_xxx_from_memory()
// NOTE: Used to read compressed textures data (multiple formats support)
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic pop
#endif