mirror of
https://github.com/glfw/glfw.git
synced 2025-06-15 20:22:15 +00:00
input: Cache parsed mappings.h
Parsing this file was taking the most time out of glfwInit(), or about 14ms on my PinePhone before the start of this series. In the case where the string is already in cache, it now takes only 0.95ms. It would be possible to make that even faster, and additionally to keep that page shared with other GLFW processes, by using mmap() instead, but for now I opted to not change the memory representation.
This commit is contained in:
parent
acee7eadd7
commit
04922c63de
@ -218,6 +218,16 @@ if (_GLFW_WAYLAND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# TODO: Move that elsewhere, as other platforms would equally benefit from it.
|
||||
if (_GLFW_X11 OR _GLFW_WAYLAND)
|
||||
include(FindPkgConfig)
|
||||
pkg_check_modules(XXHash3 libxxhash)
|
||||
if (XXHash3_FOUND)
|
||||
list(APPEND glfw_INCLUDE_DIRS "${XXHash3_INCLUDE_DIRS}")
|
||||
set(HAVE_XXHASH TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Use Cocoa for window creation and NSOpenGL for context creation
|
||||
#--------------------------------------------------------------------
|
||||
|
@ -4,6 +4,10 @@ add_library(glfw "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h"
|
||||
internal.h mappings.h context.c init.c input.c monitor.c
|
||||
vulkan.c window.c)
|
||||
|
||||
if (HAVE_XXHASH)
|
||||
target_sources(glfw PRIVATE cache.c cache.h)
|
||||
endif()
|
||||
|
||||
if (_GLFW_COCOA)
|
||||
target_sources(glfw PRIVATE cocoa_platform.h cocoa_joystick.h posix_thread.h
|
||||
nsgl_context.h egl_context.h osmesa_context.h
|
||||
|
223
src/cache.c
Normal file
223
src/cache.c
Normal file
@ -0,0 +1,223 @@
|
||||
//========================================================================
|
||||
// GLFW 3.4 - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would
|
||||
// be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not
|
||||
// be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// Please use C89 style variable declarations in this file because VS 2010
|
||||
//========================================================================
|
||||
|
||||
#define _XOPEN_SOURCE 500
|
||||
|
||||
#include "cache.h"
|
||||
|
||||
#ifndef HAVE_XXHASH
|
||||
#error "libxxhash is required for the compose cache"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#define XXH_INLINE_ALL
|
||||
#include <xxhash.h>
|
||||
|
||||
static GLFWbool
|
||||
hashString(const char *string, size_t len, XXH128_canonical_t *out)
|
||||
{
|
||||
XXH3_state_t *state;
|
||||
XXH_errorcode err;
|
||||
XXH128_hash_t hash;
|
||||
|
||||
state = XXH3_createState();
|
||||
if (!state)
|
||||
return GLFW_FALSE;
|
||||
|
||||
err = XXH3_128bits_reset(state);
|
||||
if (err != XXH_OK)
|
||||
{
|
||||
XXH3_freeState(state);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
err = XXH3_128bits_update(state, string, len);
|
||||
if (err != XXH_OK)
|
||||
{
|
||||
XXH3_freeState(state);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
hash = XXH3_128bits_digest(state);
|
||||
XXH3_freeState(state);
|
||||
XXH128_canonicalFromHash(out, hash);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
GLFWbool
|
||||
cacheGetPathFromString(const char *string, char **cachePath)
|
||||
{
|
||||
const char *home, *xdg;
|
||||
char *path;
|
||||
int length;
|
||||
char *hash_part;
|
||||
XXH128_canonical_t hash;
|
||||
|
||||
/* Mostly copied over from xkb_context_include_path_append_default(). */
|
||||
home = getenv("HOME");
|
||||
xdg = getenv("XDG_CACHE_HOME");
|
||||
if (xdg != NULL)
|
||||
{
|
||||
length = strlen(xdg) + strlen("/glfw/") + 32 + 1;
|
||||
path = malloc(length);
|
||||
sprintf(path, "%s/glfw/", xdg);
|
||||
}
|
||||
else if (home != NULL)
|
||||
{
|
||||
/* XDG_CACHE_HOME fallback is $HOME/.cache/ */
|
||||
length = strlen(home) + strlen("/.cache/glfw/") + 32 + 1;
|
||||
path = malloc(length);
|
||||
sprintf(path, "%s/.cache/glfw/", home);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (!hashString(string, strlen(string), &hash))
|
||||
{
|
||||
free(path);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
hash_part = strrchr(path, '/') + 1;
|
||||
*hash_part = '\0';
|
||||
mkdir(path, 0755);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
sprintf(hash_part, "%2.2x", hash.digest[i]);
|
||||
hash_part += 2;
|
||||
}
|
||||
|
||||
*cachePath = path;
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
GLFWbool
|
||||
cacheRead(const char *path, _GLFWmapping** out, int* count)
|
||||
{
|
||||
FILE *f;
|
||||
int length, i;
|
||||
_GLFWmapping* mappings;
|
||||
_GLFWmapping* mapping;
|
||||
|
||||
#define FREAD(ptr, size, nmemb, stream) \
|
||||
do \
|
||||
{ \
|
||||
if (fread(ptr, size, nmemb, stream) < nmemb) \
|
||||
return GLFW_FALSE; \
|
||||
} while (0)
|
||||
#define FREAD_n(ptr, nmemb, stream) \
|
||||
FREAD(ptr, sizeof(*ptr), nmemb, stream)
|
||||
#define FREAD_1(value, stream) \
|
||||
FREAD_n(&value, 1, stream)
|
||||
|
||||
f = fopen(path, "rb");
|
||||
if (!f)
|
||||
return GLFW_FALSE;
|
||||
|
||||
FREAD_1(length, f);
|
||||
mappings = malloc(length * sizeof(_GLFWmapping));
|
||||
if (!mappings)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < length; ++i)
|
||||
{
|
||||
mapping = &mappings[i];
|
||||
FREAD_n(mapping->name, 128, f);
|
||||
FREAD_n(mapping->guid, 32, f);
|
||||
mapping->guid[32] = '\0';
|
||||
FREAD_n(mapping->buttons, 15, f);
|
||||
FREAD_n(mapping->axes, 6, f);
|
||||
}
|
||||
|
||||
#undef FREAD_1
|
||||
#undef FREAD_n
|
||||
#undef FREAD
|
||||
|
||||
fclose(f);
|
||||
*count = length;
|
||||
*out = mappings;
|
||||
return GLFW_TRUE;
|
||||
|
||||
error:
|
||||
fclose(f);
|
||||
unlink(path);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
cacheWrite(const char *path, const _GLFWmapping* mappings, int count)
|
||||
{
|
||||
FILE *f;
|
||||
int i;
|
||||
const _GLFWmapping* mapping;
|
||||
|
||||
#define FWRITE(ptr, size, nmemb, stream) \
|
||||
do \
|
||||
{ \
|
||||
if (fwrite(ptr, size, nmemb, stream) < nmemb) \
|
||||
goto error; \
|
||||
} while (0)
|
||||
#define FWRITE_n(ptr, nmemb, stream) \
|
||||
FWRITE(ptr, sizeof(*ptr), nmemb, stream)
|
||||
#define FWRITE_1(value, stream) \
|
||||
FWRITE_n(&value, 1, stream)
|
||||
|
||||
f = fopen(path, "wb");
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
FWRITE_1(count, f);
|
||||
for (i = 0; i < count; ++i) {
|
||||
mapping = &mappings[i];
|
||||
FWRITE_n(mapping->name, 128, f);
|
||||
FWRITE_n(mapping->guid, 32, f);
|
||||
FWRITE_n(mapping->buttons, 15, f);
|
||||
FWRITE_n(mapping->axes, 6, f);
|
||||
}
|
||||
|
||||
if (fclose(f) != 0)
|
||||
goto fclose_error;
|
||||
|
||||
return;
|
||||
|
||||
#undef FWRITE_1
|
||||
#undef FWRITE_n
|
||||
#undef FWRITE
|
||||
|
||||
error:
|
||||
fclose(f);
|
||||
fclose_error:
|
||||
unlink(path);
|
||||
}
|
44
src/cache.h
Normal file
44
src/cache.h
Normal file
@ -0,0 +1,44 @@
|
||||
//========================================================================
|
||||
// GLFW 3.4 - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would
|
||||
// be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not
|
||||
// be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// Please use C89 style variable declarations in this file because VS 2010
|
||||
//========================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#ifndef HAVE_XXHASH
|
||||
#error "libxxhash is required for the compose cache"
|
||||
#endif
|
||||
|
||||
GLFWbool
|
||||
cacheGetPathFromString(const char *string, char **cachePath);
|
||||
|
||||
GLFWbool
|
||||
cacheRead(const char *path, _GLFWmapping** out, int* count);
|
||||
|
||||
void
|
||||
cacheWrite(const char *path, const _GLFWmapping* mappings, int count);
|
@ -55,4 +55,6 @@
|
||||
#cmakedefine HAVE_XKBCOMMON_COMPOSE_H
|
||||
// Define this to 1 if the libc supports memfd_create()
|
||||
#cmakedefine HAVE_MEMFD_CREATE
|
||||
// Define this to 1 if xxhash has been found at build time
|
||||
#cmakedefine HAVE_XXHASH
|
||||
|
||||
|
29
src/input.c
29
src/input.c
@ -35,6 +35,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_XXHASH
|
||||
#include "cache.h"
|
||||
#endif
|
||||
|
||||
// Internal key state used for sticky keys
|
||||
#define _GLFW_STICK 3
|
||||
|
||||
@ -1158,11 +1162,25 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
|
||||
{
|
||||
int jid;
|
||||
const char* c = string;
|
||||
#ifdef HAVE_XXHASH
|
||||
char *cachePath;
|
||||
GLFWbool usableCache, readFromCache = GLFW_FALSE;
|
||||
#endif
|
||||
|
||||
assert(string != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
|
||||
#ifdef HAVE_XXHASH
|
||||
usableCache = cacheGetPathFromString(string, &cachePath);
|
||||
|
||||
if (usableCache && cacheRead(cachePath, &_glfw.mappings, &_glfw.mappingCount))
|
||||
{
|
||||
readFromCache = GLFW_TRUE;
|
||||
goto doMapping;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (*c)
|
||||
{
|
||||
if ((*c >= '0' && *c <= '9') ||
|
||||
@ -1204,6 +1222,10 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_XXHASH
|
||||
doMapping:
|
||||
#endif
|
||||
|
||||
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
|
||||
{
|
||||
_GLFWjoystick* js = _glfw.joysticks + jid;
|
||||
@ -1211,6 +1233,13 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
|
||||
js->mapping = findValidMapping(js);
|
||||
}
|
||||
|
||||
#ifdef HAVE_XXHASH
|
||||
if (usableCache && !readFromCache)
|
||||
cacheWrite(cachePath, _glfw.mappings, _glfw.mappingCount);
|
||||
|
||||
free(cachePath);
|
||||
#endif
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user