taisei/src/video.c

769 lines
20 KiB
C
Raw Normal View History

2012-07-28 22:53:53 +02:00
/*
* This software is licensed under the terms of the MIT License.
2012-07-28 22:53:53 +02:00
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
2019-07-03 20:00:56 +02:00
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
2012-07-28 22:53:53 +02:00
*/
#include "taisei.h"
#include <png.h>
2012-07-28 22:53:53 +02:00
#include "global.h"
#include "video.h"
#include "renderer/api.h"
#include "util/pngcruft.h"
#include "util/fbmgr.h"
#include "taskmanager.h"
#include "video_postprocess.h"
Refactored the gl loader into a polyglot macro monstrosity We no longer link to libGL by default. All GL functions are loaded dynamically through SDL apis. Use -DLINK_TO_LIBGL to enable linking, in which case Taisei won't try to dynamically load any of the gl functions. Previously we used a strange inconsistent setup where some functions were loaded dynamically and others pulled from the linked library. We also no longer link to libGLU even if LINK_TO_LIBGL is set. The only function we used from that library was gluPerspective. taiseigl now provides a simple substitute via glFrustum. The SDL2 gl headers are now used instead of the system ones, this should be more portable. The taiseigl.h header contains generated code partially based on content from those headers. It also doubles as a python3 script that actually generates that code and inserts it into itself. It scans the Taisei source tree for gl* calls and generates code only for the functions we use, and a few manually specified ones that are called indirectly. Assumptions such as "linux = glx" are no longer made. SDL takes care of platform specifics, as it should. The GL_EXT_draw_instanced/GL_ARB_draw_instanced extension detection has been improved. Taisei should be able to figure out which one to use without a compile-time check, and support for the ARB version has been actually implemented for the laser snippet loader. I've tested it on Windows 8 with Intel drivers that don't support the EXT version but do support the ARB one, instanced drawing works and the lasers don't lag! OSX should benefit from this change as well, although I've not yet tested the OSX build, beyond simply compiling it.
2017-02-24 01:54:28 +01:00
2017-04-01 02:08:35 +02:00
Video video;
2012-07-28 22:53:53 +02:00
typedef struct ScreenshotTaskData {
char *dest_path;
OpenGL ES 3.0 rendering backend (#148) * First steps towards shader transpilation Needs to be manually enabled via -Dshader_transpiler=true. Requires shaderc. https://github.com/google/shaderc Not yet functional due to missing SPIRV-Cross integration. SPIRV-Cross currently does not have an official C API, and crossc is too minimal to be useful. The current plan is to extend crossc and vendor it, while also sending PRs upstream. * Integrate crossc; shader transpilation for GLES now works * fix leak * gles30 backend now playable on Mesa with 3.2 context Some rendering issues are present. Identified so far: - Marisa's lasers are invisible - Death effect looks wrong Also, a small pixmap manipulation library has been written, and the texture uploading API redesigned around it. * fix marisa lasers in GLES (uniform name clashed with builtin) * fix player death effect in GLES (another name clash) * Dump ANGLE's translated shader code in debug log * fix screenshots * Drop support for triangle fans, switch to strips Fans offer no advantage over strips, and they've been removed in D3D10+, so ANGLE has to emulate them. * crude workaround for an ANGLE bug * Re-enable GL debug labels, fix an issue with them that affected ANGLE (but was always technically a bug) * fix race condition in shaderc initialization * New SDL_RWops interface for vertex buffers * Optimize VBO streaming via buffering updates Measurable performance improvement even with the main gl33 renderer, drastic improvement with ANGLE. * Fix the depth texture binding problem under ANGLE Apparently it hates GL_DEPTH_COMPONENT16 for some reason. Sized internal formats are not supported in GLES 2.0 anyway, so not using them is probably a good idea. * fix GLES2.0 segfault (the backend still doesn't work, though) * dump GL extensions at info log level, not debug * get around a Mesa bug; more correct texture format table for GLES2 * Correct GLES3 texture format table according to the spec Not a Mesa bug after all * require crossc>=1.5.0, fallback to subproject * Request at least 8bit per color channel in GL backends * Forbid lto for static windows builds with shader_transpiler=true * fix edge case segfault * Add basic ANGLE bundling support to the build system Windows only, and no NSIS support yet * Fix various windows-related build system and installer brokenness * Disable gles backends by default * update documentation
2018-10-02 00:36:10 +02:00
Pixmap image;
} ScreenshotTaskData;
2012-07-28 22:53:53 +02:00
static VideoPostProcess *v_postprocess;
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
VideoCapabilityState (*video_query_capability)(VideoCapability cap);
static VideoCapabilityState video_query_capability_generic(VideoCapability cap) {
switch(cap) {
case VIDEO_CAP_FULLSCREEN:
return VIDEO_AVAILABLE;
case VIDEO_CAP_EXTERNAL_RESIZE:
return video_is_fullscreen() ? VIDEO_CURRENTLY_UNAVAILABLE : VIDEO_AVAILABLE;
case VIDEO_CAP_CHANGE_RESOLUTION:
if(video_is_fullscreen() && config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
return VIDEO_CURRENTLY_UNAVAILABLE;
} else {
return VIDEO_AVAILABLE;
}
case VIDEO_CAP_VSYNC_ADAPTIVE:
return VIDEO_AVAILABLE;
}
UNREACHABLE;
}
static VideoCapabilityState video_query_capability_alwaysfullscreen(VideoCapability cap) {
switch(cap) {
case VIDEO_CAP_FULLSCREEN:
return VIDEO_ALWAYS_ENABLED;
case VIDEO_CAP_EXTERNAL_RESIZE:
return VIDEO_NEVER_AVAILABLE;
// XXX: Might not be actually working, but let's be optimistic.
case VIDEO_CAP_CHANGE_RESOLUTION:
return VIDEO_AVAILABLE;
case VIDEO_CAP_VSYNC_ADAPTIVE:
return VIDEO_AVAILABLE;
}
UNREACHABLE;
}
static VideoCapabilityState video_query_capability_webcanvas(VideoCapability cap) {
switch(cap) {
case VIDEO_CAP_EXTERNAL_RESIZE:
return VIDEO_NEVER_AVAILABLE;
case VIDEO_CAP_VSYNC_ADAPTIVE:
return VIDEO_NEVER_AVAILABLE;
default:
return video_query_capability_generic(cap);
}
}
2012-07-28 22:53:53 +02:00
static void video_add_mode(int width, int height) {
if(video.modes) {
for(uint i = 0; i < video.mcount; ++i) {
VideoMode *m = video.modes + i;
if(m->width == width && m->height == height) {
2012-07-28 22:53:53 +02:00
return;
}
2012-07-28 22:53:53 +02:00
}
}
2012-07-28 22:53:53 +02:00
video.modes = (VideoMode*)realloc(video.modes, (++video.mcount) * sizeof(VideoMode));
video.modes[video.mcount-1].width = width;
video.modes[video.mcount-1].height = height;
}
static int video_compare_modes(const void *a, const void *b) {
VideoMode *va = (VideoMode*)a;
VideoMode *vb = (VideoMode*)b;
return va->width * va->height - vb->width * vb->height;
}
void video_get_viewport_size(float *width, float *height) {
float w = video.current.width;
float h = video.current.height;
float r = w / h;
if(r > VIDEO_ASPECT_RATIO) {
w = h * VIDEO_ASPECT_RATIO;
} else if(r < VIDEO_ASPECT_RATIO) {
h = w / VIDEO_ASPECT_RATIO;
}
*width = w;
*height = h;
}
void video_get_viewport(FloatRect *vp) {
video_get_viewport_size(&vp->w, &vp->h);
vp->x = (int)((video.current.width - vp->w) / 2);
vp->y = (int)((video.current.height - vp->h) / 2);
}
static void video_set_viewport(void) {
FloatRect vp;
video_get_viewport(&vp);
r_framebuffer_viewport_rect(NULL, vp);
}
2017-09-19 16:11:42 +02:00
static void video_update_vsync(void) {
if(global.frameskip || config_get_int(CONFIG_VSYNC) == 0) {
r_vsync(VSYNC_NONE);
} else {
switch(config_get_int(CONFIG_VSYNC)) {
case 1: r_vsync(VSYNC_NORMAL); break;
default: r_vsync(VSYNC_ADAPTIVE); break;
}
}
}
2017-03-05 03:22:41 +01:00
2017-09-19 14:13:04 +02:00
static uint32_t get_fullscreen_flag(void) {
if(config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
return SDL_WINDOW_FULLSCREEN_DESKTOP;
} else {
return SDL_WINDOW_FULLSCREEN;
}
}
static void video_check_fullscreen_sanity(void) {
SDL_DisplayMode mode;
if(SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(video.window), &mode)) {
log_sdl_error(LOG_WARN, "SDL_GetCurrentDisplayMode");
2017-09-19 14:13:04 +02:00
return;
}
if(video.current.width != mode.w || video.current.height != mode.h) {
log_error("BUG: window is not actually fullscreen after modesetting. Video mode: %ix%i, window size: %ix%i",
2017-09-19 14:13:04 +02:00
mode.w, mode.h, video.current.width, video.current.height);
}
}
static void video_update_mode_settings(void) {
SDL_ShowCursor(false);
video_update_vsync();
SDL_GetWindowSize(video.window, &video.current.width, &video.current.height);
2017-09-19 14:13:04 +02:00
video_set_viewport();
events_emit(TE_VIDEO_MODE_CHANGED, 0, NULL, NULL);
2017-09-19 14:13:04 +02:00
2017-09-19 16:11:42 +02:00
if(video_is_fullscreen() && !config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
2017-09-19 14:13:04 +02:00
video_check_fullscreen_sanity();
}
}
static const char* modeflagsstr(uint32_t flags) {
if(WINFLAGS_IS_FAKE_FULLSCREEN(flags)) {
return "fake fullscreen";
} else if(WINFLAGS_IS_REAL_FULLSCREEN(flags)) {
return "true fullscreen";
} else if(flags & SDL_WINDOW_RESIZABLE) {
return "windowed, resizable";
} else {
return "windowed";
}
}
static void video_new_window_internal(uint display, uint w, uint h, uint32_t flags, bool fallback) {
2017-02-04 03:56:40 +01:00
if(video.window) {
SDL_DestroyWindow(video.window);
video.window = NULL;
}
2018-01-26 18:39:17 +01:00
char title[sizeof(WINDOW_TITLE) + strlen(TAISEI_VERSION) + 2];
2017-12-28 08:29:17 +01:00
snprintf(title, sizeof(title), "%s v%s", WINDOW_TITLE, TAISEI_VERSION);
video.window = r_create_window(
title,
SDL_WINDOWPOS_CENTERED_DISPLAY(display),
SDL_WINDOWPOS_CENTERED_DISPLAY(display),
w, h, flags
);
2017-02-04 03:56:40 +01:00
if(video.window) {
SDL_SetWindowMinimumSize(video.window, SCREEN_W / 4, SCREEN_H / 4);
2017-09-19 14:13:04 +02:00
video_update_mode_settings();
2017-02-04 03:56:40 +01:00
return;
}
if(fallback) {
2017-09-19 14:13:04 +02:00
log_fatal("Failed to create window with mode %ix%i (%s): %s", w, h, modeflagsstr(flags), SDL_GetError());
2017-02-04 03:56:40 +01:00
return;
2012-07-28 22:53:53 +02:00
}
video_new_window_internal(display, RESX, RESY, flags & ~SDL_WINDOW_FULLSCREEN_DESKTOP, true);
2012-07-28 22:53:53 +02:00
}
static void video_new_window(uint display, uint w, uint h, bool fs, bool resizable) {
uint32_t flags = 0;
if(fs) {
2017-09-19 14:13:04 +02:00
flags |= get_fullscreen_flag();
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
} else if(resizable && video.backend != VIDEO_BACKEND_EMSCRIPTEN) {
flags |= SDL_WINDOW_RESIZABLE;
}
video_new_window_internal(display, w, h, flags, false);
display = video_current_display();
log_info("Created a new window: %ix%i (%s), on display #%i %s",
video.current.width,
video.current.height,
modeflagsstr(SDL_GetWindowFlags(video.window)),
display,
video_display_name(display)
);
events_pause_keyrepeat();
SDL_RaiseWindow(video.window);
2012-07-28 22:53:53 +02:00
}
static bool video_set_display_mode(uint display, uint w, uint h) {
2017-09-19 14:13:04 +02:00
SDL_DisplayMode closest, target = { .w = w, .h = h };
if(!SDL_GetClosestDisplayMode(display, &target, &closest)) {
log_error("No available display modes for %ix%i on display %i", w, h, display);
2017-09-19 14:13:04 +02:00
return false;
}
if(closest.w != w || closest.h != h) {
log_warn("Can't use %ix%i, closest available is %ix%i", w, h, closest.w, closest.h);
}
if(SDL_SetWindowDisplayMode(video.window, &closest)) {
log_error("Failed to set display mode for %ix%i on display %i: %s", closest.w, closest.h, display, SDL_GetError());
2017-09-19 14:13:04 +02:00
return false;
}
return true;
}
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
static void video_set_fullscreen_internal(bool fullscreen) {
2017-09-19 16:11:42 +02:00
uint32_t flags = fullscreen ? get_fullscreen_flag() : 0;
events_pause_keyrepeat();
2017-09-19 16:11:42 +02:00
if(SDL_SetWindowFullscreen(video.window, flags) < 0) {
log_error("Failed to switch to %s mode: %s", modeflagsstr(flags), SDL_GetError());
2017-09-19 16:11:42 +02:00
}
SDL_RaiseWindow(video.window);
2017-09-19 16:11:42 +02:00
}
void video_set_mode(uint display, uint w, uint h, bool fs, bool resizable) {
2017-09-19 14:13:04 +02:00
video.intended.width = w;
video.intended.height = h;
if(display >= video_num_displays()) {
log_warn("Display index %u is invalid, falling back to 0 (%s)", display, video_display_name(0));
display = 0;
}
2017-09-19 14:13:04 +02:00
if(!video.window) {
video_new_window(display, w, h, fs, resizable);
return;
}
uint old_display = video_current_display();
bool display_changed = display != old_display;
bool size_changed = w != video.current.width || h != video.current.height;
if(display_changed) {
video_new_window(display, w, h, fs, resizable);
2017-09-19 14:13:04 +02:00
return;
}
if(size_changed) {
2017-09-19 14:13:04 +02:00
if(fs && !config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
video_set_display_mode(display, w, h);
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
video_set_fullscreen_internal(fs);
2017-09-19 14:13:04 +02:00
video_update_mode_settings();
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
} else if(video.backend == VIDEO_BACKEND_X11) {
2017-09-19 14:13:04 +02:00
// XXX: I would like to use SDL_SetWindowSize for size changes, but apparently it's impossible to reliably detect
// when it fails to actually resize the window. For example, a tiling WM (awesome) may be getting in its way
// and we'd never know. SDL_GL_GetDrawableSize/SDL_GetWindowSize aren't helping as of SDL 2.0.5.
2017-09-19 14:13:04 +02:00
//
// There's not much to be done about it. We're at mercy of SDL here and SDL is at mercy of the WM.
video_new_window(display, w, h, fs, resizable);
2017-09-19 14:13:04 +02:00
return;
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
} else if(video.backend == VIDEO_BACKEND_EMSCRIPTEN && !fs) {
// Needed to work around various SDL bugs and HTML/DOM quirks...
video_new_window(display, w, h, fs, resizable);
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
return;
} else {
SDL_SetWindowSize(video.window, w, h);
video_update_mode_settings();
2017-09-19 14:13:04 +02:00
}
}
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
video_set_fullscreen_internal(fs);
2017-09-19 14:13:04 +02:00
SDL_SetWindowResizable(video.window, resizable);
}
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
void video_set_fullscreen(bool fullscreen) {
video_set_mode(
SDL_GetWindowDisplayIndex(video.window),
video.intended.width,
video.intended.height,
fullscreen,
config_get_int(CONFIG_VID_RESIZABLE)
);
}
void video_set_display(uint idx) {
video_set_mode(
idx,
video.intended.width,
video.intended.height,
config_get_int(CONFIG_FULLSCREEN),
config_get_int(CONFIG_VID_RESIZABLE)
);
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
}
static void* video_screenshot_task(void *arg) {
ScreenshotTaskData *tdata = arg;
OpenGL ES 3.0 rendering backend (#148) * First steps towards shader transpilation Needs to be manually enabled via -Dshader_transpiler=true. Requires shaderc. https://github.com/google/shaderc Not yet functional due to missing SPIRV-Cross integration. SPIRV-Cross currently does not have an official C API, and crossc is too minimal to be useful. The current plan is to extend crossc and vendor it, while also sending PRs upstream. * Integrate crossc; shader transpilation for GLES now works * fix leak * gles30 backend now playable on Mesa with 3.2 context Some rendering issues are present. Identified so far: - Marisa's lasers are invisible - Death effect looks wrong Also, a small pixmap manipulation library has been written, and the texture uploading API redesigned around it. * fix marisa lasers in GLES (uniform name clashed with builtin) * fix player death effect in GLES (another name clash) * Dump ANGLE's translated shader code in debug log * fix screenshots * Drop support for triangle fans, switch to strips Fans offer no advantage over strips, and they've been removed in D3D10+, so ANGLE has to emulate them. * crude workaround for an ANGLE bug * Re-enable GL debug labels, fix an issue with them that affected ANGLE (but was always technically a bug) * fix race condition in shaderc initialization * New SDL_RWops interface for vertex buffers * Optimize VBO streaming via buffering updates Measurable performance improvement even with the main gl33 renderer, drastic improvement with ANGLE. * Fix the depth texture binding problem under ANGLE Apparently it hates GL_DEPTH_COMPONENT16 for some reason. Sized internal formats are not supported in GLES 2.0 anyway, so not using them is probably a good idea. * fix GLES2.0 segfault (the backend still doesn't work, though) * dump GL extensions at info log level, not debug * get around a Mesa bug; more correct texture format table for GLES2 * Correct GLES3 texture format table according to the spec Not a Mesa bug after all * require crossc>=1.5.0, fallback to subproject * Request at least 8bit per color channel in GL backends * Forbid lto for static windows builds with shader_transpiler=true * fix edge case segfault * Add basic ANGLE bundling support to the build system Windows only, and no NSIS support yet * Fix various windows-related build system and installer brokenness * Disable gles backends by default * update documentation
2018-10-02 00:36:10 +02:00
pixmap_convert_inplace_realloc(&tdata->image, PIXMAP_FORMAT_RGB8);
pixmap_flip_to_origin_inplace(&tdata->image, PIXMAP_ORIGIN_BOTTOMLEFT);
uint width = tdata->image.width;
uint height = tdata->image.height;
uint8_t *pixels = tdata->image.data.untyped;
SDL_RWops *output = vfs_open(tdata->dest_path, VFS_MODE_WRITE);
if(!output) {
log_error("VFS error: %s", vfs_get_error());
return NULL;
}
char *syspath = vfs_repr(tdata->dest_path, true);
log_info("Saving screenshot as %s", syspath);
free(syspath);
const char *error = NULL;
png_structp png_ptr;
png_infop info_ptr;
png_byte *volatile row_pointers[height];
memset((void*)row_pointers, 0, sizeof(row_pointers));
png_ptr = pngutil_create_write_struct();
if(png_ptr == NULL) {
error = "pngutil_create_write_struct() failed";
goto done;
}
Implemented a simple and consistent logging subsystem The goal of this change is mainly to clean up Taisei's codebase and improve its console output. I've been frustrated by files littered with inconsistent printf/fprintf/warnx/errx calls for a long time, and now I actually did something about it. All the above functions are now considered deprecated and result in a compile-time warning when used. Instead, the following macros should be used: log_debug(format, ...) log_info(format, ...) log_warn(format, ...) log_err(format, ...) As you can see, all of them have the same printf-like interface. But they have different functionality and purpose: log_debug is intended for very verbose and specific information. It does nothing in release builds, much like assert(), so don't use expressions with side-effects in its arguments. log_info is for various status updates that are expected during normal operation of the program. log_warn is for non-critical failures or other things that may be worth investigating, but don't inherently render the program non-functional. log_err is for when the only choice is to give up. Like errx, it also terminates the program. Unlike errx, it actually calls abort(), which means the cleanup functions are not ran -- but on the other hand, you get a debuggable backtrace. However, if you're trying to catch programming errors, consider using assert() instead. All of them produce output that contains a timestamp, the log level identifier, the calling function's name, and the formatted message. The newline at the end of the format string is not required -- no, it is actually *prohibited*. The logging system will take care of the line breaks by itself, don't litter the code with that shit. Internally, the logging system is based on the SDL_RWops abstraction, and may have multiple, configurable destinations. This makes it easily extensible. Currently, log_debug and log_info are set to write to stdout, log_warn and log_err to stderr, and all of them also to the file log.txt in the Taisei config directory. Consequently, the nasty freopen hacks we used to make Taisei write to log files on Windows are no longer needed -- which is a very good thing, considering they probably would break if the configdir path contains UTF-8 characters. SDL_RWFromFile does not suffer this limitation. As an added bonus, it's also thread-safe. Note about printf and fprintf: in very few cases, the logging system is not a good substitute for these functions. That is, when you care about writing exactly to stdout/stderr and about exactly how the output looks. However, I insist on keeping the deprecation warnings on them to not tempt anyone to use them for logging/debugging out of habit and/or laziness. For this reason, I've added a tsfprintf function to util.c. It is functionally identical to fprintf, except it returns void. Yes, the name is deliberately ugly. Avoid using it if possible, but if you must, only use it to write to stdout or stderr. Do not write to actual files with it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == NULL) {
error = "png_create_info_struct() failed";
goto done;
}
if(setjmp(png_jmpbuf(png_ptr))) {
error = "PNG error";
goto done;
}
png_set_IHDR(
png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT
);
for(int y = 0; y < height; y++) {
row_pointers[y] = png_malloc(png_ptr, width * 3);
memcpy(row_pointers[y], pixels + width * 3 * (height - 1 - y), width * 3);
}
pngutil_init_rwops_write(png_ptr, output);
png_set_rows(png_ptr, info_ptr, (void*)row_pointers);
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
done:
if(error) {
log_error("Couldn't save screenshot: %s", error);
}
for(int y = 0; y < height; y++) {
png_free(png_ptr, row_pointers[y]);
}
if(png_ptr != NULL) {
png_destroy_write_struct(&png_ptr, info_ptr ? &info_ptr : NULL);
}
SDL_RWclose(output);
return NULL;
}
static void video_screenshot_free_task_data(void *arg) {
ScreenshotTaskData *tdata = arg;
OpenGL ES 3.0 rendering backend (#148) * First steps towards shader transpilation Needs to be manually enabled via -Dshader_transpiler=true. Requires shaderc. https://github.com/google/shaderc Not yet functional due to missing SPIRV-Cross integration. SPIRV-Cross currently does not have an official C API, and crossc is too minimal to be useful. The current plan is to extend crossc and vendor it, while also sending PRs upstream. * Integrate crossc; shader transpilation for GLES now works * fix leak * gles30 backend now playable on Mesa with 3.2 context Some rendering issues are present. Identified so far: - Marisa's lasers are invisible - Death effect looks wrong Also, a small pixmap manipulation library has been written, and the texture uploading API redesigned around it. * fix marisa lasers in GLES (uniform name clashed with builtin) * fix player death effect in GLES (another name clash) * Dump ANGLE's translated shader code in debug log * fix screenshots * Drop support for triangle fans, switch to strips Fans offer no advantage over strips, and they've been removed in D3D10+, so ANGLE has to emulate them. * crude workaround for an ANGLE bug * Re-enable GL debug labels, fix an issue with them that affected ANGLE (but was always technically a bug) * fix race condition in shaderc initialization * New SDL_RWops interface for vertex buffers * Optimize VBO streaming via buffering updates Measurable performance improvement even with the main gl33 renderer, drastic improvement with ANGLE. * Fix the depth texture binding problem under ANGLE Apparently it hates GL_DEPTH_COMPONENT16 for some reason. Sized internal formats are not supported in GLES 2.0 anyway, so not using them is probably a good idea. * fix GLES2.0 segfault (the backend still doesn't work, though) * dump GL extensions at info log level, not debug * get around a Mesa bug; more correct texture format table for GLES2 * Correct GLES3 texture format table according to the spec Not a Mesa bug after all * require crossc>=1.5.0, fallback to subproject * Request at least 8bit per color channel in GL backends * Forbid lto for static windows builds with shader_transpiler=true * fix edge case segfault * Add basic ANGLE bundling support to the build system Windows only, and no NSIS support yet * Fix various windows-related build system and installer brokenness * Disable gles backends by default * update documentation
2018-10-02 00:36:10 +02:00
free(tdata->image.data.untyped);
free(tdata->dest_path);
free(tdata);
}
void video_take_screenshot(void) {
ScreenshotTaskData tdata;
memset(&tdata, 0, sizeof(tdata));
OpenGL ES 3.0 rendering backend (#148) * First steps towards shader transpilation Needs to be manually enabled via -Dshader_transpiler=true. Requires shaderc. https://github.com/google/shaderc Not yet functional due to missing SPIRV-Cross integration. SPIRV-Cross currently does not have an official C API, and crossc is too minimal to be useful. The current plan is to extend crossc and vendor it, while also sending PRs upstream. * Integrate crossc; shader transpilation for GLES now works * fix leak * gles30 backend now playable on Mesa with 3.2 context Some rendering issues are present. Identified so far: - Marisa's lasers are invisible - Death effect looks wrong Also, a small pixmap manipulation library has been written, and the texture uploading API redesigned around it. * fix marisa lasers in GLES (uniform name clashed with builtin) * fix player death effect in GLES (another name clash) * Dump ANGLE's translated shader code in debug log * fix screenshots * Drop support for triangle fans, switch to strips Fans offer no advantage over strips, and they've been removed in D3D10+, so ANGLE has to emulate them. * crude workaround for an ANGLE bug * Re-enable GL debug labels, fix an issue with them that affected ANGLE (but was always technically a bug) * fix race condition in shaderc initialization * New SDL_RWops interface for vertex buffers * Optimize VBO streaming via buffering updates Measurable performance improvement even with the main gl33 renderer, drastic improvement with ANGLE. * Fix the depth texture binding problem under ANGLE Apparently it hates GL_DEPTH_COMPONENT16 for some reason. Sized internal formats are not supported in GLES 2.0 anyway, so not using them is probably a good idea. * fix GLES2.0 segfault (the backend still doesn't work, though) * dump GL extensions at info log level, not debug * get around a Mesa bug; more correct texture format table for GLES2 * Correct GLES3 texture format table according to the spec Not a Mesa bug after all * require crossc>=1.5.0, fallback to subproject * Request at least 8bit per color channel in GL backends * Forbid lto for static windows builds with shader_transpiler=true * fix edge case segfault * Add basic ANGLE bundling support to the build system Windows only, and no NSIS support yet * Fix various windows-related build system and installer brokenness * Disable gles backends by default * update documentation
2018-10-02 00:36:10 +02:00
if(!r_screenshot(&tdata.image)) {
log_error("Failed to take a screenshot");
return;
}
SystemTime systime;
char timestamp[FILENAME_TIMESTAMP_MIN_BUF_SIZE];
get_system_time(&systime);
filename_timestamp(timestamp, sizeof(timestamp), systime);
tdata.dest_path = strfmt("storage/screenshots/taisei_%s.png", timestamp);
task_detach(taskmgr_global_submit((TaskParams) {
.callback = video_screenshot_task,
.userdata = memdup(&tdata, sizeof(tdata)),
.userdata_free_callback = video_screenshot_free_task_data,
}));
}
2017-09-19 16:11:42 +02:00
bool video_is_resizable(void) {
return SDL_GetWindowFlags(video.window) & SDL_WINDOW_RESIZABLE;
}
2017-09-19 16:11:42 +02:00
bool video_is_fullscreen(void) {
2017-09-19 14:13:04 +02:00
return WINFLAGS_IS_FULLSCREEN(SDL_GetWindowFlags(video.window));
}
static void video_init_sdl(void) {
// XXX: workaround for an SDL bug: https://bugzilla.libsdl.org/show_bug.cgi?id=4127
SDL_SetHintWithPriority(SDL_HINT_FRAMEBUFFER_ACCELERATION, "0", SDL_HINT_OVERRIDE);
uint num_drivers = SDL_GetNumVideoDrivers();
void *buf;
SDL_RWops *out = SDL_RWAutoBuffer(&buf, 256);
SDL_RWprintf(out, "Available video drivers:");
for(uint i = 0; i < num_drivers; ++i) {
SDL_RWprintf(out, " %s", SDL_GetVideoDriver(i));
}
SDL_WriteU8(out, 0);
log_info("%s", (char*)buf);
SDL_RWclose(out);
const char *prefer_drivers = env_get("TAISEI_PREFER_SDL_VIDEODRIVERS", "");
const char *force_driver = env_get("TAISEI_VIDEO_DRIVER", "");
if(*force_driver) {
2017-11-11 20:37:24 +01:00
log_warn("TAISEI_VIDEO_DRIVER is deprecated and will be removed, use TAISEI_PREFER_SDL_VIDEODRIVERS or SDL_VIDEODRIVER instead");
} else {
force_driver = env_get("SDL_VIDEODRIVER", "");
2017-11-11 20:37:24 +01:00
}
2017-11-11 20:37:24 +01:00
if(!(prefer_drivers && *prefer_drivers)) {
// https://bugzilla.libsdl.org/show_bug.cgi?id=3948
// A suboptimal X11 server may be available on top of those systems,
// so we push X11 down in the priority list.
prefer_drivers = "wayland,cocoa,windows,x11";
}
if(prefer_drivers && *prefer_drivers && !*force_driver) {
2017-11-11 20:37:24 +01:00
char buf[strlen(prefer_drivers) + 1];
char *driver, *bufptr = buf;
int drivernum = 0;
strcpy(buf, prefer_drivers);
2017-11-11 20:37:24 +01:00
while((driver = strtok_r(NULL, " :;,", &bufptr))) {
++drivernum;
log_info("Trying preferred driver #%i: %s", drivernum, driver);
2017-11-11 20:37:24 +01:00
if(SDL_VideoInit(driver)) {
log_info("Driver '%s' failed: %s", driver, SDL_GetError());
} else {
return;
}
}
2017-11-11 20:37:24 +01:00
if(drivernum) {
log_info("All preferred drivers failed, falling back to SDL default");
}
}
2017-11-11 20:37:24 +01:00
if(SDL_VideoInit(force_driver)) {
log_fatal("SDL_VideoInit() failed: %s", SDL_GetError());
}
}
static void video_handle_resize(int w, int h) {
int minw, minh;
SDL_GetWindowMinimumSize(video.window, &minw, &minh);
if(w < minw || h < minh) {
log_warn("Bad resize: %ix%i is too small!", w, h);
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
// FIXME: the video_new_window is actually a workaround for Wayland.
// I'm not sure if it's necessary for anything else.
video_new_window(video_current_display(), video.intended.width, video.intended.height, false, video_is_resizable());
return;
}
log_debug("%ix%i --> %ix%i", video.current.width, video.current.height, w, h);
video.current.width = w;
video.current.height = h;
video_set_viewport();
events_emit(TE_VIDEO_MODE_CHANGED, 0, NULL, NULL);
}
static bool video_handle_window_event(SDL_Event *event, void *arg) {
switch(event->window.event) {
case SDL_WINDOWEVENT_RESIZED:
video_handle_resize(event->window.data1, event->window.data2);
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
if(config_get_int(CONFIG_FOCUS_LOSS_PAUSE)) {
events_emit(TE_GAME_PAUSE, 0, NULL, NULL);
}
break;
}
return true;
}
static bool video_handle_config_event(SDL_Event *evt, void *arg) {
ConfigValue *val = evt->user.data1;
switch(evt->user.code) {
case CONFIG_FULLSCREEN:
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
video_set_fullscreen(val->i);
break;
case CONFIG_VID_DISPLAY:
video_set_display(val->i);
break;
case CONFIG_VID_RESIZABLE:
SDL_SetWindowResizable(video.window, val->i);
break;
case CONFIG_VSYNC:
video_update_vsync();
break;
}
return false;
}
uint video_num_displays(void) {
int displays = SDL_GetNumVideoDisplays();
if(displays < 1) {
if(displays == 0) {
log_warn("SDL_GetNumVideoDisplays() returned 0, this shouldn't happen");
} else {
log_sdl_error(LOG_WARN, "SDL_GetNumVideoDisplays");
}
displays = 1;
}
return displays;
}
const char *video_display_name(uint id) {
assert(id < video_num_displays());
const char *name = SDL_GetDisplayName(id);
if(name == NULL) {
log_sdl_error(LOG_WARN, "SDL_GetDisplayName");
name = "Unknown";
}
return name;
}
uint video_current_display(void) {
int display = SDL_GetWindowDisplayIndex(video.window);
if(display < 0) {
log_sdl_error(LOG_WARN, "SDL_GetWindowDisplayIndex");
display = 1;
}
return display;
}
2012-07-28 22:53:53 +02:00
void video_init(void) {
bool fullscreen_available = false;
2017-02-04 03:56:40 +01:00
video_init_sdl();
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
const char *driver = SDL_GetCurrentVideoDriver();
log_info("Using driver '%s'", driver);
video_query_capability = video_query_capability_generic;
if(!strcmp(driver, "x11")) {
video.backend = VIDEO_BACKEND_X11;
} else if(!strcmp(driver, "emscripten")) {
video.backend = VIDEO_BACKEND_EMSCRIPTEN;
video_query_capability = video_query_capability_webcanvas;
// We can not start in fullscreen in the browser properly, so disable it here.
// Fullscreen is still accessible via the settings menu and the shortcut key.
config_set_int(CONFIG_FULLSCREEN, false);
} else if(!strcmp(driver, "KMSDRM")) {
video.backend = VIDEO_BACKEND_KMSDRM;
video_query_capability = video_query_capability_alwaysfullscreen;
} else if(!strcmp(driver, "RPI")) {
video.backend = VIDEO_BACKEND_RPI;
video_query_capability = video_query_capability_alwaysfullscreen;
} else if(!strcmp(driver, "Switch")) {
video.backend = VIDEO_BACKEND_SWITCH;
video_query_capability = video_query_capability_alwaysfullscreen;
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
} else {
video.backend = VIDEO_BACKEND_OTHER;
}
r_init();
// Register all resolutions that are available in fullscreen
for(int s = 0; s < video_num_displays(); ++s) {
log_info("Found display #%i: %s", s, video_display_name(s));
for(int i = 0; i < SDL_GetNumDisplayModes(s); ++i) {
2017-02-04 03:56:40 +01:00
SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
2017-02-04 03:56:40 +01:00
if(SDL_GetDisplayMode(s, i, &mode) != 0) {
log_sdl_error(LOG_WARN, "SDL_GetDisplayMode");
2017-02-04 03:56:40 +01:00
} else {
video_add_mode(mode.w, mode.h);
fullscreen_available = true;
2017-02-04 03:56:40 +01:00
}
}
}
if(!fullscreen_available) {
Implemented a simple and consistent logging subsystem The goal of this change is mainly to clean up Taisei's codebase and improve its console output. I've been frustrated by files littered with inconsistent printf/fprintf/warnx/errx calls for a long time, and now I actually did something about it. All the above functions are now considered deprecated and result in a compile-time warning when used. Instead, the following macros should be used: log_debug(format, ...) log_info(format, ...) log_warn(format, ...) log_err(format, ...) As you can see, all of them have the same printf-like interface. But they have different functionality and purpose: log_debug is intended for very verbose and specific information. It does nothing in release builds, much like assert(), so don't use expressions with side-effects in its arguments. log_info is for various status updates that are expected during normal operation of the program. log_warn is for non-critical failures or other things that may be worth investigating, but don't inherently render the program non-functional. log_err is for when the only choice is to give up. Like errx, it also terminates the program. Unlike errx, it actually calls abort(), which means the cleanup functions are not ran -- but on the other hand, you get a debuggable backtrace. However, if you're trying to catch programming errors, consider using assert() instead. All of them produce output that contains a timestamp, the log level identifier, the calling function's name, and the formatted message. The newline at the end of the format string is not required -- no, it is actually *prohibited*. The logging system will take care of the line breaks by itself, don't litter the code with that shit. Internally, the logging system is based on the SDL_RWops abstraction, and may have multiple, configurable destinations. This makes it easily extensible. Currently, log_debug and log_info are set to write to stdout, log_warn and log_err to stderr, and all of them also to the file log.txt in the Taisei config directory. Consequently, the nasty freopen hacks we used to make Taisei write to log files on Windows are no longer needed -- which is a very good thing, considering they probably would break if the configdir path contains UTF-8 characters. SDL_RWFromFile does not suffer this limitation. As an added bonus, it's also thread-safe. Note about printf and fprintf: in very few cases, the logging system is not a good substitute for these functions. That is, when you care about writing exactly to stdout/stderr and about exactly how the output looks. However, I insist on keeping the deprecation warnings on them to not tempt anyone to use them for logging/debugging out of habit and/or laziness. For this reason, I've added a tsfprintf function to util.c. It is functionally identical to fprintf, except it returns void. Yes, the name is deliberately ugly. Avoid using it if possible, but if you must, only use it to write to stdout or stderr. Do not write to actual files with it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
log_warn("No available fullscreen modes");
2017-02-17 17:03:49 +01:00
config_set_int(CONFIG_FULLSCREEN, false);
2012-07-28 22:53:53 +02:00
}
2017-02-04 03:56:40 +01:00
2012-07-28 22:53:53 +02:00
// Then, add some common 4:3 modes for the windowed mode if they are not there yet.
// This is required for some multihead setups.
VideoMode common_modes[] = {
{RESX, RESY},
{SCREEN_W, SCREEN_H},
{640, 480},
{800, 600},
{1024, 768},
{1280, 960},
{1152, 864},
{1400, 1050},
{1440, 1080},
{0, 0},
};
if(video_query_capability(VIDEO_CAP_FULLSCREEN) != VIDEO_ALWAYS_ENABLED) {
for(int i = 0; common_modes[i].width; ++i) {
video_add_mode(common_modes[i].width, common_modes[i].height);
}
}
2012-07-28 22:53:53 +02:00
// sort it, mainly for the options menu
qsort(video.modes, video.mcount, sizeof(VideoMode), video_compare_modes);
2017-05-05 03:49:00 +02:00
video_set_mode(
config_get_int(CONFIG_VID_DISPLAY),
config_get_int(CONFIG_VID_WIDTH),
config_get_int(CONFIG_VID_HEIGHT),
config_get_int(CONFIG_FULLSCREEN),
config_get_int(CONFIG_VID_RESIZABLE)
);
events_register_handler(&(EventHandler) {
.proc = video_handle_window_event,
.priority = EPRIO_SYSTEM,
.event_type = SDL_WINDOWEVENT,
});
events_register_handler(&(EventHandler) {
.proc = video_handle_config_event,
.priority = EPRIO_SYSTEM,
.event_type = MAKE_TAISEI_EVENT(TE_CONFIG_UPDATED),
});
log_info("Video subsystem initialized");
2012-07-28 22:53:53 +02:00
}
void video_post_init(void) {
fbmgr_init();
v_postprocess = video_postprocess_init();
r_framebuffer(video_get_screen_framebuffer());
}
2012-07-28 22:53:53 +02:00
void video_shutdown(void) {
video_postprocess_shutdown(v_postprocess);
fbmgr_shutdown();
events_unregister_handler(video_handle_window_event);
events_unregister_handler(video_handle_config_event);
2017-02-04 03:56:40 +01:00
SDL_DestroyWindow(video.window);
r_shutdown();
2012-07-28 22:53:53 +02:00
free(video.modes);
SDL_VideoQuit();
2012-07-28 22:53:53 +02:00
}
Framebuffer *video_get_screen_framebuffer(void) {
return video_postprocess_get_framebuffer(v_postprocess);
}
void video_swap_buffers(void) {
Framebuffer *pp_fb = video_postprocess_render(v_postprocess);
if(pp_fb) {
r_flush_sprites();
r_state_push();
r_mat_mode(MM_PROJECTION);
r_mat_push();
set_ortho(SCREEN_W, SCREEN_H);
r_mat_mode(MM_MODELVIEW);
r_framebuffer(NULL);
r_shader_standard();
r_color3(1, 1, 1);
draw_framebuffer_tex(pp_fb, SCREEN_W, SCREEN_H);
r_framebuffer_clear(pp_fb, CLEAR_ALL, RGBA(0, 0, 0, 0), 1);
r_mat_mode(MM_PROJECTION);
r_mat_pop();
r_state_pop();
}
r_swap(video.window);
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
// XXX: Unfortunately, there seems to be no reliable way to sync this up with events
config_set_int(CONFIG_FULLSCREEN, video_is_fullscreen());
}