diff --git a/.gitignore b/.gitignore index 89426b7bff..bc65d666b0 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,10 @@ menu/driverspzarch.c .pc /media/shaders_glsl/ /obj-w32/ +.cproject +.settings +libretro-super +run.sh # Wii U *.depend diff --git a/.project b/.project index 871dee54b0..b5f91e201f 100644 --- a/.project +++ b/.project @@ -5,7 +5,23 @@ + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature diff --git a/CHANGES.md b/CHANGES.md index 3674186a41..c9ed1b06de 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,12 @@ # 1.7.4 (future) - ANDROID: Add sustained performance mode, can be turned on/off in Power Management settings menu. - ANDROID: Powerstate/battery level support. +- CHEEVOS: Fix crash when scrolling Achievement List while Unofficial Achievements enabled (#6732). +- CHEEVOS: Added hitcounts support for PauseIf/ResetIf (#6817). - COMMON: Automatically hide "Configuration Override options" in Quick Menu. - COMMON: Small Bugfix to not trigger savestate code when pressing Reset. +- COMMON: Added libsixel video driver. +- EMSCRIPTEN: Fix Game Focus Toggle. - HID/OSX: Fix to set hid device registration deterministic (#6497), to address issue #6640 re-adding dynamic device registration. - LOCALIZATION: Update Italian translation. - LOCALIZATION: Update Japanese translation. @@ -14,8 +18,10 @@ - MIDI: Add a Windows driver for MIDI, based on winmm. - MENU/QT/WIMP: QT QSlider styling for Dark Theme. - MENU/QT/WIMP: Remove button ghostly inside highlighting. -- METAL: Initial work-in-progress video driver for Metal. macOS-only right now, and currently requires macOS 10.13. Works with RGUI right now. +- METAL: Initial work-in-progress video driver for Metal. macOS-only right now, and currently requires macOS 10.13. +- METAL: Supports XMB/MaterialUI, has a menu display driver. Has a font rendering driver. - METAL/SLANG: Slang shaders should be compatible with Metal video driver. +- QNX: Fix Game Focus Toggle. - PS3: Add audio mixer support for FLAC and MP3. - PSP: Use proper button labels, fix inverted R-Stick Y axis. - REMAPS: Fix the way offsets are calculated for keyboard remapping. @@ -24,9 +30,14 @@ - VULKAN: Fix two validation errors. - VULKAN: Try to avoid creating swapchains redundantly. Should fix black screen and having to alt tab out of window again to get display working on Nvidia GPUs (Windows). - VULKAN/OSX: Initial MoltenVK support. Not enabled yet, several MoltenVK bugs should be fixed first before we can have it fully working. +- WINDOWS/DINPUT: Add rumble support. +- WINDOWS/DINPUT: Fix Game Focus Toggle. +- WINDOWS/RAWINPUT: Fix Game Focus Toggle. +- X11: Fix Game Focus Toggle. - WII: Change deflicker setting to work in 480p or higher, and always enables vfilter so that the user can easily change brightness. - WIIU: Fix out-of-bounds rendering bug - WIIU: Implement UDP broadcast network logging on Wii U. +- WIIU: Audio should no longer clip. # 1.7.3 - AUDIO: Audio mixer supports FLAC/MP3 file types now! diff --git a/Makefile.common b/Makefile.common index bb29f29791..b2c83a60b8 100644 --- a/Makefile.common +++ b/Makefile.common @@ -743,11 +743,16 @@ ifeq ($(HW_CONTEXT_MENU_DRIVERS), 1) ifeq ($(HAVE_XMB),) HAVE_XMB = 1 endif + + ifeq ($(HAVE_STRIPES),) + HAVE_STRIPES = 1 + endif else HAVE_ZARCH ?= 0 HAVE_MATERIALUI ?= 0 #HAVE_NUKLEAR ?= 0 HAVE_XMB ?= 0 + HAVE_STRIPES ?= 0 endif ifeq ($(HAVE_RGUI), 1) @@ -779,6 +784,12 @@ ifeq ($(HAVE_XMB), 1) HAVE_MENU_COMMON = 1 endif +ifeq ($(HAVE_STRIPES), 1) + OBJ += menu/drivers/stripes.o + DEFINES += -DHAVE_STRIPES + HAVE_MENU_COMMON = 1 +endif + ifeq ($(HAVE_LAKKA), 1) DEFINES += -DHAVE_LAKKA endif @@ -1046,6 +1057,18 @@ ifeq ($(HAVE_CACA), 1) endif endif +ifeq ($(HAVE_SIXEL), 1) + DEFINES += -DHAVE_SIXEL + CFLAGS += -I/usr/include/sixel + OBJ += gfx/drivers/sixel_gfx.o gfx/drivers_font/sixel_font.o \ + gfx/drivers_context/sixel_ctx.o + LIBS += -lsixel + + ifeq ($(HAVE_MENU_COMMON), 1) + OBJ += menu/drivers_display/menu_display_sixel.o + endif +endif + ifeq ($(HAVE_PLAIN_DRM), 1) OBJ += gfx/drivers/drm_gfx.o CFLAGS += -I/usr/include/libdrm @@ -1107,6 +1130,20 @@ endif endif endif +ifeq ($(HAVE_METAL), 1) + DEFINES += -DHAVE_METAL + OBJ += gfx/common/metal/Context.o \ + gfx/common/metal/Filter.o \ + gfx/common/metal/RendererCommon.o \ + gfx/common/metal/View.o \ + gfx/common/metal/TexturedView.o \ + gfx/common/metal/MenuDisplay.o \ + gfx/common/metal_common.o \ + gfx/drivers/metal.o \ + menu/drivers_display/menu_display_metal.o \ + gfx/drivers_font/metal_raster_font.o +endif + ifeq ($(HAVE_MPV), 1) OBJ += cores/libretro-mpv/mpv-libretro.o DEFINES += -I$(DEPS_DIR) -DHAVE_MPV diff --git a/audio/drivers/wiiu_audio.c b/audio/drivers/wiiu_audio.c index 437ee310bf..0ea9106206 100644 --- a/audio/drivers/wiiu_audio.c +++ b/audio/drivers/wiiu_audio.c @@ -84,7 +84,7 @@ static void* ax_audio_init(const char* device, unsigned rate, unsigned latency, u16 setup_buf[0x30] = {0}; setup_buf[0x25] = 2; /* we request 2 channels */ AXInitParams init = {AX_INIT_RENDERER_48KHZ, 0, 0}; - AXVoiceVeData ve = {0xF000, 0}; + AXVoiceVeData ve = {0x8000, 0}; ax_audio_t* ax = (ax_audio_t*)calloc(1, sizeof(ax_audio_t)); if (!ax) diff --git a/command.c b/command.c index 23fa89a565..5775e4f6a2 100644 --- a/command.c +++ b/command.c @@ -1051,8 +1051,11 @@ static void command_event_deinit_core(bool reinit) cheevos_unload(); #endif + RARCH_LOG("Unloading game..\n"); core_unload_game(); + RARCH_LOG("Unloading core..\n"); core_unload(); + RARCH_LOG("Unloading core symbols..\n"); core_uninit_symbols(); if (reinit) @@ -1395,7 +1398,6 @@ static bool command_event_save_config( static bool command_event_save_core_config(void) { char msg[128]; - bool ret = false; bool found_path = false; bool overrides_active = false; const char *core_path = NULL; @@ -1502,7 +1504,7 @@ static bool command_event_save_core_config(void) free(config_dir); free(config_name); free(config_path); - return ret; + return true; } /** @@ -1835,14 +1837,21 @@ bool command_event(enum event_command cmd, void *data) if (cheevos_hardcore_active) return false; #endif - - return command_event_main_state(cmd); + if (!command_event_main_state(cmd)) + return false; + break; case CMD_EVENT_UNDO_LOAD_STATE: - return command_event_main_state(cmd); + if (!command_event_main_state(cmd)) + return false; + break; case CMD_EVENT_UNDO_SAVE_STATE: - return command_event_main_state(cmd); + if (!command_event_main_state(cmd)) + return false; + break; case CMD_EVENT_RESIZE_WINDOWED_SCALE: - return command_event_resize_windowed_scale(); + if (!command_event_resize_windowed_scale()) + return false; + break; case CMD_EVENT_MENU_TOGGLE: #ifdef HAVE_MENU if (menu_driver_is_alive()) @@ -1883,7 +1892,9 @@ bool command_event(enum event_command cmd, void *data) configuration_set_int(settings, settings->ints.state_slot, new_state_slot); } } - return command_event_main_state(cmd); + if (!command_event_main_state(cmd)) + return false; + break; case CMD_EVENT_SAVE_STATE_DECREMENT: { settings_t *settings = config_get_ptr(); @@ -1933,7 +1944,9 @@ bool command_event(enum event_command cmd, void *data) } break; case CMD_EVENT_QUIT: - return retroarch_main_quit(); + if (!retroarch_main_quit()) + return false; + break; case CMD_EVENT_CHEEVOS_HARDCORE_MODE_TOGGLE: #ifdef HAVE_CHEEVOS cheevos_toggle_hardcore_mode(); @@ -1997,7 +2010,7 @@ TODO: Add a setting for these tweaks */ if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) #endif { - state_manager_event_init((unsigned)settings->rewind_buffer_size); + state_manager_event_init((unsigned)settings->sizes.rewind_buffer_size); } } } @@ -2043,9 +2056,13 @@ TODO: Add a setting for these tweaks */ break; case CMD_EVENT_AUDIO_STOP: midi_driver_set_all_sounds_off(); - return audio_driver_stop(); + if (!audio_driver_stop()) + return false; + break; case CMD_EVENT_AUDIO_START: - return audio_driver_start(rarch_ctl(RARCH_CTL_IS_SHUTDOWN, NULL)); + if (!audio_driver_start(rarch_ctl(RARCH_CTL_IS_SHUTDOWN, NULL))) + return false; + break; case CMD_EVENT_AUDIO_MUTE_TOGGLE: { bool audio_mute_enable = *(audio_get_bool_ptr(AUDIO_ACTION_MUTE_ENABLE)); @@ -2630,14 +2647,13 @@ TODO: Add a setting for these tweaks */ command_event(CMD_EVENT_REMOTE_DEINIT, NULL); input_driver_init_remote(); break; - case CMD_EVENT_MAPPER_DEINIT: input_driver_deinit_mapper(); break; case CMD_EVENT_MAPPER_INIT: command_event(CMD_EVENT_MAPPER_DEINIT, NULL); input_driver_init_mapper(); - break; + break; case CMD_EVENT_LOG_FILE_DEINIT: retro_main_log_file_deinit(); break; @@ -2646,8 +2662,10 @@ TODO: Add a setting for these tweaks */ const char *path = (const char*)data; if (string_is_empty(path)) return false; - return command_event_disk_control_append_image(path); + if (!command_event_disk_control_append_image(path)) + return false; } + break; case CMD_EVENT_DISK_EJECT_TOGGLE: { rarch_system_info_t *info = runloop_get_system_info(); @@ -2754,10 +2772,8 @@ TODO: Add a setting for these tweaks */ } break; case CMD_EVENT_UI_COMPANION_TOGGLE: - { - ui_companion_driver_toggle(true); - break; - } + ui_companion_driver_toggle(true); + break; case CMD_EVENT_GAME_FOCUS_TOGGLE: { static bool game_focus_state = false; diff --git a/config.def.h b/config.def.h index 546e7bebfd..189449b793 100644 --- a/config.def.h +++ b/config.def.h @@ -524,6 +524,9 @@ static const bool rewind_enable = false; * 15-20MB per minute. Very game dependant. */ static const unsigned rewind_buffer_size = 20 << 20; /* 20MiB */ +/* The amount of MB to increase/decrease the rewind_buffer_size when it is changed via the UI. */ +static const unsigned rewind_buffer_size_step = 10; /* 10MB */ + /* How many frames to rewind at a time. */ static const unsigned rewind_granularity = 1; @@ -701,12 +704,16 @@ static const unsigned midi_volume = 100; static const bool sustained_performance_mode = false; #if defined(ANDROID) -#if defined(ANDROID_ARM) +#if defined(ANDROID_ARM_V7) static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/armeabi-v7a/"; +#elif defined(ANDROID_ARM) +static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/armeabi/"; #elif defined(ANDROID_AARCH64) static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/arm64-v8a/"; #elif defined(ANDROID_X86) static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/x86/"; +#elif defined(ANDROID_X64) +static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/x86_64/"; #else static char buildbot_server_url[] = ""; #endif diff --git a/configuration.c b/configuration.c index 13da1997f1..63e3d29d85 100644 --- a/configuration.c +++ b/configuration.c @@ -92,6 +92,16 @@ struct config_uint_setting enum rarch_override_setting override; }; +struct config_size_setting +{ + const char *ident; + size_t *ptr; + bool def_enable; + size_t def; + bool handle; + enum rarch_override_setting override; +}; + struct config_float_setting { const char *ident; @@ -125,6 +135,7 @@ enum video_driver_enum { VIDEO_GL = 0, VIDEO_VULKAN, + VIDEO_METAL, VIDEO_DRM, VIDEO_XVIDEO, VIDEO_SDL, @@ -273,6 +284,7 @@ enum menu_driver_enum MENU_XUI, MENU_MATERIALUI, MENU_XMB, + MENU_STRIPES, MENU_NUKLEAR, MENU_NULL }; @@ -291,6 +303,8 @@ enum midi_driver_enum #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) || defined(__CELLOS_LV2__) static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_GL; +#elif defined(HAVE_METAL) +static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_METAL; #elif defined(GEKKO) static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_WII; #elif defined(WIIU) @@ -515,6 +529,8 @@ static enum location_driver_enum LOCATION_DEFAULT_DRIVER = LOCATION_NULL; static enum menu_driver_enum MENU_DEFAULT_DRIVER = MENU_XUI; #elif defined(HAVE_MATERIALUI) && defined(RARCH_MOBILE) static enum menu_driver_enum MENU_DEFAULT_DRIVER = MENU_MATERIALUI; +#elif defined(HAVE_STRIPES) && !defined(_XBOX) +static enum menu_driver_enum MENU_DEFAULT_DRIVER = MENU_STRIPES; #elif defined(HAVE_XMB) && !defined(_XBOX) static enum menu_driver_enum MENU_DEFAULT_DRIVER = MENU_XMB; #elif defined(HAVE_RGUI) @@ -547,6 +563,9 @@ static enum menu_driver_enum MENU_DEFAULT_DRIVER = MENU_NULL; #define SETTING_UINT(key, configval, default_enable, default_setting, handle_setting) \ GENERAL_SETTING(key, configval, default_enable, default_setting, struct config_uint_setting, handle_setting) +#define SETTING_SIZE(key, configval, default_enable, default_setting, handle_setting) \ + GENERAL_SETTING(key, configval, default_enable, default_setting, struct config_size_setting, handle_setting) + #define SETTING_PATH(key, configval, default_enable, default_setting, handle_setting) \ GENERAL_SETTING(key, configval, default_enable, default_setting, struct config_path_setting, handle_setting) @@ -711,6 +730,8 @@ const char *config_get_default_video(void) return "gl"; case VIDEO_VULKAN: return "vulkan"; + case VIDEO_METAL: + return "metal"; case VIDEO_DRM: return "drm"; case VIDEO_WII: @@ -1008,6 +1029,8 @@ const char *config_get_default_menu(void) return "glui"; case MENU_XMB: return "xmb"; + case MENU_STRIPES: + return "stripes"; case MENU_NUKLEAR: return "nuklear"; case MENU_NULL: @@ -1503,6 +1526,7 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, SETTING_UINT("audio_resampler_quality", &settings->uints.audio_resampler_quality, true, audio_resampler_quality_level, false); SETTING_UINT("audio_block_frames", &settings->uints.audio_block_frames, true, 0, false); SETTING_UINT("rewind_granularity", &settings->uints.rewind_granularity, true, rewind_granularity, false); + SETTING_UINT("rewind_buffer_size_step", &settings->uints.rewind_buffer_size_step, true, rewind_buffer_size_step, false); SETTING_UINT("autosave_interval", &settings->uints.autosave_interval, true, autosave_interval, false); SETTING_UINT("libretro_log_level", &settings->uints.libretro_log_level, true, libretro_log_level, false); SETTING_UINT("keyboard_gamepad_mapping_type",&settings->uints.input_keyboard_gamepad_mapping_type, true, 1, false); @@ -1579,6 +1603,18 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, return tmp; } +static struct config_size_setting *populate_settings_size(settings_t *settings, int *size) +{ + unsigned count = 0; + struct config_size_setting *tmp = (struct config_size_setting*)malloc((*size + 1) * sizeof(struct config_size_setting)); + + SETTING_SIZE("rewind_buffer_size", &settings->sizes.rewind_buffer_size, true, rewind_buffer_size, false); + + *size = count; + + return tmp; +} + static struct config_int_setting *populate_settings_int(settings_t *settings, int *size) { unsigned count = 0; @@ -1614,6 +1650,7 @@ static void config_set_defaults(void) int float_settings_size = sizeof(settings->floats) / sizeof(settings->floats.placeholder); int int_settings_size = sizeof(settings->ints) / sizeof(settings->ints.placeholder); int uint_settings_size = sizeof(settings->uints) / sizeof(settings->uints.placeholder); + int size_settings_size = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder); const char *def_video = config_get_default_video(); const char *def_audio = config_get_default_audio(); const char *def_audio_resampler = config_get_default_audio_resampler(); @@ -1633,6 +1670,7 @@ static void config_set_defaults(void) struct config_bool_setting *bool_settings = populate_settings_bool (settings, &bool_settings_size); struct config_int_setting *int_settings = populate_settings_int (settings, &int_settings_size); struct config_uint_setting *uint_settings = populate_settings_uint (settings, &uint_settings_size); + struct config_size_setting *size_settings = populate_settings_size (settings, &size_settings_size); if (bool_settings && (bool_settings_size > 0)) { @@ -1667,6 +1705,17 @@ static void config_set_defaults(void) free(uint_settings); } + if (size_settings && (size_settings_size > 0)) + { + for (i = 0; i < (unsigned)size_settings_size; i++) + { + if (size_settings[i].def_enable) + *size_settings[i].ptr = size_settings[i].def; + } + + free(size_settings); + } + if (float_settings && (float_settings_size > 0)) { for (i = 0; i < (unsigned)float_settings_size; i++) @@ -1758,8 +1807,6 @@ static void config_set_defaults(void) audio_set_float(AUDIO_ACTION_VOLUME_GAIN, settings->floats.audio_volume); audio_set_float(AUDIO_ACTION_MIXER_VOLUME_GAIN, settings->floats.audio_mixer_volume); - settings->rewind_buffer_size = rewind_buffer_size; - #ifdef HAVE_LAKKA settings->bools.ssh_enable = filestream_exists(LAKKA_SSH_PATH); settings->bools.samba_enable = filestream_exists(LAKKA_SAMBA_PATH); @@ -2357,6 +2404,7 @@ static bool check_shader_compatibility(enum file_path_enum enum_idx) settings_t *settings = config_get_ptr(); if (string_is_equal(settings->arrays.video_driver, "vulkan") || + string_is_equal(settings->arrays.video_driver, "metal") || string_is_equal(settings->arrays.video_driver, "d3d11") || string_is_equal(settings->arrays.video_driver, "d3d12") || string_is_equal(settings->arrays.video_driver, "gx2")) @@ -2457,12 +2505,14 @@ static bool config_load_file(const char *path, bool set_defaults, int float_settings_size = sizeof(settings->floats) / sizeof(settings->floats.placeholder); int int_settings_size = sizeof(settings->ints) / sizeof(settings->ints.placeholder); int uint_settings_size = sizeof(settings->uints) / sizeof(settings->uints.placeholder); + int size_settings_size = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder); int array_settings_size = sizeof(settings->arrays) / sizeof(settings->arrays.placeholder); int path_settings_size = sizeof(settings->paths) / sizeof(settings->paths.placeholder); struct config_bool_setting *bool_settings = populate_settings_bool (settings, &bool_settings_size); struct config_float_setting *float_settings = populate_settings_float (settings, &float_settings_size); struct config_int_setting *int_settings = populate_settings_int (settings, &int_settings_size); struct config_uint_setting *uint_settings = populate_settings_uint (settings, &uint_settings_size); + struct config_size_setting *size_settings = populate_settings_size (settings, &size_settings_size); struct config_array_setting *array_settings = populate_settings_array (settings, &array_settings_size); struct config_path_setting *path_settings = populate_settings_path (settings, &path_settings_size); @@ -2587,6 +2637,23 @@ static bool config_load_file(const char *path, bool set_defaults, *uint_settings[i].ptr = tmp; } + for (i = 0; i < (unsigned)size_settings_size; i++) + { + size_t tmp = 0; + if (config_get_size_t(conf, size_settings[i].ident, &tmp)) + *size_settings[i].ptr = tmp ; + /* Special case for rewind_buffer_size - need to convert low values to what they were + * intended to be based on the default value in config.def.h + * If the value is less than 10000 then multiple by 1MB because if the retroarch.cfg + * file contains rewind_buffer_size = "100" then that ultimately gets interpreted as + * 100MB, so ensure the internal values represent that.*/ + if ( strcmp(size_settings[i].ident, "rewind_buffer_size") == 0 ) { + if ( *size_settings[i].ptr < 10000) { + *size_settings[i].ptr = *size_settings[i].ptr * 1024 * 1024 ; + } + } + } + for (i = 0; i < MAX_USERS; i++) { char buf[64]; @@ -2618,13 +2685,6 @@ static bool config_load_file(const char *path, bool set_defaults, CONFIG_GET_INT_BASE(conf, settings, uints.led_map[i], buf); } - { - /* ugly hack around C89 not allowing mixing declarations and code */ - int buffer_size = 0; - if (config_get_int(conf, "rewind_buffer_size", &buffer_size)) - settings->rewind_buffer_size = buffer_size * UINT64_C(1000000); - } - /* Hexadecimal settings */ @@ -3948,6 +4008,7 @@ bool config_save_file(const char *path) struct config_bool_setting *bool_settings = NULL; struct config_int_setting *int_settings = NULL; struct config_uint_setting *uint_settings = NULL; + struct config_size_setting *size_settings = NULL; struct config_float_setting *float_settings = NULL; struct config_array_setting *array_settings = NULL; struct config_path_setting *path_settings = NULL; @@ -3957,6 +4018,7 @@ bool config_save_file(const char *path) int float_settings_size = sizeof(settings->floats)/ sizeof(settings->floats.placeholder); int int_settings_size = sizeof(settings->ints) / sizeof(settings->ints.placeholder); int uint_settings_size = sizeof(settings->uints) / sizeof(settings->uints.placeholder); + int size_settings_size = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder); int array_settings_size = sizeof(settings->arrays)/ sizeof(settings->arrays.placeholder); int path_settings_size = sizeof(settings->paths) / sizeof(settings->paths.placeholder); @@ -3973,6 +4035,7 @@ bool config_save_file(const char *path) bool_settings = populate_settings_bool (settings, &bool_settings_size); int_settings = populate_settings_int (settings, &int_settings_size); uint_settings = populate_settings_uint (settings, &uint_settings_size); + size_settings = populate_settings_size (settings, &size_settings_size); float_settings = populate_settings_float (settings, &float_settings_size); array_settings = populate_settings_array (settings, &array_settings_size); path_settings = populate_settings_path (settings, &path_settings_size); @@ -4050,6 +4113,18 @@ bool config_save_file(const char *path) free(uint_settings); } + if (size_settings && (size_settings_size > 0)) + { + for (i = 0; i < (unsigned)size_settings_size; i++) + if (!size_settings[i].override || + !retroarch_override_setting_is_set(size_settings[i].override, NULL)) + config_set_int(conf, + size_settings[i].ident, + *size_settings[i].ptr); + + free(size_settings); + } + for (i = 0; i < MAX_USERS; i++) { char cfg[64]; @@ -4169,8 +4244,10 @@ bool config_save_overrides(int override_type) struct config_bool_setting *bool_overrides = NULL; struct config_int_setting *int_settings = NULL; struct config_uint_setting *uint_settings = NULL; + struct config_size_setting *size_settings = NULL; struct config_int_setting *int_overrides = NULL; struct config_uint_setting *uint_overrides = NULL; + struct config_size_setting *size_overrides = NULL; struct config_float_setting *float_settings = NULL; struct config_float_setting *float_overrides= NULL; struct config_array_setting *array_settings = NULL; @@ -4187,6 +4264,7 @@ bool config_save_overrides(int override_type) int float_settings_size = sizeof(settings->floats) / sizeof(settings->floats.placeholder); int int_settings_size = sizeof(settings->ints) / sizeof(settings->ints.placeholder); int uint_settings_size = sizeof(settings->uints) / sizeof(settings->uints.placeholder); + int size_settings_size = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder); int array_settings_size = sizeof(settings->arrays) / sizeof(settings->arrays.placeholder); int path_settings_size = sizeof(settings->paths) / sizeof(settings->paths.placeholder); rarch_system_info_t *system = runloop_get_system_info(); @@ -4255,6 +4333,10 @@ bool config_save_overrides(int override_type) tmp_i = sizeof(settings->uints) / sizeof(settings->uints.placeholder); uint_overrides = populate_settings_uint (overrides, &tmp_i); + size_settings = populate_settings_size(settings, &size_settings_size); + tmp_i = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder); + size_overrides = populate_settings_size (overrides, &tmp_i); + float_settings = populate_settings_float(settings, &float_settings_size); tmp_i = sizeof(settings->floats) / sizeof(settings->floats.placeholder); float_overrides = populate_settings_float(overrides, &tmp_i); @@ -4307,6 +4389,18 @@ bool config_save_overrides(int override_type) (*uint_overrides[i].ptr)); } } + for (i = 0; i < (unsigned)size_settings_size; i++) + { + if ((*size_settings[i].ptr) != (*size_overrides[i].ptr)) + { + RARCH_LOG(" original: %s=%d\n", + size_settings[i].ident, (*size_settings[i].ptr)); + RARCH_LOG(" override: %s=%d\n", + size_overrides[i].ident, (*size_overrides[i].ptr)); + config_set_int(conf, size_overrides[i].ident, + (*size_overrides[i].ptr)); + } + } for (i = 0; i < (unsigned)float_settings_size; i++) { if ((*float_settings[i].ptr) != (*float_overrides[i].ptr)) @@ -4423,6 +4517,8 @@ bool config_save_overrides(int override_type) free(int_settings); if (uint_settings) free(uint_settings); + if (size_settings) + free(size_settings); if (int_overrides) free(int_overrides); if (uint_overrides) diff --git a/configuration.h b/configuration.h index db968639f5..8955319959 100644 --- a/configuration.h +++ b/configuration.h @@ -348,6 +348,7 @@ typedef struct settings unsigned content_history_size; unsigned libretro_log_level; unsigned rewind_granularity; + unsigned rewind_buffer_size_step; unsigned autosave_interval; unsigned network_cmd_port; unsigned network_remote_base_port; @@ -411,6 +412,12 @@ typedef struct settings unsigned midi_volume; } uints; + struct + { + size_t placeholder; + size_t rewind_buffer_size; + } sizes; + struct { char placeholder; @@ -509,7 +516,6 @@ typedef struct settings video_viewport_t video_viewport_custom; - size_t rewind_buffer_size; } settings_t; /** diff --git a/core_impl.c b/core_impl.c index 7c2585a0c2..f1516fab7c 100644 --- a/core_impl.c +++ b/core_impl.c @@ -290,6 +290,8 @@ bool core_load_game(retro_ctx_load_content_info_t *load_info) bool contentless = false; bool is_inited = false; + video_driver_set_cached_frame_ptr(NULL); + #ifdef HAVE_RUNAHEAD set_load_content_info(load_info); clear_controller_port_map(); @@ -373,12 +375,16 @@ bool core_get_system_av_info(struct retro_system_av_info *av_info) bool core_reset(void) { + video_driver_set_cached_frame_ptr(NULL); + current_core.retro_reset(); return true; } bool core_init(void) { + video_driver_set_cached_frame_ptr(NULL); + current_core.retro_init(); current_core.inited = true; return true; @@ -386,6 +392,8 @@ bool core_init(void) bool core_unload(void) { + video_driver_set_cached_frame_ptr(NULL); + current_core.retro_deinit(); return true; } @@ -396,9 +404,12 @@ bool core_unload_game(void) video_driver_free_hw_context(); audio_driver_stop(); + video_driver_set_cached_frame_ptr(NULL); + current_core.retro_unload_game(); current_core.game_loaded = false; + return true; } diff --git a/deps/SPIRV-Cross/CMakeLists.txt b/deps/SPIRV-Cross/CMakeLists.txt index 9226afd46f..2849b6957d 100644 --- a/deps/SPIRV-Cross/CMakeLists.txt +++ b/deps/SPIRV-Cross/CMakeLists.txt @@ -93,6 +93,10 @@ spirv_cross_add_library(spirv-cross-cpp spirv_cross_cpp STATIC ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.hpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.cpp) +spirv_cross_add_library(spirv-cross-reflect spirv_cross_reflect STATIC + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.cpp) + spirv_cross_add_library(spirv-cross-msl spirv_cross_msl STATIC ${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.hpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.cpp) @@ -110,7 +114,7 @@ target_compile_options(spirv-cross PRIVATE ${spirv-compiler-options}) target_compile_definitions(spirv-cross PRIVATE ${spirv-compiler-defines}) install(TARGETS spirv-cross RUNTIME DESTINATION bin) -target_link_libraries(spirv-cross spirv-cross-glsl spirv-cross-hlsl spirv-cross-cpp spirv-cross-msl spirv-cross-util spirv-cross-core) +target_link_libraries(spirv-cross spirv-cross-glsl spirv-cross-hlsl spirv-cross-cpp spirv-cross-reflect spirv-cross-msl spirv-cross-util spirv-cross-core) target_link_libraries(spirv-cross-util spirv-cross-core) target_link_libraries(spirv-cross-glsl spirv-cross-core) target_link_libraries(spirv-cross-msl spirv-cross-glsl) diff --git a/deps/SPIRV-Cross/README.md b/deps/SPIRV-Cross/README.md index 5f92698602..d3c1a93a07 100644 --- a/deps/SPIRV-Cross/README.md +++ b/deps/SPIRV-Cross/README.md @@ -11,6 +11,7 @@ SPIRV-Cross is a tool designed for parsing and converting SPIR-V to other shader - Convert SPIR-V to readable, usable and efficient Metal Shading Language (MSL) - Convert SPIR-V to readable, usable and efficient HLSL - Convert SPIR-V to debuggable C++ [EXPERIMENTAL] + - Convert SPIR-V to a JSON reflection format [EXPERIMENTAL] - Reflection API to simplify the creation of Vulkan pipeline layouts - Reflection API to modify and tweak OpDecorations - Supports "all" of vertex, fragment, tessellation, geometry and compute shaders. diff --git a/deps/SPIRV-Cross/main.cpp b/deps/SPIRV-Cross/main.cpp index b309a82ac3..69bd135c1a 100644 --- a/deps/SPIRV-Cross/main.cpp +++ b/deps/SPIRV-Cross/main.cpp @@ -19,6 +19,7 @@ #include "spirv_glsl.hpp" #include "spirv_hlsl.hpp" #include "spirv_msl.hpp" +#include "spirv_reflect.hpp" #include #include #include @@ -149,6 +150,22 @@ struct CLIParser return val; } + // Return a string only if it's not prefixed with `--`, otherwise return the default value + const char *next_value_string(const char *default_value) + { + if (!argc) + { + return default_value; + } + + if (0 == strncmp("--", *argv, 2)) + { + return default_value; + } + + return next_string(); + } + const char *next_string() { if (!argc) @@ -461,6 +478,7 @@ struct CLIArguments bool fixup = false; bool yflip = false; bool sso = false; + bool support_nonzero_baseinstance = true; vector pls_in; vector pls_out; vector remaps; @@ -481,6 +499,7 @@ struct CLIArguments uint32_t iterations = 1; bool cpp = false; + string reflect; bool msl = false; bool hlsl = false; bool hlsl_compat = false; @@ -512,6 +531,7 @@ static void print_help() "\t[--msl]\n" "\t[--msl-version ]\n" "\t[--hlsl]\n" + "\t[--reflect]\n" "\t[--shader-model]\n" "\t[--hlsl-enable-compat]\n" "\t[--separate-shader-objects]\n" @@ -528,7 +548,8 @@ static void print_help() "\t[--rename-interface-variable ]\n" "\t[--set-hlsl-vertex-input-semantic ]\n" "\t[--rename-entry-point ]\n" - "\t[--combined-samplers-inherit-bindings]" + "\t[--combined-samplers-inherit-bindings]\n" + "\t[--no-support-nonzero-baseinstance]\n" "\n"); } @@ -663,6 +684,7 @@ static int main_inner(int argc, char *argv[]) cbs.add("--flip-vert-y", [&args](CLIParser &) { args.yflip = true; }); cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); }); cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; }); + cbs.add("--reflect", [&args](CLIParser &parser) { args.reflect = parser.next_value_string("json"); }); cbs.add("--cpp-interface-name", [&args](CLIParser &parser) { args.cpp_interface_name = parser.next_string(); }); cbs.add("--metal", [&args](CLIParser &) { args.msl = true; }); // Legacy compatibility cbs.add("--msl", [&args](CLIParser &) { args.msl = true; }); @@ -737,6 +759,8 @@ static int main_inner(int argc, char *argv[]) cbs.add("--combined-samplers-inherit-bindings", [&args](CLIParser &) { args.combined_samplers_inherit_bindings = true; }); + cbs.add("--no-support-nonzero-baseinstance", [&](CLIParser &) { args.support_nonzero_baseinstance = false; }); + cbs.default_handler = [&args](const char *value) { args.input = value; }; cbs.error_handler = [] { print_help(); }; @@ -757,8 +781,20 @@ static int main_inner(int argc, char *argv[]) return EXIT_FAILURE; } - unique_ptr compiler; + // Special case reflection because it has little to do with the path followed by code-outputting compilers + if (!args.reflect.empty()) + { + CompilerReflection compiler(read_spirv_file(args.input)); + compiler.set_format(args.reflect); + auto json = compiler.compile(); + if (args.output) + write_string_to_file(args.output, json.c_str()); + else + printf("%s", json.c_str()); + return EXIT_SUCCESS; + } + unique_ptr compiler; bool combined_image_samplers = false; bool build_dummy_sampler = false; @@ -895,6 +931,7 @@ static int main_inner(int argc, char *argv[]) opts.vulkan_semantics = args.vulkan_semantics; opts.vertex.fixup_clipspace = args.fixup; opts.vertex.flip_vert_y = args.yflip; + opts.vertex.support_nonzero_base_instance = args.support_nonzero_baseinstance; compiler->set_common_options(opts); // Set HLSL specific options. diff --git a/deps/SPIRV-Cross/msvc/SPIRV-Cross.vcxproj b/deps/SPIRV-Cross/msvc/SPIRV-Cross.vcxproj index 6040e2e116..d2287dcbe9 100644 --- a/deps/SPIRV-Cross/msvc/SPIRV-Cross.vcxproj +++ b/deps/SPIRV-Cross/msvc/SPIRV-Cross.vcxproj @@ -127,6 +127,7 @@ + @@ -138,6 +139,7 @@ + diff --git a/deps/SPIRV-Cross/msvc/SPIRV-Cross.vcxproj.filters b/deps/SPIRV-Cross/msvc/SPIRV-Cross.vcxproj.filters index f853c08b5b..3ff2c29a58 100644 --- a/deps/SPIRV-Cross/msvc/SPIRV-Cross.vcxproj.filters +++ b/deps/SPIRV-Cross/msvc/SPIRV-Cross.vcxproj.filters @@ -24,6 +24,9 @@ Source Files + + Source Files + Source Files @@ -50,6 +53,9 @@ Header Files + + Header Files + Header Files diff --git a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000000..142ef5efa8 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp @@ -0,0 +1,32 @@ +RWByteAddressBuffer bar : register(u0); +RWByteAddressBuffer foo : register(u1); + +void comp_main() +{ + [unroll] + for (int _135 = 0; _135 < 16; ) + { + bar.Store4(_135 * 16 + 0, asuint(asfloat(foo.Load4(_135 * 16 + 0)))); + _135++; + continue; + } + [loop] + for (int _136 = 0; _136 < 16; ) + { + bar.Store4((15 - _136) * 16 + 0, asuint(asfloat(foo.Load4(_136 * 16 + 0)))); + _136++; + continue; + } + [branch] + if (asfloat(bar.Load(160)) > 10.0f) + { + foo.Store4(320, asuint(5.0f.xxxx)); + } + foo.Store4(320, asuint(20.0f.xxxx)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..5deae3a569 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,57 @@ +static const float _46[16] = { 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f }; +static const float4 _76[4] = { 0.0f.xxxx, 1.0f.xxxx, 8.0f.xxxx, 5.0f.xxxx }; +static const float4 _90[4] = { 20.0f.xxxx, 30.0f.xxxx, 50.0f.xxxx, 60.0f.xxxx }; + +static float FragColor; +static int index; + +struct SPIRV_Cross_Input +{ + nointerpolation int index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 foobar[4] = _76; + float4 baz[4] = _76; + FragColor = _46[index]; + if (index < 10) + { + FragColor += _46[index ^ 1]; + } + else + { + FragColor += _46[index & 1]; + } + bool _99 = index > 30; + if (_99) + { + FragColor += _76[index & 3].y; + } + else + { + FragColor += _76[index & 1].x; + } + if (_99) + { + foobar[1].z = 20.0f; + } + int _37 = index & 3; + FragColor += foobar[_37].z; + baz = _90; + FragColor += baz[_37].z; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + index = stage_input.index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..5b894de831 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,31 @@ +Texture2D uShadow : register(t0); +SamplerComparisonState _uShadow_sampler : register(s0); +Texture2D uTexture : register(t1); +SamplerComparisonState uSampler : register(s2); + +static float3 vUV; +static float FragColor; + +struct SPIRV_Cross_Input +{ + float3 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uShadow.SampleCmp(_uShadow_sampler, vUV.xy, vUV.z) + uTexture.SampleCmp(uSampler, vUV.xy, vUV.z); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..0d1e8cc534 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,28 @@ +static float4 gl_Position; +static int gl_VertexIndex; +static int gl_InstanceIndex; +struct SPIRV_Cross_Input +{ + uint gl_VertexIndex : SV_VertexID; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = float(uint(gl_VertexIndex) + uint(gl_InstanceIndex)).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_VertexIndex = int(stage_input.gl_VertexIndex); + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/comp/globallycoherent.comp b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000000..1637727deb --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/comp/globallycoherent.comp @@ -0,0 +1,16 @@ +globallycoherent RWByteAddressBuffer _29 : register(u3); +ByteAddressBuffer _33 : register(t2); +RWTexture2D uImageIn : register(u0); +globallycoherent RWTexture2D uImageOut : register(u1); + +void comp_main() +{ + uImageOut[int2(9, 7)] = uImageIn[int2(9, 7)].x; + _29.Store(0, asuint(asfloat(_33.Load(0)))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag index 8cb52f0a4d..3adf7d9852 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag +++ b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag @@ -15,11 +15,10 @@ struct SPIRV_Cross_Output void frag_main() { - float lut[5] = _17; for (int _46 = 0; _46 < 4; ) { int _33 = _46 + 1; - FragColor += lut[_33].xxxx; + FragColor += _17[_33].xxxx; _46 = _33; continue; } diff --git a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/constant-composites.frag b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/constant-composites.frag index 0514eef1ee..2613e1c2c5 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/constant-composites.frag +++ b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/constant-composites.frag @@ -30,7 +30,7 @@ void frag_main() lut = _16; foos = _28; FragColor = lut[_line].xxxx; - FragColor += (foos[_line].a * (foos[1 - _line].a)).xxxx; + FragColor += (foos[_line].a * foos[1 - _line].a).xxxx; } SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) diff --git a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/lut-promotion.frag b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/lut-promotion.frag new file mode 100644 index 0000000000..aae0d39de2 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/lut-promotion.frag @@ -0,0 +1,57 @@ +static const float _16[16] = { 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f }; +static const float4 _60[4] = { 0.0f.xxxx, 1.0f.xxxx, 8.0f.xxxx, 5.0f.xxxx }; +static const float4 _104[4] = { 20.0f.xxxx, 30.0f.xxxx, 50.0f.xxxx, 60.0f.xxxx }; + +static float FragColor; +static int index; + +struct SPIRV_Cross_Input +{ + nointerpolation int index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _16[index]; + if (index < 10) + { + FragColor += _16[index ^ 1]; + } + else + { + FragColor += _16[index & 1]; + } + bool _63 = index > 30; + if (_63) + { + FragColor += _60[index & 3].y; + } + else + { + FragColor += _60[index & 1].x; + } + float4 foobar[4] = _60; + if (_63) + { + foobar[1].z = 20.0f; + } + int _91 = index & 3; + FragColor += foobar[_91].z; + float4 baz[4] = _60; + baz = _104; + FragColor += baz[_91].z; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + index = stage_input.index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..12e0f5bd79 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag @@ -0,0 +1,23 @@ +static const uint s = 10u; +static const bool _13 = (s > 20u); +static const uint _16 = _13 ? 30u : 50u; + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float(_16); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/deps/SPIRV-Cross/reference/opt/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag b/deps/SPIRV-Cross/reference/opt/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..9db6b5470d --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,69 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +constant float _46[16] = {1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0}; +constant float4 _76[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; +constant float4 _90[4] = {float4(20.0), float4(30.0), float4(50.0), float4(60.0)}; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment. +template +void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N]) +{ + for (uint i = 0; i < N; dst[i] = src[i], i++); +} + +// An overload for constant arrays. +template +void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N]) +{ + for (uint i = 0; i < N; dst[i] = src[i], i++); +} + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + float4 foobar[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; + float4 baz[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; + main0_out out = {}; + out.FragColor = _46[in.index]; + if (in.index < 10) + { + out.FragColor += _46[in.index ^ 1]; + } + else + { + out.FragColor += _46[in.index & 1]; + } + bool _99 = in.index > 30; + if (_99) + { + out.FragColor += _76[in.index & 3].y; + } + else + { + out.FragColor += _76[in.index & 1].x; + } + if (_99) + { + foobar[1].z = 20.0; + } + int _37 = in.index & 3; + out.FragColor += foobar[_37].z; + spvArrayCopyConstant(baz, _90); + out.FragColor += baz[_37].z; + return out; +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/deps/SPIRV-Cross/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..e8a88623a2 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uShadow [[texture(0)]], depth2d uTexture [[texture(1)]], sampler uShadowSmplr [[sampler(0)]], sampler uSampler [[sampler(2)]]) +{ + main0_out out = {}; + out.FragColor = uShadow.sample_compare(uShadowSmplr, in.vUV.xy, in.vUV.z) + uTexture.sample_compare(uSampler, in.vUV.xy, in.vUV.z); + return out; +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/deps/SPIRV-Cross/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..d453aadef0 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + out.gl_Position = float4(float(gl_VertexIndex + gl_InstanceIndex)); + return out; +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag b/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag index 6c8299fa91..60868acd87 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag +++ b/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag @@ -1,5 +1,3 @@ -#pragma clang diagnostic ignored "-Wmissing-prototypes" - #include #include @@ -12,28 +10,13 @@ struct main0_out float4 FragColor [[color(0)]]; }; -// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment. -template -void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N]) -{ - for (uint i = 0; i < N; dst[i] = src[i], i++); -} - -// An overload for constant arrays. -template -void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N]) -{ - for (uint i = 0; i < N; dst[i] = src[i], i++); -} - fragment main0_out main0() { main0_out out = {}; - float lut[5] = {1.0, 2.0, 3.0, 4.0, 5.0}; for (int _46 = 0; _46 < 4; ) { int _33 = _46 + 1; - out.FragColor += float4(lut[_33]); + out.FragColor += float4(_17[_33]); _46 = _33; continue; } diff --git a/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/constant-array.frag b/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/constant-array.frag index 63576f109c..bb55934c6d 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/constant-array.frag +++ b/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/constant-array.frag @@ -44,10 +44,8 @@ void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N]) fragment main0_out main0(main0_in in [[stage_in]]) { main0_out out = {}; - float4 indexable[3] = {float4(1.0), float4(2.0), float4(3.0)}; - float4 indexable_1[2][2] = {{float4(1.0), float4(2.0)}, {float4(8.0), float4(10.0)}}; - Foobar indexable_2[2] = {{10.0, 40.0}, {90.0, 70.0}}; - out.FragColor = ((indexable[in.index] + (indexable_1[in.index][in.index + 1])) + float4(30.0)) + float4(indexable_2[in.index].a + indexable_2[in.index].b); + Foobar indexable[2] = {{10.0, 40.0}, {90.0, 70.0}}; + out.FragColor = ((_37[in.index] + _55[in.index][in.index + 1]) + float4(30.0)) + float4(indexable[in.index].a + indexable[in.index].b); return out; } diff --git a/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/constant-composites.frag b/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/constant-composites.frag index ec5d66e86d..cb3e592337 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/constant-composites.frag +++ b/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/constant-composites.frag @@ -44,7 +44,7 @@ fragment main0_out main0(main0_in in [[stage_in]]) float lut[4] = {1.0, 4.0, 3.0, 2.0}; Foo foos[2] = {{10.0, 20.0}, {30.0, 40.0}}; out.FragColor = float4(lut[in.line]); - out.FragColor += float4(foos[in.line].a * (foos[1 - in.line].a)); + out.FragColor += float4(foos[in.line].a * foos[1 - in.line].a); return out; } diff --git a/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/lut-promotion.frag b/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/lut-promotion.frag new file mode 100644 index 0000000000..4efdf4a196 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/lut-promotion.frag @@ -0,0 +1,69 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +constant float _16[16] = {1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0}; +constant float4 _60[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; +constant float4 _104[4] = {float4(20.0), float4(30.0), float4(50.0), float4(60.0)}; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment. +template +void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N]) +{ + for (uint i = 0; i < N; dst[i] = src[i], i++); +} + +// An overload for constant arrays. +template +void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N]) +{ + for (uint i = 0; i < N; dst[i] = src[i], i++); +} + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = _16[in.index]; + if (in.index < 10) + { + out.FragColor += _16[in.index ^ 1]; + } + else + { + out.FragColor += _16[in.index & 1]; + } + bool _63 = in.index > 30; + if (_63) + { + out.FragColor += _60[in.index & 3].y; + } + else + { + out.FragColor += _60[in.index & 1].x; + } + float4 foobar[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; + if (_63) + { + foobar[1].z = 20.0; + } + int _91 = in.index & 3; + out.FragColor += foobar[_91].z; + float4 baz[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; + spvArrayCopyConstant(baz, _104); + out.FragColor += baz[_91].z; + return out; +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/spec-constant-ternary.frag b/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..5ab6b4fcb1 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-msl/frag/spec-constant-ternary.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +constant uint s_tmp [[function_constant(0)]]; +constant uint s = is_function_constant_defined(s_tmp) ? s_tmp : 10u; +constant bool _13 = (s > 20u); +constant uint _16 = _13 ? 30u : 50u; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float(_16); + return out; +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders-msl/vert/set_builtin_in_func.vert b/deps/SPIRV-Cross/reference/opt/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000000..51a858af1e --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders-msl/vert/set_builtin_in_func.vert @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + out.gl_PointSize = 1.0; + out.gl_Position = float4(out.gl_PointSize); + return out; +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders-msl/vert/texture_buffer.vert b/deps/SPIRV-Cross/reference/opt/shaders-msl/vert/texture_buffer.vert index f7bcb7918b..c45d298134 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders-msl/vert/texture_buffer.vert +++ b/deps/SPIRV-Cross/reference/opt/shaders-msl/vert/texture_buffer.vert @@ -1,3 +1,5 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + #include #include @@ -8,10 +10,16 @@ struct main0_out float4 gl_Position [[position]]; }; +// Returns 2D texture coords corresponding to 1D texel buffer coords +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + vertex main0_out main0(texture2d uSamp [[texture(4)]], texture2d uSampo [[texture(5)]]) { main0_out out = {}; - out.gl_Position = uSamp.read(uint2(10, 0)) + uSampo.read(uint2(100, 0)); + out.gl_Position = uSamp.read(spvTexelBufferCoord(10)) + uSampo.read(spvTexelBufferCoord(100)); return out; } diff --git a/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/lut-promotion-initializer.asm.frag b/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..d88c0e36d9 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,42 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _46[16] = float[](1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0); +const vec4 _76[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + +layout(location = 0) out float FragColor; +layout(location = 0) flat in mediump int index; + +void main() +{ + vec4 foobar[4] = _76; + vec4 baz[4] = _76; + FragColor = _46[index]; + if (index < 10) + { + FragColor += _46[index ^ 1]; + } + else + { + FragColor += _46[index & 1]; + } + bool _99 = index > 30; + if (_99) + { + FragColor += _76[index & 3].y; + } + else + { + FragColor += _76[index & 1].x; + } + if (_99) + { + foobar[1].z = 20.0; + } + mediump int _37 = index & 3; + FragColor += foobar[_37].z; + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[_37].z; +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag b/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000000..ade9044e35 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag @@ -0,0 +1,33 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) flat in mediump int vIndex; +layout(location = 0) out float FragColor; + +void main() +{ + highp float _19; + switch (vIndex) + { + case 0: + case 2: + { + _19 = 1.0; + break; + } + case 1: + default: + { + _19 = 3.0; + break; + } + case 8: + { + _19 = 8.0; + break; + } + } + FragColor = _19; +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000000..6953ec61d0 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(binding = 0) uniform sampler2DShadow uShadow; +uniform sampler2DShadow SPIRV_Cross_CombineduTextureuSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = texture(uShadow, vec3(vUV.xy, vUV.z)) + texture(SPIRV_Cross_CombineduTextureuSampler, vec3(vUV.xy, vUV.z)); +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk b/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk new file mode 100644 index 0000000000..2f997036f5 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk @@ -0,0 +1,14 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2DShadow uShadow; +layout(set = 0, binding = 1) uniform texture2D uTexture; +layout(set = 0, binding = 2) uniform samplerShadow uSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = texture(uShadow, vec3(vUV.xy, vUV.z)) + texture(sampler2DShadow(uTexture, uSampler), vec3(vUV.xy, vUV.z)); +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders/asm/geom/store-uint-layer.invalid.asm.geom b/deps/SPIRV-Cross/reference/opt/shaders/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000000..c768d5da86 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders/asm/geom/store-uint-layer.invalid.asm.geom @@ -0,0 +1,41 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +struct VertexOutput +{ + vec4 pos; +}; + +struct GeometryOutput +{ + vec4 pos; + uint layer; +}; + +void _main(VertexOutput _input[3], GeometryOutput stream) +{ + GeometryOutput _output; + _output.layer = 1u; + for (int v = 0; v < 3; v++) + { + _output.pos = _input[v].pos; + gl_Position = _output.pos; + gl_Layer = int(_output.layer); + EmitVertex(); + } + EndPrimitive(); +} + +void main() +{ + VertexOutput _input[3]; + _input[0].pos = gl_in[0].gl_Position; + _input[1].pos = gl_in[1].gl_Position; + _input[2].pos = gl_in[2].gl_Position; + VertexOutput param[3] = _input; + GeometryOutput param_1; + _main(param, param_1); + GeometryOutput stream = param_1; +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/deps/SPIRV-Cross/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..c25e9bbe5b --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,9 @@ +#version 450 + +uniform int SPIRV_Cross_BaseInstance; + +void main() +{ + gl_Position = vec4(float(uint(gl_VertexID) + uint((gl_InstanceID + SPIRV_Cross_BaseInstance)))); +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders/comp/generate_height.comp b/deps/SPIRV-Cross/reference/opt/shaders/comp/generate_height.comp index 17d3073d2e..ff96e7505a 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders/comp/generate_height.comp +++ b/deps/SPIRV-Cross/reference/opt/shaders/comp/generate_height.comp @@ -48,8 +48,8 @@ void main() vec2 _387 = _316.xx; vec2 _392 = _316.yy; vec2 _395 = _392 * _137.distribution[_280].yx; - vec2 _421 = _392 * (_137.distribution[(_476 * _448) + _475]).yx; - vec2 _429 = ((_137.distribution[(_476 * _448) + _475]) * _387) + vec2(-_421.x, _421.y); + vec2 _421 = _392 * _137.distribution[(_476 * _448) + _475].yx; + vec2 _429 = (_137.distribution[(_476 * _448) + _475] * _387) + vec2(-_421.x, _421.y); _225.heights[_280] = packHalf2x16(((_137.distribution[_280] * _387) + vec2(-_395.x, _395.y)) + vec2(_429.x, -_429.y)); } diff --git a/deps/SPIRV-Cross/reference/opt/shaders/flatten/copy.flatten.vert b/deps/SPIRV-Cross/reference/opt/shaders/flatten/copy.flatten.vert index 27ce450d62..33caec4f78 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders/flatten/copy.flatten.vert +++ b/deps/SPIRV-Cross/reference/opt/shaders/flatten/copy.flatten.vert @@ -19,7 +19,7 @@ void main() for (int _96 = 0; _96 < 4; ) { vec3 _68 = aVertex.xyz - Light(UBO[_96 * 2 + 4].xyz, UBO[_96 * 2 + 4].w, UBO[_96 * 2 + 5]).Position; - vColor += (((UBO[_96 * 2 + 5]) * clamp(1.0 - (length(_68) / Light(UBO[_96 * 2 + 4].xyz, UBO[_96 * 2 + 4].w, UBO[_96 * 2 + 5]).Radius), 0.0, 1.0)) * dot(aNormal, normalize(_68))); + vColor += ((UBO[_96 * 2 + 5] * clamp(1.0 - (length(_68) / Light(UBO[_96 * 2 + 4].xyz, UBO[_96 * 2 + 4].w, UBO[_96 * 2 + 5]).Radius), 0.0, 1.0)) * dot(aNormal, normalize(_68))); _96++; continue; } diff --git a/deps/SPIRV-Cross/reference/opt/shaders/flatten/dynamic.flatten.vert b/deps/SPIRV-Cross/reference/opt/shaders/flatten/dynamic.flatten.vert index 8fc8ff6eed..7129af2869 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders/flatten/dynamic.flatten.vert +++ b/deps/SPIRV-Cross/reference/opt/shaders/flatten/dynamic.flatten.vert @@ -18,8 +18,8 @@ void main() vColor = vec4(0.0); for (int _82 = 0; _82 < 4; ) { - vec3 _54 = aVertex.xyz - (UBO[_82 * 2 + 4].xyz); - vColor += (((UBO[_82 * 2 + 5]) * clamp(1.0 - (length(_54) / (UBO[_82 * 2 + 4].w)), 0.0, 1.0)) * dot(aNormal, normalize(_54))); + vec3 _54 = aVertex.xyz - UBO[_82 * 2 + 4].xyz; + vColor += ((UBO[_82 * 2 + 5] * clamp(1.0 - (length(_54) / UBO[_82 * 2 + 4].w), 0.0, 1.0)) * dot(aNormal, normalize(_54))); _82++; continue; } diff --git a/deps/SPIRV-Cross/reference/opt/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag b/deps/SPIRV-Cross/reference/opt/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag index 3657298f18..2ecee89377 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag +++ b/deps/SPIRV-Cross/reference/opt/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag @@ -25,6 +25,6 @@ void main() } } } - FragColor = ((values3[1 * 3 * 1 + 2 * 1 + 0]) + (values3[0 * 3 * 1 + 2 * 1 + 0])) + (values3[(vIndex + 1) * 3 * 1 + 2 * 1 + vIndex]); + FragColor = (values3[1 * 3 * 1 + 2 * 1 + 0] + values3[0 * 3 * 1 + 2 * 1 + 0]) + values3[(vIndex + 1) * 3 * 1 + 2 * 1 + vIndex]; } diff --git a/deps/SPIRV-Cross/reference/opt/shaders/frag/array-lut-no-loop-variable.frag b/deps/SPIRV-Cross/reference/opt/shaders/frag/array-lut-no-loop-variable.frag index 7bdfe543e4..869a76e4e7 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders/frag/array-lut-no-loop-variable.frag +++ b/deps/SPIRV-Cross/reference/opt/shaders/frag/array-lut-no-loop-variable.frag @@ -2,15 +2,16 @@ precision mediump float; precision highp int; +const float _17[5] = float[](1.0, 2.0, 3.0, 4.0, 5.0); + layout(location = 0) out vec4 FragColor; void main() { - float lut[5] = float[](1.0, 2.0, 3.0, 4.0, 5.0); for (int _46 = 0; _46 < 4; ) { mediump int _33 = _46 + 1; - FragColor += vec4(lut[_33]); + FragColor += vec4(_17[_33]); _46 = _33; continue; } diff --git a/deps/SPIRV-Cross/reference/opt/shaders/frag/constant-array.frag b/deps/SPIRV-Cross/reference/opt/shaders/frag/constant-array.frag index 2af87ad80d..749fc80980 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders/frag/constant-array.frag +++ b/deps/SPIRV-Cross/reference/opt/shaders/frag/constant-array.frag @@ -2,6 +2,9 @@ precision mediump float; precision highp int; +const vec4 _37[3] = vec4[](vec4(1.0), vec4(2.0), vec4(3.0)); +const vec4 _55[2][2] = vec4[][](vec4[](vec4(1.0), vec4(2.0)), vec4[](vec4(8.0), vec4(10.0))); + struct Foobar { float a; @@ -13,9 +16,7 @@ layout(location = 0) flat in mediump int index; void main() { - highp vec4 indexable[3] = vec4[](vec4(1.0), vec4(2.0), vec4(3.0)); - highp vec4 indexable_1[2][2] = vec4[][](vec4[](vec4(1.0), vec4(2.0)), vec4[](vec4(8.0), vec4(10.0))); - Foobar indexable_2[2] = Foobar[](Foobar(10.0, 40.0), Foobar(90.0, 70.0)); - FragColor = ((indexable[index] + (indexable_1[index][index + 1])) + vec4(30.0)) + vec4(indexable_2[index].a + indexable_2[index].b); + Foobar indexable[2] = Foobar[](Foobar(10.0, 40.0), Foobar(90.0, 70.0)); + FragColor = ((_37[index] + _55[index][index + 1]) + vec4(30.0)) + vec4(indexable[index].a + indexable[index].b); } diff --git a/deps/SPIRV-Cross/reference/opt/shaders/frag/constant-composites.frag b/deps/SPIRV-Cross/reference/opt/shaders/frag/constant-composites.frag index b105dbd26c..c65c60613d 100644 --- a/deps/SPIRV-Cross/reference/opt/shaders/frag/constant-composites.frag +++ b/deps/SPIRV-Cross/reference/opt/shaders/frag/constant-composites.frag @@ -18,6 +18,6 @@ void main() lut = float[](1.0, 4.0, 3.0, 2.0); foos = Foo[](Foo(10.0, 20.0), Foo(30.0, 40.0)); FragColor = vec4(lut[line]); - FragColor += vec4(foos[line].a * (foos[1 - line].a)); + FragColor += vec4(foos[line].a * foos[1 - line].a); } diff --git a/deps/SPIRV-Cross/reference/opt/shaders/frag/lut-promotion.frag b/deps/SPIRV-Cross/reference/opt/shaders/frag/lut-promotion.frag new file mode 100644 index 0000000000..21c925796e --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders/frag/lut-promotion.frag @@ -0,0 +1,42 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _16[16] = float[](1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0); +const vec4 _60[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + +layout(location = 0) out float FragColor; +layout(location = 0) flat in mediump int index; + +void main() +{ + FragColor = _16[index]; + if (index < 10) + { + FragColor += _16[index ^ 1]; + } + else + { + FragColor += _16[index & 1]; + } + bool _63 = index > 30; + if (_63) + { + FragColor += _60[index & 3].y; + } + else + { + FragColor += _60[index & 1].x; + } + vec4 foobar[4] = _60; + if (_63) + { + foobar[1].z = 20.0; + } + mediump int _91 = index & 3; + FragColor += foobar[_91].z; + vec4 baz[4] = _60; + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[_91].z; +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/deps/SPIRV-Cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000000..91b0331b79 --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float((10u > 20u) ? 30u : 50u); +} + diff --git a/deps/SPIRV-Cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk b/deps/SPIRV-Cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk new file mode 100644 index 0000000000..59d3b99b9c --- /dev/null +++ b/deps/SPIRV-Cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk @@ -0,0 +1,13 @@ +#version 450 + +layout(constant_id = 0) const uint s = 10u; +const bool _13 = (s > 20u); +const uint _16 = _13 ? 30u : 50u; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float(_16); +} + diff --git a/deps/SPIRV-Cross/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/deps/SPIRV-Cross/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000000..9700100348 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp @@ -0,0 +1,41 @@ +RWByteAddressBuffer bar : register(u0); +RWByteAddressBuffer foo : register(u1); + +void _main() +{ + [unroll] + for (int i = 0; i < 16; i++) + { + bar.Store4(i * 16 + 0, asuint(asfloat(foo.Load4(i * 16 + 0)))); + } + [loop] + for (int i_1 = 0; i_1 < 16; i_1++) + { + bar.Store4((15 - i_1) * 16 + 0, asuint(asfloat(foo.Load4(i_1 * 16 + 0)))); + } + float v = asfloat(bar.Load(160)); + float w = asfloat(foo.Load(160)); + [branch] + if (v > 10.0f) + { + foo.Store4(320, asuint(5.0f.xxxx)); + } + float value = 20.0f; + [flatten] + if (w > 40.0f) + { + value = 20.0f; + } + foo.Store4(320, asuint(value.xxxx)); +} + +void comp_main() +{ + _main(); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/deps/SPIRV-Cross/reference/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag b/deps/SPIRV-Cross/reference/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..a7aec01bad --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,55 @@ +static const float _46[16] = { 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f }; +static const float4 _76[4] = { 0.0f.xxxx, 1.0f.xxxx, 8.0f.xxxx, 5.0f.xxxx }; +static const float4 _90[4] = { 20.0f.xxxx, 30.0f.xxxx, 50.0f.xxxx, 60.0f.xxxx }; + +static float FragColor; +static int index; + +struct SPIRV_Cross_Input +{ + nointerpolation int index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 foobar[4] = _76; + float4 baz[4] = _76; + FragColor = _46[index]; + if (index < 10) + { + FragColor += _46[index ^ 1]; + } + else + { + FragColor += _46[index & 1]; + } + if (index > 30) + { + FragColor += _76[index & 3].y; + } + else + { + FragColor += _76[index & 1].x; + } + if (index > 30) + { + foobar[1].z = 20.0f; + } + FragColor += foobar[index & 3].z; + baz = _90; + FragColor += baz[index & 3].z; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + index = stage_input.index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/deps/SPIRV-Cross/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/deps/SPIRV-Cross/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..027dd00c5a --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,41 @@ +Texture2D uShadow : register(t0); +SamplerComparisonState _uShadow_sampler : register(s0); +Texture2D uTexture : register(t1); +SamplerComparisonState uSampler : register(s2); + +static float3 vUV; +static float FragColor; + +struct SPIRV_Cross_Input +{ + float3 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +float sample_combined() +{ + return uShadow.SampleCmp(_uShadow_sampler, vUV.xy, vUV.z); +} + +float sample_separate() +{ + return uTexture.SampleCmp(uSampler, vUV.xy, vUV.z); +} + +void frag_main() +{ + FragColor = sample_combined() + sample_separate(); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/deps/SPIRV-Cross/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/deps/SPIRV-Cross/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..a18c6e7056 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,37 @@ +static float4 gl_Position; +static int gl_VertexIndex; +static int gl_InstanceIndex; +struct SPIRV_Cross_Input +{ + uint gl_VertexIndex : SV_VertexID; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +float4 _main(uint vid, uint iid) +{ + return float(vid + iid).xxxx; +} + +void vert_main() +{ + uint vid = uint(gl_VertexIndex); + uint iid = uint(gl_InstanceIndex); + uint param = vid; + uint param_1 = iid; + gl_Position = _main(param, param_1); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_VertexIndex = int(stage_input.gl_VertexIndex); + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/deps/SPIRV-Cross/reference/shaders-hlsl/comp/globallycoherent.comp b/deps/SPIRV-Cross/reference/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000000..69886256f8 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-hlsl/comp/globallycoherent.comp @@ -0,0 +1,18 @@ +globallycoherent RWByteAddressBuffer _29 : register(u3); +ByteAddressBuffer _33 : register(t2); +RWTexture2D uImageIn : register(u0); +globallycoherent RWTexture2D uImageOut : register(u1); + +void comp_main() +{ + int2 coord = int2(9, 7); + float4 indata = uImageIn[coord].xxxx; + uImageOut[coord] = indata.x; + _29.Store(0, asuint(asfloat(_33.Load(0)))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/deps/SPIRV-Cross/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag b/deps/SPIRV-Cross/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag index 04d4d7fa75..407fa2bda4 100644 --- a/deps/SPIRV-Cross/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag +++ b/deps/SPIRV-Cross/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag @@ -15,8 +15,7 @@ struct SPIRV_Cross_Output void frag_main() { - float lut[5] = _17; - for (int i = 0; i < 4; i++, FragColor += lut[i].xxxx) + for (int i = 0; i < 4; i++, FragColor += _17[i].xxxx) { } } diff --git a/deps/SPIRV-Cross/reference/shaders-hlsl/frag/constant-composites.frag b/deps/SPIRV-Cross/reference/shaders-hlsl/frag/constant-composites.frag index 0514eef1ee..2613e1c2c5 100644 --- a/deps/SPIRV-Cross/reference/shaders-hlsl/frag/constant-composites.frag +++ b/deps/SPIRV-Cross/reference/shaders-hlsl/frag/constant-composites.frag @@ -30,7 +30,7 @@ void frag_main() lut = _16; foos = _28; FragColor = lut[_line].xxxx; - FragColor += (foos[_line].a * (foos[1 - _line].a)).xxxx; + FragColor += (foos[_line].a * foos[1 - _line].a).xxxx; } SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) diff --git a/deps/SPIRV-Cross/reference/shaders-hlsl/frag/lut-promotion.frag b/deps/SPIRV-Cross/reference/shaders-hlsl/frag/lut-promotion.frag new file mode 100644 index 0000000000..d148bc12cb --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-hlsl/frag/lut-promotion.frag @@ -0,0 +1,55 @@ +static const float _16[16] = { 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f }; +static const float4 _60[4] = { 0.0f.xxxx, 1.0f.xxxx, 8.0f.xxxx, 5.0f.xxxx }; +static const float4 _104[4] = { 20.0f.xxxx, 30.0f.xxxx, 50.0f.xxxx, 60.0f.xxxx }; + +static float FragColor; +static int index; + +struct SPIRV_Cross_Input +{ + nointerpolation int index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _16[index]; + if (index < 10) + { + FragColor += _16[index ^ 1]; + } + else + { + FragColor += _16[index & 1]; + } + if (index > 30) + { + FragColor += _60[index & 3].y; + } + else + { + FragColor += _60[index & 1].x; + } + float4 foobar[4] = _60; + if (index > 30) + { + foobar[1].z = 20.0f; + } + FragColor += foobar[index & 3].z; + float4 baz[4] = _60; + baz = _104; + FragColor += baz[index & 3].z; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + index = stage_input.index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/deps/SPIRV-Cross/reference/shaders-hlsl/frag/spec-constant-ternary.frag b/deps/SPIRV-Cross/reference/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..12e0f5bd79 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-hlsl/frag/spec-constant-ternary.frag @@ -0,0 +1,23 @@ +static const uint s = 10u; +static const bool _13 = (s > 20u); +static const uint _16 = _13 ? 30u : 50u; + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float(_16); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/deps/SPIRV-Cross/reference/shaders-msl-no-opt/vert/functions_nested.vert b/deps/SPIRV-Cross/reference/shaders-msl-no-opt/vert/functions_nested.vert index 43a508613b..037ead3922 100644 --- a/deps/SPIRV-Cross/reference/shaders-msl-no-opt/vert/functions_nested.vert +++ b/deps/SPIRV-Cross/reference/shaders-msl-no-opt/vert/functions_nested.vert @@ -36,6 +36,12 @@ struct main0_out float4 gl_Position [[position]]; }; +// Returns 2D texture coords corresponding to 1D texel buffer coords +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + attr_desc fetch_desc(thread const int& location, constant VertexBuffer& v_227) { int attribute_flags = v_227.input_attributes[location].w; @@ -76,10 +82,10 @@ float4 fetch_attr(thread const attr_desc& desc, thread const int& vertex_id, thr { int _131 = first_byte; first_byte = _131 + 1; - tmp.x = input_stream.read(uint2(_131, 0)).x; + tmp.x = input_stream.read(spvTexelBufferCoord(_131)).x; int _138 = first_byte; first_byte = _138 + 1; - tmp.y = input_stream.read(uint2(_138, 0)).x; + tmp.y = input_stream.read(spvTexelBufferCoord(_138)).x; uint4 param = tmp; int param_1 = desc.swap_bytes; result[n] = float(get_bits(param, param_1)); @@ -89,16 +95,16 @@ float4 fetch_attr(thread const attr_desc& desc, thread const int& vertex_id, thr { int _156 = first_byte; first_byte = _156 + 1; - tmp.x = input_stream.read(uint2(_156, 0)).x; + tmp.x = input_stream.read(spvTexelBufferCoord(_156)).x; int _163 = first_byte; first_byte = _163 + 1; - tmp.y = input_stream.read(uint2(_163, 0)).x; + tmp.y = input_stream.read(spvTexelBufferCoord(_163)).x; int _170 = first_byte; first_byte = _170 + 1; - tmp.z = input_stream.read(uint2(_170, 0)).x; + tmp.z = input_stream.read(spvTexelBufferCoord(_170)).x; int _177 = first_byte; first_byte = _177 + 1; - tmp.w = input_stream.read(uint2(_177, 0)).x; + tmp.w = input_stream.read(spvTexelBufferCoord(_177)).x; uint4 param_2 = tmp; int param_3 = desc.swap_bytes; result[n] = as_type(get_bits(param_2, param_3)); @@ -108,7 +114,7 @@ float4 fetch_attr(thread const attr_desc& desc, thread const int& vertex_id, thr { int _195 = first_byte; first_byte = _195 + 1; - result[n] = float(input_stream.read(uint2(_195, 0)).x); + result[n] = float(input_stream.read(spvTexelBufferCoord(_195)).x); reverse_order = desc.swap_bytes != 0; break; } diff --git a/deps/SPIRV-Cross/reference/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag b/deps/SPIRV-Cross/reference/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..72d41f3e70 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,67 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +constant float _46[16] = {1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0}; +constant float4 _76[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; +constant float4 _90[4] = {float4(20.0), float4(30.0), float4(50.0), float4(60.0)}; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment. +template +void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N]) +{ + for (uint i = 0; i < N; dst[i] = src[i], i++); +} + +// An overload for constant arrays. +template +void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N]) +{ + for (uint i = 0; i < N; dst[i] = src[i], i++); +} + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + float4 foobar[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; + float4 baz[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; + main0_out out = {}; + out.FragColor = _46[in.index]; + if (in.index < 10) + { + out.FragColor += _46[in.index ^ 1]; + } + else + { + out.FragColor += _46[in.index & 1]; + } + if (in.index > 30) + { + out.FragColor += _76[in.index & 3].y; + } + else + { + out.FragColor += _76[in.index & 1].x; + } + if (in.index > 30) + { + foobar[1].z = 20.0; + } + out.FragColor += foobar[in.index & 3].z; + spvArrayCopyConstant(baz, _90); + out.FragColor += baz[in.index & 3].z; + return out; +} + diff --git a/deps/SPIRV-Cross/reference/shaders-msl/asm/frag/op-constant-null.asm.frag b/deps/SPIRV-Cross/reference/shaders-msl/asm/frag/op-constant-null.asm.frag index 1d9d11c978..670cb5f371 100644 --- a/deps/SPIRV-Cross/reference/shaders-msl/asm/frag/op-constant-null.asm.frag +++ b/deps/SPIRV-Cross/reference/shaders-msl/asm/frag/op-constant-null.asm.frag @@ -1,5 +1,3 @@ -#pragma clang diagnostic ignored "-Wmissing-prototypes" - #include #include @@ -18,20 +16,6 @@ struct main0_out float FragColor [[color(0)]]; }; -// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment. -template -void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N]) -{ - for (uint i = 0; i < N; dst[i] = src[i], i++); -} - -// An overload for constant arrays. -template -void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N]) -{ - for (uint i = 0; i < N; dst[i] = src[i], i++); -} - fragment main0_out main0() { main0_out out = {}; @@ -39,7 +23,6 @@ fragment main0_out main0() float4 b = float4(0.0); float2x3 c = float2x3(float3(0.0), float3(0.0)); D d = {float4(0.0), 0.0}; - float4 e[4] = {float4(0.0), float4(0.0), float4(0.0), float4(0.0)}; out.FragColor = a; return out; } diff --git a/deps/SPIRV-Cross/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/deps/SPIRV-Cross/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..005af22e3a --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,34 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +float sample_combined(thread float3& vUV, thread depth2d uShadow, thread const sampler uShadowSmplr) +{ + return uShadow.sample_compare(uShadowSmplr, vUV.xy, vUV.z); +} + +float sample_separate(thread float3& vUV, thread depth2d uTexture, thread sampler uSampler) +{ + return uTexture.sample_compare(uSampler, vUV.xy, vUV.z); +} + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uShadow [[texture(0)]], depth2d uTexture [[texture(1)]], sampler uShadowSmplr [[sampler(0)]], sampler uSampler [[sampler(2)]]) +{ + main0_out out = {}; + out.FragColor = sample_combined(in.vUV, uShadow, uShadowSmplr) + sample_separate(in.vUV, uTexture, uSampler); + return out; +} + diff --git a/deps/SPIRV-Cross/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/deps/SPIRV-Cross/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..89ca17f98b --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,28 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +float4 _main(thread const uint& vid, thread const uint& iid) +{ + return float4(float(vid + iid)); +} + +vertex main0_out main0(uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + uint vid = gl_VertexIndex; + uint iid = gl_InstanceIndex; + uint param = vid; + uint param_1 = iid; + out.gl_Position = _main(param, param_1); + return out; +} + diff --git a/deps/SPIRV-Cross/reference/shaders-msl/comp/composite-construct.comp b/deps/SPIRV-Cross/reference/shaders-msl/comp/composite-construct.comp index fb4ed1f101..ef0412b1dc 100644 --- a/deps/SPIRV-Cross/reference/shaders-msl/comp/composite-construct.comp +++ b/deps/SPIRV-Cross/reference/shaders-msl/comp/composite-construct.comp @@ -42,8 +42,7 @@ kernel void main0(device SSBO0& _16 [[buffer(0)]], device SSBO1& _32 [[buffer(1) float4 _37[2] = { _16.as[gl_GlobalInvocationID.x], _32.bs[gl_GlobalInvocationID.x] }; float4 values[2]; spvArrayCopy(values, _37); - float4 copy_values[2] = {float4(20.0), float4(40.0)}; - Composite c = Composite{ values[0], copy_values[1] }; + Composite c = Composite{ values[0], _43[1] }; _16.as[0] = values[gl_LocalInvocationIndex]; _32.bs[1] = c.b; } diff --git a/deps/SPIRV-Cross/reference/shaders-msl/frag/array-lut-no-loop-variable.frag b/deps/SPIRV-Cross/reference/shaders-msl/frag/array-lut-no-loop-variable.frag index 8943a8e5a1..37f83aae26 100644 --- a/deps/SPIRV-Cross/reference/shaders-msl/frag/array-lut-no-loop-variable.frag +++ b/deps/SPIRV-Cross/reference/shaders-msl/frag/array-lut-no-loop-variable.frag @@ -1,5 +1,3 @@ -#pragma clang diagnostic ignored "-Wmissing-prototypes" - #include #include @@ -12,25 +10,10 @@ struct main0_out float4 FragColor [[color(0)]]; }; -// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment. -template -void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N]) -{ - for (uint i = 0; i < N; dst[i] = src[i], i++); -} - -// An overload for constant arrays. -template -void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N]) -{ - for (uint i = 0; i < N; dst[i] = src[i], i++); -} - fragment main0_out main0() { main0_out out = {}; - float lut[5] = {1.0, 2.0, 3.0, 4.0, 5.0}; - for (int i = 0; i < 4; i++, out.FragColor += float4(lut[i])) + for (int i = 0; i < 4; i++, out.FragColor += float4(_17[i])) { } return out; diff --git a/deps/SPIRV-Cross/reference/shaders-msl/frag/constant-array.frag b/deps/SPIRV-Cross/reference/shaders-msl/frag/constant-array.frag index 09f123b29a..212d4f0803 100644 --- a/deps/SPIRV-Cross/reference/shaders-msl/frag/constant-array.frag +++ b/deps/SPIRV-Cross/reference/shaders-msl/frag/constant-array.frag @@ -49,12 +49,10 @@ float4 resolve(thread const Foobar& f) fragment main0_out main0(main0_in in [[stage_in]]) { main0_out out = {}; - float4 indexable[3] = {float4(1.0), float4(2.0), float4(3.0)}; - float4 indexable_1[2][2] = {{float4(1.0), float4(2.0)}, {float4(8.0), float4(10.0)}}; Foobar param = {10.0, 20.0}; - Foobar indexable_2[2] = {{10.0, 40.0}, {90.0, 70.0}}; - Foobar param_1 = indexable_2[in.index]; - out.FragColor = ((indexable[in.index] + (indexable_1[in.index][in.index + 1])) + resolve(param)) + resolve(param_1); + Foobar indexable[2] = {{10.0, 40.0}, {90.0, 70.0}}; + Foobar param_1 = indexable[in.index]; + out.FragColor = ((_37[in.index] + _55[in.index][in.index + 1]) + resolve(param)) + resolve(param_1); return out; } diff --git a/deps/SPIRV-Cross/reference/shaders-msl/frag/constant-composites.frag b/deps/SPIRV-Cross/reference/shaders-msl/frag/constant-composites.frag index ec5d66e86d..cb3e592337 100644 --- a/deps/SPIRV-Cross/reference/shaders-msl/frag/constant-composites.frag +++ b/deps/SPIRV-Cross/reference/shaders-msl/frag/constant-composites.frag @@ -44,7 +44,7 @@ fragment main0_out main0(main0_in in [[stage_in]]) float lut[4] = {1.0, 4.0, 3.0, 2.0}; Foo foos[2] = {{10.0, 20.0}, {30.0, 40.0}}; out.FragColor = float4(lut[in.line]); - out.FragColor += float4(foos[in.line].a * (foos[1 - in.line].a)); + out.FragColor += float4(foos[in.line].a * foos[1 - in.line].a); return out; } diff --git a/deps/SPIRV-Cross/reference/shaders-msl/frag/lut-promotion.frag b/deps/SPIRV-Cross/reference/shaders-msl/frag/lut-promotion.frag new file mode 100644 index 0000000000..a8eda3e13a --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-msl/frag/lut-promotion.frag @@ -0,0 +1,67 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +constant float _16[16] = {1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0}; +constant float4 _60[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; +constant float4 _104[4] = {float4(20.0), float4(30.0), float4(50.0), float4(60.0)}; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment. +template +void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N]) +{ + for (uint i = 0; i < N; dst[i] = src[i], i++); +} + +// An overload for constant arrays. +template +void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N]) +{ + for (uint i = 0; i < N; dst[i] = src[i], i++); +} + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = _16[in.index]; + if (in.index < 10) + { + out.FragColor += _16[in.index ^ 1]; + } + else + { + out.FragColor += _16[in.index & 1]; + } + if (in.index > 30) + { + out.FragColor += _60[in.index & 3].y; + } + else + { + out.FragColor += _60[in.index & 1].x; + } + float4 foobar[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; + if (in.index > 30) + { + foobar[1].z = 20.0; + } + out.FragColor += foobar[in.index & 3].z; + float4 baz[4] = {float4(0.0), float4(1.0), float4(8.0), float4(5.0)}; + spvArrayCopyConstant(baz, _104); + out.FragColor += baz[in.index & 3].z; + return out; +} + diff --git a/deps/SPIRV-Cross/reference/shaders-msl/frag/spec-constant-ternary.frag b/deps/SPIRV-Cross/reference/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..5ab6b4fcb1 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-msl/frag/spec-constant-ternary.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +constant uint s_tmp [[function_constant(0)]]; +constant uint s = is_function_constant_defined(s_tmp) ? s_tmp : 10u; +constant bool _13 = (s > 20u); +constant uint _16 = _13 ? 30u : 50u; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float(_16); + return out; +} + diff --git a/deps/SPIRV-Cross/reference/shaders-msl/vert/set_builtin_in_func.vert b/deps/SPIRV-Cross/reference/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000000..2952748dc0 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-msl/vert/set_builtin_in_func.vert @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; +}; + +void write_outblock(thread float4& gl_Position, thread float& gl_PointSize) +{ + gl_PointSize = 1.0; + gl_Position = float4(gl_PointSize); +} + +vertex main0_out main0() +{ + main0_out out = {}; + write_outblock(out.gl_Position, out.gl_PointSize); + return out; +} + diff --git a/deps/SPIRV-Cross/reference/shaders-msl/vert/texture_buffer.vert b/deps/SPIRV-Cross/reference/shaders-msl/vert/texture_buffer.vert index f7bcb7918b..c45d298134 100644 --- a/deps/SPIRV-Cross/reference/shaders-msl/vert/texture_buffer.vert +++ b/deps/SPIRV-Cross/reference/shaders-msl/vert/texture_buffer.vert @@ -1,3 +1,5 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + #include #include @@ -8,10 +10,16 @@ struct main0_out float4 gl_Position [[position]]; }; +// Returns 2D texture coords corresponding to 1D texel buffer coords +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + vertex main0_out main0(texture2d uSamp [[texture(4)]], texture2d uSampo [[texture(5)]]) { main0_out out = {}; - out.gl_Position = uSamp.read(uint2(10, 0)) + uSampo.read(uint2(100, 0)); + out.gl_Position = uSamp.read(spvTexelBufferCoord(10)) + uSampo.read(spvTexelBufferCoord(100)); return out; } diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json b/deps/SPIRV-Cross/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json new file mode 100644 index 0000000000..45adf50b14 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json @@ -0,0 +1,49 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "vert" + }, + { + "name" : "main2", + "mode" : "vert" + }, + { + "name" : "main", + "mode" : "frag" + }, + { + "name" : "main2", + "mode" : "frag" + } + ], + "types" : { + "_8" : { + "name" : "_8", + "members" : [ + { + "name" : "_m0", + "type" : "vec4" + }, + { + "name" : "_m1", + "type" : "float" + }, + { + "name" : "_m2", + "type" : "float", + "array" : [ + 1 + ] + }, + { + "name" : "_m3", + "type" : "float", + "array" : [ + 1 + ] + } + ] + } + } +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/comp/struct-layout.comp.json b/deps/SPIRV-Cross/reference/shaders-reflection/comp/struct-layout.comp.json new file mode 100644 index 0000000000..3004454b80 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/comp/struct-layout.comp.json @@ -0,0 +1,64 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp" + } + ], + "types" : { + "_19" : { + "name" : "Foo", + "members" : [ + { + "name" : "m", + "type" : "mat4", + "offset" : 0 + } + ] + }, + "_21" : { + "name" : "SSBO2", + "members" : [ + { + "name" : "out_data", + "type" : "_19", + "array" : [ + 0 + ], + "offset" : 0 + } + ] + }, + "_28" : { + "name" : "SSBO", + "members" : [ + { + "name" : "in_data", + "type" : "_19", + "array" : [ + 0 + ], + "offset" : 0 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_21", + "name" : "SSBO2", + "writeonly" : true, + "block_size" : 0, + "set" : 0, + "binding" : 1 + }, + { + "type" : "_28", + "name" : "SSBO", + "readonly" : true, + "block_size" : 0, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/comp/struct-packing.comp.json b/deps/SPIRV-Cross/reference/shaders-reflection/comp/struct-packing.comp.json new file mode 100644 index 0000000000..22a41584d9 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/comp/struct-packing.comp.json @@ -0,0 +1,474 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp" + } + ], + "types" : { + "_11" : { + "name" : "S0", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 8 + } + ] + }, + "_14" : { + "name" : "S1", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 12 + } + ] + }, + "_17" : { + "name" : "S2", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 16 + } + ] + }, + "_19" : { + "name" : "S3", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 8 + } + ] + }, + "_20" : { + "name" : "S4", + "members" : [ + { + "name" : "c", + "type" : "vec2", + "offset" : 0 + } + ] + }, + "_23" : { + "name" : "Content", + "members" : [ + { + "name" : "m0s", + "type" : "_11", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "m1s", + "type" : "_14", + "array" : [ + 1 + ], + "offset" : 16 + }, + { + "name" : "m2s", + "type" : "_17", + "array" : [ + 1 + ], + "offset" : 32 + }, + { + "name" : "m0", + "type" : "_11", + "offset" : 64 + }, + { + "name" : "m1", + "type" : "_14", + "offset" : 80 + }, + { + "name" : "m2", + "type" : "_17", + "offset" : 96 + }, + { + "name" : "m3", + "type" : "_19", + "offset" : 128 + }, + { + "name" : "m4", + "type" : "float", + "offset" : 144 + }, + { + "name" : "m3s", + "type" : "_20", + "array" : [ + 8 + ], + "offset" : 152 + } + ] + }, + "_36" : { + "name" : "SSBO1", + "members" : [ + { + "name" : "content", + "type" : "_23", + "offset" : 0 + }, + { + "name" : "content1", + "type" : "_23", + "array" : [ + 2 + ], + "offset" : 224 + }, + { + "name" : "content2", + "type" : "_23", + "offset" : 672 + }, + { + "name" : "m0", + "type" : "mat2", + "offset" : 896 + }, + { + "name" : "m1", + "type" : "mat2", + "offset" : 912 + }, + { + "name" : "m2", + "type" : "mat2x3", + "array" : [ + 4 + ], + "offset" : 928 + }, + { + "name" : "m3", + "type" : "mat3x2", + "offset" : 1056 + }, + { + "name" : "m4", + "type" : "mat2", + "row_major" : true, + "offset" : 1080 + }, + { + "name" : "m5", + "type" : "mat2", + "row_major" : true, + "array" : [ + 9 + ], + "offset" : 1096 + }, + { + "name" : "m6", + "type" : "mat2x3", + "row_major" : true, + "array" : [ + 2, + 4 + ], + "offset" : 1240 + }, + { + "name" : "m7", + "type" : "mat3x2", + "row_major" : true, + "offset" : 1440 + }, + { + "name" : "array", + "type" : "float", + "array" : [ + 0 + ], + "offset" : 1472 + } + ] + }, + "_42" : { + "name" : "S0", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 16 + } + ] + }, + "_44" : { + "name" : "S1", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 12 + } + ] + }, + "_47" : { + "name" : "S2", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 16 + } + ] + }, + "_49" : { + "name" : "S3", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 8 + } + ] + }, + "_50" : { + "name" : "S4", + "members" : [ + { + "name" : "c", + "type" : "vec2", + "offset" : 0 + } + ] + }, + "_52" : { + "name" : "Content", + "members" : [ + { + "name" : "m0s", + "type" : "_42", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "m1s", + "type" : "_44", + "array" : [ + 1 + ], + "offset" : 32 + }, + { + "name" : "m2s", + "type" : "_47", + "array" : [ + 1 + ], + "offset" : 48 + }, + { + "name" : "m0", + "type" : "_42", + "offset" : 80 + }, + { + "name" : "m1", + "type" : "_44", + "offset" : 112 + }, + { + "name" : "m2", + "type" : "_47", + "offset" : 128 + }, + { + "name" : "m3", + "type" : "_49", + "offset" : 160 + }, + { + "name" : "m4", + "type" : "float", + "offset" : 176 + }, + { + "name" : "m3s", + "type" : "_50", + "array" : [ + 8 + ], + "offset" : 192 + } + ] + }, + "_59" : { + "name" : "SSBO0", + "members" : [ + { + "name" : "content", + "type" : "_52", + "offset" : 0 + }, + { + "name" : "content1", + "type" : "_52", + "array" : [ + 2 + ], + "offset" : 320 + }, + { + "name" : "content2", + "type" : "_52", + "offset" : 960 + }, + { + "name" : "m0", + "type" : "mat2", + "offset" : 1280 + }, + { + "name" : "m1", + "type" : "mat2", + "offset" : 1312 + }, + { + "name" : "m2", + "type" : "mat2x3", + "array" : [ + 4 + ], + "offset" : 1344 + }, + { + "name" : "m3", + "type" : "mat3x2", + "offset" : 1472 + }, + { + "name" : "m4", + "type" : "mat2", + "row_major" : true, + "offset" : 1520 + }, + { + "name" : "m5", + "type" : "mat2", + "row_major" : true, + "array" : [ + 9 + ], + "offset" : 1552 + }, + { + "name" : "m6", + "type" : "mat2x3", + "row_major" : true, + "array" : [ + 2, + 4 + ], + "offset" : 1840 + }, + { + "name" : "m7", + "type" : "mat3x2", + "row_major" : true, + "offset" : 2224 + }, + { + "name" : "array", + "type" : "float", + "array" : [ + 0 + ], + "offset" : 2256 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_36", + "name" : "SSBO1", + "restrict" : true, + "block_size" : 1472, + "set" : 0, + "binding" : 1 + }, + { + "type" : "_59", + "name" : "SSBO0", + "restrict" : true, + "block_size" : 2256, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json b/deps/SPIRV-Cross/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json new file mode 100644 index 0000000000..5b4d3c6f7b --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json @@ -0,0 +1,37 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "outputs" : [ + { + "type" : "float", + "name" : "FragColor", + "location" : 0 + } + ], + "separate_images" : [ + { + "type" : "texture2D", + "name" : "uDepth", + "set" : 0, + "binding" : 2 + } + ], + "separate_samplers" : [ + { + "type" : "sampler", + "name" : "uSampler", + "set" : 0, + "binding" : 0 + }, + { + "type" : "sampler", + "name" : "uSampler1", + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json b/deps/SPIRV-Cross/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json new file mode 100644 index 0000000000..8b6a184299 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json @@ -0,0 +1,50 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "inputs" : [ + { + "type" : "vec2", + "name" : "vTex", + "location" : 0 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "separate_images" : [ + { + "type" : "texture2D", + "name" : "uTexture0", + "set" : 0, + "binding" : 2 + }, + { + "type" : "texture2D", + "name" : "uTexture1", + "set" : 0, + "binding" : 3 + } + ], + "separate_samplers" : [ + { + "type" : "sampler", + "name" : "uSampler0", + "set" : 0, + "binding" : 0 + }, + { + "type" : "sampler", + "name" : "uSampler1", + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json b/deps/SPIRV-Cross/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json new file mode 100644 index 0000000000..527ea2bfee --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json @@ -0,0 +1,47 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "_entryPointOutput", + "location" : 0 + } + ], + "textures" : [ + { + "type" : "sampler2D", + "name" : "ROIm", + "set" : 0, + "binding" : 1 + } + ], + "separate_images" : [ + { + "type" : "samplerBuffer", + "name" : "ROBuf", + "set" : 0, + "binding" : 0 + } + ], + "images" : [ + { + "type" : "image2D", + "name" : "RWIm", + "set" : 0, + "binding" : 1, + "format" : "rgba32f" + }, + { + "type" : "imageBuffer", + "name" : "RWBuf", + "set" : 0, + "binding" : 0, + "format" : "rgba32f" + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json b/deps/SPIRV-Cross/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json new file mode 100644 index 0000000000..5f381911ac --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json @@ -0,0 +1,31 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "subpass_inputs" : [ + { + "type" : "subpassInputMS", + "name" : "uSubpass0", + "set" : 0, + "binding" : 0, + "input_attachment_index" : 0 + }, + { + "type" : "subpassInputMS", + "name" : "uSubpass1", + "set" : 0, + "binding" : 1, + "input_attachment_index" : 1 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/frag/input-attachment.vk.frag.json b/deps/SPIRV-Cross/reference/shaders-reflection/frag/input-attachment.vk.frag.json new file mode 100644 index 0000000000..16ae6a4683 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/frag/input-attachment.vk.frag.json @@ -0,0 +1,31 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "subpass_inputs" : [ + { + "type" : "subpassInput", + "name" : "uSubpass0", + "set" : 0, + "binding" : 0, + "input_attachment_index" : 0 + }, + { + "type" : "subpassInput", + "name" : "uSubpass1", + "set" : 0, + "binding" : 1, + "input_attachment_index" : 1 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/frag/push-constant.vk.frag.json b/deps/SPIRV-Cross/reference/shaders-reflection/frag/push-constant.vk.frag.json new file mode 100644 index 0000000000..f72a8fd654 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/frag/push-constant.vk.frag.json @@ -0,0 +1,46 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "types" : { + "_13" : { + "name" : "PushConstants", + "members" : [ + { + "name" : "value0", + "type" : "vec4", + "offset" : 0 + }, + { + "name" : "value1", + "type" : "vec4", + "offset" : 16 + } + ] + } + }, + "inputs" : [ + { + "type" : "vec4", + "name" : "vColor", + "location" : 0 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "push_constants" : [ + { + "type" : "_13", + "name" : "push", + "push_constant" : true + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json b/deps/SPIRV-Cross/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json new file mode 100644 index 0000000000..9216d93e5d --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json @@ -0,0 +1,73 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "inputs" : [ + { + "type" : "vec2", + "name" : "vTex", + "location" : 0 + }, + { + "type" : "vec3", + "name" : "vTex3", + "location" : 1 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "separate_images" : [ + { + "type" : "texture2D", + "name" : "uTexture", + "array" : [ + 4 + ], + "set" : 0, + "binding" : 1 + }, + { + "type" : "texture2DArray", + "name" : "uTextureArray", + "array" : [ + 4 + ], + "set" : 0, + "binding" : 4 + }, + { + "type" : "textureCube", + "name" : "uTextureCube", + "array" : [ + 4 + ], + "set" : 0, + "binding" : 3 + }, + { + "type" : "texture3D", + "name" : "uTexture3D", + "array" : [ + 4 + ], + "set" : 0, + "binding" : 2 + } + ], + "separate_samplers" : [ + { + "type" : "sampler", + "name" : "uSampler", + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/frag/spec-constant.vk.frag.json b/deps/SPIRV-Cross/reference/shaders-reflection/frag/spec-constant.vk.frag.json new file mode 100644 index 0000000000..0add298666 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/frag/spec-constant.vk.frag.json @@ -0,0 +1,71 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "types" : { + "_137" : { + "name" : "Foo", + "members" : [ + { + "name" : "elems", + "type" : "float", + "array" : [ + 135 + ] + } + ] + } + }, + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "specialization_constants" : [ + { + "id" : 1, + "type" : "float", + "default_value" : 1.5 + }, + { + "id" : 2, + "type" : "float", + "default_value" : 2.5 + }, + { + "id" : 3, + "type" : "int", + "default_value" : 3 + }, + { + "id" : 4, + "type" : "int", + "default_value" : 4 + }, + { + "id" : 5, + "type" : "uint", + "default_value" : 5 + }, + { + "id" : 6, + "type" : "uint", + "default_value" : 6 + }, + { + "id" : 7, + "type" : "bool", + "default_value" : false + }, + { + "id" : 8, + "type" : "bool", + "default_value" : true + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/vert/read-from-row-major-array.vert.json b/deps/SPIRV-Cross/reference/shaders-reflection/vert/read-from-row-major-array.vert.json new file mode 100644 index 0000000000..d92fb67fb5 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/vert/read-from-row-major-array.vert.json @@ -0,0 +1,61 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "vert" + } + ], + "types" : { + "_89" : { + "name" : "gl_PerVertex", + "members" : [ + { + "name" : "gl_Position", + "type" : "vec4" + }, + { + "name" : "gl_PointSize", + "type" : "float" + } + ] + }, + "_102" : { + "name" : "Block", + "members" : [ + { + "name" : "var", + "type" : "mat2x3", + "row_major" : true, + "array" : [ + 4, + 3 + ], + "offset" : 0 + } + ] + } + }, + "inputs" : [ + { + "type" : "vec4", + "name" : "a_position", + "location" : 0 + } + ], + "outputs" : [ + { + "type" : "float", + "name" : "v_vtxResult", + "location" : 0 + } + ], + "ubos" : [ + { + "type" : "_102", + "name" : "Block", + "block_size" : 576, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders-reflection/vert/texture_buffer.vert.json b/deps/SPIRV-Cross/reference/shaders-reflection/vert/texture_buffer.vert.json new file mode 100644 index 0000000000..3c69e24cbc --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders-reflection/vert/texture_buffer.vert.json @@ -0,0 +1,40 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "vert" + } + ], + "types" : { + "_8" : { + "name" : "gl_PerVertex", + "members" : [ + { + "name" : "gl_Position", + "type" : "vec4" + }, + { + "name" : "gl_PointSize", + "type" : "float" + } + ] + } + }, + "textures" : [ + { + "type" : "samplerBuffer", + "name" : "uSamp", + "set" : 0, + "binding" : 4 + } + ], + "images" : [ + { + "type" : "imageBuffer", + "name" : "uSampo", + "set" : 0, + "binding" : 5, + "format" : "rgba32f" + } + ] +} \ No newline at end of file diff --git a/deps/SPIRV-Cross/reference/shaders/asm/frag/lut-promotion-initializer.asm.frag b/deps/SPIRV-Cross/reference/shaders/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..c08bc2c781 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,40 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _46[16] = float[](1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0); +const vec4 _76[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + +layout(location = 0) out float FragColor; +layout(location = 0) flat in mediump int index; + +void main() +{ + vec4 foobar[4] = _76; + vec4 baz[4] = _76; + FragColor = _46[index]; + if (index < 10) + { + FragColor += _46[index ^ 1]; + } + else + { + FragColor += _46[index & 1]; + } + if (index > 30) + { + FragColor += _76[index & 3].y; + } + else + { + FragColor += _76[index & 1].x; + } + if (index > 30) + { + foobar[1].z = 20.0; + } + FragColor += foobar[index & 3].z; + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[index & 3].z; +} + diff --git a/deps/SPIRV-Cross/reference/shaders/asm/frag/op-constant-null.asm.frag b/deps/SPIRV-Cross/reference/shaders/asm/frag/op-constant-null.asm.frag index c4ae981f64..970b4c4a66 100644 --- a/deps/SPIRV-Cross/reference/shaders/asm/frag/op-constant-null.asm.frag +++ b/deps/SPIRV-Cross/reference/shaders/asm/frag/op-constant-null.asm.frag @@ -2,6 +2,8 @@ precision mediump float; precision highp int; +const vec4 _14[4] = vec4[](vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); + struct D { vec4 a; @@ -16,7 +18,6 @@ void main() vec4 b = vec4(0.0); mat2x3 c = mat2x3(vec3(0.0), vec3(0.0)); D d = D(vec4(0.0), 0.0); - vec4 e[4] = vec4[](vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); FragColor = a; } diff --git a/deps/SPIRV-Cross/reference/shaders/asm/frag/switch-label-shared-block.asm.frag b/deps/SPIRV-Cross/reference/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000000..ade9044e35 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders/asm/frag/switch-label-shared-block.asm.frag @@ -0,0 +1,33 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) flat in mediump int vIndex; +layout(location = 0) out float FragColor; + +void main() +{ + highp float _19; + switch (vIndex) + { + case 0: + case 2: + { + _19 = 1.0; + break; + } + case 1: + default: + { + _19 = 3.0; + break; + } + case 8: + { + _19 = 8.0; + break; + } + } + FragColor = _19; +} + diff --git a/deps/SPIRV-Cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/deps/SPIRV-Cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000000..7729d30c09 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(binding = 0) uniform sampler2DShadow uShadow; +uniform sampler2DShadow SPIRV_Cross_CombineduTextureuSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +float sample_combined() +{ + return texture(uShadow, vec3(vUV.xy, vUV.z)); +} + +float sample_separate() +{ + return texture(SPIRV_Cross_CombineduTextureuSampler, vec3(vUV.xy, vUV.z)); +} + +void main() +{ + FragColor = sample_combined() + sample_separate(); +} + diff --git a/deps/SPIRV-Cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk b/deps/SPIRV-Cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk new file mode 100644 index 0000000000..711fa27763 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk @@ -0,0 +1,24 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2DShadow uShadow; +layout(set = 0, binding = 1) uniform texture2D uTexture; +layout(set = 0, binding = 2) uniform samplerShadow uSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +float sample_combined() +{ + return texture(uShadow, vec3(vUV.xy, vUV.z)); +} + +float sample_separate() +{ + return texture(sampler2DShadow(uTexture, uSampler), vec3(vUV.xy, vUV.z)); +} + +void main() +{ + FragColor = sample_combined() + sample_separate(); +} + diff --git a/deps/SPIRV-Cross/reference/shaders/asm/geom/store-uint-layer.invalid.asm.geom b/deps/SPIRV-Cross/reference/shaders/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000000..c768d5da86 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders/asm/geom/store-uint-layer.invalid.asm.geom @@ -0,0 +1,41 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +struct VertexOutput +{ + vec4 pos; +}; + +struct GeometryOutput +{ + vec4 pos; + uint layer; +}; + +void _main(VertexOutput _input[3], GeometryOutput stream) +{ + GeometryOutput _output; + _output.layer = 1u; + for (int v = 0; v < 3; v++) + { + _output.pos = _input[v].pos; + gl_Position = _output.pos; + gl_Layer = int(_output.layer); + EmitVertex(); + } + EndPrimitive(); +} + +void main() +{ + VertexOutput _input[3]; + _input[0].pos = gl_in[0].gl_Position; + _input[1].pos = gl_in[1].gl_Position; + _input[2].pos = gl_in[2].gl_Position; + VertexOutput param[3] = _input; + GeometryOutput param_1; + _main(param, param_1); + GeometryOutput stream = param_1; +} + diff --git a/deps/SPIRV-Cross/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/deps/SPIRV-Cross/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..31f13bd777 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,18 @@ +#version 450 + +uniform int SPIRV_Cross_BaseInstance; + +vec4 _main(uint vid, uint iid) +{ + return vec4(float(vid + iid)); +} + +void main() +{ + uint vid = uint(gl_VertexID); + uint iid = uint((gl_InstanceID + SPIRV_Cross_BaseInstance)); + uint param = vid; + uint param_1 = iid; + gl_Position = _main(param, param_1); +} + diff --git a/deps/SPIRV-Cross/reference/shaders/comp/composite-construct.comp b/deps/SPIRV-Cross/reference/shaders/comp/composite-construct.comp index 91bb5348f5..3018be8f1b 100644 --- a/deps/SPIRV-Cross/reference/shaders/comp/composite-construct.comp +++ b/deps/SPIRV-Cross/reference/shaders/comp/composite-construct.comp @@ -1,6 +1,9 @@ #version 310 es layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; +const vec4 _66[2] = vec4[](vec4(10.0), vec4(30.0)); +const float _94[2][3] = float[][](float[](1.0, 1.0, 1.0), float[](2.0, 2.0, 2.0)); + struct Composite { vec4 a[2]; @@ -25,13 +28,11 @@ vec4 summe(vec4 values[3][2]) void main() { vec4 values[2] = vec4[](_41.as[gl_GlobalInvocationID.x], _55.bs[gl_GlobalInvocationID.x]); - vec4 const_values[2] = vec4[](vec4(10.0), vec4(30.0)); - vec4 copy_values[2] = const_values; + vec4 copy_values[2] = _66; vec4 copy_values2[2] = values; vec4 param[3][2] = vec4[][](values, copy_values, copy_values2); _41.as[gl_GlobalInvocationID.x] = summe(param); Composite c = Composite(values, copy_values); - float arrayofarray[2][3] = float[][](float[](1.0, 1.0, 1.0), float[](2.0, 2.0, 2.0)); float b = 10.0; float values_scalar[4] = float[](b, b, b, b); } diff --git a/deps/SPIRV-Cross/reference/shaders/flatten/copy.flatten.vert b/deps/SPIRV-Cross/reference/shaders/flatten/copy.flatten.vert index 2bdd723886..6416a4f425 100644 --- a/deps/SPIRV-Cross/reference/shaders/flatten/copy.flatten.vert +++ b/deps/SPIRV-Cross/reference/shaders/flatten/copy.flatten.vert @@ -23,7 +23,7 @@ void main() light.Radius = Light(UBO[i * 2 + 4].xyz, UBO[i * 2 + 4].w, UBO[i * 2 + 5]).Radius; light.Color = Light(UBO[i * 2 + 4].xyz, UBO[i * 2 + 4].w, UBO[i * 2 + 5]).Color; vec3 L = aVertex.xyz - light.Position; - vColor += (((UBO[i * 2 + 5]) * clamp(1.0 - (length(L) / light.Radius), 0.0, 1.0)) * dot(aNormal, normalize(L))); + vColor += ((UBO[i * 2 + 5] * clamp(1.0 - (length(L) / light.Radius), 0.0, 1.0)) * dot(aNormal, normalize(L))); } } diff --git a/deps/SPIRV-Cross/reference/shaders/flatten/dynamic.flatten.vert b/deps/SPIRV-Cross/reference/shaders/flatten/dynamic.flatten.vert index 6214ca450a..8be397ea3f 100644 --- a/deps/SPIRV-Cross/reference/shaders/flatten/dynamic.flatten.vert +++ b/deps/SPIRV-Cross/reference/shaders/flatten/dynamic.flatten.vert @@ -18,8 +18,8 @@ void main() vColor = vec4(0.0); for (int i = 0; i < 4; i++) { - vec3 L = aVertex.xyz - (UBO[i * 2 + 4].xyz); - vColor += (((UBO[i * 2 + 5]) * clamp(1.0 - (length(L) / (UBO[i * 2 + 4].w)), 0.0, 1.0)) * dot(aNormal, normalize(L))); + vec3 L = aVertex.xyz - UBO[i * 2 + 4].xyz; + vColor += ((UBO[i * 2 + 5] * clamp(1.0 - (length(L) / UBO[i * 2 + 4].w), 0.0, 1.0)) * dot(aNormal, normalize(L))); } } diff --git a/deps/SPIRV-Cross/reference/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag b/deps/SPIRV-Cross/reference/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag index 21c3363ca6..ef6bb526ab 100644 --- a/deps/SPIRV-Cross/reference/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag +++ b/deps/SPIRV-Cross/reference/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag @@ -19,6 +19,6 @@ void main() } } } - FragColor = ((values3[1 * 3 * 1 + 2 * 1 + 0]) + (values3[0 * 3 * 1 + 2 * 1 + 0])) + (values3[(vIndex + 1) * 3 * 1 + 2 * 1 + vIndex]); + FragColor = (values3[1 * 3 * 1 + 2 * 1 + 0] + values3[0 * 3 * 1 + 2 * 1 + 0]) + values3[(vIndex + 1) * 3 * 1 + 2 * 1 + vIndex]; } diff --git a/deps/SPIRV-Cross/reference/shaders/frag/array-lut-no-loop-variable.frag b/deps/SPIRV-Cross/reference/shaders/frag/array-lut-no-loop-variable.frag index 54d7bf774c..baf2302519 100644 --- a/deps/SPIRV-Cross/reference/shaders/frag/array-lut-no-loop-variable.frag +++ b/deps/SPIRV-Cross/reference/shaders/frag/array-lut-no-loop-variable.frag @@ -2,12 +2,13 @@ precision mediump float; precision highp int; +const float _17[5] = float[](1.0, 2.0, 3.0, 4.0, 5.0); + layout(location = 0) out vec4 FragColor; void main() { - float lut[5] = float[](1.0, 2.0, 3.0, 4.0, 5.0); - for (mediump int i = 0; i < 4; i++, FragColor += vec4(lut[i])) + for (mediump int i = 0; i < 4; i++, FragColor += vec4(_17[i])) { } } diff --git a/deps/SPIRV-Cross/reference/shaders/frag/constant-array.frag b/deps/SPIRV-Cross/reference/shaders/frag/constant-array.frag index 4da9b8948b..be033f3873 100644 --- a/deps/SPIRV-Cross/reference/shaders/frag/constant-array.frag +++ b/deps/SPIRV-Cross/reference/shaders/frag/constant-array.frag @@ -2,6 +2,9 @@ precision mediump float; precision highp int; +const vec4 _37[3] = vec4[](vec4(1.0), vec4(2.0), vec4(3.0)); +const vec4 _55[2][2] = vec4[][](vec4[](vec4(1.0), vec4(2.0)), vec4[](vec4(8.0), vec4(10.0))); + struct Foobar { float a; @@ -18,11 +21,9 @@ vec4 resolve(Foobar f) void main() { - highp vec4 indexable[3] = vec4[](vec4(1.0), vec4(2.0), vec4(3.0)); - highp vec4 indexable_1[2][2] = vec4[][](vec4[](vec4(1.0), vec4(2.0)), vec4[](vec4(8.0), vec4(10.0))); Foobar param = Foobar(10.0, 20.0); - Foobar indexable_2[2] = Foobar[](Foobar(10.0, 40.0), Foobar(90.0, 70.0)); - Foobar param_1 = indexable_2[index]; - FragColor = ((indexable[index] + (indexable_1[index][index + 1])) + resolve(param)) + resolve(param_1); + Foobar indexable[2] = Foobar[](Foobar(10.0, 40.0), Foobar(90.0, 70.0)); + Foobar param_1 = indexable[index]; + FragColor = ((_37[index] + _55[index][index + 1]) + resolve(param)) + resolve(param_1); } diff --git a/deps/SPIRV-Cross/reference/shaders/frag/constant-composites.frag b/deps/SPIRV-Cross/reference/shaders/frag/constant-composites.frag index b105dbd26c..c65c60613d 100644 --- a/deps/SPIRV-Cross/reference/shaders/frag/constant-composites.frag +++ b/deps/SPIRV-Cross/reference/shaders/frag/constant-composites.frag @@ -18,6 +18,6 @@ void main() lut = float[](1.0, 4.0, 3.0, 2.0); foos = Foo[](Foo(10.0, 20.0), Foo(30.0, 40.0)); FragColor = vec4(lut[line]); - FragColor += vec4(foos[line].a * (foos[1 - line].a)); + FragColor += vec4(foos[line].a * foos[1 - line].a); } diff --git a/deps/SPIRV-Cross/reference/shaders/frag/lut-promotion.frag b/deps/SPIRV-Cross/reference/shaders/frag/lut-promotion.frag new file mode 100644 index 0000000000..019393f9f3 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders/frag/lut-promotion.frag @@ -0,0 +1,40 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _16[16] = float[](1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0); +const vec4 _60[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + +layout(location = 0) out float FragColor; +layout(location = 0) flat in mediump int index; + +void main() +{ + FragColor = _16[index]; + if (index < 10) + { + FragColor += _16[index ^ 1]; + } + else + { + FragColor += _16[index & 1]; + } + if (index > 30) + { + FragColor += _60[index & 3].y; + } + else + { + FragColor += _60[index & 1].x; + } + vec4 foobar[4] = _60; + if (index > 30) + { + foobar[1].z = 20.0; + } + FragColor += foobar[index & 3].z; + vec4 baz[4] = _60; + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[index & 3].z; +} + diff --git a/deps/SPIRV-Cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/deps/SPIRV-Cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000000..91b0331b79 --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float((10u > 20u) ? 30u : 50u); +} + diff --git a/deps/SPIRV-Cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk b/deps/SPIRV-Cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk new file mode 100644 index 0000000000..59d3b99b9c --- /dev/null +++ b/deps/SPIRV-Cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk @@ -0,0 +1,13 @@ +#version 450 + +layout(constant_id = 0) const uint s = 10u; +const bool _13 = (s > 20u); +const uint _16 = _13 ? 30u : 50u; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float(_16); +} + diff --git a/deps/SPIRV-Cross/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/deps/SPIRV-Cross/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000000..74a15955c2 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-hlsl/asm/comp/control-flow-hints.asm.comp @@ -0,0 +1,146 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 85 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %i "i" + OpName %bar "bar" + OpMemberName %bar 0 "@data" + OpName %bar_0 "bar" + OpName %foo "foo" + OpName %i_0 "i" + OpName %v "v" + OpName %w "w" + OpName %value "value" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %bar 0 Offset 0 + OpDecorate %bar BufferBlock + OpDecorate %bar_0 DescriptorSet 0 + OpDecorate %bar_0 Binding 0 + OpDecorate %foo DescriptorSet 0 + OpDecorate %foo Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + %bool = OpTypeBool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %bar = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_bar = OpTypePointer Uniform %bar + %bar_0 = OpVariable %_ptr_Uniform_bar Uniform + %foo = OpVariable %_ptr_Uniform_bar Uniform +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_1 = OpConstant %int 1 + %int_15 = OpConstant %int 15 +%_ptr_Function_float = OpTypePointer Function %float + %int_10 = OpConstant %int 10 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_10 = OpConstant %float 10 + %int_20 = OpConstant %int 20 + %float_5 = OpConstant %float 5 + %72 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %float_20 = OpConstant %float 20 + %float_40 = OpConstant %float 40 + %main = OpFunction %void None %3 + %5 = OpLabel + %84 = OpFunctionCall %void %_main_ + OpReturn + OpFunctionEnd + %_main_ = OpFunction %void None %3 + %7 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %i_0 = OpVariable %_ptr_Function_int Function + %v = OpVariable %_ptr_Function_float Function + %w = OpVariable %_ptr_Function_float Function + %value = OpVariable %_ptr_Function_float Function + OpStore %i %int_0 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 Unroll + OpBranch %16 + %16 = OpLabel + %17 = OpLoad %int %i + %20 = OpSLessThan %bool %17 %int_16 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %27 = OpLoad %int %i + %29 = OpLoad %int %i + %31 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %29 + %32 = OpLoad %v4float %31 + %33 = OpAccessChain %_ptr_Uniform_v4float %bar_0 %int_0 %27 + OpStore %33 %32 + OpBranch %15 + %15 = OpLabel + %34 = OpLoad %int %i + %36 = OpIAdd %int %34 %int_1 + OpStore %i %36 + OpBranch %12 + %14 = OpLabel + OpStore %i_0 %int_0 + OpBranch %38 + %38 = OpLabel + OpLoopMerge %40 %41 DontUnroll + OpBranch %42 + %42 = OpLabel + %43 = OpLoad %int %i_0 + %44 = OpSLessThan %bool %43 %int_16 + OpBranchConditional %44 %39 %40 + %39 = OpLabel + %46 = OpLoad %int %i_0 + %47 = OpISub %int %int_15 %46 + %48 = OpLoad %int %i_0 + %49 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %48 + %50 = OpLoad %v4float %49 + %51 = OpAccessChain %_ptr_Uniform_v4float %bar_0 %int_0 %47 + OpStore %51 %50 + OpBranch %41 + %41 = OpLabel + %52 = OpLoad %int %i_0 + %53 = OpIAdd %int %52 %int_1 + OpStore %i_0 %53 + OpBranch %38 + %40 = OpLabel + %60 = OpAccessChain %_ptr_Uniform_float %bar_0 %int_0 %int_10 %uint_0 + %61 = OpLoad %float %60 + OpStore %v %61 + %63 = OpAccessChain %_ptr_Uniform_float %foo %int_0 %int_10 %uint_0 + %64 = OpLoad %float %63 + OpStore %w %64 + %65 = OpLoad %float %v + %67 = OpFOrdGreaterThan %bool %65 %float_10 + OpSelectionMerge %69 DontFlatten + OpBranchConditional %67 %68 %69 + %68 = OpLabel + %73 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %int_20 + OpStore %73 %72 + OpBranch %69 + %69 = OpLabel + OpStore %value %float_20 + %76 = OpLoad %float %w + %78 = OpFOrdGreaterThan %bool %76 %float_40 + OpSelectionMerge %80 Flatten + OpBranchConditional %78 %79 %80 + %79 = OpLabel + OpStore %value %float_20 + OpBranch %80 + %80 = OpLabel + %81 = OpLoad %float %value + %82 = OpCompositeConstruct %v4float %81 %81 %81 %81 + %83 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %int_20 + OpStore %83 %82 + OpReturn + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag b/deps/SPIRV-Cross/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..320e5ebfbd --- /dev/null +++ b/deps/SPIRV-Cross/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,195 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 111 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %index + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %index "index" + OpName %indexable "indexable" + OpName %indexable_0 "indexable" + OpName %indexable_1 "indexable" + OpName %foo "foo" + OpName %foobar "foobar" + OpName %baz "baz" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %index RelaxedPrecision + OpDecorate %index Flat + OpDecorate %index Location 0 + OpDecorate %20 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %foo RelaxedPrecision + OpDecorate %61 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %71 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %79 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + OpDecorate %foobar RelaxedPrecision + OpDecorate %83 RelaxedPrecision + OpDecorate %90 RelaxedPrecision + OpDecorate %91 RelaxedPrecision + OpDecorate %93 RelaxedPrecision + OpDecorate %94 RelaxedPrecision + OpDecorate %95 RelaxedPrecision + OpDecorate %baz RelaxedPrecision + OpDecorate %105 RelaxedPrecision + OpDecorate %106 RelaxedPrecision + OpDecorate %108 RelaxedPrecision + OpDecorate %109 RelaxedPrecision + OpDecorate %110 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %uint = OpTypeInt 32 0 + %uint_16 = OpConstant %uint 16 +%_arr_float_uint_16 = OpTypeArray %float %uint_16 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %16 = OpConstantComposite %_arr_float_uint_16 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %index = OpVariable %_ptr_Input_int Input +%_ptr_Function__arr_float_uint_16 = OpTypePointer Function %_arr_float_uint_16 +%_ptr_Function_float = OpTypePointer Function %float + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %v4float = OpTypeVector %float 4 + %uint_4 = OpConstant %uint 4 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 +%_ptr_Function__arr_v4float_uint_4 = OpTypePointer Function %_arr_v4float_uint_4 + %float_0 = OpConstant %float 0 + %54 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %55 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_8 = OpConstant %float 8 + %57 = OpConstantComposite %v4float %float_8 %float_8 %float_8 %float_8 + %float_5 = OpConstant %float 5 + %59 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %60 = OpConstantComposite %_arr_v4float_uint_4 %54 %55 %57 %59 + %int_30 = OpConstant %int 30 + %int_3 = OpConstant %int 3 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %float_20 = OpConstant %float 20 + %uint_2 = OpConstant %uint 2 + %97 = OpConstantComposite %v4float %float_20 %float_20 %float_20 %float_20 + %float_30 = OpConstant %float 30 + %99 = OpConstantComposite %v4float %float_30 %float_30 %float_30 %float_30 + %float_50 = OpConstant %float 50 + %101 = OpConstantComposite %v4float %float_50 %float_50 %float_50 %float_50 + %float_60 = OpConstant %float 60 + %103 = OpConstantComposite %v4float %float_60 %float_60 %float_60 %float_60 + %104 = OpConstantComposite %_arr_v4float_uint_4 %97 %99 %101 %103 + %main = OpFunction %void None %3 + %5 = OpLabel + %indexable = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_0 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_1 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 + %foo = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %foobar = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %baz = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %20 = OpLoad %int %index + %24 = OpAccessChain %_ptr_Function_float %indexable %20 + %25 = OpLoad %float %24 + OpStore %FragColor %25 + %26 = OpLoad %int %index + %29 = OpSLessThan %bool %26 %int_10 + OpSelectionMerge %31 None + OpBranchConditional %29 %30 %40 + %30 = OpLabel + %32 = OpLoad %int %index + %34 = OpBitwiseXor %int %32 %int_1 + %36 = OpAccessChain %_ptr_Function_float %indexable_0 %34 + %37 = OpLoad %float %36 + %38 = OpLoad %float %FragColor + %39 = OpFAdd %float %38 %37 + OpStore %FragColor %39 + OpBranch %31 + %40 = OpLabel + %41 = OpLoad %int %index + %42 = OpBitwiseAnd %int %41 %int_1 + %44 = OpAccessChain %_ptr_Function_float %indexable_1 %42 + %45 = OpLoad %float %44 + %46 = OpLoad %float %FragColor + %47 = OpFAdd %float %46 %45 + OpStore %FragColor %47 + OpBranch %31 + %31 = OpLabel + %61 = OpLoad %int %index + %63 = OpSGreaterThan %bool %61 %int_30 + OpSelectionMerge %65 None + OpBranchConditional %63 %64 %74 + %64 = OpLabel + %66 = OpLoad %int %index + %68 = OpBitwiseAnd %int %66 %int_3 + %70 = OpAccessChain %_ptr_Function_float %foo %68 %uint_1 + %71 = OpLoad %float %70 + %72 = OpLoad %float %FragColor + %73 = OpFAdd %float %72 %71 + OpStore %FragColor %73 + OpBranch %65 + %74 = OpLabel + %75 = OpLoad %int %index + %76 = OpBitwiseAnd %int %75 %int_1 + %78 = OpAccessChain %_ptr_Function_float %foo %76 %uint_0 + %79 = OpLoad %float %78 + %80 = OpLoad %float %FragColor + %81 = OpFAdd %float %80 %79 + OpStore %FragColor %81 + OpBranch %65 + %65 = OpLabel + %83 = OpLoad %int %index + %84 = OpSGreaterThan %bool %83 %int_30 + OpSelectionMerge %86 None + OpBranchConditional %84 %85 %86 + %85 = OpLabel + %89 = OpAccessChain %_ptr_Function_float %foobar %int_1 %uint_2 + OpStore %89 %float_20 + OpBranch %86 + %86 = OpLabel + %90 = OpLoad %int %index + %91 = OpBitwiseAnd %int %90 %int_3 + %92 = OpAccessChain %_ptr_Function_float %foobar %91 %uint_2 + %93 = OpLoad %float %92 + %94 = OpLoad %float %FragColor + %95 = OpFAdd %float %94 %93 + OpStore %FragColor %95 + OpStore %baz %104 + %105 = OpLoad %int %index + %106 = OpBitwiseAnd %int %105 %int_3 + %107 = OpAccessChain %_ptr_Function_float %baz %106 %uint_2 + %108 = OpLoad %float %107 + %109 = OpLoad %float %FragColor + %110 = OpFAdd %float %109 %108 + OpStore %FragColor %110 + OpReturn + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/deps/SPIRV-Cross/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..89036f0eb2 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 44 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vUV %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %sample_combined_ "sample_combined(" + OpName %sample_separate_ "sample_separate(" + OpName %uShadow "uShadow" + OpName %vUV "vUV" + OpName %uTexture "uTexture" + OpName %uSampler "uSampler" + OpName %FragColor "FragColor" + OpDecorate %uShadow DescriptorSet 0 + OpDecorate %uShadow Binding 0 + OpDecorate %vUV Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 2 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float + %12 = OpTypeImage %float 2D 2 0 0 1 Unknown + %13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %uShadow = OpVariable %_ptr_UniformConstant_13 UniformConstant + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %vUV = OpVariable %_ptr_Input_v3float Input +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_25 UniformConstant + %29 = OpTypeSampler +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 + %uSampler = OpVariable %_ptr_UniformConstant_29 UniformConstant +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %41 = OpFunctionCall %float %sample_combined_ + %42 = OpFunctionCall %float %sample_separate_ + %43 = OpFAdd %float %41 %42 + OpStore %FragColor %43 + OpReturn + OpFunctionEnd +%sample_combined_ = OpFunction %float None %7 + %9 = OpLabel + %16 = OpLoad %13 %uShadow + %20 = OpLoad %v3float %vUV + %21 = OpCompositeExtract %float %20 2 + %22 = OpImageSampleDrefImplicitLod %float %16 %20 %21 + OpReturnValue %22 + OpFunctionEnd +%sample_separate_ = OpFunction %float None %7 + %11 = OpLabel + %28 = OpLoad %12 %uTexture + %32 = OpLoad %29 %uSampler + %33 = OpSampledImage %13 %28 %32 + %34 = OpLoad %v3float %vUV + %35 = OpCompositeExtract %float %34 2 + %36 = OpImageSampleDrefImplicitLod %float %33 %34 %35 + OpReturnValue %36 + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/deps/SPIRV-Cross/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..29b0076a1e --- /dev/null +++ b/deps/SPIRV-Cross/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %vid_1 %iid_1 %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_u1_u1_ "@main(u1;u1;" + OpName %vid "vid" + OpName %iid "iid" + OpName %vid_0 "vid" + OpName %vid_1 "vid" + OpName %iid_0 "iid" + OpName %iid_1 "iid" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %vid_1 BuiltIn VertexIndex + OpDecorate %iid_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_uint %_ptr_Function_uint +%_ptr_Input_uint = OpTypePointer Input %uint + %vid_1 = OpVariable %_ptr_Input_uint Input + %iid_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %vid_0 = OpVariable %_ptr_Function_uint Function + %iid_0 = OpVariable %_ptr_Function_uint Function + %param = OpVariable %_ptr_Function_uint Function + %param_0 = OpVariable %_ptr_Function_uint Function + %25 = OpLoad %uint %vid_1 + OpStore %vid_0 %25 + %28 = OpLoad %uint %iid_1 + OpStore %iid_0 %28 + %32 = OpLoad %uint %vid_0 + OpStore %param %32 + %34 = OpLoad %uint %iid_0 + OpStore %param_0 %34 + %35 = OpFunctionCall %v4float %_main_u1_u1_ %param %param_0 + OpStore %_entryPointOutput %35 + OpReturn + OpFunctionEnd +%_main_u1_u1_ = OpFunction %v4float None %10 + %vid = OpFunctionParameter %_ptr_Function_uint + %iid = OpFunctionParameter %_ptr_Function_uint + %14 = OpLabel + %15 = OpLoad %uint %vid + %16 = OpLoad %uint %iid + %17 = OpIAdd %uint %15 %16 + %18 = OpConvertUToF %float %17 + %19 = OpCompositeConstruct %v4float %18 %18 %18 %18 + OpReturnValue %19 + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders-hlsl/comp/globallycoherent.comp b/deps/SPIRV-Cross/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000000..168b9404ea --- /dev/null +++ b/deps/SPIRV-Cross/shaders-hlsl/comp/globallycoherent.comp @@ -0,0 +1,25 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(r32f, binding = 0) uniform readonly image2D uImageIn; +layout(r32f, binding = 1) uniform coherent writeonly image2D uImageOut; + +layout(set = 0, binding = 2) readonly buffer Foo +{ + float foo; +}; + +layout(set = 0, binding = 3) coherent writeonly buffer Bar +{ + float bar; +}; + +void main() +{ + ivec2 coord = ivec2(9, 7); + vec4 indata = imageLoad(uImageIn, coord); + imageStore(uImageOut, coord, indata); + + bar = foo; +} + diff --git a/deps/SPIRV-Cross/shaders-hlsl/frag/lut-promotion.frag b/deps/SPIRV-Cross/shaders-hlsl/frag/lut-promotion.frag new file mode 100644 index 0000000000..0cdc8148f9 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-hlsl/frag/lut-promotion.frag @@ -0,0 +1,44 @@ +#version 310 es +precision mediump float; +layout(location = 0) out float FragColor; +layout(location = 0) flat in int index; + +const float LUT[16] = float[]( + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0); + +void main() +{ + // Try reading LUTs, both in branches and not branch. + FragColor = LUT[index]; + if (index < 10) + FragColor += LUT[index ^ 1]; + else + FragColor += LUT[index & 1]; + + // Not declared as a LUT, but can be promoted to one. + vec4 foo[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + FragColor += foo[index & 3].y; + } + else + { + FragColor += foo[index & 1].x; + } + + // Not declared as a LUT, but this cannot be promoted, because we have a partial write. + vec4 foobar[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + foobar[1].z = 20.0; + } + FragColor += foobar[index & 3].z; + + // Not declared as a LUT, but this cannot be promoted, because we have two complete writes. + vec4 baz[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[index & 3].z; +} diff --git a/deps/SPIRV-Cross/shaders-hlsl/frag/spec-constant-ternary.frag b/deps/SPIRV-Cross/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..78dccbf044 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-hlsl/frag/spec-constant-ternary.frag @@ -0,0 +1,9 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(constant_id = 0) const uint s = 10u; +const uint f = s > 20u ? 30u : 50u; + +void main() +{ + FragColor = float(f); +} diff --git a/deps/SPIRV-Cross/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag b/deps/SPIRV-Cross/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..320e5ebfbd --- /dev/null +++ b/deps/SPIRV-Cross/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,195 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 111 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %index + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %index "index" + OpName %indexable "indexable" + OpName %indexable_0 "indexable" + OpName %indexable_1 "indexable" + OpName %foo "foo" + OpName %foobar "foobar" + OpName %baz "baz" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %index RelaxedPrecision + OpDecorate %index Flat + OpDecorate %index Location 0 + OpDecorate %20 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %foo RelaxedPrecision + OpDecorate %61 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %71 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %79 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + OpDecorate %foobar RelaxedPrecision + OpDecorate %83 RelaxedPrecision + OpDecorate %90 RelaxedPrecision + OpDecorate %91 RelaxedPrecision + OpDecorate %93 RelaxedPrecision + OpDecorate %94 RelaxedPrecision + OpDecorate %95 RelaxedPrecision + OpDecorate %baz RelaxedPrecision + OpDecorate %105 RelaxedPrecision + OpDecorate %106 RelaxedPrecision + OpDecorate %108 RelaxedPrecision + OpDecorate %109 RelaxedPrecision + OpDecorate %110 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %uint = OpTypeInt 32 0 + %uint_16 = OpConstant %uint 16 +%_arr_float_uint_16 = OpTypeArray %float %uint_16 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %16 = OpConstantComposite %_arr_float_uint_16 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %index = OpVariable %_ptr_Input_int Input +%_ptr_Function__arr_float_uint_16 = OpTypePointer Function %_arr_float_uint_16 +%_ptr_Function_float = OpTypePointer Function %float + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %v4float = OpTypeVector %float 4 + %uint_4 = OpConstant %uint 4 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 +%_ptr_Function__arr_v4float_uint_4 = OpTypePointer Function %_arr_v4float_uint_4 + %float_0 = OpConstant %float 0 + %54 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %55 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_8 = OpConstant %float 8 + %57 = OpConstantComposite %v4float %float_8 %float_8 %float_8 %float_8 + %float_5 = OpConstant %float 5 + %59 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %60 = OpConstantComposite %_arr_v4float_uint_4 %54 %55 %57 %59 + %int_30 = OpConstant %int 30 + %int_3 = OpConstant %int 3 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %float_20 = OpConstant %float 20 + %uint_2 = OpConstant %uint 2 + %97 = OpConstantComposite %v4float %float_20 %float_20 %float_20 %float_20 + %float_30 = OpConstant %float 30 + %99 = OpConstantComposite %v4float %float_30 %float_30 %float_30 %float_30 + %float_50 = OpConstant %float 50 + %101 = OpConstantComposite %v4float %float_50 %float_50 %float_50 %float_50 + %float_60 = OpConstant %float 60 + %103 = OpConstantComposite %v4float %float_60 %float_60 %float_60 %float_60 + %104 = OpConstantComposite %_arr_v4float_uint_4 %97 %99 %101 %103 + %main = OpFunction %void None %3 + %5 = OpLabel + %indexable = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_0 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_1 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 + %foo = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %foobar = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %baz = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %20 = OpLoad %int %index + %24 = OpAccessChain %_ptr_Function_float %indexable %20 + %25 = OpLoad %float %24 + OpStore %FragColor %25 + %26 = OpLoad %int %index + %29 = OpSLessThan %bool %26 %int_10 + OpSelectionMerge %31 None + OpBranchConditional %29 %30 %40 + %30 = OpLabel + %32 = OpLoad %int %index + %34 = OpBitwiseXor %int %32 %int_1 + %36 = OpAccessChain %_ptr_Function_float %indexable_0 %34 + %37 = OpLoad %float %36 + %38 = OpLoad %float %FragColor + %39 = OpFAdd %float %38 %37 + OpStore %FragColor %39 + OpBranch %31 + %40 = OpLabel + %41 = OpLoad %int %index + %42 = OpBitwiseAnd %int %41 %int_1 + %44 = OpAccessChain %_ptr_Function_float %indexable_1 %42 + %45 = OpLoad %float %44 + %46 = OpLoad %float %FragColor + %47 = OpFAdd %float %46 %45 + OpStore %FragColor %47 + OpBranch %31 + %31 = OpLabel + %61 = OpLoad %int %index + %63 = OpSGreaterThan %bool %61 %int_30 + OpSelectionMerge %65 None + OpBranchConditional %63 %64 %74 + %64 = OpLabel + %66 = OpLoad %int %index + %68 = OpBitwiseAnd %int %66 %int_3 + %70 = OpAccessChain %_ptr_Function_float %foo %68 %uint_1 + %71 = OpLoad %float %70 + %72 = OpLoad %float %FragColor + %73 = OpFAdd %float %72 %71 + OpStore %FragColor %73 + OpBranch %65 + %74 = OpLabel + %75 = OpLoad %int %index + %76 = OpBitwiseAnd %int %75 %int_1 + %78 = OpAccessChain %_ptr_Function_float %foo %76 %uint_0 + %79 = OpLoad %float %78 + %80 = OpLoad %float %FragColor + %81 = OpFAdd %float %80 %79 + OpStore %FragColor %81 + OpBranch %65 + %65 = OpLabel + %83 = OpLoad %int %index + %84 = OpSGreaterThan %bool %83 %int_30 + OpSelectionMerge %86 None + OpBranchConditional %84 %85 %86 + %85 = OpLabel + %89 = OpAccessChain %_ptr_Function_float %foobar %int_1 %uint_2 + OpStore %89 %float_20 + OpBranch %86 + %86 = OpLabel + %90 = OpLoad %int %index + %91 = OpBitwiseAnd %int %90 %int_3 + %92 = OpAccessChain %_ptr_Function_float %foobar %91 %uint_2 + %93 = OpLoad %float %92 + %94 = OpLoad %float %FragColor + %95 = OpFAdd %float %94 %93 + OpStore %FragColor %95 + OpStore %baz %104 + %105 = OpLoad %int %index + %106 = OpBitwiseAnd %int %105 %int_3 + %107 = OpAccessChain %_ptr_Function_float %baz %106 %uint_2 + %108 = OpLoad %float %107 + %109 = OpLoad %float %FragColor + %110 = OpFAdd %float %109 %108 + OpStore %FragColor %110 + OpReturn + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/deps/SPIRV-Cross/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..89036f0eb2 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-msl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 44 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vUV %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %sample_combined_ "sample_combined(" + OpName %sample_separate_ "sample_separate(" + OpName %uShadow "uShadow" + OpName %vUV "vUV" + OpName %uTexture "uTexture" + OpName %uSampler "uSampler" + OpName %FragColor "FragColor" + OpDecorate %uShadow DescriptorSet 0 + OpDecorate %uShadow Binding 0 + OpDecorate %vUV Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 2 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float + %12 = OpTypeImage %float 2D 2 0 0 1 Unknown + %13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %uShadow = OpVariable %_ptr_UniformConstant_13 UniformConstant + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %vUV = OpVariable %_ptr_Input_v3float Input +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_25 UniformConstant + %29 = OpTypeSampler +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 + %uSampler = OpVariable %_ptr_UniformConstant_29 UniformConstant +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %41 = OpFunctionCall %float %sample_combined_ + %42 = OpFunctionCall %float %sample_separate_ + %43 = OpFAdd %float %41 %42 + OpStore %FragColor %43 + OpReturn + OpFunctionEnd +%sample_combined_ = OpFunction %float None %7 + %9 = OpLabel + %16 = OpLoad %13 %uShadow + %20 = OpLoad %v3float %vUV + %21 = OpCompositeExtract %float %20 2 + %22 = OpImageSampleDrefImplicitLod %float %16 %20 %21 + OpReturnValue %22 + OpFunctionEnd +%sample_separate_ = OpFunction %float None %7 + %11 = OpLabel + %28 = OpLoad %12 %uTexture + %32 = OpLoad %29 %uSampler + %33 = OpSampledImage %13 %28 %32 + %34 = OpLoad %v3float %vUV + %35 = OpCompositeExtract %float %34 2 + %36 = OpImageSampleDrefImplicitLod %float %33 %34 %35 + OpReturnValue %36 + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/deps/SPIRV-Cross/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..29b0076a1e --- /dev/null +++ b/deps/SPIRV-Cross/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %vid_1 %iid_1 %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_u1_u1_ "@main(u1;u1;" + OpName %vid "vid" + OpName %iid "iid" + OpName %vid_0 "vid" + OpName %vid_1 "vid" + OpName %iid_0 "iid" + OpName %iid_1 "iid" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %vid_1 BuiltIn VertexIndex + OpDecorate %iid_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_uint %_ptr_Function_uint +%_ptr_Input_uint = OpTypePointer Input %uint + %vid_1 = OpVariable %_ptr_Input_uint Input + %iid_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %vid_0 = OpVariable %_ptr_Function_uint Function + %iid_0 = OpVariable %_ptr_Function_uint Function + %param = OpVariable %_ptr_Function_uint Function + %param_0 = OpVariable %_ptr_Function_uint Function + %25 = OpLoad %uint %vid_1 + OpStore %vid_0 %25 + %28 = OpLoad %uint %iid_1 + OpStore %iid_0 %28 + %32 = OpLoad %uint %vid_0 + OpStore %param %32 + %34 = OpLoad %uint %iid_0 + OpStore %param_0 %34 + %35 = OpFunctionCall %v4float %_main_u1_u1_ %param %param_0 + OpStore %_entryPointOutput %35 + OpReturn + OpFunctionEnd +%_main_u1_u1_ = OpFunction %v4float None %10 + %vid = OpFunctionParameter %_ptr_Function_uint + %iid = OpFunctionParameter %_ptr_Function_uint + %14 = OpLabel + %15 = OpLoad %uint %vid + %16 = OpLoad %uint %iid + %17 = OpIAdd %uint %15 %16 + %18 = OpConvertUToF %float %17 + %19 = OpCompositeConstruct %v4float %18 %18 %18 %18 + OpReturnValue %19 + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders-msl/frag/lut-promotion.frag b/deps/SPIRV-Cross/shaders-msl/frag/lut-promotion.frag new file mode 100644 index 0000000000..0cdc8148f9 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-msl/frag/lut-promotion.frag @@ -0,0 +1,44 @@ +#version 310 es +precision mediump float; +layout(location = 0) out float FragColor; +layout(location = 0) flat in int index; + +const float LUT[16] = float[]( + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0); + +void main() +{ + // Try reading LUTs, both in branches and not branch. + FragColor = LUT[index]; + if (index < 10) + FragColor += LUT[index ^ 1]; + else + FragColor += LUT[index & 1]; + + // Not declared as a LUT, but can be promoted to one. + vec4 foo[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + FragColor += foo[index & 3].y; + } + else + { + FragColor += foo[index & 1].x; + } + + // Not declared as a LUT, but this cannot be promoted, because we have a partial write. + vec4 foobar[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + foobar[1].z = 20.0; + } + FragColor += foobar[index & 3].z; + + // Not declared as a LUT, but this cannot be promoted, because we have two complete writes. + vec4 baz[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[index & 3].z; +} diff --git a/deps/SPIRV-Cross/shaders-msl/frag/spec-constant-ternary.frag b/deps/SPIRV-Cross/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..78dccbf044 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-msl/frag/spec-constant-ternary.frag @@ -0,0 +1,9 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(constant_id = 0) const uint s = 10u; +const uint f = s > 20u ? 30u : 50u; + +void main() +{ + FragColor = float(f); +} diff --git a/deps/SPIRV-Cross/shaders-msl/vert/set_builtin_in_func.vert b/deps/SPIRV-Cross/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000000..dd991e3545 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-msl/vert/set_builtin_in_func.vert @@ -0,0 +1,12 @@ +#version 450 + +void write_outblock() +{ + gl_PointSize = 1.0; + gl_Position = vec4(gl_PointSize); +} + +void main() +{ + write_outblock(); +} diff --git a/deps/SPIRV-Cross/shaders-reflection/asm/aliased-entry-point-names.asm.multi b/deps/SPIRV-Cross/shaders-reflection/asm/aliased-entry-point-names.asm.multi new file mode 100644 index 0000000000..d60cf3039c --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/asm/aliased-entry-point-names.asm.multi @@ -0,0 +1,60 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ + OpEntryPoint Vertex %main2 "main2" %_ + OpEntryPoint Fragment %main3 "main" %FragColor + OpEntryPoint Fragment %main4 "main2" %FragColor + OpSource GLSL 450 + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %FragColor Location 0 + OpDecorate %gl_PerVertex Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v4floatptr = OpTypePointer Output %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %FragColor = OpVariable %v4floatptr Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %17 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_2 = OpConstant %float 2 + %18 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %19 %17 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %3 + %6 = OpLabel + %20 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %20 %18 + OpReturn + OpFunctionEnd + %main3 = OpFunction %void None %3 + %7 = OpLabel + OpStore %FragColor %17 + OpReturn + OpFunctionEnd + %main4 = OpFunction %void None %3 + %8 = OpLabel + OpStore %FragColor %18 + OpReturn + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders-reflection/comp/struct-layout.comp b/deps/SPIRV-Cross/shaders-reflection/comp/struct-layout.comp new file mode 100644 index 0000000000..5a2b7802df --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/comp/struct-layout.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 1) in; + +struct Foo +{ + mat4 m; +}; + +layout(std430, binding = 0) readonly buffer SSBO +{ + Foo in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + Foo out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + out_data[ident].m = in_data[ident].m * in_data[ident].m; +} + diff --git a/deps/SPIRV-Cross/shaders-reflection/comp/struct-packing.comp b/deps/SPIRV-Cross/shaders-reflection/comp/struct-packing.comp new file mode 100644 index 0000000000..d2ffbaef50 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/comp/struct-packing.comp @@ -0,0 +1,87 @@ +#version 450 + +layout(local_size_x = 1) in; + +struct S0 +{ + vec2 a[1]; + float b; +}; + +struct S1 +{ + vec3 a; + float b; +}; + +struct S2 +{ + vec3 a[1]; + float b; +}; + +struct S3 +{ + vec2 a; + float b; +}; + +struct S4 +{ + vec2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + + S4 m3s[8]; +}; + +layout(binding = 1, std430) restrict buffer SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_430; + +layout(binding = 0, std140) restrict buffer SSBO0 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + + float array[]; +} ssbo_140; + +void main() +{ + ssbo_430.content = ssbo_140.content; +} + diff --git a/deps/SPIRV-Cross/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag b/deps/SPIRV-Cross/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag new file mode 100644 index 0000000000..2fabb5ea8a --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump samplerShadow uSampler; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; +layout(set = 0, binding = 2) uniform texture2D uDepth; +layout(location = 0) out float FragColor; + +float samp2(texture2D t, mediump samplerShadow s) +{ + return texture(sampler2DShadow(t, s), vec3(1.0)); +} + +float samp3(texture2D t, mediump sampler s) +{ + return texture(sampler2D(t, s), vec2(1.0)).x; +} + +float samp(texture2D t, mediump samplerShadow s, mediump sampler s1) +{ + float r0 = samp2(t, s); + float r1 = samp3(t, s1); + return r0 + r1; +} + +void main() +{ + FragColor = samp(uDepth, uSampler, uSampler1); +} diff --git a/deps/SPIRV-Cross/shaders-reflection/frag/combined-texture-sampler.vk.frag b/deps/SPIRV-Cross/shaders-reflection/frag/combined-texture-sampler.vk.frag new file mode 100644 index 0000000000..b7de8d47e9 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/frag/combined-texture-sampler.vk.frag @@ -0,0 +1,47 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler uSampler0; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; +layout(set = 0, binding = 2) uniform mediump texture2D uTexture0; +layout(set = 0, binding = 3) uniform mediump texture2D uTexture1; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTex; + +vec4 sample_dual(mediump sampler samp, mediump texture2D tex) +{ + return texture(sampler2D(tex, samp), vTex); +} + +vec4 sample_global_tex(mediump sampler samp) +{ + vec4 a = texture(sampler2D(uTexture0, samp), vTex); + vec4 b = sample_dual(samp, uTexture1); + return a + b; +} + +vec4 sample_global_sampler(mediump texture2D tex) +{ + vec4 a = texture(sampler2D(tex, uSampler0), vTex); + vec4 b = sample_dual(uSampler1, tex); + return a + b; +} + +vec4 sample_duals() +{ + vec4 a = sample_dual(uSampler0, uTexture0); + vec4 b = sample_dual(uSampler1, uTexture1); + return a + b; +} + +void main() +{ + vec4 c0 = sample_duals(); + vec4 c1 = sample_global_tex(uSampler0); + vec4 c2 = sample_global_tex(uSampler1); + vec4 c3 = sample_global_sampler(uTexture0); + vec4 c4 = sample_global_sampler(uTexture1); + + FragColor = c0 + c1 + c2 + c3 + c4; +} diff --git a/deps/SPIRV-Cross/shaders-reflection/frag/image-load-store-uint-coord.asm.frag b/deps/SPIRV-Cross/shaders-reflection/frag/image-load-store-uint-coord.asm.frag new file mode 100644 index 0000000000..a9bf1a7497 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/frag/image-load-store-uint-coord.asm.frag @@ -0,0 +1,103 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 63 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %storeTemp "storeTemp" + OpName %RWIm "RWIm" + OpName %v "v" + OpName %RWBuf "RWBuf" + OpName %ROIm "ROIm" + OpName %ROBuf "ROBuf" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %RWIm DescriptorSet 0 + OpDecorate %RWIm Binding 1 + OpDecorate %RWBuf DescriptorSet 0 + OpDecorate %RWBuf Binding 0 + OpDecorate %ROIm DescriptorSet 0 + OpDecorate %ROIm Binding 1 + OpDecorate %ROBuf DescriptorSet 0 + OpDecorate %ROBuf Binding 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float + %float_10 = OpConstant %float 10 + %float_0_5 = OpConstant %float 0.5 + %float_8 = OpConstant %float 8 + %float_2 = OpConstant %float 2 + %17 = OpConstantComposite %v4float %float_10 %float_0_5 %float_8 %float_2 + %18 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %RWIm = OpVariable %_ptr_UniformConstant_18 UniformConstant + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %uint_10 = OpConstant %uint 10 + %25 = OpConstantComposite %v2uint %uint_10 %uint_10 + %uint_30 = OpConstant %uint 30 + %30 = OpConstantComposite %v2uint %uint_30 %uint_30 + %32 = OpTypeImage %float Buffer 0 0 0 2 Rgba32f +%_ptr_UniformConstant_32 = OpTypePointer UniformConstant %32 + %RWBuf = OpVariable %_ptr_UniformConstant_32 UniformConstant + %uint_80 = OpConstant %uint 80 + %38 = OpTypeImage %float 2D 0 0 0 1 Unknown + %SampledImage = OpTypeSampledImage %38 +%_ptr_UniformConstant_38 = OpTypePointer UniformConstant %SampledImage + %ROIm = OpVariable %_ptr_UniformConstant_38 UniformConstant + %uint_50 = OpConstant %uint 50 + %uint_60 = OpConstant %uint 60 + %44 = OpConstantComposite %v2uint %uint_50 %uint_60 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %50 = OpTypeImage %float Buffer 0 0 0 1 Rgba32f +%_ptr_UniformConstant_50 = OpTypePointer UniformConstant %50 + %ROBuf = OpVariable %_ptr_UniformConstant_50 UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %62 = OpFunctionCall %v4float %_main_ + OpStore %_entryPointOutput %62 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v4float None %8 + %10 = OpLabel + %storeTemp = OpVariable %_ptr_Function_v4float Function + %v = OpVariable %_ptr_Function_v4float Function + OpStore %storeTemp %17 + %21 = OpLoad %18 %RWIm + %26 = OpLoad %v4float %storeTemp + OpImageWrite %21 %25 %26 + %28 = OpLoad %18 %RWIm + %31 = OpImageRead %v4float %28 %30 + OpStore %v %31 + %35 = OpLoad %32 %RWBuf + %37 = OpLoad %v4float %v + OpImageWrite %35 %uint_80 %37 + %41 = OpLoad %SampledImage %ROIm + %ROImage = OpImage %38 %41 + %47 = OpImageFetch %v4float %ROImage %44 Lod %int_0 + %48 = OpLoad %v4float %v + %49 = OpFAdd %v4float %48 %47 + OpStore %v %49 + %53 = OpLoad %50 %ROBuf + %54 = OpImageFetch %v4float %53 %uint_80 + %55 = OpLoad %v4float %v + %56 = OpFAdd %v4float %55 %54 + OpStore %v %56 + %57 = OpLoad %v4float %v + OpReturnValue %57 + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders-reflection/frag/input-attachment-ms.vk.frag b/deps/SPIRV-Cross/shaders-reflection/frag/input-attachment-ms.vk.frag new file mode 100644 index 0000000000..e060738846 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/frag/input-attachment-ms.vk.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2) + subpassLoad(uSubpass0, gl_SampleID); +} diff --git a/deps/SPIRV-Cross/shaders-reflection/frag/input-attachment.vk.frag b/deps/SPIRV-Cross/shaders-reflection/frag/input-attachment.vk.frag new file mode 100644 index 0000000000..f082d15b2a --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/frag/input-attachment.vk.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0) + subpassLoad(uSubpass1); +} diff --git a/deps/SPIRV-Cross/shaders-reflection/frag/push-constant.vk.frag b/deps/SPIRV-Cross/shaders-reflection/frag/push-constant.vk.frag new file mode 100644 index 0000000000..6180faba31 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/frag/push-constant.vk.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(push_constant, std430) uniform PushConstants +{ + vec4 value0; + vec4 value1; +} push; + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor + push.value0 + push.value1; +} diff --git a/deps/SPIRV-Cross/shaders-reflection/frag/separate-sampler-texture-array.vk.frag b/deps/SPIRV-Cross/shaders-reflection/frag/separate-sampler-texture-array.vk.frag new file mode 100644 index 0000000000..b3501c1d8d --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/frag/separate-sampler-texture-array.vk.frag @@ -0,0 +1,42 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler uSampler; +layout(set = 0, binding = 1) uniform mediump texture2D uTexture[4]; +layout(set = 0, binding = 2) uniform mediump texture3D uTexture3D[4]; +layout(set = 0, binding = 3) uniform mediump textureCube uTextureCube[4]; +layout(set = 0, binding = 4) uniform mediump texture2DArray uTextureArray[4]; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; + +vec4 sample_func(mediump sampler samp, vec2 uv) +{ + return texture(sampler2D(uTexture[2], samp), uv); +} + +vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv) +{ + return texture(sampler2D(tex, samp), uv); +} + +vec4 sample_func_dual_array(mediump sampler samp, mediump texture2D tex[4], vec2 uv) +{ + return texture(sampler2D(tex[1], samp), uv); +} + +void main() +{ + vec2 off = 1.0 / vec2(textureSize(sampler2D(uTexture[1], uSampler), 0)); + vec2 off2 = 1.0 / vec2(textureSize(sampler2D(uTexture[2], uSampler), 1)); + + vec4 c0 = sample_func(uSampler, vTex + off + off2); + vec4 c1 = sample_func_dual(uSampler, uTexture[1], vTex + off + off2); + vec4 c2 = sample_func_dual_array(uSampler, uTexture, vTex + off + off2); + vec4 c3 = texture(sampler2DArray(uTextureArray[3], uSampler), vTex3); + vec4 c4 = texture(samplerCube(uTextureCube[1], uSampler), vTex3); + vec4 c5 = texture(sampler3D(uTexture3D[2], uSampler), vTex3); + + FragColor = c0 + c1 + c2 + c3 + c4 + c5; +} diff --git a/deps/SPIRV-Cross/shaders-reflection/frag/spec-constant.vk.frag b/deps/SPIRV-Cross/shaders-reflection/frag/spec-constant.vk.frag new file mode 100644 index 0000000000..e62a26059b --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/frag/spec-constant.vk.frag @@ -0,0 +1,78 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(constant_id = 1) const float a = 1.5; +layout(constant_id = 2) const float b = 2.5; +layout(constant_id = 3) const int c = 3; +layout(constant_id = 4) const int d = 4; +layout(constant_id = 5) const uint e = 5u; +layout(constant_id = 6) const uint f = 6u; +layout(constant_id = 7) const bool g = false; +layout(constant_id = 8) const bool h = true; + +// glslang doesn't seem to support partial spec constants or composites yet, so only test the basics. + +struct Foo +{ + float elems[d + 2]; +}; + +void main() +{ + float t0 = a; + float t1 = b; + + uint c0 = uint(c); // OpIAdd with different types. + // FConvert, float-to-double. + int c1 = -c; // SNegate + int c2 = ~c; // OpNot + int c3 = c + d; // OpIAdd + int c4 = c - d; // OpISub + int c5 = c * d; // OpIMul + int c6 = c / d; // OpSDiv + uint c7 = e / f; // OpUDiv + int c8 = c % d; // OpSMod + uint c9 = e % f; // OpUMod + // TODO: OpSRem, any way to access this in GLSL? + int c10 = c >> d; // OpShiftRightArithmetic + uint c11 = e >> f; // OpShiftRightLogical + int c12 = c << d; // OpShiftLeftLogical + int c13 = c | d; // OpBitwiseOr + int c14 = c ^ d; // OpBitwiseXor + int c15 = c & d; // OpBitwiseAnd + // VectorShuffle, CompositeExtract, CompositeInsert, not testable atm. + bool c16 = g || h; // OpLogicalOr + bool c17 = g && h; // OpLogicalAnd + bool c18 = !g; // OpLogicalNot + bool c19 = g == h; // OpLogicalEqual + bool c20 = g != h; // OpLogicalNotEqual + // OpSelect not testable atm. + bool c21 = c == d; // OpIEqual + bool c22 = c != d; // OpINotEqual + bool c23 = c < d; // OpSLessThan + bool c24 = e < f; // OpULessThan + bool c25 = c > d; // OpSGreaterThan + bool c26 = e > f; // OpUGreaterThan + bool c27 = c <= d; // OpSLessThanEqual + bool c28 = e <= f; // OpULessThanEqual + bool c29 = c >= d; // OpSGreaterThanEqual + bool c30 = e >= f; // OpUGreaterThanEqual + // OpQuantizeToF16 not testable atm. + + int c31 = c8 + c3; + + int c32 = int(e); // OpIAdd with different types. + bool c33 = bool(c); // int -> bool + bool c34 = bool(e); // uint -> bool + int c35 = int(g); // bool -> int + uint c36 = uint(g); // bool -> uint + float c37 = float(g); // bool -> float + + // Flexible sized arrays with spec constants and spec constant ops. + float vec0[c + 3][8]; + float vec1[c + 2]; + + Foo foo; + FragColor = vec4(t0 + t1) + vec0[0][0] + vec1[0] + foo.elems[c]; +} diff --git a/deps/SPIRV-Cross/shaders-reflection/vert/read-from-row-major-array.vert b/deps/SPIRV-Cross/shaders-reflection/vert/read-from-row-major-array.vert new file mode 100644 index 0000000000..792fb8e36c --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/vert/read-from-row-major-array.vert @@ -0,0 +1,20 @@ +#version 310 es +layout(location = 0) in highp vec4 a_position; +layout(location = 0) out mediump float v_vtxResult; + +layout(set = 0, binding = 0, std140, row_major) uniform Block +{ + highp mat2x3 var[3][4]; +}; + +mediump float compare_float (highp float a, highp float b) { return abs(a - b) < 0.05 ? 1.0 : 0.0; } +mediump float compare_vec3 (highp vec3 a, highp vec3 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z); } +mediump float compare_mat2x3 (highp mat2x3 a, highp mat2x3 b){ return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1]); } + +void main (void) +{ + gl_Position = a_position; + mediump float result = 1.0; + result *= compare_mat2x3(var[0][0], mat2x3(2.0, 6.0, -6.0, 0.0, 5.0, 5.0)); + v_vtxResult = result; +} diff --git a/deps/SPIRV-Cross/shaders-reflection/vert/texture_buffer.vert b/deps/SPIRV-Cross/shaders-reflection/vert/texture_buffer.vert new file mode 100644 index 0000000000..6bc7ddfae2 --- /dev/null +++ b/deps/SPIRV-Cross/shaders-reflection/vert/texture_buffer.vert @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_OES_texture_buffer : require + +layout(binding = 4) uniform highp samplerBuffer uSamp; +layout(rgba32f, binding = 5) uniform readonly highp imageBuffer uSampo; + +void main() +{ + gl_Position = texelFetch(uSamp, 10) + imageLoad(uSampo, 100); +} diff --git a/deps/SPIRV-Cross/shaders/asm/frag/lut-promotion-initializer.asm.frag b/deps/SPIRV-Cross/shaders/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..320e5ebfbd --- /dev/null +++ b/deps/SPIRV-Cross/shaders/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,195 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 111 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %index + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %index "index" + OpName %indexable "indexable" + OpName %indexable_0 "indexable" + OpName %indexable_1 "indexable" + OpName %foo "foo" + OpName %foobar "foobar" + OpName %baz "baz" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %index RelaxedPrecision + OpDecorate %index Flat + OpDecorate %index Location 0 + OpDecorate %20 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %foo RelaxedPrecision + OpDecorate %61 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %71 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %79 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + OpDecorate %foobar RelaxedPrecision + OpDecorate %83 RelaxedPrecision + OpDecorate %90 RelaxedPrecision + OpDecorate %91 RelaxedPrecision + OpDecorate %93 RelaxedPrecision + OpDecorate %94 RelaxedPrecision + OpDecorate %95 RelaxedPrecision + OpDecorate %baz RelaxedPrecision + OpDecorate %105 RelaxedPrecision + OpDecorate %106 RelaxedPrecision + OpDecorate %108 RelaxedPrecision + OpDecorate %109 RelaxedPrecision + OpDecorate %110 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %uint = OpTypeInt 32 0 + %uint_16 = OpConstant %uint 16 +%_arr_float_uint_16 = OpTypeArray %float %uint_16 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %16 = OpConstantComposite %_arr_float_uint_16 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %index = OpVariable %_ptr_Input_int Input +%_ptr_Function__arr_float_uint_16 = OpTypePointer Function %_arr_float_uint_16 +%_ptr_Function_float = OpTypePointer Function %float + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %v4float = OpTypeVector %float 4 + %uint_4 = OpConstant %uint 4 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 +%_ptr_Function__arr_v4float_uint_4 = OpTypePointer Function %_arr_v4float_uint_4 + %float_0 = OpConstant %float 0 + %54 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %55 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_8 = OpConstant %float 8 + %57 = OpConstantComposite %v4float %float_8 %float_8 %float_8 %float_8 + %float_5 = OpConstant %float 5 + %59 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %60 = OpConstantComposite %_arr_v4float_uint_4 %54 %55 %57 %59 + %int_30 = OpConstant %int 30 + %int_3 = OpConstant %int 3 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %float_20 = OpConstant %float 20 + %uint_2 = OpConstant %uint 2 + %97 = OpConstantComposite %v4float %float_20 %float_20 %float_20 %float_20 + %float_30 = OpConstant %float 30 + %99 = OpConstantComposite %v4float %float_30 %float_30 %float_30 %float_30 + %float_50 = OpConstant %float 50 + %101 = OpConstantComposite %v4float %float_50 %float_50 %float_50 %float_50 + %float_60 = OpConstant %float 60 + %103 = OpConstantComposite %v4float %float_60 %float_60 %float_60 %float_60 + %104 = OpConstantComposite %_arr_v4float_uint_4 %97 %99 %101 %103 + %main = OpFunction %void None %3 + %5 = OpLabel + %indexable = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_0 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_1 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 + %foo = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %foobar = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %baz = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %20 = OpLoad %int %index + %24 = OpAccessChain %_ptr_Function_float %indexable %20 + %25 = OpLoad %float %24 + OpStore %FragColor %25 + %26 = OpLoad %int %index + %29 = OpSLessThan %bool %26 %int_10 + OpSelectionMerge %31 None + OpBranchConditional %29 %30 %40 + %30 = OpLabel + %32 = OpLoad %int %index + %34 = OpBitwiseXor %int %32 %int_1 + %36 = OpAccessChain %_ptr_Function_float %indexable_0 %34 + %37 = OpLoad %float %36 + %38 = OpLoad %float %FragColor + %39 = OpFAdd %float %38 %37 + OpStore %FragColor %39 + OpBranch %31 + %40 = OpLabel + %41 = OpLoad %int %index + %42 = OpBitwiseAnd %int %41 %int_1 + %44 = OpAccessChain %_ptr_Function_float %indexable_1 %42 + %45 = OpLoad %float %44 + %46 = OpLoad %float %FragColor + %47 = OpFAdd %float %46 %45 + OpStore %FragColor %47 + OpBranch %31 + %31 = OpLabel + %61 = OpLoad %int %index + %63 = OpSGreaterThan %bool %61 %int_30 + OpSelectionMerge %65 None + OpBranchConditional %63 %64 %74 + %64 = OpLabel + %66 = OpLoad %int %index + %68 = OpBitwiseAnd %int %66 %int_3 + %70 = OpAccessChain %_ptr_Function_float %foo %68 %uint_1 + %71 = OpLoad %float %70 + %72 = OpLoad %float %FragColor + %73 = OpFAdd %float %72 %71 + OpStore %FragColor %73 + OpBranch %65 + %74 = OpLabel + %75 = OpLoad %int %index + %76 = OpBitwiseAnd %int %75 %int_1 + %78 = OpAccessChain %_ptr_Function_float %foo %76 %uint_0 + %79 = OpLoad %float %78 + %80 = OpLoad %float %FragColor + %81 = OpFAdd %float %80 %79 + OpStore %FragColor %81 + OpBranch %65 + %65 = OpLabel + %83 = OpLoad %int %index + %84 = OpSGreaterThan %bool %83 %int_30 + OpSelectionMerge %86 None + OpBranchConditional %84 %85 %86 + %85 = OpLabel + %89 = OpAccessChain %_ptr_Function_float %foobar %int_1 %uint_2 + OpStore %89 %float_20 + OpBranch %86 + %86 = OpLabel + %90 = OpLoad %int %index + %91 = OpBitwiseAnd %int %90 %int_3 + %92 = OpAccessChain %_ptr_Function_float %foobar %91 %uint_2 + %93 = OpLoad %float %92 + %94 = OpLoad %float %FragColor + %95 = OpFAdd %float %94 %93 + OpStore %FragColor %95 + OpStore %baz %104 + %105 = OpLoad %int %index + %106 = OpBitwiseAnd %int %105 %int_3 + %107 = OpAccessChain %_ptr_Function_float %baz %106 %uint_2 + %108 = OpLoad %float %107 + %109 = OpLoad %float %FragColor + %110 = OpFAdd %float %109 %108 + OpStore %FragColor %110 + OpReturn + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders/asm/frag/switch-label-shared-block.asm.frag b/deps/SPIRV-Cross/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000000..8f55bcf536 --- /dev/null +++ b/deps/SPIRV-Cross/shaders/asm/frag/switch-label-shared-block.asm.frag @@ -0,0 +1,45 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vIndex %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %vIndex "vIndex" + OpName %FragColor "FragColor" + OpDecorate %vIndex RelaxedPrecision + OpDecorate %vIndex Flat + OpDecorate %vIndex Location 0 + OpDecorate %13 RelaxedPrecision + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_8 = OpConstant %float 8 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %vIndex = OpVariable %_ptr_Input_int Input + %float_1 = OpConstant %float 1 + %float_3 = OpConstant %float 3 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %int %vIndex + OpSelectionMerge %17 None + OpSwitch %13 %15 0 %14 2 %14 1 %15 8 %17 + %15 = OpLabel + OpBranch %17 + %14 = OpLabel + OpBranch %17 + %17 = OpLabel + %27 = OpPhi %float %float_3 %15 %float_1 %14 %float_8 %5 + OpStore %FragColor %27 + OpReturn + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/deps/SPIRV-Cross/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000000..89036f0eb2 --- /dev/null +++ b/deps/SPIRV-Cross/shaders/asm/frag/unknown-depth-state.asm.vk.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 44 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vUV %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %sample_combined_ "sample_combined(" + OpName %sample_separate_ "sample_separate(" + OpName %uShadow "uShadow" + OpName %vUV "vUV" + OpName %uTexture "uTexture" + OpName %uSampler "uSampler" + OpName %FragColor "FragColor" + OpDecorate %uShadow DescriptorSet 0 + OpDecorate %uShadow Binding 0 + OpDecorate %vUV Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 2 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float + %12 = OpTypeImage %float 2D 2 0 0 1 Unknown + %13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %uShadow = OpVariable %_ptr_UniformConstant_13 UniformConstant + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %vUV = OpVariable %_ptr_Input_v3float Input +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_25 UniformConstant + %29 = OpTypeSampler +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 + %uSampler = OpVariable %_ptr_UniformConstant_29 UniformConstant +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %41 = OpFunctionCall %float %sample_combined_ + %42 = OpFunctionCall %float %sample_separate_ + %43 = OpFAdd %float %41 %42 + OpStore %FragColor %43 + OpReturn + OpFunctionEnd +%sample_combined_ = OpFunction %float None %7 + %9 = OpLabel + %16 = OpLoad %13 %uShadow + %20 = OpLoad %v3float %vUV + %21 = OpCompositeExtract %float %20 2 + %22 = OpImageSampleDrefImplicitLod %float %16 %20 %21 + OpReturnValue %22 + OpFunctionEnd +%sample_separate_ = OpFunction %float None %7 + %11 = OpLabel + %28 = OpLoad %12 %uTexture + %32 = OpLoad %29 %uSampler + %33 = OpSampledImage %13 %28 %32 + %34 = OpLoad %v3float %vUV + %35 = OpCompositeExtract %float %34 2 + %36 = OpImageSampleDrefImplicitLod %float %33 %34 %35 + OpReturnValue %36 + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders/asm/geom/store-uint-layer.invalid.asm.geom b/deps/SPIRV-Cross/shaders/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000000..550fc4e990 --- /dev/null +++ b/deps/SPIRV-Cross/shaders/asm/geom/store-uint-layer.invalid.asm.geom @@ -0,0 +1,130 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 74 +; Schema: 0 + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %stream_pos %stream_layer %input_pos + OpExecutionMode %main Triangles + OpExecutionMode %main Invocations 1 + OpExecutionMode %main OutputTriangleStrip + OpExecutionMode %main OutputVertices 3 + OpSource HLSL 500 + OpName %main "main" + OpName %VertexOutput "VertexOutput" + OpMemberName %VertexOutput 0 "pos" + OpName %GeometryOutput "GeometryOutput" + OpMemberName %GeometryOutput 0 "pos" + OpMemberName %GeometryOutput 1 "layer" + OpName %_main_struct_VertexOutput_vf41_3__struct_GeometryOutput_vf4_u11_ "@main(struct-VertexOutput-vf41[3];struct-GeometryOutput-vf4-u11;" + OpName %input "input" + OpName %stream "stream" + OpName %output "output" + OpName %v "v" + OpName %stream_pos "stream.pos" + OpName %stream_layer "stream.layer" + OpName %input_0 "input" + OpName %input_pos "input.pos" + OpName %stream_0 "stream" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %stream_pos BuiltIn Position + OpDecorate %stream_layer BuiltIn Layer + OpDecorate %input_pos BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%VertexOutput = OpTypeStruct %v4float + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_VertexOutput_uint_3 = OpTypeArray %VertexOutput %uint_3 +%_ptr_Function__arr_VertexOutput_uint_3 = OpTypePointer Function %_arr_VertexOutput_uint_3 +%GeometryOutput = OpTypeStruct %v4float %uint +%_ptr_Function_GeometryOutput = OpTypePointer Function %GeometryOutput + %15 = OpTypeFunction %void %_ptr_Function__arr_VertexOutput_uint_3 %_ptr_Function_GeometryOutput + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %uint_1 = OpConstant %uint 1 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_3 = OpConstant %int 3 + %bool = OpTypeBool +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %stream_pos = OpVariable %_ptr_Output_v4float Output +%_ptr_Output_uint = OpTypePointer Output %uint +%stream_layer = OpVariable %_ptr_Output_uint Output +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 + %input_pos = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float + %int_2 = OpConstant %int 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %input_0 = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %stream_0 = OpVariable %_ptr_Function_GeometryOutput Function + %param = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %param_0 = OpVariable %_ptr_Function_GeometryOutput Function + %58 = OpAccessChain %_ptr_Input_v4float %input_pos %int_0 + %59 = OpLoad %v4float %58 + %60 = OpAccessChain %_ptr_Function_v4float %input_0 %int_0 %int_0 + OpStore %60 %59 + %61 = OpAccessChain %_ptr_Input_v4float %input_pos %int_1 + %62 = OpLoad %v4float %61 + %63 = OpAccessChain %_ptr_Function_v4float %input_0 %int_1 %int_0 + OpStore %63 %62 + %65 = OpAccessChain %_ptr_Input_v4float %input_pos %int_2 + %66 = OpLoad %v4float %65 + %67 = OpAccessChain %_ptr_Function_v4float %input_0 %int_2 %int_0 + OpStore %67 %66 + %70 = OpLoad %_arr_VertexOutput_uint_3 %input_0 + OpStore %param %70 + %72 = OpFunctionCall %void %_main_struct_VertexOutput_vf41_3__struct_GeometryOutput_vf4_u11_ %param %param_0 + %73 = OpLoad %GeometryOutput %param_0 + OpStore %stream_0 %73 + OpReturn + OpFunctionEnd +%_main_struct_VertexOutput_vf41_3__struct_GeometryOutput_vf4_u11_ = OpFunction %void None %15 + %input = OpFunctionParameter %_ptr_Function__arr_VertexOutput_uint_3 + %stream = OpFunctionParameter %_ptr_Function_GeometryOutput + %19 = OpLabel + %output = OpVariable %_ptr_Function_GeometryOutput Function + %v = OpVariable %_ptr_Function_int Function + %25 = OpAccessChain %_ptr_Function_uint %output %int_1 + OpStore %25 %uint_1 + OpStore %v %int_0 + OpBranch %29 + %29 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %34 = OpLoad %int %v + %37 = OpSLessThan %bool %34 %int_3 + OpBranchConditional %37 %30 %31 + %30 = OpLabel + %38 = OpLoad %int %v + %40 = OpAccessChain %_ptr_Function_v4float %input %38 %int_0 + %41 = OpLoad %v4float %40 + %42 = OpAccessChain %_ptr_Function_v4float %output %int_0 + OpStore %42 %41 + %45 = OpAccessChain %_ptr_Function_v4float %output %int_0 + %46 = OpLoad %v4float %45 + OpStore %stream_pos %46 + %49 = OpAccessChain %_ptr_Function_uint %output %int_1 + %50 = OpLoad %uint %49 + OpStore %stream_layer %50 + OpEmitVertex + OpBranch %32 + %32 = OpLabel + %51 = OpLoad %int %v + %52 = OpIAdd %int %51 %int_1 + OpStore %v %52 + OpBranch %29 + %31 = OpLabel + OpEndPrimitive + OpReturn + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/deps/SPIRV-Cross/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..29b0076a1e --- /dev/null +++ b/deps/SPIRV-Cross/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %vid_1 %iid_1 %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_u1_u1_ "@main(u1;u1;" + OpName %vid "vid" + OpName %iid "iid" + OpName %vid_0 "vid" + OpName %vid_1 "vid" + OpName %iid_0 "iid" + OpName %iid_1 "iid" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %vid_1 BuiltIn VertexIndex + OpDecorate %iid_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_uint %_ptr_Function_uint +%_ptr_Input_uint = OpTypePointer Input %uint + %vid_1 = OpVariable %_ptr_Input_uint Input + %iid_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %vid_0 = OpVariable %_ptr_Function_uint Function + %iid_0 = OpVariable %_ptr_Function_uint Function + %param = OpVariable %_ptr_Function_uint Function + %param_0 = OpVariable %_ptr_Function_uint Function + %25 = OpLoad %uint %vid_1 + OpStore %vid_0 %25 + %28 = OpLoad %uint %iid_1 + OpStore %iid_0 %28 + %32 = OpLoad %uint %vid_0 + OpStore %param %32 + %34 = OpLoad %uint %iid_0 + OpStore %param_0 %34 + %35 = OpFunctionCall %v4float %_main_u1_u1_ %param %param_0 + OpStore %_entryPointOutput %35 + OpReturn + OpFunctionEnd +%_main_u1_u1_ = OpFunction %v4float None %10 + %vid = OpFunctionParameter %_ptr_Function_uint + %iid = OpFunctionParameter %_ptr_Function_uint + %14 = OpLabel + %15 = OpLoad %uint %vid + %16 = OpLoad %uint %iid + %17 = OpIAdd %uint %15 %16 + %18 = OpConvertUToF %float %17 + %19 = OpCompositeConstruct %v4float %18 %18 %18 %18 + OpReturnValue %19 + OpFunctionEnd diff --git a/deps/SPIRV-Cross/shaders/frag/lut-promotion.frag b/deps/SPIRV-Cross/shaders/frag/lut-promotion.frag new file mode 100644 index 0000000000..0cdc8148f9 --- /dev/null +++ b/deps/SPIRV-Cross/shaders/frag/lut-promotion.frag @@ -0,0 +1,44 @@ +#version 310 es +precision mediump float; +layout(location = 0) out float FragColor; +layout(location = 0) flat in int index; + +const float LUT[16] = float[]( + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0); + +void main() +{ + // Try reading LUTs, both in branches and not branch. + FragColor = LUT[index]; + if (index < 10) + FragColor += LUT[index ^ 1]; + else + FragColor += LUT[index & 1]; + + // Not declared as a LUT, but can be promoted to one. + vec4 foo[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + FragColor += foo[index & 3].y; + } + else + { + FragColor += foo[index & 1].x; + } + + // Not declared as a LUT, but this cannot be promoted, because we have a partial write. + vec4 foobar[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + foobar[1].z = 20.0; + } + FragColor += foobar[index & 3].z; + + // Not declared as a LUT, but this cannot be promoted, because we have two complete writes. + vec4 baz[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[index & 3].z; +} diff --git a/deps/SPIRV-Cross/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/deps/SPIRV-Cross/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000000..78dccbf044 --- /dev/null +++ b/deps/SPIRV-Cross/shaders/vulkan/frag/spec-constant-ternary.vk.frag @@ -0,0 +1,9 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(constant_id = 0) const uint s = 10u; +const uint f = s > 20u ? 30u : 50u; + +void main() +{ + FragColor = float(f); +} diff --git a/deps/SPIRV-Cross/spirv_common.hpp b/deps/SPIRV-Cross/spirv_common.hpp index c4716a2388..0ea94fcb0c 100644 --- a/deps/SPIRV-Cross/spirv_common.hpp +++ b/deps/SPIRV-Cross/spirv_common.hpp @@ -578,6 +578,15 @@ struct SPIRBlock : IVariant MergeSelection }; + enum Hints + { + HintNone, + HintUnroll, + HintDontUnroll, + HintFlatten, + HintDontFlatten + }; + enum Method { MergeToSelectForLoop, @@ -610,6 +619,7 @@ struct SPIRBlock : IVariant Terminator terminator = Unknown; Merge merge = MergeNone; + Hints hint = HintNone; uint32_t next_block = 0; uint32_t merge_block = 0; uint32_t continue_block = 0; @@ -754,7 +764,6 @@ struct SPIRFunction : IVariant bool active = false; bool flush_undeclared = true; bool do_combined_parameters = true; - bool analyzed_variable_scope = false; }; struct SPIRAccessChain : IVariant @@ -1092,6 +1101,9 @@ struct SPIRConstant : IVariant // If this constant is used as an array length which creates specialization restrictions on some backends. bool is_used_as_array_length = false; + // If true, this is a LUT, and should always be declared in the outer scope. + bool is_used_as_lut = false; + // For composites which are constant arrays, etc. std::vector subconstants; }; diff --git a/deps/SPIRV-Cross/spirv_cpp.cpp b/deps/SPIRV-Cross/spirv_cpp.cpp index 9302a07441..7d781e9172 100644 --- a/deps/SPIRV-Cross/spirv_cpp.cpp +++ b/deps/SPIRV-Cross/spirv_cpp.cpp @@ -300,6 +300,7 @@ string CompilerCPP::compile() backend.explicit_struct_type = true; backend.use_initializer_list = true; + build_function_control_flow_graphs_and_analyze(); update_active_builtins(); uint32_t pass_count = 0; diff --git a/deps/SPIRV-Cross/spirv_cross.cpp b/deps/SPIRV-Cross/spirv_cross.cpp index 0402562e0b..dd2a44ca33 100644 --- a/deps/SPIRV-Cross/spirv_cross.cpp +++ b/deps/SPIRV-Cross/spirv_cross.cpp @@ -1063,6 +1063,34 @@ const SPIRType &Compiler::get_type_from_variable(uint32_t id) const return get(get(id).basetype); } +uint32_t Compiler::get_non_pointer_type_id(uint32_t type_id) const +{ + auto *p_type = &get(type_id); + while (p_type->pointer) + { + assert(p_type->parent_type); + type_id = p_type->parent_type; + p_type = &get(type_id); + } + return type_id; +} + +const SPIRType &Compiler::get_non_pointer_type(const SPIRType &type) const +{ + auto *p_type = &type; + while (p_type->pointer) + { + assert(p_type->parent_type); + p_type = &get(p_type->parent_type); + } + return *p_type; +} + +const SPIRType &Compiler::get_non_pointer_type(uint32_t type_id) const +{ + return get_non_pointer_type(get(type_id)); +} + void Compiler::set_member_decoration_string(uint32_t id, uint32_t index, spv::Decoration decoration, const std::string &argument) { @@ -1814,7 +1842,7 @@ void Compiler::parse(const Instruction &instruction) type.basetype = SPIRType::Image; type.image.type = ops[1]; type.image.dim = static_cast(ops[2]); - type.image.depth = ops[3] != 0; + type.image.depth = ops[3] == 1; type.image.arrayed = ops[4] != 0; type.image.ms = ops[5] != 0; type.image.sampled = ops[6]; @@ -2221,6 +2249,14 @@ void Compiler::parse(const Instruction &instruction) current_block->next_block = ops[0]; current_block->merge = SPIRBlock::MergeSelection; selection_merge_targets.insert(current_block->next_block); + + if (length >= 2) + { + if (ops[1] & SelectionControlFlattenMask) + current_block->hint = SPIRBlock::HintFlatten; + else if (ops[1] & SelectionControlDontFlattenMask) + current_block->hint = SPIRBlock::HintDontFlatten; + } break; } @@ -2243,6 +2279,14 @@ void Compiler::parse(const Instruction &instruction) // they are treated as continues. if (current_block->continue_block != current_block->self) continue_blocks.insert(current_block->continue_block); + + if (length >= 3) + { + if (ops[2] & LoopControlUnrollMask) + current_block->hint = SPIRBlock::HintUnroll; + else if (ops[2] & LoopControlDontUnrollMask) + current_block->hint = SPIRBlock::HintDontUnroll; + } break; } @@ -3603,303 +3647,449 @@ void Compiler::analyze_parameter_preservation( } } -void Compiler::analyze_variable_scope(SPIRFunction &entry) +Compiler::AnalyzeVariableScopeAccessHandler::AnalyzeVariableScopeAccessHandler(Compiler &compiler_, + SPIRFunction &entry_) + : compiler(compiler_) + , entry(entry_) { - struct AccessHandler : OpcodeHandler +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::follow_function_call(const SPIRFunction &) +{ + // Only analyze within this function. + return false; +} + +void Compiler::AnalyzeVariableScopeAccessHandler::set_current_block(const SPIRBlock &block) +{ + current_block = █ + + // If we're branching to a block which uses OpPhi, in GLSL + // this will be a variable write when we branch, + // so we need to track access to these variables as well to + // have a complete picture. + const auto test_phi = [this, &block](uint32_t to) { + auto &next = compiler.get(to); + for (auto &phi : next.phi_variables) + { + if (phi.parent == block.self) + { + accessed_variables_to_block[phi.function_variable].insert(block.self); + // Phi variables are also accessed in our target branch block. + accessed_variables_to_block[phi.function_variable].insert(next.self); + + notify_variable_access(phi.local_variable, block.self); + } + } + }; + + switch (block.terminator) { - public: - AccessHandler(Compiler &compiler_, SPIRFunction &entry_) - : compiler(compiler_) - , entry(entry_) - { - } + case SPIRBlock::Direct: + notify_variable_access(block.condition, block.self); + test_phi(block.next_block); + break; - bool follow_function_call(const SPIRFunction &) - { - // Only analyze within this function. + case SPIRBlock::Select: + notify_variable_access(block.condition, block.self); + test_phi(block.true_block); + test_phi(block.false_block); + break; + + case SPIRBlock::MultiSelect: + notify_variable_access(block.condition, block.self); + for (auto &target : block.cases) + test_phi(target.block); + if (block.default_block) + test_phi(block.default_block); + break; + + default: + break; + } +} + +void Compiler::AnalyzeVariableScopeAccessHandler::notify_variable_access(uint32_t id, uint32_t block) +{ + if (id_is_phi_variable(id)) + accessed_variables_to_block[id].insert(block); + else if (id_is_potential_temporary(id)) + accessed_temporaries_to_block[id].insert(block); +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_phi_variable(uint32_t id) const +{ + if (id >= compiler.get_current_id_bound()) + return false; + auto *var = compiler.maybe_get(id); + return var && var->phi_variable; +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_potential_temporary(uint32_t id) const +{ + if (id >= compiler.get_current_id_bound()) + return false; + + // Temporaries are not created before we start emitting code. + return compiler.ids[id].empty() || (compiler.ids[id].get_type() == TypeExpression); +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length) +{ + // Keep track of the types of temporaries, so we can hoist them out as necessary. + uint32_t result_type, result_id; + if (compiler.instruction_to_result_type(result_type, result_id, op, args, length)) + result_id_to_type[result_id] = result_type; + + switch (op) + { + case OpStore: + { + if (length < 2) return false; - } - void set_current_block(const SPIRBlock &block) + uint32_t ptr = args[0]; + auto *var = compiler.maybe_get_backing_variable(ptr); + + // If we store through an access chain, we have a partial write. + if (var) { - current_block = █ - - // If we're branching to a block which uses OpPhi, in GLSL - // this will be a variable write when we branch, - // so we need to track access to these variables as well to - // have a complete picture. - const auto test_phi = [this, &block](uint32_t to) { - auto &next = compiler.get(to); - for (auto &phi : next.phi_variables) - { - if (phi.parent == block.self) - { - accessed_variables_to_block[phi.function_variable].insert(block.self); - // Phi variables are also accessed in our target branch block. - accessed_variables_to_block[phi.function_variable].insert(next.self); - - notify_variable_access(phi.local_variable, block.self); - } - } - }; - - switch (block.terminator) - { - case SPIRBlock::Direct: - notify_variable_access(block.condition, block.self); - test_phi(block.next_block); - break; - - case SPIRBlock::Select: - notify_variable_access(block.condition, block.self); - test_phi(block.true_block); - test_phi(block.false_block); - break; - - case SPIRBlock::MultiSelect: - notify_variable_access(block.condition, block.self); - for (auto &target : block.cases) - test_phi(target.block); - if (block.default_block) - test_phi(block.default_block); - break; - - default: - break; - } + accessed_variables_to_block[var->self].insert(current_block->self); + if (var->self == ptr) + complete_write_variables_to_block[var->self].insert(current_block->self); + else + partial_write_variables_to_block[var->self].insert(current_block->self); } - void notify_variable_access(uint32_t id, uint32_t block) + // Might try to store a Phi variable here. + notify_variable_access(args[1], current_block->self); + break; + } + + case OpAccessChain: + case OpInBoundsAccessChain: + { + if (length < 3) + return false; + + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get(ptr); + if (var) + accessed_variables_to_block[var->self].insert(current_block->self); + + for (uint32_t i = 3; i < length; i++) + notify_variable_access(args[i], current_block->self); + + // The result of an access chain is a fixed expression and is not really considered a temporary. + auto &e = compiler.set(args[1], "", args[0], true); + auto *backing_variable = compiler.maybe_get_backing_variable(ptr); + e.loaded_from = backing_variable ? backing_variable->self : 0; + + // Other backends might use SPIRAccessChain for this later. + compiler.ids[args[1]].set_allow_type_rewrite(); + break; + } + + case OpCopyMemory: + { + if (length < 2) + return false; + + uint32_t lhs = args[0]; + uint32_t rhs = args[1]; + auto *var = compiler.maybe_get_backing_variable(lhs); + + // If we store through an access chain, we have a partial write. + if (var) { - if (id_is_phi_variable(id)) - accessed_variables_to_block[id].insert(block); - else if (id_is_potential_temporary(id)) - accessed_temporaries_to_block[id].insert(block); + accessed_variables_to_block[var->self].insert(current_block->self); + if (var->self == lhs) + complete_write_variables_to_block[var->self].insert(current_block->self); + else + partial_write_variables_to_block[var->self].insert(current_block->self); } - bool id_is_phi_variable(uint32_t id) + var = compiler.maybe_get_backing_variable(rhs); + if (var) + accessed_variables_to_block[var->self].insert(current_block->self); + break; + } + + case OpCopyObject: + { + if (length < 3) + return false; + + auto *var = compiler.maybe_get_backing_variable(args[2]); + if (var) + accessed_variables_to_block[var->self].insert(current_block->self); + + // Might try to copy a Phi variable here. + notify_variable_access(args[2], current_block->self); + break; + } + + case OpLoad: + { + if (length < 3) + return false; + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var) + accessed_variables_to_block[var->self].insert(current_block->self); + + // Loaded value is a temporary. + notify_variable_access(args[1], current_block->self); + break; + } + + case OpFunctionCall: + { + if (length < 3) + return false; + + length -= 3; + args += 3; + + for (uint32_t i = 0; i < length; i++) { - if (id >= compiler.get_current_id_bound()) - return false; - auto *var = compiler.maybe_get(id); - return var && var->phi_variable; + auto *var = compiler.maybe_get_backing_variable(args[i]); + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + // Assume we can get partial writes to this variable. + partial_write_variables_to_block[var->self].insert(current_block->self); + } + + // Cannot easily prove if argument we pass to a function is completely written. + // Usually, functions write to a dummy variable, + // which is then copied to in full to the real argument. + + // Might try to copy a Phi variable here. + notify_variable_access(args[i], current_block->self); } - bool id_is_potential_temporary(uint32_t id) + // Return value may be a temporary. + notify_variable_access(args[1], current_block->self); + break; + } + + case OpExtInst: + { + for (uint32_t i = 4; i < length; i++) + notify_variable_access(args[i], current_block->self); + notify_variable_access(args[1], current_block->self); + break; + } + + case OpArrayLength: + // Uses literals, but cannot be a phi variable, so ignore. + break; + + // Atomics shouldn't be able to access function-local variables. + // Some GLSL builtins access a pointer. + + case OpCompositeInsert: + case OpVectorShuffle: + // Specialize for opcode which contains literals. + for (uint32_t i = 1; i < 4; i++) + notify_variable_access(args[i], current_block->self); + break; + + case OpCompositeExtract: + // Specialize for opcode which contains literals. + for (uint32_t i = 1; i < 3; i++) + notify_variable_access(args[i], current_block->self); + break; + + default: + { + // Rather dirty way of figuring out where Phi variables are used. + // As long as only IDs are used, we can scan through instructions and try to find any evidence that + // the ID of a variable has been used. + // There are potential false positives here where a literal is used in-place of an ID, + // but worst case, it does not affect the correctness of the compile. + // Exhaustive analysis would be better here, but it's not worth it for now. + for (uint32_t i = 0; i < length; i++) + notify_variable_access(args[i], current_block->self); + break; + } + } + return true; +} + +Compiler::StaticExpressionAccessHandler::StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_) + : compiler(compiler_) + , variable_id(variable_id_) +{ +} + +bool Compiler::StaticExpressionAccessHandler::follow_function_call(const SPIRFunction &) +{ + return false; +} + +bool Compiler::StaticExpressionAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length) +{ + switch (op) + { + case OpStore: + if (length < 2) + return false; + if (args[0] == variable_id) { - if (id >= compiler.get_current_id_bound()) - return false; - - // Temporaries are not created before we start emitting code. - return compiler.ids[id].empty() || (compiler.ids[id].get_type() == TypeExpression); + static_expression = args[1]; + write_count++; } + break; - bool handle(spv::Op op, const uint32_t *args, uint32_t length) + case OpLoad: + if (length < 3) + return false; + if (args[2] == variable_id && static_expression == 0) // Tried to read from variable before it was initialized. + return false; + break; + + case OpAccessChain: + case OpInBoundsAccessChain: + if (length < 3) + return false; + if (args[2] == variable_id) // If we try to access chain our candidate variable before we store to it, bail. + return false; + break; + + default: + break; + } + + return true; +} + +void Compiler::find_function_local_luts(SPIRFunction &entry, const AnalyzeVariableScopeAccessHandler &handler) +{ + auto &cfg = *function_cfgs.find(entry.self)->second; + + // For each variable which is statically accessed. + for (auto &accessed_var : handler.accessed_variables_to_block) + { + auto &blocks = accessed_var.second; + auto &var = get(accessed_var.first); + auto &type = expression_type(accessed_var.first); + + // Only consider function local variables here. + if (var.storage != StorageClassFunction) + continue; + + // We cannot be a phi variable. + if (var.phi_variable) + continue; + + // Only consider arrays here. + if (type.array.empty()) + continue; + + // HACK: Do not consider structs. This is a quirk with how types are currently being emitted. + // Structs are emitted after specialization constants and composite constants. + // FIXME: Fix declaration order so declared constants can have struct types. + if (type.basetype == SPIRType::Struct) + continue; + + // If the variable has an initializer, make sure it is a constant expression. + uint32_t static_constant_expression = 0; + if (var.initializer) { - // Keep track of the types of temporaries, so we can hoist them out as necessary. - uint32_t result_type, result_id; - if (compiler.instruction_to_result_type(result_type, result_id, op, args, length)) - result_id_to_type[result_id] = result_type; + if (ids[var.initializer].get_type() != TypeConstant) + continue; + static_constant_expression = var.initializer; - switch (op) - { - case OpStore: - { - if (length < 2) - return false; + // There can be no stores to this variable, we have now proved we have a LUT. + if (handler.complete_write_variables_to_block.count(var.self) != 0 || + handler.partial_write_variables_to_block.count(var.self) != 0) + continue; + } + else + { + // We can have one, and only one write to the variable, and that write needs to be a constant. - uint32_t ptr = args[0]; - auto *var = compiler.maybe_get_backing_variable(ptr); - if (var && var->storage == StorageClassFunction) - accessed_variables_to_block[var->self].insert(current_block->self); + // No partial writes allowed. + if (handler.partial_write_variables_to_block.count(var.self) != 0) + continue; - // If we store through an access chain, we have a partial write. - if (var && var->self == ptr && var->storage == StorageClassFunction) - complete_write_variables_to_block[var->self].insert(current_block->self); + auto itr = handler.complete_write_variables_to_block.find(var.self); - // Might try to store a Phi variable here. - notify_variable_access(args[1], current_block->self); - break; - } + // No writes? + if (itr == end(handler.complete_write_variables_to_block)) + continue; - case OpAccessChain: - case OpInBoundsAccessChain: - { - if (length < 3) - return false; + // We write to the variable in more than one block. + auto &write_blocks = itr->second; + if (write_blocks.size() != 1) + continue; - uint32_t ptr = args[2]; - auto *var = compiler.maybe_get(ptr); - if (var && var->storage == StorageClassFunction) - accessed_variables_to_block[var->self].insert(current_block->self); + // The write needs to happen in the dominating block. + DominatorBuilder builder(cfg); + for (auto &block : blocks) + builder.add_block(block); + uint32_t dominator = builder.get_dominator(); - for (uint32_t i = 3; i < length; i++) - notify_variable_access(args[i], current_block->self); + // The complete write happened in a branch or similar, cannot deduce static expression. + if (write_blocks.count(dominator) == 0) + continue; - // The result of an access chain is a fixed expression and is not really considered a temporary. - auto &e = compiler.set(args[1], "", args[0], true); - auto *backing_variable = compiler.maybe_get_backing_variable(ptr); - e.loaded_from = backing_variable ? backing_variable->self : 0; + // Find the static expression for this variable. + StaticExpressionAccessHandler static_expression_handler(*this, var.self); + traverse_all_reachable_opcodes(get(dominator), static_expression_handler); - // Other backends might use SPIRAccessChain for this later. - compiler.ids[args[1]].set_allow_type_rewrite(); - break; - } + // We want one, and exactly one write + if (static_expression_handler.write_count != 1 || static_expression_handler.static_expression == 0) + continue; - case OpCopyMemory: - { - if (length < 2) - return false; + // Is it a constant expression? + if (ids[static_expression_handler.static_expression].get_type() != TypeConstant) + continue; - uint32_t lhs = args[0]; - uint32_t rhs = args[1]; - auto *var = compiler.maybe_get_backing_variable(lhs); - if (var && var->storage == StorageClassFunction) - accessed_variables_to_block[var->self].insert(current_block->self); - - // If we store through an access chain, we have a partial write. - if (var && var->self == lhs) - complete_write_variables_to_block[var->self].insert(current_block->self); - - var = compiler.maybe_get_backing_variable(rhs); - if (var && var->storage == StorageClassFunction) - accessed_variables_to_block[var->self].insert(current_block->self); - break; - } - - case OpCopyObject: - { - if (length < 3) - return false; - - auto *var = compiler.maybe_get_backing_variable(args[2]); - if (var && var->storage == StorageClassFunction) - accessed_variables_to_block[var->self].insert(current_block->self); - - // Might try to copy a Phi variable here. - notify_variable_access(args[2], current_block->self); - break; - } - - case OpLoad: - { - if (length < 3) - return false; - uint32_t ptr = args[2]; - auto *var = compiler.maybe_get_backing_variable(ptr); - if (var && var->storage == StorageClassFunction) - accessed_variables_to_block[var->self].insert(current_block->self); - - // Loaded value is a temporary. - notify_variable_access(args[1], current_block->self); - break; - } - - case OpFunctionCall: - { - if (length < 3) - return false; - - length -= 3; - args += 3; - for (uint32_t i = 0; i < length; i++) - { - auto *var = compiler.maybe_get_backing_variable(args[i]); - if (var && var->storage == StorageClassFunction) - accessed_variables_to_block[var->self].insert(current_block->self); - - // Cannot easily prove if argument we pass to a function is completely written. - // Usually, functions write to a dummy variable, - // which is then copied to in full to the real argument. - - // Might try to copy a Phi variable here. - notify_variable_access(args[i], current_block->self); - } - - // Return value may be a temporary. - notify_variable_access(args[1], current_block->self); - break; - } - - case OpExtInst: - { - for (uint32_t i = 4; i < length; i++) - notify_variable_access(args[i], current_block->self); - notify_variable_access(args[1], current_block->self); - break; - } - - case OpArrayLength: - // Uses literals, but cannot be a phi variable, so ignore. - break; - - // Atomics shouldn't be able to access function-local variables. - // Some GLSL builtins access a pointer. - - case OpCompositeInsert: - case OpVectorShuffle: - // Specialize for opcode which contains literals. - for (uint32_t i = 1; i < 4; i++) - notify_variable_access(args[i], current_block->self); - break; - - case OpCompositeExtract: - // Specialize for opcode which contains literals. - for (uint32_t i = 1; i < 3; i++) - notify_variable_access(args[i], current_block->self); - break; - - default: - { - // Rather dirty way of figuring out where Phi variables are used. - // As long as only IDs are used, we can scan through instructions and try to find any evidence that - // the ID of a variable has been used. - // There are potential false positives here where a literal is used in-place of an ID, - // but worst case, it does not affect the correctness of the compile. - // Exhaustive analysis would be better here, but it's not worth it for now. - for (uint32_t i = 0; i < length; i++) - notify_variable_access(args[i], current_block->self); - break; - } - } - return true; + // We found a LUT! + static_constant_expression = static_expression_handler.static_expression; } - Compiler &compiler; - SPIRFunction &entry; - std::unordered_map> accessed_variables_to_block; - std::unordered_map> accessed_temporaries_to_block; - std::unordered_map result_id_to_type; - std::unordered_map> complete_write_variables_to_block; - const SPIRBlock *current_block = nullptr; - } handler(*this, entry); + get(static_constant_expression).is_used_as_lut = true; + var.static_expression = static_constant_expression; + var.statically_assigned = true; + var.remapped_variable = true; + } +} +void Compiler::analyze_variable_scope(SPIRFunction &entry, AnalyzeVariableScopeAccessHandler &handler) +{ // First, we map out all variable access within a function. // Essentially a map of block -> { variables accessed in the basic block } - this->traverse_all_reachable_opcodes(entry, handler); + traverse_all_reachable_opcodes(entry, handler); - // Compute the control flow graph for this function. - CFG cfg(*this, entry); + auto &cfg = *function_cfgs.find(entry.self)->second; // Analyze if there are parameters which need to be implicitly preserved with an "in" qualifier. - this->analyze_parameter_preservation(entry, cfg, handler.accessed_variables_to_block, - handler.complete_write_variables_to_block); + analyze_parameter_preservation(entry, cfg, handler.accessed_variables_to_block, + handler.complete_write_variables_to_block); unordered_map potential_loop_variables; // For each variable which is statically accessed. for (auto &var : handler.accessed_variables_to_block) { + // Only deal with variables which are considered local variables in this function. + if (find(begin(entry.local_variables), end(entry.local_variables), var.first) == end(entry.local_variables)) + continue; + DominatorBuilder builder(cfg); auto &blocks = var.second; - auto &type = this->expression_type(var.first); + auto &type = expression_type(var.first); // Figure out which block is dominating all accesses of those variables. for (auto &block : blocks) { // If we're accessing a variable inside a continue block, this variable might be a loop variable. // We can only use loop variables with scalars, as we cannot track static expressions for vectors. - if (this->is_continue(block)) + if (is_continue(block)) { // Potentially awkward case to check for. // We might have a variable inside a loop, which is touched by the continue block, @@ -3907,7 +4097,7 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry) // The continue block is dominated by the inner part of the loop, which does not make sense in high-level // language output because it will be declared before the body, // so we will have to lift the dominator up to the relevant loop header instead. - builder.add_block(this->continue_block_to_loop_header[block]); + builder.add_block(continue_block_to_loop_header[block]); // Arrays or structs cannot be loop variables. if (type.vecsize == 1 && type.columns == 1 && type.basetype != SPIRType::Struct && type.array.empty()) @@ -3934,9 +4124,9 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry) // will be completely eliminated. if (dominating_block) { - auto &block = this->get(dominating_block); + auto &block = get(dominating_block); block.dominated_variables.push_back(var.first); - this->get(var.first).dominator = dominating_block; + get(var.first).dominator = dominating_block; } } @@ -3962,9 +4152,9 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry) // If a temporary is used in more than one block, we might have to lift continue block // access up to loop header like we did for variables. - if (blocks.size() != 1 && this->is_continue(block)) - builder.add_block(this->continue_block_to_loop_header[block]); - else if (blocks.size() != 1 && this->is_single_block_loop(block)) + if (blocks.size() != 1 && is_continue(block)) + builder.add_block(continue_block_to_loop_header[block]); + else if (blocks.size() != 1 && is_single_block_loop(block)) { // Awkward case, because the loop header is also the continue block. force_temporary = true; @@ -3983,10 +4173,10 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry) // This should be very rare, but if we try to declare a temporary inside a loop, // and that temporary is used outside the loop as well (spirv-opt inliner likes this) // we should actually emit the temporary outside the loop. - this->hoisted_temporaries.insert(var.first); - this->forced_temporaries.insert(var.first); + hoisted_temporaries.insert(var.first); + forced_temporaries.insert(var.first); - auto &block_temporaries = this->get(dominating_block).declare_temporary; + auto &block_temporaries = get(dominating_block).declare_temporary; block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first); } else if (blocks.size() > 1) @@ -3996,7 +4186,7 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry) // In this case, the header is actually inside the for (;;) {} block, and we have problems. // What we need to do is hoist the temporaries outside the for (;;) {} block in case the header block // declares the temporary. - auto &block_temporaries = this->get(dominating_block).potential_declare_temporary; + auto &block_temporaries = get(dominating_block).potential_declare_temporary; block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first); } } @@ -4007,7 +4197,7 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry) // Now, try to analyze whether or not these variables are actually loop variables. for (auto &loop_variable : potential_loop_variables) { - auto &var = this->get(loop_variable.first); + auto &var = get(loop_variable.first); auto dominator = var.dominator; auto block = loop_variable.second; @@ -4022,9 +4212,9 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry) uint32_t header = 0; // Find the loop header for this block. - for (auto b : this->loop_blocks) + for (auto b : loop_blocks) { - auto &potential_header = this->get(b); + auto &potential_header = get(b); if (potential_header.continue_block == block) { header = b; @@ -4033,7 +4223,7 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry) } assert(header); - auto &header_block = this->get(header); + auto &header_block = get(header); auto &blocks = handler.accessed_variables_to_block[loop_variable.first]; // If a loop variable is not used before the loop, it's probably not a loop variable. @@ -4089,7 +4279,7 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry) // Need to sort here as variables come from an unordered container, and pushing stuff in wrong order // will break reproducability in regression runs. sort(begin(header_block.loop_variables), end(header_block.loop_variables)); - this->get(loop_variable.first).loop_variable = true; + get(loop_variable.first).loop_variable = true; } } @@ -4243,14 +4433,8 @@ bool Compiler::ActiveBuiltinHandler::handle(spv::Op opcode, const uint32_t *args // Required if we access chain into builtins like gl_GlobalInvocationID. add_if_builtin(args[2]); - auto *type = &compiler.get(var->basetype); - // Start traversing type hierarchy at the proper non-pointer types. - while (type->pointer) - { - assert(type->parent_type); - type = &compiler.get(type->parent_type); - } + auto *type = &compiler.get_non_pointer_type(var->basetype); auto &flags = type->storage == StorageClassInput ? compiler.active_input_builtins : compiler.active_output_builtins; @@ -4329,11 +4513,109 @@ bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage) void Compiler::analyze_image_and_sampler_usage() { - CombinedImageSamplerUsageHandler handler(*this); + CombinedImageSamplerDrefHandler dref_handler(*this); + traverse_all_reachable_opcodes(get(entry_point), dref_handler); + + CombinedImageSamplerUsageHandler handler(*this, dref_handler.dref_combined_samplers); traverse_all_reachable_opcodes(get(entry_point), handler); - comparison_samplers = move(handler.comparison_samplers); - comparison_images = move(handler.comparison_images); + comparison_ids = move(handler.comparison_ids); need_subpass_input = handler.need_subpass_input; + + // Forward information from separate images and samplers into combined image samplers. + for (auto &combined : combined_image_samplers) + if (comparison_ids.count(combined.sampler_id)) + comparison_ids.insert(combined.combined_id); +} + +bool Compiler::CombinedImageSamplerDrefHandler::handle(spv::Op opcode, const uint32_t *args, uint32_t) +{ + // Mark all sampled images which are used with Dref. + switch (opcode) + { + case OpImageSampleDrefExplicitLod: + case OpImageSampleDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleProjDrefExplicitLod: + case OpImageSparseSampleDrefExplicitLod: + case OpImageDrefGather: + case OpImageSparseDrefGather: + dref_combined_samplers.insert(args[2]); + return true; + + default: + break; + } + + return true; +} + +void Compiler::build_function_control_flow_graphs_and_analyze() +{ + CFGBuilder handler(*this); + handler.function_cfgs[entry_point].reset(new CFG(*this, get(entry_point))); + traverse_all_reachable_opcodes(get(entry_point), handler); + function_cfgs = move(handler.function_cfgs); + + for (auto &f : function_cfgs) + { + auto &func = get(f.first); + AnalyzeVariableScopeAccessHandler scope_handler(*this, func); + analyze_variable_scope(func, scope_handler); + find_function_local_luts(func, scope_handler); + + // Check if we can actually use the loop variables we found in analyze_variable_scope. + // To use multiple initializers, we need the same type and qualifiers. + for (auto block : func.blocks) + { + auto &b = get(block); + if (b.loop_variables.size() < 2) + continue; + + auto &flags = get_decoration_bitset(b.loop_variables.front()); + uint32_t type = get(b.loop_variables.front()).basetype; + bool invalid_initializers = false; + for (auto loop_variable : b.loop_variables) + { + if (flags != get_decoration_bitset(loop_variable) || + type != get(b.loop_variables.front()).basetype) + { + invalid_initializers = true; + break; + } + } + + if (invalid_initializers) + { + for (auto loop_variable : b.loop_variables) + get(loop_variable).loop_variable = false; + b.loop_variables.clear(); + } + } + } +} + +Compiler::CFGBuilder::CFGBuilder(spirv_cross::Compiler &compiler_) + : compiler(compiler_) +{ +} + +bool Compiler::CFGBuilder::handle(spv::Op, const uint32_t *, uint32_t) +{ + return true; +} + +bool Compiler::CFGBuilder::follow_function_call(const SPIRFunction &func) +{ + if (function_cfgs.find(func.self) == end(function_cfgs)) + { + function_cfgs[func.self].reset(new CFG(compiler, func)); + return true; + } + else + return false; } bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint32_t *args, uint32_t length) @@ -4354,20 +4636,12 @@ bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint return true; } -void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_images(uint32_t image) +void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_ids(uint32_t id) { - // Traverse the variable dependency hierarchy and tag everything in its path with comparison images. - comparison_images.insert(image); - for (auto &img : dependency_hierarchy[image]) - add_hierarchy_to_comparison_images(img); -} - -void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_samplers(uint32_t sampler) -{ - // Traverse the variable dependency hierarchy and tag everything in its path with comparison samplers. - comparison_samplers.insert(sampler); - for (auto &samp : dependency_hierarchy[sampler]) - add_hierarchy_to_comparison_samplers(samp); + // Traverse the variable dependency hierarchy and tag everything in its path with comparison ids. + comparison_ids.insert(id); + for (auto &dep_id : dependency_hierarchy[id]) + add_hierarchy_to_comparison_ids(dep_id); } bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_t *args, uint32_t length) @@ -4387,6 +4661,10 @@ bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_ auto &type = compiler.get(args[0]); if (type.image.dim == DimSubpassData) need_subpass_input = true; + + // If we load a SampledImage and it will be used with Dref, propagate the state up. + if (dref_combined_samplers.count(args[1]) != 0) + add_hierarchy_to_comparison_ids(args[1]); break; } @@ -4396,16 +4674,20 @@ bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_ return false; uint32_t result_type = args[0]; + uint32_t result_id = args[1]; auto &type = compiler.get(result_type); - if (type.image.depth) + if (type.image.depth || dref_combined_samplers.count(result_id) != 0) { // This image must be a depth image. uint32_t image = args[2]; - add_hierarchy_to_comparison_images(image); + add_hierarchy_to_comparison_ids(image); - // This sampler must be a SamplerComparisionState, and not a regular SamplerState. + // This sampler must be a SamplerComparisonState, and not a regular SamplerState. uint32_t sampler = args[3]; - add_hierarchy_to_comparison_samplers(sampler); + add_hierarchy_to_comparison_ids(sampler); + + // Mark the OpSampledImage itself as being comparison state. + comparison_ids.insert(result_id); } return true; } @@ -4567,3 +4849,57 @@ bool Compiler::instruction_to_result_type(uint32_t &result_type, uint32_t &resul return false; } } + +Bitset Compiler::combined_decoration_for_member(const SPIRType &type, uint32_t index) const +{ + Bitset flags; + auto &memb = meta[type.self].members; + if (index >= memb.size()) + return flags; + auto &dec = memb[index]; + + // If our type is a struct, traverse all the members as well recursively. + flags.merge_or(dec.decoration_flags); + for (uint32_t i = 0; i < type.member_types.size(); i++) + flags.merge_or(combined_decoration_for_member(get(type.member_types[i]), i)); + + return flags; +} + +bool Compiler::is_desktop_only_format(spv::ImageFormat format) +{ + switch (format) + { + // Desktop-only formats + case ImageFormatR11fG11fB10f: + case ImageFormatR16f: + case ImageFormatRgb10A2: + case ImageFormatR8: + case ImageFormatRg8: + case ImageFormatR16: + case ImageFormatRg16: + case ImageFormatRgba16: + case ImageFormatR16Snorm: + case ImageFormatRg16Snorm: + case ImageFormatRgba16Snorm: + case ImageFormatR8Snorm: + case ImageFormatRg8Snorm: + case ImageFormatR8ui: + case ImageFormatRg8ui: + case ImageFormatR16ui: + case ImageFormatRgb10a2ui: + case ImageFormatR8i: + case ImageFormatRg8i: + case ImageFormatR16i: + return true; + default: + break; + } + + return false; +} + +bool Compiler::image_is_comparison(const spirv_cross::SPIRType &type, uint32_t id) const +{ + return type.image.depth || (comparison_ids.count(id) != 0); +} diff --git a/deps/SPIRV-Cross/spirv_cross.hpp b/deps/SPIRV-Cross/spirv_cross.hpp index 12dcae34e9..159e1988be 100644 --- a/deps/SPIRV-Cross/spirv_cross.hpp +++ b/deps/SPIRV-Cross/spirv_cross.hpp @@ -18,11 +18,11 @@ #define SPIRV_CROSS_HPP #include "spirv.hpp" +#include "spirv_cfg.hpp" #include "spirv_common.hpp" namespace spirv_cross { -class CFG; struct Resource { // Resources are identified with their SPIR-V ID. @@ -170,6 +170,15 @@ public: // Gets the SPIR-V type of a variable. const SPIRType &get_type_from_variable(uint32_t id) const; + // Gets the id of SPIR-V type underlying the given type_id, which might be a pointer. + uint32_t get_non_pointer_type_id(uint32_t type_id) const; + + // Gets the SPIR-V type underlying the given type, which might be a pointer. + const SPIRType &get_non_pointer_type(const SPIRType &type) const; + + // Gets the SPIR-V type underlying the given type_id, which might be a pointer. + const SPIRType &get_non_pointer_type(uint32_t type_id) const; + // Gets the underlying storage class for an OpVariable. spv::StorageClass get_storage_class(uint32_t id) const; @@ -667,8 +676,6 @@ protected: variable_remap_callback(type, var_name, type_name); } - void analyze_variable_scope(SPIRFunction &function); - void parse(); void parse(const Instruction &i); @@ -817,8 +824,7 @@ protected: // There might be unrelated IDs found in this set which do not correspond to actual variables. // This set should only be queried for the existence of samplers which are already known to be variables or parameter IDs. // Similar is implemented for images, as well as if subpass inputs are needed. - std::unordered_set comparison_samplers; - std::unordered_set comparison_images; + std::unordered_set comparison_ids; bool need_subpass_input = false; // In certain backends, we will need to use a dummy sampler to be able to emit code. @@ -827,26 +833,89 @@ protected: uint32_t dummy_sampler_id = 0; void analyze_image_and_sampler_usage(); + + struct CombinedImageSamplerDrefHandler : OpcodeHandler + { + CombinedImageSamplerDrefHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + std::unordered_set dref_combined_samplers; + }; + struct CombinedImageSamplerUsageHandler : OpcodeHandler { - CombinedImageSamplerUsageHandler(Compiler &compiler_) + CombinedImageSamplerUsageHandler(Compiler &compiler_, + const std::unordered_set &dref_combined_samplers_) : compiler(compiler_) + , dref_combined_samplers(dref_combined_samplers_) { } bool begin_function_scope(const uint32_t *args, uint32_t length) override; bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; Compiler &compiler; + const std::unordered_set &dref_combined_samplers; std::unordered_map> dependency_hierarchy; - std::unordered_set comparison_images; - std::unordered_set comparison_samplers; + std::unordered_set comparison_ids; - void add_hierarchy_to_comparison_samplers(uint32_t sampler); - void add_hierarchy_to_comparison_images(uint32_t sampler); + void add_hierarchy_to_comparison_ids(uint32_t ids); bool need_subpass_input = false; }; + void build_function_control_flow_graphs_and_analyze(); + std::unordered_map> function_cfgs; + struct CFGBuilder : OpcodeHandler + { + CFGBuilder(Compiler &compiler_); + + bool follow_function_call(const SPIRFunction &func) override; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + Compiler &compiler; + std::unordered_map> function_cfgs; + }; + + struct AnalyzeVariableScopeAccessHandler : OpcodeHandler + { + AnalyzeVariableScopeAccessHandler(Compiler &compiler_, SPIRFunction &entry_); + + bool follow_function_call(const SPIRFunction &) override; + void set_current_block(const SPIRBlock &block) override; + + void notify_variable_access(uint32_t id, uint32_t block); + bool id_is_phi_variable(uint32_t id) const; + bool id_is_potential_temporary(uint32_t id) const; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + SPIRFunction &entry; + std::unordered_map> accessed_variables_to_block; + std::unordered_map> accessed_temporaries_to_block; + std::unordered_map result_id_to_type; + std::unordered_map> complete_write_variables_to_block; + std::unordered_map> partial_write_variables_to_block; + const SPIRBlock *current_block = nullptr; + }; + + struct StaticExpressionAccessHandler : OpcodeHandler + { + StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_); + bool follow_function_call(const SPIRFunction &) override; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + uint32_t variable_id; + uint32_t static_expression = 0; + uint32_t write_count = 0; + }; + + void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler); + void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler); + void make_constant_null(uint32_t id, uint32_t type); std::vector declared_capabilities; @@ -856,6 +925,11 @@ protected: bool instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op, const uint32_t *args, uint32_t length); + Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const; + static bool is_desktop_only_format(spv::ImageFormat format); + + bool image_is_comparison(const SPIRType &type, uint32_t id) const; + private: // Used only to implement the old deprecated get_entry_point() interface. const SPIREntryPoint &get_first_entry_point(const std::string &name) const; diff --git a/deps/SPIRV-Cross/spirv_glsl.cpp b/deps/SPIRV-Cross/spirv_glsl.cpp index bc8fddd243..a973274616 100644 --- a/deps/SPIRV-Cross/spirv_glsl.cpp +++ b/deps/SPIRV-Cross/spirv_glsl.cpp @@ -418,6 +418,7 @@ string CompilerGLSL::compile() backend.supports_extensions = true; // Scan the SPIR-V to find trivial uses of extensions. + build_function_control_flow_graphs_and_analyze(); find_static_extensions(); fixup_image_load_store_access(); update_active_builtins(); @@ -689,22 +690,6 @@ void CompilerGLSL::emit_struct(SPIRType &type) statement(""); } -Bitset CompilerGLSL::combined_decoration_for_member(const SPIRType &type, uint32_t index) -{ - Bitset flags; - auto &memb = meta[type.self].members; - if (index >= memb.size()) - return flags; - auto &dec = memb[index]; - - // If our type is a struct, traverse all the members as well recursively. - flags.merge_or(dec.decoration_flags); - for (uint32_t i = 0; i < type.member_types.size(); i++) - flags.merge_or(combined_decoration_for_member(get(type.member_types[i]), i)); - - return flags; -} - string CompilerGLSL::to_interpolation_qualifiers(const Bitset &flags) { string res; @@ -786,10 +771,8 @@ string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index) const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) { - auto check_desktop = [this] { - if (options.es) - SPIRV_CROSS_THROW("Attempting to use image format not supported in ES profile."); - }; + if (options.es && is_desktop_only_format(format)) + SPIRV_CROSS_THROW("Attempting to use image format not supported in ES profile."); switch (format) { @@ -807,7 +790,6 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) return "rg32f"; case ImageFormatRg16f: return "rg16f"; - case ImageFormatRgba32i: return "rgba32i"; case ImageFormatRgba16i: @@ -820,7 +802,6 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) return "rg32i"; case ImageFormatRg16i: return "rg16i"; - case ImageFormatRgba32ui: return "rgba32ui"; case ImageFormatRgba16ui: @@ -833,71 +814,46 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) return "rg32ui"; case ImageFormatRg16ui: return "rg16ui"; - - // Desktop-only formats case ImageFormatR11fG11fB10f: - check_desktop(); return "r11f_g11f_b10f"; case ImageFormatR16f: - check_desktop(); return "r16f"; case ImageFormatRgb10A2: - check_desktop(); return "rgb10_a2"; case ImageFormatR8: - check_desktop(); return "r8"; case ImageFormatRg8: - check_desktop(); return "rg8"; case ImageFormatR16: - check_desktop(); return "r16"; case ImageFormatRg16: - check_desktop(); return "rg16"; case ImageFormatRgba16: - check_desktop(); return "rgba16"; case ImageFormatR16Snorm: - check_desktop(); return "r16_snorm"; case ImageFormatRg16Snorm: - check_desktop(); return "rg16_snorm"; case ImageFormatRgba16Snorm: - check_desktop(); return "rgba16_snorm"; case ImageFormatR8Snorm: - check_desktop(); return "r8_snorm"; case ImageFormatRg8Snorm: - check_desktop(); return "rg8_snorm"; - case ImageFormatR8ui: - check_desktop(); return "r8ui"; case ImageFormatRg8ui: - check_desktop(); return "rg8ui"; case ImageFormatR16ui: - check_desktop(); return "r16ui"; case ImageFormatRgb10a2ui: - check_desktop(); return "rgb10_a2ui"; - case ImageFormatR8i: - check_desktop(); return "r8i"; case ImageFormatRg8i: - check_desktop(); return "rg8i"; case ImageFormatR16i: - check_desktop(); return "r16i"; - default: case ImageFormatUnknown: return nullptr; @@ -1695,7 +1651,7 @@ void CompilerGLSL::emit_specialization_constant_op(const SPIRConstantOp &constan statement("const ", variable_decl(type, name), " = ", constant_op_expression(constant), ";"); } -void CompilerGLSL::emit_specialization_constant(const SPIRConstant &constant) +void CompilerGLSL::emit_constant(const SPIRConstant &constant) { auto &type = get(constant.constant_type); auto name = to_name(constant.self); @@ -2116,25 +2072,25 @@ void CompilerGLSL::emit_resources() // // TODO: If we have the fringe case that we create a spec constant which depends on a struct type, // we'll have to deal with that, but there's currently no known way to express that. - if (options.vulkan_semantics) + for (auto &id : ids) { - for (auto &id : ids) + if (id.get_type() == TypeConstant) { - if (id.get_type() == TypeConstant) - { - auto &c = id.get(); - if (!c.specialization) - continue; + auto &c = id.get(); - emit_specialization_constant(c); - emitted = true; - } - else if (id.get_type() == TypeConstantOp) + bool needs_declaration = (c.specialization && options.vulkan_semantics) || c.is_used_as_lut; + + if (needs_declaration) { - emit_specialization_constant_op(id.get()); + emit_constant(c); emitted = true; } } + else if (options.vulkan_semantics && id.get_type() == TypeConstantOp) + { + emit_specialization_constant_op(id.get()); + emitted = true; + } } if (emitted) @@ -2246,7 +2202,8 @@ void CompilerGLSL::emit_resources() { // For gl_InstanceIndex emulation on GLES, the API user needs to // supply this uniform. - if (meta[var.self].decoration.builtin_type == BuiltInInstanceIndex && !options.vulkan_semantics) + if (options.vertex.support_nonzero_base_instance && + meta[var.self].decoration.builtin_type == BuiltInInstanceIndex && !options.vulkan_semantics) { statement("uniform int SPIRV_Cross_BaseInstance;"); emitted = true; @@ -2342,9 +2299,9 @@ string CompilerGLSL::enclose_expression(const string &expr) uint32_t paren_count = 0; for (auto c : expr) { - if (c == '(') + if (c == '(' || c == '[') paren_count++; - else if (c == ')') + else if (c == ')' || c == ']') { assert(paren_count); paren_count--; @@ -2468,6 +2425,8 @@ string CompilerGLSL::to_expression(uint32_t id) return builtin_to_glsl(dec.builtin_type, StorageClassGeneric); else if (c.specialization && options.vulkan_semantics) return to_name(id); + else if (c.is_used_as_lut) + return to_name(id); else if (type.basetype == SPIRType::Struct && !backend.can_declare_struct_inline) return to_name(id); else if (!type.array.empty() && !backend.can_declare_arrays_inline) @@ -2545,15 +2504,15 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop) break; #define GLSL_BOP(opname, x) \ - case Op##opname: \ - binary = true; \ - op = x; \ + case Op##opname: \ + binary = true; \ + op = x; \ break #define GLSL_UOP(opname, x) \ - case Op##opname: \ - unary = true; \ - op = x; \ + case Op##opname: \ + unary = true; \ + op = x; \ break GLSL_UOP(SNegate, "-"); @@ -2597,11 +2556,14 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop) // In order to preserve its compile-time constness in Vulkan GLSL, // we need to reduce the OpSelect expression back to this simplified model. // If we cannot, fail. - if (!to_trivial_mix_op(type, op, cop.arguments[2], cop.arguments[1], cop.arguments[0])) + if (to_trivial_mix_op(type, op, cop.arguments[2], cop.arguments[1], cop.arguments[0])) { - SPIRV_CROSS_THROW( - "Cannot implement specialization constant op OpSelect. " - "Need trivial select implementation which can be resolved to a simple cast from boolean."); + // Implement as a simple cast down below. + } + else + { + // Implement a ternary and pray the compiler understands it :) + return to_ternary_expression(type, cop.arguments[0], cop.arguments[1], cop.arguments[2]); } break; } @@ -3501,7 +3463,7 @@ bool CompilerGLSL::check_explicit_lod_allowed(uint32_t lod) return allowed; } -string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod) +string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod, uint32_t tex) { const char *type; switch (imgtype.image.dim) @@ -3531,7 +3493,7 @@ string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtyp bool use_explicit_lod = check_explicit_lod_allowed(lod); - if (op == "textureLod" || op == "textureProjLod" || op == "textureGrad") + if (op == "textureLod" || op == "textureProjLod" || op == "textureGrad" || op == "textureProjGrad") { if (is_legacy_es()) { @@ -3542,25 +3504,64 @@ string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtyp require_extension_internal("GL_ARB_shader_texture_lod"); } + if (op == "textureLodOffset" || op == "textureProjLodOffset") + { + if (is_legacy_es()) + SPIRV_CROSS_THROW(join(op, " not allowed in legacy ES")); + + require_extension_internal("GL_EXT_gpu_shader4"); + } + + // GLES has very limited support for shadow samplers. + // Basically shadow2D and shadow2DProj work through EXT_shadow_samplers, + // everything else can just throw + if (image_is_comparison(imgtype, tex) && is_legacy_es()) + { + if (op == "texture" || op == "textureProj") + require_extension_internal("GL_EXT_shadow_samplers"); + else + SPIRV_CROSS_THROW(join(op, " not allowed on depth samplers in legacy ES")); + } + + bool is_es_and_depth = is_legacy_es() && image_is_comparison(imgtype, tex); + std::string type_prefix = image_is_comparison(imgtype, tex) ? "shadow" : "texture"; + if (op == "texture") - return join("texture", type); + return is_es_and_depth ? join(type_prefix, type, "EXT") : join(type_prefix, type); else if (op == "textureLod") { if (use_explicit_lod) - return join("texture", type, is_legacy_es() ? "LodEXT" : "Lod"); + return join(type_prefix, type, is_legacy_es() ? "LodEXT" : "Lod"); else - return join("texture", type); + return join(type_prefix, type); } else if (op == "textureProj") - return join("texture", type, "Proj"); + return join(type_prefix, type, is_es_and_depth ? "ProjEXT" : "Proj"); else if (op == "textureGrad") - return join("texture", type, is_legacy_es() ? "GradEXT" : is_legacy_desktop() ? "GradARB" : "Grad"); + return join(type_prefix, type, is_legacy_es() ? "GradEXT" : is_legacy_desktop() ? "GradARB" : "Grad"); else if (op == "textureProjLod") { if (use_explicit_lod) - return join("texture", type, is_legacy_es() ? "ProjLodEXT" : "ProjLod"); + return join(type_prefix, type, is_legacy_es() ? "ProjLodEXT" : "ProjLod"); else - return join("texture", type); + return join(type_prefix, type, "Proj"); + } + else if (op == "textureLodOffset") + { + if (use_explicit_lod) + return join(type_prefix, type, "LodOffset"); + else + return join(type_prefix, type); + } + else if (op == "textureProjGrad") + return join(type_prefix, type, + is_legacy_es() ? "ProjGradEXT" : is_legacy_desktop() ? "ProjGradARB" : "ProjGrad"); + else if (op == "textureProjLodOffset") + { + if (use_explicit_lod) + return join(type_prefix, type, "ProjLodOffset"); + else + return join(type_prefix, type, "ProjOffset"); } else { @@ -3622,6 +3623,37 @@ bool CompilerGLSL::to_trivial_mix_op(const SPIRType &type, string &op, uint32_t return ret; } +string CompilerGLSL::to_ternary_expression(const SPIRType &restype, uint32_t select, uint32_t true_value, + uint32_t false_value) +{ + string expr; + auto &lerptype = expression_type(select); + + if (lerptype.vecsize == 1) + expr = join(to_enclosed_expression(select), " ? ", to_enclosed_expression(true_value), " : ", + to_enclosed_expression(false_value)); + else + { + auto swiz = [this](uint32_t expression, uint32_t i) { return to_extract_component_expression(expression, i); }; + + expr = type_to_glsl_constructor(restype); + expr += "("; + for (uint32_t i = 0; i < restype.vecsize; i++) + { + expr += swiz(select, i); + expr += " ? "; + expr += swiz(true_value, i); + expr += " : "; + expr += swiz(false_value, i); + if (i + 1 < restype.vecsize) + expr += ", "; + } + expr += ")"; + } + + return expr; +} + void CompilerGLSL::emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp) { auto &lerptype = expression_type(lerp); @@ -3652,31 +3684,7 @@ void CompilerGLSL::emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, // Could use GL_EXT_shader_integer_mix on desktop at least, // but Apple doesn't support it. :( // Just implement it as ternary expressions. - string expr; - if (lerptype.vecsize == 1) - expr = join(to_enclosed_expression(lerp), " ? ", to_enclosed_expression(right), " : ", - to_enclosed_expression(left)); - else - { - auto swiz = [this](uint32_t expression, uint32_t i) { - return to_extract_component_expression(expression, i); - }; - - expr = type_to_glsl_constructor(restype); - expr += "("; - for (uint32_t i = 0; i < restype.vecsize; i++) - { - expr += swiz(lerp, i); - expr += " ? "; - expr += swiz(right, i); - expr += " : "; - expr += swiz(left, i); - if (i + 1 < restype.vecsize) - expr += ", "; - } - expr += ")"; - } - + auto expr = to_ternary_expression(get(result_type), lerp, right, left); emit_op(result_type, id, expr, should_forward(left) && should_forward(right) && should_forward(lerp)); inherit_expression_dependencies(id, left); inherit_expression_dependencies(id, right); @@ -3759,7 +3767,7 @@ void CompilerGLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_i if (options.vulkan_semantics && combined_image_samplers.empty()) { emit_binary_func_op(result_type, result_id, image_id, samp_id, - type_to_glsl(get(result_type)).c_str()); + type_to_glsl(get(result_type), result_id).c_str()); // Make sure to suppress usage tracking. It is illegal to create temporaries of opaque types. forwarded_temporaries.erase(result_id); @@ -3924,6 +3932,10 @@ void CompilerGLSL::emit_texture_op(const Instruction &i) coffset, offset, bias, comp, sample, &forward); expr += ")"; + // texture(samplerXShadow) returns float. shadowX() returns vec4. Swizzle here. + if (is_legacy() && image_is_comparison(imgtype, img)) + expr += ".r"; + emit_op(result_type, id, expr, forward); for (auto &inherit : inherited_expressions) inherit_expression_dependencies(id, inherit); @@ -3944,8 +3956,9 @@ void CompilerGLSL::emit_texture_op(const Instruction &i) // Returns the function name for a texture sampling function for the specified image and sampling characteristics. // For some subclasses, the function is a method on the specified image. -string CompilerGLSL::to_function_name(uint32_t, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj, - bool has_array_offsets, bool has_offset, bool has_grad, bool, uint32_t lod) +string CompilerGLSL::to_function_name(uint32_t tex, const SPIRType &imgtype, bool is_fetch, bool is_gather, + bool is_proj, bool has_array_offsets, bool has_offset, bool has_grad, bool, + uint32_t lod) { string fname; @@ -3955,7 +3968,7 @@ string CompilerGLSL::to_function_name(uint32_t, const SPIRType &imgtype, bool is // This happens for HLSL SampleCmpLevelZero on Texture2DArray and TextureCube. bool workaround_lod_array_shadow_as_grad = false; if (((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || imgtype.image.dim == DimCube) && - imgtype.image.depth && lod) + image_is_comparison(imgtype, tex) && lod) { auto *constant_lod = maybe_get(lod); if (!constant_lod || constant_lod->scalar_f32() != 0.0f) @@ -3985,7 +3998,7 @@ string CompilerGLSL::to_function_name(uint32_t, const SPIRType &imgtype, bool is if (has_offset) fname += "Offset"; - return is_legacy() ? legacy_tex_op(fname, imgtype, lod) : fname; + return is_legacy() ? legacy_tex_op(fname, imgtype, lod, tex) : fname; } std::string CompilerGLSL::convert_separate_image_to_combined(uint32_t id) @@ -4070,7 +4083,7 @@ string CompilerGLSL::to_function_args(uint32_t img, const SPIRType &imgtype, boo // This happens for HLSL SampleCmpLevelZero on Texture2DArray and TextureCube. bool workaround_lod_array_shadow_as_grad = ((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || imgtype.image.dim == DimCube) && - imgtype.image.depth && lod; + image_is_comparison(imgtype, img) && lod; if (dref) { @@ -4484,6 +4497,16 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtOffset"); break; + case GLSLstd450NMin: + emit_binary_func_op(result_type, id, args[0], args[1], "unsupported_glsl450_nmin"); + break; + case GLSLstd450NMax: + emit_binary_func_op(result_type, id, args[0], args[1], "unsupported_glsl450_nmax"); + break; + case GLSLstd450NClamp: + emit_binary_func_op(result_type, id, args[0], args[1], "unsupported_glsl450_nclamp"); + break; + default: statement("// unimplemented GLSL op ", eop); break; @@ -4802,7 +4825,7 @@ void CompilerGLSL::emit_subgroup_op(const Instruction &i) break; // clang-format off -#define GROUP_OP(op, glsl_op) \ +#define GLSL_GROUP_OP(op, glsl_op) \ case OpGroupNonUniform##op: \ { \ auto operation = static_cast(ops[3]); \ @@ -4818,20 +4841,20 @@ case OpGroupNonUniform##op: \ SPIRV_CROSS_THROW("Invalid group operation."); \ break; \ } - GROUP_OP(FAdd, Add) - GROUP_OP(FMul, Mul) - GROUP_OP(FMin, Min) - GROUP_OP(FMax, Max) - GROUP_OP(IAdd, Add) - GROUP_OP(IMul, Mul) - GROUP_OP(SMin, Min) - GROUP_OP(SMax, Max) - GROUP_OP(UMin, Min) - GROUP_OP(UMax, Max) - GROUP_OP(BitwiseAnd, And) - GROUP_OP(BitwiseOr, Or) - GROUP_OP(BitwiseXor, Xor) -#undef GROUP_OP + GLSL_GROUP_OP(FAdd, Add) + GLSL_GROUP_OP(FMul, Mul) + GLSL_GROUP_OP(FMin, Min) + GLSL_GROUP_OP(FMax, Max) + GLSL_GROUP_OP(IAdd, Add) + GLSL_GROUP_OP(IMul, Mul) + GLSL_GROUP_OP(SMin, Min) + GLSL_GROUP_OP(SMax, Max) + GLSL_GROUP_OP(UMin, Min) + GLSL_GROUP_OP(UMax, Max) + GLSL_GROUP_OP(BitwiseAnd, And) + GLSL_GROUP_OP(BitwiseOr, Or) + GLSL_GROUP_OP(BitwiseXor, Xor) +#undef GLSL_GROUP_OP // clang-format on case OpGroupNonUniformQuadSwap: @@ -4961,10 +4984,15 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) case BuiltInInstanceIndex: if (options.vulkan_semantics) return "gl_InstanceIndex"; - else + else if (options.vertex.support_nonzero_base_instance) return "(gl_InstanceID + SPIRV_Cross_BaseInstance)"; // ... but not gl_InstanceID. + else + return "gl_InstanceID"; case BuiltInPrimitiveId: - return "gl_PrimitiveID"; + if (storage == StorageClassInput && get_entry_point().model == ExecutionModelGeometry) + return "gl_PrimitiveIDIn"; + else + return "gl_PrimitiveID"; case BuiltInInvocationId: return "gl_InvocationID"; case BuiltInLayer: @@ -5119,16 +5147,10 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice if (!chain_only) expr = to_enclosed_expression(base); - uint32_t type_id = expression_type_id(base); - const auto *type = &get(type_id); - // Start traversing type hierarchy at the proper non-pointer types, // but keep type_id referencing the original pointer for use below. - while (type->pointer) - { - assert(type->parent_type); - type = &get(type->parent_type); - } + uint32_t type_id = expression_type_id(base); + const auto *type = &get_non_pointer_type(type_id); bool access_chain_is_arrayed = expr.find_first_of('[') != string::npos; bool row_major_matrix_needs_conversion = is_non_native_row_major_matrix(base); @@ -5571,14 +5593,8 @@ std::pair CompilerGLSL::flattened_access_chain_offset(con bool *need_transpose, uint32_t *out_matrix_stride) { - const auto *type = &basetype; - // Start traversing type hierarchy at the proper non-pointer types. - while (type->pointer) - { - assert(type->parent_type); - type = &get(type->parent_type); - } + const auto *type = &get_non_pointer_type(basetype); // This holds the type of the current pointer which we are traversing through. // We always start out from a struct type which is the block. @@ -6085,10 +6101,10 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) #define GLSL_BOP_CAST(op, type) \ emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, glsl_opcode_is_sign_invariant(opcode)) #define GLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) -#define QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) +#define GLSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) #define GLSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) #define GLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) -#define GLSL_GLSL_BFOP_CAST(op, type) \ +#define GLSL_BFOP_CAST(op, type) \ emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, glsl_opcode_is_sign_invariant(opcode)) #define GLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) #define GLSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) @@ -6125,6 +6141,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) auto expr = to_expression(ptr); + // We might need to bitcast in order to load from a builtin. + bitcast_from_builtin_load(ptr, expr, get(result_type)); + if (ptr_expression) ptr_expression->need_transpose = old_need_transpose; @@ -6174,6 +6193,10 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) var->static_expression = ops[1]; else if (var && var->loop_variable && !var->loop_variable_enable) var->static_expression = ops[1]; + else if (var && var->remapped_variable) + { + // Skip the write. + } else if (var && flattened_structs.count(ops[0])) { store_flattened_struct(*var, ops[1]); @@ -6182,11 +6205,15 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) else { auto rhs = to_expression(ops[1]); + // Statements to OpStore may be empty if it is a struct with zero members. Just forward the store to /dev/null. if (!rhs.empty()) { auto lhs = to_expression(ops[0]); + // We might need to bitcast in order to store to a builtin. + bitcast_to_builtin_store(ops[0], rhs, expression_type(ops[1])); + // Tries to optimize assignments like " = op expr". // While this is purely cosmetic, this is important for legacy ESSL where loop // variable increments must be in either i++ or i += const-expr. @@ -6852,7 +6879,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpIEqual: { if (expression_type(ops[2]).vecsize > 1) - GLSL_GLSL_BFOP_CAST(equal, SPIRType::Int); + GLSL_BFOP_CAST(equal, SPIRType::Int); else GLSL_BOP_CAST(==, SPIRType::Int); break; @@ -6871,7 +6898,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpINotEqual: { if (expression_type(ops[2]).vecsize > 1) - GLSL_GLSL_BFOP_CAST(notEqual, SPIRType::Int); + GLSL_BFOP_CAST(notEqual, SPIRType::Int); else GLSL_BOP_CAST(!=, SPIRType::Int); break; @@ -6892,7 +6919,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpUGreaterThan ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - GLSL_GLSL_BFOP_CAST(greaterThan, type); + GLSL_BFOP_CAST(greaterThan, type); else GLSL_BOP_CAST(>, type); break; @@ -6912,7 +6939,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpUGreaterThanEqual ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - GLSL_GLSL_BFOP_CAST(greaterThanEqual, type); + GLSL_BFOP_CAST(greaterThanEqual, type); else GLSL_BOP_CAST(>=, type); break; @@ -6932,7 +6959,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpULessThan ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - GLSL_GLSL_BFOP_CAST(lessThan, type); + GLSL_BFOP_CAST(lessThan, type); else GLSL_BOP_CAST(<, type); break; @@ -6952,7 +6979,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpULessThanEqual ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - GLSL_GLSL_BFOP_CAST(lessThanEqual, type); + GLSL_BFOP_CAST(lessThanEqual, type); else GLSL_BOP_CAST(<=, type); break; @@ -7126,7 +7153,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // Bitfield case OpBitFieldInsert: // TODO: The signedness of inputs is strict in GLSL, but not in SPIR-V, bitcast if necessary. - QFOP(bitfieldInsert); + GLSL_QFOP(bitfieldInsert); break; case OpBitFieldSExtract: @@ -7987,6 +8014,30 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) emit_subgroup_op(instruction); break; + case OpFUnordEqual: + GLSL_BFOP(unsupported_FUnordEqual); + break; + + case OpFUnordNotEqual: + GLSL_BFOP(unsupported_FUnordNotEqual); + break; + + case OpFUnordLessThan: + GLSL_BFOP(unsupported_FUnordLessThan); + break; + + case OpFUnordGreaterThan: + GLSL_BFOP(unsupported_FUnordGreaterThan); + break; + + case OpFUnordLessThanEqual: + GLSL_BFOP(unsupported_FUnordLessThanEqual); + break; + + case OpFUnordGreaterThanEqual: + GLSL_BFOP(unsupported_FUnordGreaterThanEqual); + break; + default: statement("// unimplemented op ", instruction.op); break; @@ -8245,6 +8296,11 @@ string CompilerGLSL::argument_decl(const SPIRFunction::Parameter &arg) return join(direction, to_qualifiers_glsl(arg.id), variable_decl(type, to_name(arg.id), arg.id)); } +string CompilerGLSL::to_initializer_expression(const SPIRVariable &var) +{ + return to_expression(var.initializer); +} + string CompilerGLSL::variable_decl(const SPIRVariable &variable) { // Ignore the pointer type since GLSL doesn't have pointers. @@ -8262,7 +8318,7 @@ string CompilerGLSL::variable_decl(const SPIRVariable &variable) { uint32_t expr = variable.initializer; if (ids[expr].get_type() != TypeUndef) - res += join(" = ", to_expression(variable.initializer)); + res += join(" = ", to_initializer_expression(variable)); } return res; } @@ -8375,7 +8431,7 @@ string CompilerGLSL::type_to_array_glsl(const SPIRType &type) } } -string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t /* id */) +string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t id) { auto &imagetype = get(type.image.type); string res; @@ -8448,8 +8504,11 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t /* id */) } // "Shadow" state in GLSL only exists for samplers and combined image samplers. - if (((type.basetype == SPIRType::SampledImage) || (type.basetype == SPIRType::Sampler)) && type.image.depth) + if (((type.basetype == SPIRType::SampledImage) || (type.basetype == SPIRType::Sampler)) && + image_is_comparison(type, id)) + { res += "Shadow"; + } return res; } @@ -8495,7 +8554,7 @@ string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id) case SPIRType::Sampler: // The depth field is set by calling code based on the variable ID of the sampler, effectively reintroducing // this distinction into the type system. - return comparison_samplers.count(id) ? "samplerShadow" : "sampler"; + return comparison_ids.count(id) ? "samplerShadow" : "sampler"; case SPIRType::Void: return "void"; @@ -8713,13 +8772,8 @@ void CompilerGLSL::add_function_overload(const SPIRFunction &func) // Parameters can vary with pointer type or not, // but that will not change the signature in GLSL/HLSL, // so strip the pointer type before hashing. - uint32_t type_id = arg.type; - auto *type = &get(type_id); - while (type->pointer) - { - type_id = type->parent_type; - type = &get(type_id); - } + uint32_t type_id = get_non_pointer_type_id(arg.type); + auto &type = get(type_id); if (!combined_image_samplers.empty()) { @@ -8727,8 +8781,8 @@ void CompilerGLSL::add_function_overload(const SPIRFunction &func) // we pass down to callees, because they may be shuffled around. // Ignore these arguments, to make sure that functions need to differ in some other way // to be considered different overloads. - if (type->basetype == SPIRType::SampledImage || - (type->basetype == SPIRType::Image && type->image.sampled == 1) || type->basetype == SPIRType::Sampler) + if (type.basetype == SPIRType::SampledImage || + (type.basetype == SPIRType::Image && type.image.sampled == 1) || type.basetype == SPIRType::Sampler) { continue; } @@ -8866,41 +8920,6 @@ void CompilerGLSL::emit_function(SPIRFunction &func, const Bitset &return_flags) current_function = &func; auto &entry_block = get(func.entry_block); - if (!func.analyzed_variable_scope) - { - analyze_variable_scope(func); - - // Check if we can actually use the loop variables we found in analyze_variable_scope. - // To use multiple initializers, we need the same type and qualifiers. - for (auto block : func.blocks) - { - auto &b = get(block); - if (b.loop_variables.size() < 2) - continue; - - auto &flags = get_decoration_bitset(b.loop_variables.front()); - uint32_t type = get(b.loop_variables.front()).basetype; - bool invalid_initializers = false; - for (auto loop_variable : b.loop_variables) - { - if (flags != get_decoration_bitset(loop_variable) || - type != get(b.loop_variables.front()).basetype) - { - invalid_initializers = true; - break; - } - } - - if (invalid_initializers) - { - for (auto loop_variable : b.loop_variables) - get(loop_variable).loop_variable = false; - b.loop_variables.clear(); - } - } - func.analyzed_variable_scope = true; - } - for (auto &v : func.local_variables) { auto &var = get(v); @@ -8927,6 +8946,11 @@ void CompilerGLSL::emit_function(SPIRFunction &func, const Bitset &return_flags) entry_block.dominated_variables.push_back(var.self); var.deferred_declaration = true; } + else if (var.storage == StorageClassFunction && var.remapped_variable && var.static_expression) + { + // No need to declare this variable, it has a static expression. + var.deferred_declaration = false; + } else if (expression_is_lvalue(v)) { add_local_variable_name(var.self); @@ -9123,6 +9147,7 @@ void CompilerGLSL::branch(uint32_t from, uint32_t cond, uint32_t true_block, uin if (true_sub) { + emit_block_hints(get(from)); statement("if (", to_expression(cond), ")"); begin_scope(); branch(from, true_block); @@ -9146,6 +9171,7 @@ void CompilerGLSL::branch(uint32_t from, uint32_t cond, uint32_t true_block, uin else if (false_sub && !true_sub) { // Only need false path, use negative conditional. + emit_block_hints(get(from)); statement("if (!", to_enclosed_expression(cond), ")"); begin_scope(); branch(from, false_block); @@ -9374,6 +9400,7 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method { // This block may be a dominating block, so make sure we flush undeclared variables before building the for loop header. flush_undeclared_variables(block); + emit_block_hints(block); // Important that we do this in this order because // emitting the continue block can invalidate the condition expression. @@ -9392,6 +9419,7 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method case SPIRBlock::WhileLoop: // This block may be a dominating block, so make sure we flush undeclared variables before building the while loop header. flush_undeclared_variables(block); + emit_block_hints(block); statement("while (", to_expression(block.condition), ")"); break; @@ -9439,11 +9467,13 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method auto initializer = emit_for_loop_initializers(block); auto condition = to_expression(child.condition); auto continue_block = emit_continue_block(block.continue_block); + emit_block_hints(block); statement("for (", initializer, "; ", condition, "; ", continue_block, ")"); break; } case SPIRBlock::WhileLoop: + emit_block_hints(block); statement("while (", to_expression(child.condition), ")"); break; @@ -9654,35 +9684,65 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) auto &type = expression_type(block.condition); bool uint32_t_case = type.basetype == SPIRType::UInt; + emit_block_hints(block); statement("switch (", to_expression(block.condition), ")"); begin_scope(); + // Multiple case labels can branch to same block, so find all unique blocks. + bool emitted_default = false; + unordered_set emitted_blocks; + for (auto &c : block.cases) { - auto case_value = - uint32_t_case ? convert_to_string(uint32_t(c.value)) : convert_to_string(int32_t(c.value)); - statement("case ", case_value, ":"); + if (emitted_blocks.count(c.block) != 0) + continue; + + // Emit all case labels which branch to our target. + // FIXME: O(n^2), revisit if we hit shaders with 100++ case labels ... + for (auto &other_case : block.cases) + { + if (other_case.block == c.block) + { + auto case_value = uint32_t_case ? convert_to_string(uint32_t(other_case.value)) : + convert_to_string(int32_t(other_case.value)); + statement("case ", case_value, ":"); + } + } + + // Maybe we share with default block? + if (block.default_block == c.block) + { + statement("default:"); + emitted_default = true; + } + + // Complete the target. + emitted_blocks.insert(c.block); + begin_scope(); branch(block.self, c.block); end_scope(); } - if (block.default_block != block.next_block) + if (!emitted_default) { - statement("default:"); - begin_scope(); - if (is_break(block.default_block)) - SPIRV_CROSS_THROW("Cannot break; out of a switch statement and out of a loop at the same time ..."); - branch(block.self, block.default_block); - end_scope(); - } - else if (flush_phi_required(block.self, block.next_block)) - { - statement("default:"); - begin_scope(); - flush_phi(block.self, block.next_block); - statement("break;"); - end_scope(); + if (block.default_block != block.next_block) + { + statement("default:"); + begin_scope(); + if (is_break(block.default_block)) + SPIRV_CROSS_THROW("Cannot break; out of a switch statement and out of a loop at the same time ..."); + branch(block.self, block.default_block); + end_scope(); + } + else if (flush_phi_required(block.self, block.next_block)) + { + statement("default:"); + begin_scope(); + flush_phi(block.self, block.next_block); + statement("break;"); + end_scope(); + } } end_scope(); @@ -9874,3 +9934,70 @@ void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t rhs_id) { statement(lhs, " = ", to_expression(rhs_id), ";"); } + +void CompilerGLSL::bitcast_from_builtin_load(uint32_t source_id, std::string &expr, + const spirv_cross::SPIRType &expr_type) +{ + // Only interested in standalone builtin variables. + if (!has_decoration(source_id, DecorationBuiltIn)) + return; + + auto builtin = static_cast(get_decoration(source_id, DecorationBuiltIn)); + auto expected_type = expr_type.basetype; + + // TODO: Fill in for more builtins. + switch (builtin) + { + case BuiltInLayer: + case BuiltInPrimitiveId: + case BuiltInViewportIndex: + case BuiltInInstanceId: + case BuiltInInstanceIndex: + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInSampleId: + expected_type = SPIRType::Int; + break; + + default: + break; + } + + if (expected_type != expr_type.basetype) + expr = bitcast_expression(expr_type, expected_type, expr); +} + +void CompilerGLSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr, + const spirv_cross::SPIRType &expr_type) +{ + // Only interested in standalone builtin variables. + if (!has_decoration(target_id, DecorationBuiltIn)) + return; + + auto builtin = static_cast(get_decoration(target_id, DecorationBuiltIn)); + auto expected_type = expr_type.basetype; + + // TODO: Fill in for more builtins. + switch (builtin) + { + case BuiltInLayer: + case BuiltInPrimitiveId: + case BuiltInViewportIndex: + expected_type = SPIRType::Int; + break; + + default: + break; + } + + if (expected_type != expr_type.basetype) + { + auto type = expr_type; + type.basetype = expected_type; + expr = bitcast_expression(type, expr_type.basetype, expr); + } +} + +void CompilerGLSL::emit_block_hints(const SPIRBlock &) +{ +} diff --git a/deps/SPIRV-Cross/spirv_glsl.hpp b/deps/SPIRV-Cross/spirv_glsl.hpp index 442a31c602..e426e5061e 100644 --- a/deps/SPIRV-Cross/spirv_glsl.hpp +++ b/deps/SPIRV-Cross/spirv_glsl.hpp @@ -106,6 +106,11 @@ public: // Inverts gl_Position.y or equivalent. bool flip_vert_y = false; + + // If true, the backend will assume that InstanceIndex will need to apply + // a base instance offset. Set to false if you know you will never use base instance + // functionality as it might remove some internal uniforms. + bool support_nonzero_base_instance = true; } vertex; struct @@ -277,7 +282,6 @@ protected: { for (uint32_t i = 0; i < indent; i++) (*buffer) << " "; - statement_inner(std::forward(ts)...); (*buffer) << '\n'; } @@ -371,7 +375,7 @@ protected: void emit_flattened_io_block(const SPIRVariable &var, const char *qual); void emit_block_chain(SPIRBlock &block); void emit_hoisted_temporaries(std::vector> &temporaries); - void emit_specialization_constant(const SPIRConstant &constant); + void emit_constant(const SPIRConstant &constant); void emit_specialization_constant_op(const SPIRConstantOp &constant); std::string emit_continue_block(uint32_t continue_block); bool attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method); @@ -405,6 +409,9 @@ protected: SPIRType binary_op_bitcast_helper(std::string &cast_op0, std::string &cast_op1, SPIRType::BaseType &input_type, uint32_t op0, uint32_t op1, bool skip_cast_if_equal_type); + std::string to_ternary_expression(const SPIRType &result_type, uint32_t select, uint32_t true_value, + uint32_t false_value); + void emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); bool expression_is_forwarded(uint32_t id); SPIRExpression &emit_op(uint32_t result_type, uint32_t result_id, const std::string &rhs, bool forward_rhs, @@ -452,11 +459,12 @@ protected: const char *format_to_glsl(spv::ImageFormat format); virtual std::string layout_for_member(const SPIRType &type, uint32_t index); virtual std::string to_interpolation_qualifiers(const Bitset &flags); - Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index); std::string layout_for_variable(const SPIRVariable &variable); std::string to_combined_image_sampler(uint32_t image_id, uint32_t samp_id); virtual bool skip_argument(uint32_t id) const; virtual void emit_array_copy(const std::string &lhs, uint32_t rhs_id); + virtual void emit_block_hints(const SPIRBlock &block); + virtual std::string to_initializer_expression(const SPIRVariable &var); bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, uint32_t start_offset = 0, uint32_t end_offset = UINT32_MAX); @@ -485,7 +493,7 @@ protected: void replace_fragment_output(SPIRVariable &var); void replace_fragment_outputs(); bool check_explicit_lod_allowed(uint32_t lod); - std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod); + std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod, uint32_t id); uint32_t indent = 0; @@ -563,6 +571,12 @@ protected: std::string convert_separate_image_to_combined(uint32_t id); + // Builtins in GLSL are always specific signedness, but the SPIR-V can declare them + // as either unsigned or signed. + // Sometimes we will need to automatically perform bitcasts on load and store to make this work. + virtual void bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type); + virtual void bitcast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type); + private: void init() { diff --git a/deps/SPIRV-Cross/spirv_hlsl.cpp b/deps/SPIRV-Cross/spirv_hlsl.cpp index b294b79d1d..34e1d19fb5 100644 --- a/deps/SPIRV-Cross/spirv_hlsl.cpp +++ b/deps/SPIRV-Cross/spirv_hlsl.cpp @@ -224,7 +224,7 @@ static bool hlsl_opcode_is_sign_invariant(Op opcode) } } -string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type) +string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type, uint32_t) { auto &imagetype = get(type.image.type); const char *dim = nullptr; @@ -275,7 +275,7 @@ string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type) ">"); } -string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type) +string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type, uint32_t id) { auto &imagetype = get(type.image.type); string res; @@ -338,18 +338,18 @@ string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type) res += "MS"; if (type.image.arrayed) res += "Array"; - if (type.image.depth) + if (image_is_comparison(type, id)) res += "Shadow"; return res; } -string CompilerHLSL::image_type_hlsl(const SPIRType &type) +string CompilerHLSL::image_type_hlsl(const SPIRType &type, uint32_t id) { if (hlsl_options.shader_model <= 30) - return image_type_hlsl_legacy(type); + return image_type_hlsl_legacy(type, id); else - return image_type_hlsl_modern(type); + return image_type_hlsl_modern(type, id); } // The optional id parameter indicates the object whose type we are trying @@ -370,10 +370,10 @@ string CompilerHLSL::type_to_glsl(const SPIRType &type, uint32_t id) case SPIRType::Image: case SPIRType::SampledImage: - return image_type_hlsl(type); + return image_type_hlsl(type, id); case SPIRType::Sampler: - return comparison_samplers.count(id) ? "SamplerComparisonState" : "SamplerState"; + return comparison_ids.count(id) ? "SamplerComparisonState" : "SamplerState"; case SPIRType::Void: return "void"; @@ -1838,9 +1838,10 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var) { Bitset flags = get_buffer_block_flags(var); bool is_readonly = flags.get(DecorationNonWritable); + bool is_coherent = flags.get(DecorationCoherent); add_resource_name(var.self); - statement(is_readonly ? "ByteAddressBuffer " : "RWByteAddressBuffer ", to_name(var.self), - type_to_array_glsl(type), to_resource_binding(var), ";"); + statement(is_coherent ? "globallycoherent " : "", is_readonly ? "ByteAddressBuffer " : "RWByteAddressBuffer ", + to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";"); } else { @@ -2059,7 +2060,7 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &ret { // Manufacture automatic sampler arg for SampledImage texture decl += ", "; - decl += join(arg_type.image.depth ? "SamplerComparisonState " : "SamplerState ", + decl += join(image_is_comparison(arg_type, arg.id) ? "SamplerComparisonState " : "SamplerState ", to_sampler_expression(arg.id), type_to_array_glsl(arg_type)); } @@ -2574,7 +2575,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i) { texop += img_expr; - if (imgtype.image.depth) + if (image_is_comparison(imgtype, img)) { if (gather) { @@ -2922,13 +2923,17 @@ void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var) case SPIRType::SampledImage: case SPIRType::Image: { - statement(image_type_hlsl_modern(type), " ", to_name(var.self), type_to_array_glsl(type), - to_resource_binding(var), ";"); + bool is_coherent = false; + if (type.basetype == SPIRType::Image && type.image.sampled == 2) + is_coherent = has_decoration(var.self, DecorationCoherent); + + statement(is_coherent ? "globallycoherent " : "", image_type_hlsl_modern(type, var.self), " ", + to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";"); if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer) { // For combined image samplers, also emit a combined image sampler. - if (type.image.depth) + if (image_is_comparison(type, var.self)) statement("SamplerComparisonState ", to_sampler_expression(var.self), type_to_array_glsl(type), to_resource_binding_sampler(var), ";"); else @@ -2939,7 +2944,7 @@ void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var) } case SPIRType::Sampler: - if (comparison_samplers.count(var.self)) + if (comparison_ids.count(var.self)) statement("SamplerComparisonState ", to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";"); else @@ -3524,14 +3529,8 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction) else base = to_expression(ops[2]); - auto *basetype = &type; - // Start traversing type hierarchy at the proper non-pointer types. - while (basetype->pointer) - { - assert(basetype->parent_type); - basetype = &get(basetype->parent_type); - } + auto *basetype = &get_non_pointer_type(type); // Traverse the type hierarchy down to the actual buffer types. for (uint32_t i = 0; i < to_plain_buffer_length; i++) @@ -3767,7 +3766,7 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i) } // clang-format off -#define GROUP_OP(op, hlsl_op, supports_scan) \ +#define HLSL_GROUP_OP(op, hlsl_op, supports_scan) \ case OpGroupNonUniform##op: \ { \ auto operation = static_cast(ops[3]); \ @@ -3787,20 +3786,20 @@ case OpGroupNonUniform##op: \ SPIRV_CROSS_THROW("Invalid group operation."); \ break; \ } - GROUP_OP(FAdd, Sum, true) - GROUP_OP(FMul, Product, true) - GROUP_OP(FMin, Min, false) - GROUP_OP(FMax, Max, false) - GROUP_OP(IAdd, Sum, true) - GROUP_OP(IMul, Product, true) - GROUP_OP(SMin, Min, false) - GROUP_OP(SMax, Max, false) - GROUP_OP(UMin, Min, false) - GROUP_OP(UMax, Max, false) - GROUP_OP(BitwiseAnd, BitAnd, false) - GROUP_OP(BitwiseOr, BitOr, false) - GROUP_OP(BitwiseXor, BitXor, false) -#undef GROUP_OP + HLSL_GROUP_OP(FAdd, Sum, true) + HLSL_GROUP_OP(FMul, Product, true) + HLSL_GROUP_OP(FMin, Min, false) + HLSL_GROUP_OP(FMax, Max, false) + HLSL_GROUP_OP(IAdd, Sum, true) + HLSL_GROUP_OP(IMul, Product, true) + HLSL_GROUP_OP(SMin, Min, false) + HLSL_GROUP_OP(SMax, Max, false) + HLSL_GROUP_OP(UMin, Min, false) + HLSL_GROUP_OP(UMax, Max, false) + HLSL_GROUP_OP(BitwiseAnd, BitAnd, false) + HLSL_GROUP_OP(BitwiseOr, BitOr, false) + HLSL_GROUP_OP(BitwiseXor, BitXor, false) +#undef HLSL_GROUP_OP // clang-format on case OpGroupNonUniformQuadSwap: @@ -3839,7 +3838,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) #define HLSL_BOP_CAST(op, type) \ emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, hlsl_opcode_is_sign_invariant(opcode)) #define HLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) -#define HLS_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) +#define HLSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) #define HLSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) #define HLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) #define HLSL_BFOP_CAST(op, type) \ @@ -4573,6 +4572,7 @@ string CompilerHLSL::compile() backend.can_declare_arrays_inline = false; backend.can_return_array = false; + build_function_control_flow_graphs_and_analyze(); update_active_builtins(); analyze_image_and_sampler_usage(); @@ -4605,3 +4605,24 @@ string CompilerHLSL::compile() return buffer->str(); } + +void CompilerHLSL::emit_block_hints(const SPIRBlock &block) +{ + switch (block.hint) + { + case SPIRBlock::HintFlatten: + statement("[flatten]"); + break; + case SPIRBlock::HintDontFlatten: + statement("[branch]"); + break; + case SPIRBlock::HintUnroll: + statement("[unroll]"); + break; + case SPIRBlock::HintDontUnroll: + statement("[loop]"); + break; + default: + break; + } +} diff --git a/deps/SPIRV-Cross/spirv_hlsl.hpp b/deps/SPIRV-Cross/spirv_hlsl.hpp index df330d0588..bcc82b1043 100644 --- a/deps/SPIRV-Cross/spirv_hlsl.hpp +++ b/deps/SPIRV-Cross/spirv_hlsl.hpp @@ -118,9 +118,9 @@ public: private: std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; - std::string image_type_hlsl(const SPIRType &type); - std::string image_type_hlsl_modern(const SPIRType &type); - std::string image_type_hlsl_legacy(const SPIRType &type); + std::string image_type_hlsl(const SPIRType &type, uint32_t id); + std::string image_type_hlsl_modern(const SPIRType &type, uint32_t id); + std::string image_type_hlsl_legacy(const SPIRType &type, uint32_t id); void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; void emit_hlsl_entry_point(); void emit_header() override; @@ -158,6 +158,7 @@ private: void emit_store(const Instruction &instruction); void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op); void emit_subgroup_op(const Instruction &i) override; + void emit_block_hints(const SPIRBlock &block) override; void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, const std::string &qualifier, uint32_t base_offset = 0) override; diff --git a/deps/SPIRV-Cross/spirv_msl.cpp b/deps/SPIRV-Cross/spirv_msl.cpp index fe7a345d16..3f8de118b1 100644 --- a/deps/SPIRV-Cross/spirv_msl.cpp +++ b/deps/SPIRV-Cross/spirv_msl.cpp @@ -280,6 +280,7 @@ string CompilerMSL::compile() struct_member_padding.clear(); + build_function_control_flow_graphs_and_analyze(); update_active_builtins(); analyze_image_and_sampler_usage(); build_implicit_builtins(); @@ -534,19 +535,44 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: // Add the global variables as arguments to the function if (func_id != entry_point) { - uint32_t next_id = increase_bound_by(uint32_t(added_arg_ids.size())); for (uint32_t arg_id : added_arg_ids) { - auto var = get(arg_id); + auto &var = get(arg_id); uint32_t type_id = var.basetype; - func.add_parameter(type_id, next_id, true); - set(next_id, type_id, StorageClassFunction, 0, arg_id); + auto *p_type = &get(type_id); - // Ensure the existing variable has a valid name and the new variable has all the same meta info - set_name(arg_id, ensure_valid_name(to_name(arg_id), "v")); - meta[next_id] = meta[arg_id]; + if (is_builtin_variable(var) && p_type->basetype == SPIRType::Struct) + { + // Get the non-pointer type + type_id = get_non_pointer_type_id(type_id); + p_type = &get(type_id); - next_id++; + uint32_t mbr_idx = 0; + for (auto &mbr_type_id : p_type->member_types) + { + BuiltIn builtin; + bool is_builtin = is_member_builtin(*p_type, mbr_idx, &builtin); + if (is_builtin && has_active_builtin(builtin, var.storage)) + { + // Add a arg variable with the same type and decorations as the member + uint32_t next_id = increase_bound_by(1); + func.add_parameter(mbr_type_id, next_id, true); + set(next_id, mbr_type_id, StorageClassFunction); + meta[next_id].decoration = meta[type_id].members[mbr_idx]; + } + mbr_idx++; + } + } + else + { + uint32_t next_id = increase_bound_by(1); + func.add_parameter(type_id, next_id, true); + set(next_id, type_id, StorageClassFunction, 0, arg_id); + + // Ensure the existing variable has a valid name and the new variable has all the same meta info + set_name(arg_id, ensure_valid_name(to_name(arg_id), "v")); + meta[next_id] = meta[arg_id]; + } } } } @@ -1148,6 +1174,18 @@ void CompilerMSL::emit_custom_functions() statement(""); break; + case SPVFuncImplTexelBufferCoords: + { + string tex_width_str = convert_to_string(msl_options.texel_buffer_texture_width); + statement("// Returns 2D texture coords corresponding to 1D texel buffer coords"); + statement("uint2 spvTexelBufferCoord(uint tc)"); + begin_scope(); + statement(join("return uint2(tc % ", tex_width_str, ", tc / ", tex_width_str, ");")); + end_scope(); + statement(""); + break; + } + case SPVFuncImplInverse4x4: statement("// Returns the determinant of a 2x2 matrix."); statement("inline float spvDet2x2(float a1, float a2, float b1, float b2)"); @@ -1505,23 +1543,17 @@ void CompilerMSL::emit_specialization_constants() // Override for MSL-specific syntax instructions void CompilerMSL::emit_instruction(const Instruction &instruction) { -#undef BOP -#undef BOP_CAST -#undef UOP -#undef QFOP -#undef TFOP -#undef BFOP -#undef BFOP_CAST -#define BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) -#define BOP_CAST(op, type) \ + +#define MSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) +#define MSL_BOP_CAST(op, type) \ emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode)) -#define UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) -#define QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) -#define TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) -#define BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) -#define BFOP_CAST(op, type) \ +#define MSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) +#define MSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) +#define MSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) +#define MSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define MSL_BFOP_CAST(op, type) \ emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode)) -#define UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) +#define MSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) auto ops = stream(instruction); auto opcode = static_cast(instruction.op); @@ -1533,81 +1565,81 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) case OpIEqual: case OpLogicalEqual: case OpFOrdEqual: - BOP(==); + MSL_BOP(==); break; case OpINotEqual: case OpLogicalNotEqual: case OpFOrdNotEqual: - BOP(!=); + MSL_BOP(!=); break; case OpUGreaterThan: case OpSGreaterThan: case OpFOrdGreaterThan: - BOP(>); + MSL_BOP(>); break; case OpUGreaterThanEqual: case OpSGreaterThanEqual: case OpFOrdGreaterThanEqual: - BOP(>=); + MSL_BOP(>=); break; case OpULessThan: case OpSLessThan: case OpFOrdLessThan: - BOP(<); + MSL_BOP(<); break; case OpULessThanEqual: case OpSLessThanEqual: case OpFOrdLessThanEqual: - BOP(<=); + MSL_BOP(<=); break; // Derivatives case OpDPdx: case OpDPdxFine: case OpDPdxCoarse: - UFOP(dfdx); + MSL_UFOP(dfdx); register_control_dependent_expression(ops[1]); break; case OpDPdy: case OpDPdyFine: case OpDPdyCoarse: - UFOP(dfdy); + MSL_UFOP(dfdy); register_control_dependent_expression(ops[1]); break; case OpFwidth: case OpFwidthCoarse: case OpFwidthFine: - UFOP(fwidth); + MSL_UFOP(fwidth); register_control_dependent_expression(ops[1]); break; // Bitfield case OpBitFieldInsert: - QFOP(insert_bits); + MSL_QFOP(insert_bits); break; case OpBitFieldSExtract: case OpBitFieldUExtract: - TFOP(extract_bits); + MSL_TFOP(extract_bits); break; case OpBitReverse: - UFOP(reverse_bits); + MSL_UFOP(reverse_bits); break; case OpBitCount: - UFOP(popcount); + MSL_UFOP(popcount); break; case OpFRem: - BFOP(fmod); + MSL_BFOP(fmod); break; // Atomics @@ -1660,7 +1692,7 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) break; } -#define AFMOImpl(op, valsrc) \ +#define MSL_AFMO_IMPL(op, valsrc) \ do \ { \ uint32_t result_type = ops[0]; \ @@ -1671,45 +1703,45 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) emit_atomic_func_op(result_type, id, "atomic_fetch_" #op "_explicit", mem_sem, mem_sem, false, ptr, val); \ } while (false) -#define AFMO(op) AFMOImpl(op, ops[5]) -#define AFMIO(op) AFMOImpl(op, 1) +#define MSL_AFMO(op) MSL_AFMO_IMPL(op, ops[5]) +#define MSL_AFMIO(op) MSL_AFMO_IMPL(op, 1) case OpAtomicIIncrement: - AFMIO(add); + MSL_AFMIO(add); break; case OpAtomicIDecrement: - AFMIO(sub); + MSL_AFMIO(sub); break; case OpAtomicIAdd: - AFMO(add); + MSL_AFMO(add); break; case OpAtomicISub: - AFMO(sub); + MSL_AFMO(sub); break; case OpAtomicSMin: case OpAtomicUMin: - AFMO(min); + MSL_AFMO(min); break; case OpAtomicSMax: case OpAtomicUMax: - AFMO(max); + MSL_AFMO(max); break; case OpAtomicAnd: - AFMO(and); + MSL_AFMO(and); break; case OpAtomicOr: - AFMO(or); + MSL_AFMO(or); break; case OpAtomicXor: - AFMO (xor); + MSL_AFMO (xor); break; // Images @@ -1833,7 +1865,7 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) break; } -#define ImgQry(qrytype) \ +#define MSL_ImgQry(qrytype) \ do \ { \ uint32_t rslt_type_id = ops[0]; \ @@ -1846,11 +1878,11 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) } while (false) case OpImageQueryLevels: - ImgQry(mip_levels); + MSL_ImgQry(mip_levels); break; case OpImageQuerySamples: - ImgQry(samples); + MSL_ImgQry(samples); break; // Casting @@ -1932,7 +1964,7 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) e->need_transpose = true; } else - BOP(*); + MSL_BOP(*); break; } @@ -2078,6 +2110,11 @@ bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs) return false; auto *var = maybe_get(id_lhs); + + // Is this a remapped, static constant? Don't do anything. + if (var->remapped_variable && var->statically_assigned) + return true; + if (ids[id_rhs].get_type() == TypeConstant && var && var->deferred_declaration) { // Special case, if we end up declaring a variable when assigning the constant array, @@ -2435,8 +2472,9 @@ string CompilerMSL::to_function_args(uint32_t img, const SPIRType &imgtype, bool if (coord_type.vecsize > 1) tex_coords += ".x"; + // Metal texel buffer textures are 2D, so convert 1D coord to 2D. if (is_fetch) - tex_coords = "uint2(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ", 0)"; // Metal textures are 2D + tex_coords = "spvTexelBufferCoord(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; alt_coord = ".y"; @@ -3559,17 +3597,13 @@ std::string CompilerMSL::sampler_type(const SPIRType &type) SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of samplers."); // Arrays of samplers in MSL must be declared with a special array syntax ala C++11 std::array. - auto *parent = &type; - while (parent->pointer) - parent = &get(parent->parent_type); - parent = &get(parent->parent_type); - uint32_t array_size = type.array_size_literal.back() ? type.array.back() : get(type.array.back()).scalar(); - if (array_size == 0) SPIRV_CROSS_THROW("Unsized array of samplers is not supported in MSL."); - return join("array<", sampler_type(*parent), ", ", array_size, ">"); + + auto &parent = get(get_non_pointer_type(type).parent_type); + return join("array<", sampler_type(parent), ", ", array_size, ">"); } else return "sampler"; @@ -3592,25 +3626,20 @@ string CompilerMSL::image_type_glsl(const SPIRType &type, uint32_t id) SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of textures."); // Arrays of images in MSL must be declared with a special array syntax ala C++11 std::array. - auto *parent = &type; - while (parent->pointer) - parent = &get(parent->parent_type); - parent = &get(parent->parent_type); - uint32_t array_size = type.array_size_literal.back() ? type.array.back() : get(type.array.back()).scalar(); if (array_size == 0) SPIRV_CROSS_THROW("Unsized array of images is not supported in MSL."); - return join("array<", image_type_glsl(*parent, id), ", ", array_size, ">"); + + auto &parent = get(get_non_pointer_type(type).parent_type); + return join("array<", image_type_glsl(parent, id), ", ", array_size, ">"); } string img_type_name; // Bypass pointers because we need the real image struct auto &img_type = get(type.self).image; - bool shadow_image = comparison_images.count(id) != 0; - - if (img_type.depth || shadow_image) + if (image_is_comparison(type, id)) { switch (img_type.dim) { @@ -4041,14 +4070,15 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o auto &return_type = compiler.get(args[0]); if (!return_type.array.empty()) return SPVFuncImplArrayCopy; - else - return SPVFuncImplNone; + + break; } case OpStore: { // Get the result type of the RHS. Since this is run as a pre-processing stage, // we must extract the result type directly from the Instruction, rather than the ID. + uint32_t id_lhs = args[0]; uint32_t id_rhs = args[1]; const SPIRType *type = nullptr; @@ -4060,14 +4090,30 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o else { // Or ... an expression. - if (result_types[id_rhs] != 0) - type = &compiler.get(result_types[id_rhs]); + uint32_t tid = result_types[id_rhs]; + if (tid) + type = &compiler.get(tid); } - if (type && compiler.is_array(*type)) + auto *var = compiler.maybe_get(id_lhs); + + // Are we simply assigning to a statically assigned variable which takes a constant? + // Don't bother emitting this function. + bool static_expression_lhs = + var && var->storage == StorageClassFunction && var->statically_assigned && var->remapped_variable; + if (type && compiler.is_array(*type) && !static_expression_lhs) return SPVFuncImplArrayCopy; - else - return SPVFuncImplNone; + + break; + } + + case OpImageFetch: + { + // Retrieve the image type, and if it's a Buffer, emit a texel coordinate function + uint32_t tid = result_types[args[2]]; + if (tid && compiler.get(tid).image.dim == DimBuffer) + return SPVFuncImplTexelBufferCoords; + break; } @@ -4176,7 +4222,7 @@ CompilerMSL::MemberSorter::MemberSorter(SPIRType &t, Meta &m, SortAspect sa) meta.members.resize(max(type.member_types.size(), meta.members.size())); } -void CompilerMSL::remap_constexpr_sampler(uint32_t id, const spirv_cross::MSLConstexprSampler &sampler) +void CompilerMSL::remap_constexpr_sampler(uint32_t id, const MSLConstexprSampler &sampler) { auto &type = get(get(id).basetype); if (type.basetype != SPIRType::SampledImage && type.basetype != SPIRType::Sampler) @@ -4185,3 +4231,24 @@ void CompilerMSL::remap_constexpr_sampler(uint32_t id, const spirv_cross::MSLCon SPIRV_CROSS_THROW("Can not remap array of samplers."); constexpr_samplers[id] = sampler; } + +// MSL always declares builtins with their SPIR-V type. +void CompilerMSL::bitcast_from_builtin_load(uint32_t, std::string &, const SPIRType &) +{ +} + +void CompilerMSL::bitcast_to_builtin_store(uint32_t, std::string &, const SPIRType &) +{ +} + +std::string CompilerMSL::to_initializer_expression(const SPIRVariable &var) +{ + // We risk getting an array initializer here with MSL. If we have an array. + // FIXME: We cannot handle non-constant arrays being initialized. + // We will need to inject spvArrayCopy here somehow ... + auto &type = get(var.basetype); + if (ids[var.initializer].get_type() == TypeConstant && (!type.array.empty() || type.basetype == SPIRType::Struct)) + return constant_expression(get(var.initializer)); + else + return CompilerGLSL::to_initializer_expression(var); +} diff --git a/deps/SPIRV-Cross/spirv_msl.hpp b/deps/SPIRV-Cross/spirv_msl.hpp index 7af66c9c8a..4d412ace00 100644 --- a/deps/SPIRV-Cross/spirv_msl.hpp +++ b/deps/SPIRV-Cross/spirv_msl.hpp @@ -153,6 +153,7 @@ public: Platform platform = macOS; uint32_t msl_version = make_msl_version(1, 2); + uint32_t texel_buffer_texture_width = 4096; // Width of 2D Metal textures used as 1D texel buffers bool enable_point_size_builtin = true; bool resolve_specialized_array_lengths = true; @@ -216,6 +217,7 @@ public: SPVFuncImplFindSMsb, SPVFuncImplFindUMsb, SPVFuncImplArrayCopy, + SPVFuncImplTexelBufferCoords, SPVFuncImplInverse4x4, SPVFuncImplInverse3x3, SPVFuncImplInverse2x2, @@ -292,6 +294,7 @@ protected: uint32_t coord, uint32_t coord_components, uint32_t dref, uint32_t grad_x, uint32_t grad_y, uint32_t lod, uint32_t coffset, uint32_t offset, uint32_t bias, uint32_t comp, uint32_t sample, bool *p_forward) override; + std::string to_initializer_expression(const SPIRVariable &var) override; std::string unpack_expression_type(std::string expr_str, const SPIRType &type) override; std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; bool skip_argument(uint32_t id) const override; @@ -357,6 +360,9 @@ protected: void emit_entry_point_declarations() override; uint32_t builtin_frag_coord_id = 0; + void bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override; + void bitcast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override; + Options msl_options; std::set spv_function_implementations; std::unordered_map vtx_attrs_by_location; diff --git a/deps/SPIRV-Cross/spirv_reflect.cpp b/deps/SPIRV-Cross/spirv_reflect.cpp new file mode 100644 index 0000000000..c322d972bd --- /dev/null +++ b/deps/SPIRV-Cross/spirv_reflect.cpp @@ -0,0 +1,573 @@ +/* + * Copyright 2018 Bradley Austin Davis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_reflect.hpp" +#include "spirv_glsl.hpp" +#include + +using namespace spv; +using namespace spirv_cross; +using namespace std; + +namespace simple_json +{ +enum class Type +{ + Object, + Array, +}; + +using State = std::pair; +using Stack = std::stack; + +class Stream +{ + Stack stack; + std::ostringstream buffer; + uint32_t indent{ 0 }; + +public: + void begin_json_object(); + void end_json_object(); + void emit_json_key(const std::string &key); + void emit_json_key_value(const std::string &key, const std::string &value); + void emit_json_key_value(const std::string &key, bool value); + void emit_json_key_value(const std::string &key, uint32_t value); + void emit_json_key_value(const std::string &key, int32_t value); + void emit_json_key_value(const std::string &key, float value); + void emit_json_key_object(const std::string &key); + void emit_json_key_array(const std::string &key); + + void begin_json_array(); + void end_json_array(); + void emit_json_array_value(const std::string &value); + void emit_json_array_value(uint32_t value); + + std::string str() const + { + return buffer.str(); + } + +private: + inline void statement_indent() + { + for (uint32_t i = 0; i < indent; i++) + buffer << " "; + } + + template + inline void statement_inner(T &&t) + { + buffer << std::forward(t); + } + + template + inline void statement_inner(T &&t, Ts &&... ts) + { + buffer << std::forward(t); + statement_inner(std::forward(ts)...); + } + + template + inline void statement(Ts &&... ts) + { + statement_indent(); + statement_inner(std::forward(ts)...); + buffer << '\n'; + } + + template + void statement_no_return(Ts &&... ts) + { + statement_indent(); + statement_inner(std::forward(ts)...); + } +}; +} // namespace simple_json + +using namespace simple_json; + +// Hackery to emit JSON without using nlohmann/json C++ library (which requires a +// higher level of compiler compliance than is required by SPIRV-Cross +void Stream::begin_json_array() +{ + if (!stack.empty() && stack.top().second) + { + statement_inner(",\n"); + } + statement("["); + ++indent; + stack.emplace(Type::Array, false); +} + +void Stream::end_json_array() +{ + if (stack.empty() || stack.top().first != Type::Array) + SPIRV_CROSS_THROW("Invalid JSON state"); + if (stack.top().second) + { + statement_inner("\n"); + } + --indent; + statement_no_return("]"); + stack.pop(); + if (!stack.empty()) + { + stack.top().second = true; + } +} + +void Stream::emit_json_array_value(const std::string &value) +{ + if (stack.empty() || stack.top().first != Type::Array) + SPIRV_CROSS_THROW("Invalid JSON state"); + + if (stack.top().second) + statement_inner(",\n"); + + statement_no_return("\"", value, "\""); + stack.top().second = true; +} + +void Stream::emit_json_array_value(uint32_t value) +{ + if (stack.empty() || stack.top().first != Type::Array) + SPIRV_CROSS_THROW("Invalid JSON state"); + if (stack.top().second) + statement_inner(",\n"); + statement_no_return(std::to_string(value)); + stack.top().second = true; +} + +void Stream::begin_json_object() +{ + if (!stack.empty() && stack.top().second) + { + statement_inner(",\n"); + } + statement("{"); + ++indent; + stack.emplace(Type::Object, false); +} + +void Stream::end_json_object() +{ + if (stack.empty() || stack.top().first != Type::Object) + SPIRV_CROSS_THROW("Invalid JSON state"); + if (stack.top().second) + { + statement_inner("\n"); + } + --indent; + statement_no_return("}"); + stack.pop(); + if (!stack.empty()) + { + stack.top().second = true; + } +} + +void Stream::emit_json_key(const std::string &key) +{ + if (stack.empty() || stack.top().first != Type::Object) + SPIRV_CROSS_THROW("Invalid JSON state"); + + if (stack.top().second) + statement_inner(",\n"); + statement_no_return("\"", key, "\" : "); + stack.top().second = true; +} + +void Stream::emit_json_key_value(const std::string &key, const std::string &value) +{ + emit_json_key(key); + statement_inner("\"", value, "\""); +} + +void Stream::emit_json_key_value(const std::string &key, uint32_t value) +{ + emit_json_key(key); + statement_inner(value); +} + +void Stream::emit_json_key_value(const std::string &key, int32_t value) +{ + emit_json_key(key); + statement_inner(value); +} + +void Stream::emit_json_key_value(const std::string &key, float value) +{ + emit_json_key(key); + statement_inner(value); +} + +void Stream::emit_json_key_value(const std::string &key, bool value) +{ + emit_json_key(key); + statement_inner(value ? "true" : "false"); +} + +void Stream::emit_json_key_object(const std::string &key) +{ + emit_json_key(key); + statement_inner("{\n"); + ++indent; + stack.emplace(Type::Object, false); +} + +void Stream::emit_json_key_array(const std::string &key) +{ + emit_json_key(key); + statement_inner("[\n"); + ++indent; + stack.emplace(Type::Array, false); +} + +void CompilerReflection::set_format(const std::string &format) +{ + if (format != "json") + { + SPIRV_CROSS_THROW("Unsupported format"); + } +} + +string CompilerReflection::compile() +{ + // Force a classic "C" locale, reverts when function returns + ClassicLocale classic_locale; + + // Move constructor for this type is broken on GCC 4.9 ... + json_stream = std::make_shared(); + json_stream->begin_json_object(); + emit_entry_points(); + emit_types(); + emit_resources(); + emit_specialization_constants(); + json_stream->end_json_object(); + return json_stream->str(); +} + +void CompilerReflection::emit_types() +{ + bool emitted_open_tag = false; + for (auto &id : ids) + { + auto idType = id.get_type(); + if (idType == TypeType) + { + auto &type = id.get(); + if (type.basetype == SPIRType::Struct && !type.pointer && type.array.empty()) + { + emit_type(type, emitted_open_tag); + } + } + } + + if (emitted_open_tag) + { + json_stream->end_json_object(); + } +} + +void CompilerReflection::emit_type(const SPIRType &type, bool &emitted_open_tag) +{ + auto name = type_to_glsl(type); + + if (type.type_alias != 0) + return; + + if (!emitted_open_tag) + { + json_stream->emit_json_key_object("types"); + emitted_open_tag = true; + } + json_stream->emit_json_key_object("_" + std::to_string(type.self)); + json_stream->emit_json_key_value("name", name); + json_stream->emit_json_key_array("members"); + // FIXME ideally we'd like to emit the size of a structure as a + // convenience to people parsing the reflected JSON. The problem + // is that there's no implicit size for a type. It's final size + // will be determined by the top level declaration in which it's + // included. So there might be one size for the struct if it's + // included in a std140 uniform block and another if it's included + // in a std430 uniform block. + // The solution is to include *all* potential sizes as a map of + // layout type name to integer, but that will probably require + // some additional logic being written in this class, or in the + // parent CompilerGLSL class. + auto size = type.member_types.size(); + for (uint32_t i = 0; i < size; ++i) + { + emit_type_member(type, i); + } + json_stream->end_json_array(); + json_stream->end_json_object(); +} + +void CompilerReflection::emit_type_member(const SPIRType &type, uint32_t index) +{ + auto &membertype = get(type.member_types[index]); + json_stream->begin_json_object(); + auto name = to_member_name(type, index); + // FIXME we'd like to emit the offset of each member, but such offsets are + // context dependent. See the comment above regarding structure sizes + json_stream->emit_json_key_value("name", name); + if (membertype.basetype == SPIRType::Struct) + { + json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.self)); + } + else + { + json_stream->emit_json_key_value("type", type_to_glsl(membertype)); + } + emit_type_member_qualifiers(type, index); + json_stream->end_json_object(); +} + +void CompilerReflection::emit_type_array(const SPIRType &type) +{ + if (!type.array.empty()) + { + json_stream->emit_json_key_array("array"); + // Note that we emit the zeros here as a means of identifying + // unbounded arrays. This is necessary as otherwise there would + // be no way of differentiating between float[4] and float[4][] + for (const auto &value : type.array) + json_stream->emit_json_array_value(value); + json_stream->end_json_array(); + } +} + +void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint32_t index) +{ + auto flags = combined_decoration_for_member(type, index); + if (flags.get(DecorationRowMajor)) + json_stream->emit_json_key_value("row_major", true); + + auto &membertype = get(type.member_types[index]); + emit_type_array(membertype); + auto &memb = meta[type.self].members; + if (index < memb.size()) + { + auto &dec = memb[index]; + if (dec.decoration_flags.get(DecorationLocation)) + json_stream->emit_json_key_value("location", dec.location); + if (dec.decoration_flags.get(DecorationOffset)) + json_stream->emit_json_key_value("offset", dec.offset); + } +} + +string CompilerReflection::execution_model_to_str(spv::ExecutionModel model) +{ + switch (model) + { + case spv::ExecutionModelVertex: + return "vert"; + case spv::ExecutionModelTessellationControl: + return "tesc"; + case ExecutionModelTessellationEvaluation: + return "tese"; + case ExecutionModelGeometry: + return "geom"; + case ExecutionModelFragment: + return "frag"; + case ExecutionModelGLCompute: + return "comp"; + default: + return "???"; + } +} + +// FIXME include things like the local_size dimensions, geometry output vertex count, etc +void CompilerReflection::emit_entry_points() +{ + auto entries = get_entry_points_and_stages(); + if (!entries.empty()) + { + json_stream->emit_json_key_array("entryPoints"); + for (auto &e : entries) + { + json_stream->begin_json_object(); + json_stream->emit_json_key_value("name", e.name); + json_stream->emit_json_key_value("mode", execution_model_to_str(e.execution_model)); + json_stream->end_json_object(); + } + json_stream->end_json_array(); + } +} + +void CompilerReflection::emit_resources() +{ + auto res = get_shader_resources(); + emit_resources("subpass_inputs", res.subpass_inputs); + emit_resources("inputs", res.stage_inputs); + emit_resources("outputs", res.stage_outputs); + emit_resources("textures", res.sampled_images); + emit_resources("separate_images", res.separate_images); + emit_resources("separate_samplers", res.separate_samplers); + emit_resources("images", res.storage_images); + emit_resources("ssbos", res.storage_buffers); + emit_resources("ubos", res.uniform_buffers); + emit_resources("push_constants", res.push_constant_buffers); + emit_resources("counters", res.atomic_counters); +} + +void CompilerReflection::emit_resources(const char *tag, const vector &resources) +{ + if (resources.empty()) + { + return; + } + + json_stream->emit_json_key_array(tag); + for (auto &res : resources) + { + auto &type = get_type(res.type_id); + auto typeflags = meta[type.self].decoration.decoration_flags; + auto &mask = get_decoration_bitset(res.id); + + // If we don't have a name, use the fallback for the type instead of the variable + // for SSBOs and UBOs since those are the only meaningful names to use externally. + // Push constant blocks are still accessed by name and not block name, even though they are technically Blocks. + bool is_push_constant = get_storage_class(res.id) == StorageClassPushConstant; + bool is_block = get_decoration_bitset(type.self).get(DecorationBlock) || + get_decoration_bitset(type.self).get(DecorationBufferBlock); + + uint32_t fallback_id = !is_push_constant && is_block ? res.base_type_id : res.id; + + json_stream->begin_json_object(); + + if (type.basetype == SPIRType::Struct) + { + json_stream->emit_json_key_value("type", "_" + std::to_string(res.base_type_id)); + } + else + { + json_stream->emit_json_key_value("type", type_to_glsl(type)); + } + + json_stream->emit_json_key_value("name", !res.name.empty() ? res.name : get_fallback_name(fallback_id)); + { + bool ssbo_block = type.storage == StorageClassStorageBuffer || + (type.storage == StorageClassUniform && typeflags.get(DecorationBufferBlock)); + if (ssbo_block) + { + auto buffer_flags = get_buffer_block_flags(res.id); + if (buffer_flags.get(DecorationNonReadable)) + json_stream->emit_json_key_value("writeonly", true); + if (buffer_flags.get(DecorationNonWritable)) + json_stream->emit_json_key_value("readonly", true); + if (buffer_flags.get(DecorationRestrict)) + json_stream->emit_json_key_value("restrict", true); + if (buffer_flags.get(DecorationCoherent)) + json_stream->emit_json_key_value("coherent", true); + } + } + + emit_type_array(type); + + { + bool is_sized_block = is_block && (get_storage_class(res.id) == StorageClassUniform || + get_storage_class(res.id) == StorageClassUniformConstant); + if (is_sized_block) + { + uint32_t block_size = uint32_t(get_declared_struct_size(get_type(res.base_type_id))); + json_stream->emit_json_key_value("block_size", block_size); + } + } + + if (type.storage == StorageClassPushConstant) + json_stream->emit_json_key_value("push_constant", true); + if (mask.get(DecorationLocation)) + json_stream->emit_json_key_value("location", get_decoration(res.id, DecorationLocation)); + if (mask.get(DecorationRowMajor)) + json_stream->emit_json_key_value("row_major", true); + if (mask.get(DecorationColMajor)) + json_stream->emit_json_key_value("column_major", true); + if (mask.get(DecorationIndex)) + json_stream->emit_json_key_value("index", get_decoration(res.id, DecorationIndex)); + if (type.storage != StorageClassPushConstant && mask.get(DecorationDescriptorSet)) + json_stream->emit_json_key_value("set", get_decoration(res.id, DecorationDescriptorSet)); + if (mask.get(DecorationBinding)) + json_stream->emit_json_key_value("binding", get_decoration(res.id, DecorationBinding)); + if (mask.get(DecorationInputAttachmentIndex)) + json_stream->emit_json_key_value("input_attachment_index", + get_decoration(res.id, DecorationInputAttachmentIndex)); + if (mask.get(DecorationOffset)) + json_stream->emit_json_key_value("offset", get_decoration(res.id, DecorationOffset)); + + // For images, the type itself adds a layout qualifer. + // Only emit the format for storage images. + if (type.basetype == SPIRType::Image && type.image.sampled == 2) + { + const char *fmt = format_to_glsl(type.image.format); + if (fmt != nullptr) + json_stream->emit_json_key_value("format", std::string(fmt)); + } + json_stream->end_json_object(); + } + json_stream->end_json_array(); +} + +void CompilerReflection::emit_specialization_constants() +{ + auto specialization_constants = get_specialization_constants(); + if (specialization_constants.empty()) + return; + + json_stream->emit_json_key_array("specialization_constants"); + for (const auto spec_const : specialization_constants) + { + auto &c = get(spec_const.id); + auto type = get(c.constant_type); + json_stream->begin_json_object(); + json_stream->emit_json_key_value("id", spec_const.constant_id); + json_stream->emit_json_key_value("type", type_to_glsl(type)); + switch (type.basetype) + { + case SPIRType::UInt: + json_stream->emit_json_key_value("default_value", c.scalar()); + break; + + case SPIRType::Int: + json_stream->emit_json_key_value("default_value", c.scalar_i32()); + break; + + case SPIRType::Float: + json_stream->emit_json_key_value("default_value", c.scalar_f32()); + break; + + case SPIRType::Boolean: + json_stream->emit_json_key_value("default_value", c.scalar() != 0); + break; + + default: + break; + } + json_stream->end_json_object(); + } + json_stream->end_json_array(); +} + +string CompilerReflection::to_member_name(const SPIRType &type, uint32_t index) const +{ + auto &memb = meta[type.self].members; + if (index < memb.size() && !memb[index].alias.empty()) + return memb[index].alias; + else + return join("_m", index); +} diff --git a/deps/SPIRV-Cross/spirv_reflect.hpp b/deps/SPIRV-Cross/spirv_reflect.hpp new file mode 100644 index 0000000000..e402fa8fb9 --- /dev/null +++ b/deps/SPIRV-Cross/spirv_reflect.hpp @@ -0,0 +1,72 @@ +/* + * Copyright 2018 Bradley Austin Davis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_REFLECT_HPP +#define SPIRV_CROSS_REFLECT_HPP + +#include "spirv_glsl.hpp" +#include +#include + +namespace simple_json +{ +class Stream; +} + +namespace spirv_cross +{ +class CompilerReflection : public CompilerGLSL +{ + using Parent = CompilerGLSL; + +public: + CompilerReflection(std::vector spirv_) + : Parent(move(spirv_)) + { + options.vulkan_semantics = true; + } + + CompilerReflection(const uint32_t *ir, size_t word_count) + : Parent(ir, word_count) + { + options.vulkan_semantics = true; + } + + void set_format(const std::string &format); + std::string compile() override; + +private: + static std::string execution_model_to_str(spv::ExecutionModel model); + + void emit_entry_points(); + void emit_types(); + void emit_resources(); + void emit_specialization_constants(); + + void emit_type(const SPIRType &type, bool &emitted_open_tag); + void emit_type_member(const SPIRType &type, uint32_t index); + void emit_type_member_qualifiers(const SPIRType &type, uint32_t index); + void emit_type_array(const SPIRType &type); + void emit_resources(const char *tag, const std::vector &resources); + + std::string to_member_name(const SPIRType &type, uint32_t index) const; + + std::shared_ptr json_stream; +}; + +} // namespace spirv_cross + +#endif diff --git a/deps/SPIRV-Cross/test_shaders.py b/deps/SPIRV-Cross/test_shaders.py index 3efcf923d1..40e1c2e2c3 100755 --- a/deps/SPIRV-Cross/test_shaders.py +++ b/deps/SPIRV-Cross/test_shaders.py @@ -11,8 +11,13 @@ import hashlib import shutil import argparse import codecs +import json +import multiprocessing +import errno +from functools import partial -force_no_external_validation = False +backend = 'glsl' +args = {} def remove_file(path): #print('Removing file:', path) @@ -78,7 +83,7 @@ def print_msl_compiler_version(): subprocess.check_call(['xcrun', '--sdk', 'iphoneos', 'metal', '--version']) print('...are the Metal compiler characteristics.\n') # display after so xcrun FNF is silent except OSError as e: - if (e.errno != os.errno.ENOENT): # Ignore xcrun not found error + if (e.errno != errno.ENOENT): # Ignore xcrun not found error raise def validate_shader_msl(shader, opt): @@ -90,7 +95,7 @@ def validate_shader_msl(shader, opt): subprocess.check_call(['xcrun', '--sdk', msl_os, 'metal', '-x', 'metal', '-std=osx-metal{}'.format('2.0' if msl2 else '1.2'), '-Werror', '-Wno-unused-variable', msl_path]) print('Compiled Metal shader: ' + msl_path) # display after so xcrun FNF is silent except OSError as oe: - if (oe.errno != os.errno.ENOENT): # Ignore xcrun not found error + if (oe.errno != errno.ENOENT): # Ignore xcrun not found error raise except subprocess.CalledProcessError: print('Error compiling Metal shader: ' + msl_path) @@ -140,7 +145,7 @@ def shader_to_win_path(shader): stdout_data, stderr_data = f.communicate() return stdout_data.decode('utf-8') except OSError as oe: - if (oe.errno != os.errno.ENOENT): # Ignore not found errors + if (oe.errno != errno.ENOENT): # Ignore not found errors return shader except subprocess.CalledProcessError: raise @@ -152,12 +157,12 @@ def validate_shader_hlsl(shader): subprocess.check_call(['glslangValidator', '-e', 'main', '-D', '--target-env', 'vulkan1.1', '-V', shader]) is_no_fxc = '.nofxc.' in shader global ignore_fxc - if (not ignore_fxc) and (not force_no_external_validation) and (not is_no_fxc): + if (not ignore_fxc) and (not args.force_no_external_validation) and (not is_no_fxc): try: win_path = shader_to_win_path(shader) subprocess.check_call(['fxc', '-nologo', shader_model_hlsl(shader), win_path]) except OSError as oe: - if (oe.errno != os.errno.ENOENT): # Ignore not found errors + if (oe.errno != errno.ENOENT): # Ignore not found errors raise else: ignore_fxc = True @@ -199,6 +204,24 @@ def cross_compile_hlsl(shader, spirv, opt): return (spirv_path, hlsl_path) +def cross_compile_reflect(shader, spirv, opt): + spirv_path = create_temporary() + reflect_path = create_temporary(os.path.basename(shader)) + + if spirv: + subprocess.check_call(['spirv-as', '-o', spirv_path, shader]) + else: + subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) + + if opt: + subprocess.check_call(['spirv-opt', '-O', '-o', spirv_path, spirv_path]) + + spirv_cross_path = './spirv-cross' + + sm = shader_to_sm(shader) + subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', reflect_path, spirv_path, '--reflect']) + return (spirv_path, reflect_path) + def validate_shader(shader, vulkan): if vulkan: subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', shader]) @@ -277,6 +300,58 @@ def reference_path(directory, relpath, opt): reference_dir = os.path.join(reference_dir, split_paths[1]) return os.path.join(reference_dir, relpath) +def json_ordered(obj): + if isinstance(obj, dict): + return sorted((k, json_ordered(v)) for k, v in obj.items()) + if isinstance(obj, list): + return sorted(json_ordered(x) for x in obj) + else: + return obj + +def json_compare(json_a, json_b): + return json_ordered(json_a) == json_ordered(json_b) + +def regression_check_reflect(shader, json_file, update, keep, opt): + reference = reference_path(shader[0], shader[1], opt) + '.json' + joined_path = os.path.join(shader[0], shader[1]) + print('Reference shader reflection path:', reference) + if os.path.exists(reference): + actual = '' + expected = '' + with open(json_file) as f: + actual_json = f.read(); + actual = json.loads(actual_json) + with open(reference) as f: + expected = json.load(f) + if (json_compare(actual, expected) != True): + if update: + print('Generated reflection json has changed for {}!'.format(reference)) + # If we expect changes, update the reference file. + if os.path.exists(reference): + remove_file(reference) + make_reference_dir(reference) + shutil.move(json_file, reference) + else: + print('Generated reflection json in {} does not match reference {}!'.format(json_file, reference)) + with open(json_file, 'r') as f: + print('') + print('Generated:') + print('======================') + print(f.read()) + print('======================') + print('') + + # Otherwise, fail the test. Keep the shader file around so we can inspect. + if not keep: + remove_file(json_file) + sys.exit(1) + else: + remove_file(json_file) + else: + print('Found new shader {}. Placing generated source code in {}'.format(joined_path, reference)) + make_reference_dir(reference) + shutil.move(json_file, reference) + def regression_check(shader, glsl, update, keep, opt): reference = reference_path(shader[0], shader[1], opt) joined_path = os.path.join(shader[0], shader[1]) @@ -396,7 +471,7 @@ def test_shader_msl(stats, shader, update, keep, opt): # executable from Xcode using args: `--msl --entry main --output msl_path spirv_path`. # print('SPRIV shader: ' + spirv) - if not force_no_external_validation: + if not args.force_no_external_validation: validate_shader_msl(shader, opt) remove_file(spirv) @@ -410,26 +485,50 @@ def test_shader_hlsl(stats, shader, update, keep, opt): regression_check(shader, hlsl, update, keep, opt) remove_file(spirv) -def test_shaders_helper(stats, shader_dir, update, malisc, keep, opt, backend): - for root, dirs, files in os.walk(os.path.join(shader_dir)): +def test_shader_reflect(stats, shader, update, keep, opt): + joined_path = os.path.join(shader[0], shader[1]) + print('Testing shader reflection:', joined_path) + is_spirv = shader_is_spirv(shader[1]) + noopt = shader_is_noopt(shader[1]) + spirv, reflect = cross_compile_reflect(joined_path, is_spirv, opt and (not noopt)) + regression_check_reflect(shader, reflect, update, keep, opt) + remove_file(spirv) + +def test_shader_file(relpath, stats, shader_dir, update, keep, opt, backend): + if backend == 'msl': + test_shader_msl(stats, (shader_dir, relpath), update, keep, opt) + elif backend == 'hlsl': + test_shader_hlsl(stats, (shader_dir, relpath), update, keep, opt) + elif backend == 'reflect': + test_shader_reflect(stats, (shader_dir, relpath), update, keep, opt) + else: + test_shader(stats, (shader_dir, relpath), update, keep, opt) + +def test_shaders_helper(stats): + all_files = [] + for root, dirs, files in os.walk(os.path.join(args.folder)): files = [ f for f in files if not f.startswith(".") ] #ignore system files (esp OSX) for i in files: path = os.path.join(root, i) - relpath = os.path.relpath(path, shader_dir) - if backend == 'msl': - test_shader_msl(stats, (shader_dir, relpath), update, keep, opt) - elif backend == 'hlsl': - test_shader_hlsl(stats, (shader_dir, relpath), update, keep, opt) - else: - test_shader(stats, (shader_dir, relpath), update, keep, opt) + relpath = os.path.relpath(path, args.folder) + all_files.append(relpath) -def test_shaders(shader_dir, update, malisc, keep, opt, backend): - if malisc: + # The child processes in parallel execution mode don't have the proper state for the global args variable, so + # at this point we need to switch to explicit arguments + if args.parallel: + pool = multiprocessing.Pool(multiprocessing.cpu_count()) + pool.map(partial(test_shader_file, stats=stats, shader_dir=args.folder, update=args.update, keep=args.keep, opt=args.opt, backend=backend), all_files) + else: + for i in all_files: + test_shader_file(i, stats, args.folder, args.update, args.keep, args.opt, backend) + +def test_shaders(): + if args.malisc: with open('stats.csv', 'w') as stats: print('Shader,OrigRegs,OrigUniRegs,OrigALUShort,OrigLSShort,OrigTEXShort,OrigALULong,OrigLSLong,OrigTEXLong,CrossRegs,CrossUniRegs,CrossALUShort,CrossLSShort,CrossTEXShort,CrossALULong,CrossLSLong,CrossTEXLong', file = stats) - test_shaders_helper(stats, shader_dir, update, malisc, keep, backend) + test_shaders_helper(stats) else: - test_shaders_helper(None, shader_dir, update, malisc, keep, opt, backend) + test_shaders_helper(None) def main(): parser = argparse.ArgumentParser(description = 'Script for regression testing.') @@ -459,19 +558,35 @@ def main(): parser.add_argument('--opt', action = 'store_true', help = 'Run SPIRV-Tools optimization passes as well.') + parser.add_argument('--reflect', + action = 'store_true', + help = 'Test reflection backend.') + parser.add_argument('--parallel', + action = 'store_true', + help = 'Execute tests in parallel. Useful for doing regression quickly, but bad for debugging and stat output.') + + global args args = parser.parse_args() - if not args.folder: sys.stderr.write('Need shader folder.\n') sys.exit(1) + if (args.parallel and (args.malisc or args.force_no_external_validation or args.update)): + sys.stderr.write('Parallel execution is disabled when using the flags --update, --malisc or --force-no-external-validation\n') + args.parallel = False + if args.msl: print_msl_compiler_version() - global force_no_external_validation - force_no_external_validation = args.force_no_external_validation + global backend + if (args.msl or args.metal): + backend = 'msl' + elif args.hlsl: + backend = 'hlsl' + elif args.reflect: + backend = 'reflect' - test_shaders(args.folder, args.update, args.malisc, args.keep, args.opt, 'msl' if (args.msl or args.metal) else ('hlsl' if args.hlsl else 'glsl')) + test_shaders() if args.malisc: print('Stats in stats.csv!') print('Tests completed!') diff --git a/deps/SPIRV-Cross/test_shaders.sh b/deps/SPIRV-Cross/test_shaders.sh index e96978a1e6..8a43afb4df 100755 --- a/deps/SPIRV-Cross/test_shaders.sh +++ b/deps/SPIRV-Cross/test_shaders.sh @@ -16,4 +16,5 @@ echo "Using spirv-opt in: $(which spirv-opt)." ./test_shaders.py shaders-hlsl --hlsl || exit 1 ./test_shaders.py shaders-hlsl --hlsl --opt || exit 1 ./test_shaders.py shaders-hlsl-no-opt --hlsl || exit 1 +./test_shaders.py shaders-reflection --reflect || exit 1 diff --git a/deps/SPIRV-Cross/update_test_shaders.sh b/deps/SPIRV-Cross/update_test_shaders.sh index 4bc87a1564..1582c6c3f3 100755 --- a/deps/SPIRV-Cross/update_test_shaders.sh +++ b/deps/SPIRV-Cross/update_test_shaders.sh @@ -16,5 +16,6 @@ echo "Using spirv-opt in: $(which spirv-opt)." ./test_shaders.py shaders-hlsl --update --hlsl || exit 1 ./test_shaders.py shaders-hlsl --update --hlsl --opt || exit 1 ./test_shaders.py shaders-hlsl-no-opt --update --hlsl || exit 1 +./test_shaders.py shaders-reflection --reflect --update || exit 1 diff --git a/file_path_special.c b/file_path_special.c index 85313af6b4..fa6c3690a8 100644 --- a/file_path_special.c +++ b/file_path_special.c @@ -130,6 +130,10 @@ bool fill_pathname_application_data(char *s, size_t len) const char* xmb_theme_ident(void); #endif +#ifdef HAVE_STRIPES +const char* stripes_theme_ident(void); +#endif + void fill_pathname_application_special(char *s, size_t len, enum application_special_type type) { diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index b410d8eff8..497da8e44d 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -8,23 +8,61 @@ #import #import +#import "RendererCommon.h" -NS_ASSUME_NONNULL_BEGIN +@interface Texture : NSObject +@property (nonatomic, readonly) id texture; +@property (nonatomic, readonly) id sampler; +@end +typedef struct +{ + void *data; + NSUInteger offset; + __unsafe_unretained id buffer; +} BufferRange; + +/*! @brief Context contains the render state used by various components */ @interface Context : NSObject -@property (readonly) id device; -@property (readonly) id library; -@property (readonly) id commandQueue; -/*! @brief Returns the command buffer for the current frame */ -@property (readonly) id commandBuffer; -@property (readonly) id nextDrawable; -@property (readonly) id renderTexture; +@property (nonatomic, readonly) id device; +@property (nonatomic, readonly) id library; +@property (nonatomic, readwrite) MTLClearColor clearColor; +@property (nonatomic, readwrite) video_viewport_t *viewport; +@property (nonatomic, readonly) Uniforms *uniforms; -+ (instancetype)newContextWithDevice:(id)d - layer:(CAMetalLayer *)layer - library:(id)l - commandQueue:(id)q; +/*! @brief Specifies whether rendering is synchronized with the display */ +@property (nonatomic, readwrite) bool displaySyncEnabled; + +/*! @brief Returns the command buffer used for pre-render work, + * such as mip maps and shader effects + * */ +@property (nonatomic, readonly) id blitCommandBuffer; + +/*! @brief Returns the command buffer for the current frame */ +@property (nonatomic, readonly) id commandBuffer; +@property (nonatomic, readonly) id nextDrawable; + +/*! @brief Main render encoder to back buffer */ +@property (nonatomic, readonly) id rce; + +- (instancetype)initWithDevice:(id)d + layer:(CAMetalLayer *)layer + library:(id)l; + +- (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter; +- (id)newTexture:(struct texture_image)image mipmapped:(bool)mipmapped; +- (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst; +- (id)getStockShader:(int)index blend:(bool)blend; + +/*! @brief resets the viewport for the main render encoder to the drawable size */ +- (void)resetRenderViewport; + +/*! @brief draws a quad at the specified position (normalized coordinates) using the main render encoder */ +- (void)drawQuadX:(float)x y:(float)y w:(float)w h:(float)h + r:(float)r g:(float)g b:(float)b a:(float)a; + +- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length; /*! @brief begin marks the beginning of a frame */ - (void)begin; @@ -33,5 +71,3 @@ NS_ASSUME_NONNULL_BEGIN - (void)end; @end - -NS_ASSUME_NONNULL_END diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index 14df7a7217..f610320c24 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -7,55 +7,690 @@ // #import "Context.h" +#import "Filter.h" #import -@interface Context() -{ - CAMetalLayer *_layer; - id _drawable; -} +@interface BufferNode : NSObject +@property (nonatomic, readonly) id src; +@property (nonatomic, readwrite) NSUInteger allocated; +@property (nonatomic, readwrite) BufferNode *next; +@end +@interface BufferChain : NSObject +- (instancetype)initWithDevice:(id)device blockLen:(NSUInteger)blockLen; +- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length; +- (void)commitRanges; +- (void)discard; +@end + +@interface Texture() +@property (nonatomic, readwrite) id texture; +@property (nonatomic, readwrite) id sampler; +@end + +@interface Context() +- (bool)_initConversionFilters; @end @implementation Context - -+ (instancetype)newContextWithDevice:(id)d - layer:(CAMetalLayer *)layer - library:(id)l - commandQueue:(id)q { - Context *c = [Context new]; - c->_device = d; - c->_layer = layer; - c->_library = l; - c->_commandQueue = q; - - return c; + dispatch_semaphore_t _inflightSemaphore; + id _commandQueue; + CAMetalLayer *_layer; + id _drawable; + video_viewport_t _viewport; + id _samplers[TEXTURE_FILTER_MIPMAP_NEAREST + 1]; + Filter *_filters[RPixelFormatCount]; // convert to bgra8888 + + // main render pass state + id _rce; + + id _blitCommandBuffer; + + NSUInteger _currentChain; + BufferChain *_chain[CHAIN_LENGTH]; + MTLClearColor _clearColor; + + id _states[GFX_MAX_SHADERS][2]; + id _clearState; + Uniforms _uniforms; } -- (id)nextDrawable { - if (_drawable == nil) { +- (instancetype)initWithDevice:(id)d + layer:(CAMetalLayer *)layer + library:(id)l +{ + if (self = [super init]) + { + _inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT); + _device = d; + _layer = layer; + _layer.displaySyncEnabled = YES; + _library = l; + _commandQueue = [_device newCommandQueue]; + _clearColor = MTLClearColorMake(0, 0, 0, 1); + _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); + + { + MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; + + sd.label = @"NEAREST"; + _samplers[TEXTURE_FILTER_NEAREST] = [d newSamplerStateWithDescriptor:sd]; + + sd.mipFilter = MTLSamplerMipFilterNearest; + sd.label = @"MIPMAP_NEAREST"; + _samplers[TEXTURE_FILTER_MIPMAP_NEAREST] = [d newSamplerStateWithDescriptor:sd]; + + sd.mipFilter = MTLSamplerMipFilterNotMipmapped; + sd.minFilter = MTLSamplerMinMagFilterLinear; + sd.magFilter = MTLSamplerMinMagFilterLinear; + sd.label = @"LINEAR"; + _samplers[TEXTURE_FILTER_LINEAR] = [d newSamplerStateWithDescriptor:sd]; + + sd.mipFilter = MTLSamplerMipFilterLinear; + sd.label = @"MIPMAP_LINEAR"; + _samplers[TEXTURE_FILTER_MIPMAP_LINEAR] = [d newSamplerStateWithDescriptor:sd]; + } + + if (![self _initConversionFilters]) + return nil; + + if (![self _initClearState]) + return nil; + + if (![self _initMenuStates]) + return nil; + + for (int i = 0; i < CHAIN_LENGTH; i++) + { + _chain[i] = [[BufferChain alloc] initWithDevice:_device blockLen:65536]; + } + } + return self; +} + +- (video_viewport_t *)viewport +{ + return &_viewport; +} + +- (void)setViewport:(video_viewport_t *)viewport +{ + _viewport = *viewport; + _uniforms.outputSize = simd_make_float2(_viewport.full_width, _viewport.full_height); +} + +- (Uniforms *)uniforms +{ + return &_uniforms; +} + +- (void)setDisplaySyncEnabled:(bool)displaySyncEnabled +{ + _layer.displaySyncEnabled = displaySyncEnabled; +} + +#pragma mark - shaders + +- (id)getStockShader:(int)index blend:(bool)blend +{ + assert(index > 0 && index < GFX_MAX_SHADERS); + + switch (index) + { + case VIDEO_SHADER_STOCK_BLEND: + case VIDEO_SHADER_MENU: + case VIDEO_SHADER_MENU_2: + case VIDEO_SHADER_MENU_3: + case VIDEO_SHADER_MENU_4: + case VIDEO_SHADER_MENU_5: + case VIDEO_SHADER_MENU_6: + break; + default: + index = VIDEO_SHADER_STOCK_BLEND; + break; + } + + return _states[index][blend ? 1 : 0]; +} + +- (bool)displaySyncEnabled +{ + return _layer.displaySyncEnabled; +} + +- (MTLVertexDescriptor *)_spriteVertexDescriptor +{ + MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; + vd.attributes[0].offset = 0; + vd.attributes[0].format = MTLVertexFormatFloat2; + vd.attributes[1].offset = offsetof(SpriteVertex, texCoord); + vd.attributes[1].format = MTLVertexFormatFloat2; + vd.attributes[2].offset = offsetof(SpriteVertex, color); + vd.attributes[2].format = MTLVertexFormatFloat4; + vd.layouts[0].stride = sizeof(SpriteVertex); + return vd; +} + +- (bool)_initClearState +{ + MTLVertexDescriptor *vd = [self _spriteVertexDescriptor]; + MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; + psd.label = @"clear_state"; + + MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; + ca.pixelFormat = _layer.pixelFormat; + + psd.vertexDescriptor = vd; + psd.vertexFunction = [_library newFunctionWithName:@"stock_vertex"]; + psd.fragmentFunction = [_library newFunctionWithName:@"stock_fragment_color"]; + + NSError *err; + _clearState = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating clear pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + return YES; +} + +- (bool)_initMenuStates +{ + MTLVertexDescriptor *vd = [self _spriteVertexDescriptor]; + MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; + psd.label = @"stock"; + + MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; + ca.pixelFormat = _layer.pixelFormat; + ca.blendingEnabled = NO; + ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + + psd.sampleCount = 1; + psd.vertexDescriptor = vd; + psd.vertexFunction = [_library newFunctionWithName:@"stock_vertex"]; + psd.fragmentFunction = [_library newFunctionWithName:@"stock_fragment"]; + + NSError *err; + _states[VIDEO_SHADER_STOCK_BLEND][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"stock_blend"; + ca.blendingEnabled = YES; + _states[VIDEO_SHADER_STOCK_BLEND][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + MTLFunctionConstantValues *vals; + + psd.label = @"snow_simple"; + ca.blendingEnabled = YES; + { + vals = [MTLFunctionConstantValues new]; + float values[3] = { + 1.25f, // baseScale + 0.50f, // density + 0.15f, // speed + }; + [vals setConstantValue:&values[0] type:MTLDataTypeFloat withName:@"snowBaseScale"]; + [vals setConstantValue:&values[1] type:MTLDataTypeFloat withName:@"snowDensity"]; + [vals setConstantValue:&values[2] type:MTLDataTypeFloat withName:@"snowSpeed"]; + } + psd.fragmentFunction = [_library newFunctionWithName:@"snow_fragment" constantValues:vals error:&err]; + _states[VIDEO_SHADER_MENU_3][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"snow"; + ca.blendingEnabled = YES; + { + vals = [MTLFunctionConstantValues new]; + float values[3] = { + 3.50f, // baseScale + 0.70f, // density + 0.25f, // speed + }; + [vals setConstantValue:&values[0] type:MTLDataTypeFloat withName:@"snowBaseScale"]; + [vals setConstantValue:&values[1] type:MTLDataTypeFloat withName:@"snowDensity"]; + [vals setConstantValue:&values[2] type:MTLDataTypeFloat withName:@"snowSpeed"]; + } + psd.fragmentFunction = [_library newFunctionWithName:@"snow_fragment" constantValues:vals error:&err]; + _states[VIDEO_SHADER_MENU_4][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"bokeh"; + ca.blendingEnabled = YES; + psd.fragmentFunction = [_library newFunctionWithName:@"bokeh_fragment"]; + _states[VIDEO_SHADER_MENU_5][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"snowflake"; + ca.blendingEnabled = YES; + psd.fragmentFunction = [_library newFunctionWithName:@"snowflake_fragment"]; + _states[VIDEO_SHADER_MENU_6][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"ribbon"; + ca.blendingEnabled = NO; + psd.vertexFunction = [_library newFunctionWithName:@"ribbon_vertex"]; + psd.fragmentFunction = [_library newFunctionWithName:@"ribbon_fragment"]; + _states[VIDEO_SHADER_MENU][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"ribbon_blend"; + ca.blendingEnabled = YES; + ca.sourceRGBBlendFactor = MTLBlendFactorOne; + ca.destinationRGBBlendFactor = MTLBlendFactorOne; + _states[VIDEO_SHADER_MENU][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"ribbon_simple"; + ca.blendingEnabled = NO; + psd.vertexFunction = [_library newFunctionWithName:@"ribbon_simple_vertex"]; + psd.fragmentFunction = [_library newFunctionWithName:@"ribbon_simple_fragment"]; + _states[VIDEO_SHADER_MENU_2][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"ribbon_simple_blend"; + ca.blendingEnabled = YES; + ca.sourceRGBBlendFactor = MTLBlendFactorOne; + ca.destinationRGBBlendFactor = MTLBlendFactorOne; + _states[VIDEO_SHADER_MENU_2][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + return YES; +} + + +- (bool)_initConversionFilters +{ + NSError *err = nil; + _filters[RPixelFormatBGRA4Unorm] = [Filter newFilterWithFunctionName:@"convert_bgra4444_to_bgra8888" + device:_device + library:_library + error:&err]; + if (err) + { + RARCH_LOG("[Metal]: unable to create 'convert_bgra4444_to_bgra8888' conversion filter: %s\n", + err.localizedDescription.UTF8String); + return NO; + } + + _filters[RPixelFormatB5G6R5Unorm] = [Filter newFilterWithFunctionName:@"convert_rgb565_to_bgra8888" + device:_device + library:_library + error:&err]; + if (err) + { + RARCH_LOG("[Metal]: unable to create 'convert_rgb565_to_bgra8888' conversion filter: %s\n", + err.localizedDescription.UTF8String); + return NO; + } + + return YES; +} + +- (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter +{ + assert(filter >= TEXTURE_FILTER_LINEAR && filter <= TEXTURE_FILTER_MIPMAP_NEAREST); + + if (!image.pixels && !image.width && !image.height) + { + /* Create a dummy texture instead. */ +#define T0 0xff000000u +#define T1 0xffffffffu + static const uint32_t checkerboard[] = { + T0, T1, T0, T1, T0, T1, T0, T1, + T1, T0, T1, T0, T1, T0, T1, T0, + T0, T1, T0, T1, T0, T1, T0, T1, + T1, T0, T1, T0, T1, T0, T1, T0, + T0, T1, T0, T1, T0, T1, T0, T1, + T1, T0, T1, T0, T1, T0, T1, T0, + T0, T1, T0, T1, T0, T1, T0, T1, + T1, T0, T1, T0, T1, T0, T1, T0, + }; +#undef T0 +#undef T1 + + image.pixels = (uint32_t *)checkerboard; + image.width = 8; + image.height = 8; + } + + BOOL mipmapped = filter == TEXTURE_FILTER_MIPMAP_LINEAR || filter == TEXTURE_FILTER_MIPMAP_NEAREST; + + Texture *tex = [Texture new]; + tex.texture = [self newTexture:image mipmapped:mipmapped]; + tex.sampler = _samplers[filter]; + + return tex; +} + +- (id)newTexture:(struct texture_image)image mipmapped:(bool)mipmapped +{ + MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:image.width + height:image.height + mipmapped:mipmapped]; + + id t = [_device newTextureWithDescriptor:td]; + [t replaceRegion:MTLRegionMake2D(0, 0, image.width, image.height) + mipmapLevel:0 + withBytes:image.pixels + bytesPerRow:4 * image.width]; + + if (mipmapped) + { + id cb = self.blitCommandBuffer; + id bce = [cb blitCommandEncoder]; + [bce generateMipmapsForTexture:t]; + [bce endEncoding]; + } + + return t; +} + +- (id)nextDrawable +{ + if (_drawable == nil) + { _drawable = _layer.nextDrawable; } return _drawable; } -- (id)renderTexture { - return self.nextDrawable.texture; +- (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst +{ + assert(dst.width * dst.height == src.length / RPixelFormatToBPP(fmt)); + assert(fmt >= 0 && fmt < RPixelFormatCount); + Filter *conv = _filters[fmt]; + assert(conv != nil); + [conv apply:self.blitCommandBuffer inBuf:src outTex:dst]; +} + +- (id)blitCommandBuffer +{ + if (!_blitCommandBuffer) + _blitCommandBuffer = [_commandQueue commandBuffer]; + return _blitCommandBuffer; +} + +- (void)_nextChain +{ + _currentChain = (_currentChain + 1) % CHAIN_LENGTH; + [_chain[_currentChain] discard]; } - (void)begin { assert(_commandBuffer == nil); + dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER); _commandBuffer = [_commandQueue commandBuffer]; } +- (id)rce +{ + assert(_commandBuffer != nil); + if (_rce == nil) + { + MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; + rpd.colorAttachments[0].clearColor = _clearColor; + rpd.colorAttachments[0].loadAction = MTLLoadActionClear; + rpd.colorAttachments[0].texture = self.nextDrawable.texture; + _rce = [_commandBuffer renderCommandEncoderWithDescriptor:rpd]; + } + return _rce; +} + +- (void)resetRenderViewport +{ + MTLViewport vp = { + .originX = 0, + .originY = 0, + .width = _viewport.full_width, + .height = _viewport.full_height, + .znear = 0, + .zfar = 1, + }; + [self.rce setViewport:vp]; +} + +- (void)drawQuadX:(float)x y:(float)y w:(float)w h:(float)h + r:(float)r g:(float)g b:(float)b a:(float)a +{ + SpriteVertex v[4]; + v[0].position = simd_make_float2(x, y); + v[1].position = simd_make_float2(x + w, y); + v[2].position = simd_make_float2(x, y + h); + v[3].position = simd_make_float2(x + w, y + h); + + simd_float4 color = simd_make_float4(r, g, b, a); + v[0].color = color; + v[1].color = color; + v[2].color = color; + v[3].color = color; + + id rce = self.rce; + [rce setRenderPipelineState:_clearState]; + [rce setVertexBytes:&v length:sizeof(v) atIndex:BufferIndexPositions]; + [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; + [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; +} + - (void)end { - assert(self->_commandBuffer != nil); + assert(_commandBuffer != nil); + + [_chain[_currentChain] commitRanges]; + + if (_blitCommandBuffer) + { + // pending blits for mipmaps or render passes for slang shaders + [_blitCommandBuffer commit]; + [_blitCommandBuffer waitUntilCompleted]; + _blitCommandBuffer = nil; + } + + if (_rce) + { + [_rce endEncoding]; + _rce = nil; + } + + __block dispatch_semaphore_t inflight = _inflightSemaphore; + [_commandBuffer addCompletedHandler:^(id _) { + dispatch_semaphore_signal(inflight); + }]; + + if (self.nextDrawable) + { + [_commandBuffer presentDrawable:self.nextDrawable]; + } + [_commandBuffer commit]; + _commandBuffer = nil; _drawable = nil; + [self _nextChain]; +} + +- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length +{ + return [_chain[_currentChain] allocRange:range length:length]; } +@end + +@implementation Texture +@end + +@implementation BufferNode + +- (instancetype)initWithBuffer:(id)src +{ + if (self = [super init]) + { + _src = src; + } + return self; +} + +@end + +@implementation BufferChain +{ + id _device; + NSUInteger _blockLen; + BufferNode *_head; + NSUInteger _offset; // offset into _current + BufferNode *_current; + NSUInteger _length; + NSUInteger _allocated; +} + +/* macOS requires constants in a buffer to have a 256 byte alignment. */ +#ifdef TARGET_OS_MAC +static const NSUInteger kConstantAlignment = 256; +#else +static const NSUInteger kConstantAlignment = 4; +#endif + + +- (instancetype)initWithDevice:(id)device blockLen:(NSUInteger)blockLen +{ + if (self = [super init]) + { + _device = device; + _blockLen = blockLen; + } + return self; +} + +- (NSString *)debugDescription +{ + return [NSString stringWithFormat:@"length=%ld, allocated=%ld", _length, _allocated]; +} + +- (void)commitRanges +{ + for (BufferNode *n = _head; n != nil; n = n.next) + { + if (n.allocated > 0) + { + [n.src didModifyRange:NSMakeRange(0, n.allocated)]; + } + } +} + +- (void)discard +{ + _current = _head; + _offset = 0; + _allocated = 0; +} + +- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length +{ + bzero(range, sizeof(*range)); + + if (!_head) + { + _head = [[BufferNode alloc] initWithBuffer:[_device newBufferWithLength:_blockLen options:MTLResourceStorageModeManaged]]; + _length += _blockLen; + _current = _head; + _offset = 0; + } + + if ([self _subAllocRange:range length:length]) + return YES; + + while (_current.next) + { + [self _nextNode]; + if ([self _subAllocRange:range length:length]) + return YES; + } + + NSUInteger blockLen = _blockLen; + if (length > blockLen) + { + blockLen = length; + } + + _current.next = [[BufferNode alloc] initWithBuffer:[_device newBufferWithLength:blockLen options:MTLResourceStorageModeManaged]]; + if (!_current.next) + return NO; + + _length += blockLen; + + [self _nextNode]; + retro_assert([self _subAllocRange:range length:length]); + return YES; +} + +- (void)_nextNode +{ + _current = _current.next; + _offset = 0; +} + +- (BOOL)_subAllocRange:(BufferRange *)range length:(NSUInteger)length +{ + NSUInteger nextOffset = _offset + length; + if (nextOffset <= _current.src.length) + { + _current.allocated = nextOffset; + _allocated += length; + range->data = _current.src.contents + _offset; + range->buffer = _current.src; + range->offset = _offset; + _offset = MTL_ALIGN_BUFFER(nextOffset); + return YES; + } + return NO; +} + + @end diff --git a/gfx/common/metal/Filter.h b/gfx/common/metal/Filter.h index cf185beba0..82094ad67f 100644 --- a/gfx/common/metal/Filter.h +++ b/gfx/common/metal/Filter.h @@ -10,17 +10,17 @@ #import @protocol FilterDelegate --(void)configure:(id)encoder; +- (void)configure:(id)encoder; @end @interface Filter : NSObject -@property (readwrite) id delegate; -@property (readonly) id sampler; +@property (nonatomic, readwrite) id delegate; +@property (nonatomic, readonly) id sampler; --(void)apply:(id)cb in:(id)tin out:(id)tout; --(void)apply:(id)cb inBuf:(id)tin outTex:(id)tout; +- (void)apply:(id)cb in:(id)tin out:(id)tout; +- (void)apply:(id)cb inBuf:(id)tin outTex:(id)tout; -+(instancetype)newFilterWithFunctionName:(NSString *)name device:(id)device library:(id)library error:(NSError **)error; ++ (instancetype)newFilterWithFunctionName:(NSString *)name device:(id)device library:(id)library error:(NSError **)error; @end diff --git a/gfx/common/metal/Filter.m b/gfx/common/metal/Filter.m index fa36811ae6..94490aff66 100644 --- a/gfx/common/metal/Filter.m +++ b/gfx/common/metal/Filter.m @@ -10,79 +10,87 @@ #import @interface Filter() --( instancetype)initWithKernel:(id)kernel sampler:(id)sampler; +- (instancetype)initWithKernel:(id)kernel sampler:(id)sampler; @end -@implementation Filter { - id _kernel; +@implementation Filter +{ + id _kernel; } -+(instancetype)newFilterWithFunctionName:(NSString *)name device:(id)device library:(id)library error:(NSError **)error { - id function = [library newFunctionWithName:name]; - id kernel = [device newComputePipelineStateWithFunction:function error:error]; - if (*error != nil) { - return nil; - } - - MTLSamplerDescriptor * sd = [MTLSamplerDescriptor new]; - sd.minFilter = MTLSamplerMinMagFilterNearest; - sd.magFilter = MTLSamplerMinMagFilterNearest; - sd.sAddressMode = MTLSamplerAddressModeClampToEdge; - sd.tAddressMode = MTLSamplerAddressModeClampToEdge; - sd.mipFilter = MTLSamplerMipFilterNotMipmapped; - id sampler = [device newSamplerStateWithDescriptor:sd]; - - return [[Filter alloc] initWithKernel:kernel sampler:sampler]; ++ (instancetype)newFilterWithFunctionName:(NSString *)name device:(id)device library:(id)library error:(NSError **)error +{ + id function = [library newFunctionWithName:name]; + id kernel = [device newComputePipelineStateWithFunction:function error:error]; + if (*error != nil) + { + return nil; + } + + MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; + sd.minFilter = MTLSamplerMinMagFilterNearest; + sd.magFilter = MTLSamplerMinMagFilterNearest; + sd.sAddressMode = MTLSamplerAddressModeClampToEdge; + sd.tAddressMode = MTLSamplerAddressModeClampToEdge; + sd.mipFilter = MTLSamplerMipFilterNotMipmapped; + id sampler = [device newSamplerStateWithDescriptor:sd]; + + return [[Filter alloc] initWithKernel:kernel sampler:sampler]; } --( instancetype)initWithKernel:(id)kernel sampler:(id)sampler { - if (self = [super init]) { - _kernel = kernel; - _sampler = sampler; - } - return self; +- (instancetype)initWithKernel:(id)kernel sampler:(id)sampler +{ + if (self = [super init]) + { + _kernel = kernel; + _sampler = sampler; + } + return self; } --(void)apply:(id)cb in:(id)tin out:(id)tout { - id ce = [cb computeCommandEncoder]; - ce.label = @"filter kernel"; - [ce pushDebugGroup:@"filter kernel"]; - - [ce setComputePipelineState:_kernel]; - - [ce setTexture:tin atIndex:0]; - [ce setTexture:tout atIndex:1]; - - [self.delegate configure:ce]; - - MTLSize size = MTLSizeMake(16, 16, 1); - MTLSize count = MTLSizeMake((tin.width + size.width + 1) / size.width, (tin.height + size.height + 1) / size.height, 1); - - [ce dispatchThreadgroups:count threadsPerThreadgroup:size]; - - [ce popDebugGroup]; - [ce endEncoding]; +- (void)apply:(id)cb in:(id)tin out:(id)tout +{ + id ce = [cb computeCommandEncoder]; + ce.label = @"filter kernel"; + [ce pushDebugGroup:@"filter kernel"]; + + [ce setComputePipelineState:_kernel]; + + [ce setTexture:tin atIndex:0]; + [ce setTexture:tout atIndex:1]; + + [self.delegate configure:ce]; + + MTLSize size = MTLSizeMake(16, 16, 1); + MTLSize count = MTLSizeMake((tin.width + size.width + 1) / size.width, (tin.height + size.height + 1) / size.height, + 1); + + [ce dispatchThreadgroups:count threadsPerThreadgroup:size]; + + [ce popDebugGroup]; + [ce endEncoding]; } --(void)apply:(id)cb inBuf:(id)tin outTex:(id)tout { - id ce = [cb computeCommandEncoder]; - ce.label = @"filter kernel"; - [ce pushDebugGroup:@"filter kernel"]; - - [ce setComputePipelineState:_kernel]; - - [ce setBuffer:tin offset:0 atIndex:0]; - [ce setTexture:tout atIndex:0]; - - [self.delegate configure:ce]; - - MTLSize size = MTLSizeMake(32, 1, 1); - MTLSize count = MTLSizeMake((tin.length + 00) / 32, 1, 1); - - [ce dispatchThreadgroups:count threadsPerThreadgroup:size]; - - [ce popDebugGroup]; - [ce endEncoding]; +- (void)apply:(id)cb inBuf:(id)tin outTex:(id)tout +{ + id ce = [cb computeCommandEncoder]; + ce.label = @"filter kernel"; + [ce pushDebugGroup:@"filter kernel"]; + + [ce setComputePipelineState:_kernel]; + + [ce setBuffer:tin offset:0 atIndex:0]; + [ce setTexture:tout atIndex:0]; + + [self.delegate configure:ce]; + + MTLSize size = MTLSizeMake(32, 1, 1); + MTLSize count = MTLSizeMake((tin.length + 00) / 32, 1, 1); + + [ce dispatchThreadgroups:count threadsPerThreadgroup:size]; + + [ce popDebugGroup]; + [ce endEncoding]; } @end diff --git a/gfx/common/metal/MenuDisplay.h b/gfx/common/metal/MenuDisplay.h new file mode 100644 index 0000000000..12d5bbd6fd --- /dev/null +++ b/gfx/common/metal/MenuDisplay.h @@ -0,0 +1,24 @@ +// +// Created by Stuart Carnie on 6/24/18. +// + +#import + +@class Context; + +@interface MenuDisplay : NSObject + +@property (nonatomic, readwrite) BOOL blend; +@property (nonatomic, readwrite) MTLClearColor clearColor; + +- (instancetype)initWithContext:(Context *)context; +- (void)drawPipeline:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video; +- (void)draw:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video; + +#pragma mark - static methods + ++ (const float *)defaultVertices; ++ (const float *)defaultTexCoords; ++ (const float *)defaultColor; + +@end diff --git a/gfx/common/metal/MenuDisplay.m b/gfx/common/metal/MenuDisplay.m new file mode 100644 index 0000000000..cde9edc265 --- /dev/null +++ b/gfx/common/metal/MenuDisplay.m @@ -0,0 +1,220 @@ +// +// Created by Stuart Carnie on 6/24/18. +// + +#import "Context.h" +#import "MenuDisplay.h" +#import "ShaderTypes.h" +#import "menu_driver.h" +#import +// TODO(sgc): this dependency is incorrect +#import "../metal_common.h" + +@implementation MenuDisplay +{ + Context *_context; + MTLClearColor _clearColor; + bool _clearNextRender; + Uniforms _uniforms; +} + +- (instancetype)initWithContext:(Context *)context +{ + if (self = [super init]) + { + _context = context; + _clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); + _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); + } + return self; +} + ++ (const float *)defaultVertices +{ + static float dummy[] = { + 0.0f, 0.0f, + 1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f, + }; + return &dummy[0]; +} + ++ (const float *)defaultTexCoords +{ + static float dummy[] = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + }; + return &dummy[0]; +} + ++ (const float *)defaultColor +{ + static float dummy[] = { + 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 1.0f, + }; + return &dummy[0]; +} + +- (void)setClearColor:(MTLClearColor)clearColor +{ + _clearColor = clearColor; + _clearNextRender = YES; +} + +- (MTLClearColor)clearColor +{ + return _clearColor; +} + +- (MTLPrimitiveType)_toPrimitiveType:(enum menu_display_prim_type)prim +{ + switch (prim) + { + case MENU_DISPLAY_PRIM_TRIANGLESTRIP: + return MTLPrimitiveTypeTriangleStrip; + case MENU_DISPLAY_PRIM_TRIANGLES: + return MTLPrimitiveTypeTriangle; + default: + RARCH_LOG("unexpected primitive type %d\n", prim); + return MTLPrimitiveTypeTriangle; + } +} + +- (void)drawPipeline:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video +{ + static struct video_coords blank_coords; + + draw->x = 0; + draw->y = 0; + draw->matrix_data = NULL; + + _uniforms.outputSize = simd_make_float2(_context.viewport->full_width, _context.viewport->full_height); + + draw->pipeline.backend_data = &_uniforms; + draw->pipeline.backend_data_size = sizeof(_uniforms); + + switch (draw->pipeline.id) + { + // ribbon + default: + case VIDEO_SHADER_MENU: + case VIDEO_SHADER_MENU_2: + { + video_coord_array_t *ca = menu_display_get_coords_array(); + draw->coords = (struct video_coords *)&ca->coords; + break; + } + + case VIDEO_SHADER_MENU_3: + case VIDEO_SHADER_MENU_4: + case VIDEO_SHADER_MENU_5: + case VIDEO_SHADER_MENU_6: + { + draw->coords = &blank_coords; + blank_coords.vertices = 4; + draw->prim_type = MENU_DISPLAY_PRIM_TRIANGLESTRIP; + break; + } + } + + _uniforms.time += 0.01; +} + +- (void)draw:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video +{ + const float *vertex = draw->coords->vertex ?: MenuDisplay.defaultVertices; + const float *tex_coord = draw->coords->tex_coord ?: MenuDisplay.defaultTexCoords; + const float *color = draw->coords->color ?: MenuDisplay.defaultColor; + + NSUInteger needed = draw->coords->vertices * sizeof(SpriteVertex); + BufferRange range; + if (![_context allocRange:&range length:needed]) + { + RARCH_ERR("[Metal]: MenuDisplay unable to allocate buffer of %d bytes", needed); + return; + } + + NSUInteger vertexCount = draw->coords->vertices; + SpriteVertex *pv = (SpriteVertex *)range.data; + for (unsigned i = 0; i < draw->coords->vertices; i++, pv++) + { + pv->position = simd_make_float2(vertex[0], 1.0f - vertex[1]); + vertex += 2; + + pv->texCoord = simd_make_float2(tex_coord[0], tex_coord[1]); + tex_coord += 2; + + pv->color = simd_make_float4(color[0], color[1], color[2], color[3]); + color += 4; + } + + id rce = _context.rce; + if (_clearNextRender) + { + [_context resetRenderViewport]; + [_context drawQuadX:0 + y:0 + w:1 + h:1 + r:(float)_clearColor.red + g:(float)_clearColor.green + b:(float)_clearColor.blue + a:(float)_clearColor.alpha + ]; + _clearNextRender = NO; + } + + MTLViewport vp = { + .originX = draw->x, + .originY = _context.viewport->full_height - draw->y - draw->height, + .width = draw->width, + .height = draw->height, + .znear = 0, + .zfar = 1, + }; + [rce setViewport:vp]; + + switch (draw->pipeline.id) + { +#if HAVE_SHADERPIPELINE + case VIDEO_SHADER_MENU: + case VIDEO_SHADER_MENU_2: + case VIDEO_SHADER_MENU_3: + case VIDEO_SHADER_MENU_4: + case VIDEO_SHADER_MENU_5: + case VIDEO_SHADER_MENU_6: + [rce setRenderPipelineState:[_context getStockShader:draw->pipeline.id blend:_blend]]; + [rce setVertexBytes:draw->pipeline.backend_data length:draw->pipeline.backend_data_size atIndex:BufferIndexUniforms]; + [rce setVertexBuffer:range.buffer offset:range.offset atIndex:BufferIndexPositions]; + [rce setFragmentBytes:draw->pipeline.backend_data length:draw->pipeline.backend_data_size atIndex:BufferIndexUniforms]; + [rce drawPrimitives:[self _toPrimitiveType:draw->prim_type] vertexStart:0 vertexCount:vertexCount]; + return; +#endif + default: + break; + } + + Texture *tex = (__bridge Texture *)(void *)draw->texture; + if (tex == nil) + return; + + [rce setRenderPipelineState:[_context getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; + + Uniforms uniforms = { + .projectionMatrix = draw->matrix_data ? make_matrix_float4x4((const float *)draw->matrix_data) + : _uniforms.projectionMatrix + }; + [rce setVertexBytes:&uniforms length:sizeof(uniforms) atIndex:BufferIndexUniforms]; + [rce setVertexBuffer:range.buffer offset:range.offset atIndex:BufferIndexPositions]; + [rce setFragmentTexture:tex.texture atIndex:TextureIndexColor]; + [rce setFragmentSamplerState:tex.sampler atIndex:SamplerIndexDraw]; + [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:vertexCount]; +} +@end diff --git a/gfx/common/metal/MetalRenderer.h b/gfx/common/metal/MetalRenderer.h deleted file mode 100644 index 184d7ec637..0000000000 --- a/gfx/common/metal/MetalRenderer.h +++ /dev/null @@ -1,10 +0,0 @@ -// -// MetalRenderer.h -// MetalRenderer -// -// Created by Stuart Carnie on 6/7/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import "Renderer.h" -#import "RView.h" diff --git a/gfx/common/metal/PixelConverter+private.h b/gfx/common/metal/PixelConverter+private.h deleted file mode 100644 index b72790f359..0000000000 --- a/gfx/common/metal/PixelConverter+private.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// PixelConverter+private.h -// MetalRenderer -// -// Created by Stuart Carnie on 6/9/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// -#import "PixelConverter.h" -#import "Context.h" - -@interface PixelConverter() -- (instancetype)initWithContext:(Context *)c; -@end diff --git a/gfx/common/metal/PixelConverter.h b/gfx/common/metal/PixelConverter.h deleted file mode 100644 index 7386ce83ad..0000000000 --- a/gfx/common/metal/PixelConverter.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// PixelConverter.h -// MetalRenderer -// -// Created by Stuart Carnie on 6/9/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import -#import - -#import "RendererCommon.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface PixelConverter : NSObject -- (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst; -@end - -NS_ASSUME_NONNULL_END diff --git a/gfx/common/metal/PixelConverter.m b/gfx/common/metal/PixelConverter.m deleted file mode 100644 index 24aa14e9a8..0000000000 --- a/gfx/common/metal/PixelConverter.m +++ /dev/null @@ -1,49 +0,0 @@ -// -// PixelConverter.m -// MetalRenderer -// -// Created by Stuart Carnie on 6/9/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import "PixelConverter+private.h" -#import "Filter.h" -#import "Context.h" - -@implementation PixelConverter { - Context *_context; - Filter *_filters[RPixelFormatCount]; // convert to bgra8888 -} - -- (instancetype)initWithContext:(Context *)c -{ - if (self = [super init]) - { - _context = c; - NSError *err = nil; - _filters[RPixelFormatBGRA4Unorm] = [Filter newFilterWithFunctionName:@"convert_bgra4444_to_bgra8888" - device:c.device library:c.library - error:&err]; - _filters[RPixelFormatB5G6R5Unorm] = [Filter newFilterWithFunctionName:@"convert_rgb565_to_bgra8888" - device:c.device - library:c.library - error:&err]; - if (err) - { - NSLog(@"unable to create pixel conversion filter: %@", err.localizedDescription); - abort(); - } - } - return self; -} - -- (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst -{ - assert(dst.width*dst.height == src.length/RPixelFormatToBPP(fmt)); - assert(fmt >= 0 && fmt < RPixelFormatCount); - Filter *conv = _filters[fmt]; - assert(conv != nil); - [conv apply:_context.commandBuffer inBuf:src outTex:dst]; -} - -@end diff --git a/gfx/common/metal/PixelConverter.metal b/gfx/common/metal/PixelConverter.metal deleted file mode 100644 index 97fc1ae636..0000000000 --- a/gfx/common/metal/PixelConverter.metal +++ /dev/null @@ -1,49 +0,0 @@ -// -// PixelConverter.metal -// MetalRenderer -// -// Created by Stuart Carnie on 6/9/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#include -using namespace metal; - - -#pragma mark - filter kernels - -kernel void convert_bgra4444_to_bgra8888(device uint16_t * in [[ buffer(0) ]], - texture2d out [[ texture(0) ]], - uint id [[ thread_position_in_grid ]]) -{ - uint16_t pix = in[id]; - uchar4 pix2 = uchar4( - extract_bits(pix, 4, 4), - extract_bits(pix, 8, 4), - extract_bits(pix, 12, 4), - extract_bits(pix, 0, 4) - ); - - uint ypos = id / out.get_width(); - uint xpos = id % out.get_width(); - - out.write(half4(pix2) / 15.0, uint2(xpos, ypos)); -} - -kernel void convert_rgb565_to_bgra8888(device uint16_t * in [[ buffer(0) ]], - texture2d out [[ texture(0) ]], - uint id [[ thread_position_in_grid ]]) -{ - uint16_t pix = in[id]; - uchar4 pix2 = uchar4( - extract_bits(pix, 11, 5), - extract_bits(pix, 5, 6), - extract_bits(pix, 0, 5), - 0xf - ); - - uint ypos = id / out.get_width(); - uint xpos = id % out.get_width(); - - out.write(half4(pix2) / half4(0x1f, 0x3f, 0x1f, 0xf), uint2(xpos, ypos)); -} diff --git a/gfx/common/metal/Renderer.h b/gfx/common/metal/Renderer.h deleted file mode 100644 index 5142e7f09a..0000000000 --- a/gfx/common/metal/Renderer.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Renderer.h -// MetalRenderer -// -// Created by Stuart Carnie on 5/31/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - - -#import -#import -#import "Context.h" -#import "PixelConverter.h" - -@protocol View; - -@interface Renderer : NSObject - -@property (readonly) Context* context; -@property (readonly) PixelConverter* conv; - -- (instancetype)initWithDevice:(id)device layer:(CAMetalLayer *)layer; -- (void)drawableSizeWillChange:(CGSize)size; - -- (void)beginFrame; -- (void)drawViews; -- (void)endFrame; - -#pragma mark - view management - -- (void)addView:(id)view; -- (void)removeView:(id)view; -- (void)bringViewToFront:(id)view; -- (void)sendViewToBack:(id)view; - -@end - diff --git a/gfx/common/metal/Renderer.m b/gfx/common/metal/Renderer.m deleted file mode 100644 index f7d53af9d7..0000000000 --- a/gfx/common/metal/Renderer.m +++ /dev/null @@ -1,263 +0,0 @@ -// -// Renderer.m -// MetalRenderer -// -// Created by Stuart Carnie on 5/31/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import - -#import "RendererCommon.h" -#import "Renderer.h" -#import "View.h" -#import "PixelConverter+private.h" - -// Include header shared between C code here, which executes Metal API commands, and .metal files -#import "ShaderTypes.h" - -@implementation Renderer -{ - dispatch_semaphore_t _inflightSemaphore; - id _device; - id _library; - id _commandQueue; - Context *_context; - - PixelConverter *_conv; - - CAMetalLayer *_layer; - - // render target layer state - id _t_pipelineState; - id _t_pipelineStateNoAlpha; - MTLRenderPassDescriptor *_t_rpd; - - id _samplerStateLinear; - id _samplerStateNearest; - - // views - - NSMutableArray> *_views; - - // other state - Uniforms _uniforms; - BOOL _begin, _end; -} - -- (instancetype)initWithDevice:(id)device layer:(CAMetalLayer *)layer -{ - self = [super init]; - if (self) { - _inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT); - _device = device; - _layer = layer; - _views = [NSMutableArray new]; - [self _initMetal]; - - _conv = [[PixelConverter alloc] initWithContext:_context]; - _begin = NO; - _end = NO; - } - - return self; -} - -- (void)_initMetal -{ - _commandQueue = [_device newCommandQueue]; - _library = [_device newDefaultLibrary]; - _context = [Context newContextWithDevice:_device - layer:_layer - library:_library - commandQueue:_commandQueue]; - - { - MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; - vd.attributes[0].offset = 0; - vd.attributes[0].format = MTLVertexFormatFloat3; - vd.attributes[1].offset = offsetof(Vertex, texCoord); - vd.attributes[1].format = MTLVertexFormatFloat2; - vd.layouts[0].stride = sizeof(Vertex); - vd.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex; - - MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; - psd.label = @"Pipeline+Alpha"; - - MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; - ca.pixelFormat = _layer.pixelFormat; - ca.blendingEnabled = YES; - ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; - ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; - ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; - ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; - - psd.sampleCount = 1; - psd.vertexDescriptor = vd; - psd.vertexFunction = [_library newFunctionWithName:@"basic_vertex_proj_tex"]; - psd.fragmentFunction = [_library newFunctionWithName:@"basic_fragment_proj_tex"]; - - NSError *err; - _t_pipelineState = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) { - NSLog(@"error creating pipeline state: %@", err.localizedDescription); - abort(); - } - - ca.blendingEnabled = NO; - _t_pipelineStateNoAlpha = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) { - NSLog(@"error creating pipeline state: %@", err.localizedDescription); - abort(); - } - } - - { - MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; - rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare; - rpd.colorAttachments[0].storeAction = MTLStoreActionStore; - _t_rpd = rpd; - } - - { - MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; - _samplerStateNearest = [_device newSamplerStateWithDescriptor:sd]; - - sd.minFilter = MTLSamplerMinMagFilterLinear; - sd.magFilter = MTLSamplerMinMagFilterLinear; - _samplerStateLinear = [_device newSamplerStateWithDescriptor:sd]; - } -} - -- (void)_updateUniforms -{ - //CGSize s = _layer.drawableSize; - //_uniforms.projectionMatrix = matrix_proj_ortho(0, s.width, 0, s.height); - _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); -} - -- (void)beginFrame -{ - assert(!_begin && !_end); - _begin = YES; - dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER); - [_context begin]; - [self _updateUniforms]; -} - -- (void)endFrame -{ - assert(!_begin && _end); - _end = NO; - - id cb = _context.commandBuffer; - __block dispatch_semaphore_t inflight = _inflightSemaphore; - [cb addCompletedHandler:^(id _) { - dispatch_semaphore_signal(inflight); - }]; - - [cb presentDrawable:_context.nextDrawable]; - [_context end]; -} - -- (void)drawViews -{ - @autoreleasepool { - assert(_begin && !_end); - _begin = NO; - _end = YES; - - id cb = _context.commandBuffer; - cb.label = @"renderer cb"; - - for (id v in _views) { - if (!v.visible) continue; - if ([v respondsToSelector:@selector(drawWithContext:)]) { - [v drawWithContext:_context]; - } - } - - BOOL pendingDraws = NO; - for (id v in _views) { - if (v.visible && (v.drawState & ViewDrawStateEncoder) != 0) { - pendingDraws = YES; - break; - } - } - - if (pendingDraws) { - id drawable = _context.nextDrawable; - _t_rpd.colorAttachments[0].texture = drawable.texture; - - id rce = [cb renderCommandEncoderWithDescriptor:_t_rpd]; - [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; - - for (id v in _views) { - if (!v.visible || - ![v respondsToSelector:@selector(drawWithEncoder:)] || - (v.drawState & ViewDrawStateEncoder) == 0) { - continue; - } - - // set view state - if (v.format == RPixelFormatBGRX8Unorm || v.format == RPixelFormatB5G6R5Unorm) { - [rce setRenderPipelineState:_t_pipelineStateNoAlpha]; - } - else { - [rce setRenderPipelineState:_t_pipelineState]; - } - - if (v.filter == RTextureFilterNearest) { - [rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw]; - } - else { - [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; - } - - [v drawWithEncoder:rce]; - } - - [rce endEncoding]; - } - } -} - -#pragma mark - view APIs - -- (void)bringViewToFront:(id)view -{ - NSUInteger pos = [_views indexOfObject:view]; - if (pos == NSNotFound || pos == _views.count - 1) - return; - [_views removeObjectAtIndex:pos]; - [_views addObject:view]; -} - -- (void)sendViewToBack:(id)view -{ - NSUInteger pos = [_views indexOfObject:view]; - if (pos == NSNotFound || pos == 0) - return; - [_views removeObjectAtIndex:pos]; - [_views insertObject:view atIndex:0]; -} - -- (void)addView:(id)view -{ - [_views addObject:view]; -} - -- (void)removeView:(id)view -{ - NSUInteger pos = [_views indexOfObject:view]; - if (pos == NSNotFound) - return; - [_views removeObjectAtIndex:pos]; -} - -- (void)drawableSizeWillChange:(CGSize)size -{ - _layer.drawableSize = size; -} - -@end diff --git a/gfx/common/metal/RendererCommon.h b/gfx/common/metal/RendererCommon.h index 8a443a2161..977cf7b607 100644 --- a/gfx/common/metal/RendererCommon.h +++ b/gfx/common/metal/RendererCommon.h @@ -10,14 +10,26 @@ #define RendererCommon_h #import +#import "ShaderTypes.h" // TODO(sgc): implement triple buffering /*! @brief maximum inflight frames */ #define MAX_INFLIGHT 1 +#define CHAIN_LENGTH 3 + +/* macOS requires constants in a buffer to have a 256 byte alignment. */ +#ifdef TARGET_OS_MAC +#define kMetalBufferAlignment 256 +#else +#define kMetalBufferAlignment 4 +#endif + +#define MTL_ALIGN_BUFFER(size) ((size + kMetalBufferAlignment - 1) & (~(kMetalBufferAlignment - 1))) #pragma mark - Pixel Formats -typedef NS_ENUM(NSUInteger, RPixelFormat) { +typedef NS_ENUM(NSUInteger, RPixelFormat) +{ RPixelFormatInvalid, @@ -34,7 +46,8 @@ typedef NS_ENUM(NSUInteger, RPixelFormat) { extern NSUInteger RPixelFormatToBPP(RPixelFormat format); extern NSString *NSStringFromRPixelFormat(RPixelFormat format); -typedef NS_ENUM(NSUInteger, RTextureFilter) { +typedef NS_ENUM(NSUInteger, RTextureFilter) +{ RTextureFilterNearest, RTextureFilterLinear, @@ -42,5 +55,6 @@ typedef NS_ENUM(NSUInteger, RTextureFilter) { }; extern matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bottom); +extern matrix_float4x4 make_matrix_float4x4(const float *v); #endif /* RendererCommon_h */ diff --git a/gfx/common/metal/RendererCommon.m b/gfx/common/metal/RendererCommon.m index 4491e8ea44..c3be58dc86 100644 --- a/gfx/common/metal/RendererCommon.m +++ b/gfx/common/metal/RendererCommon.m @@ -11,45 +11,59 @@ NSUInteger RPixelFormatToBPP(RPixelFormat format) { - switch (format) - { - case RPixelFormatBGRA8Unorm: - case RPixelFormatBGRX8Unorm: - return 4; - - case RPixelFormatB5G6R5Unorm: - case RPixelFormatBGRA4Unorm: - return 2; - - default: - NSLog(@"Unknown format %ld", format); - abort(); - } + switch (format) + { + case RPixelFormatBGRA8Unorm: + case RPixelFormatBGRX8Unorm: + return 4; + + case RPixelFormatB5G6R5Unorm: + case RPixelFormatBGRA4Unorm: + return 2; + + default: + RARCH_ERR("[Metal]: unknown RPixel format: %d\n", format); + return 4; + } } -static NSString * RPixelStrings[RPixelFormatCount]; +static NSString *RPixelStrings[RPixelFormatCount]; NSString *NSStringFromRPixelFormat(RPixelFormat format) { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + #define STRING(literal) RPixelStrings[literal] = @#literal - STRING(RPixelFormatInvalid); - STRING(RPixelFormatB5G6R5Unorm); - STRING(RPixelFormatBGRA4Unorm); - STRING(RPixelFormatBGRA8Unorm); - STRING(RPixelFormatBGRX8Unorm); + STRING(RPixelFormatInvalid); + STRING(RPixelFormatB5G6R5Unorm); + STRING(RPixelFormatBGRA4Unorm); + STRING(RPixelFormatBGRA8Unorm); + STRING(RPixelFormatBGRX8Unorm); #undef STRING - - }); - - if (format >= RPixelFormatCount) - { - format = 0; - } - - return RPixelStrings[format]; + + }); + + if (format >= RPixelFormatCount) + { + format = RPixelFormatInvalid; + } + + return RPixelStrings[format]; +} + +matrix_float4x4 make_matrix_float4x4(const float *v) +{ + simd_float4 P = simd_make_float4(v[0], v[1], v[2], v[3]); + v += 4; + simd_float4 Q = simd_make_float4(v[0], v[1], v[2], v[3]); + v += 4; + simd_float4 R = simd_make_float4(v[0], v[1], v[2], v[3]); + v += 4; + simd_float4 S = simd_make_float4(v[0], v[1], v[2], v[3]); + + matrix_float4x4 mat = {P, Q, R, S}; + return mat; } matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bottom) @@ -64,10 +78,10 @@ matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bott float ty = (top + bottom) / (bottom - top); float tz = near / (far - near); - vector_float4 P = {sx, 0, 0, 0}; - vector_float4 Q = {0, sy, 0, 0}; - vector_float4 R = {0, 0, sz, 0}; - vector_float4 S = {tx, ty, tz, 1}; + simd_float4 P = simd_make_float4(sx, 0, 0, 0); + simd_float4 Q = simd_make_float4(0, sy, 0, 0); + simd_float4 R = simd_make_float4(0, 0, sz, 0); + simd_float4 S = simd_make_float4(tx, ty, tz, 1); matrix_float4x4 mat = {P, Q, R, S}; return mat; diff --git a/gfx/common/metal/ShaderTypes.h b/gfx/common/metal/ShaderTypes.h index f6ffdc9eb9..42a588a1fe 100644 --- a/gfx/common/metal/ShaderTypes.h +++ b/gfx/common/metal/ShaderTypes.h @@ -13,49 +13,44 @@ #define ShaderTypes_h #ifdef __METAL_VERSION__ - #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type #define NSInteger metal::int32_t #define METAL_ATTRIBUTE(x) [[attribute(x)]] #define METAL_POSITION [[position]] - #else - #import - #define METAL_ATTRIBUTE(x) #define METAL_POSITION - #endif #include typedef NS_ENUM(NSInteger, BufferIndex) { - BufferIndexPositions = 0, - BufferIndexUniforms = 1 + BufferIndexPositions = 0, + BufferIndexUniforms = 1 }; typedef NS_ENUM(NSInteger, VertexAttribute) { - VertexAttributePosition = 0, - VertexAttributeTexcoord = 1, - VertexAttributeColor = 2, + VertexAttributePosition = 0, + VertexAttributeTexcoord = 1, + VertexAttributeColor = 2, }; typedef NS_ENUM(NSInteger, TextureIndex) { - TextureIndexColor = 0, + TextureIndexColor = 0, }; - typedef NS_ENUM(NSInteger, SamplerIndex) { - SamplerIndexDraw = 0, + SamplerIndexDraw = 0, }; -typedef struct { +typedef struct +{ vector_float3 position METAL_ATTRIBUTE(VertexAttributePosition); vector_float2 texCoord METAL_ATTRIBUTE(VertexAttributeTexcoord); } Vertex; @@ -68,16 +63,20 @@ typedef struct typedef struct { - matrix_float4x4 projectionMatrix; + matrix_float4x4 projectionMatrix; + vector_float2 outputSize; + float time; } Uniforms; -typedef struct { +typedef struct +{ vector_float2 position METAL_ATTRIBUTE(VertexAttributePosition); vector_float2 texCoord METAL_ATTRIBUTE(VertexAttributeTexcoord); vector_float4 color METAL_ATTRIBUTE(VertexAttributeColor); -} FontVertex; +} SpriteVertex; -typedef struct { +typedef struct +{ vector_float4 position METAL_POSITION; vector_float2 texCoord; vector_float4 color; diff --git a/gfx/common/metal/Shaders.metal b/gfx/common/metal/Shaders.metal index 365408ebc1..c5449b2989 100644 --- a/gfx/common/metal/Shaders.metal +++ b/gfx/common/metal/Shaders.metal @@ -36,27 +36,9 @@ fragment float4 basic_fragment_proj_tex(ColorInOut in [[stage_in]], return float4(colorSample); } -#pragma mark - functions using normalized device coordinates +#pragma mark - functions for rendering sprites -vertex ColorInOut basic_vertex_ndc_tex(const Vertex in [[ stage_in ]]) -{ - ColorInOut out; - out.position = float4(in.position, 1.0); - out.texCoord = in.texCoord; - return out; -} - -fragment float4 basic_fragment_ndc_tex(ColorInOut in [[stage_in]], - texture2d tex [[ texture(TextureIndexColor) ]], - sampler samp [[ sampler(SamplerIndexDraw) ]]) -{ - half4 colorSample = tex.sample(samp, in.texCoord.xy); - return float4(colorSample); -} - -#pragma mark - functions for rendering fonts - -vertex FontFragmentIn font_vertex(const FontVertex in [[ stage_in ]], const device Uniforms &uniforms [[ buffer(BufferIndexUniforms) ]]) +vertex FontFragmentIn sprite_vertex(const SpriteVertex in [[ stage_in ]], const device Uniforms &uniforms [[ buffer(BufferIndexUniforms) ]]) { FontFragmentIn out; out.position = uniforms.projectionMatrix * float4(in.position, 0, 1); @@ -65,10 +47,72 @@ vertex FontFragmentIn font_vertex(const FontVertex in [[ stage_in ]], const devi return out; } -fragment float4 font_fragment(FontFragmentIn in [[ stage_in ]], +fragment float4 sprite_fragment_a8(FontFragmentIn in [[ stage_in ]], texture2d tex [[ texture(TextureIndexColor) ]], sampler samp [[ sampler(SamplerIndexDraw) ]]) { half4 colorSample = tex.sample(samp, in.texCoord.xy); return float4(in.color.rgb, in.color.a * colorSample.r); } + +#pragma mark - functions for rendering sprites + +vertex FontFragmentIn stock_vertex(const SpriteVertex in [[ stage_in ]], const device Uniforms &uniforms [[ buffer(BufferIndexUniforms) ]]) +{ + FontFragmentIn out; + out.position = uniforms.projectionMatrix * float4(in.position, 0, 1); + out.texCoord = in.texCoord; + out.color = in.color; + return out; +} + +fragment float4 stock_fragment(FontFragmentIn in [[ stage_in ]], + texture2d tex [[ texture(TextureIndexColor) ]], + sampler samp [[ sampler(SamplerIndexDraw) ]]) +{ + float4 colorSample = tex.sample(samp, in.texCoord.xy); + return colorSample * in.color; +} + +fragment half4 stock_fragment_color(FontFragmentIn in [[ stage_in ]]) +{ + return half4(in.color); +} + +#pragma mark - filter kernels + +kernel void convert_bgra4444_to_bgra8888(device uint16_t * in [[ buffer(0) ]], + texture2d out [[ texture(0) ]], + uint id [[ thread_position_in_grid ]]) +{ + uint16_t pix = in[id]; + uchar4 pix2 = uchar4( + extract_bits(pix, 4, 4), + extract_bits(pix, 8, 4), + extract_bits(pix, 12, 4), + extract_bits(pix, 0, 4) + ); + + uint ypos = id / out.get_width(); + uint xpos = id % out.get_width(); + + out.write(half4(pix2) / 15.0, uint2(xpos, ypos)); +} + +kernel void convert_rgb565_to_bgra8888(device uint16_t * in [[ buffer(0) ]], + texture2d out [[ texture(0) ]], + uint id [[ thread_position_in_grid ]]) +{ + uint16_t pix = in[id]; + uchar4 pix2 = uchar4( + extract_bits(pix, 11, 5), + extract_bits(pix, 5, 6), + extract_bits(pix, 0, 5), + 0xf + ); + + uint ypos = id / out.get_width(); + uint xpos = id % out.get_width(); + + out.write(half4(pix2) / half4(0x1f, 0x3f, 0x1f, 0xf), uint2(xpos, ypos)); +} diff --git a/gfx/common/metal/TexturedView.h b/gfx/common/metal/TexturedView.h index aa5398ae4f..750dd6d7e5 100644 --- a/gfx/common/metal/TexturedView.h +++ b/gfx/common/metal/TexturedView.h @@ -4,18 +4,16 @@ #import "View.h" -@class Renderer; +@interface TexturedView : NSObject -@interface TexturedView : NSObject +@property (nonatomic, readonly) RPixelFormat format; +@property (nonatomic, readonly) RTextureFilter filter; +@property (nonatomic, readwrite) BOOL visible; +@property (nonatomic, readwrite) CGRect frame; +@property (nonatomic, readwrite) CGSize size; +@property (nonatomic, readonly) ViewDrawState drawState; -@property (readonly) RPixelFormat format; -@property (readonly) RTextureFilter filter; -@property (readwrite) BOOL visible; -@property (readwrite) CGRect frame; -@property (readwrite) CGSize size; -@property (readonly) ViewDrawState drawState; - -- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer; +- (instancetype)initWithDescriptor:(ViewDescriptor *)td context:(Context *)c; - (void)drawWithContext:(Context *)ctx; - (void)drawWithEncoder:(id)rce; diff --git a/gfx/common/metal/TexturedView.m b/gfx/common/metal/TexturedView.m index 4dcc8634cc..f89346a654 100644 --- a/gfx/common/metal/TexturedView.m +++ b/gfx/common/metal/TexturedView.m @@ -4,40 +4,38 @@ #import "TexturedView.h" #import "RendererCommon.h" -#import "Renderer.h" #import "View.h" #import "Filter.h" -#import "ShaderTypes.h" - - @implementation TexturedView { - __weak Renderer *_renderer; Context *_context; id _texture; // optimal render texture Vertex _v[4]; CGSize _size; // size of view in pixels CGRect _frame; NSUInteger _bpp; - + id _pixels; // frame buffer in _srcFmt bool _pixelsDirty; } -- (instancetype)initWithDescriptor:(ViewDescriptor *)d renderer:(Renderer *)r +- (instancetype)initWithDescriptor:(ViewDescriptor *)d context:(Context *)c { self = [super init]; - if (self) { - _renderer = r; + if (self) + { _format = d.format; _bpp = RPixelFormatToBPP(_format); _filter = d.filter; - _context = r.context; + _context = c; _visible = YES; - if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) { + if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) + { _drawState = ViewDrawStateEncoder; - } else { + } + else + { _drawState = ViewDrawStateAll; } self.size = d.size; @@ -48,12 +46,13 @@ - (void)setSize:(CGSize)size { - if (CGSizeEqualToSize(_size, size)) { + if (CGSizeEqualToSize(_size, size)) + { return; } - + _size = size; - + // create new texture { MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm @@ -63,8 +62,9 @@ td.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; _texture = [_context.device newTextureWithDescriptor:td]; } - - if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) { + + if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) + { _pixels = [_context.device newBufferWithLength:(NSUInteger)(size.width * size.height * 2) options:MTLResourceStorageModeManaged]; } @@ -77,21 +77,22 @@ - (void)setFrame:(CGRect)frame { - if (CGRectEqualToRect(_frame, frame)) { + if (CGRectEqualToRect(_frame, frame)) + { return; } - + _frame = frame; - + // update vertices CGPoint o = frame.origin; CGSize s = frame.size; - + float l = o.x; float t = o.y; float r = o.x + s.width; float b = o.y + s.height; - + Vertex v[4] = { {{l, b, 0}, {0, 1}}, {{r, b, 0}, {1, 1}}, @@ -110,15 +111,16 @@ { if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) return; - + if (!_pixelsDirty) return; - - [_renderer.conv convertFormat:_format from:_pixels to:_texture]; + + [_context convertFormat:_format from:_pixels to:_texture]; _pixelsDirty = NO; } -- (void)drawWithContext:(Context *)ctx { +- (void)drawWithContext:(Context *)ctx +{ [self _convertFormat]; } @@ -131,27 +133,32 @@ - (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch { - if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) { + if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) + { [_texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height) mipmapLevel:0 withBytes:src - bytesPerRow:(NSUInteger)(4 * _size.width)]; + bytesPerRow:(NSUInteger)(4 * pitch)]; } - else { + else + { void *dst = _pixels.contents; size_t len = (size_t)(_bpp * _size.width); assert(len <= pitch); // the length can't be larger? - - if (len < pitch) { - for (int i = 0; i < _size.height; i++) { + + if (len < pitch) + { + for (int i = 0; i < _size.height; i++) + { memcpy(dst, src, len); dst += len; src += pitch; } } - else { + else + { memcpy(dst, src, _pixels.length); } - + [_pixels didModifyRange:NSMakeRange(0, _pixels.length)]; _pixelsDirty = YES; } diff --git a/gfx/common/metal/View.h b/gfx/common/metal/View.h index 5f390cbc34..ce1485a31a 100644 --- a/gfx/common/metal/View.h +++ b/gfx/common/metal/View.h @@ -12,32 +12,17 @@ typedef NS_ENUM(NSInteger, ViewDrawState) { - ViewDrawStateNone = 0x00, + ViewDrawStateNone = 0x00, ViewDrawStateContext = 0x01, ViewDrawStateEncoder = 0x02, - ViewDrawStateAll = 0x03, + ViewDrawStateAll = 0x03, }; -@protocol View - -@property (readonly) RPixelFormat format; -@property (readonly) RTextureFilter filter; -@property (readwrite) BOOL visible; -@property (readwrite) CGRect frame; -@property (readwrite) CGSize size; -@property (readonly) ViewDrawState drawState; - -@optional -- (void)drawWithContext:(Context *)ctx; -- (void)drawWithEncoder:(id)rce; - -@end - @interface ViewDescriptor : NSObject -@property (readwrite) RPixelFormat format; -@property (readwrite) RTextureFilter filter; -@property (readwrite) CGSize size; +@property (nonatomic, readwrite) RPixelFormat format; +@property (nonatomic, readwrite) RTextureFilter filter; +@property (nonatomic, readwrite) CGSize size; - (instancetype)init; @end diff --git a/gfx/common/metal/View.m b/gfx/common/metal/View.m index 72cfeaca2c..475983bc86 100644 --- a/gfx/common/metal/View.m +++ b/gfx/common/metal/View.m @@ -6,6 +6,7 @@ // Copyright © 2018 Stuart Carnie. All rights reserved. // +#import "View.h" #import "RendererCommon.h" @implementation ViewDescriptor @@ -13,7 +14,8 @@ - (instancetype)init { self = [super init]; - if (self) { + if (self) + { _format = RPixelFormatBGRA8Unorm; } return self; diff --git a/gfx/common/metal/menu_pipeline.metal b/gfx/common/metal/menu_pipeline.metal new file mode 100644 index 0000000000..df1e587496 --- /dev/null +++ b/gfx/common/metal/menu_pipeline.metal @@ -0,0 +1,273 @@ +// +// pipeline_ribbon.metal +// RetroArch +// +// Created by Stuart Carnie on 6/30/18. +// + +#include + +#import "ShaderTypes.h" + +using namespace metal; + +#pragma mark - ribbon simple + +namespace ribbon { + +float iqhash(float n) +{ + return fract(sin(n) * 43758.5453); +} + +float noise(float3 x) +{ + float3 p = floor(x); + float3 f = fract(x); + f = f * f * (3.0 - 2.0 * f); + float n = p.x + p.y * 57.0 + 113.0 * p.z; + return mix(mix(mix(iqhash(n), iqhash(n + 1.0), f.x), + mix(iqhash(n + 57.0), iqhash(n + 58.0), f.x), f.y), + mix(mix(iqhash(n + 113.0), iqhash(n + 114.0), f.x), + mix(iqhash(n + 170.0), iqhash(n + 171.0), f.x), f.y), f.z); +} + +float xmb_noise2(float3 x, const device Uniforms &constants) +{ + return cos(x.z * 4.0) * cos(x.z + constants.time / 10.0 + x.x); +} + +} + +#pragma mark - ribbon simple + +vertex FontFragmentIn ribbon_simple_vertex(const SpriteVertex in [[ stage_in ]], const device Uniforms &constants [[ buffer(BufferIndexUniforms) ]]) +{ + float4 t = (constants.projectionMatrix * float4(in.position, 0, 1)); + + float3 v = float3(t.x, 0.0, 1.0-t.y); + float3 v2 = v; + + v2.x = v2.x + constants.time / 2.0; + v2.z = v.z * 3.0; + v.y = cos((v.x + v.z / 3.0 + constants.time) * 2.0) / 10.0 + ribbon::noise(v2.xyz) / 4.0; + v.y = -v.y; + + FontFragmentIn out; + out.position = float4(v, 1.0); + return out; +} + +fragment float4 ribbon_simple_fragment() +{ + return float4(0.05, 0.05, 0.05, 1.0); +} + +#pragma mark - ribbon + +typedef struct +{ + vector_float4 position [[position]]; + vector_float3 vEC; +} RibbonOutIn; + + +vertex RibbonOutIn ribbon_vertex(const SpriteVertex in [[ stage_in ]], const device Uniforms &constants [[ buffer(BufferIndexUniforms) ]]) +{ + float4 t = (constants.projectionMatrix * float4(in.position, 0, 1)); + + float3 v = float3(t.x, 0.0, 1.0-t.y); + float3 v2 = v; + float3 v3 = v; + + v.y = ribbon::xmb_noise2(v2, constants) / 8.0; + + v3.x -= constants.time / 5.0; + v3.x /= 4.0; + + v3.z -= constants.time / 10.0; + v3.y -= constants.time / 100.0; + + v.z -= ribbon::noise(v3 * 7.0) / 15.0; + v.y -= ribbon::noise(v3 * 7.0) / 15.0 + cos(v.x * 2.0 - constants.time / 2.0) / 5.0 - 0.3; + v.y = -v.y; + + RibbonOutIn out; + out.vEC = v; + out.position = float4(v, 1.0); + return out; +} + +fragment float4 ribbon_fragment(RibbonOutIn in [[ stage_in ]]) +{ + const float3 up = float3(0.0, 0.0, 1.0); + float3 x = dfdx(in.vEC); + float3 y = dfdy(in.vEC); + float3 normal = normalize(cross(x, y)); + float c = 1.0 - dot(normal, up); + c = (1.0 - cos(c * c)) / 13.0; + return float4(c, c, c, 1.0); +} + +#pragma mark - snow constants + +constant float snowBaseScale [[ function_constant(0) ]]; // [1.0 .. 10.0] +constant float snowDensity [[ function_constant(1) ]]; // [0.01 .. 1.0] +constant float snowSpeed [[ function_constant(2) ]]; // [0.1 .. 1.0] + +#pragma mark - snow simple + +namespace snow +{ + +float rand(float2 co) +{ + return fract(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453); +} + +float dist_func(float2 distv) +{ + float dist = sqrt((distv.x * distv.x) + (distv.y * distv.y)) * (40.0 / snowBaseScale); + dist = clamp(dist, 0.0, 1.0); + return cos(dist * (3.14159265358 * 0.5)) * 0.5; +} + +float random_dots(float2 co) +{ + float part = 1.0 / 20.0; + float2 cd = floor(co / part); + float p = rand(cd); + + if (p > 0.005 * (snowDensity * 40.0)) + return 0.0; + + float2 dpos = (float2(fract(p * 2.0) , p) + float2(2.0, 2.0)) * 0.25; + + float2 cellpos = fract(co / part); + float2 distv = (cellpos - dpos); + + return dist_func(distv); +} + +float snow(float2 pos, float time, float scale) +{ + // add wobble + pos.x += cos(pos.y * 1.2 + time * 3.14159 * 2.0 + 1.0 / scale) / (8.0 / scale) * 4.0; + // add gravity + pos += time * scale * float2(-0.5, 1.0) * 4.0; + return random_dots(pos / scale) * (scale * 0.5 + 0.5); +} + +} + +fragment float4 snow_fragment(FontFragmentIn in [[ stage_in ]], + const device Uniforms &constants [[ buffer(BufferIndexUniforms) ]]) +{ + float tim = constants.time * 0.4 * snowSpeed; + float2 pos = in.position.xy / constants.outputSize.xx; + pos.y = 1.0 - pos.y; // Flip Y + float a = 0.0; + // Each of these is a layer of snow + // Remove some for better performance + // Changing the scale (3rd value) will mess with the looping + a += snow::snow(pos, tim, 1.0); + a += snow::snow(pos, tim, 0.7); + a += snow::snow(pos, tim, 0.6); + a += snow::snow(pos, tim, 0.5); + a += snow::snow(pos, tim, 0.4); + a += snow::snow(pos, tim, 0.3); + a += snow::snow(pos, tim, 0.25); + a += snow::snow(pos, tim, 0.125); + a = a * min(pos.y * 4.0, 1.0); + return float4(1.0, 1.0, 1.0, a); +} + +fragment float4 bokeh_fragment(FontFragmentIn in [[ stage_in ]], + const device Uniforms &constants [[ buffer(BufferIndexUniforms) ]]) +{ + float speed = constants.time * 4.0; + float2 uv = -1.0 + 2.0 * in.position.xy / constants.outputSize; + uv.x *= constants.outputSize.x / constants.outputSize.y; + float3 color = float3(0.0); + + for( int i=0; i < 8; i++ ) + { + float pha = sin(float(i) * 546.13 + 1.0) * 0.5 + 0.5; + float siz = pow(sin(float(i) * 651.74 + 5.0) * 0.5 + 0.5, 4.0); + float pox = sin(float(i) * 321.55 + 4.1) * constants.outputSize.x / constants.outputSize.y; + float rad = 0.1 + 0.5 * siz + sin(pha + siz) / 4.0; + float2 pos = float2(pox + sin(speed / 15. + pha + siz), - 1.0 - rad + (2.0 + 2.0 * rad) * fract(pha + 0.3 * (speed / 7.) * (0.2 + 0.8 * siz))); + float dis = length(uv - pos); + if(dis < rad) + { + float3 col = mix(float3(0.194 * sin(speed / 6.0) + 0.3, 0.2, 0.3 * pha), float3(1.1 * sin(speed / 9.0) + 0.3, 0.2 * pha, 0.4), 0.5 + 0.5 * sin(float(i))); + color += col.zyx * (1.0 - smoothstep(rad * 0.15, rad, dis)); + } + } + color *= sqrt(1.5 - 0.5 * length(uv)); + return float4(color.r, color.g, color.b , 0.5); +} + +namespace snowflake { + +float rand_float(float x) +{ + return snow::rand(float2(x, 1.0)); +} + +float snow(float3 pos, float2 uv, float o, float atime) +{ + float2 d = (pos.xy - uv); + float a = atan(d.y / d.x) + sin(atime*1.0 + o) * 10.0; + + float dist = d.x*d.x + d.y*d.y; + + if(dist < pos.z/400.0) + { + float col = 0.0; + if(sin(a * 8.0) < 0.0) + { + col=1.0; + } + if(dist < pos.z/800.0) + { + col+=1.0; + } + return col * pos.z; + } + + return 0.0; +} + +float col(float2 c, const device Uniforms &constants) +{ + float color = 0.0; + float atime = (constants.time + 1.0) / 4.0; + + for (int i = 1; i < 15; i++) + { + float o = rand_float(float(i) / 3.0) * 15.0; + float z = rand_float(float(i) + 13.0); + float x = 1.8 - (3.6) * (rand_float(floor((constants.time*((z + 1.0) / 2.0) +o) / 2.0)) + sin(constants.time * o /1000.0) / 10.0); + float y = 1.0 - fmod((constants.time * ((z + 1.0)/2.0)) + o, 2.0); + + color += snow(float3(x,y,z), c, o, atime); + } + + return color; +} + +} + +fragment float4 snowflake_fragment(FontFragmentIn in [[ stage_in ]], + const device Uniforms &constants [[ buffer(BufferIndexUniforms) ]]) +{ + float2 uv = in.position.xy / constants.outputSize.xy; + uv = uv * 2.0 - 1.0; + float2 p = uv; + p.x *= constants.outputSize.x / constants.outputSize.y; + //p.y = -p.y; + + float c = snowflake::col(p, constants); + return float4(c,c,c,c); +} diff --git a/gfx/common/metal/metal_common.h b/gfx/common/metal/metal_common.h index 63bfc0f43d..45232b236d 100644 --- a/gfx/common/metal/metal_common.h +++ b/gfx/common/metal/metal_common.h @@ -6,6 +6,7 @@ // #import "RendererCommon.h" -#import "Renderer.h" +#import "Context.h" #import "View.h" #import "TexturedView.h" +#import "MenuDisplay.h" diff --git a/gfx/common/metal_common.h b/gfx/common/metal_common.h index aa64ea6209..c7271cee01 100644 --- a/gfx/common/metal_common.h +++ b/gfx/common/metal_common.h @@ -26,17 +26,16 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); #pragma mark - Classes -@interface FrameView : NSObject +@interface FrameView : NSObject -@property (readonly) RPixelFormat format; -@property (readonly) RTextureFilter filter; -@property (readwrite) BOOL visible; -@property (readwrite) CGRect frame; -@property (readwrite) CGSize size; -@property (readonly) ViewDrawState drawState; -@property (readonly) struct video_shader* shader; - -@property (readwrite) uint64_t frameCount; +@property (nonatomic, readonly) RPixelFormat format; +@property (nonatomic, readonly) RTextureFilter filter; +@property (nonatomic, readwrite) BOOL visible; +@property (nonatomic, readwrite) CGRect frame; +@property (nonatomic, readwrite) CGSize size; +@property (nonatomic, readonly) ViewDrawState drawState; +@property (nonatomic, readonly) struct video_shader *shader; +@property (nonatomic, readwrite) uint64_t frameCount; - (void)setFilteringIndex:(int)index smooth:(bool)smooth; - (BOOL)setShaderFromPath:(NSString *)path; @@ -47,8 +46,9 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @interface MetalMenu : NSObject -@property (nonatomic, readwrite) BOOL enabled; -@property (readwrite) float alpha; +@property (nonatomic, readonly) bool hasFrame; +@property (nonatomic, readwrite) bool enabled; +@property (nonatomic, readwrite) float alpha; - (void)updateFrame:(void const *)source; @@ -58,22 +58,40 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); filter:(RTextureFilter)filter; @end + +@interface Overlay : NSObject +@property (nonatomic, readwrite) bool enabled; +@property (nonatomic, readwrite) bool fullscreen; + +- (bool)loadImages:(const struct texture_image *)images count:(NSUInteger)count; +- (void)updateVertexX:(float)x y:(float)y w:(float)w h:(float)h index:(NSUInteger)index; +- (void)updateTextureCoordsX:(float)x y:(float)y w:(float)w h:(float)h index:(NSUInteger)index; +- (void)updateAlpha:(float)alpha index:(NSUInteger)index; +@end + @interface MetalDriver : NSObject -@property (readonly) video_viewport_t* viewport; -@property (readwrite) bool keepAspect; -@property (readonly) MetalMenu* menu; -@property (readwrite) uint64_t frameCount; -@property (readonly) FrameView* frameView; -@property (readonly) Context* context; +@property (nonatomic, readonly) video_viewport_t *viewport; +@property (nonatomic, readwrite) bool keepAspect; +@property (nonatomic, readonly) MetalMenu *menu; +@property (nonatomic, readonly) FrameView *frameView; +@property (nonatomic, readonly) MenuDisplay *display; +@property (nonatomic, readonly) Overlay *overlay; +@property (nonatomic, readonly) Context *context; +@property (nonatomic, readonly) Uniforms *viewportMVP; -- (instancetype)init NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithVideo:(const video_info_t *)video + input:(const input_driver_t **)input + inputData:(void **)inputData; - (void)setVideo:(const video_info_t *)video; - -- (void)beginFrame; -- (void)drawViews; -- (void)endFrame; +- (bool)renderFrame:(const void *)data + width:(unsigned)width + height:(unsigned)height + frameCount:(uint64_t)frameCount + pitch:(unsigned)pitch + msg:(const char *)msg + info:(video_frame_info_t *)video_info; /*! @brief setNeedsResize triggers a display resize */ - (void)setNeedsResize; diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 55e24128bb..ad433da056 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -14,6 +14,7 @@ #import #import #include +#import "Context.h" #define STRUCT_ASSIGN(x, y) \ { \ @@ -25,40 +26,120 @@ } \ if (__y != nil) \ x = (__bridge __typeof__(x))(__bridge_retained void *)((NSObject *)__y); \ -} + } + +#pragma mark - private categories @interface FrameView() -@property (readwrite) video_viewport_t *viewport; +@property (nonatomic, readwrite) video_viewport_t *viewport; -- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer; +- (instancetype)initWithDescriptor:(ViewDescriptor *)td context:(Context *)context; - (void)drawWithContext:(Context *)ctx; - (void)drawWithEncoder:(id)rce; @end -#pragma mark - private categories - @interface MetalMenu() -@property (readwrite) Renderer *renderer; +@property (nonatomic, readonly) TexturedView *view; +- (instancetype)initWithContext:(Context *)context; +@end + +@interface Overlay() +- (instancetype)initWithContext:(Context *)context; +- (void)drawWithEncoder:(id)rce; @end @implementation MetalDriver { - id _device; - - Renderer *_renderer; FrameView *_frameView; - + MetalMenu *_menu; + Overlay *_overlay; + video_info_t _video; + + id _device; + id _library; + Context *_context; + + CAMetalLayer *_layer; + + // render target layer state + id _t_pipelineState; + id _t_pipelineStateNoAlpha; + + id _samplerStateLinear; + id _samplerStateNearest; + + // other state + Uniforms _uniforms; + Uniforms _viewportMVP; } -- (instancetype)init +- (instancetype)initWithVideo:(const video_info_t *)video + input:(const input_driver_t **)input + inputData:(void **)inputData { - if (self = [super init]) { - _frameCount = 0; + if (self = [super init]) + { + _device = MTLCreateSystemDefaultDevice(); + MetalView *view = (MetalView *)apple_platform.renderView; + view.device = _device; + view.delegate = self; + _layer = (CAMetalLayer *)view.layer; + + if (![self _initMetal]) + { + return nil; + } + + _video = *video; _viewport = (video_viewport_t *)calloc(1, sizeof(video_viewport_t)); - _menu = [MetalMenu new]; + _viewportMVP.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); + + _keepAspect = _video.force_aspect; + + gfx_ctx_mode_t mode = { + .width = _video.width, + .height = _video.height, + .fullscreen = _video.fullscreen, + }; + + if (mode.width == 0 || mode.height == 0) + { + // 0 indicates full screen, so we'll use the view's dimensions, which should already be full screen + // If this turns out to be the wrong assumption, we can use NSScreen to query the dimensions + CGSize size = view.frame.size; + mode.width = (unsigned int)size.width; + mode.height = (unsigned int)size.height; + } + + [apple_platform setVideoMode:mode]; + + *input = NULL; + *inputData = NULL; + + // menu display + _display = [[MenuDisplay alloc] initWithContext:_context]; + + // menu view + _menu = [[MetalMenu alloc] initWithContext:_context]; + + // frame buffer view + { + ViewDescriptor *vd = [ViewDescriptor new]; + vd.format = _video.rgb32 ? RPixelFormatBGRX8Unorm : RPixelFormatB5G6R5Unorm; + vd.size = CGSizeMake(video->width, video->height); + vd.filter = _video.smooth ? RTextureFilterLinear : RTextureFilterNearest; + _frameView = [[FrameView alloc] initWithDescriptor:vd context:_context]; + _frameView.viewport = _viewport; + [_frameView setFilteringIndex:0 smooth:video->smooth]; + } + + // overlay view + _overlay = [[Overlay alloc] initWithContext:_context]; + + font_driver_init_osd((__bridge void *)self, false, video->is_threaded, FONT_DRIVER_RENDER_METAL_API); } return self; } @@ -66,60 +147,258 @@ - (void)dealloc { RARCH_LOG("[MetalDriver]: destroyed\n"); - if (_viewport) { + if (_viewport) + { free(_viewport); _viewport = nil; } + font_driver_free_osd(); } -- (Context *)context { - return _renderer.context; +- (bool)_initMetal +{ + _library = [_device newDefaultLibrary]; + _context = [[Context alloc] initWithDevice:_device + layer:_layer + library:_library]; + + { + MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; + vd.attributes[0].offset = 0; + vd.attributes[0].format = MTLVertexFormatFloat3; + vd.attributes[1].offset = offsetof(Vertex, texCoord); + vd.attributes[1].format = MTLVertexFormatFloat2; + vd.layouts[0].stride = sizeof(Vertex); + + MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; + psd.label = @"Pipeline+Alpha"; + + MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; + ca.pixelFormat = _layer.pixelFormat; + ca.blendingEnabled = YES; + ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + + psd.sampleCount = 1; + psd.vertexDescriptor = vd; + psd.vertexFunction = [_library newFunctionWithName:@"basic_vertex_proj_tex"]; + psd.fragmentFunction = [_library newFunctionWithName:@"basic_fragment_proj_tex"]; + + NSError *err; + _t_pipelineState = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"Pipeline+No Alpha"; + ca.blendingEnabled = NO; + _t_pipelineStateNoAlpha = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state (no alpha) %s\n", err.localizedDescription.UTF8String); + return NO; + } + } + + { + MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; + _samplerStateNearest = [_device newSamplerStateWithDescriptor:sd]; + + sd.minFilter = MTLSamplerMinMagFilterLinear; + sd.magFilter = MTLSamplerMinMagFilterLinear; + _samplerStateLinear = [_device newSamplerStateWithDescriptor:sd]; + } + + return YES; +} + +- (void)_updateUniforms +{ + _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); +} + +- (void)_updateViewport:(CGSize)size +{ + RARCH_LOG("[Metal]: _updateViewport size %.0fx%.0f\n", size.width, size.height); + + _viewport->full_width = (unsigned int)size.width; + _viewport->full_height = (unsigned int)size.height; + video_driver_set_size(&_viewport->full_width, &_viewport->full_height); + _layer.drawableSize = size; + video_driver_update_viewport(_viewport, NO, _keepAspect); + + _context.viewport = _viewport; + + _viewportMVP.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height); } #pragma mark - video - (void)setVideo:(const video_info_t *)video { - _video = *video; - - if (!_renderer) { - id device = MTLCreateSystemDefaultDevice(); - _device = device; - MetalView *view = (MetalView *)apple_platform.renderView; - view.device = device; - CAMetalLayer *layer = (CAMetalLayer *)view.layer; - //layer.device = device; - _renderer = [[Renderer alloc] initWithDevice:device layer:layer]; - _menu.renderer = _renderer; - } - - if (!_frameView) { - ViewDescriptor *vd = [ViewDescriptor new]; - vd.format = _video.rgb32 ? RPixelFormatBGRX8Unorm : RPixelFormatB5G6R5Unorm; - vd.size = CGSizeMake(video->width, video->height); - vd.filter = _video.smooth ? RTextureFilterLinear : RTextureFilterNearest; - _frameView = [[FrameView alloc] initWithDescriptor:vd renderer:_renderer]; - _frameView.viewport = _viewport; - [_renderer addView:_frameView]; - [_renderer sendViewToBack:_frameView]; - [_frameView setFilteringIndex:0 smooth:video->smooth]; - } + } -- (void)beginFrame +- (bool)renderFrame:(const void *)data + width:(unsigned)width + height:(unsigned)height + frameCount:(uint64_t)frameCount + pitch:(unsigned)pitch + msg:(const char *)msg + info:(video_frame_info_t *)video_info +{ + @autoreleasepool + { + [self _beginFrame]; + + _frameView.frameCount = frameCount; + _frameView.size = CGSizeMake(width, height); + [_frameView updateFrame:data pitch:pitch]; + + [self _drawViews:video_info]; + + if (video_info->statistics_show) + { + struct font_params *osd_params = (struct font_params *)&video_info->osd_stat_params; + + if (osd_params) + { + font_driver_render_msg(video_info, NULL, video_info->stat_text, osd_params); + } + } + +#ifdef HAVE_OVERLAY + if (_overlay.enabled) + { + id rce = _context.rce; + [rce pushDebugGroup:@"overlay"]; + [rce setRenderPipelineState:[_context getStockShader:VIDEO_SHADER_STOCK_BLEND blend:YES]]; + [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; + [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; + [_overlay drawWithEncoder:rce]; + [rce popDebugGroup]; + } +#endif + + if (msg && *msg) + [self _renderMessage:msg info:video_info]; + + [self _endFrame]; + } + + return YES; +} + +- (void)_renderMessage:(const char *)msg + info:(video_frame_info_t *)video_info +{ + settings_t *settings = config_get_ptr(); + if (settings && settings->bools.video_msg_bgcolor_enable) + { + int msg_width = + font_driver_get_message_width(NULL, msg, (unsigned)strlen(msg), 1.0f); + + float x = video_info->font_msg_pos_x; + float y = 1.0f - video_info->font_msg_pos_y; + float width = msg_width / (float)_viewport->full_width; + float height = + settings->floats.video_font_size / (float)_viewport->full_height; + + y -= height; + + + float x2 = 0.005f; /* extend background around text */ + float y2 = 0.005f; + + x -= x2; + y -= y2; + width += x2; + height += y2; + + float r = settings->uints.video_msg_bgcolor_red / 255.0f; + float g = settings->uints.video_msg_bgcolor_green / 255.0f; + float b = settings->uints.video_msg_bgcolor_blue / 255.0f; + float a = settings->floats.video_msg_bgcolor_opacity; + [_context resetRenderViewport]; + [_context drawQuadX:x y:y w:width h:height r:r g:g b:b a:a]; + } + + font_driver_render_msg(video_info, NULL, msg, NULL); +} + +- (void)_beginFrame { video_driver_update_viewport(_viewport, NO, _keepAspect); - - [_renderer beginFrame]; + [_context begin]; + [self _updateUniforms]; } -- (void)drawViews { - [_renderer drawViews]; -} - -- (void)endFrame +- (void)_drawViews:(video_frame_info_t *)video_info { - [_renderer endFrame]; + id rce = _context.rce; + + // draw back buffer + [rce pushDebugGroup:@"core frame"]; + [_frameView drawWithContext:_context]; + + if ((_frameView.drawState & ViewDrawStateEncoder) != 0) + { + [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; + [rce setRenderPipelineState:_t_pipelineStateNoAlpha]; + if (_frameView.filter == RTextureFilterNearest) + { + [rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw]; + } + else + { + [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; + } + [_frameView drawWithEncoder:rce]; + } + [rce popDebugGroup]; + + if (_menu.enabled && _menu.hasFrame) + { + [_menu.view drawWithContext:_context]; + [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; + [rce setRenderPipelineState:_t_pipelineState]; + if (_menu.view.filter == RTextureFilterNearest) + { + [rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw]; + } + else + { + [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; + } + [_menu.view drawWithEncoder:rce]; + } + +#if defined(HAVE_MENU) + if (_menu.enabled) + { + MTLViewport viewport = { + .originX = 0.0f, + .originY = 0.0f, + .width = _viewport->full_width, + .height = _viewport->full_height, + .znear = 0.0f, + .zfar = 1.0, + }; + [rce setViewport:viewport]; + [rce pushDebugGroup:@"menu"]; + menu_driver_frame(video_info); + [rce popDebugGroup]; + } +#endif +} + +- (void)_endFrame +{ + [_context end]; } - (void)setNeedsResize @@ -127,38 +406,54 @@ // TODO(sgc): resize all drawables } -#pragma mark - MTKViewDelegate - -- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { - RARCH_LOG("[MetalDriver] drawableSizeWillChange: %s\n", NSStringFromSize(size).UTF8String); - _viewport->full_width = (unsigned int)size.width; - _viewport->full_height = (unsigned int)size.height; - video_driver_set_size(&_viewport->full_width, &_viewport->full_height); - [_renderer drawableSizeWillChange:size]; - video_driver_update_viewport(_viewport, NO, _keepAspect); +- (Uniforms *)viewportMVP +{ + return &_viewportMVP; } -- (void)drawInMTKView:(MTKView *)view { +#pragma mark - MTKViewDelegate +- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size +{ + [self _updateViewport:size]; +} + +- (void)drawInMTKView:(MTKView *)view +{ + } @end @implementation MetalMenu { - Renderer *_renderer; + Context *_context; TexturedView *_view; - BOOL _enabled; + bool _enabled; } -- (void)setEnabled:(BOOL)enabled +- (instancetype)initWithContext:(Context *)context +{ + if (self = [super init]) + { + _context = context; + } + return self; +} + +- (bool)hasFrame +{ + return _view != nil; +} + +- (void)setEnabled:(bool)enabled { if (_enabled == enabled) return; _enabled = enabled; _view.visible = enabled; } -- (BOOL)enabled +- (bool)enabled { return _enabled; } @@ -169,23 +464,24 @@ filter:(RTextureFilter)filter { CGSize size = CGSizeMake(width, height); - - if (_view) { + + if (_view) + { if (!(CGSizeEqualToSize(_view.size, size) && _view.format == format && - _view.filter == filter)) { - [_renderer removeView:_view]; + _view.filter == filter)) + { _view = nil; } } - - if (!_view) { + + if (!_view) + { ViewDescriptor *vd = [ViewDescriptor new]; vd.format = format; vd.filter = filter; vd.size = size; - _view = [[TexturedView alloc] initWithDescriptor:vd renderer:_renderer]; - [_renderer addView:_view]; + _view = [[TexturedView alloc] initWithDescriptor:vd context:_context]; _view.visible = _enabled; } } @@ -199,7 +495,7 @@ #pragma mark - FrameView -#define ALIGN(x) __attribute__((aligned(x))) +#define MTLALIGN(x) __attribute__((aligned(x))) typedef struct { @@ -215,17 +511,17 @@ typedef struct texture float4_t size_data; } texture_t; -typedef struct ALIGN(16) +typedef struct MTLALIGN(16) { matrix_float4x4 mvp; - + struct { texture_t texture[GFX_MAX_FRAME_HISTORY + 1]; MTLViewport viewport; float4_t output_size; } frame; - + struct { __unsafe_unretained id buffers[SLANG_CBUFFER_MAX]; @@ -236,53 +532,54 @@ typedef struct ALIGN(16) MTLViewport viewport; __unsafe_unretained id _state; } pass[GFX_MAX_SHADERS]; - + texture_t luts[GFX_MAX_TEXTURES]; - + } engine_t; @implementation FrameView { - __weak Renderer *_renderer; Context *_context; id _texture; // final render texture Vertex _v[4]; CGSize _size; // size of view in pixels CGRect _frame; NSUInteger _bpp; - + id _pixels; // frame buffer in _srcFmt bool _pixelsDirty; - + id _samplers[RARCH_FILTER_MAX][RARCH_WRAP_MAX]; struct video_shader *_shader; - id _fence; - + engine_t _engine; - + bool resize_render_targets; bool init_history; video_viewport_t *_viewport; } -- (instancetype)initWithDescriptor:(ViewDescriptor *)d renderer:(Renderer *)r +- (instancetype)initWithDescriptor:(ViewDescriptor *)d context:(Context *)c { self = [super init]; - if (self) { - _renderer = r; - _context = r.context; + if (self) + { + _context = c; _format = d.format; _bpp = RPixelFormatToBPP(_format); _filter = d.filter; - if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) { + if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) + { _drawState = ViewDrawStateEncoder; - } else { + } + else + { _drawState = ViewDrawStateAll; } _visible = YES; _engine.mvp = matrix_proj_ortho(0, 1, 0, 1); [self _initSamplers]; - + self.size = d.size; self.frame = CGRectMake(0, 0, 1, 1); resize_render_targets = YES; @@ -293,26 +590,28 @@ typedef struct ALIGN(16) - (void)_initSamplers { MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; - + /* Initialize samplers */ - for (unsigned i = 0; i < RARCH_WRAP_MAX; i++) { - switch (i) { + for (unsigned i = 0; i < RARCH_WRAP_MAX; i++) + { + switch (i) + { case RARCH_WRAP_BORDER: sd.sAddressMode = MTLSamplerAddressModeClampToBorderColor; break; - + case RARCH_WRAP_EDGE: sd.sAddressMode = MTLSamplerAddressModeClampToEdge; break; - + case RARCH_WRAP_REPEAT: sd.sAddressMode = MTLSamplerAddressModeRepeat; break; - + case RARCH_WRAP_MIRRORED_REPEAT: sd.sAddressMode = MTLSamplerAddressModeMirrorRepeat; break; - + default: continue; } @@ -320,13 +619,13 @@ typedef struct ALIGN(16) sd.rAddressMode = sd.sAddressMode; sd.minFilter = MTLSamplerMinMagFilterLinear; sd.magFilter = MTLSamplerMinMagFilterLinear; - + id ss = [_context.device newSamplerStateWithDescriptor:sd]; _samplers[RARCH_FILTER_LINEAR][i] = ss; - + sd.minFilter = MTLSamplerMinMagFilterNearest; sd.magFilter = MTLSamplerMinMagFilterNearest; - + ss = [_context.device newSamplerStateWithDescriptor:sd]; _samplers[RARCH_FILTER_NEAREST][i] = ss; } @@ -334,7 +633,8 @@ typedef struct ALIGN(16) - (void)setFilteringIndex:(int)index smooth:(bool)smooth { - for (int i = 0; i < RARCH_WRAP_MAX; i++) { + for (int i = 0; i < RARCH_WRAP_MAX; i++) + { if (smooth) _samplers[RARCH_FILTER_UNSPEC][i] = _samplers[RARCH_FILTER_LINEAR][i]; else @@ -344,15 +644,17 @@ typedef struct ALIGN(16) - (void)setSize:(CGSize)size { - if (CGSizeEqualToSize(_size, size)) { + if (CGSizeEqualToSize(_size, size)) + { return; } - + _size = size; - + resize_render_targets = YES; - - if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) { + + if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) + { _pixels = [_context.device newBufferWithLength:(NSUInteger)(size.width * size.height * 2) options:MTLResourceStorageModeManaged]; } @@ -365,26 +667,27 @@ typedef struct ALIGN(16) - (void)setFrame:(CGRect)frame { - if (CGRectEqualToRect(_frame, frame)) { + if (CGRectEqualToRect(_frame, frame)) + { return; } - + _frame = frame; - + // update vertices CGPoint o = frame.origin; CGSize s = frame.size; - + CGFloat l = o.x; CGFloat t = o.y; CGFloat r = o.x + s.width; CGFloat b = o.y + s.height; - + Vertex v[4] = { - {{l, b, 0}, {0, 1}}, - {{r, b, 0}, {1, 1}}, - {{l, t, 0}, {0, 0}}, - {{r, t, 0}, {1, 0}}, + {simd_make_float3(l, b, 0), simd_make_float2(0, 1)}, + {simd_make_float3(r, b, 0), simd_make_float2(1, 1)}, + {simd_make_float3(l, t, 0), simd_make_float2(0, 0)}, + {simd_make_float3(r, t, 0), simd_make_float2(1, 0)}, }; memcpy(_v, v, sizeof(_v)); } @@ -398,21 +701,24 @@ typedef struct ALIGN(16) { if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) return; - + if (!_pixelsDirty) return; - - [_renderer.conv convertFormat:_format from:_pixels to:_texture]; + + [_context convertFormat:_format from:_pixels to:_texture]; _pixelsDirty = NO; } - (void)_updateHistory { - if (_shader) { - if (_shader->history_size) { + if (_shader) + { + if (_shader->history_size) + { if (init_history) [self _initHistory]; - else { + else + { int k; /* todo: what about frame-duping ? * maybe clone d3d10_texture_t with AddRef */ @@ -423,10 +729,11 @@ typedef struct ALIGN(16) } } } - + /* either no history, or we moved a texture of a different size in the front slot */ if (_engine.frame.texture[0].size_data.x != _size.width || - _engine.frame.texture[0].size_data.y != _size.height) { + _engine.frame.texture[0].size_data.y != _size.height) + { MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:(NSUInteger)_size.width height:(NSUInteger)_size.height @@ -439,10 +746,11 @@ typedef struct ALIGN(16) - (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch { if (_shader && (_engine.frame.output_size.x != _viewport->width || - _engine.frame.output_size.y != _viewport->height)) { + _engine.frame.output_size.y != _viewport->height)) + { resize_render_targets = YES; } - + _engine.frame.viewport.originX = _viewport->x; _engine.frame.viewport.originY = _viewport->y; _engine.frame.viewport.width = _viewport->width; @@ -453,35 +761,41 @@ typedef struct ALIGN(16) _engine.frame.output_size.y = _viewport->height; _engine.frame.output_size.z = 1.0f / _viewport->width; _engine.frame.output_size.w = 1.0f / _viewport->height; - - if (resize_render_targets) { + + if (resize_render_targets) + { [self _updateRenderTargets]; } - + [self _updateHistory]; - - if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) { + + if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) + { id tex = _engine.frame.texture[0].view; [tex replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height) mipmapLevel:0 withBytes:src - bytesPerRow:(NSUInteger)(4 * _size.width)]; + bytesPerRow:pitch]; } - else { + else + { void *dst = _pixels.contents; size_t len = (size_t)(_bpp * _size.width); assert(len <= pitch); // the length can't be larger? - - if (len < pitch) { - for (int i = 0; i < _size.height; i++) { + + if (len < pitch) + { + for (int i = 0; i < _size.height; i++) + { memcpy(dst, src, len); dst += len; src += pitch; } } - else { + else + { memcpy(dst, src, _pixels.length); } - + [_pixels didModifyRange:NSMakeRange(0, _pixels.length)]; _pixelsDirty = YES; } @@ -503,8 +817,9 @@ typedef struct ALIGN(16) height:(NSUInteger)_size.height mipmapped:false]; td.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite | MTLTextureUsageRenderTarget; - - for (int i = 0; i < _shader->history_size + 1; i++) { + + for (int i = 0; i < _shader->history_size + 1; i++) + { [self _initTexture:&_engine.frame.texture[i] withDescriptor:td]; } init_history = NO; @@ -525,7 +840,8 @@ static vertex_t vertex_bytes[] = { - (void)drawWithEncoder:(id)rce { - if (_texture) { + if (_texture) + { [rce setViewport:_engine.frame.viewport]; [rce setVertexBytes:&_v length:sizeof(_v) atIndex:BufferIndexPositions]; [rce setFragmentTexture:_texture atIndex:TextureIndexColor]; @@ -537,172 +853,192 @@ static vertex_t vertex_bytes[] = { { _texture = _engine.frame.texture[0].view; [self _convertFormat]; - - if (!_shader || _shader->passes == 0) { + + if (!_shader || _shader->passes == 0) + { return; } - - for (unsigned i = 0; i < _shader->passes; i++) { - if (_shader->pass[i].feedback) { + + for (unsigned i = 0; i < _shader->passes; i++) + { + if (_shader->pass[i].feedback) + { texture_t tmp = _engine.pass[i].feedback; _engine.pass[i].feedback = _engine.pass[i].rt; _engine.pass[i].rt = tmp; } } - - id cb = ctx.commandBuffer; - - MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; - rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0); - rpd.colorAttachments[0].loadAction = MTLLoadActionClear; - rpd.colorAttachments[0].storeAction = MTLStoreActionStore; - - BOOL firstPass = YES; - for (unsigned i = 0; i < _shader->passes; i++) { + id cb = ctx.blitCommandBuffer; + + MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; + rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare; + rpd.colorAttachments[0].storeAction = MTLStoreActionStore; + + for (unsigned i = 0; i < _shader->passes; i++) + { + id rce = nil; + BOOL backBuffer = (_engine.pass[i].rt.view == nil); - - if (backBuffer) { - rpd.colorAttachments[0].texture = _context.nextDrawable.texture; + + if (backBuffer) + { + rce = _context.rce; } - else { + else + { rpd.colorAttachments[0].texture = _engine.pass[i].rt.view; + rce = [cb renderCommandEncoderWithDescriptor:rpd]; } - id rce = [cb renderCommandEncoderWithDescriptor:rpd]; - if (firstPass) { - firstPass = NO; - } else { - [rce waitForFence:_fence beforeStages:MTLRenderStageVertex]; - } +#if METAL_DEBUG + rce.label = [NSString stringWithFormat:@"pass %d", i]; +#endif [rce setRenderPipelineState:_engine.pass[i]._state]; - + _engine.pass[i].frame_count = (uint32_t)_frameCount; if (_shader->pass[i].frame_count_mod) _engine.pass[i].frame_count %= _shader->pass[i].frame_count_mod; - - for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) { + + for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) + { id buffer = _engine.pass[i].buffers[j]; cbuffer_sem_t *buffer_sem = &_engine.pass[i].semantics.cbuffers[j]; - - if (buffer_sem->stage_mask && buffer_sem->uniforms) { + + if (buffer_sem->stage_mask && buffer_sem->uniforms) + { void *data = buffer.contents; uniform_sem_t *uniform = buffer_sem->uniforms; - - while (uniform->size) { + + while (uniform->size) + { if (uniform->data) memcpy((uint8_t *)data + uniform->offset, uniform->data, uniform->size); uniform++; } - + if (buffer_sem->stage_mask & SLANG_STAGE_VERTEX_MASK) [rce setVertexBuffer:buffer offset:0 atIndex:buffer_sem->binding]; - + if (buffer_sem->stage_mask & SLANG_STAGE_FRAGMENT_MASK) [rce setFragmentBuffer:buffer offset:0 atIndex:buffer_sem->binding]; [buffer didModifyRange:NSMakeRange(0, buffer.length)]; } } - + __unsafe_unretained id textures[SLANG_NUM_BINDINGS] = {NULL}; id samplers[SLANG_NUM_BINDINGS] = {NULL}; - + texture_sem_t *texture_sem = _engine.pass[i].semantics.textures; - while (texture_sem->stage_mask) { + while (texture_sem->stage_mask) + { int binding = texture_sem->binding; id tex = (__bridge id)*(void **)texture_sem->texture_data; textures[binding] = tex; samplers[binding] = _samplers[texture_sem->filter][texture_sem->wrap]; texture_sem++; } - - if (backBuffer) { + + if (backBuffer) + { [rce setViewport:_engine.frame.viewport]; } - else { + else + { [rce setViewport:_engine.pass[i].viewport]; } - + [rce setFragmentTextures:textures withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)]; [rce setFragmentSamplerStates:samplers withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)]; [rce setVertexBytes:vertex_bytes length:sizeof(vertex_bytes) atIndex:4]; [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; - [rce updateFence:_fence afterStages:MTLRenderStageFragment]; - [rce endEncoding]; + + if (!backBuffer) + { + [rce endEncoding]; + } + _texture = _engine.pass[i].rt.view; } - if (_texture == nil) { + + if (_texture == nil) _drawState = ViewDrawStateContext; - } else { + else _drawState = ViewDrawStateAll; - } } - (void)_updateRenderTargets { if (!_shader || !resize_render_targets) return; - + // release existing targets - for (int i = 0; i < _shader->passes; i++) { + for (int i = 0; i < _shader->passes; i++) + { STRUCT_ASSIGN(_engine.pass[i].rt.view, nil); STRUCT_ASSIGN(_engine.pass[i].feedback.view, nil); memset(&_engine.pass[i].rt, 0, sizeof(_engine.pass[i].rt)); memset(&_engine.pass[i].feedback, 0, sizeof(_engine.pass[i].feedback)); } - + NSUInteger width = (NSUInteger)_size.width, height = (NSUInteger)_size.height; - - for (unsigned i = 0; i < _shader->passes; i++) { + + for (unsigned i = 0; i < _shader->passes; i++) + { struct video_shader_pass *shader_pass = &_shader->pass[i]; - - if (shader_pass->fbo.valid) { - switch (shader_pass->fbo.type_x) { + + if (shader_pass->fbo.valid) + { + switch (shader_pass->fbo.type_x) + { case RARCH_SCALE_INPUT: width *= shader_pass->fbo.scale_x; break; - + case RARCH_SCALE_VIEWPORT: width = (NSUInteger)(_viewport->width * shader_pass->fbo.scale_x); break; - + case RARCH_SCALE_ABSOLUTE: width = shader_pass->fbo.abs_x; break; - + default: break; } - + if (!width) width = _viewport->width; - - switch (shader_pass->fbo.type_y) { + + switch (shader_pass->fbo.type_y) + { case RARCH_SCALE_INPUT: height *= shader_pass->fbo.scale_y; break; - + case RARCH_SCALE_VIEWPORT: height = (NSUInteger)(_viewport->height * shader_pass->fbo.scale_y); break; - + case RARCH_SCALE_ABSOLUTE: height = shader_pass->fbo.abs_y; break; - + default: break; } - + if (!height) height = _viewport->height; } - else if (i == (_shader->passes - 1)) { + else if (i == (_shader->passes - 1)) + { width = _viewport->width; height = _viewport->height; } - + RARCH_LOG("[Metal]: Updating framebuffer size %u x %u.\n", width, height); + MTLPixelFormat fmt = SelectOptimalPixelFormat(glslang_format_to_metal(_engine.pass[i].semantics.format)); if ((i != (_shader->passes - 1)) || (width != _viewport->width) || (height != _viewport->height) || @@ -712,26 +1048,29 @@ static vertex_t vertex_bytes[] = { _engine.pass[i].viewport.height = height; _engine.pass[i].viewport.znear = 0.0; _engine.pass[i].viewport.zfar = 1.0; - + MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:fmt width:width height:height mipmapped:false]; + td.storageMode = MTLStorageModePrivate; td.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; [self _initTexture:&_engine.pass[i].rt withDescriptor:td]; - - if (shader_pass->feedback) { + + if (shader_pass->feedback) + { [self _initTexture:&_engine.pass[i].feedback withDescriptor:td]; } } - else { + else + { _engine.pass[i].rt.size_data.x = width; _engine.pass[i].rt.size_data.y = height; _engine.pass[i].rt.size_data.z = 1.0f / width; _engine.pass[i].rt.size_data.w = 1.0f / height; } } - + resize_render_targets = NO; } @@ -739,67 +1078,71 @@ static vertex_t vertex_bytes[] = { { if (!shader) return; - - for (int i = 0; i < GFX_MAX_SHADERS; i++) { + + for (int i = 0; i < GFX_MAX_SHADERS; i++) + { STRUCT_ASSIGN(_engine.pass[i].rt.view, nil); STRUCT_ASSIGN(_engine.pass[i].feedback.view, nil); memset(&_engine.pass[i].rt, 0, sizeof(_engine.pass[i].rt)); memset(&_engine.pass[i].feedback, 0, sizeof(_engine.pass[i].feedback)); - + STRUCT_ASSIGN(_engine.pass[i]._state, nil); - - for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) { + + for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) + { STRUCT_ASSIGN(_engine.pass[i].buffers[j], nil); } } - - for (int i = 0; i < GFX_MAX_TEXTURES; i++) { + + for (int i = 0; i < GFX_MAX_TEXTURES; i++) + { STRUCT_ASSIGN(_engine.luts[i].view, nil); } - + free(shader); - _fence = nil; } - (BOOL)setShaderFromPath:(NSString *)path { [self _freeVideoShader:_shader]; _shader = nil; - + config_file_t *conf = config_file_new(path.UTF8String); struct video_shader *shader = (struct video_shader *)calloc(1, sizeof(*shader)); - - @try { + + @try + { if (!video_shader_read_conf_cgp(conf, shader)) return NO; - + video_shader_resolve_relative(shader, path.UTF8String); - + texture_t *source = &_engine.frame.texture[0]; - for (unsigned i = 0; i < shader->passes; source = &_engine.pass[i++].rt) { + for (unsigned i = 0; i < shader->passes; source = &_engine.pass[i++].rt) + { /* clang-format off */ semantics_map_t semantics_map = { { /* Original */ {&_engine.frame.texture[0].view, 0, &_engine.frame.texture[0].size_data, 0}, - + /* Source */ {&source->view, 0, &source->size_data, 0}, - + /* OriginalHistory */ {&_engine.frame.texture[0].view, sizeof(*_engine.frame.texture), &_engine.frame.texture[0].size_data, sizeof(*_engine.frame.texture)}, - + /* PassOutput */ {&_engine.pass[0].rt.view, sizeof(*_engine.pass), &_engine.pass[0].rt.size_data, sizeof(*_engine.pass)}, - + /* PassFeedback */ {&_engine.pass[0].feedback.view, sizeof(*_engine.pass), &_engine.pass[0].feedback.size_data, sizeof(*_engine.pass)}, - + /* User */ {&_engine.luts[0].view, sizeof(*_engine.luts), &_engine.luts[0].size_data, sizeof(*_engine.luts)}, @@ -812,20 +1155,21 @@ static vertex_t vertex_bytes[] = { } }; /* clang-format on */ - + if (!slang_process(shader, i, RARCH_SHADER_METAL, 20000, &semantics_map, &_engine.pass[i].semantics)) return NO; #ifdef DEBUG - bool save_msl = true; + bool save_msl = true; #else bool save_msl = false; #endif NSString *vs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.vertex]; NSString *fs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.fragment]; - + // vertex descriptor - @try { + @try + { MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; vd.attributes[0].offset = offsetof(vertex_t, pos); vd.attributes[0].format = MTLVertexFormatFloat4; @@ -835,28 +1179,30 @@ static vertex_t vertex_bytes[] = { vd.attributes[1].bufferIndex = 4; vd.layouts[4].stride = sizeof(vertex_t); vd.layouts[4].stepFunction = MTLVertexStepFunctionPerVertex; - + MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; psd.label = [NSString stringWithFormat:@"pass %d", i]; - + MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; ca.pixelFormat = SelectOptimalPixelFormat(glslang_format_to_metal(_engine.pass[i].semantics.format)); - + // TODO(sgc): confirm we never need blending for render passes ca.blendingEnabled = NO; ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; - + psd.sampleCount = 1; psd.vertexDescriptor = vd; - + NSError *err; id lib = [_context.device newLibraryWithSource:vs_src options:nil error:&err]; - if (err != nil) { - if (lib == nil) { + if (err != nil) + { + if (lib == nil) + { save_msl = true; RARCH_ERR("Metal]: unable to compile vertex shader: %s\n", err.localizedDescription.UTF8String); return NO; @@ -865,12 +1211,14 @@ static vertex_t vertex_bytes[] = { RARCH_WARN("[Metal]: warnings compiling vertex shader: %s\n", err.localizedDescription.UTF8String); #endif } - + psd.vertexFunction = [lib newFunctionWithName:@"main0"]; - + lib = [_context.device newLibraryWithSource:fs_src options:nil error:&err]; - if (err != nil) { - if (lib == nil) { + if (err != nil) + { + if (lib == nil) + { save_msl = true; RARCH_ERR("Metal]: unable to compile fragment shader: %s\n", err.localizedDescription.UTF8String); return NO; @@ -880,35 +1228,41 @@ static vertex_t vertex_bytes[] = { #endif } psd.fragmentFunction = [lib newFunctionWithName:@"main0"]; - + STRUCT_ASSIGN(_engine.pass[i]._state, [_context.device newRenderPipelineStateWithDescriptor:psd error:&err]); - if (err != nil) { + if (err != nil) + { save_msl = true; RARCH_ERR("error creating pipeline state: %s", err.localizedDescription.UTF8String); return NO; } - - for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) { + + for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) + { unsigned int size = _engine.pass[i].semantics.cbuffers[j].size; - if (size == 0) { + if (size == 0) + { continue; } - + id buf = [_context.device newBufferWithLength:size options:MTLResourceStorageModeManaged]; STRUCT_ASSIGN(_engine.pass[i].buffers[j], buf); } - } @finally { - if (save_msl) { + } @finally + { + if (save_msl) + { RARCH_LOG("[Metal]: saving metal shader files\n"); - + NSError *err = nil; NSString *basePath = [[NSString stringWithUTF8String:shader->pass[i].source.path] stringByDeletingPathExtension]; [vs_src writeToFile:[basePath stringByAppendingPathExtension:@"vs.metal"] atomically:NO encoding:NSStringEncodingConversionAllowLossy error:&err]; - if (err != nil) { + if (err != nil) + { RARCH_ERR("[Metal]: unable to save vertex shader source: %s\n", err.localizedDescription.UTF8String); } @@ -917,106 +1271,220 @@ static vertex_t vertex_bytes[] = { atomically:NO encoding:NSStringEncodingConversionAllowLossy error:&err]; - if (err != nil) { - RARCH_ERR("[Metal]: unable to save fragment shader source: %s\n", err.localizedDescription.UTF8String); + if (err != nil) + { + RARCH_ERR("[Metal]: unable to save fragment shader source: %s\n", + err.localizedDescription.UTF8String); } } - + free(shader->pass[i].source.string.vertex); free(shader->pass[i].source.string.fragment); - + shader->pass[i].source.string.vertex = NULL; shader->pass[i].source.string.fragment = NULL; } } - - for (unsigned i = 0; i < shader->luts; i++) { + + for (unsigned i = 0; i < shader->luts; i++) + { struct texture_image image = {0}; image.supports_rgba = true; - + if (!image_texture_load(&image, shader->lut[i].path)) return NO; - + MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm width:image.width height:image.height mipmapped:shader->lut[i].mipmap]; td.usage = MTLTextureUsageShaderRead; [self _initTexture:&_engine.luts[i] withDescriptor:td]; - + [_engine.luts[i].view replaceRegion:MTLRegionMake2D(0, 0, image.width, image.height) mipmapLevel:0 withBytes:image.pixels bytesPerRow:4 * image.width]; - + // TODO(sgc): generate mip maps image_texture_free(&image); } - + video_shader_resolve_current_parameters(conf, shader); _shader = shader; shader = nil; - _fence = [_context.device newFence]; } - @finally { - if (shader) { + @finally + { + if (shader) + { [self _freeVideoShader:shader]; } - - if (conf) { + + if (conf) + { config_file_free(conf); conf = nil; } } - + resize_render_targets = YES; init_history = YES; - + return YES; } @end +@implementation Overlay +{ + Context *_context; + NSMutableArray> *_images; + id _vert; + bool _vertDirty; +} + +- (instancetype)initWithContext:(Context *)context +{ + if (self = [super init]) + { + _context = context; + } + return self; +} + +- (bool)loadImages:(const struct texture_image *)images count:(NSUInteger)count +{ + [self _freeImages]; + + _images = [NSMutableArray arrayWithCapacity:count]; + + NSUInteger needed = sizeof(SpriteVertex) * count * 4; + if (!_vert || _vert.length < needed) + { + _vert = [_context.device newBufferWithLength:needed options:MTLResourceStorageModeManaged]; + } + + for (NSUInteger i = 0; i < count; i++) + { + _images[i] = [_context newTexture:images[i] mipmapped:NO]; + [self updateVertexX:0 y:0 w:1 h:1 index:i]; + [self updateTextureCoordsX:0 y:0 w:1 h:1 index:i]; + [self _updateColorRed:1.0 green:1.0 blue:1.0 alpha:1.0 index:i]; + } + + _vertDirty = YES; + + return YES; +} + +- (void)drawWithEncoder:(id)rce +{ + if (_vertDirty) + { + [_vert didModifyRange:NSMakeRange(0, _vert.length)]; + _vertDirty = NO; + } + + NSUInteger count = _images.count; + for (NSUInteger i = 0; i < count; ++i) + { + NSUInteger offset = sizeof(SpriteVertex) * 4 * i; + [rce setVertexBuffer:_vert offset:offset atIndex:BufferIndexPositions]; + [rce setFragmentTexture:_images[i] atIndex:TextureIndexColor]; + [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + } +} + +- (SpriteVertex *)_getForIndex:(NSUInteger)index +{ + SpriteVertex *pv = (SpriteVertex *)_vert.contents; + return &pv[index * 4]; +} + +- (void)_updateColorRed:(float)r green:(float)g blue:(float)b alpha:(float)a index:(NSUInteger)index +{ + simd_float4 color = simd_make_float4(r, g, b, a); + SpriteVertex *pv = [self _getForIndex:index]; + pv[0].color = color; + pv[1].color = color; + pv[2].color = color; + pv[3].color = color; + _vertDirty = YES; +} + +- (void)updateAlpha:(float)alpha index:(NSUInteger)index +{ + [self _updateColorRed:1.0 green:1.0 blue:1.0 alpha:alpha index:index]; +} + +- (void)updateVertexX:(float)x y:(float)y w:(float)w h:(float)h index:(NSUInteger)index +{ + SpriteVertex *pv = [self _getForIndex:index]; + pv[0].position = simd_make_float2(x, y); + pv[1].position = simd_make_float2(x + w, y); + pv[2].position = simd_make_float2(x, y + h); + pv[3].position = simd_make_float2(x + w, y + h); + _vertDirty = YES; +} + +- (void)updateTextureCoordsX:(float)x y:(float)y w:(float)w h:(float)h index:(NSUInteger)index +{ + SpriteVertex *pv = [self _getForIndex:index]; + pv[0].texCoord = simd_make_float2(x, y); + pv[1].texCoord = simd_make_float2(x + w, y); + pv[2].texCoord = simd_make_float2(x, y + h); + pv[3].texCoord = simd_make_float2(x + w, y + h); + _vertDirty = YES; +} + +- (void)_freeImages +{ + _images = nil; +} + +@end + MTLPixelFormat glslang_format_to_metal(glslang_format fmt) { #undef FMT2 -#define FMT2(x,y) case SLANG_FORMAT_##x: return MTLPixelFormat##y +#define FMT2(x, y) case SLANG_FORMAT_##x: return MTLPixelFormat##y switch (fmt) { - FMT2(R8_UNORM, R8Unorm); - FMT2(R8_SINT, R8Sint); - FMT2(R8_UINT, R8Uint); - FMT2(R8G8_UNORM, RG8Unorm); - FMT2(R8G8_SINT, RG8Sint); - FMT2(R8G8_UINT, RG8Uint); - FMT2(R8G8B8A8_UNORM, RGBA8Unorm); - FMT2(R8G8B8A8_SINT, RGBA8Sint); - FMT2(R8G8B8A8_UINT, RGBA8Uint); - FMT2(R8G8B8A8_SRGB, RGBA8Unorm_sRGB); - - FMT2(A2B10G10R10_UNORM_PACK32, RGB10A2Unorm); - FMT2(A2B10G10R10_UINT_PACK32, RGB10A2Uint); - - FMT2(R16_UINT, R16Uint); - FMT2(R16_SINT, R16Sint); - FMT2(R16_SFLOAT, R16Float); - FMT2(R16G16_UINT, RG16Uint); - FMT2(R16G16_SINT, RG16Sint); - FMT2(R16G16_SFLOAT, RG16Float); - FMT2(R16G16B16A16_UINT, RGBA16Uint); - FMT2(R16G16B16A16_SINT, RGBA16Sint); - FMT2(R16G16B16A16_SFLOAT, RGBA16Float); - - FMT2(R32_UINT, R32Uint); - FMT2(R32_SINT, R32Sint); - FMT2(R32_SFLOAT, R32Float); - FMT2(R32G32_UINT, RG32Uint); - FMT2(R32G32_SINT, RG32Sint); - FMT2(R32G32_SFLOAT, RG32Float); - FMT2(R32G32B32A32_UINT, RGBA32Uint); - FMT2(R32G32B32A32_SINT, RGBA32Sint); - FMT2(R32G32B32A32_SFLOAT, RGBA32Float); - + FMT2(R8_UNORM, R8Unorm); + FMT2(R8_SINT, R8Sint); + FMT2(R8_UINT, R8Uint); + FMT2(R8G8_UNORM, RG8Unorm); + FMT2(R8G8_SINT, RG8Sint); + FMT2(R8G8_UINT, RG8Uint); + FMT2(R8G8B8A8_UNORM, RGBA8Unorm); + FMT2(R8G8B8A8_SINT, RGBA8Sint); + FMT2(R8G8B8A8_UINT, RGBA8Uint); + FMT2(R8G8B8A8_SRGB, RGBA8Unorm_sRGB); + + FMT2(A2B10G10R10_UNORM_PACK32, RGB10A2Unorm); + FMT2(A2B10G10R10_UINT_PACK32, RGB10A2Uint); + + FMT2(R16_UINT, R16Uint); + FMT2(R16_SINT, R16Sint); + FMT2(R16_SFLOAT, R16Float); + FMT2(R16G16_UINT, RG16Uint); + FMT2(R16G16_SINT, RG16Sint); + FMT2(R16G16_SFLOAT, RG16Float); + FMT2(R16G16B16A16_UINT, RGBA16Uint); + FMT2(R16G16B16A16_SINT, RGBA16Sint); + FMT2(R16G16B16A16_SFLOAT, RGBA16Float); + + FMT2(R32_UINT, R32Uint); + FMT2(R32_SINT, R32Sint); + FMT2(R32_SFLOAT, R32Float); + FMT2(R32G32_UINT, RG32Uint); + FMT2(R32G32_SINT, RG32Sint); + FMT2(R32G32_SFLOAT, RG32Float); + FMT2(R32G32B32A32_UINT, RGBA32Uint); + FMT2(R32G32B32A32_SINT, RGBA32Sint); + FMT2(R32G32B32A32_SFLOAT, RGBA32Float); + case SLANG_FORMAT_UNKNOWN: default: break; @@ -1031,10 +1499,10 @@ MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt) { case MTLPixelFormatRGBA8Unorm: return MTLPixelFormatBGRA8Unorm; - + case MTLPixelFormatRGBA8Unorm_sRGB: return MTLPixelFormatBGRA8Unorm_sRGB; - + default: return fmt; } diff --git a/gfx/common/sixel_common.h b/gfx/common/sixel_common.h new file mode 100644 index 0000000000..5d6e40ced1 --- /dev/null +++ b/gfx/common/sixel_common.h @@ -0,0 +1,34 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * copyright (c) 2011-2017 - Daniel De Matteis + * copyright (c) 2016-2017 - Brad Parker + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __SIXEL_COMMON_H +#define __SIXEL_COMMON_H + +#include + +#define SIXEL_COLORS 256 + +typedef struct sixel +{ + SIXELSTATUS sixel_status; + unsigned video_width; + unsigned video_height; + unsigned screen_width; + unsigned screen_height; +} sixel_t; + +#endif diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 5725ed6883..31c7f3d620 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -43,6 +43,29 @@ static VkInstance cached_instance_vk; static VkDevice cached_device_vk; static retro_vulkan_destroy_device_t cached_destroy_device_vk; +//#define WSI_HARDENING_TEST +#ifdef WSI_HARDENING_TEST +static unsigned wsi_harden_counter = 0; +static unsigned wsi_harden_counter2 = 0; + +static void trigger_spurious_error_vkresult(VkResult *res) +{ + ++wsi_harden_counter; + if ((wsi_harden_counter & 15) == 12) + *res = VK_ERROR_OUT_OF_DATE_KHR; + else if ((wsi_harden_counter & 31) == 13) + *res = VK_ERROR_OUT_OF_DATE_KHR; + else if ((wsi_harden_counter & 15) == 6) + *res = VK_ERROR_SURFACE_LOST_KHR; +} + +static bool trigger_spurious_error(void) +{ + ++wsi_harden_counter2; + return ((wsi_harden_counter2 & 15) == 9) || ((wsi_harden_counter2 & 15) == 10); +} +#endif + #ifdef VULKAN_DEBUG static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_debug_cb( VkDebugReportFlagsEXT flags, @@ -2278,6 +2301,32 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk, return true; } +static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk) +{ + unsigned i; + + if (vk->swapchain != VK_NULL_HANDLE) + { + vkDeviceWaitIdle(vk->context.device); + vkDestroySwapchainKHR(vk->context.device, vk->swapchain, NULL); + memset(vk->context.swapchain_images, 0, sizeof(vk->context.swapchain_images)); + vk->swapchain = VK_NULL_HANDLE; + } + + for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++) + { + if (vk->context.swapchain_semaphores[i] != VK_NULL_HANDLE) + vkDestroySemaphore(vk->context.device, + vk->context.swapchain_semaphores[i], NULL); + if (vk->context.swapchain_fences[i] != VK_NULL_HANDLE) + vkDestroyFence(vk->context.device, + vk->context.swapchain_fences[i], NULL); + } + + memset(vk->context.swapchain_semaphores, 0, sizeof(vk->context.swapchain_semaphores)); + memset(vk->context.swapchain_fences, 0, sizeof(vk->context.swapchain_fences)); +} + void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index) { VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; @@ -2304,10 +2353,14 @@ void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index) #endif err = vkQueuePresentKHR(vk->context.queue, &present); +#ifdef WSI_HARDENING_TEST + trigger_spurious_error_vkresult(&err); +#endif + if (err != VK_SUCCESS || result != VK_SUCCESS) { - RARCH_LOG("[Vulkan]: QueuePresent failed, invalidating swapchain.\n"); - vk->context.invalid_swapchain = true; + RARCH_LOG("[Vulkan]: QueuePresent failed, destroying swapchain.\n"); + vulkan_destroy_swapchain(vk); } #ifdef HAVE_THREADS @@ -2325,12 +2378,8 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk, if (vk->context.device) vkDeviceWaitIdle(vk->context.device); - if (vk->swapchain) - { - vkDestroySwapchainKHR(vk->context.device, - vk->swapchain, NULL); - vk->swapchain = VK_NULL_HANDLE; - } + + vulkan_destroy_swapchain(vk); if (destroy_surface && vk->vk_surface != VK_NULL_HANDLE) { @@ -2339,16 +2388,6 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk, vk->vk_surface = VK_NULL_HANDLE; } - for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++) - { - if (vk->context.swapchain_semaphores[i] != VK_NULL_HANDLE) - vkDestroySemaphore(vk->context.device, - vk->context.swapchain_semaphores[i], NULL); - if (vk->context.swapchain_fences[i] != VK_NULL_HANDLE) - vkDestroyFence(vk->context.device, - vk->context.swapchain_fences[i], NULL); - } - #ifdef VULKAN_DEBUG if (vk->context.debug_callback) vkDestroyDebugReportCallbackEXT(vk->context.instance, vk->context.debug_callback, NULL); @@ -2423,8 +2462,10 @@ void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk) { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; VkSemaphoreCreateInfo sem_info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - bool is_retrying = false; + bool is_retrying = false; + +retry: if (vk->swapchain == VK_NULL_HANDLE) { /* We don't have a swapchain, try to create one now. */ @@ -2441,52 +2482,63 @@ void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk) vk->context.current_swapchain_index = 0; vulkan_acquire_clear_fences(vk); vulkan_acquire_wait_fences(vk); + vk->context.invalid_swapchain = true; return; } } -retry: vkCreateFence(vk->context.device, &fence_info, NULL, &fence); err = vkAcquireNextImageKHR(vk->context.device, vk->swapchain, UINT64_MAX, VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index); - index = vk->context.current_swapchain_index; - if (vk->context.swapchain_semaphores[index] == VK_NULL_HANDLE) - vkCreateSemaphore(vk->context.device, &sem_info, - NULL, &vk->context.swapchain_semaphores[index]); - if (err == VK_SUCCESS) vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX); + +#ifdef WSI_HARDENING_TEST + trigger_spurious_error_vkresult(&err); +#endif + vkDestroyFence(vk->context.device, fence, NULL); - vulkan_acquire_wait_fences(vk); - - if (err != VK_SUCCESS) + if (err == VK_ERROR_OUT_OF_DATE_KHR) { + /* Throw away the old swapchain and try again. */ + vulkan_destroy_swapchain(vk); + if (is_retrying) { - RARCH_ERR("[Vulkan]: Tried acquring next swapchain image after creating new one, but failed ...\n"); + RARCH_ERR("[Vulkan]: Swapchain is out of date, trying to create new one. Have tried multiple times ...\n"); + retro_sleep(10); } else - { - RARCH_LOG("[Vulkan]: AcquireNextImage failed, invalidating swapchain.\n"); - vk->context.invalid_swapchain = true; - - RARCH_LOG("[Vulkan]: AcquireNextImage failed, so trying to recreate swapchain.\n"); - if (!vulkan_create_swapchain(vk, vk->context.swapchain_width, - vk->context.swapchain_height, vk->context.swap_interval)) - { - RARCH_ERR("[Vulkan]: Failed to create new swapchain.\n"); - } - else - { - is_retrying = true; - goto retry; - } - } + RARCH_ERR("[Vulkan]: Swapchain is out of date, trying to create new one.\n"); + is_retrying = true; + vulkan_acquire_clear_fences(vk); + goto retry; } + else if (err != VK_SUCCESS) + { + /* We are screwed, don't try anymore. Maybe it will work later. */ + vulkan_destroy_swapchain(vk); + RARCH_ERR("[Vulkan]: Failed to acquire from swapchain (err = %d).\n", + (int)err); + if (err == VK_ERROR_SURFACE_LOST_KHR) + RARCH_ERR("[Vulkan]: Got VK_ERROR_SURFACE_LOST_KHR.\n"); + /* Force driver to reset swapchain image handles. */ + vk->context.invalid_swapchain = true; + vulkan_acquire_clear_fences(vk); + return; + } + + index = vk->context.current_swapchain_index; + if (vk->context.swapchain_semaphores[index] == VK_NULL_HANDLE) + { + vkCreateSemaphore(vk->context.device, &sem_info, + NULL, &vk->context.swapchain_semaphores[index]); + } + vulkan_acquire_wait_fences(vk); } bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, @@ -2511,6 +2563,7 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, VkCompositeAlphaFlagBitsKHR composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; vkDeviceWaitIdle(vk->context.device); + vulkan_acquire_clear_fences(vk); vk->created_new_swapchain = true; if (vk->swapchain != VK_NULL_HANDLE && @@ -2613,10 +2666,8 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, else swapchain_size = surface_properties.currentExtent; -#if 0 - /* Tests for deferred creation. */ - static unsigned retry_count = 0; - if (++retry_count < 50) +#ifdef WSI_HARDENING_TEST + if (trigger_spurious_error()) { surface_properties.maxImageExtent.width = 0; surface_properties.maxImageExtent.height = 0; @@ -2657,11 +2708,7 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, * We hard sync against the swapchain, so if we have 2 images, * we would be unable to overlap CPU and GPU, which can get very slow * for GPU-rendered cores. */ - desired_swapchain_images = 3; - - /* Limit latency. */ - if (desired_swapchain_images > settings->uints.video_max_swapchain_images) - desired_swapchain_images = settings->uints.video_max_swapchain_images; + desired_swapchain_images = settings->uints.video_max_swapchain_images; /* Clamp images requested to what is supported by the implementation. */ if (desired_swapchain_images < surface_properties.minImageCount) @@ -2763,8 +2810,7 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, RARCH_LOG("[Vulkan]: Got %u swapchain images.\n", vk->context.num_swapchain_images); - vulkan_acquire_clear_fences(vk); + /* Force driver to reset swapchain image handles. */ vk->context.invalid_swapchain = true; - return true; } diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index a95ee155c9..69f82746f6 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -56,6 +56,7 @@ #include "../../input/input_keymaps.h" #include "../video_thread_wrapper.h" #include "../video_display_server.h" +#include "../../gfx/video_driver.h" #include #ifdef HAVE_MENU @@ -92,78 +93,6 @@ extern void *dinput_wgl; extern void *dinput; #endif -typedef enum { - DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED_CUSTOM = 0, - DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE_CUSTOM = 1, - DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_CUSTOM = 2, - DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_UPPERFIELDFIRST_CUSTOM = DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_CUSTOM, - DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_LOWERFIELDFIRST_CUSTOM = 3, - DISPLAYCONFIG_SCANLINE_ORDERING_FORCE_UINT32_CUSTOM = 0xFFFFFFFF -} DISPLAYCONFIG_SCANLINE_ORDERING_CUSTOM; - -typedef enum { - DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE_CUSTOM = 1, - DISPLAYCONFIG_MODE_INFO_TYPE_TARGET_CUSTOM = 2, - DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE_CUSTOM = 3, - DISPLAYCONFIG_MODE_INFO_TYPE_FORCE_UINT32_CUSTOM = 0xFFFFFFFF -} DISPLAYCONFIG_MODE_INFO_TYPE_CUSTOM; - -typedef enum { - DISPLAYCONFIG_PIXELFORMAT_8BPP_CUSTOM = 1, - DISPLAYCONFIG_PIXELFORMAT_16BPP_CUSTOM = 2, - DISPLAYCONFIG_PIXELFORMAT_24BPP_CUSTOM = 3, - DISPLAYCONFIG_PIXELFORMAT_32BPP_CUSTOM = 4, - DISPLAYCONFIG_PIXELFORMAT_NONGDI_CUSTOM = 5, - DISPLAYCONFIG_PIXELFORMAT_FORCE_UINT32_CUSTOM = 0xffffffff -} DISPLAYCONFIG_PIXELFORMAT_CUSTOM; - -typedef enum { - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER_CUSTOM = -1, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15_CUSTOM = 0, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SVIDEO_CUSTOM = 1, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPOSITE_VIDEO_CUSTOM = 2, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPONENT_VIDEO_CUSTOM = 3, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI_CUSTOM = 4, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI_CUSTOM = 5, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_LVDS_CUSTOM = 6, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_D_JPN_CUSTOM = 8, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDI_CUSTOM = 9, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL_CUSTOM = 10, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED_CUSTOM = 11, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EXTERNAL_CUSTOM = 12, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED_CUSTOM = 13, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDTVDONGLE_CUSTOM = 14, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_MIRACAST_CUSTOM = 15, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL_CUSTOM = 0x80000000, - DISPLAYCONFIG_OUTPUT_TECHNOLOGY_FORCE_UINT32_CUSTOM = 0xFFFFFFFF -} DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY_CUSTOM; - -typedef enum { - DISPLAYCONFIG_ROTATION_IDENTITY_CUSTOM = 1, - DISPLAYCONFIG_ROTATION_ROTATE90_CUSTOM = 2, - DISPLAYCONFIG_ROTATION_ROTATE180_CUSTOM = 3, - DISPLAYCONFIG_ROTATION_ROTATE270_CUSTOM = 4, - DISPLAYCONFIG_ROTATION_FORCE_UINT32_CUSTOM = 0xFFFFFFFF -} DISPLAYCONFIG_ROTATION_CUSTOM; - -typedef enum { - DISPLAYCONFIG_SCALING_IDENTITY_CUSTOM = 1, - DISPLAYCONFIG_SCALING_CENTERED_CUSTOM = 2, - DISPLAYCONFIG_SCALING_STRETCHED_CUSTOM = 3, - DISPLAYCONFIG_SCALING_ASPECTRATIOCENTEREDMAX_CUSTOM = 4, - DISPLAYCONFIG_SCALING_CUSTOM_CUSTOM = 5, - DISPLAYCONFIG_SCALING_PREFERRED_CUSTOM = 128, - DISPLAYCONFIG_SCALING_FORCE_UINT32_CUSTOM = 0xFFFFFFFF -} DISPLAYCONFIG_SCALING_CUST; - -typedef enum { - DISPLAYCONFIG_TOPOLOGY_INTERNAL_CUSTOM = 0x00000001, - DISPLAYCONFIG_TOPOLOGY_CLONE_CUSTOM = 0x00000002, - DISPLAYCONFIG_TOPOLOGY_EXTEND_CUSTOM = 0x00000004, - DISPLAYCONFIG_TOPOLOGY_EXTERNAL_CUSTOM = 0x00000008, - DISPLAYCONFIG_TOPOLOGY_FORCE_UINT32_CUSTOM = 0xFFFFFFFF -} DISPLAYCONFIG_TOPOLOGY_ID_CUSTOM; - typedef struct DISPLAYCONFIG_RATIONAL_CUSTOM { UINT32 Numerator; UINT32 Denominator; @@ -188,7 +117,7 @@ typedef struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO_CUSTOM { } AdditionalSignalInfo; UINT32 videoStandard; }; - DISPLAYCONFIG_SCANLINE_ORDERING_CUSTOM scanLineOrdering; + UINT32 scanLineOrdering; } DISPLAYCONFIG_VIDEO_SIGNAL_INFO_CUSTOM; typedef struct DISPLAYCONFIG_TARGET_MODE_CUSTOM { @@ -217,12 +146,12 @@ typedef struct DISPLAYCONFIG_DESKTOP_IMAGE_INFO_CUSTOM { typedef struct DISPLAYCONFIG_SOURCE_MODE_CUSTOM { UINT32 width; UINT32 height; - DISPLAYCONFIG_PIXELFORMAT_CUSTOM pixelFormat; + UINT32 pixelFormat; POINTL position; } DISPLAYCONFIG_SOURCE_MODE_CUSTOM; typedef struct DISPLAYCONFIG_MODE_INFO_CUSTOM { - DISPLAYCONFIG_MODE_INFO_TYPE_CUSTOM infoType; + UINT32 infoType; UINT32 id; LUID adapterId; union { @@ -242,13 +171,13 @@ typedef struct DISPLAYCONFIG_PATH_TARGET_INFO_CUSTOM { UINT32 targetModeInfoIdx :16; }; }; - DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY_CUSTOM outputTechnology; - DISPLAYCONFIG_ROTATION_CUSTOM rotation; - DISPLAYCONFIG_SCALING_CUST scaling; - DISPLAYCONFIG_RATIONAL_CUSTOM refreshRate; - DISPLAYCONFIG_SCANLINE_ORDERING_CUSTOM scanLineOrdering; - BOOL targetAvailable; - UINT32 statusFlags; + UINT32 outputTechnology; + UINT32 rotation; + UINT32 scaling; + DISPLAYCONFIG_RATIONAL_CUSTOM refreshRate; + UINT32 scanLineOrdering; + BOOL targetAvailable; + UINT32 statusFlags; } DISPLAYCONFIG_PATH_TARGET_INFO_CUSTOM; @@ -259,7 +188,7 @@ typedef struct DISPLAYCONFIG_PATH_INFO_CUSTOM { UINT32 flags; } DISPLAYCONFIG_PATH_INFO_CUSTOM; -typedef LONG (WINAPI *QUERYDISPLAYCONFIG)(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO_CUSTOM*, UINT32*, DISPLAYCONFIG_MODE_INFO_CUSTOM*, DISPLAYCONFIG_TOPOLOGY_ID_CUSTOM*); +typedef LONG (WINAPI *QUERYDISPLAYCONFIG)(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO_CUSTOM*, UINT32*, DISPLAYCONFIG_MODE_INFO_CUSTOM*, UINT32*); typedef LONG (WINAPI *GETDISPLAYCONFIGBUFFERSIZES)(UINT32, UINT32*, UINT32*); static bool g_win32_resized = false; @@ -1117,12 +1046,14 @@ void win32_check_window(bool *quit, bool *resize, unsigned *width, unsigned *height) { #if !defined(_XBOX) - const ui_application_t *application = - ui_companion_driver_get_application_ptr(); - if (application) - application->process_events(); + if (video_driver_is_threaded()) + { + const ui_application_t *application = + ui_companion_driver_get_application_ptr(); + if (application) + application->process_events(); + } *quit = g_win32_quit; -#endif if (g_win32_resized) { @@ -1131,6 +1062,7 @@ void win32_check_window(bool *quit, bool *resize, *height = g_win32_resize_height; g_win32_resized = false; } +#endif } bool win32_suppress_screensaver(void *data, bool enable) @@ -1261,7 +1193,7 @@ void win32_set_window(unsigned *width, unsigned *height, settings_t *settings = config_get_ptr(); const ui_window_t *window = ui_companion_driver_get_window_ptr(); - if (!fullscreen && settings->bools.ui_menubar_enable) + if (!fullscreen && settings->bools.ui_menubar_enable && !video_driver_is_threaded()) { RECT rc_temp; rc_temp.left = 0; @@ -1458,23 +1390,23 @@ float win32_get_refresh_rate(void *data) float refresh_rate = 0.0f; #if _WIN32_WINNT >= 0x0601 || _WIN32_WINDOWS >= 0x0601 /* Win 7 */ OSVERSIONINFO version_info; - DISPLAYCONFIG_TOPOLOGY_ID_CUSTOM TopologyID; + UINT32 TopologyID; unsigned int NumPathArrayElements = 0; unsigned int NumModeInfoArrayElements = 0; DISPLAYCONFIG_PATH_INFO_CUSTOM *PathInfoArray = NULL; DISPLAYCONFIG_MODE_INFO_CUSTOM *ModeInfoArray = NULL; int result = 0; #ifdef HAVE_DYNAMIC - static QUERYDISPLAYCONFIG pQueryDisplayConfig; - static GETDISPLAYCONFIGBUFFERSIZES pGetDisplayConfigBufferSizes; - if (!pQueryDisplayConfig) - pQueryDisplayConfig = (QUERYDISPLAYCONFIG)GetProcAddress(GetModuleHandle("user32.dll"), "QueryDisplayConfig"); + static QUERYDISPLAYCONFIG pQueryDisplayConfig; + static GETDISPLAYCONFIGBUFFERSIZES pGetDisplayConfigBufferSizes; + if (!pQueryDisplayConfig) + pQueryDisplayConfig = (QUERYDISPLAYCONFIG)GetProcAddress(GetModuleHandle("user32.dll"), "QueryDisplayConfig"); - if (!pGetDisplayConfigBufferSizes) - pGetDisplayConfigBufferSizes = (GETDISPLAYCONFIGBUFFERSIZES)GetProcAddress(GetModuleHandle("user32.dll"), "GetDisplayConfigBufferSizes"); + if (!pGetDisplayConfigBufferSizes) + pGetDisplayConfigBufferSizes = (GETDISPLAYCONFIGBUFFERSIZES)GetProcAddress(GetModuleHandle("user32.dll"), "GetDisplayConfigBufferSizes"); #else - static QUERYDISPLAYCONFIG pQueryDisplayConfig = QueryDisplayConfig; - static GETDISPLAYCONFIGBUFFERSIZES pGetDisplayConfigBufferSizes = GetDisplayConfigBufferSizes; + static QUERYDISPLAYCONFIG pQueryDisplayConfig = QueryDisplayConfig; + static GETDISPLAYCONFIGBUFFERSIZES pGetDisplayConfigBufferSizes = GetDisplayConfigBufferSizes; #endif version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); diff --git a/gfx/display_servers/dispserv_win32.c b/gfx/display_servers/dispserv_win32.c index 243b0ecf2b..87f0253409 100644 --- a/gfx/display_servers/dispserv_win32.c +++ b/gfx/display_servers/dispserv_win32.c @@ -68,6 +68,7 @@ be received by your application before it calls any ITaskbarList3 method. static unsigned win32_orig_width = 0; static unsigned win32_orig_height = 0; +static unsigned win32_orig_refresh = 0; static void* win32_display_server_init(void) { @@ -106,7 +107,7 @@ static void win32_display_server_destroy(void *data) if (win32_orig_width > 0 && win32_orig_height > 0 ) video_display_server_switch_resolution(win32_orig_width, win32_orig_height, - 60, 60); + win32_orig_refresh , (float)win32_orig_refresh ); #ifdef HAS_TASKBAR_EXT if (g_taskbarList && win32_taskbar_is_created()) @@ -214,14 +215,15 @@ static bool win32_display_server_set_resolution(void *data, if (!serv) return false; + + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevmode); if (win32_orig_width == 0) win32_orig_width = GetSystemMetrics(SM_CXSCREEN); + win32_orig_refresh = curDevmode.dmDisplayFrequency; if (win32_orig_height == 0) win32_orig_height = GetSystemMetrics(SM_CYSCREEN); - EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevmode); - /* Used to stop super resolution bug */ if (width == curDevmode.dmPelsWidth) width = 0; diff --git a/gfx/display_servers/dispserv_x11.c b/gfx/display_servers/dispserv_x11.c index aa160a7242..aeb45e71f0 100644 --- a/gfx/display_servers/dispserv_x11.c +++ b/gfx/display_servers/dispserv_x11.c @@ -109,11 +109,6 @@ static bool x11_set_resolution(void *data, video_monitor_set_refresh_rate(hz); /* following code is the mode line genorator */ - if (width < 300) - { - width = width*2; - crt_aspect_ratio_switch(width, height); - } hfp = width+8; hbp = width*1.32; diff --git a/gfx/drivers/caca_gfx.c b/gfx/drivers/caca_gfx.c index a9405a6be1..b245502cb7 100644 --- a/gfx/drivers/caca_gfx.c +++ b/gfx/drivers/caca_gfx.c @@ -363,4 +363,5 @@ video_driver_t video_caca = { NULL, /* overlay_interface */ #endif caca_gfx_get_poke_interface, + NULL /* wrap_type_to_enum */ }; diff --git a/gfx/drivers/gl_shaders/core_pipeline_bokeh.glsl.frag.h b/gfx/drivers/gl_shaders/core_pipeline_bokeh.glsl.frag.h new file mode 100644 index 0000000000..94a9360c60 --- /dev/null +++ b/gfx/drivers/gl_shaders/core_pipeline_bokeh.glsl.frag.h @@ -0,0 +1,33 @@ +#include "shaders_common.h" + +static const char* stock_fragment_xmb_bokeh_core = GLSL( + uniform float time; + uniform vec2 OutputSize; + out vec4 FragColor; + + void main(void) + { + float speed = time * 4.0; + vec2 uv = -1.0 + 2.0 * gl_FragCoord.xy / OutputSize.xy; + uv.x *= OutputSize.x / OutputSize.y; + vec3 color = vec3(0.0); + + for( int i=0; i < 8; i++ ) + { + float pha = sin(float(i) * 546.13 + 1.0) * 0.5 + 0.5; + float siz = pow(sin(float(i) * 651.74 + 5.0) * 0.5 + 0.5, 4.0); + float pox = sin(float(i) * 321.55 + 4.1) * OutputSize.x / OutputSize.y; + float rad = 0.1 + 0.5 * siz + sin(pha + siz) / 4.0; + vec2 pos = vec2(pox + sin(speed / 15. + pha + siz), - 1.0 - rad + (2.0 + 2.0 * rad) * fract(pha + 0.3 * (speed / 7.) * (0.2 + 0.8 * siz))); + float dis = length(uv - pos); + if(dis < rad) + { + vec3 col = mix(vec3(0.194 * sin(speed / 6.0) + 0.3, 0.2, 0.3 * pha), vec3(1.1 * sin(speed / 9.0) + 0.3, 0.2 * pha, 0.4), 0.5 + 0.5 * sin(float(i))); + color += col.zyx * (1.0 - smoothstep(rad * 0.15, rad, dis)); + } + } + color *= sqrt(1.5 - 0.5 * length(uv)); + FragColor = vec4(color.r, color.g, color.b , 0.5); + } + +); diff --git a/gfx/drivers/gl_shaders/core_pipeline_snow.glsl.frag.h b/gfx/drivers/gl_shaders/core_pipeline_snow.glsl.frag.h new file mode 100644 index 0000000000..b7c61c620d --- /dev/null +++ b/gfx/drivers/gl_shaders/core_pipeline_snow.glsl.frag.h @@ -0,0 +1,72 @@ +#include "shaders_common.h" + +static const char* stock_fragment_xmb_snow_core = GLSL( + uniform float time; + uniform vec2 OutputSize; + out vec4 FragColor; + + float baseScale = 3.5; /* [1.0 .. 10.0] */ + float density = 0.7; /* [0.01 .. 1.0] */ + float speed = 0.25; /* [0.1 .. 1.0] */ + + float rand(vec2 co) + { + return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); + } + + float dist_func(vec2 distv) + { + float dist = sqrt((distv.x * distv.x) + (distv.y * distv.y)) * (40.0 / baseScale); + dist = clamp(dist, 0.0, 1.0); + return cos(dist * (3.14159265358 * 0.5)) * 0.5; + } + + float random_dots(vec2 co) + { + float part = 1.0 / 20.0; + vec2 cd = floor(co / part); + float p = rand(cd); + + if (p > 0.005 * (density * 40.0)) + return 0.0; + + vec2 dpos = (vec2(fract(p * 2.0) , p) + vec2(2.0, 2.0)) * 0.25; + + vec2 cellpos = fract(co / part); + vec2 distv = (cellpos - dpos); + + return dist_func(distv); + } + + float snow(vec2 pos, float time, float scale) + { + /* add wobble */ + pos.x += cos(pos.y * 1.2 + time * 3.14159 * 2.0 + 1.0 / scale) / (8.0 / scale) * 4.0; + /* add gravity */ + pos += time * scale * vec2(-0.5, 1.0) * 4.0; + return random_dots(pos / scale) * (scale * 0.5 + 0.5); + } + + void main(void) + { + float tim = time * 0.4 * speed; + vec2 pos = gl_FragCoord.xy / OutputSize.xx; + float a = 0.0; + /** + * Each of these is a layer of snow + * Remove some for better performance + * Changing the scale (3rd value) will mess with the looping + **/ + a += snow(pos, tim, 1.0); + a += snow(pos, tim, 0.7); + a += snow(pos, tim, 0.6); + a += snow(pos, tim, 0.5); + a += snow(pos, tim, 0.4); + a += snow(pos, tim, 0.3); + a += snow(pos, tim, 0.25); + a += snow(pos, tim, 0.125); + a = a * min(pos.y * 4.0, 1.0); + FragColor = vec4(1.0, 1.0, 1.0, a); + } + +); diff --git a/gfx/drivers/gl_shaders/core_pipeline_snow_simple.glsl.frag.h b/gfx/drivers/gl_shaders/core_pipeline_snow_simple.glsl.frag.h new file mode 100644 index 0000000000..7368542409 --- /dev/null +++ b/gfx/drivers/gl_shaders/core_pipeline_snow_simple.glsl.frag.h @@ -0,0 +1,72 @@ +#include "shaders_common.h" + +static const char *stock_fragment_xmb_simple_snow_core = GLSL( + uniform float time; + uniform vec2 OutputSize; + out vec4 FragColor; + + float baseScale = 1.25; /* [1.0 .. 10.0] */ + float density = 0.5; /* [0.01 .. 1.0] */ + float speed = 0.15; /* [0.1 .. 1.0] */ + + float rand(vec2 co) + { + return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); + } + + float dist_func(vec2 distv) + { + float dist = sqrt((distv.x * distv.x) + (distv.y * distv.y)) * (40.0 / baseScale); + dist = clamp(dist, 0.0, 1.0); + return cos(dist * (3.14159265358 * 0.5)) * 0.5; + } + + float random_dots(vec2 co) + { + float part = 1.0 / 20.0; + vec2 cd = floor(co / part); + float p = rand(cd); + + if (p > 0.005 * (density * 40.0)) + return 0.0; + + vec2 dpos = (vec2(fract(p * 2.0) , p) + vec2(2.0, 2.0)) * 0.25; + + vec2 cellpos = fract(co / part); + vec2 distv = (cellpos - dpos); + + return dist_func(distv); + } + + float snow(vec2 pos, float time, float scale) + { + /* add wobble */ + pos.x += cos(pos.y * 1.2 + time * 3.14159 * 2.0 + 1.0 / scale) / (8.0 / scale) * 4.0; + /* add gravity */ + pos += time * scale * vec2(-0.5, 1.0) * 4.0; + return random_dots(pos / scale) * (scale * 0.5 + 0.5); + } + + void main(void) + { + float tim = time * 0.4 * speed; + vec2 pos = gl_FragCoord.xy / OutputSize.xx; + float a = 0.0; + /** + * Each of these is a layer of snow + * Remove some for better performance + * Changing the scale (3rd value) will mess with the looping + **/ + a += snow(pos, tim, 1.0); + a += snow(pos, tim, 0.7); + a += snow(pos, tim, 0.6); + a += snow(pos, tim, 0.5); + a += snow(pos, tim, 0.4); + a += snow(pos, tim, 0.3); + a += snow(pos, tim, 0.25); + a += snow(pos, tim, 0.125); + a = a * min(pos.y * 4.0, 1.0); + FragColor = vec4(1.0, 1.0, 1.0, a); + } + +); diff --git a/gfx/drivers/gl_shaders/core_pipeline_snowflake.glsl.frag.h b/gfx/drivers/gl_shaders/core_pipeline_snowflake.glsl.frag.h new file mode 100644 index 0000000000..99620f3ec7 --- /dev/null +++ b/gfx/drivers/gl_shaders/core_pipeline_snowflake.glsl.frag.h @@ -0,0 +1,77 @@ +/* credits to: TheTimJames + https://www.shadertoy.com/view/Md2GRw +*/ + +#include "shaders_common.h" + +static const char* stock_fragment_xmb_snowflake_core = GLSL( + uniform float time; + uniform vec2 OutputSize; + vec2 uv; + out vec4 FragColor; + + float atime; + + float rand(vec2 co) + { + return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); + } + + float rand_float(float x) + { + return rand(vec2(x, 1.0)); + } + + float snow(vec3 pos, vec2 uv, float o) + { + vec2 d = (pos.xy - uv); + float a = atan(d.y,d.x) + sin(time*1.0 + o) * 10.0; + + float dist = d.x*d.x + d.y*d.y; + + if(dist < pos.z/400.0) + { + float col = 0.0; + if(sin(a * 8.0) < 0.0) + { + col=1.0; + } + if(dist < pos.z/800.0) + { + col+=1.0; + } + return col * pos.z; + } + + return 0.0; + } + + float col(vec2 c) + { + float color = 0.0; + for (int i = 1; i < 15; i++) + { + float o = rand_float(float(i) / 3.0) * 15.0; + float z = rand_float(float(i) + 13.0); + float x = 1.8 - (3.6) * (rand_float(floor((time*((z + 1.0) / 2.0) +o) / 2.0)) + sin(time * o /1000.0) / 10.0); + float y = 1.0 - mod((time * ((z + 1.0)/2.0)) + o, 2.0); + + color += snow(vec3(x,y,z), c, o); + } + + return color; + } + + void main(void) + { + uv = gl_FragCoord.xy / OutputSize.xy; + uv = uv * 2.0 - 1.0; + vec2 p = uv; + p.x *= OutputSize.x / OutputSize.y; + + atime = (time + 1.0) / 4.0; + + FragColor = vec4(col(p)); + } + +); diff --git a/gfx/drivers/gl_shaders/core_pipeline_xmb_ribbon_simple.glsl.frag.h b/gfx/drivers/gl_shaders/core_pipeline_xmb_ribbon_simple.glsl.frag.h new file mode 100644 index 0000000000..1c1404199c --- /dev/null +++ b/gfx/drivers/gl_shaders/core_pipeline_xmb_ribbon_simple.glsl.frag.h @@ -0,0 +1,11 @@ +#include "shaders_common.h" + +static const char *stock_fragment_xmb_ribbon_simple_core = GLSL( + uniform float time; + out vec4 FragColor; + + void main() + { + FragColor = vec4(0.05, 0.05, 0.05, 1.0); + } +); diff --git a/gfx/drivers/gl_shaders/modern_pipeline_snow.glsl.vert.h b/gfx/drivers/gl_shaders/pipeline_snow.glsl.vert.h similarity index 88% rename from gfx/drivers/gl_shaders/modern_pipeline_snow.glsl.vert.h rename to gfx/drivers/gl_shaders/pipeline_snow.glsl.vert.h index 405ab02e96..0ebbf254dd 100644 --- a/gfx/drivers/gl_shaders/modern_pipeline_snow.glsl.vert.h +++ b/gfx/drivers/gl_shaders/pipeline_snow.glsl.vert.h @@ -2,7 +2,7 @@ /* Need to duplicate these to work around broken stuff on Android. * Must enforce alpha = 1.0 or 32-bit games can potentially go black. */ -static const char *stock_vertex_xmb_snow_modern = GLSL( +static const char *stock_vertex_xmb_snow = GLSL( attribute vec2 TexCoord; attribute vec2 VertexCoord; attribute vec4 Color; diff --git a/gfx/drivers/gl_shaders/pipeline_snow_core.glsl.vert.h b/gfx/drivers/gl_shaders/pipeline_snow_core.glsl.vert.h new file mode 100644 index 0000000000..cae4002833 --- /dev/null +++ b/gfx/drivers/gl_shaders/pipeline_snow_core.glsl.vert.h @@ -0,0 +1,16 @@ +#include "shaders_common.h" + +/* Need to duplicate these to work around broken stuff on Android. + * Must enforce alpha = 1.0 or 32-bit games can potentially go black. */ +static const char *stock_vertex_xmb_snow_core = GLSL( + in vec2 TexCoord; + in vec2 VertexCoord; + in vec4 Color; + uniform mat4 MVPMatrix; + out vec2 tex_coord; + + void main() { + gl_Position = MVPMatrix * vec4(VertexCoord, 0.0, 1.0); + tex_coord = TexCoord; + } +); diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index eb18c55670..d788d09bfa 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -46,36 +46,29 @@ #import "../video_coord_array.h" +static bool metal_set_shader(void *data, + enum rarch_shader_type type, const char *path); + static void *metal_init(const video_info_t *video, const input_driver_t **input, void **input_data) { - gfx_ctx_mode_t mode; - [apple_platform setViewType:APPLE_VIEW_TYPE_METAL]; - MetalDriver *md = [MetalDriver new]; - if (md == nil) { + + MetalDriver *md = [[MetalDriver alloc] initWithVideo:video input:input inputData:input_data]; + if (md == nil) + { return NULL; } - MetalView *view = (MetalView *)apple_platform.renderView; - view.delegate = md; - - md.keepAspect = video->force_aspect; - - RARCH_LOG("[Metal]: Detecting screen resolution %ux%u.\n", video->width, video->height); - - mode.width = video->width; - mode.height = video->height; - mode.fullscreen = video->fullscreen; - - [md setVideo:video]; - [apple_platform setVideoMode:mode]; - - *input = NULL; - *input_data = NULL; - font_driver_init_osd((__bridge_retained void *)md, false, video->is_threaded, FONT_DRIVER_RENDER_METAL_API); - + const char *shader_path = retroarch_get_shader_preset(); + + if (shader_path) + { + enum rarch_shader_type type = video_shader_parse_type(shader_path, RARCH_SHADER_SLANG); + metal_set_shader(((__bridge void *)md), type, shader_path); + } + return (__bridge_retained void *)md; } @@ -85,46 +78,19 @@ static bool metal_frame(void *data, const void *frame, unsigned pitch, const char *msg, video_frame_info_t *video_info) { MetalDriver *md = (__bridge MetalDriver *)data; - @autoreleasepool { - [md beginFrame]; - - FrameView *v = md.frameView; - v.frameCount = frame_count; - v.size = CGSizeMake(frame_width, frame_height); - [v updateFrame:frame pitch:pitch]; - - #if defined(HAVE_MENU) - if (md.menu.enabled) { - menu_driver_frame(video_info); - } - #endif - - [md drawViews]; - - if (video_info->statistics_show) - { - struct font_params* osd_params = (struct font_params*)&video_info->osd_stat_params; - - if (osd_params) - { - font_driver_render_msg(video_info, NULL, video_info->stat_text, - (const struct font_params*)&video_info->osd_stat_params); - } - } - - if (msg && *msg) - { - font_driver_render_msg(video_info, NULL, msg, NULL); - } - - [md endFrame]; - } - - return YES; + return [md renderFrame:frame + width:frame_width + height:frame_height + frameCount:frame_count + pitch:pitch + msg:msg + info:video_info]; } -static void metal_set_nonblock_state(void *data, bool state) +static void metal_set_nonblock_state(void *data, bool non_block) { + MetalDriver *md = (__bridge MetalDriver *)data; + md.context.displaySyncEnabled = !non_block; } static bool metal_alive(void *data) @@ -132,17 +98,20 @@ static bool metal_alive(void *data) return true; } +static bool metal_has_windowed(void *data) +{ + return true; +} + static bool metal_focus(void *data) { return apple_platform.hasFocus; } -static bool metal_suppress_screensaver(void *data, bool enable) +static bool metal_suppress_screensaver(void *data, bool disable) { - bool enabled = enable; - (void)data; - - return video_context_driver_suppress_screensaver(&enabled); + RARCH_LOG("[Metal]: suppress screen saver: %s\n", disable ? "YES" : "NO"); + return [apple_platform setDisableDisplaySleep:disable]; } static bool metal_set_shader(void *data, @@ -154,12 +123,13 @@ static bool metal_set_shader(void *data, return false; if (!path) return true; - - if (type != RARCH_SHADER_SLANG) { + + if (type != RARCH_SHADER_SLANG) + { RARCH_WARN("[Metal] Only .slang or .slangp shaders are supported. Falling back to stock.\n"); return false; } - + return [md.frameView setShaderFromPath:[NSString stringWithUTF8String:path]]; #else return false; @@ -170,13 +140,15 @@ static void metal_free(void *data) { MetalDriver *md = (__bridge_transfer MetalDriver *)data; md = nil; - font_driver_free_osd(); } static void metal_set_viewport(void *data, unsigned viewport_width, unsigned viewport_height, bool force_full, bool allow_rotate) { - RARCH_LOG("[Metal]: set_viewport\n"); +// RARCH_LOG("[Metal]: set_viewport size: %dx%d full: %s rotate: %s\n", +// viewport_width, viewport_height, +// force_full ? "YES" : "NO", +// allow_rotate ? "YES" : "NO"); } static void metal_set_rotation(void *data, unsigned rotation) @@ -194,81 +166,43 @@ static bool metal_read_viewport(void *data, uint8_t *buffer, bool is_idle) return true; } -#ifdef HAVE_OVERLAY - -static const video_overlay_interface_t metal_overlay_interface = { -// metal_overlay_enable, -// metal_overlay_load, -// metal_overlay_tex_geom, -// metal_overlay_vertex_geom, -// metal_overlay_full_screen, -// metal_overlay_set_alpha, -}; - -static void metal_get_overlay_interface(void *data, - const video_overlay_interface_t **iface) -{ - (void)data; - *iface = &metal_overlay_interface; -} - -#endif - static uintptr_t metal_load_texture(void *video_data, void *data, bool threaded, enum texture_filter_type filter_type) { MetalDriver *md = (__bridge MetalDriver *)video_data; - struct texture_image *image = (struct texture_image *)data; - if (!image) + struct texture_image *img = (struct texture_image *)data; + if (!img) return 0; - - if (!image->pixels && !image->width && !image->height) { - /* Create a dummy texture instead. */ -#define T0 0xff000000u -#define T1 0xffffffffu - static const uint32_t checkerboard[] = { - T0, T1, T0, T1, T0, T1, T0, T1, - T1, T0, T1, T0, T1, T0, T1, T0, - T0, T1, T0, T1, T0, T1, T0, T1, - T1, T0, T1, T0, T1, T0, T1, T0, - T0, T1, T0, T1, T0, T1, T0, T1, - T1, T0, T1, T0, T1, T0, T1, T0, - T0, T1, T0, T1, T0, T1, T0, T1, - T1, T0, T1, T0, T1, T0, T1, T0, - }; -#undef T0 -#undef T1 - - } - else { - } - - return 0; + + struct texture_image image = *img; + Texture *t = [md.context newTexture:image filter:filter_type]; + return (uintptr_t)(__bridge_retained void *)(t); } static void metal_unload_texture(void *data, uintptr_t handle) { + if (!handle) + { + return; + } + Texture *t = (__bridge_transfer Texture *)(void *)handle; + t = nil; } static void metal_set_video_mode(void *data, unsigned width, unsigned height, bool fullscreen) { - MetalDriver *md = (__bridge MetalDriver *)data; - gfx_ctx_mode_t mode = { - .width = width, - .height = height, - .fullscreen = fullscreen, - }; - - //[md setVideoMode:mode]; + RARCH_LOG("[Metal]: set_video_mode res=%dx%d fullscreen=%s\n", + width, height, + fullscreen ? "YES" : "NO"); } static float metal_get_refresh_rate(void *data) { MetalDriver *md = (__bridge MetalDriver *)data; (void)md; - + return 0.0f; } @@ -281,27 +215,28 @@ static void metal_set_filtering(void *data, unsigned index, bool smooth) static void metal_set_aspect_ratio(void *data, unsigned aspect_ratio_idx) { MetalDriver *md = (__bridge MetalDriver *)data; - - switch (aspect_ratio_idx) { + + switch (aspect_ratio_idx) + { case ASPECT_RATIO_SQUARE: video_driver_set_viewport_square_pixel(); break; - + case ASPECT_RATIO_CORE: video_driver_set_viewport_core(); break; - + case ASPECT_RATIO_CONFIG: video_driver_set_viewport_config(); break; - + default: break; } - + video_driver_set_aspect_ratio_value( aspectratio_lut[aspect_ratio_idx].value); - + md.keepAspect = YES; [md setNeedsResize]; } @@ -318,7 +253,7 @@ static void metal_set_texture_frame(void *data, const void *frame, { MetalDriver *md = (__bridge MetalDriver *)data; settings_t *settings = config_get_ptr(); - + [md.menu updateWidth:width height:height format:rgb32 ? RPixelFormatBGRA8Unorm : RPixelFormatBGRA4Unorm @@ -332,23 +267,30 @@ static void metal_set_texture_enable(void *data, bool state, bool full_screen) MetalDriver *md = (__bridge MetalDriver *)data; if (!md) return; - + md.menu.enabled = state; //md.menu.fullScreen = full_screen; } +static void metal_set_osd_msg(void *data, + video_frame_info_t *video_info, + const char *msg, + const void *params, void *font) +{ + font_driver_render_msg(video_info, font, msg, (const struct font_params *)params); +} static void metal_show_mouse(void *data, bool state) { [apple_platform setCursorVisible:state]; } -static struct video_shader* metal_get_current_shader(void* data) +static struct video_shader *metal_get_current_shader(void *data) { MetalDriver *md = (__bridge MetalDriver *)data; if (!md) return NULL; - + return md.frameView.shader; } @@ -356,11 +298,11 @@ static struct video_shader* metal_get_current_shader(void* data) static uint32_t metal_get_flags(void *data) { uint32_t flags = 0; - + BIT32_SET(flags, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES); BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION); BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING); - + return flags; } @@ -375,6 +317,7 @@ static const video_poke_interface_t metal_poke_interface = { .apply_state_changes = metal_apply_state_changes, .set_texture_frame = metal_set_texture_frame, .set_texture_enable = metal_set_texture_enable, + .set_osd_msg = metal_set_osd_msg, .show_mouse = metal_show_mouse, .get_current_shader = metal_get_current_shader, }; @@ -386,12 +329,89 @@ static void metal_get_poke_interface(void *data, *iface = &metal_poke_interface; } +#ifdef HAVE_OVERLAY + +static void metal_overlay_enable(void *data, bool state) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return; + md.overlay.enabled = state; +} + +static bool metal_overlay_load(void *data, + const void *images, unsigned num_images) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return NO; + + return [md.overlay loadImages:(const struct texture_image *)images count:num_images]; +} + +static void metal_overlay_tex_geom(void *data, unsigned index, + float x, float y, float w, float h) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return; + + [md.overlay updateTextureCoordsX:x y:y w:w h:h index:index]; +} + +static void metal_overlay_vertex_geom(void *data, unsigned index, + float x, float y, float w, float h) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return; + + [md.overlay updateVertexX:x y:y w:w h:h index:index]; +} + +static void metal_overlay_full_screen(void *data, bool enable) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return; + + md.overlay.fullscreen = enable; +} + +static void metal_overlay_set_alpha(void *data, unsigned index, float mod) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return; + + [md.overlay updateAlpha:mod index:index]; +} + +static const video_overlay_interface_t metal_overlay_interface = { + .enable = metal_overlay_enable, + .load = metal_overlay_load, + .tex_geom = metal_overlay_tex_geom, + .vertex_geom = metal_overlay_vertex_geom, + .full_screen = metal_overlay_full_screen, + .set_alpha = metal_overlay_set_alpha, +}; + +static void metal_get_overlay_interface(void *data, + const video_overlay_interface_t **iface) +{ + (void)data; + *iface = &metal_overlay_interface; +} + +#endif + video_driver_t video_metal = { .init = metal_init, .frame = metal_frame, .set_nonblock_state = metal_set_nonblock_state, .alive = metal_alive, + .has_windowed = metal_has_windowed, .focus = metal_focus, .suppress_screensaver = metal_suppress_screensaver, .set_shader = metal_set_shader, diff --git a/gfx/drivers/sdl2_gfx.c b/gfx/drivers/sdl2_gfx.c index 75f3105e5e..baf468a369 100644 --- a/gfx/drivers/sdl2_gfx.c +++ b/gfx/drivers/sdl2_gfx.c @@ -104,8 +104,9 @@ static void sdl2_init_font(sdl2_video_t *vid, const char *font_path, if (!settings->bools.video_font_enable) return; - if (!font_renderer_create_default((const void**)&vid->font_driver, &vid->font_data, - *font_path ? font_path : NULL, font_size)) + if (!font_renderer_create_default( + &vid->font_driver, &vid->font_data, + *font_path ? font_path : NULL, font_size)) { RARCH_WARN("[SDL]: Could not initialize fonts.\n"); return; diff --git a/gfx/drivers/sdl_gfx.c b/gfx/drivers/sdl_gfx.c index 83ad492680..931f96a926 100644 --- a/gfx/drivers/sdl_gfx.c +++ b/gfx/drivers/sdl_gfx.c @@ -96,7 +96,8 @@ static void sdl_init_font(sdl_video_t *vid, const char *font_path, unsigned font if (!settings->bools.video_font_enable) return; - if (!font_renderer_create_default((const void**)&vid->font_driver, &vid->font, + if (!font_renderer_create_default( + &vid->font_driver, &vid->font, *settings->paths.path_font ? settings->paths.path_font : NULL, settings->floats.video_font_size)) { diff --git a/gfx/drivers/sixel_gfx.c b/gfx/drivers/sixel_gfx.c new file mode 100644 index 0000000000..4777587688 --- /dev/null +++ b/gfx/drivers/sixel_gfx.c @@ -0,0 +1,668 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2018 - Brad Parker + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#ifdef HAVE_MENU +#include "../../menu/menu_driver.h" +#endif + +#include "../font_driver.h" + +#include "../../driver.h" +#include "../../configuration.h" +#include "../../verbosity.h" +#include "../../frontend/frontend_driver.h" +#include "../common/sixel_common.h" + +#ifndef _WIN32 +#define HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_TERMIOS_H +#include +#endif + +#define HAVE_SYS_SELECT_H + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#ifndef SIXEL_PIXELFORMAT_BGRA8888 +#error "Old version of libsixel detected, please upgrade to at least 1.6.0." +#endif + +static unsigned char *sixel_menu_frame = NULL; +static unsigned sixel_menu_width = 0; +static unsigned sixel_menu_height = 0; +static unsigned sixel_menu_pitch = 0; +static unsigned sixel_video_width = 0; +static unsigned sixel_video_height = 0; +static unsigned sixel_video_pitch = 0; +static unsigned sixel_video_bits = 0; +static unsigned sixel_menu_bits = 0; +static double sixel_video_scale = 1; +static bool sixel_rgb32 = false; +static bool sixel_menu_rgb32 = false; +static unsigned *sixel_temp_buf = NULL; + +static int sixel_write(char *data, int size, void *priv) +{ + return fwrite(data, 1, size, (FILE*)priv); +} + +static SIXELSTATUS output_sixel(unsigned char *pixbuf, int width, int height, + int ncolors, int pixelformat) +{ + sixel_output_t *context; + sixel_dither_t *dither; + SIXELSTATUS status; + + context = sixel_output_create(sixel_write, stdout); + dither = sixel_dither_create(ncolors); + status = sixel_dither_initialize(dither, pixbuf, + width, height, + pixelformat, + SIXEL_LARGE_AUTO, + SIXEL_REP_AUTO, + SIXEL_QUALITY_AUTO); + if (SIXEL_FAILED(status)) + return status; + status = sixel_encode(pixbuf, width, height, + pixelformat, dither, context); + if (SIXEL_FAILED(status)) + return status; + sixel_output_unref(context); + sixel_dither_unref(dither); + + return status; +} + +#ifdef HAVE_SYS_IOCTL_H +# ifdef HAVE_TERMIOS_H +static int wait_stdin(int usec) +{ +#ifdef HAVE_SYS_SELECT_H + fd_set rfds; + struct timeval tv; +#endif /* HAVE_SYS_SELECT_H */ + int ret = 0; + +#ifdef HAVE_SYS_SELECT_H + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + FD_ZERO(&rfds); + FD_SET(STDIN_FILENO, &rfds); + ret = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv); +#else + (void) usec; +#endif /* HAVE_SYS_SELECT_H */ + + return ret; +} +# endif +#endif + +static void scroll_on_demand(int pixelheight) +{ +#ifdef HAVE_SYS_IOCTL_H + struct winsize size = {0, 0, 0, 0}; +#endif +#ifdef HAVE_TERMIOS_H + struct termios old_termios; + struct termios new_termios; +#endif + int row = 0; + int col = 0; + int cellheight; + int scroll; + +#ifdef HAVE_SYS_IOCTL_H + ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); + if (size.ws_ypixel <= 0) + { + printf("\033[H\0337"); + return; + } +# ifdef HAVE_TERMIOS_H + /* set the terminal to cbreak mode */ + tcgetattr(STDIN_FILENO, &old_termios); + memcpy(&new_termios, &old_termios, sizeof(old_termios)); + new_termios.c_lflag &= ~(ECHO | ICANON); + new_termios.c_cc[VMIN] = 1; + new_termios.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios); + + /* request cursor position report */ + printf("\033[6n"); + + if (wait_stdin(1000 * 1000) != (-1)) + { + /* wait 1 sec */ + if (scanf("\033[%d;%dR", &row, &col) == 2) + { + cellheight = pixelheight * size.ws_row / size.ws_ypixel + 1; + scroll = cellheight + row - size.ws_row + 1; + printf("\033[%dS\033[%dA", scroll, scroll); + printf("\0337"); + } + else + { + printf("\033[H\0337"); + } + } + + tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_termios); +# else + printf("\033[H\0337"); +# endif /* HAVE_TERMIOS_H */ +#else + printf("\033[H\0337"); +#endif /* HAVE_SYS_IOCTL_H */ +} + +static void *sixel_gfx_init(const video_info_t *video, + const input_driver_t **input, void **input_data) +{ + settings_t *settings = config_get_ptr(); + sixel_t *sixel = (sixel_t*)calloc(1, sizeof(*sixel)); + gfx_ctx_input_t inp; + const gfx_ctx_driver_t *ctx_driver = NULL; + const char *scale_str = NULL; + + *input = NULL; + *input_data = NULL; + + sixel_rgb32 = video->rgb32; + sixel_video_bits = video->rgb32 ? 32 : 16; + + if (video->rgb32) + sixel_video_pitch = video->width * 4; + else + sixel_video_pitch = video->width * 2; + + scale_str = getenv("SIXEL_SCALE"); + + if (scale_str) + { + sixel_video_scale = atof(scale_str); + + /* just in case the conversion fails, pick something sane */ + if (!sixel_video_scale) + sixel_video_scale = 1.0; + } + + ctx_driver = video_context_driver_init_first(sixel, + settings->arrays.video_context_driver, + GFX_CTX_SIXEL_API, 1, 0, false); + + if (!ctx_driver) + goto error; + + video_context_driver_set((const gfx_ctx_driver_t*)ctx_driver); + + RARCH_LOG("[SIXEL]: Found SIXEL context: %s\n", ctx_driver->ident); + + inp.input = input; + inp.input_data = input_data; + + video_context_driver_input_driver(&inp); + + if (settings->bools.video_font_enable) + font_driver_init_osd(sixel, false, + video->is_threaded, + FONT_DRIVER_RENDER_SIXEL); + + RARCH_LOG("[SIXEL]: Init complete.\n"); + + return sixel; + +error: + video_context_driver_destroy(); + if (sixel) + free(sixel); + return NULL; +} + +static bool sixel_gfx_frame(void *data, const void *frame, + unsigned frame_width, unsigned frame_height, uint64_t frame_count, + unsigned pitch, const char *msg, video_frame_info_t *video_info) +{ + gfx_ctx_mode_t mode; + const void *frame_to_copy = frame; + unsigned width = 0; + unsigned height = 0; + unsigned bits = sixel_video_bits; + unsigned pixfmt = SIXEL_PIXELFORMAT_RGB565; + bool draw = true; + sixel_t *sixel = (sixel_t*)data; + + if (!frame || !frame_width || !frame_height) + return true; + +#ifdef HAVE_MENU + menu_driver_frame(video_info); +#endif + + if (sixel_video_width != frame_width || sixel_video_height != frame_height || sixel_video_pitch != pitch) + { + if (frame_width > 4 && frame_height > 4) + { + sixel_video_width = frame_width; + sixel_video_height = frame_height; + sixel_video_pitch = pitch; + sixel->screen_width = sixel_video_width * sixel_video_scale; + sixel->screen_height = sixel_video_height * sixel_video_scale; + } + } + + if (sixel_menu_frame && video_info->menu_is_alive) + { + frame_to_copy = sixel_menu_frame; + width = sixel_menu_width; + height = sixel_menu_height; + pitch = sixel_menu_pitch; + bits = sixel_menu_bits; + } + else + { + width = sixel_video_width; + height = sixel_video_height; + pitch = sixel_video_pitch; + + if (frame_width == 4 && frame_height == 4 && (frame_width < width && frame_height < height)) + draw = false; + + if (video_info->menu_is_alive) + draw = false; + } + + if (sixel->video_width != width || sixel->video_height != height) + { + scroll_on_demand(sixel->screen_height); + + sixel->video_width = width; + sixel->video_height = height; + + if (sixel_temp_buf) + { + free(sixel_temp_buf); + } + + sixel_temp_buf = (unsigned*)malloc(sixel->screen_width * sixel->screen_height * sizeof(unsigned)); + } + + if (bits == 16) + { + if (sixel_temp_buf) + { + if (frame_to_copy == sixel_menu_frame) + { + /* Scale and convert 16-bit RGBX4444 image to 32-bit RGBX8888. */ + unsigned x, y; + + for (y = 0; y < sixel->screen_height; y++) + { + for (x = 0; x < sixel->screen_width; x++) + { + /* scale incoming frame to fit the screen */ + unsigned scaled_x = (width * x) / sixel->screen_width; + unsigned scaled_y = (height * y) / sixel->screen_height; + unsigned short pixel = ((unsigned short*)frame_to_copy)[width * scaled_y + scaled_x]; + + /* convert RGBX4444 to RGBX8888 */ + unsigned r = ((pixel & 0xF000) << 8) | ((pixel & 0xF000) << 4); + unsigned g = ((pixel & 0x0F00) << 4) | ((pixel & 0x0F00) << 0); + unsigned b = ((pixel & 0x00F0) << 0) | ((pixel & 0x00F0) >> 4); + + sixel_temp_buf[sixel->screen_width * y + x] = 0xFF000000 | b | g | r; + } + } + + pixfmt = SIXEL_PIXELFORMAT_RGBA8888; + frame_to_copy = sixel_temp_buf; + } + else + { + /* Scale and convert 16-bit RGB565 image to 32-bit RGBX8888. */ + unsigned x, y; + + for (y = 0; y < sixel->screen_height; y++) + { + for (x = 0; x < sixel->screen_width; x++) + { + /* scale incoming frame to fit the screen */ + unsigned scaled_x = (width * x) / sixel->screen_width; + unsigned scaled_y = (height * y) / sixel->screen_height; + unsigned short pixel = ((unsigned short*)frame_to_copy)[(pitch / (bits / 8)) * scaled_y + scaled_x]; + + /* convert RGB565 to RGBX8888 */ + unsigned r = ((pixel & 0x001F) << 3) | ((pixel & 0x001C) >> 2); + unsigned g = ((pixel & 0x07E0) << 5) | ((pixel & 0x0600) >> 1); + unsigned b = ((pixel & 0xF800) << 8) | ((pixel & 0xE000) << 3); + + sixel_temp_buf[sixel->screen_width * y + x] = 0xFF000000 | b | g | r; + } + } + + pixfmt = SIXEL_PIXELFORMAT_BGRA8888; + frame_to_copy = sixel_temp_buf; + } + } + else + { + /* no temp buffer available yet */ + } + } + else + { + /* Scale 32-bit RGBX8888 image to output geometry. */ + unsigned x, y; + + for (y = 0; y < sixel->screen_height; y++) + { + for (x = 0; x < sixel->screen_width; x++) + { + /* scale incoming frame to fit the screen */ + unsigned scaled_x = (width * x) / sixel->screen_width; + unsigned scaled_y = (height * y) / sixel->screen_height; + unsigned pixel = ((unsigned*)frame_to_copy)[(pitch / (bits / 8)) * scaled_y + scaled_x]; + + sixel_temp_buf[sixel->screen_width * y + x] = pixel; + } + } + + pixfmt = SIXEL_PIXELFORMAT_BGRA8888; + frame_to_copy = sixel_temp_buf; + } + + if (draw && sixel->screen_width > 0 && sixel->screen_height > 0) + { + printf("\0338"); + + sixel->sixel_status = output_sixel((unsigned char*)frame_to_copy, sixel->screen_width, sixel->screen_height, + SIXEL_COLORS, pixfmt); + + if (SIXEL_FAILED(sixel->sixel_status)) + { + fprintf(stderr, "%s\n%s\n", + sixel_helper_format_error(sixel->sixel_status), + sixel_helper_get_additional_message()); + } + } + + if (msg) + font_driver_render_msg(video_info, NULL, msg, NULL); + + return true; +} + +static void sixel_gfx_set_nonblock_state(void *data, bool toggle) +{ + (void)data; + (void)toggle; +} + +static bool sixel_gfx_alive(void *data) +{ + gfx_ctx_size_t size_data; + unsigned temp_width = 0; + unsigned temp_height = 0; + bool quit = false; + bool resize = false; + + /* Needed because some context drivers don't track their sizes */ + video_driver_get_size(&temp_width, &temp_height); + + size_data.quit = &quit; + size_data.resize = &resize; + size_data.width = &temp_width; + size_data.height = &temp_height; + + video_context_driver_check_window(&size_data); + + if (temp_width != 0 && temp_height != 0) + video_driver_set_size(&temp_width, &temp_height); + + return true; +} + +static bool sixel_gfx_focus(void *data) +{ + (void)data; + return true; +} + +static bool sixel_gfx_suppress_screensaver(void *data, bool enable) +{ + (void)data; + (void)enable; + return false; +} + +static bool sixel_gfx_has_windowed(void *data) +{ + (void)data; + return true; +} + +static void sixel_gfx_free(void *data) +{ + sixel_t *sixel = (sixel_t*)data; + + printf("\033\\"); + + if (sixel_menu_frame) + { + free(sixel_menu_frame); + sixel_menu_frame = NULL; + } + + if (sixel_temp_buf) + { + free(sixel_temp_buf); + sixel_temp_buf = NULL; + } + + font_driver_free_osd(); + + if (sixel) + free(sixel); +} + +static bool sixel_gfx_set_shader(void *data, + enum rarch_shader_type type, const char *path) +{ + (void)data; + (void)type; + (void)path; + + return false; +} + +static void sixel_gfx_set_rotation(void *data, + unsigned rotation) +{ + (void)data; + (void)rotation; +} + +static void sixel_gfx_viewport_info(void *data, + struct video_viewport *vp) +{ + (void)data; + (void)vp; +} + +static bool sixel_gfx_read_viewport(void *data, uint8_t *buffer, bool is_idle) +{ + (void)data; + (void)buffer; + + return true; +} + +static void sixel_set_texture_frame(void *data, + const void *frame, bool rgb32, unsigned width, unsigned height, + float alpha) +{ + unsigned pitch = width * 2; + + if (rgb32) + pitch = width * 4; + + if (sixel_menu_frame) + { + free(sixel_menu_frame); + sixel_menu_frame = NULL; + } + + if (!sixel_menu_frame || sixel_menu_width != width || sixel_menu_height != height || sixel_menu_pitch != pitch) + if (pitch && height) + sixel_menu_frame = (unsigned char*)malloc(pitch * height); + + if (sixel_menu_frame && frame && pitch && height) + { + memcpy(sixel_menu_frame, frame, pitch * height); + sixel_menu_width = width; + sixel_menu_height = height; + sixel_menu_pitch = pitch; + sixel_menu_bits = rgb32 ? 32 : 16; + } +} + +static void sixel_set_osd_msg(void *data, + video_frame_info_t *video_info, + const char *msg, + const void *params, void *font) +{ + font_driver_render_msg(video_info, font, msg, params); +} + +static void sixel_get_video_output_size(void *data, + unsigned *width, unsigned *height) +{ + gfx_ctx_size_t size_data; + size_data.width = width; + size_data.height = height; + video_context_driver_get_video_output_size(&size_data); +} + +static void sixel_get_video_output_prev(void *data) +{ + video_context_driver_get_video_output_prev(); +} + +static void sixel_get_video_output_next(void *data) +{ + video_context_driver_get_video_output_next(); +} + +static void sixel_set_video_mode(void *data, unsigned width, unsigned height, + bool fullscreen) +{ + gfx_ctx_mode_t mode; + + mode.width = width; + mode.height = height; + mode.fullscreen = fullscreen; + + video_context_driver_set_video_mode(&mode); +} + +static const video_poke_interface_t sixel_poke_interface = { + NULL, + NULL, + NULL, + NULL, + NULL, + sixel_set_video_mode, + NULL, + NULL, + sixel_get_video_output_size, + sixel_get_video_output_prev, + sixel_get_video_output_next, + NULL, + NULL, + NULL, + NULL, +#if defined(HAVE_MENU) + sixel_set_texture_frame, + NULL, + sixel_set_osd_msg, + NULL, +#else + NULL, + NULL, + NULL, + NULL, +#endif + NULL, + NULL, + NULL, + NULL, +}; + +static void sixel_gfx_get_poke_interface(void *data, + const video_poke_interface_t **iface) +{ + (void)data; + *iface = &sixel_poke_interface; +} + +static void sixel_gfx_set_viewport(void *data, unsigned viewport_width, + unsigned viewport_height, bool force_full, bool allow_rotate) +{ +} + +bool sixel_has_menu_frame(void) +{ + return (sixel_menu_frame != NULL); +} + +video_driver_t video_sixel = { + sixel_gfx_init, + sixel_gfx_frame, + sixel_gfx_set_nonblock_state, + sixel_gfx_alive, + sixel_gfx_focus, + sixel_gfx_suppress_screensaver, + sixel_gfx_has_windowed, + sixel_gfx_set_shader, + sixel_gfx_free, + "sixel", + sixel_gfx_set_viewport, + sixel_gfx_set_rotation, + sixel_gfx_viewport_info, + sixel_gfx_read_viewport, + NULL, /* read_frame_raw */ +#ifdef HAVE_OVERLAY + NULL, /* overlay_interface */ +#endif + sixel_gfx_get_poke_interface, + NULL /* wrap_type_to_enum */ +}; diff --git a/gfx/drivers/vg.c b/gfx/drivers/vg.c index 64d2451e48..16bcf23b5f 100644 --- a/gfx/drivers/vg.c +++ b/gfx/drivers/vg.c @@ -194,7 +194,8 @@ static void *vg_init(const video_info_t *video, video_context_driver_input_driver(&inp); if ( video->font_enable - && font_renderer_create_default((const void**)&vg->font_driver, &vg->mFontRenderer, + && font_renderer_create_default( + &vg->font_driver, &vg->mFontRenderer, *settings->paths.path_font ? settings->paths.path_font : NULL, settings->floats.video_font_size)) { vg->mFont = vgCreateFont(0); diff --git a/gfx/drivers/xvideo.c b/gfx/drivers/xvideo.c index d81bc83d61..619cbaa712 100644 --- a/gfx/drivers/xvideo.c +++ b/gfx/drivers/xvideo.c @@ -139,7 +139,8 @@ static void xv_init_font(xv_t *xv, const char *font_path, unsigned font_size) if (!settings->bools.video_font_enable) return; - if (font_renderer_create_default((const void**)&xv->font_driver, + if (font_renderer_create_default( + &xv->font_driver, &xv->font, *settings->paths.path_font ? settings->paths.path_font : NULL, settings->floats.video_font_size)) diff --git a/gfx/drivers_context/sixel_ctx.c b/gfx/drivers_context/sixel_ctx.c new file mode 100644 index 0000000000..cee11e0027 --- /dev/null +++ b/gfx/drivers_context/sixel_ctx.c @@ -0,0 +1,211 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2018 - Brad Parker + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +/* SIXEL context. */ + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include "../../configuration.h" +#include "../../dynamic.h" +#include "../../verbosity.h" +#include "../../ui/ui_companion_driver.h" +#include "../video_driver.h" + +#if defined(_WIN32) && !defined(_XBOX) +#include "../common/win32_common.h" +#endif + +static enum gfx_ctx_api sixel_ctx_api = GFX_CTX_NONE; + +static void gfx_ctx_sixel_check_window(void *data, bool *quit, + bool *resize, unsigned *width, unsigned *height, bool is_shutdown) +{ +} + +static bool gfx_ctx_sixel_set_resize(void *data, + unsigned width, unsigned height) +{ + (void)data; + (void)width; + (void)height; + + return false; +} + +static void gfx_ctx_sixel_update_window_title(void *data, void *data2) +{ +#if defined(_WIN32) && !defined(_XBOX) + const ui_window_t *window = ui_companion_driver_get_window_ptr(); + char title[128]; + + title[0] = '\0'; + + video_driver_get_window_title(title, sizeof(title)); + + if (window && title[0]) + window->set_title(&main_window, title); +#endif +} + +static void gfx_ctx_sixel_get_video_size(void *data, + unsigned *width, unsigned *height) +{ + (void)data; +} + +static void *gfx_ctx_sixel_init( + video_frame_info_t *video_info, void *video_driver) +{ + (void)video_driver; + + return (void*)"sixel"; +} + +static void gfx_ctx_sixel_destroy(void *data) +{ + (void)data; +} + +static bool gfx_ctx_sixel_set_video_mode(void *data, + video_frame_info_t *video_info, + unsigned width, unsigned height, + bool fullscreen) +{ + return true; +} + + +static void gfx_ctx_sixel_input_driver(void *data, + const char *joypad_name, + const input_driver_t **input, void **input_data) +{ + (void)data; + +#ifdef HAVE_UDEV + *input_data = input_udev.init(joypad_name); + + if (*input_data) + { + *input = &input_udev; + return; + } +#endif + *input = NULL; + *input_data = NULL; +} + +static bool gfx_ctx_sixel_has_focus(void *data) +{ + return true; +} + +static bool gfx_ctx_sixel_suppress_screensaver(void *data, bool enable) +{ + return true; +} + +static bool gfx_ctx_sixel_has_windowed(void *data) +{ + (void)data; + + return true; +} + +static bool gfx_ctx_sixel_get_metrics(void *data, + enum display_metric_types type, float *value) +{ + return false; +} + +static enum gfx_ctx_api gfx_ctx_sixel_get_api(void *data) +{ + return sixel_ctx_api; +} + +static bool gfx_ctx_sixel_bind_api(void *data, + enum gfx_ctx_api api, unsigned major, unsigned minor) +{ + (void)data; + + return true; +} + +static void gfx_ctx_sixel_show_mouse(void *data, bool state) +{ + (void)data; +} + +static void gfx_ctx_sixel_swap_interval(void *data, unsigned interval) +{ + (void)data; + (void)interval; +} + +static void gfx_ctx_sixel_set_flags(void *data, uint32_t flags) +{ + (void)data; + (void)flags; +} + +static uint32_t gfx_ctx_sixel_get_flags(void *data) +{ + uint32_t flags = 0; + BIT32_SET(flags, GFX_CTX_FLAGS_NONE); + return flags; +} + +static void gfx_ctx_sixel_swap_buffers(void *data, void *data2) +{ + (void)data; +} + +const gfx_ctx_driver_t gfx_ctx_sixel = { + gfx_ctx_sixel_init, + gfx_ctx_sixel_destroy, + gfx_ctx_sixel_get_api, + gfx_ctx_sixel_bind_api, + gfx_ctx_sixel_swap_interval, + gfx_ctx_sixel_set_video_mode, + gfx_ctx_sixel_get_video_size, + NULL, /* get_refresh_rate */ + NULL, /* get_video_output_size */ + NULL, /* get_video_output_prev */ + NULL, /* get_video_output_next */ + gfx_ctx_sixel_get_metrics, + NULL, + gfx_ctx_sixel_update_window_title, + gfx_ctx_sixel_check_window, + gfx_ctx_sixel_set_resize, + gfx_ctx_sixel_has_focus, + gfx_ctx_sixel_suppress_screensaver, + gfx_ctx_sixel_has_windowed, + gfx_ctx_sixel_swap_buffers, + gfx_ctx_sixel_input_driver, + NULL, + NULL, + NULL, + gfx_ctx_sixel_show_mouse, + "sixel", + gfx_ctx_sixel_get_flags, + gfx_ctx_sixel_set_flags, + NULL, + NULL, + NULL +}; + diff --git a/gfx/drivers_font/caca_font.c b/gfx/drivers_font/caca_font.c index f98f8e38ed..f36f60c3e3 100644 --- a/gfx/drivers_font/caca_font.c +++ b/gfx/drivers_font/caca_font.c @@ -45,7 +45,8 @@ static void *caca_init_font(void *data, font->caca = (caca_t*)data; - if (!font_renderer_create_default((const void**)&font->caca_font_driver, + if (!font_renderer_create_default( + &font->caca_font_driver, &font->caca_font_data, font_path, font_size)) { RARCH_WARN("Couldn't initialize font renderer.\n"); @@ -128,18 +129,13 @@ static void caca_render_msg(video_frame_info_t *video_info, caca_refresh_display(*font->caca->caca_display); } -static void caca_font_bind_block(void* data, void* userdata) -{ - (void)data; -} - font_renderer_t caca_font = { caca_init_font, caca_render_free_font, caca_render_msg, "caca font", caca_font_get_glyph, /* get_glyph */ - caca_font_bind_block, /* bind_block */ + NULL, /* bind_block */ NULL, /* flush */ caca_get_message_width /* get_message_width */ }; diff --git a/gfx/drivers_font/ctr_font.c b/gfx/drivers_font/ctr_font.c index c128ee05a7..4eb656f625 100644 --- a/gfx/drivers_font/ctr_font.c +++ b/gfx/drivers_font/ctr_font.c @@ -58,8 +58,9 @@ static void* ctr_font_init_font(void* data, const char* font_path, return NULL; font_size = 10; - if (!font_renderer_create_default((const void**)&font->font_driver, - &font->font_data, font_path, font_size)) + if (!font_renderer_create_default( + &font->font_driver, + &font->font_data, font_path, font_size)) { RARCH_WARN("Couldn't initialize font renderer.\n"); free(font); @@ -446,12 +447,6 @@ static const struct font_glyph* ctr_font_get_glyph( return font->font_driver->get_glyph((void*)font->font_driver, code); } -static void ctr_font_bind_block(void* data, void* userdata) -{ - (void)data; -} - - font_renderer_t ctr_font = { ctr_font_init_font, @@ -459,7 +454,7 @@ font_renderer_t ctr_font = ctr_font_render_msg, "ctrfont", ctr_font_get_glyph, - ctr_font_bind_block, + NULL, /* bind_block */ NULL, /* flush_block */ ctr_font_get_message_width, }; diff --git a/gfx/drivers_font/d3d10_font.c b/gfx/drivers_font/d3d10_font.c index 3252854303..80bb3c357d 100644 --- a/gfx/drivers_font/d3d10_font.c +++ b/gfx/drivers_font/d3d10_font.c @@ -46,7 +46,7 @@ d3d10_font_init_font(void* data, const char* font_path, float font_size, bool is return NULL; if (!font_renderer_create_default( - (const void**)&font->font_driver, &font->font_data, font_path, font_size)) + &font->font_driver, &font->font_data, font_path, font_size)) { RARCH_WARN("Couldn't initialize font renderer.\n"); free(font); @@ -362,18 +362,13 @@ static const struct font_glyph* d3d10_font_get_glyph(void *data, uint32_t code) return font->font_driver->get_glyph((void*)font->font_driver, code); } -static void d3d10_font_bind_block(void* data, void *userdata) -{ - (void)data; -} - font_renderer_t d3d10_font = { d3d10_font_init_font, d3d10_font_free_font, d3d10_font_render_msg, "d3d10font", d3d10_font_get_glyph, - d3d10_font_bind_block, + NULL, /* bind_block */ NULL, /* flush */ d3d10_font_get_message_width, }; diff --git a/gfx/drivers_font/d3d11_font.c b/gfx/drivers_font/d3d11_font.c index f0113d870d..de4f29ade3 100644 --- a/gfx/drivers_font/d3d11_font.c +++ b/gfx/drivers_font/d3d11_font.c @@ -45,7 +45,7 @@ d3d11_font_init_font(void* data, const char* font_path, float font_size, bool is return NULL; if (!font_renderer_create_default( - (const void**)&font->font_driver, &font->font_data, font_path, font_size)) + &font->font_driver, &font->font_data, font_path, font_size)) { RARCH_WARN("Couldn't initialize font renderer.\n"); free(font); @@ -359,18 +359,13 @@ static const struct font_glyph* d3d11_font_get_glyph(void *data, uint32_t code) return font->font_driver->get_glyph((void*)font->font_driver, code); } -static void d3d11_font_bind_block(void* data, void *userdata) -{ - (void)data; -} - font_renderer_t d3d11_font = { d3d11_font_init_font, d3d11_font_free_font, d3d11_font_render_msg, "d3d11font", d3d11_font_get_glyph, - d3d11_font_bind_block, + NULL, /* bind_block */ NULL, /* flush */ d3d11_font_get_message_width, }; diff --git a/gfx/drivers_font/d3d12_font.c b/gfx/drivers_font/d3d12_font.c index f15b1c9cdb..d7f81262bc 100644 --- a/gfx/drivers_font/d3d12_font.c +++ b/gfx/drivers_font/d3d12_font.c @@ -46,7 +46,7 @@ d3d12_font_init_font(void* data, const char* font_path, return NULL; if (!font_renderer_create_default( - (const void**)&font->font_driver, + &font->font_driver, &font->font_data, font_path, font_size)) { RARCH_WARN("Couldn't initialize font renderer.\n"); @@ -376,18 +376,13 @@ static const struct font_glyph* d3d12_font_get_glyph( return font->font_driver->get_glyph((void*)font->font_driver, code); } -static void d3d12_font_bind_block(void* data, void* userdata) -{ - (void)data; -} - font_renderer_t d3d12_font = { d3d12_font_init_font, d3d12_font_free_font, d3d12_font_render_msg, "d3d12font", d3d12_font_get_glyph, - d3d12_font_bind_block, + NULL, /* bind_block */ NULL, /* flush */ d3d12_font_get_message_width, }; diff --git a/gfx/drivers_font/gdi_font.c b/gfx/drivers_font/gdi_font.c index 78a41c4163..1c37ddd5ed 100644 --- a/gfx/drivers_font/gdi_font.c +++ b/gfx/drivers_font/gdi_font.c @@ -51,7 +51,8 @@ static void *gdi_init_font(void *data, font->gdi = (gdi_t*)data; - if (!font_renderer_create_default((const void**)&font->gdi_font_driver, + if (!font_renderer_create_default( + &font->gdi_font_driver, &font->gdi_font_data, font_path, font_size)) { RARCH_WARN("Couldn't initialize font renderer.\n"); diff --git a/gfx/drivers_font/gl_raster_font.c b/gfx/drivers_font/gl_raster_font.c index c9b43abfaa..a1442f8a9b 100644 --- a/gfx/drivers_font/gl_raster_font.c +++ b/gfx/drivers_font/gl_raster_font.c @@ -178,7 +178,8 @@ static void *gl_raster_font_init_font(void *data, font->gl = (gl_t*)data; - if (!font_renderer_create_default((const void**)&font->font_driver, + if (!font_renderer_create_default( + &font->font_driver, &font->font_data, font_path, font_size)) { RARCH_WARN("Couldn't initialize font renderer.\n"); diff --git a/gfx/drivers_font/metal_raster_font.m b/gfx/drivers_font/metal_raster_font.m index f04ccb4b74..287410f150 100644 --- a/gfx/drivers_font/metal_raster_font.m +++ b/gfx/drivers_font/metal_raster_font.m @@ -24,6 +24,7 @@ @interface MetalRaster : NSObject { + __weak MetalDriver *_driver; const font_renderer_driver_t *_font_driver; void *_font_data; struct font_atlas *_atlas; @@ -32,7 +33,6 @@ id _buffer; id _texture; - MTLRenderPassDescriptor *_rpd; id _state; id _sampler; @@ -40,41 +40,61 @@ Uniforms _uniforms; id _vert; + unsigned _capacity; + unsigned _offset; unsigned _vertices; } -@property (readwrite) MetalDriver *metal; @property (readonly) struct font_atlas *atlas; -@property (readwrite) bool needsUpdate; -- (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size; +- (instancetype)initWithDriver:(MetalDriver *)driver fontPath:(const char *)font_path fontSize:(unsigned)font_size; -- (int)getWidthForMessage:(const char *)msg length:(unsigned int)length scale:(float)scale; +- (int)getWidthForMessage:(const char *)msg length:(NSUInteger)length scale:(float)scale; - (const struct font_glyph *)getGlyph:(uint32_t)code; @end @implementation MetalRaster -- (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size +- (instancetype)initWithDriver:(MetalDriver *)driver fontPath:(const char *)font_path fontSize:(unsigned)font_size { - if (self = [super init]) { - if (metal == nil) + if (self = [super init]) + { + if (driver == nil) return nil; - _metal = metal; - _context = metal.context; - if (!font_renderer_create_default((const void **)&_font_driver, - &_font_data, font_path, font_size)) { + _driver = driver; + _context = driver.context; + if (!font_renderer_create_default( + &_font_driver, + &_font_data, font_path, font_size)) + { RARCH_WARN("Couldn't initialize font renderer.\n"); return nil; } _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); _atlas = _font_driver->get_atlas(_font_data); - _stride = _atlas->width; - _buffer = [_context.device newBufferWithBytes:_atlas->buffer - length:(NSUInteger)(_atlas->width * _atlas->height) - options:MTLResourceStorageModeManaged]; + _stride = MTL_ALIGN_BUFFER(_atlas->width); + if (_stride == _atlas->width) + { + _buffer = [_context.device newBufferWithBytes:_atlas->buffer + length:(NSUInteger)(_stride * _atlas->height) + options:MTLResourceStorageModeManaged]; + } + else + { + _buffer = [_context.device newBufferWithLength:(NSUInteger)(_stride * _atlas->height) + options:MTLResourceStorageModeManaged]; + void *dst = _buffer.contents; + void *src = _atlas->buffer; + for (unsigned i = 0; i < _atlas->height; i++) + { + memcpy(dst, src, _atlas->width); + dst += _stride; + src += _atlas->width; + } + [_buffer didModifyRange:NSMakeRange(0, _buffer.length)]; + } MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm width:_atlas->width @@ -83,9 +103,11 @@ _texture = [_buffer newTextureWithDescriptor:td offset:0 bytesPerRow:_stride]; - _vert = [_context.device newBufferWithLength:sizeof(FontVertex) * 500 options:MTLResourceStorageModeManaged]; - _needsUpdate = true; - if (![self _initializeState]) { + _capacity = 12000; + _vert = [_context.device newBufferWithLength:sizeof(SpriteVertex) * + _capacity options:MTLResourceStorageModeManaged]; + if (![self _initializeState]) + { return nil; } } @@ -98,11 +120,11 @@ MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; vd.attributes[0].offset = 0; vd.attributes[0].format = MTLVertexFormatFloat2; - vd.attributes[1].offset = offsetof(FontVertex, texCoord); + vd.attributes[1].offset = offsetof(SpriteVertex, texCoord); vd.attributes[1].format = MTLVertexFormatFloat2; - vd.attributes[2].offset = offsetof(FontVertex, color); + vd.attributes[2].offset = offsetof(SpriteVertex, color); vd.attributes[2].format = MTLVertexFormatFloat4; - vd.layouts[0].stride = sizeof(FontVertex); + vd.layouts[0].stride = sizeof(SpriteVertex); vd.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex; MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; @@ -118,23 +140,18 @@ psd.sampleCount = 1; psd.vertexDescriptor = vd; - psd.vertexFunction = [_context.library newFunctionWithName:@"font_vertex"]; - psd.fragmentFunction = [_context.library newFunctionWithName:@"font_fragment"]; + psd.vertexFunction = [_context.library newFunctionWithName:@"sprite_vertex"]; + psd.fragmentFunction = [_context.library newFunctionWithName:@"sprite_fragment_a8"]; NSError *err; _state = [_context.device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) { + if (err != nil) + { RARCH_ERR("[MetalRaster]: error creating pipeline state: %s\n", err.localizedDescription.UTF8String); return NO; } } - { - _rpd = [MTLRenderPassDescriptor renderPassDescriptor]; - _rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare; - _rpd.colorAttachments[0].storeAction = MTLStoreActionStore; - } - { MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; sd.minFilter = MTLSamplerMinMagFilterLinear; @@ -146,9 +163,11 @@ - (void)updateGlyph:(const struct font_glyph *)glyph { - if (_atlas->dirty) { + if (_atlas->dirty) + { unsigned row; - for (row = glyph->atlas_offset_y; row < (glyph->atlas_offset_y + glyph->height); row++) { + for (row = glyph->atlas_offset_y; row < (glyph->atlas_offset_y + glyph->height); row++) + { uint8_t *src = _atlas->buffer + row * _atlas->width + glyph->atlas_offset_x; uint8_t *dst = (uint8_t *)_buffer.contents + row * _stride + glyph->atlas_offset_x; memcpy(dst, src, glyph->width); @@ -159,27 +178,28 @@ [_buffer didModifyRange:NSMakeRange(offset, len)]; _atlas->dirty = false; - _needsUpdate = true; } } -- (int)getWidthForMessage:(const char *)msg length:(unsigned int)length scale:(float)scale +- (int)getWidthForMessage:(const char *)msg length:(NSUInteger)length scale:(float)scale { int delta_x = 0; - for (unsigned i = 0; i < length; i++) { + for (NSUInteger i = 0; i < length; i++) + { const struct font_glyph *glyph = _font_driver->get_glyph(_font_data, (uint8_t)msg[i]); if (!glyph) /* Do something smarter here ... */ glyph = _font_driver->get_glyph(_font_data, '?'); - if (glyph) { + if (glyph) + { [self updateGlyph:glyph]; delta_x += glyph->advance_x; } } - return delta_x * scale; + return (int)(delta_x * scale); } - (const struct font_glyph *)getGlyph:(uint32_t)code @@ -188,22 +208,18 @@ return NULL; const struct font_glyph *glyph = _font_driver->get_glyph((void *)_font_driver, code); - if (glyph) { + if (glyph) + { [self updateGlyph:glyph]; } return glyph; } -typedef struct color -{ - float r, g, b, a; -} color_t; - -static INLINE void write_quad(FontVertex *pv, - float x, float y, float width, float height, - float tex_x, float tex_y, float tex_width, float tex_height, - const vector_float4 *color) +static INLINE void write_quad6(SpriteVertex *pv, + float x, float y, float width, float height, + float tex_x, float tex_y, float tex_width, float tex_height, + const vector_float4 *color) { unsigned i; static const float strip[2 * 6] = { @@ -215,11 +231,12 @@ static INLINE void write_quad(FontVertex *pv, 0.0f, 1.0f, }; - for (i = 0; i < 6; i++) { - pv[i].position.x = x + strip[2 * i + 0] * width; - pv[i].position.y = y + strip[2 * i + 1] * height; - pv[i].texCoord.x = tex_x + strip[2 * i + 0] * tex_width; - pv[i].texCoord.y = tex_y + strip[2 * i + 1] * tex_height; + for (i = 0; i < 6; i++) + { + pv[i].position = simd_make_float2(x + strip[2 * i + 0] * width, + y + strip[2 * i + 1] * height); + pv[i].texCoord = simd_make_float2(tex_x + strip[2 * i + 0] * tex_width, + tex_y + strip[2 * i + 1] * tex_height); pv[i].color = *color; } } @@ -233,33 +250,36 @@ static INLINE void write_quad(FontVertex *pv, posY:(float)posY aligned:(unsigned)aligned { - const char* msg_end = msg + length; - int x = roundf(posX * _metal.viewport->width); - int y = roundf((1.0f - posY) * _metal.viewport->height); - int delta_x = 0; - int delta_y = 0; + const char *msg_end = msg + length; + int x = (int)roundf(posX * _driver.viewport->full_width); + int y = (int)roundf((1.0f - posY) * _driver.viewport->full_height); + int delta_x = 0; + int delta_y = 0; float inv_tex_size_x = 1.0f / _texture.width; float inv_tex_size_y = 1.0f / _texture.height; - float inv_win_width = 1.0f / _metal.viewport->width; - float inv_win_height = 1.0f / _metal.viewport->height; + float inv_win_width = 1.0f / _driver.viewport->full_width; + float inv_win_height = 1.0f / _driver.viewport->full_height; - switch (aligned) { + switch (aligned) + { case TEXT_ALIGN_RIGHT: x -= [self getWidthForMessage:msg length:length scale:scale]; break; - + case TEXT_ALIGN_CENTER: x -= [self getWidthForMessage:msg length:length scale:scale] / 2; break; - + default: break; } - FontVertex *v = (FontVertex *)_vert.contents; + SpriteVertex *v = (SpriteVertex *)_vert.contents; + v += _offset + _vertices; - while (msg < msg_end) { - unsigned code = utf8_walk(&msg); + while (msg < msg_end) + { + unsigned code = utf8_walk(&msg); const struct font_glyph *glyph = _font_driver->get_glyph(_font_data, code); if (!glyph) /* Do something smarter here ... */ @@ -271,46 +291,50 @@ static INLINE void write_quad(FontVertex *pv, [self updateGlyph:glyph]; int off_x, off_y, tex_x, tex_y, width, height; - off_x = glyph->draw_offset_x; - off_y = glyph->draw_offset_y; - tex_x = glyph->atlas_offset_x; - tex_y = glyph->atlas_offset_y; - width = glyph->width; + off_x = glyph->draw_offset_x; + off_y = glyph->draw_offset_y; + tex_x = glyph->atlas_offset_x; + tex_y = glyph->atlas_offset_y; + width = glyph->width; height = glyph->height; - write_quad(v + _vertices, - (x + off_x + delta_x * scale) * inv_win_width, - (y + off_y + delta_y * scale) * inv_win_height, - width * scale * inv_win_width, - height * scale * inv_win_height, - tex_x * inv_tex_size_x, - tex_y * inv_tex_size_y, - width * inv_tex_size_x, - height * inv_tex_size_y, - &color); + write_quad6(v, + (x + off_x + delta_x * scale) * inv_win_width, + (y + off_y + delta_y * scale) * inv_win_height, + width * scale * inv_win_width, + height * scale * inv_win_height, + tex_x * inv_tex_size_x, + tex_y * inv_tex_size_y, + width * inv_tex_size_x, + height * inv_tex_size_y, + &color); _vertices += 6; + v += 6; - delta_x += glyph->advance_x; - delta_y += glyph->advance_y; + delta_x += glyph->advance_x; + delta_y += glyph->advance_y; } } -- (void)_flush { - [_vert didModifyRange:NSMakeRange(0, sizeof(FontVertex)*_vertices)]; - _rpd.colorAttachments[0].texture = _context.nextDrawable.texture; +- (void)_flush +{ + NSUInteger start = _offset * sizeof(SpriteVertex); + [_vert didModifyRange:NSMakeRange(start, sizeof(SpriteVertex) * _vertices)]; - id cb = _context.commandBuffer; - id rce = [cb renderCommandEncoderWithDescriptor:_rpd]; + id rce = _context.rce; [rce pushDebugGroup:@"render fonts"]; + + [_context resetRenderViewport]; [rce setRenderPipelineState:_state]; - [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; - [rce setVertexBuffer:_vert offset:0 atIndex:BufferIndexPositions]; + [rce setVertexBytes:&_uniforms length:sizeof(Uniforms) atIndex:BufferIndexUniforms]; + [rce setVertexBuffer:_vert offset:start atIndex:BufferIndexPositions]; [rce setFragmentTexture:_texture atIndex:TextureIndexColor]; [rce setFragmentSamplerState:_sampler atIndex:SamplerIndexDraw]; [rce drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_vertices]; [rce popDebugGroup]; - [rce endEncoding]; + + _offset += _vertices; _vertices = 0; } @@ -323,7 +347,8 @@ static INLINE void write_quad(FontVertex *pv, aligned:(unsigned)aligned { /* If the font height is not supported just draw as usual */ - if (!_font_driver->get_line_height) { + if (!_font_driver->get_line_height) + { [self _renderLine:msg video:video length:strlen(msg) scale:scale color:color posX:posX posY:posY aligned:aligned]; return; } @@ -331,12 +356,14 @@ static INLINE void write_quad(FontVertex *pv, int lines = 0; float line_height = _font_driver->get_line_height(_font_data) * scale / video->height; - for (;;) { + for (;;) + { const char *delim = strchr(msg, '\n'); /* Draw the line */ - if (delim) { - unsigned msg_len = delim - msg; + if (delim) + { + NSUInteger msg_len = delim - msg; [self _renderLine:msg video:video length:msg_len @@ -348,8 +375,9 @@ static INLINE void write_quad(FontVertex *pv, msg += msg_len + 1; lines++; } - else { - unsigned msg_len = strlen(msg); + else + { + NSUInteger msg_len = strlen(msg); [self _renderLine:msg video:video length:msg_len @@ -378,7 +406,8 @@ static INLINE void write_quad(FontVertex *pv, unsigned width = video->width; unsigned height = video->height; - if (params) { + if (params) + { x = params->x; y = params->y; scale = params->scale; @@ -387,21 +416,26 @@ static INLINE void write_quad(FontVertex *pv, drop_y = params->drop_y; drop_mod = params->drop_mod; drop_alpha = params->drop_alpha; - color.x = FONT_COLOR_GET_RED(params->color) / 255.0f; - color.y = FONT_COLOR_GET_GREEN(params->color) / 255.0f; - color.z = FONT_COLOR_GET_BLUE(params->color) / 255.0f; - color.w = FONT_COLOR_GET_ALPHA(params->color) / 255.0f; + + color = simd_make_float4( + FONT_COLOR_GET_RED(params->color) / 255.0f, + FONT_COLOR_GET_GREEN(params->color) / 255.0f, + FONT_COLOR_GET_BLUE(params->color) / 255.0f, + FONT_COLOR_GET_ALPHA(params->color) / 255.0f); + } - else { + else + { x = video->font_msg_pos_x; y = video->font_msg_pos_y; scale = 1.0f; text_align = TEXT_ALIGN_LEFT; - color.x = video->font_msg_color_r; - color.y = video->font_msg_color_g; - color.z = video->font_msg_color_b; - color.w = 1.0; + color = simd_make_float4( + video->font_msg_color_r, + video->font_msg_color_g, + video->font_msg_color_b, + 1.0f); drop_x = -2; drop_y = -2; @@ -409,19 +443,20 @@ static INLINE void write_quad(FontVertex *pv, drop_alpha = 1.0f; } - @autoreleasepool { + @autoreleasepool + { NSUInteger max_glyphs = strlen(msg); if (drop_x || drop_y) max_glyphs *= 2; - NSUInteger needed = sizeof(FontVertex) * max_glyphs * 6; - if (_vert.length < needed) + if (max_glyphs * 6 + _offset > _capacity) { - _vert = [_context.device newBufferWithLength:needed options:MTLResourceStorageModeManaged]; + _offset = 0; } - if (drop_x || drop_y) { + if (drop_x || drop_y) + { color_dark.x = color.x * drop_mod; color_dark.y = color.y * drop_mod; color_dark.z = color.z * drop_mod; @@ -456,7 +491,7 @@ static void *metal_raster_font_init_font(void *data, const char *font_path, float font_size, bool is_threaded) { - MetalRaster *r = [[MetalRaster alloc] initWithDriver:(__bridge_transfer MetalDriver *)data fontPath:font_path fontSize:(unsigned)font_size]; + MetalRaster *r = [[MetalRaster alloc] initWithDriver:(__bridge MetalDriver *)data fontPath:font_path fontSize:(unsigned)font_size]; if (!r) return NULL; @@ -478,23 +513,23 @@ static int metal_get_message_width(void *data, const char *msg, } static void metal_raster_font_render_msg( - video_frame_info_t *video_info, - void *data, const char *msg, - const struct font_params *params) + video_frame_info_t *video_info, + void *data, const char *msg, + const struct font_params *params) { MetalRaster *r = (__bridge MetalRaster *)data; [r renderMessage:msg video:video_info params:params]; } static const struct font_glyph *metal_raster_font_get_glyph( - void *data, uint32_t code) + void *data, uint32_t code) { MetalRaster *r = (__bridge MetalRaster *)data; return [r getGlyph:code]; } static void metal_raster_font_flush_block(unsigned width, unsigned height, - void *data, video_frame_info_t *video_info) + void *data, video_frame_info_t *video_info) { (void)data; } diff --git a/gfx/drivers_font/sixel_font.c b/gfx/drivers_font/sixel_font.c new file mode 100644 index 0000000000..674d9d460a --- /dev/null +++ b/gfx/drivers_font/sixel_font.c @@ -0,0 +1,139 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2018 - Brad Parker + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include "../font_driver.h" +#include "../../verbosity.h" +#include "../common/sixel_common.h" + +typedef struct +{ + const font_renderer_driver_t *sixel_font_driver; + void *sixel_font_data; + sixel_t *sixel; +} sixel_raster_t; + +static void *sixel_init_font(void *data, + const char *font_path, float font_size, + bool is_threaded) +{ + sixel_raster_t *font = (sixel_raster_t*)calloc(1, sizeof(*font)); + + if (!font) + return NULL; + + font->sixel = (sixel_t*)data; + + if (!font_renderer_create_default( + &font->sixel_font_driver, + &font->sixel_font_data, font_path, font_size)) + { + RARCH_WARN("Couldn't initialize font renderer.\n"); + return NULL; + } + + return font; +} + +static void sixel_render_free_font(void *data, bool is_threaded) +{ + (void)data; + (void)is_threaded; +} + +static int sixel_get_message_width(void *data, const char *msg, + unsigned msg_len, float scale) +{ + return 0; +} + +static const struct font_glyph *sixel_font_get_glyph( + void *data, uint32_t code) +{ + return NULL; +} + +static void sixel_render_msg(video_frame_info_t *video_info, + void *data, const char *msg, + const void *userdata) +{ + float x, y, scale; + unsigned width, height; + unsigned newX, newY; + unsigned align; + sixel_raster_t *font = (sixel_raster_t*)data; + const struct font_params *params = (const struct font_params*)userdata; + + if (!font || string_is_empty(msg)) + return; + + if (params) + { + x = params->x; + y = params->y; + scale = params->scale; + align = params->text_align; + } + else + { + x = video_info->font_msg_pos_x; + y = video_info->font_msg_pos_y; + scale = 1.0f; + align = TEXT_ALIGN_LEFT; + } + + if (!font->sixel) + return; + + width = font->sixel->screen_width; + height = font->sixel->screen_height; + newY = height - (y * height * scale); + + switch (align) + { + case TEXT_ALIGN_RIGHT: + newX = (x * width * scale) - strlen(msg); + break; + case TEXT_ALIGN_CENTER: + newX = (x * width * scale) - (strlen(msg) / 2); + break; + case TEXT_ALIGN_LEFT: + default: + newX = x * width * scale; + break; + } + + /* FIXME: add text drawing support */ +} + +font_renderer_t sixel_font = { + sixel_init_font, + sixel_render_free_font, + sixel_render_msg, + "sixel font", + sixel_font_get_glyph, /* get_glyph */ + NULL, /* bind_block */ + NULL, /* flush */ + sixel_get_message_width /* get_message_width */ +}; diff --git a/gfx/drivers_font/vga_font.c b/gfx/drivers_font/vga_font.c index 0cc11f2ba2..c223b3e201 100644 --- a/gfx/drivers_font/vga_font.c +++ b/gfx/drivers_font/vga_font.c @@ -46,7 +46,8 @@ static void *vga_init_font(void *data, font_size = 1; - if (!font_renderer_create_default((const void**)&font->vga_font_driver, + if (!font_renderer_create_default( + &font->vga_font_driver, &font->vga_font_data, font_path, font_size)) { RARCH_WARN("Couldn't initialize font renderer.\n"); @@ -125,18 +126,13 @@ static void vga_render_msg(video_frame_info_t *video_info, } } -static void vga_font_bind_block(void* data, void* userdata) -{ - (void)data; -} - font_renderer_t vga_font = { vga_init_font, vga_render_free_font, vga_render_msg, "vga font", vga_font_get_glyph, /* get_glyph */ - vga_font_bind_block, /* bind_block */ + NULL, /* bind_block */ NULL, /* flush */ vga_get_message_width /* get_message_width */ }; diff --git a/gfx/drivers_font/vita2d_font.c b/gfx/drivers_font/vita2d_font.c index 5a228aea6d..72c3d847f1 100644 --- a/gfx/drivers_font/vita2d_font.c +++ b/gfx/drivers_font/vita2d_font.c @@ -48,7 +48,8 @@ static void *vita2d_font_init_font(void *data, font->vita = (vita_video_t*)data; - if (!font_renderer_create_default((const void**)&font->font_driver, + if (!font_renderer_create_default( + &font->font_driver, &font->font_data, font_path, font_size)) goto error; @@ -346,18 +347,13 @@ static const struct font_glyph *vita2d_font_get_glyph( return font->font_driver->get_glyph((void*)font->font_driver, code); } -static void vita2d_font_bind_block(void *data, void *userdata) -{ - (void)data; -} - font_renderer_t vita2d_vita_font = { vita2d_font_init_font, vita2d_font_free_font, vita2d_font_render_msg, "vita2dfont", vita2d_font_get_glyph, - vita2d_font_bind_block, + NULL, /* bind_block */ NULL, /* flush */ vita2d_font_get_message_width, }; diff --git a/gfx/drivers_font/vulkan_raster_font.c b/gfx/drivers_font/vulkan_raster_font.c index 33c9c02326..5fe9a13abc 100644 --- a/gfx/drivers_font/vulkan_raster_font.c +++ b/gfx/drivers_font/vulkan_raster_font.c @@ -60,7 +60,8 @@ static void *vulkan_raster_font_init_font(void *data, font->vk = (vk_t*)data; - if (!font_renderer_create_default((const void**)&font->font_driver, + if (!font_renderer_create_default( + &font->font_driver, &font->font_data, font_path, font_size)) { RARCH_WARN("Couldn't initialize font renderer.\n"); @@ -435,24 +436,13 @@ static const struct font_glyph *vulkan_raster_font_get_glyph( return glyph; } -static void vulkan_raster_font_flush_block(unsigned width, unsigned height, - void *data, video_frame_info_t *video_info) -{ - (void)data; -} - -static void vulkan_raster_font_bind_block(void *data, void *userdata) -{ - (void)data; -} - font_renderer_t vulkan_raster_font = { vulkan_raster_font_init_font, vulkan_raster_font_free_font, vulkan_raster_font_render_msg, "Vulkan raster", vulkan_raster_font_get_glyph, - vulkan_raster_font_bind_block, - vulkan_raster_font_flush_block, + NULL, /* bind_block */ + NULL, /* flush_block */ vulkan_get_message_width }; diff --git a/gfx/drivers_font/wiiu_font.c b/gfx/drivers_font/wiiu_font.c index 6552cf4848..447360e582 100644 --- a/gfx/drivers_font/wiiu_font.c +++ b/gfx/drivers_font/wiiu_font.c @@ -47,8 +47,9 @@ static void* wiiu_font_init_font(void* data, const char* font_path, if (!font) return NULL; - if (!font_renderer_create_default((const void**)&font->font_driver, - &font->font_data, font_path, font_size)) + if (!font_renderer_create_default( + &font->font_driver, + &font->font_data, font_path, font_size)) { RARCH_WARN("Couldn't initialize font renderer.\n"); free(font); @@ -376,12 +377,6 @@ static const struct font_glyph* wiiu_font_get_glyph( return font->font_driver->get_glyph((void*)font->font_driver, code); } -static void wiiu_font_bind_block(void* data, void* userdata) -{ - (void)data; -} - - font_renderer_t wiiu_font = { wiiu_font_init_font, @@ -389,7 +384,7 @@ font_renderer_t wiiu_font = wiiu_font_render_msg, "wiiufont", wiiu_font_get_glyph, - wiiu_font_bind_block, + NULL, /* bind_block */ NULL, /* flush */ wiiu_font_get_message_width, }; diff --git a/gfx/drivers_font/xdk360_fonts.cpp b/gfx/drivers_font/xdk360_fonts.cpp index bc67a064f5..cc5472ff9b 100644 --- a/gfx/drivers_font/xdk360_fonts.cpp +++ b/gfx/drivers_font/xdk360_fonts.cpp @@ -459,7 +459,7 @@ static void xdk360_render_msg_post(xdk360_video_font_t * font) d3d9_set_texture(dev, 0, NULL); d3d9_set_vertex_declaration(dev, NULL); - d3d9_set_vertex_shader(dev, 0, NULL); + d3d9_set_vertex_shader(dev, NULL); d3d9_set_pixel_shader(dev, NULL); d3d9_set_render_state(dev, D3DRS_VIEWPORTENABLE, font->m_dwSavedState); } @@ -496,7 +496,7 @@ static void xdk360_render_msg_pre(xdk360_video_font_t * font) d3d9_set_render_state(dev, D3DRS_VIEWPORTENABLE, FALSE); d3d9_set_vertex_declaration(dev, font->s_FontLocals.m_pFontVertexDecl); - d3d9_set_vertex_shader(dev, 0, font->s_FontLocals.m_pFontVertexShader); + d3d9_set_vertex_shader(dev, font->s_FontLocals.m_pFontVertexShader); d3d9_set_pixel_shader(dev, font->s_FontLocals.m_pFontPixelShader); d3d9_set_vertex_shader_constantf(dev, 2, vTexScale, 1); } diff --git a/gfx/drivers_font_renderer/freetype.c b/gfx/drivers_font_renderer/freetype.c index b0bef73a4a..7261e038af 100644 --- a/gfx/drivers_font_renderer/freetype.c +++ b/gfx/drivers_font_renderer/freetype.c @@ -25,7 +25,6 @@ #include #include #include -#include "../../configuration.h" #ifdef WIIU #include @@ -85,13 +84,13 @@ static freetype_atlas_slot_t* font_renderer_get_slot(ft_font_renderer_t *handle) unsigned oldest = 0; for (i = 1; i < FT_ATLAS_SIZE; i++) - if((handle->usage_counter - handle->atlas_slots[i].last_used) > + if ((handle->usage_counter - handle->atlas_slots[i].last_used) > (handle->usage_counter - handle->atlas_slots[oldest].last_used)) oldest = i; /* remove from map */ map_id = handle->atlas_slots[oldest].charcode & 0xFF; - if(handle->uc_map[map_id] == &handle->atlas_slots[oldest]) + if (handle->uc_map[map_id] == &handle->atlas_slots[oldest]) handle->uc_map[map_id] = handle->atlas_slots[oldest].next; else if (handle->uc_map[map_id]) { @@ -121,7 +120,7 @@ static const struct font_glyph *font_renderer_ft_get_glyph( while(atlas_slot) { - if(atlas_slot->charcode == charcode) + if (atlas_slot->charcode == charcode) { atlas_slot->last_used = handle->usage_counter++; return &atlas_slot->glyph; @@ -204,7 +203,7 @@ static bool font_renderer_create_atlas(ft_font_renderer_t *handle, float font_si font_renderer_ft_get_glyph(handle, i); for (i = 0; i < 256; i++) - if(isalnum(i)) + if (isalnum(i)) font_renderer_ft_get_glyph(handle, i); return true; @@ -228,12 +227,12 @@ static void *font_renderer_ft_init(const char *font_path, float font_size) goto error; #ifdef WIIU - if(!*font_path) + if (!*font_path) { - void* font_data = NULL; + void* font_data = NULL; uint32_t font_size = 0; - if(!OSGetSharedData(SHARED_FONT_DEFAULT, 0, &font_data, &font_size)) + if (!OSGetSharedData(SHARED_FONT_DEFAULT, 0, &font_data, &font_size)) goto error; err = FT_New_Memory_Face(handle->lib, font_data, font_size, 0, &handle->face); @@ -299,17 +298,22 @@ static const char *font_renderer_ft_get_default_font(void) return ""; #else size_t i; - settings_t *settings = config_get_ptr(); +#if 0 char asset_path[PATH_MAX_LENGTH]; +#endif for (i = 0; i < ARRAY_SIZE(font_paths); i++) { +#if 0 /* Check if we are getting the font from the assets directory. */ if (string_is_equal(font_paths[i], "assets://pkg/osd-font.ttf")) { - fill_pathname_join(asset_path, settings->paths.directory_assets, "pkg/osd-font.ttf", PATH_MAX_LENGTH); + settings_t *settings = config_get_ptr(); + fill_pathname_join(asset_path, + settings->paths.directory_assets, "pkg/osd-font.ttf", PATH_MAX_LENGTH); font_paths[i] = asset_path; } +#endif if (filestream_exists(font_paths[i])) return font_paths[i]; diff --git a/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c b/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c index 81b6ab630a..cfe05f895b 100644 --- a/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c +++ b/gfx/drivers_renderchain/d3d9_hlsl_renderchain.c @@ -62,7 +62,7 @@ static void *d3d9_hlsl_get_constant_by_name(void *data, const char *name) static INLINE void d3d9_hlsl_set_param_2f(void *data, void *userdata, const char *name, const void *values) { LPD3DXCONSTANTTABLE prog = (LPD3DXCONSTANTTABLE)data; - D3DXHANDLE param = d3d9_hlsl_get_constant_by_name(prog, name); + D3DXHANDLE param = (D3DXHANDLE)d3d9_hlsl_get_constant_by_name(prog, name); if (param) d3d9x_constant_table_set_float_array((LPDIRECT3DDEVICE9)userdata, prog, (void*)param, values, 2); } @@ -70,7 +70,7 @@ static INLINE void d3d9_hlsl_set_param_2f(void *data, void *userdata, const char static INLINE void d3d9_hlsl_set_param_1f(void *data, void *userdata, const char *name, const void *value) { LPD3DXCONSTANTTABLE prog = (LPD3DXCONSTANTTABLE)data; - D3DXHANDLE param = d3d9_hlsl_get_constant_by_name(prog, name); + D3DXHANDLE param = (D3DXHANDLE)d3d9_hlsl_get_constant_by_name(prog, name); float *val = (float*)value; if (param) d3d9x_constant_table_set_float(prog, (LPDIRECT3DDEVICE9)userdata, (void*)param, *val); @@ -90,7 +90,7 @@ static INLINE void d3d9_hlsl_set_param_matrix(void *data, void *userdata, const char *name, const void *values) { LPD3DXCONSTANTTABLE prog = (LPD3DXCONSTANTTABLE)data; - D3DXHANDLE param = d3d9_hlsl_get_constant_by_name(prog, name); + D3DXHANDLE param = (D3DXHANDLE)d3d9_hlsl_get_constant_by_name(prog, name); if (param) d3d9x_constant_table_set_matrix((LPDIRECT3DDEVICE9)userdata, prog, (void*)param, (D3DMATRIX*)values); @@ -390,7 +390,6 @@ static void d3d9_hlsl_destroy_resources(hlsl_renderchain_t *chain) static void hlsl_d3d9_renderchain_free(void *data) { - unsigned i; hlsl_renderchain_t *chain = (hlsl_renderchain_t*)data; if (!chain) diff --git a/gfx/drivers_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index ad12be1116..8d8dbb4590 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -117,12 +117,18 @@ static const char *glsl_prefixes[] = { #include "../drivers/gl_shaders/core_alpha_blend.glsl.frag.h" #ifdef HAVE_SHADERPIPELINE +#include "../drivers/gl_shaders/core_pipeline_snow.glsl.frag.h" +#include "../drivers/gl_shaders/core_pipeline_snow_simple.glsl.frag.h" #include "../drivers/gl_shaders/core_pipeline_xmb_ribbon.glsl.frag.h" +#include "../drivers/gl_shaders/core_pipeline_xmb_ribbon_simple.glsl.frag.h" +#include "../drivers/gl_shaders/core_pipeline_bokeh.glsl.frag.h" +#include "../drivers/gl_shaders/core_pipeline_snowflake.glsl.frag.h" #include "../drivers/gl_shaders/legacy_pipeline_xmb_ribbon_simple.glsl.vert.h" #include "../drivers/gl_shaders/modern_pipeline_xmb_ribbon_simple.glsl.vert.h" -#include "../drivers/gl_shaders/modern_pipeline_snow.glsl.vert.h" #include "../drivers/gl_shaders/pipeline_xmb_ribbon_simple.glsl.frag.h" #include "../drivers/gl_shaders/pipeline_snow.glsl.frag.h" +#include "../drivers/gl_shaders/pipeline_snow.glsl.vert.h" +#include "../drivers/gl_shaders/pipeline_snow_core.glsl.vert.h" #include "../drivers/gl_shaders/pipeline_snow_simple.glsl.frag.h" #include "../drivers/gl_shaders/legacy_pipeline_snow.glsl.vert.h" #include "../drivers/gl_shaders/legacy_pipeline_xmb_ribbon.glsl.vert.h" @@ -816,6 +822,7 @@ static void gl_glsl_init_menu_shaders(void *data) #endif shader_prog_info.is_file = false; + RARCH_LOG("[GLSL]: Compiling ribbon shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU, @@ -825,8 +832,9 @@ static void gl_glsl_init_menu_shaders(void *data) &glsl->uniforms[VIDEO_SHADER_MENU]); shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_simple_modern : stock_vertex_xmb_ribbon_simple_legacy; - shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple; + shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_ribbon_simple_core : stock_fragment_xmb_ribbon_simple; + RARCH_LOG("[GLSL]: Compiling simple ribbon shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_2, @@ -836,12 +844,14 @@ static void gl_glsl_init_menu_shaders(void *data) &glsl->uniforms[VIDEO_SHADER_MENU_2]); #if defined(HAVE_OPENGLES) - shader_prog_info.vertex = stock_vertex_xmb_snow_modern; -#else - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; -#endif + shader_prog_info.vertex = stock_vertex_xmb_snow; shader_prog_info.fragment = stock_fragment_xmb_simple_snow; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy; + shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_simple_snow_core : stock_fragment_xmb_simple_snow; +#endif + RARCH_LOG("[GLSL]: Compiling snow shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_3, @@ -851,12 +861,14 @@ static void gl_glsl_init_menu_shaders(void *data) &glsl->uniforms[VIDEO_SHADER_MENU_3]); #if defined(HAVE_OPENGLES) - shader_prog_info.vertex = stock_vertex_xmb_snow_modern; -#else - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; -#endif + shader_prog_info.vertex = stock_vertex_xmb_snow; shader_prog_info.fragment = stock_fragment_xmb_snow; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy; + shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_snow_core : stock_fragment_xmb_snow; +#endif + RARCH_LOG("[GLSL]: Compiling modern snow shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_4, @@ -866,12 +878,14 @@ static void gl_glsl_init_menu_shaders(void *data) &glsl->uniforms[VIDEO_SHADER_MENU_4]); #if defined(HAVE_OPENGLES) - shader_prog_info.vertex = stock_vertex_xmb_snow_modern; -#else - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; -#endif + shader_prog_info.vertex = stock_vertex_xmb_snow; shader_prog_info.fragment = stock_fragment_xmb_bokeh; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy; + shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_bokeh_core : stock_fragment_xmb_bokeh; +#endif + RARCH_LOG("[GLSL]: Compiling bokeh shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_5, @@ -881,12 +895,14 @@ static void gl_glsl_init_menu_shaders(void *data) &glsl->uniforms[VIDEO_SHADER_MENU_5]); #if defined(HAVE_OPENGLES) - shader_prog_info.vertex = stock_vertex_xmb_snow_modern; -#else - shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; -#endif + shader_prog_info.vertex = stock_vertex_xmb_snow; shader_prog_info.fragment = stock_fragment_xmb_snowflake; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy; + shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_snowflake_core : stock_fragment_xmb_snowflake; +#endif + RARCH_LOG("[GLSL]: Compiling snowflake shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_6, diff --git a/gfx/font_driver.c b/gfx/font_driver.c index 4d57e0162b..e8af0dca9e 100644 --- a/gfx/font_driver.c +++ b/gfx/font_driver.c @@ -46,13 +46,12 @@ static const font_renderer_driver_t *font_backends[] = { static void *video_font_driver = NULL; -int font_renderer_create_default(const void **data, void **handle, +int font_renderer_create_default( + const font_renderer_driver_t **drv, + void **handle, const char *font_path, unsigned font_size) { - unsigned i; - const font_renderer_driver_t **drv = - (const font_renderer_driver_t**)data; for (i = 0; font_backends[i]; i++) { @@ -76,7 +75,7 @@ int font_renderer_create_default(const void **data, void **handle, font_backends[i]->ident); } - *drv = NULL; + *drv = NULL; *handle = NULL; return 0; @@ -217,6 +216,37 @@ static bool caca_font_init_first( } #endif +#ifdef HAVE_SIXEL +static const font_renderer_t *sixel_font_backends[] = { + &sixel_font, + NULL, +}; + +static bool sixel_font_init_first( + const void **font_driver, void **font_handle, + void *video_data, const char *font_path, + float font_size, bool is_threaded) +{ + unsigned i; + + for (i = 0; sixel_font_backends[i]; i++) + { + void *data = sixel_font_backends[i]->init( + video_data, font_path, font_size, + is_threaded); + + if (!data) + continue; + + *font_driver = sixel_font_backends[i]; + *font_handle = data; + return true; + } + + return false; +} +#endif + #ifdef DJGPP static const font_renderer_t *vga_font_backends[] = { &vga_font, @@ -595,6 +625,11 @@ static bool font_init_first( return caca_font_init_first(font_driver, font_handle, video_data, font_path, font_size, is_threaded); #endif +#ifdef HAVE_SIXEL + case FONT_DRIVER_RENDER_SIXEL: + return sixel_font_init_first(font_driver, font_handle, + video_data, font_path, font_size, is_threaded); +#endif #if defined(_WIN32) && !defined(_XBOX) case FONT_DRIVER_RENDER_GDI: return gdi_font_init_first(font_driver, font_handle, diff --git a/gfx/font_driver.h b/gfx/font_driver.h index a843b2709f..a32b834e85 100644 --- a/gfx/font_driver.h +++ b/gfx/font_driver.h @@ -124,8 +124,10 @@ typedef struct } font_data_t; /* font_path can be NULL for default font. */ -int font_renderer_create_default(const void **driver, - void **handle, const char *font_path, unsigned font_size); +int font_renderer_create_default( + const font_renderer_driver_t **drv, + void **handle, + const char *font_path, unsigned font_size); void font_driver_render_msg(video_frame_info_t *video_info, void *font_data, const char *msg, const struct font_params *params); @@ -170,6 +172,7 @@ extern font_renderer_t d3d12_font; extern font_renderer_t caca_font; extern font_renderer_t gdi_font; extern font_renderer_t vga_font; +extern font_renderer_t sixel_font; extern font_renderer_driver_t stb_font_renderer; extern font_renderer_driver_t stb_unicode_font_renderer; diff --git a/gfx/video_defines.h b/gfx/video_defines.h index 473c72b68f..61b09d393a 100644 --- a/gfx/video_defines.h +++ b/gfx/video_defines.h @@ -35,6 +35,7 @@ enum aspect_ratio ASPECT_RATIO_16_9, ASPECT_RATIO_16_10, ASPECT_RATIO_16_15, + ASPECT_RATIO_21_9, ASPECT_RATIO_1_1, ASPECT_RATIO_2_1, ASPECT_RATIO_3_2, @@ -93,6 +94,7 @@ enum font_driver_render_api FONT_DRIVER_RENDER_VULKAN_API, FONT_DRIVER_RENDER_METAL_API, FONT_DRIVER_RENDER_CACA, + FONT_DRIVER_RENDER_SIXEL, FONT_DRIVER_RENDER_GDI, FONT_DRIVER_RENDER_VGA }; diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 56f4bd3a90..30e7f4d4b4 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -74,9 +74,9 @@ #define FPS_UPDATE_INTERVAL 256 #ifdef HAVE_THREADS -#define video_driver_is_threaded() ((!video_driver_is_hw_context() && video_driver_threaded) ? true : false) +#define video_driver_is_threaded_internal() ((!video_driver_is_hw_context() && video_driver_threaded) ? true : false) #else -#define video_driver_is_threaded() (false) +#define video_driver_is_threaded_internal() (false) #endif #ifdef HAVE_THREADS @@ -239,6 +239,7 @@ struct aspect_ratio_elem aspectratio_lut[ASPECT_RATIO_END] = { { "16:9", 1.7778f }, { "16:10", 1.6f }, { "16:15", 16.0f / 15.0f }, + { "21:9", 21.0f / 9.0f }, { "1:1", 1.0f }, { "2:1", 2.0f }, { "3:2", 1.5f }, @@ -339,11 +340,14 @@ static const video_driver_t *video_drivers[] = { #if defined(_WIN32) && !defined(_XBOX) &video_gdi, #endif -#ifdef HAVE_CACA - &video_caca, -#endif #ifdef DJGPP &video_vga, +#endif +#ifdef HAVE_SIXEL + &video_sixel, +#endif +#ifdef HAVE_CACA + &video_caca, #endif &video_null, NULL, @@ -408,6 +412,9 @@ static const gfx_ctx_driver_t *gfx_ctx_drivers[] = { #endif #if defined(_WIN32) && !defined(_XBOX) &gfx_ctx_gdi, +#endif +#ifdef HAVE_SIXEL + &gfx_ctx_sixel, #endif &gfx_ctx_null, NULL @@ -498,6 +505,11 @@ const char* config_get_video_driver_options(void) return char_list_new_special(STRING_LIST_VIDEO_DRIVERS, NULL); } +bool video_driver_is_threaded(void) +{ + return video_driver_is_threaded_internal(); +} + #ifdef HAVE_VULKAN static bool hw_render_context_is_vulkan(enum retro_hw_context_type type) { @@ -545,7 +557,7 @@ void video_driver_set_threaded(bool val) void *video_driver_get_ptr(bool force_nonthreaded_data) { #ifdef HAVE_THREADS - if (video_driver_is_threaded() && !force_nonthreaded_data) + if (video_driver_is_threaded_internal() && !force_nonthreaded_data) return video_thread_get_ptr(NULL); #endif @@ -834,7 +846,7 @@ static void video_driver_pixel_converter_free(void) static void video_driver_free_internal(void) { #ifdef HAVE_THREADS - bool is_threaded = video_driver_is_threaded(); + bool is_threaded = video_driver_is_threaded_internal(); #endif command_event(CMD_EVENT_OVERLAY_DEINIT, NULL); @@ -1038,7 +1050,7 @@ static bool video_driver_init_internal(bool *video_is_threaded) video_driver_find_driver(); #ifdef HAVE_THREADS - video.is_threaded = video_driver_is_threaded(); + video.is_threaded = video_driver_is_threaded_internal(); *video_is_threaded = video.is_threaded; if (video.is_threaded) @@ -1238,7 +1250,7 @@ void video_driver_cached_frame_get(const void **data, unsigned *width, void video_driver_get_size(unsigned *width, unsigned *height) { #ifdef HAVE_THREADS - bool is_threaded = video_driver_is_threaded(); + bool is_threaded = video_driver_is_threaded_internal(); video_driver_threaded_lock(is_threaded); #endif if (width) @@ -1253,7 +1265,7 @@ void video_driver_get_size(unsigned *width, unsigned *height) void video_driver_set_size(unsigned *width, unsigned *height) { #ifdef HAVE_THREADS - bool is_threaded = video_driver_is_threaded(); + bool is_threaded = video_driver_is_threaded_internal(); video_driver_threaded_lock(is_threaded); #endif if (width) @@ -1311,7 +1323,7 @@ bool video_monitor_fps_statistics(double *refresh_rate, unsigned samples = 0; #ifdef HAVE_THREADS - if (video_driver_is_threaded()) + if (video_driver_is_threaded_internal()) return false; #endif @@ -1595,6 +1607,7 @@ void video_driver_destroy(void) video_driver_cache_context_ack = false; video_driver_record_gpu_buffer = NULL; current_video = NULL; + video_driver_set_cached_frame_ptr(NULL); } void video_driver_set_cached_frame_ptr(const void *data) @@ -1779,6 +1792,7 @@ bool video_driver_init(bool *video_is_threaded) { video_driver_lock_new(); video_driver_filter_free(); + video_driver_set_cached_frame_ptr(NULL); return video_driver_init_internal(video_is_threaded); } @@ -1792,6 +1806,7 @@ void video_driver_free(void) video_driver_free_internal(); video_driver_lock_free(); video_driver_data = NULL; + video_driver_set_cached_frame_ptr(NULL); } void video_driver_monitor_reset(void) @@ -2671,7 +2686,7 @@ bool video_driver_texture_load(void *data, return false; *id = video_driver_poke->load_texture(video_driver_data, data, - video_driver_is_threaded(), + video_driver_is_threaded_internal(), filter_type); return true; @@ -2717,7 +2732,7 @@ void video_driver_build_info(video_frame_info_t *video_info) struct retro_hw_render_callback *hwr = video_driver_get_hw_context(); #ifdef HAVE_THREADS - bool is_threaded = video_driver_is_threaded(); + bool is_threaded = video_driver_is_threaded_internal(); video_driver_threaded_lock(is_threaded); #endif settings = config_get_ptr(); diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 2077ec383e..4dd35f110b 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -95,6 +95,7 @@ enum gfx_ctx_api GFX_CTX_DIRECT3D12_API, GFX_CTX_OPENVG_API, GFX_CTX_VULKAN_API, + GFX_CTX_SIXEL_API, GFX_CTX_METAL_API, GFX_CTX_GDI_API, GFX_CTX_GX_API, @@ -1245,6 +1246,8 @@ extern bool (*video_driver_cb_has_focus)(void); bool video_driver_started_fullscreen(void); +bool video_driver_is_threaded(void); + extern video_driver_t video_gl; extern video_driver_t video_vulkan; extern video_driver_t video_metal; @@ -1273,6 +1276,7 @@ extern video_driver_t video_xshm; extern video_driver_t video_caca; extern video_driver_t video_gdi; extern video_driver_t video_vga; +extern video_driver_t video_sixel; extern video_driver_t video_null; extern const gfx_ctx_driver_t gfx_ctx_osmesa; @@ -1294,6 +1298,7 @@ extern const gfx_ctx_driver_t gfx_ctx_emscripten; extern const gfx_ctx_driver_t gfx_ctx_opendingux_fbdev; extern const gfx_ctx_driver_t gfx_ctx_khr_display; extern const gfx_ctx_driver_t gfx_ctx_gdi; +extern const gfx_ctx_driver_t gfx_ctx_sixel; extern const gfx_ctx_driver_t gfx_ctx_null; diff --git a/griffin/griffin.c b/griffin/griffin.c index d2700ce1bf..1eaeed04c3 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1235,11 +1235,15 @@ MENU #include "../menu/drivers/rgui.c" #endif -#if defined(HAVE_OPENGL) || defined(HAVE_VITA2D) || defined(_3DS) || defined(_MSC_VER) || defined(__wiiu__) +#if defined(HAVE_OPENGL) || defined(HAVE_VITA2D) || defined(_3DS) || defined(_MSC_VER) || defined(__wiiu__) || defined(HAVE_METAL) #ifdef HAVE_XMB #include "../menu/drivers/xmb.c" #endif +#ifdef HAVE_STRIPES +#include "../menu/drivers/stripes.c" +#endif + #ifdef HAVE_MATERIALUI #include "../menu/drivers/materialui.c" #endif diff --git a/griffin/griffin_objc.m b/griffin/griffin_objc.m index c009622d53..1393d61bf1 100644 --- a/griffin/griffin_objc.m +++ b/griffin/griffin_objc.m @@ -60,11 +60,10 @@ #ifdef HAVE_METAL #import "../gfx/common/metal/Context.m" #import "../gfx/common/metal/Filter.m" -#import "../gfx/common/metal/PixelConverter.m" -#import "../gfx/common/metal/Renderer.m" #import "../gfx/common/metal/RendererCommon.m" #import "../gfx/common/metal/View.m" #import "../gfx/common/metal/TexturedView.m" +#import "../gfx/common/metal/MenuDisplay.m" #import "../gfx/common/metal_common.m" #import "../gfx/drivers/metal.m" #import "../menu/drivers_display/menu_display_metal.m" diff --git a/input/drivers/android_input.c b/input/drivers/android_input.c index 7efdbdbf9a..5121418acf 100644 --- a/input/drivers/android_input.c +++ b/input/drivers/android_input.c @@ -68,6 +68,9 @@ enum { /* Use this to enable/disable using the touch screen as mouse */ #define ENABLE_TOUCH_SCREEN_MOUSE 1 +/* TODO/FIXME - + * fix game focus toggle */ + typedef struct { float x; diff --git a/input/drivers/cocoa_input.c b/input/drivers/cocoa_input.c index 2cba64d44f..fa8454a6d7 100644 --- a/input/drivers/cocoa_input.c +++ b/input/drivers/cocoa_input.c @@ -33,6 +33,9 @@ #include "../drivers_keyboard/keyboard_event_apple.h" +/* TODO/FIXME - + * fix game focus toggle */ + /* Forward declarations */ float get_backing_scale_factor(void); diff --git a/input/drivers/ctr_input.c b/input/drivers/ctr_input.c index 90702187d4..86446f3ec4 100644 --- a/input/drivers/ctr_input.c +++ b/input/drivers/ctr_input.c @@ -29,6 +29,9 @@ #define MAX_PADS 1 +/* TODO/FIXME - + * fix game focus toggle */ + typedef struct ctr_input { bool blocked; diff --git a/input/drivers/dinput.c b/input/drivers/dinput.c index 35a252702e..4d3b7cace7 100644 --- a/input/drivers/dinput.c +++ b/input/drivers/dinput.c @@ -328,8 +328,10 @@ static bool dinput_is_pressed(struct dinput_input *di, { const struct retro_keybind *bind = &binds[id]; - if (!di->blocked && (bind->key < RETROK_LAST) && dinput_keyboard_pressed(di, bind->key)) - return true; + if ((bind->key < RETROK_LAST) && dinput_keyboard_pressed(di, bind->key)) + if ((id == RARCH_GAME_FOCUS_TOGGLE) || !di->blocked) + return true; + if (binds && binds[id].valid) { if (dinput_mbutton_pressed(di, port, bind->mbutton)) diff --git a/input/drivers/dos_input.c b/input/drivers/dos_input.c index 4c168cd5a2..2485b3b3fe 100644 --- a/input/drivers/dos_input.c +++ b/input/drivers/dos_input.c @@ -21,6 +21,9 @@ #include "../input_keymaps.h" #include "../drivers_keyboard/keyboard_event_dos.h" +/* TODO/FIXME - + * fix game focus toggle */ + typedef struct dos_input { const input_device_driver_t *joypad; diff --git a/input/drivers/gx_input.c b/input/drivers/gx_input.c index 8e4c2d42a8..5fd6801dca 100644 --- a/input/drivers/gx_input.c +++ b/input/drivers/gx_input.c @@ -31,6 +31,9 @@ #define MAX_PADS 4 #endif +/* TODO/FIXME - + * fix game focus toggle */ + typedef struct gx_input { bool blocked; diff --git a/input/drivers/linuxraw_input.c b/input/drivers/linuxraw_input.c index 72928de443..c3c3a1e21c 100644 --- a/input/drivers/linuxraw_input.c +++ b/input/drivers/linuxraw_input.c @@ -32,6 +32,9 @@ #include "../input_keymaps.h" #include "../input_driver.h" +/* TODO/FIXME - + * fix game focus toggle */ + typedef struct linuxraw_input { bool blocked; diff --git a/input/drivers/ps3_input.c b/input/drivers/ps3_input.c index f18c9ea327..7d8fee8b83 100644 --- a/input/drivers/ps3_input.c +++ b/input/drivers/ps3_input.c @@ -40,6 +40,9 @@ #define MAX_PADS 7 #endif +/* TODO/FIXME - + * fix game focus toggle */ + typedef struct { float x; diff --git a/input/drivers/psp_input.c b/input/drivers/psp_input.c index bc0cd827c0..1247a2c1f4 100644 --- a/input/drivers/psp_input.c +++ b/input/drivers/psp_input.c @@ -43,6 +43,9 @@ #include "../input_driver.h" +/* TODO/FIXME - + * fix game focus toggle */ + typedef struct psp_input { bool blocked; diff --git a/input/drivers/qnx_input.c b/input/drivers/qnx_input.c index 47b663a3ae..276a1f3ca2 100644 --- a/input/drivers/qnx_input.c +++ b/input/drivers/qnx_input.c @@ -736,12 +736,14 @@ static bool qnx_is_pressed(qnx_input_t *qnx, unsigned port, unsigned id) { const struct retro_keybind *bind = &binds[id]; + int key = bind->key; if (id >= RARCH_BIND_LIST_END) return false; - if (!qnx->blocked && qnx_keyboard_pressed(qnx, bind->key)) - return true; + if (qnx_keyboard_pressed(qnx, key)) + if ((id == RARCH_GAME_FOCUS_TOGGLE) || !qnx->blocked) + return true; if (binds && binds[id].valid && input_joypad_pressed(qnx->joypad, joypad_info, port, binds, id)) return true; diff --git a/input/drivers/rwebinput_input.c b/input/drivers/rwebinput_input.c index 3108215bd1..ae4904c53a 100644 --- a/input/drivers/rwebinput_input.c +++ b/input/drivers/rwebinput_input.c @@ -479,9 +479,9 @@ static bool rwebinput_is_pressed(rwebinput_input_t *rwebinput, const struct retro_keybind *bind = &binds[id]; int key = bind->key; - if (!rwebinput->blocked && (bind->key < RETROK_LAST) && - rwebinput_key_pressed(rwebinput, key)) - return true; + if ((key < RETROK_LAST) && rwebinput_key_pressed(rwebinput, key)) + if ((id == RARCH_GAME_FOCUS_TOGGLE) || !rwebinput->blocked) + return true; if (bind->valid) { diff --git a/input/drivers/sdl_input.c b/input/drivers/sdl_input.c index ead5a79903..3f42839bec 100644 --- a/input/drivers/sdl_input.c +++ b/input/drivers/sdl_input.c @@ -31,6 +31,9 @@ #include "../../verbosity.h" #include "../../tasks/tasks_internal.h" +/* TODO/FIXME - + * fix game focus toggle */ + typedef struct sdl_input { bool blocked; diff --git a/input/drivers/switch_input.c b/input/drivers/switch_input.c index a50bcdc80f..d363e22e74 100644 --- a/input/drivers/switch_input.c +++ b/input/drivers/switch_input.c @@ -13,6 +13,9 @@ #define MAX_PADS 10 +/* TODO/FIXME - + * fix game focus toggle */ + typedef struct switch_input { const input_device_driver_t *joypad; diff --git a/input/drivers/udev_input.c b/input/drivers/udev_input.c index b7de9b7253..2c63754ec6 100644 --- a/input/drivers/udev_input.c +++ b/input/drivers/udev_input.c @@ -900,7 +900,8 @@ static bool udev_is_pressed(udev_input_t *udev, const struct retro_keybind *bind = &binds[id]; if ( (bind->key < RETROK_LAST) && udev_keyboard_pressed(udev, bind->key) ) - return true; + if ((id == RARCH_GAME_FOCUS_TOGGLE) || !udev->blocked) + return true; if (binds && binds[id].valid) { diff --git a/input/drivers/wayland_input.c b/input/drivers/wayland_input.c index 0c23db52f4..0363624320 100644 --- a/input/drivers/wayland_input.c +++ b/input/drivers/wayland_input.c @@ -47,6 +47,9 @@ #include "../../verbosity.h" +/* TODO/FIXME - + * fix game focus toggle */ + /* Forward declaration */ void flush_wayland_fd(void *data); diff --git a/input/drivers/winraw_input.c b/input/drivers/winraw_input.c index d56deb4c75..35d40d6ad3 100644 --- a/input/drivers/winraw_input.c +++ b/input/drivers/winraw_input.c @@ -403,8 +403,9 @@ static bool winraw_is_pressed(winraw_input_t *wr, { const struct retro_keybind *bind = &binds[id]; - if (!wr->kbd_mapp_block && (bind->key < RETROK_LAST) && winraw_keyboard_pressed(wr, bind->key)) - return true; + if ((bind->key < RETROK_LAST) && winraw_keyboard_pressed(wr, bind->key)) + if ((id == RARCH_GAME_FOCUS_TOGGLE) || !wr->kbd_mapp_block) + return true; if (binds && binds[id].valid) { if (winraw_mbutton_pressed(wr, port, bind->mbutton)) diff --git a/input/drivers/x11_input.c b/input/drivers/x11_input.c index d2b1bcd9f5..466c94b2e3 100644 --- a/input/drivers/x11_input.c +++ b/input/drivers/x11_input.c @@ -131,8 +131,9 @@ static bool x_is_pressed(x11_input_t *x11, { const struct retro_keybind *bind = &binds[id]; - if ( (bind->key < RETROK_LAST) && x_keyboard_pressed(x11, bind->key) ) - return true; + if ((bind->key < RETROK_LAST) && x_keyboard_pressed(x11, bind->key) ) + if ((id == RARCH_GAME_FOCUS_TOGGLE) || !x11->blocked) + return true; if (binds && binds[id].valid) { diff --git a/input/drivers/xdk_xinput_input.c b/input/drivers/xdk_xinput_input.c index 3d4f1363df..0710c223d0 100644 --- a/input/drivers/xdk_xinput_input.c +++ b/input/drivers/xdk_xinput_input.c @@ -32,6 +32,9 @@ #define MAX_PADS 4 +/* TODO/FIXME - + * fix game focus toggle */ + typedef struct xdk_input { bool blocked; diff --git a/input/drivers/xenon360_input.c b/input/drivers/xenon360_input.c index e87d413992..2715055761 100644 --- a/input/drivers/xenon360_input.c +++ b/input/drivers/xenon360_input.c @@ -27,6 +27,9 @@ #define MAX_PADS 4 +/* TODO/FIXME - + * fix game focus toggle */ + static uint64_t state[MAX_PADS]; static void xenon360_input_poll(void *data) diff --git a/input/drivers_joypad/dinput_joypad.c b/input/drivers_joypad/dinput_joypad.c index c92d9179ad..de6f8f1d4c 100644 --- a/input/drivers_joypad/dinput_joypad.c +++ b/input/drivers_joypad/dinput_joypad.c @@ -44,6 +44,8 @@ struct dinput_joypad char* joy_friendly_name; int32_t vid; int32_t pid; + LPDIRECTINPUTEFFECT rumble_iface[2]; + DIEFFECT rumble_props; }; static struct dinput_joypad g_pads[MAX_USERS]; @@ -98,6 +100,17 @@ static void dinput_joypad_destroy(void) { if (g_pads[i].joypad) { + if (g_pads[i].rumble_iface[0]) + { + IDirectInputEffect_Stop(g_pads[i].rumble_iface[0]); + IDirectInputEffect_Release(g_pads[i].rumble_iface[0]); + } + if (g_pads[i].rumble_iface[1]) + { + IDirectInputEffect_Stop(g_pads[i].rumble_iface[1]); + IDirectInputEffect_Release(g_pads[i].rumble_iface[1]); + } + IDirectInputDevice8_Unacquire(g_pads[i].joypad); IDirectInputDevice8_Release(g_pads[i].joypad); } @@ -117,6 +130,49 @@ static void dinput_joypad_destroy(void) dinput_destroy_context(); } +static void dinput_create_rumble_effects(struct dinput_joypad *pad) +{ + LONG direction = 0; + DWORD axis = DIJOFS_X; + DICONSTANTFORCE dicf; + DIENVELOPE dienv; + HRESULT hr; + + dicf.lMagnitude = 0; + + dienv.dwSize = sizeof(DIENVELOPE); + dienv.dwAttackLevel = 5000; + dienv.dwAttackTime = 250000; + dienv.dwFadeLevel = 0; + dienv.dwFadeTime = 250000; + + pad->rumble_props.cAxes = 1; + pad->rumble_props.dwTriggerButton = DIEB_NOTRIGGER; + pad->rumble_props.dwTriggerRepeatInterval = 0; + pad->rumble_props.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + pad->rumble_props.dwDuration = INFINITE; + pad->rumble_props.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; + pad->rumble_props.dwGain = 0; + pad->rumble_props.dwSize = sizeof(DIEFFECT); + pad->rumble_props.dwStartDelay = 0; + pad->rumble_props.lpEnvelope = &dienv; + pad->rumble_props.lpvTypeSpecificParams = &dicf; + pad->rumble_props.rgdwAxes = &axis; + pad->rumble_props.rglDirection = &direction; + + hr = IDirectInputDevice8_CreateEffect(pad->joypad, &GUID_ConstantForce, + &pad->rumble_props, &pad->rumble_iface[0], NULL); + if (hr != DI_OK) + RARCH_WARN("[DINPUT]: Strong rumble unavailable.\n"); + + axis = DIJOFS_Y; + + hr = IDirectInputDevice8_CreateEffect(pad->joypad, &GUID_ConstantForce, + &pad->rumble_props, &pad->rumble_iface[1], NULL); + if (hr != DI_OK) + RARCH_WARN("[DINPUT]: Weak rumble unavailable.\n"); +} + static BOOL CALLBACK enum_axes_cb(const DIDEVICEOBJECTINSTANCE *inst, void *p) { DIPROPRANGE range; @@ -281,11 +337,13 @@ static BOOL CALLBACK enum_joypad_cb(const DIDEVICEINSTANCE *inst, void *p) IDirectInputDevice8_SetDataFormat(*pad, &c_dfDIJoystick2); IDirectInputDevice8_SetCooperativeLevel(*pad, (HWND)video_driver_window_get(), - DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + DISCL_EXCLUSIVE | DISCL_BACKGROUND); IDirectInputDevice8_EnumObjects(*pad, enum_axes_cb, *pad, DIDFT_ABSAXIS); + dinput_create_rumble_effects(&g_pads[g_joypad_cnt]); + #ifdef HAVE_XINPUT if (!is_xinput_pad) #endif @@ -486,6 +544,26 @@ static bool dinput_joypad_query_pad(unsigned pad) return pad < MAX_USERS && g_pads[pad].joypad; } +bool dinput_joypad_set_rumble(unsigned pad, + enum retro_rumble_effect type, uint16_t strenght) +{ + int i = type == RETRO_RUMBLE_STRONG ? 1 : 0; + + if (pad >= g_joypad_cnt || !g_pads[pad].rumble_iface[i]) + return false; + + if (strenght) + { + g_pads[pad].rumble_props.dwGain = + (DWORD)((double)strenght / 65535.0 * (double)DI_FFNOMINALMAX); + IDirectInputEffect_SetParameters(g_pads[pad].rumble_iface[i], + &g_pads[pad].rumble_props, DIEP_GAIN | DIEP_START); + } + else + IDirectInputEffect_Stop(g_pads[pad].rumble_iface[i]); + + return true; +} input_device_driver_t dinput_joypad = { dinput_joypad_init, @@ -495,7 +573,7 @@ input_device_driver_t dinput_joypad = { NULL, dinput_joypad_axis, dinput_joypad_poll, - NULL, + dinput_joypad_set_rumble, dinput_joypad_name, "dinput", }; diff --git a/input/drivers_joypad/xinput_joypad.c b/input/drivers_joypad/xinput_joypad.c index 97528e18ff..a7b5e639a8 100644 --- a/input/drivers_joypad/xinput_joypad.c +++ b/input/drivers_joypad/xinput_joypad.c @@ -109,7 +109,7 @@ extern bool g_xinput_block_pads; #ifdef HAVE_DYNAMIC /* For xinput1_n.dll */ -static dylib_t g_xinput_dll; +static dylib_t g_xinput_dll = NULL; #endif /* Function pointer, to be assigned with dylib_proc */ @@ -173,16 +173,10 @@ const char *xinput_joypad_name(unsigned pad) return XBOX_CONTROLLER_NAMES[xuser]; } -static bool xinput_joypad_init(void *data) -{ - unsigned i, j; - XINPUT_STATE dummy_state; - const char *version = "1.4"; - - (void)data; #ifdef HAVE_DYNAMIC - g_xinput_dll = NULL; - +static bool load_xinput_dll(void) +{ + const char *version = "1.4"; /* Find the correct path to load the DLL from. * Usually this will be from the system directory, * but occasionally a user may wish to use a third-party @@ -207,6 +201,19 @@ static bool xinput_joypad_init(void *data) } RARCH_LOG("[XInput]: Found XInput v%s.\n", version); + return true; +} +#endif + +static bool xinput_joypad_init(void *data) +{ + unsigned i, j; + XINPUT_STATE dummy_state; + +#ifdef HAVE_DYNAMIC + if (!g_xinput_dll) + if (!load_xinput_dll()) + return false; /* If we get here then an xinput DLL is correctly loaded. * First try to load ordinal 100 (XInputGetStateEx). diff --git a/intl/msg_hash_it.h b/intl/msg_hash_it.h index 72187b2d5e..bb5b06fdbd 100644 --- a/intl/msg_hash_it.h +++ b/intl/msg_hash_it.h @@ -2966,7 +2966,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_DISK_IMAGE_APPEND, MSG_HASH(MENU_ENUM_SUBLABEL_MENU_ENUM_THROTTLE_FRAMERATE, "Assicura che i fotogrammi siano attivi all'interno del menu.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_LAYOUT, - "Select a different layout for the XMB interface.") + "Seleziona un layout diverso per l'interfaccia XMB.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_THEME, "Seleziona un tema diverso per l'icona. Le modifiche avranno effetto dopo il riavvio del programma.") MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHADOWS_ENABLE, @@ -3236,9 +3236,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_REBOOT, MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_REBOOT, "Mostra/Nasconde l'opzione 'Riavvia'.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_SHOW_SHUTDOWN, - "Show Shutdown") + "Visualizza spegnimento") MSG_HASH(MENU_ENUM_SUBLABEL_MENU_SHOW_SHUTDOWN, - "Show/hide the 'Shutdown' option.") + "Mostra/Nasconde l'opzione 'Spegnimento'") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_VIEWS_SETTINGS, "Menu rapido") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_VIEWS_SETTINGS, diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 809ef5fa50..779c44441d 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -933,6 +933,10 @@ MSG_HASH(MENU_ENUM_LABEL_REWIND_ENABLE, "rewind_enable") MSG_HASH(MENU_ENUM_LABEL_REWIND_GRANULARITY, "rewind_granularity") +MSG_HASH(MENU_ENUM_LABEL_REWIND_BUFFER_SIZE, + "rewind_buffer_size") +MSG_HASH(MENU_ENUM_LABEL_REWIND_BUFFER_SIZE_STEP, + "rewind_buffer_size_step") MSG_HASH(MENU_ENUM_LABEL_REWIND_SETTINGS, "rewind_settings") MSG_HASH(MENU_ENUM_LABEL_RGUI_BROWSER_DIRECTORY, diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index 9f672e1484..25dd9860ac 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -1,4 +1,4 @@ -MSG_HASH( +MSG_HASH( MSG_COMPILER, "Kompilator" ) @@ -769,6 +769,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_DEFAULT_ALL, "Wszystkie domyślne powiązania") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_TIMEOUT, "Limit czasu powiązania") +MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_HOLD, + "Wiązanie") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DESCRIPTOR_HIDE_UNBOUND, "Ukryj niezwiązane podstawowe deskryptory wejściowe") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_DESCRIPTOR_LABEL_SHOW, @@ -1321,6 +1323,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_CONFIG, "Załaduj konfigurację nagrywania...") MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_DRIVER, "Sterowniki nagrywania") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_DRIVER, + "Sterownik MIDI") MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_ENABLE, "Włącz nagrywanie") MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_PATH, @@ -1959,6 +1963,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_USER_SETTINGS, "Zmień konto, nazwę użytkownika i ustawienia językowe.") MSG_HASH(MENU_ENUM_SUBLABEL_PRIVACY_SETTINGS, "Zmień ustawienia prywatności.") + MSG_HASH(MENU_ENUM_SUBLABEL_MIDI_SETTINGS, + "Zmień ustawienia MIDI.") MSG_HASH(MENU_ENUM_SUBLABEL_DIRECTORY_SETTINGS, "Zmień domyślne katalogi, w których znajdują się pliki.") MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_SETTINGS, @@ -2489,6 +2495,10 @@ MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_BIND_TIMEOUT, "Ilość sekund oczekiwania na przejście do następnej więzi." ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_BIND_HOLD, + "Ilość sekund do przechowywania danych wejściowych, aby je powiązać." + ) MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_TURBO_PERIOD, "Opisuje okres, w którym przełączane są przyciski z włączoną funkcją turbo. Liczby są opisane w klatkach." @@ -2846,6 +2856,10 @@ MSG_HASH( MENU_ENUM_SUBLABEL_RECORD_DRIVER, "Zapisz sterownik, którego chcesz użyć." ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MIDI_DRIVER, + "Sterownik MIDI do użycia." + ) MSG_HASH( MENU_ENUM_SUBLABEL_WIFI_DRIVER, "Sterownik WiFi do użycia." @@ -3576,6 +3590,18 @@ MSG_HASH( MENU_ENUM_SUBLABEL_DISCORD_ALLOW, "Włącz lub wyłącz wsparcie dla Discrod. Nie będzie działać z wersją przeglądarkową, jedynie z natywnym klientem." ) +MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_INPUT, + "Wejście") +MSG_HASH(MENU_ENUM_SUBLABEL_MIDI_INPUT, + "Wybierz urządzenie wejściowe.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_OUTPUT, + "Wyjście") +MSG_HASH(MENU_ENUM_SUBLABEL_MIDI_OUTPUT, + "Wybierz urządzenie wyjściowe.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_VOLUME, + "Głośność") +MSG_HASH(MENU_ENUM_SUBLABEL_MIDI_VOLUME, + "Ustaw głośność wyjściową (%).") MSG_HASH(MENU_ENUM_LABEL_VALUE_POWER_MANAGEMENT_SETTINGS, "Zarządzanie energią") MSG_HASH(MENU_ENUM_SUBLABEL_POWER_MANAGEMENT_SETTINGS, diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 65df2e805f..a828781ee8 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -1386,6 +1386,22 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) "at a time, increasing the rewinding \n" "speed."); break; + case MENU_ENUM_LABEL_REWIND_BUFFER_SIZE: + snprintf(s, len, + "Rewind buffer size (MB).\n" + " \n" + " The amount of memory in MB to reserve \n" + "for rewinding. Increasing this value \n" + "increases the rewind history length.\n"); + break; + case MENU_ENUM_LABEL_REWIND_BUFFER_SIZE_STEP: + snprintf(s, len, + "Rewind buffer size step (MB).\n" + " \n" + " Each time you increase or decrease \n" + "the rewind buffer size value via this \n" + "UI it will change by this amount.\n"); + break; case MENU_ENUM_LABEL_SCREENSHOT: snprintf(s, len, "Take screenshot."); diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 71d98babd1..bbe66247e9 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1381,6 +1381,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_ENABLE, "Rewind Enable") MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_GRANULARITY, "Rewind Granularity") +MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_BUFFER_SIZE, + "Rewind Buffer Size (MB)") +MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_BUFFER_SIZE_STEP, + "Rewind Buffer Size Step (MB)") MSG_HASH(MENU_ENUM_LABEL_VALUE_REWIND_SETTINGS, "Rewind") MSG_HASH(MENU_ENUM_LABEL_VALUE_RGUI_BROWSER_DIRECTORY, @@ -2802,6 +2806,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_REWIND_GRANULARITY, "When rewinding a defined number of frames, you can rewind several frames at a time, increasing the rewind speed." ) +MSG_HASH( + MENU_ENUM_SUBLABEL_REWIND_BUFFER_SIZE, + "The amount of memory (in MB) to reserve for the rewind buffer. Increasing this will increase the amount of rewind history." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_REWIND_BUFFER_SIZE_STEP, + "Each time you increase or decrease the rewind buffer size value via this UI it will change by this amount" + ) MSG_HASH( MENU_ENUM_SUBLABEL_LIBRETRO_LOG_LEVEL, "Sets log level for cores. If a log level issued by a core is below this value, it is ignored." diff --git a/libretro-common/file/config_file.c b/libretro-common/file/config_file.c index a8f8cfa808..18169f1304 100644 --- a/libretro-common/file/config_file.c +++ b/libretro-common/file/config_file.c @@ -68,6 +68,84 @@ struct config_include_list static config_file_t *config_file_new_internal( const char *path, unsigned depth); +static int config_sort_compare_func(struct config_entry_list *a, + struct config_entry_list *b) +{ + const char *a_key = a ? a->key : NULL; + const char *b_key = b ? b->key : NULL; + + if (!a_key || !b_key) + return 0; + + return strcasecmp(a_key, b_key); +} + +/* https://stackoverflow.com/questions/7685/merge-sort-a-linked-list */ +static struct config_entry_list* merge_sort_linked_list(struct config_entry_list *list, int (*compare)(struct config_entry_list *one,struct config_entry_list *two)) +{ + struct config_entry_list + *right = list, + *temp = list, + *last = list, + *result = 0, + *next = 0, + *tail = 0; + + /* Trivial case. */ + if (!list || !list->next) + return list; + + /* Find halfway through the list (by running two pointers, one at twice the speed of the other). */ + while (temp && temp->next) + { + last = right; + right = right->next; + temp = temp->next->next; + } + + /* Break the list in two. (prev pointers are broken here, but we fix later) */ + last->next = 0; + + /* Recurse on the two smaller lists: */ + list = merge_sort_linked_list(list, compare); + right = merge_sort_linked_list(right, compare); + + /* Merge: */ + while (list || right) + { + /* Take from empty lists, or compare: */ + if (!right) + { + next = list; + list = list->next; + } + else if (!list) + { + next = right; + right = right->next; + } + else if (compare(list, right) < 0) + { + next = list; + list = list->next; + } + else + { + next = right; + right = right->next; + } + + if (!result) + result = next; + else + tail->next = next; + + tail = next; + } + + return result; +} + static char *strip_comment(char *str) { /* Remove everything after comment. @@ -592,6 +670,24 @@ bool config_get_int(config_file_t *conf, const char *key, int *in) return false; } +bool config_get_size_t(config_file_t *conf, const char *key, size_t *in) +{ + const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + errno = 0; + + if (entry) + { + size_t val = 0; + if (sscanf(entry->value, "%" PRI_SIZET, &val) == 1) + { + *in = val; + return true; + } + } + + return false; +} + #if defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L bool config_get_uint64(config_file_t *conf, const char *key, uint64_t *in) { @@ -893,7 +989,7 @@ void config_file_dump(config_file_t *conf, FILE *file) includes = includes->next; } - list = (struct config_entry_list*)conf->entries; + list = merge_sort_linked_list((struct config_entry_list*)conf->entries, config_sort_compare_func); while (list) { diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c index 2d7b2e23ec..1b4bcbb2b2 100644 --- a/libretro-common/file/file_path.c +++ b/libretro-common/file/file_path.c @@ -921,7 +921,12 @@ void fill_pathname_join_noext(char *out_path, void fill_pathname_join_delim(char *out_path, const char *dir, const char *path, const char delim, size_t size) { - size_t copied = strlcpy(out_path, dir, size); + size_t copied; + // behavior of strlcpy is undefined if dst and src overlap + if (out_path == dir) + copied = strlen(dir); + else + copied = strlcpy(out_path, dir, size); out_path[copied] = delim; out_path[copied+1] = '\0'; diff --git a/libretro-common/include/file/config_file.h b/libretro-common/include/file/config_file.h index 6b4dde77e7..3ee7f51333 100644 --- a/libretro-common/include/file/config_file.h +++ b/libretro-common/include/file/config_file.h @@ -118,6 +118,9 @@ bool config_get_int(config_file_t *conf, const char *entry, int *in); /* Extracts an uint from config file. */ bool config_get_uint(config_file_t *conf, const char *entry, unsigned *in); +/* Extracts an size_t from config file. */ +bool config_get_size_t(config_file_t *conf, const char *key, size_t *in); + #if defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L /* Extracts an uint64 from config file. */ bool config_get_uint64(config_file_t *conf, const char *entry, uint64_t *in); diff --git a/libretro-common/include/retro_miscellaneous.h b/libretro-common/include/retro_miscellaneous.h index afcb885cf7..2fd97d54c1 100644 --- a/libretro-common/include/retro_miscellaneous.h +++ b/libretro-common/include/retro_miscellaneous.h @@ -155,4 +155,18 @@ typedef struct uint32_t data[8]; } retro_bits_t; +#ifdef _WIN32 +# ifdef _WIN64 +# define PRI_SIZET PRIu64 +# else +#if _MSC_VER == 1800 +# define PRI_SIZET PRIu32 +#else +# define PRI_SIZET "u" +#endif +# endif +#else +# define PRI_SIZET "zu" +#endif + #endif diff --git a/libretro-common/streams/file_stream.c b/libretro-common/streams/file_stream.c index 2718fbc49e..06b587b005 100644 --- a/libretro-common/streams/file_stream.c +++ b/libretro-common/streams/file_stream.c @@ -196,16 +196,13 @@ int filestream_scanf(RFILE *stream, const char* format, ...) { char buf[4096]; char subfmt[64]; - - const char * bufiter = buf; - - int64_t startpos = filestream_tell(stream); - va_list args; - int ret = 0; - - int maxlen = filestream_read(stream, buf, sizeof(buf)-1); + const char * bufiter = buf; + int64_t startpos = filestream_tell(stream); + int ret = 0; + int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1); + buf[maxlen] = '\0'; va_start(args, format); @@ -214,14 +211,12 @@ int filestream_scanf(RFILE *stream, const char* format, ...) { if (*format == '%') { - char* subfmtiter = subfmt; - - int subret; int sublen; - bool asterisk = false; + char* subfmtiter = subfmt; + bool asterisk = false; - *subfmtiter++ = *format++; /* '%' */ + *subfmtiter++ = *format++; /* '%' */ /* %[*][width][length]specifier */ @@ -276,7 +271,8 @@ int filestream_scanf(RFILE *stream, const char* format, ...) } else { - if (*bufiter != *format) break; + if (*bufiter != *format) + break; bufiter++; format++; } diff --git a/menu/cbs/menu_cbs_select.c b/menu/cbs/menu_cbs_select.c index 7a6bc5e047..81eede715f 100644 --- a/menu/cbs/menu_cbs_select.c +++ b/menu/cbs/menu_cbs_select.c @@ -65,6 +65,7 @@ static int action_select_default(const char *path, const char *label, unsigned t case ST_BOOL: case ST_INT: case ST_UINT: + case ST_SIZE: case ST_FLOAT: action = MENU_ACTION_RIGHT; break; diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 82f8c81689..8f0102625b 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -206,6 +206,8 @@ default_sublabel_macro(action_bind_sublabel_run_ahead_hide_warnings, MENU_ default_sublabel_macro(action_bind_sublabel_run_ahead_frames, MENU_ENUM_SUBLABEL_RUN_AHEAD_FRAMES) default_sublabel_macro(action_bind_sublabel_rewind, MENU_ENUM_SUBLABEL_REWIND_ENABLE) default_sublabel_macro(action_bind_sublabel_rewind_granularity, MENU_ENUM_SUBLABEL_REWIND_GRANULARITY) +default_sublabel_macro(action_bind_sublabel_rewind_buffer_size, MENU_ENUM_SUBLABEL_REWIND_BUFFER_SIZE) +default_sublabel_macro(action_bind_sublabel_rewind_buffer_size_step, MENU_ENUM_SUBLABEL_REWIND_BUFFER_SIZE_STEP) default_sublabel_macro(action_bind_sublabel_libretro_log_level, MENU_ENUM_SUBLABEL_LIBRETRO_LOG_LEVEL) default_sublabel_macro(action_bind_sublabel_perfcnt_enable, MENU_ENUM_SUBLABEL_PERFCNT_ENABLE) default_sublabel_macro(action_bind_sublabel_savestate_auto_save, MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_SAVE) @@ -1330,6 +1332,12 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_REWIND_GRANULARITY: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_rewind_granularity); break; + case MENU_ENUM_LABEL_REWIND_BUFFER_SIZE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_rewind_buffer_size); + break; + case MENU_ENUM_LABEL_REWIND_BUFFER_SIZE_STEP: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_rewind_buffer_size_step); + break; case MENU_ENUM_LABEL_SLOWMOTION_RATIO: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_slowmotion_ratio); break; diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 0975837db1..3c34362b30 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -355,6 +355,8 @@ static void materialui_draw_icon( draw.y = height - y - icon_size; draw.width = icon_size; draw.height = icon_size; + draw.scale_factor = scale_factor; + draw.rotation = rotation; draw.coords = &coords; draw.matrix_data = &mymat; draw.texture = texture; diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c new file mode 100755 index 0000000000..0170c57e62 --- /dev/null +++ b/menu/drivers/stripes.c @@ -0,0 +1,4469 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2014-2017 - Jean-André Santoni + * Copyright (C) 2016-2017 - Brad Parker + * Copyright (C) 2018 - Alfredo Monclús + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include "../../frontend/frontend_driver.h" + +#include "menu_generic.h" + +#include "../menu_driver.h" +#include "../menu_animation.h" + +#include "../../core_info.h" +#include "../../core.h" +#include "../menu_entries.h" +#include "../widgets/menu_entry.h" +#include "../widgets/menu_input_dialog.h" +#include "../widgets/menu_osk.h" +#include "../widgets/menu_filebrowser.h" + +#include "../menu_event.h" + +#include "../../verbosity.h" +#include "../../configuration.h" +#include "../../playlist.h" +#include "../../retroarch.h" + +#include "../../tasks/tasks_internal.h" + +#include "../../cheevos/badges.h" +#include "../../content.h" + +#define STRIPES_RIBBON_ROWS 64 +#define STRIPES_RIBBON_COLS 64 +#define STRIPES_RIBBON_VERTICES 2*STRIPES_RIBBON_COLS*STRIPES_RIBBON_ROWS-2*STRIPES_RIBBON_COLS + +#ifndef STRIPES_DELAY +#define STRIPES_DELAY 10 +#endif + +#define BATTERY_LEVEL_CHECK_INTERVAL (30 * 1000000) + +#if 0 +#define STRIPES_DEBUG +#endif + +/* NOTE: If you change this you HAVE to update + * stripes_alloc_node() and stripes_copy_node() */ +typedef struct +{ + float alpha; + float label_alpha; + float zoom; + float x; + float y; + float width; + uintptr_t icon; + uintptr_t content_icon; + char *fullpath; +} stripes_node_t; + +enum +{ + STRIPES_TEXTURE_MAIN_MENU = 0, + STRIPES_TEXTURE_SETTINGS, + STRIPES_TEXTURE_HISTORY, + STRIPES_TEXTURE_FAVORITES, + STRIPES_TEXTURE_MUSICS, +#ifdef HAVE_FFMPEG + STRIPES_TEXTURE_MOVIES, +#endif +#ifdef HAVE_NETWORKING + STRIPES_TEXTURE_NETPLAY, + STRIPES_TEXTURE_ROOM, +/* stub these out until we have the icons + STRIPES_TEXTURE_ROOM_LAN, + STRIPES_TEXTURE_ROOM_MITM,*/ +#endif +#ifdef HAVE_IMAGEVIEWER + STRIPES_TEXTURE_IMAGES, +#endif + STRIPES_TEXTURE_SETTING, + STRIPES_TEXTURE_SUBSETTING, + STRIPES_TEXTURE_ARROW, + STRIPES_TEXTURE_RUN, + STRIPES_TEXTURE_CLOSE, + STRIPES_TEXTURE_RESUME, + STRIPES_TEXTURE_SAVESTATE, + STRIPES_TEXTURE_LOADSTATE, + STRIPES_TEXTURE_UNDO, + STRIPES_TEXTURE_CORE_INFO, + STRIPES_TEXTURE_WIFI, + STRIPES_TEXTURE_CORE_OPTIONS, + STRIPES_TEXTURE_INPUT_REMAPPING_OPTIONS, + STRIPES_TEXTURE_CHEAT_OPTIONS, + STRIPES_TEXTURE_DISK_OPTIONS, + STRIPES_TEXTURE_SHADER_OPTIONS, + STRIPES_TEXTURE_ACHIEVEMENT_LIST, + STRIPES_TEXTURE_SCREENSHOT, + STRIPES_TEXTURE_RELOAD, + STRIPES_TEXTURE_RENAME, + STRIPES_TEXTURE_FILE, + STRIPES_TEXTURE_FOLDER, + STRIPES_TEXTURE_ZIP, + STRIPES_TEXTURE_FAVORITE, + STRIPES_TEXTURE_ADD_FAVORITE, + STRIPES_TEXTURE_MUSIC, + STRIPES_TEXTURE_IMAGE, + STRIPES_TEXTURE_MOVIE, + STRIPES_TEXTURE_CORE, + STRIPES_TEXTURE_RDB, + STRIPES_TEXTURE_CURSOR, + STRIPES_TEXTURE_SWITCH_ON, + STRIPES_TEXTURE_SWITCH_OFF, + STRIPES_TEXTURE_CLOCK, + STRIPES_TEXTURE_BATTERY_FULL, + STRIPES_TEXTURE_BATTERY_CHARGING, + STRIPES_TEXTURE_POINTER, + STRIPES_TEXTURE_ADD, + STRIPES_TEXTURE_KEY, + STRIPES_TEXTURE_KEY_HOVER, + STRIPES_TEXTURE_DIALOG_SLICE, + STRIPES_TEXTURE_LAST +}; + +enum +{ + STRIPES_SYSTEM_TAB_MAIN = 0, + STRIPES_SYSTEM_TAB_SETTINGS, + STRIPES_SYSTEM_TAB_HISTORY, + STRIPES_SYSTEM_TAB_FAVORITES, + STRIPES_SYSTEM_TAB_MUSIC, +#ifdef HAVE_FFMPEG + STRIPES_SYSTEM_TAB_VIDEO, +#endif +#ifdef HAVE_IMAGEVIEWER + STRIPES_SYSTEM_TAB_IMAGES, +#endif +#ifdef HAVE_NETWORKING + STRIPES_SYSTEM_TAB_NETPLAY, +#endif + STRIPES_SYSTEM_TAB_ADD, + + /* End of this enum - use the last one to determine num of possible tabs */ + STRIPES_SYSTEM_TAB_MAX_LENGTH +}; + +typedef struct stripes_handle +{ + bool mouse_show; + + uint8_t system_tab_end; + uint8_t tabs[STRIPES_SYSTEM_TAB_MAX_LENGTH]; + + int depth; + int old_depth; + int icon_size; + int cursor_size; + + size_t categories_selection_ptr; + size_t categories_selection_ptr_old; + size_t selection_ptr_old; + + unsigned categories_active_idx; + unsigned categories_active_idx_old; + uintptr_t thumbnail; + uintptr_t left_thumbnail; + uintptr_t savestate_thumbnail; + + float x; + float alpha; + float thumbnail_width; + float thumbnail_height; + float left_thumbnail_width; + float left_thumbnail_height; + float savestate_thumbnail_width; + float savestate_thumbnail_height; + float above_subitem_offset; + float above_item_offset; + float active_item_factor; + float under_item_offset; + float shadow_offset; + float font_size; + float font2_size; + + float margins_screen_left; + float margins_screen_top; + float margins_setting_left; + float margins_title_left; + float margins_title_top; + float margins_title_bottom; + float margins_label_left; + float margins_label_top; + float icon_spacing_horizontal; + float icon_spacing_vertical; + float items_active_alpha; + float items_active_zoom; + float items_passive_alpha; + float items_passive_zoom; + float margins_dialog; + float margins_slice; + float textures_arrow_alpha; + float categories_x_pos; + float categories_angle; + float categories_active_y; + float categories_before_y; + float categories_after_y; + float categories_active_x; + float categories_before_x; + float categories_after_x; + float categories_passive_alpha; + float categories_passive_zoom; + float categories_passive_width; + float categories_active_zoom; + float categories_active_alpha; + float categories_active_width; + + uint64_t frame_count; + + char title_name[255]; + char *box_message; + char *thumbnail_system; + char *thumbnail_content; + char *savestate_thumbnail_file_path; + char *thumbnail_file_path; + char *left_thumbnail_file_path; + char *bg_file_path; + + file_list_t *selection_buf_old; + file_list_t *horizontal_list; + + struct + { + menu_texture_item bg; + menu_texture_item list[STRIPES_TEXTURE_LAST]; + } textures; + + stripes_node_t main_menu_node; +#ifdef HAVE_IMAGEVIEWER + stripes_node_t images_tab_node; +#endif + stripes_node_t music_tab_node; +#ifdef HAVE_FFMPEG + stripes_node_t video_tab_node; +#endif + stripes_node_t settings_tab_node; + stripes_node_t history_tab_node; + stripes_node_t favorites_tab_node; + stripes_node_t add_tab_node; + stripes_node_t netplay_tab_node; + + font_data_t *font; + font_data_t *font2; + video_font_raster_block_t raster_block; + video_font_raster_block_t raster_block2; +} stripes_handle_t; + +float stripes_scale_mod[8] = { + 1, 1, 1, 1, 1, 1, 1, 1 +}; + +static float stripes_coord_shadow[] = { + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, 0 +}; + +static float stripes_coord_black[] = { + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, 0 +}; + +static float stripes_coord_white[] = { + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, 1 +}; + +static float stripes_item_color[] = { + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, 1 +}; + +static float HueToRGB(float v1, float v2, float vH) +{ + if (vH < 0) + vH += 1; + + if (vH > 1) + vH -= 1; + + if ((6 * vH) < 1) + return (v1 + (v2 - v1) * 6 * vH); + + if ((2 * vH) < 1) + return v2; + + if ((3 * vH) < 2) + return (v1 + (v2 - v1) * ((2.0f / 3) - vH) * 6); + + return v1; +} + +static void HSLToRGB(float H, float S, float L, float *rgb) { + if (S == 0) + rgb[0] = rgb[1] = rgb[2] = L; + else + { + float v1, v2; + + v2 = (L < 0.5) ? (L * (1 + S)) : ((L + S) - (L * S)); + v1 = 2 * L - v2; + + rgb[0] = HueToRGB(v1, v2, H + (1.0f / 3)); + rgb[1] = HueToRGB(v1, v2, H); + rgb[2] = HueToRGB(v1, v2, H - (1.0f / 3)); + } +} + +static void stripes_calculate_visible_range(const stripes_handle_t *stripes, + unsigned height, size_t list_size, unsigned current, + unsigned *first, unsigned *last); + +const char* stripes_theme_ident(void) +{ + settings_t *settings = config_get_ptr(); + switch (settings->uints.menu_xmb_theme) + { + case XMB_ICON_THEME_FLATUI: + return "flatui"; + case XMB_ICON_THEME_RETROACTIVE: + return "retroactive"; + case XMB_ICON_THEME_RETROSYSTEM: + return "retrosystem"; + case XMB_ICON_THEME_PIXEL: + return "pixel"; + case XMB_ICON_THEME_NEOACTIVE: + return "neoactive"; + case XMB_ICON_THEME_SYSTEMATIC: + return "systematic"; + case XMB_ICON_THEME_DOTART: + return "dot-art"; + case XMB_ICON_THEME_CUSTOM: + return "custom"; + case XMB_ICON_THEME_MONOCHROME_INVERTED: + return "monochrome-inverted"; + case XMB_ICON_THEME_MONOCHROME: + default: + break; + } + + return "monochrome"; +} + +/* NOTE: This exists because calloc()ing stripes_node_t is expensive + * when you can have big lists like MAME and fba playlists */ +static stripes_node_t *stripes_alloc_node(void) +{ + stripes_node_t *node = (stripes_node_t*)malloc(sizeof(*node)); + + node->alpha = node->label_alpha = 0; + node->zoom = node->x = node->y = 0; + node->icon = node->content_icon = 0; + node->fullpath = NULL; + + return node; +} + +static void stripes_free_node(stripes_node_t *node) +{ + if (!node) + return; + + if (node->fullpath) + free(node->fullpath); + + node->fullpath = NULL; + + free(node); +} + +/** + * @brief frees all stripes_node_t in a file_list_t + * + * file_list_t asumes userdata holds a simple structure and + * free()'s it. Can't change this at the time because other + * code depends on this behavior. + * + * @param list + * @param actiondata whether to free actiondata too + */ +static void stripes_free_list_nodes(file_list_t *list, bool actiondata) +{ + unsigned i, size = file_list_get_size(list); + + for (i = 0; i < size; ++i) + { + stripes_free_node((stripes_node_t*)file_list_get_userdata_at_offset(list, i)); + + /* file_list_set_userdata() doesn't accept NULL */ + list->list[i].userdata = NULL; + + if (actiondata) + file_list_free_actiondata(list, i); + } +} + +static stripes_node_t *stripes_copy_node(const stripes_node_t *old_node) +{ + stripes_node_t *new_node = (stripes_node_t*)malloc(sizeof(*new_node)); + + *new_node = *old_node; + new_node->fullpath = old_node->fullpath ? strdup(old_node->fullpath) : NULL; + + return new_node; +} + +static const char *stripes_thumbnails_ident(char pos) +{ + char folder = 0; + settings_t *settings = config_get_ptr(); + + if (pos == 'R') + folder = settings->uints.menu_thumbnails; + if (pos == 'L') + folder = settings->uints.menu_left_thumbnails; + + switch (folder) + { + case 1: + return "Named_Snaps"; + case 2: + return "Named_Titles"; + case 3: + return "Named_Boxarts"; + case 0: + default: + break; + } + + return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF); +} + +static size_t stripes_list_get_selection(void *data) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + + if (!stripes) + return 0; + + return stripes->categories_selection_ptr; +} + +static size_t stripes_list_get_size(void *data, enum menu_list_type type) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + + switch (type) + { + case MENU_LIST_PLAIN: + return menu_entries_get_stack_size(0); + case MENU_LIST_HORIZONTAL: + if (stripes && stripes->horizontal_list) + return file_list_get_size(stripes->horizontal_list); + break; + case MENU_LIST_TABS: + return stripes->system_tab_end; + } + + return 0; +} + +static void *stripes_list_get_entry(void *data, + enum menu_list_type type, unsigned i) +{ + size_t list_size = 0; + stripes_handle_t *stripes = (stripes_handle_t*)data; + + switch (type) + { + case MENU_LIST_PLAIN: + { + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + list_size = menu_entries_get_stack_size(0); + if (i < list_size) + return (void*)&menu_stack->list[i]; + } + break; + case MENU_LIST_HORIZONTAL: + if (stripes && stripes->horizontal_list) + list_size = file_list_get_size(stripes->horizontal_list); + if (i < list_size) + return (void*)&stripes->horizontal_list->list[i]; + break; + default: + break; + } + + return NULL; +} + +static INLINE float stripes_item_y(const stripes_handle_t *stripes, int i, size_t current) +{ + float iy = stripes->icon_spacing_vertical; + + if (i < (int)current) + if (stripes->depth > 1) + iy *= (i - (int)current + stripes->above_subitem_offset); + else + iy *= (i - (int)current + stripes->above_item_offset); + else + iy *= (i - (int)current + stripes->under_item_offset); + + if (i == (int)current) + iy = stripes->icon_spacing_vertical * stripes->active_item_factor; + + return iy; +} + +static void stripes_draw_icon( + video_frame_info_t *video_info, + int icon_size, + math_matrix_4x4 *mymat, + uintptr_t texture, + float x, + float y, + unsigned width, + unsigned height, + float alpha, + float rotation, + float scale_factor, + float *color, + float shadow_offset) +{ + menu_display_ctx_draw_t draw; + struct video_coords coords; + + if ( + (x < (-icon_size / 2.0f)) || + (x > width) || + (y < (icon_size / 2.0f)) || + (y > height + icon_size) + ) + return; + + coords.vertices = 4; + coords.vertex = NULL; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + + draw.width = icon_size; + draw.height = icon_size; + draw.rotation = rotation; + draw.scale_factor = scale_factor; +#if defined(VITA) || defined(WIIU) + draw.width *= scale_factor; + draw.height *= scale_factor; +#endif + draw.coords = &coords; + draw.matrix_data = mymat; + draw.texture = texture; + draw.prim_type = MENU_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + + if (video_info->xmb_shadows_enable) + { + menu_display_set_alpha(stripes_coord_shadow, color[3] * 0.35f); + + coords.color = stripes_coord_shadow; + draw.x = x + shadow_offset; + draw.y = height - y - shadow_offset; + +#if defined(VITA) || defined(WIIU) + if(scale_factor < 1) + { + draw.x = draw.x + (icon_size-draw.width)/2; + draw.y = draw.y + (icon_size-draw.width)/2; + } +#endif + menu_display_draw(&draw, video_info); + } + + coords.color = (const float*)color; + draw.x = x; + draw.y = height - y; + +#if defined(VITA) || defined(WIIU) + if(scale_factor < 1) + { + draw.x = draw.x + (icon_size-draw.width)/2; + draw.y = draw.y + (icon_size-draw.width)/2; + } +#endif + menu_display_draw(&draw, video_info); +} + +static void stripes_draw_text( + video_frame_info_t *video_info, + stripes_handle_t *stripes, + const char *str, float x, + float y, float scale_factor, float alpha, + enum text_alignment text_align, + unsigned width, unsigned height, font_data_t* font) +{ + uint32_t color; + uint8_t a8; + settings_t *settings; + + if (alpha > stripes->alpha) + alpha = stripes->alpha; + + a8 = 255 * alpha; + + /* Avoid drawing 100% transparent text */ + if (a8 == 0) + return; + + settings = config_get_ptr(); + color = FONT_COLOR_RGBA( + settings->uints.menu_font_color_red, + settings->uints.menu_font_color_green, + settings->uints.menu_font_color_blue, a8); + + menu_display_draw_text(font, str, x, y, + width, height, color, text_align, scale_factor, + video_info->xmb_shadows_enable, + stripes->shadow_offset); +} + +static void stripes_messagebox(void *data, const char *message) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + + if (!stripes || string_is_empty(message)) + return; + + stripes->box_message = strdup(message); +} + +static void stripes_render_keyboard( + stripes_handle_t *stripes, + video_frame_info_t *video_info, + char **grid, unsigned id) +{ + unsigned i; + int ptr_width, ptr_height; + unsigned width = video_info->width; + unsigned height = video_info->height; + float dark[16] = { + 0.00, 0.00, 0.00, 0.85, + 0.00, 0.00, 0.00, 0.85, + 0.00, 0.00, 0.00, 0.85, + 0.00, 0.00, 0.00, 0.85, + }; + + float white[16]= { + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + }; + + menu_display_draw_quad( + video_info, + 0, height/2.0, width, height/2.0, + width, height, + &dark[0]); + + ptr_width = width / 11; + ptr_height = height / 10; + + if (ptr_width >= ptr_height) + ptr_width = ptr_height; + + for (i = 0; i < 44; i++) + { + int line_y = (i / 11) * height / 10.0; + + if (i == id) + { + uintptr_t texture = stripes->textures.list[STRIPES_TEXTURE_KEY_HOVER]; + + menu_display_blend_begin(video_info); + + menu_display_draw_texture( + video_info, + width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width, + height/2.0 + ptr_height*1.5 + line_y, + ptr_width, ptr_height, + width, height, + &white[0], + texture); + + menu_display_blend_end(video_info); + } + + menu_display_draw_text(stripes->font, grid[i], + width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width + ptr_width/2.0, + height/2.0 + ptr_height + line_y + stripes->font->size / 3, + width, height, 0xffffffff, TEXT_ALIGN_CENTER, 1.0f, + false, 0); + } +} + +/* Returns the OSK key at a given position */ +static int stripes_osk_ptr_at_pos(void *data, int x, int y, unsigned width, unsigned height) +{ + unsigned i; + int ptr_width, ptr_height; + stripes_handle_t *stripes = (stripes_handle_t*)data; + + if (!stripes) + return -1; + + ptr_width = width / 11; + ptr_height = height / 10; + + if (ptr_width >= ptr_height) + ptr_width = ptr_height; + + for (i = 0; i < 44; i++) + { + int line_y = (i / 11)*height/10.0; + int ptr_x = width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width; + int ptr_y = height/2.0 + ptr_height*1.5 + line_y - ptr_height; + + if (x > ptr_x && x < ptr_x + ptr_width + && y > ptr_y && y < ptr_y + ptr_height) + return i; + } + + return -1; +} + +static void stripes_render_messagebox_internal( + video_frame_info_t *video_info, + stripes_handle_t *stripes, const char *message, float* stripes_coord_white) +{ + unsigned i, y_position; + int x, y, longest = 0, longest_width = 0; + float line_height = 0; + unsigned width = video_info->width; + unsigned height = video_info->height; + struct string_list *list = !string_is_empty(message) + ? string_split(message, "\n") : NULL; + + if (!list || !stripes || !stripes->font) + { + if (list) + string_list_free(list); + return; + } + + if (list->elems == 0) + goto end; + + line_height = stripes->font->size * 1.2; + + y_position = height / 2; + if (menu_input_dialog_get_display_kb()) + y_position = height / 4; + + x = width / 2; + y = y_position - (list->size-1) * line_height / 2; + + /* find the longest line width */ + for (i = 0; i < list->size; i++) + { + const char *msg = list->elems[i].data; + int len = (int)utf8len(msg); + + if (len > longest) + { + longest = len; + longest_width = font_driver_get_message_width( + stripes->font, msg, strlen(msg), 1); + } + } + + menu_display_blend_begin(video_info); + + menu_display_draw_texture_slice( + video_info, + x - longest_width/2 - stripes->margins_dialog, + y + stripes->margins_slice - stripes->margins_dialog, + 256, 256, + longest_width + stripes->margins_dialog * 2, + line_height * list->size + stripes->margins_dialog * 2, + width, height, + &stripes_coord_white[0], + stripes->margins_slice, 1.0, + stripes->textures.list[STRIPES_TEXTURE_DIALOG_SLICE]); + + for (i = 0; i < list->size; i++) + { + const char *msg = list->elems[i].data; + + if (msg) + menu_display_draw_text(stripes->font, msg, + x - longest_width/2.0, + y + (i+0.75) * line_height, + width, height, 0x444444ff, TEXT_ALIGN_LEFT, 1.0f, false, 0); + } + + if (menu_input_dialog_get_display_kb()) + stripes_render_keyboard(stripes, + video_info, + menu_event_get_osk_grid(), + menu_event_get_osk_ptr()); + +end: + string_list_free(list); +} + +static void stripes_update_thumbnail_path(void *data, unsigned i, char pos) +{ + menu_entry_t entry; + unsigned entry_type = 0; + char new_path[PATH_MAX_LENGTH] = {0}; + settings_t *settings = config_get_ptr(); + stripes_handle_t *stripes = (stripes_handle_t*)data; + playlist_t *playlist = NULL; + const char *dir_thumbnails = settings->paths.directory_thumbnails; + + menu_entry_init(&entry); + + if (!stripes || string_is_empty(dir_thumbnails)) + goto end; + + menu_entry_get(&entry, 0, i, NULL, true); + + entry_type = menu_entry_get_type_new(&entry); + + if (entry_type == FILE_TYPE_IMAGEVIEWER || entry_type == FILE_TYPE_IMAGE) + { + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + stripes_node_t *node = (stripes_node_t*) + file_list_get_userdata_at_offset(selection_buf, i); + + if (!string_is_empty(node->fullpath) && + (pos == 'R' || (pos == 'L' && string_is_equal(stripes_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))))) + { + if (!string_is_empty(entry.path)) + fill_pathname_join( + new_path, + node->fullpath, + entry.path, + sizeof(new_path)); + + goto end; + } + } + else if (filebrowser_get_type() != FILEBROWSER_NONE) + { + stripes->thumbnail = 0; + goto end; + } + + playlist = playlist_get_cached(); + + if (playlist) + { + const char *core_name = NULL; + playlist_get_index(playlist, i, + NULL, NULL, NULL, &core_name, NULL, NULL); + + if (string_is_equal(core_name, "imageviewer")) + { + if (pos == 'R' || (pos == 'L' && string_is_equal(stripes_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)))) + { + if (!string_is_empty(entry.label)) + strlcpy(new_path, entry.label, + sizeof(new_path)); + goto end; + } + else + { + stripes->left_thumbnail = 0; + goto end; + } + } + } + + /* Append thumbnail system directory */ + if (!string_is_empty(stripes->thumbnail_system)) + fill_pathname_join( + new_path, + dir_thumbnails, + stripes->thumbnail_system, + sizeof(new_path)); + + if (!string_is_empty(new_path)) + { + char *tmp_new2 = (char*) + malloc(PATH_MAX_LENGTH * sizeof(char)); + + tmp_new2[0] = '\0'; + + /* Append Named_Snaps/Named_Boxarts/Named_Titles */ + if (pos == 'R') + fill_pathname_join(tmp_new2, new_path, + stripes_thumbnails_ident('R'), PATH_MAX_LENGTH * sizeof(char)); + if (pos == 'L') + fill_pathname_join(tmp_new2, new_path, + stripes_thumbnails_ident('L'), PATH_MAX_LENGTH * sizeof(char)); + + strlcpy(new_path, tmp_new2, + PATH_MAX_LENGTH * sizeof(char)); + free(tmp_new2); + } + + /* Scrub characters that are not cross-platform and/or violate the + * No-Intro filename standard: + * http://datomatic.no-intro.org/stuff/The%20Official%20No-Intro%20Convention%20(20071030).zip + * Replace these characters in the entry name with underscores. + */ + if (!string_is_empty(stripes->thumbnail_content)) + { + char *scrub_char_pointer = NULL; + char *tmp_new = (char*) + malloc(PATH_MAX_LENGTH * sizeof(char)); + char *tmp = strdup(stripes->thumbnail_content); + + tmp_new[0] = '\0'; + + while((scrub_char_pointer = strpbrk(tmp, "&*/:`\"<>?\\|"))) + *scrub_char_pointer = '_'; + + /* Look for thumbnail file with this scrubbed filename */ + + fill_pathname_join(tmp_new, + new_path, + tmp, PATH_MAX_LENGTH * sizeof(char)); + + if (!string_is_empty(tmp_new)) + strlcpy(new_path, + tmp_new, sizeof(new_path)); + + free(tmp_new); + free(tmp); + } + + /* Append png extension */ + if (!string_is_empty(new_path)) + strlcat(new_path, + file_path_str(FILE_PATH_PNG_EXTENSION), + sizeof(new_path)); + +end: + if (stripes && !string_is_empty(new_path)) + { + if (pos == 'R') + stripes->thumbnail_file_path = strdup(new_path); + if (pos == 'L') + stripes->left_thumbnail_file_path = strdup(new_path); + } + + menu_entry_free(&entry); +} + +static void stripes_update_savestate_thumbnail_path(void *data, unsigned i) +{ + menu_entry_t entry; + settings_t *settings = config_get_ptr(); + stripes_handle_t *stripes = (stripes_handle_t*)data; + + if (!stripes) + return; + + menu_entry_init(&entry); + menu_entry_get(&entry, 0, i, NULL, true); + + if (!string_is_empty(stripes->savestate_thumbnail_file_path)) + free(stripes->savestate_thumbnail_file_path); + stripes->savestate_thumbnail_file_path = NULL; + + if (!string_is_empty(entry.label)) + { + if ( (settings->bools.savestate_thumbnail_enable) + && ((string_is_equal(entry.label, "state_slot")) + || (string_is_equal(entry.label, "loadstate")) + || (string_is_equal(entry.label, "savestate")))) + { + size_t path_size = 8024 * sizeof(char); + char *path = (char*)malloc(8204 * sizeof(char)); + global_t *global = global_get_ptr(); + + path[0] = '\0'; + + if (global) + { + int state_slot = settings->ints.state_slot; + + if (state_slot > 0) + snprintf(path, path_size, "%s%d", + global->name.savestate, state_slot); + else if (state_slot < 0) + fill_pathname_join_delim(path, + global->name.savestate, "auto", '.', path_size); + else + strlcpy(path, global->name.savestate, path_size); + } + + strlcat(path, file_path_str(FILE_PATH_PNG_EXTENSION), path_size); + + if (filestream_exists(path)) + { + if (!string_is_empty(stripes->savestate_thumbnail_file_path)) + free(stripes->savestate_thumbnail_file_path); + stripes->savestate_thumbnail_file_path = strdup(path); + } + + free(path); + } + } + + menu_entry_free(&entry); +} + +static void stripes_update_thumbnail_image(void *data) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + if (!stripes) + return; + + if (!(string_is_empty(stripes->thumbnail_file_path))) + { + if (filestream_exists(stripes->thumbnail_file_path)) + task_push_image_load(stripes->thumbnail_file_path, + menu_display_handle_thumbnail_upload, NULL); + else + stripes->thumbnail = 0; + + free(stripes->thumbnail_file_path); + stripes->thumbnail_file_path = NULL; + } + + if (!(string_is_empty(stripes->left_thumbnail_file_path))) + { + if (filestream_exists(stripes->left_thumbnail_file_path)) + task_push_image_load(stripes->left_thumbnail_file_path, + menu_display_handle_left_thumbnail_upload, NULL); + else + stripes->left_thumbnail = 0; + + free(stripes->left_thumbnail_file_path); + stripes->left_thumbnail_file_path = NULL; + } +} + +static void stripes_set_thumbnail_system(void *data, char*s, size_t len) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + if (!stripes) + return; + + if (!string_is_empty(stripes->thumbnail_system)) + free(stripes->thumbnail_system); + stripes->thumbnail_system = strdup(s); +} + +static void stripes_reset_thumbnail_content(void *data) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + if (!stripes) + return; + if (!string_is_empty(stripes->thumbnail_content)) + free(stripes->thumbnail_content); + stripes->thumbnail_content = NULL; +} + +static void stripes_set_thumbnail_content(void *data, char *s, size_t len) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + if (!stripes) + return; + if (!string_is_empty(stripes->thumbnail_content)) + free(stripes->thumbnail_content); + stripes->thumbnail_content = strdup(s); +} + +static void stripes_update_savestate_thumbnail_image(void *data) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + if (!stripes) + return; + + if (!string_is_empty(stripes->savestate_thumbnail_file_path) + && filestream_exists(stripes->savestate_thumbnail_file_path)) + task_push_image_load(stripes->savestate_thumbnail_file_path, + menu_display_handle_savestate_thumbnail_upload, NULL); + else + stripes->savestate_thumbnail = 0; +} + +static unsigned stripes_get_system_tab(stripes_handle_t *stripes, unsigned i) +{ + if (i <= stripes->system_tab_end) + { + return stripes->tabs[i]; + } + return UINT_MAX; +} + +static void stripes_selection_pointer_changed( + stripes_handle_t *stripes, bool allow_animations) +{ + unsigned i, end, height; + menu_animation_ctx_tag tag; + menu_entry_t entry; + size_t num = 0; + int threshold = 0; + menu_list_t *menu_list = NULL; + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + size_t selection = menu_navigation_get_selection(); + const char *thumb_ident = stripes_thumbnails_ident('R'); + const char *lft_thumb_ident= stripes_thumbnails_ident('L'); + + menu_entries_ctl(MENU_ENTRIES_CTL_LIST_GET, &menu_list); + menu_entry_init(&entry); + + if (!stripes) + goto end; + + menu_entry_get(&entry, 0, selection, NULL, true); + + end = (unsigned)menu_entries_get_size(); + threshold = stripes->icon_size * 10; + + video_driver_get_size(NULL, &height); + + tag = (uintptr_t)selection_buf; + + menu_animation_ctl(MENU_ANIMATION_CTL_KILL_BY_TAG, &tag); + menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &num); + + for (i = 0; i < end; i++) + { + float iy, real_iy; + float ia = stripes->items_passive_alpha; + float iz = stripes->items_passive_zoom; + stripes_node_t *node = (stripes_node_t*) + file_list_get_userdata_at_offset(selection_buf, i); + + if (!node) + continue; + + iy = stripes_item_y(stripes, i, selection); + real_iy = iy + stripes->margins_screen_top; + + if ( (!allow_animations) + || (real_iy < -threshold + || real_iy > height+threshold)) + { + node->alpha = node->label_alpha = ia; + node->y = iy; + node->zoom = iz; + } + else + { + menu_animation_ctx_entry_t anim_entry; + + anim_entry.duration = STRIPES_DELAY; + anim_entry.target_value = ia; + anim_entry.subject = &node->alpha; + anim_entry.easing_enum = EASING_OUT_QUAD; + anim_entry.tag = tag; + anim_entry.cb = NULL; + + menu_animation_push(&anim_entry); + + anim_entry.subject = &node->label_alpha; + + menu_animation_push(&anim_entry); + + anim_entry.target_value = iz; + anim_entry.subject = &node->zoom; + + menu_animation_push(&anim_entry); + + anim_entry.target_value = iy; + anim_entry.subject = &node->y; + + menu_animation_push(&anim_entry); + } + } + +end: + menu_entry_free(&entry); +} + +static void stripes_list_open_old(stripes_handle_t *stripes, + file_list_t *list, int dir, size_t current) +{ + unsigned i, height = 0; + int threshold = stripes->icon_size * 10; + size_t end = 0; + + end = file_list_get_size(list); + + video_driver_get_size(NULL, &height); + + for (i = 0; i < end; i++) + { + float ia = 0; + float real_y; + stripes_node_t *node = (stripes_node_t*) + file_list_get_userdata_at_offset(list, i); + + if (!node) + continue; + + if (i == current) + ia = stripes->items_active_alpha; + if (dir == -1) + ia = 0; + + real_y = node->y + stripes->margins_screen_top; + + if (real_y < -threshold || real_y > height+threshold) + { + node->alpha = ia; + node->label_alpha = 0; + node->x = stripes->icon_size * dir * -2; + } + else + { + menu_animation_ctx_entry_t anim_entry; + + anim_entry.duration = STRIPES_DELAY; + anim_entry.target_value = ia; + anim_entry.subject = &node->alpha; + anim_entry.easing_enum = EASING_OUT_QUAD; + anim_entry.tag = (uintptr_t)list; + anim_entry.cb = NULL; + + menu_animation_push(&anim_entry); + + anim_entry.target_value = 0; + anim_entry.subject = &node->label_alpha; + + menu_animation_push(&anim_entry); + + anim_entry.target_value = stripes->icon_size * dir * -2; + anim_entry.subject = &node->x; + + menu_animation_push(&anim_entry); + } + } +} + +static void stripes_list_open_new(stripes_handle_t *stripes, + file_list_t *list, int dir, size_t current) +{ + unsigned i, height; + unsigned stripes_system_tab = 0; + size_t skip = 0; + int threshold = stripes->icon_size * 10; + size_t end = file_list_get_size(list); + + video_driver_get_size(NULL, &height); + + for (i = 0; i < end; i++) + { + float ia; + float real_y; + stripes_node_t *node = (stripes_node_t*) + file_list_get_userdata_at_offset(list, i); + + if (!node) + continue; + + if (dir == 1 || (dir == -1 && i != current)) + node->alpha = 0; + + if (dir == 1 || dir == -1) + node->label_alpha = 0; + + node->x = stripes->icon_size * dir * 2; + node->y = stripes_item_y(stripes, i, current); + node->zoom = stripes->categories_passive_zoom; + + real_y = node->y + stripes->margins_screen_top; + + if (i == current) + node->zoom = stripes->categories_active_zoom; + + ia = stripes->items_passive_alpha; + if (i == current) + ia = stripes->items_active_alpha; + + if (real_y < -threshold || real_y > height+threshold) + { + node->alpha = node->label_alpha = ia; + node->x = 0; + } + else + { + menu_animation_ctx_entry_t anim_entry; + + anim_entry.duration = STRIPES_DELAY; + anim_entry.target_value = ia; + anim_entry.subject = &node->alpha; + anim_entry.easing_enum = EASING_OUT_QUAD; + anim_entry.tag = (uintptr_t)list; + anim_entry.cb = NULL; + + menu_animation_push(&anim_entry); + + anim_entry.subject = &node->label_alpha; + + menu_animation_push(&anim_entry); + + anim_entry.target_value = 0; + anim_entry.subject = &node->x; + + menu_animation_push(&anim_entry); + } + } + + stripes->old_depth = stripes->depth; + menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &skip); + + stripes_system_tab = stripes_get_system_tab(stripes, + (unsigned)stripes->categories_selection_ptr); + + if (stripes_system_tab <= STRIPES_SYSTEM_TAB_SETTINGS) + { + if (stripes->depth < 4) + stripes_reset_thumbnail_content(stripes); + stripes_update_thumbnail_path(stripes, 0, 'R'); + stripes_update_thumbnail_image(stripes); + stripes_update_thumbnail_path(stripes, 0, 'L'); + stripes_update_thumbnail_image(stripes); + } +} + +static stripes_node_t *stripes_node_allocate_userdata( + stripes_handle_t *stripes, unsigned i) +{ + stripes_node_t *tmp = NULL; + stripes_node_t *node = stripes_alloc_node(); + + if (!node) + { + RARCH_ERR("XMB node could not be allocated.\n"); + return NULL; + } + + node->alpha = stripes->categories_passive_alpha; + node->zoom = stripes->categories_passive_zoom; + + if ((i + stripes->system_tab_end) == stripes->categories_active_idx) + { + node->alpha = stripes->categories_active_alpha; + node->zoom = stripes->categories_active_zoom; + } + + tmp = (stripes_node_t*)file_list_get_userdata_at_offset( + stripes->horizontal_list, i); + stripes_free_node(tmp); + + file_list_set_userdata(stripes->horizontal_list, i, node); + + return node; +} + +static stripes_node_t* stripes_get_userdata_from_horizontal_list( + stripes_handle_t *stripes, unsigned i) +{ + return (stripes_node_t*) + file_list_get_userdata_at_offset(stripes->horizontal_list, i); +} + +static void stripes_push_animations(stripes_node_t *node, + uintptr_t tag, float ia, float ix) +{ + menu_animation_ctx_entry_t anim_entry; + + anim_entry.duration = STRIPES_DELAY; + anim_entry.target_value = ia; + anim_entry.subject = &node->alpha; + anim_entry.easing_enum = EASING_OUT_QUAD; + anim_entry.tag = tag; + anim_entry.cb = NULL; + + menu_animation_push(&anim_entry); + + anim_entry.subject = &node->label_alpha; + + menu_animation_push(&anim_entry); + + anim_entry.target_value = ix; + anim_entry.subject = &node->x; + + menu_animation_push(&anim_entry); +} + +static void stripes_list_switch_old(stripes_handle_t *stripes, + file_list_t *list, int dir, size_t current) +{ + unsigned i, first, last, height; + size_t end = file_list_get_size(list); + float ix = -stripes->icon_spacing_horizontal * dir; + float ia = 0; + + first = 0; + last = end > 0 ? end - 1 : 0; + + video_driver_get_size(NULL, &height); + stripes_calculate_visible_range(stripes, height, end, + current, &first, &last); + + for (i = 0; i < end; i++) + { + stripes_node_t *node = (stripes_node_t*) + file_list_get_userdata_at_offset(list, i); + + if (!node) + continue; + + if (i >= first && i <= last) + stripes_push_animations(node, (uintptr_t)list, ia, ix); + else + { + node->alpha = node->label_alpha = ia; + node->x = ix; + } + } +} + +static void stripes_list_switch_new(stripes_handle_t *stripes, + file_list_t *list, int dir, size_t current) +{ + unsigned i, first, last, height; + size_t end = 0; + settings_t *settings = config_get_ptr(); + + if (settings->bools.menu_dynamic_wallpaper_enable) + { + size_t path_size = PATH_MAX_LENGTH * sizeof(char); + char *path = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); + char *tmp = string_replace_substring(stripes->title_name, "/", " "); + + path[0] = '\0'; + + if (tmp) + { + fill_pathname_join_noext( + path, + settings->paths.directory_dynamic_wallpapers, + tmp, + path_size); + free(tmp); + } + + strlcat(path, + file_path_str(FILE_PATH_PNG_EXTENSION), + path_size); + + if (!filestream_exists(path)) + fill_pathname_application_special(path, path_size, + APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_BG); + + if(!string_is_equal(path, stripes->bg_file_path)) + { + if(filestream_exists(path)) + { + task_push_image_load(path, + menu_display_handle_wallpaper_upload, NULL); + if (!string_is_empty(stripes->bg_file_path)) + free(stripes->bg_file_path); + stripes->bg_file_path = strdup(path); + } + } + + free(path); + } + + end = file_list_get_size(list); + + first = 0; + last = end > 0 ? end - 1 : 0; + + video_driver_get_size(NULL, &height); + stripes_calculate_visible_range(stripes, height, end, current, &first, &last); + + for (i = 0; i < end; i++) + { + stripes_node_t *node = (stripes_node_t*) + file_list_get_userdata_at_offset(list, i); + float ia = stripes->items_passive_alpha; + + if (!node) + continue; + + node->x = stripes->icon_spacing_horizontal * dir; + node->alpha = 0; + node->label_alpha = 0; + + if (i == current) + ia = stripes->items_active_alpha; + + if (i >= first && i <= last) + stripes_push_animations(node, (uintptr_t)list, ia, 0); + else + { + node->x = 0; + node->alpha = node->label_alpha = ia; + } + } +} + +static void stripes_set_title(stripes_handle_t *stripes) +{ + if (stripes->categories_selection_ptr <= stripes->system_tab_end) + { + menu_entries_get_title(stripes->title_name, sizeof(stripes->title_name)); + } + else + { + const char *path = NULL; + menu_entries_get_at_offset( + stripes->horizontal_list, + stripes->categories_selection_ptr - (stripes->system_tab_end + 1), + &path, NULL, NULL, NULL, NULL); + + if (!path) + return; + + fill_pathname_base_noext( + stripes->title_name, path, sizeof(stripes->title_name)); + } +} + +static stripes_node_t* stripes_get_node(stripes_handle_t *stripes, unsigned i) +{ + switch (stripes_get_system_tab(stripes, i)) + { + case STRIPES_SYSTEM_TAB_SETTINGS: + return &stripes->settings_tab_node; +#ifdef HAVE_IMAGEVIEWER + case STRIPES_SYSTEM_TAB_IMAGES: + return &stripes->images_tab_node; +#endif + case STRIPES_SYSTEM_TAB_MUSIC: + return &stripes->music_tab_node; +#ifdef HAVE_FFMPEG + case STRIPES_SYSTEM_TAB_VIDEO: + return &stripes->video_tab_node; +#endif + case STRIPES_SYSTEM_TAB_HISTORY: + return &stripes->history_tab_node; + case STRIPES_SYSTEM_TAB_FAVORITES: + return &stripes->favorites_tab_node; +#ifdef HAVE_NETWORKING + case STRIPES_SYSTEM_TAB_NETPLAY: + return &stripes->netplay_tab_node; +#endif + case STRIPES_SYSTEM_TAB_ADD: + return &stripes->add_tab_node; + default: + if (i > stripes->system_tab_end) + return stripes_get_userdata_from_horizontal_list( + stripes, i - (stripes->system_tab_end + 1)); + } + + return &stripes->main_menu_node; +} + +static void stripes_list_switch_horizontal_list(stripes_handle_t *stripes) +{ + unsigned j; + size_t list_size = stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL) + + stripes->system_tab_end; + + for (j = 0; j <= list_size; j++) + { + menu_animation_ctx_entry_t entry; + float ia = stripes->categories_passive_alpha; + float iz = stripes->categories_passive_zoom; + float iw = stripes->categories_passive_width; + float ix = stripes->categories_before_x; + float iy = stripes->categories_before_y; + stripes_node_t *node = stripes_get_node(stripes, j); + + if (!node) + continue; + + if (j == stripes->categories_active_idx) + { + ia = stripes->categories_active_alpha; + iz = stripes->categories_active_zoom; + iw = stripes->categories_active_width; + ix = stripes->categories_active_x; + iy = stripes->categories_active_y; + } + else if (j < stripes->categories_active_idx) + { + ix = stripes->categories_before_x; + iy = stripes->categories_before_y; + } + else if (j > stripes->categories_active_idx) + { + ix = stripes->categories_after_x; + iy = stripes->categories_after_y; + } + + entry.duration = STRIPES_DELAY; + entry.target_value = ia; + entry.subject = &node->alpha; + entry.easing_enum = EASING_OUT_QUAD; + /* TODO/FIXME - integer conversion resulted in change of sign */ + entry.tag = -1; + entry.cb = NULL; + + menu_animation_push(&entry); + + entry.target_value = iz; + entry.subject = &node->zoom; + + menu_animation_push(&entry); + + entry.target_value = iy; + entry.subject = &node->y; + + menu_animation_push(&entry); + + entry.target_value = ix; + entry.subject = &node->x; + + menu_animation_push(&entry); + + entry.target_value = iw; + entry.subject = &node->width; + + menu_animation_push(&entry); + } +} + +static void stripes_list_switch(stripes_handle_t *stripes) +{ + menu_animation_ctx_entry_t anim_entry; + int dir = -1; + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + size_t selection = menu_navigation_get_selection(); + settings_t *settings = config_get_ptr(); + + if (stripes->categories_selection_ptr > stripes->categories_selection_ptr_old) + dir = 1; + + stripes->categories_active_idx += dir; + + stripes_list_switch_horizontal_list(stripes); + + anim_entry.duration = STRIPES_DELAY; + anim_entry.target_value = stripes->categories_passive_width + * -(float)stripes->categories_selection_ptr; + anim_entry.subject = &stripes->categories_x_pos; + anim_entry.easing_enum = EASING_OUT_QUAD; + /* TODO/FIXME - integer conversion resulted in change of sign */ + anim_entry.tag = -1; + anim_entry.cb = NULL; + + if (anim_entry.subject) + menu_animation_push(&anim_entry); + + dir = -1; + if (stripes->categories_selection_ptr > stripes->categories_selection_ptr_old) + dir = 1; + + stripes_list_switch_old(stripes, stripes->selection_buf_old, + dir, stripes->selection_ptr_old); + + /* Check if we are to have horizontal animations. */ + if (settings->bools.menu_horizontal_animation) + stripes_list_switch_new(stripes, selection_buf, dir, selection); + stripes->categories_active_idx_old = (unsigned)stripes->categories_selection_ptr; + + if (!string_is_equal(stripes_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + menu_entry_t entry; + + menu_entry_init(&entry); + menu_entry_get(&entry, 0, selection, NULL, true); + + if (!string_is_empty(entry.path)) + stripes_set_thumbnail_content(stripes, entry.path, 0 /* will be ignored */); + + menu_entry_free(&entry); + + stripes_update_thumbnail_path(stripes, 0, 'R'); + stripes_update_thumbnail_image(stripes); + } + if (!string_is_equal(stripes_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + menu_entry_t entry; + + menu_entry_init(&entry); + menu_entry_get(&entry, 0, selection, NULL, true); + + if (!string_is_empty(entry.path)) + stripes_set_thumbnail_content(stripes, entry.path, 0 /* will be ignored */); + + menu_entry_free(&entry); + + stripes_update_thumbnail_path(stripes, 0, 'L'); + stripes_update_thumbnail_image(stripes); + } +} + +static void stripes_list_open_horizontal_list(stripes_handle_t *stripes) +{ + unsigned j; + size_t list_size = stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL) + + stripes->system_tab_end; + + for (j = 0; j <= list_size; j++) + { + menu_animation_ctx_entry_t anim_entry; + float ia = 0; + stripes_node_t *node = stripes_get_node(stripes, j); + + if (!node) + continue; + + if (j == stripes->categories_active_idx) + ia = stripes->categories_active_alpha; + else if (stripes->depth <= 1) + ia = stripes->categories_passive_alpha; + + anim_entry.duration = STRIPES_DELAY; + anim_entry.target_value = ia; + anim_entry.subject = &node->alpha; + anim_entry.easing_enum = EASING_OUT_QUAD; + /* TODO/FIXME - integer conversion resulted in change of sign */ + anim_entry.tag = -1; + anim_entry.cb = NULL; + + if (anim_entry.subject) + menu_animation_push(&anim_entry); + } +} + +static void stripes_context_destroy_horizontal_list(stripes_handle_t *stripes) +{ + unsigned i; + size_t list_size = stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL); + + for (i = 0; i < list_size; i++) + { + const char *path = NULL; + stripes_node_t *node = stripes_get_userdata_from_horizontal_list(stripes, i); + + if (!node) + continue; + + file_list_get_at_offset(stripes->horizontal_list, i, + &path, NULL, NULL, NULL); + + if (!path || !strstr(path, file_path_str(FILE_PATH_LPL_EXTENSION))) + continue; + + video_driver_texture_unload(&node->icon); + video_driver_texture_unload(&node->content_icon); + } +} + +static void stripes_init_horizontal_list(stripes_handle_t *stripes) +{ + menu_displaylist_info_t info; + settings_t *settings = config_get_ptr(); + + menu_displaylist_info_init(&info); + + info.list = stripes->horizontal_list; + info.path = strdup( + settings->paths.directory_playlist); + info.label = strdup( + msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST)); + info.exts = strdup( + file_path_str(FILE_PATH_LPL_EXTENSION_NO_DOT)); + info.type_default = FILE_TYPE_PLAIN; + info.enum_idx = MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST; + + if (!string_is_empty(info.path)) + { + if (menu_displaylist_ctl(DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL, &info)) + { + size_t i; + for (i = 0; i < stripes->horizontal_list->size; i++) + stripes_node_allocate_userdata(stripes, (unsigned)i); + menu_displaylist_process(&info); + } + } + + menu_displaylist_info_free(&info); +} + +static void stripes_toggle_horizontal_list(stripes_handle_t *stripes) +{ + unsigned i; + size_t list_size = stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL) + + stripes->system_tab_end; + + for (i = 0; i <= list_size; i++) + { + stripes_node_t *node = stripes_get_node(stripes, i); + + if (!node) + continue; + + node->alpha = 0; + node->zoom = stripes->categories_passive_zoom; + + if (i == stripes->categories_active_idx) + { + node->alpha = stripes->categories_active_alpha; + node->zoom = stripes->categories_active_zoom; + } + else if (stripes->depth <= 1) + node->alpha = stripes->categories_passive_alpha; + } +} + +static void stripes_context_reset_horizontal_list( + stripes_handle_t *stripes) +{ + unsigned i; + int depth; /* keep this integer */ + size_t list_size = + stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL); + + stripes->categories_x_pos = + stripes->categories_passive_width * + -(float)stripes->categories_selection_ptr; + + depth = (stripes->depth > 1) ? 2 : 1; + stripes->x = stripes->icon_size * -(depth*2-2); + + for (i = 0; i < list_size; i++) + { + const char *path = NULL; + stripes_node_t *node = + stripes_get_userdata_from_horizontal_list(stripes, i); + + if (!node) + { + node = stripes_node_allocate_userdata(stripes, i); + if (!node) + continue; + } + + + file_list_get_at_offset(stripes->horizontal_list, i, + &path, NULL, NULL, NULL); + + if (!path) + continue; + + if (!strstr(path, file_path_str(FILE_PATH_LPL_EXTENSION))) + continue; + + { + struct texture_image ti; + char sysname[256]; + char *iconpath = (char*) + malloc(PATH_MAX_LENGTH * sizeof(char)); + char *texturepath = (char*) + malloc(PATH_MAX_LENGTH * sizeof(char)); + char *content_texturepath = (char*) + malloc(PATH_MAX_LENGTH * sizeof(char)); + + iconpath[0] = sysname[0] = + texturepath[0] = content_texturepath[0] = '\0'; + + fill_pathname_base_noext(sysname, path, sizeof(sysname)); + + fill_pathname_application_special(iconpath, + PATH_MAX_LENGTH * sizeof(char), + APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_ICONS); + + fill_pathname_join_concat(texturepath, iconpath, sysname, + file_path_str(FILE_PATH_PNG_EXTENSION), + PATH_MAX_LENGTH * sizeof(char)); + + ti.width = 0; + ti.height = 0; + ti.pixels = NULL; + ti.supports_rgba = video_driver_supports_rgba(); + + if (image_texture_load(&ti, texturepath)) + { + if(ti.pixels) + { + video_driver_texture_unload(&node->icon); + video_driver_texture_load(&ti, + TEXTURE_FILTER_MIPMAP_LINEAR, &node->icon); + } + + image_texture_free(&ti); + } + + strlcat(iconpath, sysname, PATH_MAX_LENGTH * sizeof(char)); + fill_pathname_join_delim(content_texturepath, iconpath, + file_path_str(FILE_PATH_CONTENT_BASENAME), '-', + PATH_MAX_LENGTH * sizeof(char)); + + if (image_texture_load(&ti, content_texturepath)) + { + if(ti.pixels) + { + video_driver_texture_unload(&node->content_icon); + video_driver_texture_load(&ti, + TEXTURE_FILTER_MIPMAP_LINEAR, &node->content_icon); + } + + image_texture_free(&ti); + } + + free(iconpath); + free(texturepath); + free(content_texturepath); + } + } + + stripes_toggle_horizontal_list(stripes); +} + +static void stripes_refresh_horizontal_list(stripes_handle_t *stripes) +{ + stripes_context_destroy_horizontal_list(stripes); + if (stripes->horizontal_list) + { + stripes_free_list_nodes(stripes->horizontal_list, false); + file_list_free(stripes->horizontal_list); + } + stripes->horizontal_list = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); + + stripes->horizontal_list = (file_list_t*) + calloc(1, sizeof(file_list_t)); + + if (stripes->horizontal_list) + stripes_init_horizontal_list(stripes); + + stripes_context_reset_horizontal_list(stripes); +} + +static int stripes_environ(enum menu_environ_cb type, void *data, void *userdata) +{ + stripes_handle_t *stripes = (stripes_handle_t*)userdata; + + switch (type) + { + case MENU_ENVIRON_ENABLE_MOUSE_CURSOR: + if (!stripes) + return -1; + stripes->mouse_show = true; + break; + case MENU_ENVIRON_DISABLE_MOUSE_CURSOR: + if (!stripes) + return -1; + stripes->mouse_show = false; + break; + case MENU_ENVIRON_RESET_HORIZONTAL_LIST: + if (!stripes) + return -1; + + stripes_refresh_horizontal_list(stripes); + break; + default: + return -1; + } + + return 0; +} + +static void stripes_list_open(stripes_handle_t *stripes) +{ + menu_animation_ctx_entry_t entry; + + int dir = 0; + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + size_t selection = menu_navigation_get_selection(); + + stripes->depth = (int)stripes_list_get_size(stripes, MENU_LIST_PLAIN); + + if (stripes->depth > stripes->old_depth) + dir = 1; + else if (stripes->depth < stripes->old_depth) + dir = -1; + + stripes_list_open_horizontal_list(stripes); + + stripes_list_open_old(stripes, stripes->selection_buf_old, + dir, stripes->selection_ptr_old); + stripes_list_open_new(stripes, selection_buf, + dir, selection); + + + entry.duration = STRIPES_DELAY; + entry.target_value = stripes->icon_size * -(stripes->depth*2-2); + entry.subject = &stripes->x; + entry.easing_enum = EASING_OUT_QUAD; + /* TODO/FIXME - integer conversion resulted in change of sign */ + entry.tag = -1; + entry.cb = NULL; + + switch (stripes->depth) + { + case 1: + menu_animation_push(&entry); + + entry.target_value = 0; + entry.subject = &stripes->textures_arrow_alpha; + + menu_animation_push(&entry); + break; + case 2: + menu_animation_push(&entry); + + entry.target_value = 1; + entry.subject = &stripes->textures_arrow_alpha; + + menu_animation_push(&entry); + break; + } + + stripes->old_depth = stripes->depth; +} + +static void stripes_populate_entries(void *data, + const char *path, + const char *label, unsigned k) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + + if (!stripes) + return; + + if (menu_driver_ctl(RARCH_MENU_CTL_IS_PREVENT_POPULATE, NULL)) + { + stripes_selection_pointer_changed(stripes, false); + menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL); + if (!string_is_equal(stripes_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + stripes_update_thumbnail_image(stripes); + stripes_update_savestate_thumbnail_image(stripes); + if (!string_is_equal(stripes_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + stripes_update_thumbnail_image(stripes); + return; + } + + stripes_set_title(stripes); + + if (stripes->categories_selection_ptr != stripes->categories_active_idx_old) + stripes_list_switch(stripes); + else + stripes_list_open(stripes); +} + +static uintptr_t stripes_icon_get_id(stripes_handle_t *stripes, + stripes_node_t *core_node, stripes_node_t *node, + enum msg_hash_enums enum_idx, unsigned type, bool active) +{ + switch (enum_idx) + { + case MENU_ENUM_LABEL_CORE_OPTIONS: + case MENU_ENUM_LABEL_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE: + return stripes->textures.list[STRIPES_TEXTURE_CORE_OPTIONS]; + case MENU_ENUM_LABEL_ADD_TO_FAVORITES: + case MENU_ENUM_LABEL_ADD_TO_FAVORITES_PLAYLIST: + return stripes->textures.list[STRIPES_TEXTURE_ADD_FAVORITE]; + case MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION: + return stripes->textures.list[STRIPES_TEXTURE_RENAME]; + case MENU_ENUM_LABEL_CORE_INPUT_REMAPPING_OPTIONS: + return stripes->textures.list[STRIPES_TEXTURE_INPUT_REMAPPING_OPTIONS]; + case MENU_ENUM_LABEL_CORE_CHEAT_OPTIONS: + return stripes->textures.list[STRIPES_TEXTURE_CHEAT_OPTIONS]; + case MENU_ENUM_LABEL_DISK_OPTIONS: + return stripes->textures.list[STRIPES_TEXTURE_DISK_OPTIONS]; + case MENU_ENUM_LABEL_SHADER_OPTIONS: + return stripes->textures.list[STRIPES_TEXTURE_SHADER_OPTIONS]; + case MENU_ENUM_LABEL_ACHIEVEMENT_LIST: + return stripes->textures.list[STRIPES_TEXTURE_ACHIEVEMENT_LIST]; + case MENU_ENUM_LABEL_ACHIEVEMENT_LIST_HARDCORE: + return stripes->textures.list[STRIPES_TEXTURE_ACHIEVEMENT_LIST]; + case MENU_ENUM_LABEL_SAVE_STATE: + return stripes->textures.list[STRIPES_TEXTURE_SAVESTATE]; + case MENU_ENUM_LABEL_LOAD_STATE: + return stripes->textures.list[STRIPES_TEXTURE_LOADSTATE]; + case MENU_ENUM_LABEL_PARENT_DIRECTORY: + case MENU_ENUM_LABEL_UNDO_LOAD_STATE: + case MENU_ENUM_LABEL_UNDO_SAVE_STATE: + return stripes->textures.list[STRIPES_TEXTURE_UNDO]; + case MENU_ENUM_LABEL_TAKE_SCREENSHOT: + return stripes->textures.list[STRIPES_TEXTURE_SCREENSHOT]; + case MENU_ENUM_LABEL_DELETE_ENTRY: + return stripes->textures.list[STRIPES_TEXTURE_CLOSE]; + case MENU_ENUM_LABEL_RESTART_CONTENT: + return stripes->textures.list[STRIPES_TEXTURE_RELOAD]; + case MENU_ENUM_LABEL_RENAME_ENTRY: + return stripes->textures.list[STRIPES_TEXTURE_RENAME]; + case MENU_ENUM_LABEL_RESUME_CONTENT: + return stripes->textures.list[STRIPES_TEXTURE_RESUME]; + case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE: + case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_GAME: + return stripes->textures.list[STRIPES_TEXTURE_SAVESTATE]; + case MENU_ENUM_LABEL_FAVORITES: + case MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST: + return stripes->textures.list[STRIPES_TEXTURE_FOLDER]; + case MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR: + return stripes->textures.list[STRIPES_TEXTURE_RDB]; + default: + break; + } + + switch(type) + { + case FILE_TYPE_DIRECTORY: + return stripes->textures.list[STRIPES_TEXTURE_FOLDER]; + case FILE_TYPE_PLAIN: + case FILE_TYPE_IN_CARCHIVE: + return stripes->textures.list[STRIPES_TEXTURE_FILE]; + case FILE_TYPE_RPL_ENTRY: + if (core_node) + return core_node->content_icon; + + switch (stripes_get_system_tab(stripes, + (unsigned)stripes->categories_selection_ptr)) + { + case STRIPES_SYSTEM_TAB_FAVORITES: + return stripes->textures.list[STRIPES_TEXTURE_FAVORITE]; + case STRIPES_SYSTEM_TAB_MUSIC: + return stripes->textures.list[STRIPES_TEXTURE_MUSIC]; +#ifdef HAVE_IMAGEVIEWER + case STRIPES_SYSTEM_TAB_IMAGES: + return stripes->textures.list[STRIPES_TEXTURE_IMAGE]; +#endif +#ifdef HAVE_FFMPEG + case STRIPES_SYSTEM_TAB_VIDEO: + return stripes->textures.list[STRIPES_TEXTURE_MOVIE]; +#endif + default: + break; + } + return stripes->textures.list[STRIPES_TEXTURE_FILE]; + case FILE_TYPE_CARCHIVE: + return stripes->textures.list[STRIPES_TEXTURE_ZIP]; + case FILE_TYPE_MUSIC: + return stripes->textures.list[STRIPES_TEXTURE_MUSIC]; + case FILE_TYPE_IMAGE: + case FILE_TYPE_IMAGEVIEWER: + return stripes->textures.list[STRIPES_TEXTURE_IMAGE]; + case FILE_TYPE_MOVIE: + return stripes->textures.list[STRIPES_TEXTURE_MOVIE]; + case FILE_TYPE_CORE: + case FILE_TYPE_DIRECT_LOAD: + return stripes->textures.list[STRIPES_TEXTURE_CORE]; + case FILE_TYPE_RDB: + return stripes->textures.list[STRIPES_TEXTURE_RDB]; + case FILE_TYPE_CURSOR: + return stripes->textures.list[STRIPES_TEXTURE_CURSOR]; + case FILE_TYPE_PLAYLIST_ENTRY: + case MENU_SETTING_ACTION_RUN: + return stripes->textures.list[STRIPES_TEXTURE_RUN]; + case MENU_SETTING_ACTION_CLOSE: + return stripes->textures.list[STRIPES_TEXTURE_CLOSE]; + case MENU_SETTING_ACTION_SAVESTATE: + return stripes->textures.list[STRIPES_TEXTURE_SAVESTATE]; + case MENU_SETTING_ACTION_LOADSTATE: + return stripes->textures.list[STRIPES_TEXTURE_LOADSTATE]; + case FILE_TYPE_RDB_ENTRY: + case MENU_SETTING_ACTION_CORE_INFORMATION: + return stripes->textures.list[STRIPES_TEXTURE_CORE_INFO]; + case MENU_SETTING_ACTION_CORE_OPTIONS: + return stripes->textures.list[STRIPES_TEXTURE_CORE_OPTIONS]; + case MENU_SETTING_ACTION_CORE_INPUT_REMAPPING_OPTIONS: + return stripes->textures.list[STRIPES_TEXTURE_INPUT_REMAPPING_OPTIONS]; + case MENU_SETTING_ACTION_CORE_CHEAT_OPTIONS: + return stripes->textures.list[STRIPES_TEXTURE_CHEAT_OPTIONS]; + case MENU_SETTING_ACTION_CORE_DISK_OPTIONS: + return stripes->textures.list[STRIPES_TEXTURE_DISK_OPTIONS]; + case MENU_SETTING_ACTION_CORE_SHADER_OPTIONS: + return stripes->textures.list[STRIPES_TEXTURE_SHADER_OPTIONS]; + case MENU_SETTING_ACTION_SCREENSHOT: + return stripes->textures.list[STRIPES_TEXTURE_SCREENSHOT]; + case MENU_SETTING_ACTION_DELETE_ENTRY: + return stripes->textures.list[STRIPES_TEXTURE_CLOSE]; + case MENU_SETTING_ACTION_RESET: + return stripes->textures.list[STRIPES_TEXTURE_RELOAD]; + case MENU_SETTING_ACTION: + if (stripes->depth == 3) + return stripes->textures.list[STRIPES_TEXTURE_SUBSETTING]; + return stripes->textures.list[STRIPES_TEXTURE_SETTING]; + case MENU_SETTING_GROUP: + return stripes->textures.list[STRIPES_TEXTURE_SETTING]; + case MENU_INFO_MESSAGE: + return stripes->textures.list[STRIPES_TEXTURE_CORE_INFO]; + case MENU_WIFI: + return stripes->textures.list[STRIPES_TEXTURE_WIFI]; +#ifdef HAVE_NETWORKING + case MENU_ROOM: + return stripes->textures.list[STRIPES_TEXTURE_ROOM]; +#if 0 + /* stub these out until we have the icons */ + case MENU_ROOM_LAN: + return stripes->textures.list[STRIPES_TEXTURE_ROOM_LAN]; + case MENU_ROOM_MITM: + return stripes->textures.list[STRIPES_TEXTURE_ROOM_MITM]; +#endif +#endif + } + +#ifdef HAVE_CHEEVOS + if ( + (type >= MENU_SETTINGS_CHEEVOS_START) && + (type < MENU_SETTINGS_NETPLAY_ROOMS_START) + ) + { + int new_id = type - MENU_SETTINGS_CHEEVOS_START; + if (get_badge_texture(new_id) != 0) + return get_badge_texture(new_id); + /* Should be replaced with placeholder badge icon. */ + return stripes->textures.list[STRIPES_TEXTURE_SUBSETTING]; + } +#endif + + return stripes->textures.list[STRIPES_TEXTURE_SUBSETTING]; +} + +static void stripes_calculate_visible_range(const stripes_handle_t *stripes, + unsigned height, size_t list_size, unsigned current, + unsigned *first, unsigned *last) +{ + unsigned j; + float base_y = stripes->margins_screen_top; + + *first = 0; + *last = list_size ? list_size - 1 : 0; + + if (current) + { + for (j = current; j-- > 0; ) + { + float bottom = stripes_item_y(stripes, j, current) + + base_y + stripes->icon_size; + + if (bottom < 0) + break; + + *first = j; + } + } + + for (j = current+1; j < list_size; j++) + { + float top = stripes_item_y(stripes, j, current) + base_y; + + if (top > height) + break; + + *last = j; + } +} + +static int stripes_draw_item( + video_frame_info_t *video_info, + menu_entry_t *entry, + math_matrix_4x4 *mymat, + stripes_handle_t *stripes, + stripes_node_t *core_node, + file_list_t *list, + float *color, + const char *thumb_ident, + const char *left_thumb_ident, + uint64_t frame_count, + size_t i, + size_t current, + unsigned width, + unsigned height + ) +{ + float icon_x, icon_y, label_offset; + menu_animation_ctx_ticker_t ticker; + char tmp[255]; + char *ticker_str = NULL; + unsigned entry_type = 0; + const float half_size = stripes->icon_size / 2.0f; + uintptr_t texture_switch = 0; + bool do_draw_text = false; + unsigned ticker_limit = 35 * stripes_scale_mod[0]; + stripes_node_t * node = (stripes_node_t*) + file_list_get_userdata_at_offset(list, i); + settings_t *settings = config_get_ptr(); + + if (!node) + goto iterate; + + tmp[0] = '\0'; + + icon_y = stripes->margins_screen_top + node->y + half_size; + + if (icon_y < half_size) + goto iterate; + + if (icon_y > height + stripes->icon_size) + goto end; + + icon_x = node->x + stripes->margins_screen_left + + stripes->icon_spacing_horizontal - half_size; + + if (icon_x < -half_size || icon_x > width) + goto iterate; + + entry_type = menu_entry_get_type_new(entry); + + if (entry_type == FILE_TYPE_CONTENTLIST_ENTRY) + { + char entry_path[PATH_MAX_LENGTH] = {0}; + strlcpy(entry_path, entry->path, sizeof(entry_path)); + + fill_short_pathname_representation(entry_path, entry_path, + sizeof(entry_path)); + + if (!string_is_empty(entry_path)) + { + if (!string_is_empty(entry->path)) + free(entry->path); + entry->path = strdup(entry_path); + } + } + + if (string_is_equal(entry->value, + msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) || + (string_is_equal(entry->value, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)))) + { + if (stripes->textures.list[STRIPES_TEXTURE_SWITCH_OFF]) + texture_switch = stripes->textures.list[STRIPES_TEXTURE_SWITCH_OFF]; + else + do_draw_text = true; + } + else if (string_is_equal(entry->value, + msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) || + (string_is_equal(entry->value, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)))) + { + if (stripes->textures.list[STRIPES_TEXTURE_SWITCH_ON]) + texture_switch = stripes->textures.list[STRIPES_TEXTURE_SWITCH_ON]; + else + do_draw_text = true; + } + else + { + if (!string_is_empty(entry->value)) + { + if ( + string_is_equal(entry->value, "...") || + string_is_equal(entry->value, "(COMP)") || + string_is_equal(entry->value, "(CORE)") || + string_is_equal(entry->value, "(MOVIE)") || + string_is_equal(entry->value, "(MUSIC)") || + string_is_equal(entry->value, "(DIR)") || + string_is_equal(entry->value, "(RDB)") || + string_is_equal(entry->value, "(CURSOR)")|| + string_is_equal(entry->value, "(CFILE)") || + string_is_equal(entry->value, "(FILE)") || + string_is_equal(entry->value, "(IMAGE)") + ) + { + } + else + do_draw_text = true; + } + else + do_draw_text = true; + + } + + if (string_is_empty(entry->value)) + { + if (stripes->savestate_thumbnail || + (!string_is_equal + (thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) + && stripes->thumbnail) || + (!string_is_equal + (left_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) + && stripes->left_thumbnail + && settings->bools.menu_xmb_vertical_thumbnails) + ) + ticker_limit = 40 * stripes_scale_mod[1]; + else + ticker_limit = 70 * stripes_scale_mod[2]; + } + + if (!string_is_empty(entry->path)) + ticker_str = menu_entry_get_rich_label(entry); + + ticker.s = tmp; + ticker.len = ticker_limit; + ticker.idx = frame_count / 20; + ticker.str = ticker_str; + ticker.selected = (i == current); + + if (ticker.str) + menu_animation_ticker(&ticker); + + label_offset = stripes->margins_label_top; + if (i == current && width > 320 && height > 240 + && !string_is_empty(entry->sublabel)) + { + char entry_sublabel[255] = {0}; + + label_offset = - stripes->margins_label_top; + + word_wrap(entry_sublabel, entry->sublabel, 50 * stripes_scale_mod[3], true); + + stripes_draw_text(video_info, stripes, entry_sublabel, + node->x + stripes->margins_screen_left + + stripes->icon_spacing_horizontal + stripes->margins_label_left, + stripes->margins_screen_top + node->y + stripes->margins_label_top*3.5, + 1, node->label_alpha, TEXT_ALIGN_LEFT, + width, height, stripes->font2); + } + + stripes_draw_text(video_info, stripes, tmp, + node->x + stripes->margins_screen_left + + stripes->icon_spacing_horizontal + stripes->margins_label_left, + stripes->margins_screen_top + node->y + label_offset, + 1, node->label_alpha, TEXT_ALIGN_LEFT, + width, height, stripes->font); + + tmp[0] = '\0'; + + ticker.s = tmp; + ticker.len = 35 * stripes_scale_mod[7]; + ticker.idx = frame_count / 20; + ticker.selected = (i == current); + + if (!string_is_empty(entry->value)) + { + ticker.str = entry->value; + menu_animation_ticker(&ticker); + } + + if (do_draw_text) + stripes_draw_text(video_info, stripes, tmp, + node->x + + + stripes->margins_screen_left + + stripes->icon_spacing_horizontal + + stripes->margins_label_left + + stripes->margins_setting_left, + stripes->margins_screen_top + node->y + stripes->margins_label_top, + 1, + node->label_alpha, + TEXT_ALIGN_LEFT, + width, height, stripes->font); + + + menu_display_set_alpha(color, MIN(node->alpha, stripes->alpha)); + + if (color[3] != 0) + { + math_matrix_4x4 mymat_tmp; + menu_display_ctx_rotate_draw_t rotate_draw; + uintptr_t texture = stripes_icon_get_id(stripes, core_node, node, + entry->enum_idx, entry_type, (i == current)); + float x = icon_x; + float y = icon_y; + float rotation = 0; + float scale_factor = node->zoom; + + rotate_draw.matrix = &mymat_tmp; + rotate_draw.rotation = rotation; + rotate_draw.scale_x = scale_factor; + rotate_draw.scale_y = scale_factor; + rotate_draw.scale_z = 1; + rotate_draw.scale_enable = true; + + menu_display_rotate_z(&rotate_draw, video_info); + + stripes_draw_icon(video_info, + stripes->icon_size, + &mymat_tmp, + texture, + x, + y, + width, + height, + 1.0, + rotation, + scale_factor, + &color[0], + stripes->shadow_offset); + } + + menu_display_set_alpha(color, MIN(node->alpha, stripes->alpha)); + + if (texture_switch != 0 && color[3] != 0) + stripes_draw_icon(video_info, + stripes->icon_size, + mymat, + texture_switch, + node->x + stripes->margins_screen_left + + stripes->icon_spacing_horizontal + + stripes->icon_size / 2.0 + stripes->margins_setting_left, + stripes->margins_screen_top + node->y + stripes->icon_size / 2.0, + width, height, + node->alpha, + 0, + 1, + &color[0], + stripes->shadow_offset); + +iterate: + if (!string_is_empty(ticker_str)) + free(ticker_str); + return 0; + +end: + if (!string_is_empty(ticker_str)) + free(ticker_str); + return -1; +} + +static void stripes_draw_items( + video_frame_info_t *video_info, + stripes_handle_t *stripes, + file_list_t *list, + size_t current, size_t cat_selection_ptr, float *color, + unsigned width, unsigned height) +{ + size_t i; + unsigned first, last; + math_matrix_4x4 mymat; + menu_display_ctx_rotate_draw_t rotate_draw; + stripes_node_t *core_node = NULL; + size_t end = 0; + uint64_t frame_count = stripes ? stripes->frame_count : 0; + const char *thumb_ident = stripes_thumbnails_ident('R'); + const char *left_thumb_ident= stripes_thumbnails_ident('L'); + + if (!list || !list->size || !stripes) + return; + + if (cat_selection_ptr > stripes->system_tab_end) + core_node = stripes_get_userdata_from_horizontal_list( + stripes, (unsigned)(cat_selection_ptr - (stripes->system_tab_end + 1))); + + end = file_list_get_size(list); + + rotate_draw.matrix = &mymat; + rotate_draw.rotation = 0; + rotate_draw.scale_x = 1; + rotate_draw.scale_y = 1; + rotate_draw.scale_z = 1; + rotate_draw.scale_enable = true; + + menu_display_rotate_z(&rotate_draw, video_info); + + menu_entries_ctl(MENU_ENTRIES_CTL_START_GET, &i); + + if (list == stripes->selection_buf_old) + { + stripes_node_t *node = (stripes_node_t*) + file_list_get_userdata_at_offset(list, current); + + if (node && (uint8_t)(255 * node->alpha) == 0) + return; + + i = 0; + } + + first = i; + last = end - 1; + + stripes_calculate_visible_range(stripes, height, end, current, &first, &last); + + menu_display_blend_begin(video_info); + + for (i = first; i <= last; i++) + { + int ret; + menu_entry_t entry; + menu_entry_init(&entry); + menu_entry_get(&entry, 0, i, list, true); + ret = stripes_draw_item(video_info, + &entry, + &mymat, + stripes, core_node, + list, color, thumb_ident, left_thumb_ident, + frame_count, + i, current, + width, height); + menu_entry_free(&entry); + if (ret == -1) + break; + } + + menu_display_blend_end(video_info); +} + +static void stripes_render(void *data, bool is_idle) +{ + size_t i; + float delta_time; + menu_animation_ctx_delta_t delta; + settings_t *settings = config_get_ptr(); + stripes_handle_t *stripes = (stripes_handle_t*)data; + unsigned end = (unsigned)menu_entries_get_size(); + bool mouse_enable = settings->bools.menu_mouse_enable; + bool pointer_enable = settings->bools.menu_pointer_enable; + + if (!stripes) + return; + + menu_animation_ctl(MENU_ANIMATION_CTL_DELTA_TIME, &delta_time); + + delta.current = delta_time; + + if (menu_animation_get_ideal_delta_time(&delta)) + menu_animation_update(delta.ideal); + + if (pointer_enable || mouse_enable) + { + size_t selection = menu_navigation_get_selection(); + int16_t pointer_y = menu_input_pointer_state(MENU_POINTER_Y_AXIS); + int16_t mouse_y = menu_input_mouse_state(MENU_MOUSE_Y_AXIS) + + (stripes->cursor_size/2); + unsigned first = 0, last = end; + unsigned height; + + video_driver_get_size(NULL, &height); + + if (height) + stripes_calculate_visible_range(stripes, height, + end, selection, &first, &last); + + for (i = first; i <= last; i++) + { + float item_y1 = stripes->margins_screen_top + + stripes_item_y(stripes, (int)i, selection); + float item_y2 = item_y1 + stripes->icon_size; + + if (pointer_enable) + { + if (pointer_y > item_y1 && pointer_y < item_y2) + menu_input_ctl(MENU_INPUT_CTL_POINTER_PTR, &i); + } + + if (mouse_enable) + { + if (mouse_y > item_y1 && mouse_y < item_y2) + menu_input_ctl(MENU_INPUT_CTL_MOUSE_PTR, &i); + } + } + } + + menu_entries_ctl(MENU_ENTRIES_CTL_START_GET, &i); + + if (i >= end) + { + i = 0; + menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &i); + } + + menu_animation_ctl(MENU_ANIMATION_CTL_CLEAR_ACTIVE, NULL); +} + +static bool stripes_shader_pipeline_active(video_frame_info_t *video_info) +{ + if (string_is_not_equal(menu_driver_ident(), "stripes")) + return false; + if (video_info->menu_shader_pipeline == XMB_SHADER_PIPELINE_WALLPAPER) + return false; + return true; +} + +static void stripes_draw_bg( + stripes_handle_t *stripes, + video_frame_info_t *video_info, + unsigned width, + unsigned height) +{ + menu_display_ctx_draw_t draw; + struct video_coords coords; + + float rgb[3]; + HSLToRGB(0.0,0.5,0.5, &rgb[0]) ; + float color[16] = { + rgb[0], rgb[1], rgb[2], 1, + rgb[0], rgb[1], rgb[2], 1, + rgb[0], rgb[1], rgb[2], 1, + rgb[0], rgb[1], rgb[2], 1, + }; + + coords.vertices = 4; + coords.vertex = NULL; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + coords.color = &color[0]; + + draw.x = 0; + draw.y = 0; + draw.width = width; + draw.height = height; + draw.coords = &coords; + draw.matrix_data = NULL; + draw.texture = menu_display_white_texture; + draw.prim_type = MENU_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + + menu_display_blend_begin(video_info); + menu_display_draw(&draw, video_info); + menu_display_blend_end(video_info); +} + +static void stripes_draw_dark_layer( + stripes_handle_t *stripes, + video_frame_info_t *video_info, + unsigned width, + unsigned height) +{ + menu_display_ctx_draw_t draw; + struct video_coords coords; + float black[16] = { + 0, 0, 0, 1, + 0, 0, 0, 1, + 0, 0, 0, 1, + 0, 0, 0, 1, + }; + + menu_display_set_alpha(black, MIN(stripes->alpha, 0.75)); + + coords.vertices = 4; + coords.vertex = NULL; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + coords.color = &black[0]; + + draw.x = 0; + draw.y = 0; + draw.width = width; + draw.height = height; + draw.coords = &coords; + draw.matrix_data = NULL; + draw.texture = menu_display_white_texture; + draw.prim_type = MENU_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + + menu_display_blend_begin(video_info); + menu_display_draw(&draw, video_info); + menu_display_blend_end(video_info); +} + +static void stripes_frame(void *data, video_frame_info_t *video_info) +{ + math_matrix_4x4 mymat; + unsigned i; + menu_display_ctx_rotate_draw_t rotate_draw; + char msg[1024]; + char title_msg[255]; + char title_truncated[255]; + size_t selection = 0; + size_t percent_width = 0; + const int min_thumb_size = 50; + bool render_background = false; + file_list_t *selection_buf = NULL; + unsigned width = video_info->width; + unsigned height = video_info->height; + const float under_thumb_margin = 0.96; + float scale_factor = 0.0f; + float pseudo_font_length = 0.0f; + float stack_width = 285; + stripes_handle_t *stripes = (stripes_handle_t*)data; + settings_t *settings = config_get_ptr(); + + if (!stripes) + return; + + scale_factor = (settings->uints.menu_xmb_scale_factor * (float)width) / (1920.0 * 100); + pseudo_font_length = stripes->icon_spacing_horizontal * 4 - stripes->icon_size / 4; + + stripes->frame_count++; + + msg[0] = '\0'; + title_msg[0] = '\0'; + title_truncated[0] = '\0'; + + font_driver_bind_block(stripes->font, &stripes->raster_block); + font_driver_bind_block(stripes->font2, &stripes->raster_block2); + + stripes->raster_block.carr.coords.vertices = 0; + stripes->raster_block2.carr.coords.vertices = 0; + + menu_display_set_alpha(stripes_coord_black, MIN( + (float)video_info->xmb_alpha_factor/100, stripes->alpha)); + menu_display_set_alpha(stripes_coord_white, stripes->alpha); + + stripes_draw_bg( + stripes, + video_info, + width, + height); + + selection = menu_navigation_get_selection(); + + rotate_draw.matrix = &mymat; + rotate_draw.rotation = 0; + rotate_draw.scale_x = 1; + rotate_draw.scale_y = 1; + rotate_draw.scale_z = 1; + rotate_draw.scale_enable = true; + + menu_display_rotate_z(&rotate_draw, video_info); + menu_display_blend_begin(video_info); + + /* Horizontal stripes */ + for (i = 0; i <= stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL) + + stripes->system_tab_end; i++) + { + stripes_node_t *node = stripes_get_node(stripes, i); + + if (!node) + continue; + + float rgb[3]; + HSLToRGB(0.07*(float)i,0.5,0.5, &rgb[0]) ; + float color[16] = { + rgb[0], rgb[1], rgb[2], 0.55, + rgb[0], rgb[1], rgb[2], 0.55, + rgb[0], rgb[1], rgb[2], 0.55, + rgb[0], rgb[1], rgb[2], 0.55, + }; + + menu_display_draw_polygon( + video_info, + stripes->categories_x_pos + stack_width, + 0, + stripes->categories_x_pos + stack_width + node->width, + 0, + stripes->categories_x_pos + stack_width + stripes->categories_angle, + video_info->height, + stripes->categories_x_pos + stack_width + stripes->categories_angle + node->width, + video_info->height, + video_info->width, video_info->height, + &color[0]); + + menu_display_blend_begin(video_info); + + stack_width += node->width; + } + + stack_width = 285; + + /* Horizontal tab icons */ + for (i = 0; i <= stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL) + + stripes->system_tab_end; i++) + { + stripes_node_t *node = stripes_get_node(stripes, i); + + if (!node) + continue; + + menu_display_set_alpha(stripes_item_color, MIN(node->alpha, stripes->alpha)); + + if (stripes_item_color[3] != 0) + { + menu_display_ctx_rotate_draw_t rotate_draw; + math_matrix_4x4 mymat; + uintptr_t texture = node->icon; + float x = stripes->categories_x_pos + stack_width + node->x + node->width / 2.0 + - stripes->icon_size / 2.0; + float y = node->y + stripes->icon_size / 2.0; + float rotation = 0; + float scale_factor = node->zoom; + + rotate_draw.matrix = &mymat; + rotate_draw.rotation = rotation; + rotate_draw.scale_x = scale_factor; + rotate_draw.scale_y = scale_factor; + rotate_draw.scale_z = 1; + rotate_draw.scale_enable = true; + + menu_display_rotate_z(&rotate_draw, video_info); + + stripes_draw_icon(video_info, + stripes->icon_size, + &mymat, + texture, + x, + y, + width, + height, + 1.0, + rotation, + scale_factor, + &stripes_item_color[0], + stripes->shadow_offset); + } + + stack_width += node->width; + } + + menu_display_blend_end(video_info); + + /* Vertical icons */ +// if (stripes) +// stripes_draw_items( +// video_info, +// stripes, +// stripes->selection_buf_old, +// stripes->selection_ptr_old, +// (stripes_list_get_size(stripes, MENU_LIST_PLAIN) > 1) +// ? stripes->categories_selection_ptr : +// stripes->categories_selection_ptr_old, +// &stripes_item_color[0], +// width, +// height); + +// selection_buf = menu_entries_get_selection_buf_ptr(0); + +// if (stripes) +// stripes_draw_items( +// video_info, +// stripes, +// selection_buf, +// selection, +// stripes->categories_selection_ptr, +// &stripes_item_color[0], +// width, +// height); + + font_driver_flush(video_info->width, video_info->height, stripes->font, + video_info); + font_driver_bind_block(stripes->font, NULL); + + font_driver_flush(video_info->width, video_info->height, stripes->font2, + video_info); + font_driver_bind_block(stripes->font2, NULL); + + if (menu_input_dialog_get_display_kb()) + { + const char *str = menu_input_dialog_get_buffer(); + const char *label = menu_input_dialog_get_label_buffer(); + + snprintf(msg, sizeof(msg), "%s\n%s", label, str); + render_background = true; + } + + if (!string_is_empty(stripes->box_message)) + { + strlcpy(msg, stripes->box_message, + sizeof(msg)); + free(stripes->box_message); + stripes->box_message = NULL; + render_background = true; + } + + if (render_background) + { + stripes_draw_dark_layer(stripes, video_info, width, height); + stripes_render_messagebox_internal( + video_info, stripes, msg, &stripes_coord_white[0]); + } + + /* Cursor image */ + if (stripes->mouse_show) + { + menu_display_set_alpha(stripes_coord_white, MIN(stripes->alpha, 1.00f)); + menu_display_draw_cursor( + video_info, + &stripes_coord_white[0], + stripes->cursor_size, + stripes->textures.list[STRIPES_TEXTURE_POINTER], + menu_input_mouse_state(MENU_MOUSE_X_AXIS), + menu_input_mouse_state(MENU_MOUSE_Y_AXIS), + width, + height); + } + + menu_display_unset_viewport(video_info->width, video_info->height); +} + +static void stripes_layout_ps3(stripes_handle_t *stripes, int width, int height) +{ + unsigned new_font_size, new_header_height; + settings_t *settings = config_get_ptr(); + + float scale_factor = + (settings->uints.menu_xmb_scale_factor * width) / (1920.0 * 100); + + stripes->above_subitem_offset = 1.5; + stripes->above_item_offset = -1.0; + stripes->active_item_factor = 3.0; + stripes->under_item_offset = 5.0; + + stripes->categories_active_zoom = 1.0; + stripes->categories_passive_zoom = 0.25; + + stripes->categories_angle = 400 * scale_factor; + + stripes->categories_active_y = height / 2; + stripes->categories_before_y = 64 * scale_factor; + stripes->categories_after_y = height - 64 * scale_factor; + + stripes->categories_active_x = stripes->categories_angle / 2; + stripes->categories_before_x = stripes->categories_angle - 22 * scale_factor; + stripes->categories_after_x = 22 * scale_factor; + + stripes->categories_passive_width = 128 * scale_factor; + stripes->categories_active_width = 1200 * scale_factor; + + stripes->items_active_zoom = 1.0; + stripes->items_passive_zoom = 0.5; + + stripes->categories_active_alpha = 1.0; + stripes->categories_passive_alpha = 1.0; + stripes->items_active_alpha = 1.0; + stripes->items_passive_alpha = 0.85; + + stripes->shadow_offset = 2.0; + + new_font_size = 32.0 * scale_factor; + stripes->font2_size = 24.0 * scale_factor; + new_header_height = 128.0 * scale_factor; + + + stripes->thumbnail_width = 1024.0 * scale_factor; + stripes->left_thumbnail_width = 1024.0 * scale_factor; + stripes->savestate_thumbnail_width= 460.0 * scale_factor; + stripes->cursor_size = 64.0 * scale_factor; + + stripes->icon_spacing_horizontal = 200.0 * scale_factor; + stripes->icon_spacing_vertical = 64.0 * scale_factor; + + stripes->margins_screen_top = (256+32) * scale_factor; + stripes->margins_screen_left = 336.0 * scale_factor; + + stripes->margins_title_left = 60 * scale_factor; + stripes->margins_title_top = 60 * scale_factor + new_font_size / 3; + stripes->margins_title_bottom = 60 * scale_factor - new_font_size / 3; + + stripes->margins_label_left = 85.0 * scale_factor; + stripes->margins_label_top = new_font_size / 3.0; + + stripes->margins_setting_left = 600.0 * scale_factor * stripes_scale_mod[6]; + stripes->margins_dialog = 48 * scale_factor; + + stripes->margins_slice = 16; + + stripes->icon_size = 256.0 * scale_factor; + stripes->font_size = new_font_size; + +#ifdef STRIPES_DEBUG + RARCH_LOG("[XMB] margin screen left: %.2f\n", stripes->margins_screen_left); + RARCH_LOG("[XMB] margin screen top: %.2f\n", stripes->margins_screen_top); + RARCH_LOG("[XMB] margin title left: %.2f\n", stripes->margins_title_left); + RARCH_LOG("[XMB] margin title top: %.2f\n", stripes->margins_title_top); + RARCH_LOG("[XMB] margin title bott: %.2f\n", stripes->margins_title_bottom); + RARCH_LOG("[XMB] margin label left: %.2f\n", stripes->margins_label_left); + RARCH_LOG("[XMB] margin label top: %.2f\n", stripes->margins_label_top); + RARCH_LOG("[XMB] margin sett left: %.2f\n", stripes->margins_setting_left); + RARCH_LOG("[XMB] icon spacing hor: %.2f\n", stripes->icon_spacing_horizontal); + RARCH_LOG("[XMB] icon spacing ver: %.2f\n", stripes->icon_spacing_vertical); + RARCH_LOG("[XMB] icon size: %.2f\n", stripes->icon_size); +#endif + + menu_display_set_header_height(new_header_height); +} + +static void stripes_layout_psp(stripes_handle_t *stripes, int width) +{ + unsigned new_font_size, new_header_height; + settings_t *settings = config_get_ptr(); + float scale_factor = + ((settings->uints.menu_xmb_scale_factor * width) / (1920.0 * 100)) * 1.5; +#ifdef _3DS + scale_factor = + settings->uints.menu_xmb_scale_factor / 400.0; +#endif + + stripes->above_subitem_offset = 1.5; + stripes->above_item_offset = -1.0; + stripes->active_item_factor = 2.0; + stripes->under_item_offset = 3.0; + + stripes->categories_active_zoom = 1.0; + stripes->categories_passive_zoom = 1.0; + stripes->items_active_zoom = 1.0; + stripes->items_passive_zoom = 1.0; + + stripes->categories_active_alpha = 1.0; + stripes->categories_passive_alpha = 0.85; + stripes->items_active_alpha = 1.0; + stripes->items_passive_alpha = 0.85; + + stripes->shadow_offset = 1.0; + + new_font_size = 32.0 * scale_factor; + stripes->font2_size = 24.0 * scale_factor; + new_header_height = 128.0 * scale_factor; + stripes->margins_screen_top = (256+32) * scale_factor; + + stripes->thumbnail_width = 460.0 * scale_factor; + stripes->left_thumbnail_width = 400.0 * scale_factor; + stripes->savestate_thumbnail_width= 460.0 * scale_factor; + stripes->cursor_size = 64.0; + + stripes->icon_spacing_horizontal = 250.0 * scale_factor; + stripes->icon_spacing_vertical = 108.0 * scale_factor; + + stripes->margins_screen_left = 136.0 * scale_factor; + stripes->margins_title_left = 60 * scale_factor; + stripes->margins_title_top = 60 * scale_factor + new_font_size / 3; + stripes->margins_title_bottom = 60 * scale_factor - new_font_size / 3; + stripes->margins_label_left = 85.0 * scale_factor; + stripes->margins_label_top = new_font_size / 3.0; + stripes->margins_setting_left = 600.0 * scale_factor; + stripes->margins_dialog = 48 * scale_factor; + stripes->margins_slice = 16; + stripes->icon_size = 128.0 * scale_factor; + stripes->font_size = new_font_size; + +#ifdef STRIPES_DEBUG + RARCH_LOG("[XMB] margin screen left: %.2f\n", stripes->margins_screen_left); + RARCH_LOG("[XMB] margin screen top: %.2f\n", stripes->margins_screen_top); + RARCH_LOG("[XMB] margin title left: %.2f\n", stripes->margins_title_left); + RARCH_LOG("[XMB] margin title top: %.2f\n", stripes->margins_title_top); + RARCH_LOG("[XMB] margin title bott: %.2f\n", stripes->margins_title_bottom); + RARCH_LOG("[XMB] margin label left: %.2f\n", stripes->margins_label_left); + RARCH_LOG("[XMB] margin label top: %.2f\n", stripes->margins_label_top); + RARCH_LOG("[XMB] margin sett left: %.2f\n", stripes->margins_setting_left); + RARCH_LOG("[XMB] icon spacing hor: %.2f\n", stripes->icon_spacing_horizontal); + RARCH_LOG("[XMB] icon spacing ver: %.2f\n", stripes->icon_spacing_vertical); + RARCH_LOG("[XMB] icon size: %.2f\n", stripes->icon_size); +#endif + + menu_display_set_header_height(new_header_height); +} + +static void stripes_layout(stripes_handle_t *stripes) +{ + unsigned width, height, i, current, end; + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + size_t selection = menu_navigation_get_selection(); + + video_driver_get_size(&width, &height); + + /* Mimic the layout of the PSP instead of the PS3 on tiny screens */ + if (width > 320 && height > 240) + stripes_layout_ps3(stripes, width, height); + else + stripes_layout_psp(stripes, width); + + current = (unsigned)selection; + end = (unsigned)menu_entries_get_size(); + + for (i = 0; i < end; i++) + { + float ia = stripes->items_passive_alpha; + float iz = stripes->items_passive_zoom; + stripes_node_t *node = (stripes_node_t*)file_list_get_userdata_at_offset( + selection_buf, i); + + if (!node) + continue; + + if (i == current) + { + ia = stripes->items_active_alpha; + iz = stripes->items_active_alpha; + } + + node->alpha = ia; + node->label_alpha = ia; + node->zoom = iz; + node->y = stripes_item_y(stripes, i, current); + } + + if (stripes->depth <= 1) + return; + + current = (unsigned)stripes->selection_ptr_old; + end = (unsigned)file_list_get_size(stripes->selection_buf_old); + + for (i = 0; i < end; i++) + { + float ia = 0; + float iz = stripes->items_passive_zoom; + stripes_node_t *node = (stripes_node_t*)file_list_get_userdata_at_offset( + stripes->selection_buf_old, i); + + if (!node) + continue; + + if (i == current) + { + ia = stripes->items_active_alpha; + iz = stripes->items_active_alpha; + } + + node->alpha = ia; + node->label_alpha = 0; + node->zoom = iz; + node->y = stripes_item_y(stripes, i, current); + node->x = stripes->icon_size * 1 * -2; + } +} + +static void *stripes_init(void **userdata, bool video_is_threaded) +{ + unsigned width, height; + int i; + stripes_handle_t *stripes = NULL; + settings_t *settings = config_get_ptr(); + menu_handle_t *menu = (menu_handle_t*)calloc(1, sizeof(*menu)); + float scale_value = settings->uints.menu_xmb_scale_factor; + + /* scaling multiplier formulas made from these values: */ + /* stripes_scale 50 = {2.5, 2.5, 2, 1.7, 2.5, 4, 2.4, 2.5} */ + /* stripes_scale 75 = { 2, 1.6, 1.6, 1.4, 1.5, 2.3, 1.9, 1.3} */ + if (scale_value < 100) + { + /* text length & word wrap (base 35 apply to file browser, 1st column) */ + stripes_scale_mod[0] = -0.03 * scale_value + 4.083; + /* playlist text length when thumbnail is ON (small, base 40) */ + stripes_scale_mod[1] = -0.03 * scale_value + 3.95; + /* playlist text length when thumbnail is OFF (large, base 70) */ + stripes_scale_mod[2] = -0.02 * scale_value + 3.033; + /* sub-label length & word wrap */ + stripes_scale_mod[3] = -0.014 * scale_value + 2.416; + /* thumbnail size & vertical margin from top */ + stripes_scale_mod[4] = -0.03 * scale_value + 3.916; + /* thumbnail horizontal left margin (horizontal positioning) */ + stripes_scale_mod[5] = -0.06 * scale_value + 6.933; + /* margin before 2nd column start (shaders parameters, cheats...) */ + stripes_scale_mod[6] = -0.028 * scale_value + 3.866; + /* text length & word wrap (base 35 apply to 2nd column in cheats, shaders, etc) */ + stripes_scale_mod[7] = 134.179 * pow(scale_value, -1.0778); + + for (i = 0; i < 8; i++) + if (stripes_scale_mod[i] < 1) + stripes_scale_mod[i] = 1; + } + + if (!menu) + goto error; + + if (!menu_display_init_first_driver(video_is_threaded)) + goto error; + + video_driver_get_size(&width, &height); + + stripes = (stripes_handle_t*)calloc(1, sizeof(stripes_handle_t)); + + if (!stripes) + goto error; + + *userdata = stripes; + + stripes->selection_buf_old = (file_list_t*)calloc(1, sizeof(file_list_t)); + + if (!stripes->selection_buf_old) + goto error; + + stripes->categories_active_idx = 0; + stripes->categories_active_idx_old = 0; + stripes->x = 0; + stripes->categories_x_pos = 0; + stripes->textures_arrow_alpha = 0; + stripes->depth = 1; + stripes->old_depth = 1; + stripes->alpha = 0; + + stripes->system_tab_end = 0; + stripes->tabs[stripes->system_tab_end] = STRIPES_SYSTEM_TAB_MAIN; + if (settings->bools.menu_content_show_settings && !settings->bools.kiosk_mode_enable) + stripes->tabs[++stripes->system_tab_end] = STRIPES_SYSTEM_TAB_SETTINGS; + if (settings->bools.menu_content_show_favorites) + stripes->tabs[++stripes->system_tab_end] = STRIPES_SYSTEM_TAB_FAVORITES; + if (settings->bools.menu_content_show_history) + stripes->tabs[++stripes->system_tab_end] = STRIPES_SYSTEM_TAB_HISTORY; +#ifdef HAVE_IMAGEVIEWER + if (settings->bools.menu_content_show_images) + stripes->tabs[++stripes->system_tab_end] = STRIPES_SYSTEM_TAB_IMAGES; +#endif + if (settings->bools.menu_content_show_music) + stripes->tabs[++stripes->system_tab_end] = STRIPES_SYSTEM_TAB_MUSIC; +#ifdef HAVE_FFMPEG + if (settings->bools.menu_content_show_video) + stripes->tabs[++stripes->system_tab_end] = STRIPES_SYSTEM_TAB_VIDEO; +#endif +#ifdef HAVE_NETWORKING + if (settings->bools.menu_content_show_netplay) + stripes->tabs[++stripes->system_tab_end] = STRIPES_SYSTEM_TAB_NETPLAY; +#endif +#ifdef HAVE_LIBRETRODB + if (settings->bools.menu_content_show_add && !settings->bools.kiosk_mode_enable) + stripes->tabs[++stripes->system_tab_end] = STRIPES_SYSTEM_TAB_ADD; +#endif + + menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL); + + /* TODO/FIXME - we don't use framebuffer at all + * for XMB, we should refactor this dependency + * away. */ + + menu_display_set_width(width); + menu_display_set_height(height); + + menu_display_allocate_white_texture(); + + stripes->horizontal_list = (file_list_t*)calloc(1, sizeof(file_list_t)); + + if (stripes->horizontal_list) + stripes_init_horizontal_list(stripes); + + return menu; + +error: + if (menu) + free(menu); + + if (stripes) + { + if (stripes->selection_buf_old) + free(stripes->selection_buf_old); + stripes->selection_buf_old = NULL; + if (stripes->horizontal_list) + { + stripes_free_list_nodes(stripes->horizontal_list, false); + file_list_free(stripes->horizontal_list); + } + stripes->horizontal_list = NULL; + } + return NULL; +} + +static void stripes_free(void *data) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + + if (stripes) + { + if (stripes->selection_buf_old) + { + stripes_free_list_nodes(stripes->selection_buf_old, false); + file_list_free(stripes->selection_buf_old); + } + + if (stripes->horizontal_list) + { + stripes_free_list_nodes(stripes->horizontal_list, false); + file_list_free(stripes->horizontal_list); + } + + stripes->selection_buf_old = NULL; + stripes->horizontal_list = NULL; + + video_coord_array_free(&stripes->raster_block.carr); + video_coord_array_free(&stripes->raster_block2.carr); + + if (!string_is_empty(stripes->box_message)) + free(stripes->box_message); + if (!string_is_empty(stripes->thumbnail_system)) + free(stripes->thumbnail_system); + if (!string_is_empty(stripes->thumbnail_content)) + free(stripes->thumbnail_content); + if (!string_is_empty(stripes->savestate_thumbnail_file_path)) + free(stripes->savestate_thumbnail_file_path); + if (!string_is_empty(stripes->thumbnail_file_path)) + free(stripes->thumbnail_file_path); + if (!string_is_empty(stripes->left_thumbnail_file_path)) + free(stripes->left_thumbnail_file_path); + if (!string_is_empty(stripes->bg_file_path)) + free(stripes->bg_file_path); + } + + font_driver_bind_block(NULL, NULL); +} + +static void stripes_context_bg_destroy(stripes_handle_t *stripes) +{ + if (!stripes) + return; + video_driver_texture_unload(&stripes->textures.bg); + video_driver_texture_unload(&menu_display_white_texture); +} + +static bool stripes_load_image(void *userdata, void *data, enum menu_image_type type) +{ + stripes_handle_t *stripes = (stripes_handle_t*)userdata; + + if (!stripes || !data) + return false; + + switch (type) + { + case MENU_IMAGE_NONE: + break; + case MENU_IMAGE_WALLPAPER: + stripes_context_bg_destroy(stripes); + video_driver_texture_unload(&stripes->textures.bg); + video_driver_texture_load(data, + TEXTURE_FILTER_MIPMAP_LINEAR, + &stripes->textures.bg); + menu_display_allocate_white_texture(); + break; + case MENU_IMAGE_THUMBNAIL: + { + struct texture_image *img = (struct texture_image*)data; + stripes->thumbnail_height = stripes->thumbnail_width + * (float)img->height / (float)img->width; + video_driver_texture_unload(&stripes->thumbnail); + video_driver_texture_load(data, + TEXTURE_FILTER_MIPMAP_LINEAR, &stripes->thumbnail); + } + break; + case MENU_IMAGE_LEFT_THUMBNAIL: + { + struct texture_image *img = (struct texture_image*)data; + stripes->left_thumbnail_height = stripes->left_thumbnail_width + * (float)img->height / (float)img->width; + video_driver_texture_unload(&stripes->left_thumbnail); + video_driver_texture_load(data, + TEXTURE_FILTER_MIPMAP_LINEAR, &stripes->left_thumbnail); + } + break; + case MENU_IMAGE_SAVESTATE_THUMBNAIL: + { + struct texture_image *img = (struct texture_image*)data; + stripes->savestate_thumbnail_height = stripes->savestate_thumbnail_width + * (float)img->height / (float)img->width; + video_driver_texture_unload(&stripes->savestate_thumbnail); + video_driver_texture_load(data, + TEXTURE_FILTER_MIPMAP_LINEAR, &stripes->savestate_thumbnail); + } + break; + } + + return true; +} + +static const char *stripes_texture_path(unsigned id) +{ + switch (id) + { + case STRIPES_TEXTURE_MAIN_MENU: +#if defined(HAVE_LAKKA) + return "lakka.png"; +#else + return "retroarch.png"; +#endif + case STRIPES_TEXTURE_SETTINGS: + return "settings.png"; + case STRIPES_TEXTURE_HISTORY: + return "history.png"; + case STRIPES_TEXTURE_FAVORITES: + return "favorites.png"; + case STRIPES_TEXTURE_ADD_FAVORITE: + return "add-favorite.png"; + case STRIPES_TEXTURE_MUSICS: + return "musics.png"; +#ifdef HAVE_FFMPEG + case STRIPES_TEXTURE_MOVIES: + return "movies.png"; +#endif +#ifdef HAVE_IMAGEVIEWER + case STRIPES_TEXTURE_IMAGES: + return "images.png"; +#endif + case STRIPES_TEXTURE_SETTING: + return "setting.png"; + case STRIPES_TEXTURE_SUBSETTING: + return "subsetting.png"; + case STRIPES_TEXTURE_ARROW: + return "arrow.png"; + case STRIPES_TEXTURE_RUN: + return "run.png"; + case STRIPES_TEXTURE_CLOSE: + return "close.png"; + case STRIPES_TEXTURE_RESUME: + return "resume.png"; + case STRIPES_TEXTURE_CLOCK: + return "clock.png"; + case STRIPES_TEXTURE_BATTERY_FULL: + return "battery-full.png"; + case STRIPES_TEXTURE_BATTERY_CHARGING: + return "battery-charging.png"; + case STRIPES_TEXTURE_POINTER: + return "pointer.png"; + case STRIPES_TEXTURE_SAVESTATE: + return "savestate.png"; + case STRIPES_TEXTURE_LOADSTATE: + return "loadstate.png"; + case STRIPES_TEXTURE_UNDO: + return "undo.png"; + case STRIPES_TEXTURE_CORE_INFO: + return "core-infos.png"; + case STRIPES_TEXTURE_WIFI: + return "wifi.png"; + case STRIPES_TEXTURE_CORE_OPTIONS: + return "core-options.png"; + case STRIPES_TEXTURE_INPUT_REMAPPING_OPTIONS: + return "core-input-remapping-options.png"; + case STRIPES_TEXTURE_CHEAT_OPTIONS: + return "core-cheat-options.png"; + case STRIPES_TEXTURE_DISK_OPTIONS: + return "core-disk-options.png"; + case STRIPES_TEXTURE_SHADER_OPTIONS: + return "core-shader-options.png"; + case STRIPES_TEXTURE_ACHIEVEMENT_LIST: + return "achievement-list.png"; + case STRIPES_TEXTURE_SCREENSHOT: + return "screenshot.png"; + case STRIPES_TEXTURE_RELOAD: + return "reload.png"; + case STRIPES_TEXTURE_RENAME: + return "rename.png"; + case STRIPES_TEXTURE_FILE: + return "file.png"; + case STRIPES_TEXTURE_FOLDER: + return "folder.png"; + case STRIPES_TEXTURE_ZIP: + return "zip.png"; + case STRIPES_TEXTURE_MUSIC: + return "music.png"; + case STRIPES_TEXTURE_FAVORITE: + return "favorites-content.png"; + case STRIPES_TEXTURE_IMAGE: + return "image.png"; + case STRIPES_TEXTURE_MOVIE: + return "movie.png"; + case STRIPES_TEXTURE_CORE: + return "core.png"; + case STRIPES_TEXTURE_RDB: + return "database.png"; + case STRIPES_TEXTURE_CURSOR: + return "cursor.png"; + case STRIPES_TEXTURE_SWITCH_ON: + return "on.png"; + case STRIPES_TEXTURE_SWITCH_OFF: + return "off.png"; + case STRIPES_TEXTURE_ADD: + return "add.png"; +#ifdef HAVE_NETWORKING + case STRIPES_TEXTURE_NETPLAY: + return "netplay.png"; + case STRIPES_TEXTURE_ROOM: + return "room.png"; + /* stub these out until we have the icons + case STRIPES_TEXTURE_ROOM_LAN: + return "room_lan.png"; + case STRIPES_TEXTURE_ROOM_MITM: + return "room_mitm.png"; + */ +#endif + case STRIPES_TEXTURE_KEY: + return "key.png"; + case STRIPES_TEXTURE_KEY_HOVER: + return "key-hover.png"; + case STRIPES_TEXTURE_DIALOG_SLICE: + return "dialog-slice.png"; + + } + + return NULL; +} + +static void stripes_context_reset_textures( + stripes_handle_t *stripes, const char *iconpath) +{ + unsigned i; + + for (i = 0; i < STRIPES_TEXTURE_LAST; i++) + menu_display_reset_textures_list(stripes_texture_path(i), iconpath, &stripes->textures.list[i], TEXTURE_FILTER_MIPMAP_LINEAR); + + menu_display_allocate_white_texture(); + + stripes->main_menu_node.icon = stripes->textures.list[STRIPES_TEXTURE_MAIN_MENU]; + stripes->main_menu_node.alpha = stripes->categories_active_alpha; + stripes->main_menu_node.zoom = stripes->categories_active_zoom; + + stripes->settings_tab_node.icon = stripes->textures.list[STRIPES_TEXTURE_SETTINGS]; + stripes->settings_tab_node.alpha = stripes->categories_active_alpha; + stripes->settings_tab_node.zoom = stripes->categories_active_zoom; + + stripes->history_tab_node.icon = stripes->textures.list[STRIPES_TEXTURE_HISTORY]; + stripes->history_tab_node.alpha = stripes->categories_active_alpha; + stripes->history_tab_node.zoom = stripes->categories_active_zoom; + + stripes->favorites_tab_node.icon = stripes->textures.list[STRIPES_TEXTURE_FAVORITES]; + stripes->favorites_tab_node.alpha = stripes->categories_active_alpha; + stripes->favorites_tab_node.zoom = stripes->categories_active_zoom; + + stripes->music_tab_node.icon = stripes->textures.list[STRIPES_TEXTURE_MUSICS]; + stripes->music_tab_node.alpha = stripes->categories_active_alpha; + stripes->music_tab_node.zoom = stripes->categories_active_zoom; + +#ifdef HAVE_FFMPEG + stripes->video_tab_node.icon = stripes->textures.list[STRIPES_TEXTURE_MOVIES]; + stripes->video_tab_node.alpha = stripes->categories_active_alpha; + stripes->video_tab_node.zoom = stripes->categories_active_zoom; +#endif + +#ifdef HAVE_IMAGEVIEWER + stripes->images_tab_node.icon = stripes->textures.list[STRIPES_TEXTURE_IMAGES]; + stripes->images_tab_node.alpha = stripes->categories_active_alpha; + stripes->images_tab_node.zoom = stripes->categories_active_zoom; +#endif + + stripes->add_tab_node.icon = stripes->textures.list[STRIPES_TEXTURE_ADD]; + stripes->add_tab_node.alpha = stripes->categories_active_alpha; + stripes->add_tab_node.zoom = stripes->categories_active_zoom; + +#ifdef HAVE_NETWORKING + stripes->netplay_tab_node.icon = stripes->textures.list[STRIPES_TEXTURE_NETPLAY]; + stripes->netplay_tab_node.alpha = stripes->categories_active_alpha; + stripes->netplay_tab_node.zoom = stripes->categories_active_zoom; +#endif +} + +static void stripes_context_reset_background(const char *iconpath) +{ + char *path = NULL; + settings_t *settings = config_get_ptr(); + const char *path_menu_wp = settings->paths.path_menu_wallpaper; + + if (!string_is_empty(path_menu_wp)) + path = strdup(path_menu_wp); + else if (!string_is_empty(iconpath)) + { + path = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); + path[0] = '\0'; + + fill_pathname_join(path, iconpath, "bg.png", + PATH_MAX_LENGTH * sizeof(char)); + } + + if (filestream_exists(path)) + task_push_image_load(path, + menu_display_handle_wallpaper_upload, NULL); + + if (path) + free(path); +} + +static void stripes_context_reset(void *data, bool is_threaded) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + + if (stripes) + { + char bg_file_path[PATH_MAX_LENGTH] = {0}; + char *iconpath = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); + iconpath[0] = '\0'; + + fill_pathname_application_special(bg_file_path, + sizeof(bg_file_path), APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_BG); + + if (!string_is_empty(bg_file_path)) + { + if (!string_is_empty(stripes->bg_file_path)) + free(stripes->bg_file_path); + stripes->bg_file_path = strdup(bg_file_path); + } + + fill_pathname_application_special(iconpath, + PATH_MAX_LENGTH * sizeof(char), + APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_ICONS); + + stripes_layout(stripes); + stripes->font = menu_display_font(APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_FONT, + stripes->font_size, + is_threaded); + stripes->font2 = menu_display_font(APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_FONT, + stripes->font2_size, + is_threaded); + stripes_context_reset_textures(stripes, iconpath); + stripes_context_reset_background(iconpath); + stripes_context_reset_horizontal_list(stripes); + + if (!string_is_equal(stripes_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + stripes_update_thumbnail_image(stripes); + if (!string_is_equal(stripes_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + stripes_update_thumbnail_image(stripes); + stripes_update_savestate_thumbnail_image(stripes); + + free(iconpath); + } +} + +static void stripes_navigation_clear(void *data, bool pending_push) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + if (!pending_push) + stripes_selection_pointer_changed(stripes, true); +} + +static void stripes_navigation_pointer_changed(void *data) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + stripes_selection_pointer_changed(stripes, true); +} + +static void stripes_navigation_set(void *data, bool scroll) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + stripes_selection_pointer_changed(stripes, true); +} + +static void stripes_navigation_alphabet(void *data, size_t *unused) +{ + stripes_handle_t *stripes = (stripes_handle_t*)data; + stripes_selection_pointer_changed(stripes, true); +} + +static void stripes_list_insert(void *userdata, + file_list_t *list, + const char *path, + const char *fullpath, + const char *unused, + size_t list_size, + unsigned entry_type) +{ + int current = 0; + int i = (int)list_size; + stripes_node_t *node = NULL; + stripes_handle_t *stripes = (stripes_handle_t*)userdata; + size_t selection = menu_navigation_get_selection(); + + if (!stripes || !list) + return; + + node = (stripes_node_t*)file_list_get_userdata_at_offset(list, i); + + if (!node) + node = stripes_alloc_node(); + + if (!node) + { + RARCH_ERR("XMB node could not be allocated.\n"); + return; + } + + current = (int)selection; + + if (!string_is_empty(fullpath)) + { + if (node->fullpath) + free(node->fullpath); + + node->fullpath = strdup(fullpath); + } + + node->alpha = stripes->items_passive_alpha; + node->zoom = stripes->items_passive_zoom; + node->label_alpha = node->alpha; + node->y = stripes_item_y(stripes, i, current); + node->x = 0; + + if (i == current) + { + node->alpha = stripes->items_active_alpha; + node->label_alpha = stripes->items_active_alpha; + node->zoom = stripes->items_active_alpha; + } + + file_list_set_userdata(list, i, node); +} + +static void stripes_list_clear(file_list_t *list) +{ + menu_animation_ctx_tag tag = (uintptr_t)list; + + menu_animation_ctl(MENU_ANIMATION_CTL_KILL_BY_TAG, &tag); + + stripes_free_list_nodes(list, false); +} + +static void stripes_list_free(file_list_t *list, size_t a, size_t b) +{ + stripes_list_clear(list); +} + +static void stripes_list_deep_copy(const file_list_t *src, file_list_t *dst, + size_t first, size_t last) +{ + size_t i, j = 0; + menu_animation_ctx_tag tag = (uintptr_t)dst; + + menu_animation_ctl(MENU_ANIMATION_CTL_KILL_BY_TAG, &tag); + + /* use true here because file_list_copy() doesn't free actiondata */ + stripes_free_list_nodes(dst, true); + + file_list_clear(dst); + file_list_reserve(dst, (last + 1) - first); + + for (i = first; i <= last; ++i) + { + struct item_file *d = &dst->list[j]; + struct item_file *s = &src->list[i]; + + void *src_udata = s->userdata; + void *src_adata = s->actiondata; + + *d = *s; + d->alt = string_is_empty(d->alt) ? NULL : strdup(d->alt); + d->path = string_is_empty(d->path) ? NULL : strdup(d->path); + d->label = string_is_empty(d->label) ? NULL : strdup(d->label); + + if (src_udata) + file_list_set_userdata(dst, j, (void*)stripes_copy_node((const stripes_node_t*)src_udata)); + + if (src_adata) + { + void *data = malloc(sizeof(menu_file_list_cbs_t)); + memcpy(data, src_adata, sizeof(menu_file_list_cbs_t)); + file_list_set_actiondata(dst, j, data); + } + + ++j; + } + + dst->size = j; +} + +static void stripes_list_cache(void *data, enum menu_list_type type, unsigned action) +{ + size_t stack_size, list_size; + stripes_handle_t *stripes = (stripes_handle_t*)data; + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + size_t selection = menu_navigation_get_selection(); + settings_t *settings = config_get_ptr(); + + if (!stripes) + return; + + /* Check whether to enable the horizontal animation. */ + if (settings->bools.menu_horizontal_animation) + { + unsigned first = 0, last = 0; + unsigned height = 0; + video_driver_get_size(NULL, &height); + + /* FIXME: this shouldn't be happening at all */ + if (selection >= selection_buf->size) + selection = selection_buf->size ? selection_buf->size - 1 : 0; + + stripes->selection_ptr_old = selection; + + stripes_calculate_visible_range(stripes, height, selection_buf->size, + stripes->selection_ptr_old, &first, &last); + + stripes_list_deep_copy(selection_buf, stripes->selection_buf_old, first, last); + + stripes->selection_ptr_old -= first; + last -= first; + first = 0; + } + else + stripes->selection_ptr_old = 0; + + list_size = stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL) + + stripes->system_tab_end; + + switch (type) + { + case MENU_LIST_PLAIN: + break; + case MENU_LIST_HORIZONTAL: + stripes->categories_selection_ptr_old = stripes->categories_selection_ptr; + + switch (action) + { + case MENU_ACTION_LEFT: + if (stripes->categories_selection_ptr == 0) + { + stripes->categories_selection_ptr = list_size; + stripes->categories_active_idx = (unsigned)(list_size - 1); + } + else + stripes->categories_selection_ptr--; + break; + default: + if (stripes->categories_selection_ptr == list_size) + { + stripes->categories_selection_ptr = 0; + stripes->categories_active_idx = 1; + } + else + stripes->categories_selection_ptr++; + break; + } + + stack_size = menu_stack->size; + + if (menu_stack->list[stack_size - 1].label) + free(menu_stack->list[stack_size - 1].label); + menu_stack->list[stack_size - 1].label = NULL; + + switch (stripes_get_system_tab(stripes, (unsigned)stripes->categories_selection_ptr)) + { + case STRIPES_SYSTEM_TAB_MAIN: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU)); + menu_stack->list[stack_size - 1].type = + MENU_SETTINGS; + break; + case STRIPES_SYSTEM_TAB_SETTINGS: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS_TAB)); + menu_stack->list[stack_size - 1].type = + MENU_SETTINGS_TAB; + break; +#ifdef HAVE_IMAGEVIEWER + case STRIPES_SYSTEM_TAB_IMAGES: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB)); + menu_stack->list[stack_size - 1].type = + MENU_IMAGES_TAB; + break; +#endif + case STRIPES_SYSTEM_TAB_MUSIC: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB)); + menu_stack->list[stack_size - 1].type = + MENU_MUSIC_TAB; + break; +#ifdef HAVE_FFMPEG + case STRIPES_SYSTEM_TAB_VIDEO: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB)); + menu_stack->list[stack_size - 1].type = + MENU_VIDEO_TAB; + break; +#endif + case STRIPES_SYSTEM_TAB_HISTORY: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB)); + menu_stack->list[stack_size - 1].type = + MENU_HISTORY_TAB; + break; + case STRIPES_SYSTEM_TAB_FAVORITES: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES_TAB)); + menu_stack->list[stack_size - 1].type = + MENU_FAVORITES_TAB; + break; +#ifdef HAVE_NETWORKING + case STRIPES_SYSTEM_TAB_NETPLAY: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)); + menu_stack->list[stack_size - 1].type = + MENU_NETPLAY_TAB; + break; +#endif + case STRIPES_SYSTEM_TAB_ADD: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB)); + menu_stack->list[stack_size - 1].type = + MENU_ADD_TAB; + break; + default: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU)); + menu_stack->list[stack_size - 1].type = + MENU_SETTING_HORIZONTAL_MENU; + break; + } + break; + default: + break; + } +} + + +static void stripes_context_destroy(void *data) +{ + unsigned i; + stripes_handle_t *stripes = (stripes_handle_t*)data; + + if (!stripes) + return; + + for (i = 0; i < STRIPES_TEXTURE_LAST; i++) + video_driver_texture_unload(&stripes->textures.list[i]); + + video_driver_texture_unload(&stripes->thumbnail); + video_driver_texture_unload(&stripes->left_thumbnail); + video_driver_texture_unload(&stripes->savestate_thumbnail); + + stripes_context_destroy_horizontal_list(stripes); + stripes_context_bg_destroy(stripes); + + menu_display_font_free(stripes->font); + menu_display_font_free(stripes->font2); + + stripes->font = NULL; + stripes->font2 = NULL; +} + +static void stripes_toggle(void *userdata, bool menu_on) +{ + menu_animation_ctx_entry_t entry; + bool tmp = false; + stripes_handle_t *stripes = (stripes_handle_t*)userdata; + + if (!stripes) + return; + + stripes->depth = (int)stripes_list_get_size(stripes, MENU_LIST_PLAIN); + + if (!menu_on) + { + stripes->alpha = 0; + return; + } + + entry.duration = STRIPES_DELAY * 2; + entry.target_value = 1.0f; + entry.subject = &stripes->alpha; + entry.easing_enum = EASING_OUT_QUAD; + /* TODO/FIXME - integer conversion resulted in change of sign */ + entry.tag = -1; + entry.cb = NULL; + + menu_animation_push(&entry); + + tmp = !menu_entries_ctl(MENU_ENTRIES_CTL_NEEDS_REFRESH, NULL); + + if (tmp) + menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); + else + menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL); + + stripes_toggle_horizontal_list(stripes); +} + +static int stripes_deferred_push_content_actions(menu_displaylist_info_t *info) +{ + if (!menu_displaylist_ctl( + DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info)) + return -1; + menu_displaylist_process(info); + menu_displaylist_info_free(info); + return 0; +} + +static int stripes_list_bind_init_compare_label(menu_file_list_cbs_t *cbs) +{ + if (cbs && cbs->enum_idx != MSG_UNKNOWN) + { + switch (cbs->enum_idx) + { + case MENU_ENUM_LABEL_CONTENT_ACTIONS: + cbs->action_deferred_push = stripes_deferred_push_content_actions; + break; + default: + return -1; + } + } + + return 0; +} + +static int stripes_list_bind_init(menu_file_list_cbs_t *cbs, + const char *path, const char *label, unsigned type, size_t idx) +{ + if (stripes_list_bind_init_compare_label(cbs) == 0) + return 0; + + return -1; +} + +static int stripes_list_push(void *data, void *userdata, + menu_displaylist_info_t *info, unsigned type) +{ + menu_displaylist_ctx_parse_entry_t entry; + int ret = -1; + int i = 0; + core_info_list_t *list = NULL; + menu_handle_t *menu = (menu_handle_t*)data; + + switch (type) + { + case DISPLAYLIST_LOAD_CONTENT_LIST: + { + settings_t *settings = config_get_ptr(); + + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); + + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES), + msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES), + MENU_ENUM_LABEL_FAVORITES, + MENU_SETTING_ACTION, 0, 0); + + core_info_get_list(&list); + if (core_info_list_num_info_files(list)) + { + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DOWNLOADED_FILE_DETECT_CORE_LIST), + msg_hash_to_str(MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST), + MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST, + MENU_SETTING_ACTION, 0, 0); + } + +#ifdef HAVE_LIBRETRODB + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_COLLECTION_LIST), + msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST), + MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST, + MENU_SETTING_ACTION, 0, 0); +#endif + + if (frontend_driver_parse_drive_list(info->list, true) != 0) + menu_entries_append_enum(info->list, "/", + msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR), + MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR, + MENU_SETTING_ACTION, 0, 0); + + if (!settings->bools.kiosk_mode_enable) + { + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MENU_FILE_BROWSER_SETTINGS), + msg_hash_to_str(MENU_ENUM_LABEL_MENU_FILE_BROWSER_SETTINGS), + MENU_ENUM_LABEL_MENU_FILE_BROWSER_SETTINGS, + MENU_SETTING_ACTION, 0, 0); + } + + info->need_push = true; + info->need_refresh = true; + ret = 0; + } + break; + case DISPLAYLIST_MAIN_MENU: + { + settings_t *settings = config_get_ptr(); + rarch_system_info_t *system = runloop_get_system_info(); + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); + + entry.data = menu; + entry.info = info; + entry.parse_type = PARSE_ACTION; + entry.add_empty_entry = false; + + if (!string_is_empty(system->info.library_name) && + !string_is_equal(system->info.library_name, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE))) + { + entry.enum_idx = MENU_ENUM_LABEL_CONTENT_SETTINGS; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } + + if (system->load_no_content) + { + entry.enum_idx = MENU_ENUM_LABEL_START_CORE; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } + +#ifndef HAVE_DYNAMIC + if (frontend_driver_has_fork()) +#endif + { + if (settings->bools.menu_show_load_core) + { + entry.enum_idx = MENU_ENUM_LABEL_CORE_LIST; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } + } + + if (settings->bools.menu_show_load_content) + { + const struct retro_subsystem_info* subsystem = NULL; + + entry.enum_idx = MENU_ENUM_LABEL_LOAD_CONTENT_LIST; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + + subsystem = system->subsystem.data; + + if (subsystem) + { + for (i = 0; i < system->subsystem.size; i++, subsystem++) + { + char s[PATH_MAX_LENGTH]; + if (content_get_subsystem() == i) + { + if (content_get_subsystem_rom_id() < subsystem->num_roms) + { + snprintf(s, sizeof(s), + "Load %s %s", + subsystem->desc, + i == content_get_subsystem() + ? "\u2605" : " "); + menu_entries_append_enum(info->list, + s, + msg_hash_to_str(MENU_ENUM_LABEL_SUBSYSTEM_ADD), + MENU_ENUM_LABEL_SUBSYSTEM_ADD, + MENU_SETTINGS_SUBSYSTEM_ADD + i, 0, 0); + } + else + { + snprintf(s, sizeof(s), + "Start %s %s", + subsystem->desc, + i == content_get_subsystem() + ? "\u2605" : " "); + menu_entries_append_enum(info->list, + s, + msg_hash_to_str(MENU_ENUM_LABEL_SUBSYSTEM_LOAD), + MENU_ENUM_LABEL_SUBSYSTEM_LOAD, + MENU_SETTINGS_SUBSYSTEM_LOAD, 0, 0); + } + } + else + { + snprintf(s, sizeof(s), + "Load %s %s", + subsystem->desc, + i == content_get_subsystem() + ? "\u2605" : " "); + menu_entries_append_enum(info->list, + s, + msg_hash_to_str(MENU_ENUM_LABEL_SUBSYSTEM_ADD), + MENU_ENUM_LABEL_SUBSYSTEM_ADD, + MENU_SETTINGS_SUBSYSTEM_ADD + i, 0, 0); + } + } + } + } + + entry.enum_idx = MENU_ENUM_LABEL_ADD_CONTENT_LIST; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); +#if defined(HAVE_NETWORKING) + { + settings_t *settings = config_get_ptr(); + if (settings->bools.menu_show_online_updater && !settings->bools.kiosk_mode_enable) + { + entry.enum_idx = MENU_ENUM_LABEL_ONLINE_UPDATER; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } + } +#endif + if (!settings->bools.menu_content_show_settings && !string_is_empty(settings->paths.menu_content_show_settings_password)) + { + entry.enum_idx = MENU_ENUM_LABEL_XMB_MAIN_MENU_ENABLE_SETTINGS; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } + + if (settings->bools.kiosk_mode_enable && !string_is_empty(settings->paths.kiosk_mode_password)) + { + entry.enum_idx = MENU_ENUM_LABEL_MENU_DISABLE_KIOSK_MODE; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } + + if (settings->bools.menu_show_information) + { + entry.enum_idx = MENU_ENUM_LABEL_INFORMATION_LIST; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } + +#ifndef HAVE_DYNAMIC + entry.enum_idx = MENU_ENUM_LABEL_RESTART_RETROARCH; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); +#endif + + if (settings->bools.menu_show_configurations && !settings->bools.kiosk_mode_enable) + { + entry.enum_idx = MENU_ENUM_LABEL_CONFIGURATIONS_LIST; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } + + if (settings->bools.menu_show_help) + { + entry.enum_idx = MENU_ENUM_LABEL_HELP_LIST; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } + +#if !defined(IOS) + if (settings->bools.menu_show_quit_retroarch) + { + entry.enum_idx = MENU_ENUM_LABEL_QUIT_RETROARCH; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } +#endif + + if (settings->bools.menu_show_reboot) + { + entry.enum_idx = MENU_ENUM_LABEL_REBOOT; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + } + + entry.enum_idx = MENU_ENUM_LABEL_SHUTDOWN; + menu_displaylist_ctl(DISPLAYLIST_SETTING_ENUM, &entry); + info->need_push = true; + ret = 0; + } + break; + } + return ret; +} + +static bool stripes_menu_init_list(void *data) +{ + menu_displaylist_info_t info; + + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + + menu_displaylist_info_init(&info); + + info.label = strdup( + msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU)); + info.exts = + strdup(file_path_str(FILE_PATH_LPL_EXTENSION_NO_DOT)); + info.type_default = FILE_TYPE_PLAIN; + info.enum_idx = MENU_ENUM_LABEL_MAIN_MENU; + + menu_entries_append_enum(menu_stack, info.path, + info.label, + MENU_ENUM_LABEL_MAIN_MENU, + info.type, info.flags, 0); + + info.list = selection_buf; + + if (!menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info)) + goto error; + + info.need_push = true; + + if (!menu_displaylist_process(&info)) + goto error; + + menu_displaylist_info_free(&info); + return true; + +error: + menu_displaylist_info_free(&info); + return false; +} + +static int stripes_pointer_tap(void *userdata, + unsigned x, unsigned y, unsigned ptr, + menu_file_list_cbs_t *cbs, + menu_entry_t *entry, unsigned action) +{ + unsigned header_height = menu_display_get_header_height(); + + if (y < header_height) + { + size_t selection = menu_navigation_get_selection(); + return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL); + } + else if (ptr <= (menu_entries_get_size() - 1)) + { + size_t selection = menu_navigation_get_selection(); + if (ptr == selection && cbs && cbs->action_select) + return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT); + + menu_navigation_set_selection(ptr); + menu_driver_navigation_set(false); + } + + return 0; +} + +menu_ctx_driver_t menu_ctx_stripes = { + NULL, + stripes_messagebox, + generic_menu_iterate, + stripes_render, + stripes_frame, + stripes_init, + stripes_free, + stripes_context_reset, + stripes_context_destroy, + stripes_populate_entries, + stripes_toggle, + stripes_navigation_clear, + stripes_navigation_pointer_changed, + stripes_navigation_pointer_changed, + stripes_navigation_set, + stripes_navigation_pointer_changed, + stripes_navigation_alphabet, + stripes_navigation_alphabet, + stripes_menu_init_list, + stripes_list_insert, + NULL, + stripes_list_free, + stripes_list_clear, + stripes_list_cache, + stripes_list_push, + stripes_list_get_selection, + stripes_list_get_size, + stripes_list_get_entry, + NULL, + stripes_list_bind_init, + stripes_load_image, + "stripes", + stripes_environ, + stripes_pointer_tap, + stripes_update_thumbnail_path, + stripes_update_thumbnail_image, + stripes_set_thumbnail_system, + stripes_set_thumbnail_content, + stripes_osk_ptr_at_pos, + stripes_update_savestate_thumbnail_path, + stripes_update_savestate_thumbnail_image +}; diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index a648b39e3a..f0511dc936 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -4002,8 +4002,6 @@ static void xmb_init_ribbon(xmb_handle_t * xmb) free(ribbon_verts); } - - static void *xmb_init(void **userdata, bool video_is_threaded) { unsigned width, height; diff --git a/menu/drivers_display/menu_display_gdi.c b/menu/drivers_display/menu_display_gdi.c index e3f0c2160c..3cea3ee2e9 100644 --- a/menu/drivers_display/menu_display_gdi.c +++ b/menu/drivers_display/menu_display_gdi.c @@ -59,18 +59,6 @@ static void menu_display_gdi_viewport(menu_display_ctx_draw_t *draw, static void menu_display_gdi_restore_clear_color(void) { - /*HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); - RECT rect; - HWND hwnd = win32_get_window(); - HDC hdc = GetDC(hwnd); - - GetClientRect(hwnd, &rect); - - FillRect(hdc, &rect, brush); - - DeleteObject(brush); - - ReleaseDC(hwnd, hdc);*/ } static void menu_display_gdi_clear_color( diff --git a/menu/drivers_display/menu_display_metal.m b/menu/drivers_display/menu_display_metal.m index 9e92e9b744..7ff5a5bfa7 100644 --- a/menu/drivers_display/menu_display_metal.m +++ b/menu/drivers_display/menu_display_metal.m @@ -17,88 +17,115 @@ #include "../../gfx/video_driver.h" #import "../../gfx/common/metal_common.h" +#define GET_DRIVER(video_info) (video_info ? (__bridge MetalDriver *)video_info->userdata : NULL); + +static const float *menu_display_metal_get_default_vertices(void) +{ + return [MenuDisplay defaultVertices]; +} + +static const float *menu_display_metal_get_default_tex_coords(void) +{ + return [MenuDisplay defaultTexCoords]; +} + static void *menu_display_metal_get_default_mvp(video_frame_info_t *video_info) { - return NULL; + MetalDriver *md = GET_DRIVER(video_info); + if (!md) + return NULL; + + return (void *)&md.viewportMVP->projectionMatrix; } static void menu_display_metal_blend_begin(video_frame_info_t *video_info) { + MetalDriver *md = GET_DRIVER(video_info); + if (!md) + return; + + md.display.blend = YES; } static void menu_display_metal_blend_end(video_frame_info_t *video_info) { + MetalDriver *md = GET_DRIVER(video_info); + if (!md) + return; + + md.display.blend = NO; } static void menu_display_metal_draw(menu_display_ctx_draw_t *draw, - video_frame_info_t *video_info) + video_frame_info_t *video_info) { + MetalDriver *md = GET_DRIVER(video_info); + if (!md || !draw) + return; + + [md.display draw:draw video:video_info]; } -static void menu_display_metal_draw_pipeline( - menu_display_ctx_draw_t *draw, video_frame_info_t *video_info) +static void menu_display_metal_draw_pipeline(menu_display_ctx_draw_t *draw, video_frame_info_t *video_info) { + MetalDriver *md = GET_DRIVER(video_info); + if (!md || !draw) + return; + + [md.display drawPipeline:draw video:video_info]; } static void menu_display_metal_viewport(menu_display_ctx_draw_t *draw, - video_frame_info_t *video_info) + video_frame_info_t *video_info) { } static void menu_display_metal_restore_clear_color(void) { + // nothing to do } -static void menu_display_metal_clear_color( - menu_display_ctx_clearcolor_t *clearcolor, - video_frame_info_t *video_info) +static void menu_display_metal_clear_color(menu_display_ctx_clearcolor_t *clearcolor, + video_frame_info_t *video_info) { - (void)clearcolor; + MetalDriver *md = GET_DRIVER(video_info); + if (!md) + return; + + md.display.clearColor = MTLClearColorMake(clearcolor->r, clearcolor->g, clearcolor->b, clearcolor->a); } static bool menu_display_metal_font_init_first( - void **font_handle, void *video_data, - const char *font_path, float font_size, - bool is_threaded) + void **font_handle, void *video_data, + const char *font_path, float font_size, + bool is_threaded) { - font_data_t **handle = (font_data_t**)font_handle; + font_data_t **handle = (font_data_t **)font_handle; *handle = font_driver_init_first(video_data, font_path, font_size, true, is_threaded, FONT_DRIVER_RENDER_METAL_API); - + if (*handle) return true; - + return false; } -static const float *menu_display_metal_get_default_vertices(void) -{ - static float dummy[16] = {0.0f}; - return &dummy[0]; -} - -static const float *menu_display_metal_get_default_tex_coords(void) -{ - static float dummy[16] = {0.0f}; - return &dummy[0]; -} - menu_display_ctx_driver_t menu_display_ctx_metal = { - menu_display_metal_draw, - menu_display_metal_draw_pipeline, - menu_display_metal_viewport, - menu_display_metal_blend_begin, - menu_display_metal_blend_end, - menu_display_metal_restore_clear_color, - menu_display_metal_clear_color, - menu_display_metal_get_default_mvp, - menu_display_metal_get_default_vertices, - menu_display_metal_get_default_tex_coords, - menu_display_metal_font_init_first, - MENU_VIDEO_DRIVER_GENERIC, - "menu_display_metal", - false + .draw = menu_display_metal_draw, + .draw_pipeline = menu_display_metal_draw_pipeline, + .viewport = menu_display_metal_viewport, + .blend_begin = menu_display_metal_blend_begin, + .blend_end = menu_display_metal_blend_end, + .restore_clear_color = menu_display_metal_restore_clear_color, + .clear_color = menu_display_metal_clear_color, + .get_default_mvp = menu_display_metal_get_default_mvp, + .get_default_vertices = menu_display_metal_get_default_vertices, + .get_default_tex_coords = menu_display_metal_get_default_tex_coords, + .font_init_first = menu_display_metal_font_init_first, + .type = MENU_VIDEO_DRIVER_METAL, + .ident = "menu_display_metal", + .handles_transform = NO, }; diff --git a/menu/drivers_display/menu_display_sixel.c b/menu/drivers_display/menu_display_sixel.c new file mode 100644 index 0000000000..9bc894d4f9 --- /dev/null +++ b/menu/drivers_display/menu_display_sixel.c @@ -0,0 +1,106 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2018 - Brad Parker + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#include +#include + +#include "../../config.def.h" +#include "../../gfx/font_driver.h" +#include "../../gfx/video_driver.h" + +#include "../menu_driver.h" + +static void *menu_display_sixel_get_default_mvp(void) +{ + return NULL; +} + +static void menu_display_sixel_blend_begin(void) +{ +} + +static void menu_display_sixel_blend_end(void) +{ +} + +static void menu_display_sixel_draw(void *data) +{ + (void)data; +} + +static void menu_display_sixel_draw_pipeline(void *data) +{ + (void)data; +} + +static void menu_display_sixel_viewport(void *data) +{ + (void)data; +} + +static void menu_display_sixel_restore_clear_color(void) +{ +} + +static void menu_display_sixel_clear_color(menu_display_ctx_clearcolor_t *clearcolor) +{ + (void)clearcolor; + + menu_display_sixel_restore_clear_color(); +} + +static bool menu_display_sixel_font_init_first( + void **font_handle, void *video_data, + const char *font_path, float font_size, + bool is_threaded) +{ + font_data_t **handle = (font_data_t**)font_handle; + *handle = font_driver_init_first(video_data, + font_path, font_size, true, + is_threaded, + FONT_DRIVER_RENDER_SIXEL); + return *handle; +} + +static const float *menu_display_sixel_get_default_vertices(void) +{ + static float dummy[16] = {0.0f}; + return &dummy[0]; +} + +static const float *menu_display_sixel_get_default_tex_coords(void) +{ + static float dummy[16] = {0.0f}; + return &dummy[0]; +} + +menu_display_ctx_driver_t menu_display_ctx_sixel = { + menu_display_sixel_draw, + menu_display_sixel_draw_pipeline, + menu_display_sixel_viewport, + menu_display_sixel_blend_begin, + menu_display_sixel_blend_end, + menu_display_sixel_restore_clear_color, + menu_display_sixel_clear_color, + menu_display_sixel_get_default_mvp, + menu_display_sixel_get_default_vertices, + menu_display_sixel_get_default_tex_coords, + menu_display_sixel_font_init_first, + MENU_VIDEO_DRIVER_SIXEL, + "menu_display_sixel", +}; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 87100cde54..e942861a46 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -2091,6 +2091,9 @@ static int menu_displaylist_parse_settings_internal(void *data, case PARSE_ONLY_UINT: precond = ST_UINT; break; + case PARSE_ONLY_SIZE: + precond = ST_SIZE; + break; case PARSE_ONLY_BIND: precond = ST_BIND; break; @@ -2162,6 +2165,10 @@ static int menu_displaylist_parse_settings_internal(void *data, if (type == ST_UINT) break; goto loop; + case PARSE_ONLY_SIZE: + if (type == ST_SIZE) + break; + goto loop; case PARSE_ONLY_BIND: if (type == ST_BIND) break; @@ -2226,6 +2233,7 @@ loop: case PARSE_ONLY_BOOL: case PARSE_ONLY_INT: case PARSE_ONLY_UINT: + case PARSE_ONLY_SIZE: case PARSE_ONLY_STRING: case PARSE_ONLY_PATH: case PARSE_ONLY_DIR: @@ -2283,6 +2291,9 @@ static int menu_displaylist_parse_settings_internal_enum(void *data, case PARSE_ONLY_UINT: precond = ST_UINT; break; + case PARSE_ONLY_SIZE: + precond = ST_SIZE; + break; case PARSE_ONLY_BIND: precond = ST_BIND; break; @@ -2354,6 +2365,10 @@ static int menu_displaylist_parse_settings_internal_enum(void *data, if (type == ST_UINT) break; goto loop; + case PARSE_ONLY_SIZE: + if (type == ST_SIZE) + break; + goto loop; case PARSE_ONLY_BIND: if (type == ST_BIND) break; @@ -2419,6 +2434,7 @@ loop: case PARSE_ONLY_BOOL: case PARSE_ONLY_INT: case PARSE_ONLY_UINT: + case PARSE_ONLY_SIZE: case PARSE_ONLY_PATH: case PARSE_ONLY_DIR: case PARSE_ONLY_STRING: @@ -5106,6 +5122,12 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_REWIND_GRANULARITY, PARSE_ONLY_UINT, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_REWIND_BUFFER_SIZE, + PARSE_ONLY_SIZE, false); + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_REWIND_BUFFER_SIZE_STEP, + PARSE_ONLY_UINT, false); info->need_refresh = true; info->need_push = true; diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index b3c12b9804..4924a465da 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -45,7 +45,8 @@ enum menu_displaylist_parse_type PARSE_ONLY_STRING_OPTIONS = (1 << 11), PARSE_ONLY_HEX = (1 << 12), PARSE_ONLY_DIR = (1 << 13), - PARSE_SUB_GROUP = (1 << 14) + PARSE_SUB_GROUP = (1 << 14), + PARSE_ONLY_SIZE = (1 << 15) }; enum menu_displaylist_ctl_state diff --git a/menu/menu_driver.c b/menu/menu_driver.c index fdb7025b4a..a3033c9c36 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -85,6 +85,9 @@ static const menu_ctx_driver_t *menu_ctx_drivers[] = { #if defined(HAVE_XMB) &menu_ctx_xmb, #endif +#if defined(HAVE_STRIPES) + &menu_ctx_stripes, +#endif #if defined(HAVE_RGUI) &menu_ctx_rgui, #endif @@ -130,14 +133,17 @@ static menu_display_ctx_driver_t *menu_display_ctx_drivers[] = { #ifdef WIIU &menu_display_ctx_wiiu, #endif -#ifdef HAVE_CACA - &menu_display_ctx_caca, -#endif #if defined(_WIN32) && !defined(_XBOX) &menu_display_ctx_gdi, #endif #ifdef DJGPP &menu_display_ctx_vga, +#endif +#ifdef HAVE_SIXEL + &menu_display_ctx_sixel, +#endif +#ifdef HAVE_CACA + &menu_display_ctx_caca, #endif &menu_display_ctx_null, NULL, @@ -299,6 +305,10 @@ static bool menu_display_check_compatibility( if (string_is_equal(video_driver, "gx2")) return true; break; + case MENU_VIDEO_DRIVER_SIXEL: + if (string_is_equal(video_driver, "sixel")) + return true; + break; case MENU_VIDEO_DRIVER_CACA: if (string_is_equal(video_driver, "caca")) return true; @@ -376,40 +386,28 @@ void menu_display_font_free(font_data_t *font) /* Setup: Initializes the font associated * to the menu driver */ -static font_data_t *menu_display_font_main_init( - menu_display_ctx_font_t *font, - bool is_threaded) -{ - font_data_t *font_data = NULL; - - if (!font || !menu_disp) - return NULL; - - if (!menu_disp->font_init_first((void**)&font_data, - video_driver_get_ptr(false), - font->path, font->size, is_threaded)) - return NULL; - - return font_data; -} - font_data_t *menu_display_font( enum application_special_type type, float font_size, bool is_threaded) { - menu_display_ctx_font_t font_info; char fontpath[PATH_MAX_LENGTH]; + font_data_t *font_data = NULL; + + if (!menu_disp) + return NULL; fontpath[0] = '\0'; fill_pathname_application_special( fontpath, sizeof(fontpath), type); - font_info.path = fontpath; - font_info.size = font_size; + if (!menu_disp->font_init_first((void**)&font_data, + video_driver_get_ptr(false), + fontpath, font_size, is_threaded)) + return NULL; - return menu_display_font_main_init(&font_info, is_threaded); + return font_data; } /* Reset the menu's coordinate array vertices. @@ -748,6 +746,56 @@ void menu_display_draw_quad( menu_disp->blend_end(video_info); } +void menu_display_draw_polygon( + video_frame_info_t *video_info, + int x1, int y1, + int x2, int y2, + int x3, int y3, + int x4, int y4, + unsigned width, unsigned height, + float *color) +{ + menu_display_ctx_draw_t draw; + struct video_coords coords; + + float vertex[8]; + + vertex[0] = x1 / (float)width; + vertex[1] = y1 / (float)height; + vertex[2] = x2 / (float)width; + vertex[3] = y2 / (float)height; + vertex[4] = x3 / (float)width; + vertex[5] = y3 / (float)height; + vertex[6] = x4 / (float)width; + vertex[7] = y4 / (float)height; + + coords.vertices = 4; + coords.vertex = &vertex[0]; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + coords.color = color; + + if (menu_disp && menu_disp->blend_begin) + menu_disp->blend_begin(video_info); + + draw.x = 0; + draw.y = 0; + draw.width = width; + draw.height = height; + draw.coords = &coords; + draw.matrix_data = NULL; + draw.texture = menu_display_white_texture; + draw.prim_type = MENU_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + draw.scale_factor = 1.0f; + draw.rotation = 0.0f; + + menu_display_draw(&draw, video_info); + + if (menu_disp && menu_disp->blend_end) + menu_disp->blend_end(video_info); +} + void menu_display_draw_texture( video_frame_info_t *video_info, int x, int y, unsigned w, unsigned h, diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 205197328f..ac0fe68e7a 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -315,6 +315,7 @@ enum menu_display_driver_type MENU_VIDEO_DRIVER_CTR, MENU_VIDEO_DRIVER_WIIU, MENU_VIDEO_DRIVER_CACA, + MENU_VIDEO_DRIVER_SIXEL, MENU_VIDEO_DRIVER_GDI, MENU_VIDEO_DRIVER_VGA }; @@ -454,12 +455,6 @@ typedef struct menu_display_ctx_datetime unsigned time_mode; } menu_display_ctx_datetime_t; -typedef struct menu_display_ctx_font -{ - const char *path; - float size; -} menu_display_ctx_font_t; - typedef struct menu_ctx_driver { /* Set a framebuffer texture. This is used for instance by RGUI. */ @@ -731,6 +726,14 @@ void menu_display_draw_quad( int x, int y, unsigned w, unsigned h, unsigned width, unsigned height, float *color); +void menu_display_draw_polygon( + video_frame_info_t *video_info, + int x1, int y1, + int x2, int y2, + int x3, int y3, + int x4, int y4, + unsigned width, unsigned height, + float *color); void menu_display_draw_texture( video_frame_info_t *video_info, int x, int y, unsigned w, unsigned h, @@ -814,6 +817,7 @@ extern menu_display_ctx_driver_t menu_display_ctx_wiiu; extern menu_display_ctx_driver_t menu_display_ctx_caca; extern menu_display_ctx_driver_t menu_display_ctx_gdi; extern menu_display_ctx_driver_t menu_display_ctx_vga; +extern menu_display_ctx_driver_t menu_display_ctx_sixel; extern menu_display_ctx_driver_t menu_display_ctx_null; extern menu_ctx_driver_t menu_ctx_xui; @@ -821,6 +825,7 @@ extern menu_ctx_driver_t menu_ctx_rgui; extern menu_ctx_driver_t menu_ctx_mui; extern menu_ctx_driver_t menu_ctx_nuklear; extern menu_ctx_driver_t menu_ctx_xmb; +extern menu_ctx_driver_t menu_ctx_stripes; extern menu_ctx_driver_t menu_ctx_zarch; extern menu_ctx_driver_t menu_ctx_null; diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 300d9a508b..e273fbe65d 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -775,6 +775,7 @@ int menu_action_handle_setting(rarch_setting_t *setting, case ST_BOOL: case ST_INT: case ST_UINT: + case ST_SIZE: case ST_HEX: case ST_FLOAT: case ST_STRING: @@ -931,6 +932,8 @@ void *setting_get_ptr(rarch_setting_t *setting) return setting->value.target.integer; case ST_UINT: return setting->value.target.unsigned_integer; + case ST_SIZE: + return setting->value.target.sizet; case ST_FLOAT: return setting->value.target.fraction; case ST_BIND: @@ -1636,6 +1639,14 @@ void general_write_handler(void *data) case MENU_ENUM_LABEL_SUSTAINED_PERFORMANCE_MODE: frontend_driver_set_sustained_performance_mode(settings->bools.sustained_performance_mode); break; + case MENU_ENUM_LABEL_REWIND_BUFFER_SIZE_STEP: + { + rarch_setting_t *buffer_size_setting = menu_setting_find("rewind_buffer_size"); + if ( buffer_size_setting ) { + buffer_size_setting->step = (*setting->value.target.unsigned_integer)*1024*1024 ; + } + } + break; default: break; } @@ -3083,18 +3094,6 @@ static bool setting_append_list( SD_FLAG_CMD_APPLY_AUTO); menu_settings_list_current_add_cmd(list, list_info, CMD_EVENT_REWIND_TOGGLE); -#if 0 - CONFIG_SIZE( - settings->rewind_buffer_size, - "rewind_buffer_size", - "Rewind Buffer Size", - rewind_buffer_size, - group_info, - subgroup_info, - parent_group, - general_write_handler, - general_read_handler) -#endif CONFIG_UINT( list, list_info, &settings->uints.rewind_granularity, @@ -3107,6 +3106,31 @@ static bool setting_append_list( general_write_handler, general_read_handler); menu_settings_list_current_add_range(list, list_info, 1, 32768, 1, true, true); + CONFIG_SIZE( + list, list_info, + &settings->sizes.rewind_buffer_size, + MENU_ENUM_LABEL_REWIND_BUFFER_SIZE, + MENU_ENUM_LABEL_VALUE_REWIND_BUFFER_SIZE, + rewind_buffer_size, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + &setting_get_string_representation_size_in_mb); + menu_settings_list_current_add_range(list, list_info, 1024*1024, 1024*1024*1024, settings->uints.rewind_buffer_size_step*1024*1024, true, true); + CONFIG_UINT( + list, list_info, + &settings->uints.rewind_buffer_size_step, + MENU_ENUM_LABEL_REWIND_BUFFER_SIZE_STEP, + MENU_ENUM_LABEL_VALUE_REWIND_BUFFER_SIZE_STEP, + rewind_buffer_size_step, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + menu_settings_list_current_add_range(list, list_info, 1, 100, 1, true, true); END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); diff --git a/menu/widgets/menu_entry.c b/menu/widgets/menu_entry.c index ea5be0c4cc..a5e83ab32f 100644 --- a/menu/widgets/menu_entry.c +++ b/menu/widgets/menu_entry.c @@ -54,6 +54,8 @@ enum menu_entry_type menu_entry_get_type(uint32_t i) return MENU_ENTRY_INT; case ST_UINT: return MENU_ENTRY_UINT; + case ST_SIZE: + return MENU_ENTRY_SIZE; case ST_FLOAT: return MENU_ENTRY_FLOAT; case ST_PATH: diff --git a/menu/widgets/menu_entry.h b/menu/widgets/menu_entry.h index b32ec148ee..f3ebb6b37b 100644 --- a/menu/widgets/menu_entry.h +++ b/menu/widgets/menu_entry.h @@ -40,7 +40,8 @@ enum menu_entry_type MENU_ENTRY_STRING, MENU_ENTRY_HEX, MENU_ENTRY_BIND, - MENU_ENTRY_ENUM + MENU_ENTRY_ENUM, + MENU_ENTRY_SIZE }; typedef struct menu_entry diff --git a/msg_hash.h b/msg_hash.h index 2188f93b6f..24a687cd2b 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1193,6 +1193,8 @@ enum msg_hash_enums MENU_LABEL(SCREENSHOT), MENU_LABEL(REWIND), MENU_LABEL(REWIND_GRANULARITY), + MENU_LABEL(REWIND_BUFFER_SIZE), + MENU_LABEL(REWIND_BUFFER_SIZE_STEP), MENU_LABEL(INPUT_META_REWIND), MENU_LABEL(SCREEN_RESOLUTION), diff --git a/pkg/android/phoenix/jni/Android.mk b/pkg/android/phoenix/jni/Android.mk index f3929445ba..a44f14fa8b 100644 --- a/pkg/android/phoenix/jni/Android.mk +++ b/pkg/android/phoenix/jni/Android.mk @@ -29,6 +29,10 @@ ifeq ($(TARGET_ARCH),x86) DEFINES += -DANDROID_X86 -DHAVE_SSSE3 endif +ifeq ($(TARGET_ARCH),x86_64) + DEFINES += -DANDROID_X64 +endif + ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) ifeq ($(HAVE_NEON),1) diff --git a/pkg/apple/BaseConfig.xcconfig b/pkg/apple/BaseConfig.xcconfig index 16ade1b7e1..1969463c8a 100644 --- a/pkg/apple/BaseConfig.xcconfig +++ b/pkg/apple/BaseConfig.xcconfig @@ -16,7 +16,7 @@ LIBRARY_SEARCH_PATHS[sdk=macosx*] = $(inherited) $(VULKAN_FRAMEWORK_PATH) // OTHER_LDFLAGS = $(inherited) -lMoltenVK -framework MoltenVK -OTHER_CFLAGS = $(inherited) -DHAVE_RUNAHEAD -DHAVE_GRIFFIN -DHAVE_FLAC -DHAVE_DR_FLAC -DHAVE_DR_MP3 -DHAVE_LROUND -DFLAC__HAS_OGG=0 -DHAVE_CHD -DHAVE_STB_VORBIS -DHAVE_MINIUPNPC -DHAVE_BUILTINMINIUPNPC -DHAVE_UPDATE_ASSETS -DHAVE_LANGEXTRA -DHAVE_CHEEVOS -DHAVE_IMAGEVIEWER -DHAVE_IOHIDMANAGER -DHAVE_CORETEXT -DHAVE_RGUI -DHAVE_MENU -DOSX -DHAVE_OPENGL -DHAVE_CC_RESAMPLER -DHAVE_GLSL -DINLINE=inline -D__LIBRETRO__ -DHAVE_COREAUDIO -DHAVE_DYNAMIC -DHAVE_OVERLAY -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_COCOA -DHAVE_MAIN -DHAVE_NETWORKGAMEPAD -DHAVE_NETWORKING -DRARCH_INTERNAL -DHAVE_THREADS -DHAVE_DYLIB -DHAVE_7ZIP -DHAVE_MATERIALUI -DHAVE_HID -DHAVE_XMB -DHAVE_SEGA -DHAVE_SHADERPIPELINE -DHAVE_MMAP -DHAVE_LIBRETRODB -DHAVE_GETOPT_LONG -DHAVE_METAL -DHAVE_SLANG -DHAVE_GLSLANG -DHAVE_SPIRV_CROSS -DWANT_GLSLANG -DENABLE_HLSL -DGLSLANG_OSINCLUDE_UNIX +OTHER_CFLAGS = $(inherited) -DHAVE_RUNAHEAD -DHAVE_GRIFFIN -DHAVE_FLAC -DHAVE_DR_FLAC -DHAVE_DR_MP3 -DHAVE_LROUND -DFLAC__HAS_OGG=0 -DHAVE_CHD -DHAVE_STB_VORBIS -DHAVE_MINIUPNPC -DHAVE_BUILTINMINIUPNPC -DHAVE_UPDATE_ASSETS -DHAVE_LANGEXTRA -DHAVE_CHEEVOS -DHAVE_IMAGEVIEWER -DHAVE_IOHIDMANAGER -DHAVE_CORETEXT -DHAVE_RGUI -DHAVE_MENU -DOSX -DHAVE_OPENGL -DHAVE_CC_RESAMPLER -DHAVE_GLSL -DINLINE=inline -D__LIBRETRO__ -DHAVE_COREAUDIO -DHAVE_DYNAMIC -DHAVE_OVERLAY -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_COCOA -DHAVE_MAIN -DHAVE_NETWORKGAMEPAD -DHAVE_NETWORKING -DRARCH_INTERNAL -DHAVE_THREADS -DHAVE_DYLIB -DHAVE_7ZIP -DHAVE_MATERIALUI -DHAVE_HID -DHAVE_XMB -DHAVE_SEGA -DHAVE_SHADERPIPELINE -DHAVE_MMAP -DHAVE_LIBRETRODB -DHAVE_GETOPT_LONG -DHAVE_METAL -DHAVE_SLANG -DHAVE_GLSLANG -DHAVE_SPIRV_CROSS -DWANT_GLSLANG -DENABLE_HLSL -DGLSLANG_OSINCLUDE_UNIX -DMETAL_DEBUG SRCBASE = $(SRCROOT)/../.. DEPS_DIR = $(SRCBASE)/deps diff --git a/pkg/apple/IntelliJ.xml b/pkg/apple/Project.xml similarity index 92% rename from pkg/apple/IntelliJ.xml rename to pkg/apple/Project.xml index 60c16db2de..54b041b724 100644 --- a/pkg/apple/IntelliJ.xml +++ b/pkg/apple/Project.xml @@ -4,6 +4,7 @@