From 1614519874dc697fef1daf69acd380641775f83d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 09:55:16 +0700 Subject: [PATCH 001/106] Clone xmb to stripe menu --- .vscode/c_cpp_properties.json | 251 +- Makefile.common | 11 + configuration.c | 5 + file_path_special.c | 4 + griffin/griffin.c | 4 + menu/drivers/stripes.c | 5205 +++++++++++++++++ menu/menu_driver.c | 3 + menu/menu_driver.h | 1 + pkg/apple/RetroArch.xcodeproj/project.pbxproj | 4 +- qb/config.libs.sh | 2 + qb/config.params.sh | 1 + 11 files changed, 5365 insertions(+), 126 deletions(-) create mode 100755 menu/drivers/stripes.c diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 81c2529184..c6f4ffd0eb 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,125 +1,128 @@ -{ - "configurations": [ - { - "name": "Mac", - "includePath": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - }, - "macFrameworkPath": [ - "/System/Library/Frameworks", - "/Library/Frameworks" - ] - }, - { - "name": "Linux", - "includePath": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "Win32", - "includePath": [ - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", - "${workspaceRoot}" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "msys2-mingw32", - "includePath": [ - "C:/msys64/mingw32/include", - "C:/msys64/mingw32/i686-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/msys64/mingw32/include", - "C:/msys64/mingw32/i686-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "msys2-mingw64", - "includePath": [ - "C:/msys64/mingw64/include", - "C:/msys64/mingw64/x86_64-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/msys64/mingw64/include", - "C:/msys64/mingw64/x86_64-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - } - ], - "version": 3 +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "macFrameworkPath": [ + "/System/Library/Frameworks", + "/Library/Frameworks" + ], + "compilerPath": "/usr/bin/clang", + "cStandard": "c11", + "cppStandard": "c++17" + }, + { + "name": "Linux", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "Win32", + "includePath": [ + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", + "${workspaceRoot}" + ], + "defines": [ + "_DEBUG", + "UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "msys2-mingw32", + "includePath": [ + "C:/msys64/mingw32/include", + "C:/msys64/mingw32/i686-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "defines": [ + "_DEBUG", + "UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/msys64/mingw32/include", + "C:/msys64/mingw32/i686-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "msys2-mingw64", + "includePath": [ + "C:/msys64/mingw64/include", + "C:/msys64/mingw64/x86_64-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "defines": [ + "_DEBUG", + "UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/msys64/mingw64/include", + "C:/msys64/mingw64/x86_64-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + } + ], + "version": 3 } \ No newline at end of file diff --git a/Makefile.common b/Makefile.common index 972d61e34e..7163289e68 100644 --- a/Makefile.common +++ b/Makefile.common @@ -724,11 +724,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) @@ -760,6 +765,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 diff --git a/configuration.c b/configuration.c index 9f0eed8a04..f3a2f5b6f3 100644 --- a/configuration.c +++ b/configuration.c @@ -271,6 +271,7 @@ enum menu_driver_enum MENU_XUI, MENU_MATERIALUI, MENU_XMB, + MENU_STRIPES, MENU_NUKLEAR, MENU_NULL }; @@ -502,6 +503,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_XMB; #elif defined(HAVE_XMB) && !defined(_XBOX) static enum menu_driver_enum MENU_DEFAULT_DRIVER = MENU_XMB; #elif defined(HAVE_RGUI) @@ -995,6 +998,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: diff --git a/file_path_special.c b/file_path_special.c index e58d10032f..2f1a002567 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/griffin/griffin.c b/griffin/griffin.c index bb0d9121f6..e79c991044 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1220,6 +1220,10 @@ MENU #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/menu/drivers/stripes.c b/menu/drivers/stripes.c new file mode 100755 index 0000000000..520cf87812 --- /dev/null +++ b/menu/drivers/stripes.c @@ -0,0 +1,5205 @@ +/* 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; + 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_passive_alpha; + float categories_passive_zoom; + float categories_active_zoom; + float categories_active_alpha; + + 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 +}; + + +float stripes_stripes_gradient_dark_purple[16] = { + 20/255.0, 13/255.0, 20/255.0, 1.0, + 20/255.0, 13/255.0, 20/255.0, 1.0, + 92/255.0, 44/255.0, 92/255.0, 1.0, + 148/255.0, 90/255.0, 148/255.0, 1.0, +}; + +float stripes_gradient_midnight_blue[16] = { + 44/255.0, 62/255.0, 80/255.0, 1.0, + 44/255.0, 62/255.0, 80/255.0, 1.0, + 44/255.0, 62/255.0, 80/255.0, 1.0, + 44/255.0, 62/255.0, 80/255.0, 1.0, +}; + +float stripes_gradient_golden[16] = { + 174/255.0, 123/255.0, 44/255.0, 1.0, + 205/255.0, 174/255.0, 84/255.0, 1.0, + 58/255.0, 43/255.0, 24/255.0, 1.0, + 58/255.0, 43/255.0, 24/255.0, 1.0, +}; + +float stripes_gradient_legacy_red[16] = { + 171/255.0, 70/255.0, 59/255.0, 1.0, + 171/255.0, 70/255.0, 59/255.0, 1.0, + 190/255.0, 80/255.0, 69/255.0, 1.0, + 190/255.0, 80/255.0, 69/255.0, 1.0, +}; + +float stripes_gradient_electric_blue[16] = { + 1/255.0, 2/255.0, 67/255.0, 1.0, + 1/255.0, 73/255.0, 183/255.0, 1.0, + 1/255.0, 93/255.0, 194/255.0, 1.0, + 3/255.0, 162/255.0, 254/255.0, 1.0, +}; + +float stripes_gradient_apple_green[16] = { + 102/255.0, 134/255.0, 58/255.0, 1.0, + 122/255.0, 131/255.0, 52/255.0, 1.0, + 82/255.0, 101/255.0, 35/255.0, 1.0, + 63/255.0, 95/255.0, 30/255.0, 1.0, +}; + +float stripes_gradient_undersea[16] = { + 23/255.0, 18/255.0, 41/255.0, 1.0, + 30/255.0, 72/255.0, 114/255.0, 1.0, + 52/255.0, 88/255.0, 110/255.0, 1.0, + 69/255.0, 125/255.0, 140/255.0, 1.0, + +}; + +float stripes_gradient_volcanic_red[16] = { + 1.0, 0.0, 0.1, 1.00, + 1.0, 0.1, 0.0, 1.00, + 0.1, 0.0, 0.1, 1.00, + 0.1, 0.0, 0.1, 1.00, +}; + +float stripes_gradient_dark[16] = { + 0.1, 0.1, 0.1, 1.00, + 0.1, 0.1, 0.1, 1.00, + 0.0, 0.0, 0.0, 1.00, + 0.0, 0.0, 0.0, 1.00, +}; + +float stripes_gradient_light[16] = { + 1.0, 1.0, 1.0, 1.00, + 1.0, 1.0, 1.0, 1.00, + 1.0, 1.0, 1.0, 1.00, + 1.0, 1.0, 1.0, 1.00, +}; + +float stripes_gradient_morning_blue[16] = { + 221/255.0, 241/255.0, 254/255.0, 1.00, + 135/255.0, 206/255.0, 250/255.0, 1.00, + 1.0, 1.0, 1.0, 1.00, + 170/255.0, 200/255.0, 252/255.0, 1.00, +}; + +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 float *stripes_gradient_ident(video_frame_info_t *video_info) +{ + switch (video_info->xmb_color_theme) + { + case XMB_THEME_DARK_PURPLE: + return &stripes_stripes_gradient_dark_purple[0]; + case XMB_THEME_MIDNIGHT_BLUE: + return &stripes_gradient_midnight_blue[0]; + case XMB_THEME_GOLDEN: + return &stripes_gradient_golden[0]; + case XMB_THEME_ELECTRIC_BLUE: + return &stripes_gradient_electric_blue[0]; + case XMB_THEME_APPLE_GREEN: + return &stripes_gradient_apple_green[0]; + case XMB_THEME_UNDERSEA: + return &stripes_gradient_undersea[0]; + case XMB_THEME_VOLCANIC_RED: + return &stripes_gradient_volcanic_red[0]; + case XMB_THEME_DARK: + return &stripes_gradient_dark[0]; + case XMB_THEME_LIGHT: + return &stripes_gradient_light[0]; + case XMB_THEME_MORNING_BLUE: + return &stripes_gradient_morning_blue[0]; + case XMB_THEME_LEGACY_RED: + default: + break; + } + + return &stripes_gradient_legacy_red[0]; +} + +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_thumbnail( + video_frame_info_t *video_info, + stripes_handle_t *stripes, float *color, + unsigned width, unsigned height, + float x, float y, + float w, float h, uintptr_t texture) +{ + menu_display_ctx_rotate_draw_t rotate_draw; + menu_display_ctx_draw_t draw; + struct video_coords coords; + math_matrix_4x4 mymat; + + 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); + + coords.vertices = 4; + coords.vertex = NULL; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + + draw.width = w; + draw.height = h; + 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 + stripes->shadow_offset; + draw.y = height - y - stripes->shadow_offset; + + menu_display_draw(&draw, video_info); + } + + coords.color = (const float*)color; + draw.x = x; + draw.y = height - y; + + menu_display_set_alpha(color, 1.0f); + + 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, + const 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; + } + + menu_driver_ctl(RARCH_MENU_CTL_PLAYLIST_GET, &playlist); + + 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 (i == selection) + { + unsigned depth = (unsigned)stripes_list_get_size(stripes, MENU_LIST_PLAIN); + unsigned stripes_system_tab = stripes_get_system_tab(stripes, (unsigned)stripes->categories_selection_ptr); + unsigned entry_type = menu_entry_get_type_new(&entry); + + ia = stripes->items_active_alpha; + iz = stripes->items_active_zoom; + if (!string_is_equal(thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) || !string_is_equal(lft_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + if ((stripes_system_tab > STRIPES_SYSTEM_TAB_SETTINGS && depth == 1) || + (stripes_system_tab < STRIPES_SYSTEM_TAB_SETTINGS && depth == 4)) + { + if (!string_is_empty(entry.path)) + stripes_set_thumbnail_content(stripes, entry.path, 0 /* will be ignored */); + if (!string_is_equal(thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + stripes_update_thumbnail_path(stripes, i, 'R'); + stripes_update_thumbnail_image(stripes); + } + if (!string_is_equal(lft_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + stripes_update_thumbnail_path(stripes, i, 'L'); + stripes_update_thumbnail_image(stripes); + } + } + else if (((entry_type == FILE_TYPE_IMAGE || entry_type == FILE_TYPE_IMAGEVIEWER || + entry_type == FILE_TYPE_RDB || entry_type == FILE_TYPE_RDB_ENTRY) + && stripes_system_tab <= STRIPES_SYSTEM_TAB_SETTINGS)) + { + if (!string_is_empty(entry.path)) + stripes_set_thumbnail_content(stripes, entry.path, 0 /* will be ignored */); + if (!string_is_equal(thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + stripes_update_thumbnail_path(stripes, i, 'R'); + stripes_update_thumbnail_image(stripes); + } + else if (!string_is_equal(lft_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + stripes_update_thumbnail_path(stripes, i, 'L'); + stripes_update_thumbnail_image(stripes); + } + } + else if (filebrowser_get_type() != FILEBROWSER_NONE) + { + stripes_reset_thumbnail_content(stripes); + if (!string_is_equal(thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + stripes_update_thumbnail_path(stripes, i, 'R'); + stripes_update_thumbnail_image(stripes); + } + else if (!string_is_equal(lft_thumb_ident, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + stripes_update_thumbnail_path(stripes, i, 'L'); + stripes_update_thumbnail_image(stripes); + } + } + } + stripes_update_savestate_thumbnail_path(stripes, i); + stripes_update_savestate_thumbnail_image(stripes); + } + + 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; + 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; + } + + 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); + } +} + +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->icon_spacing_horizontal + * -(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->icon_spacing_horizontal * + -(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, + float alpha, + uintptr_t texture_id, + float *stripes_coord_black, + float *stripes_coord_white) +{ + menu_display_ctx_draw_t draw; + + bool running = video_info->libretro_running; + + draw.x = 0; + draw.y = 0; + draw.texture = texture_id; + draw.width = width; + draw.height = height; + draw.color = &stripes_coord_black[0]; + draw.vertex = NULL; + draw.tex_coord = NULL; + draw.vertex_count = 4; + draw.prim_type = MENU_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + draw.pipeline.active = stripes_shader_pipeline_active(video_info); + + menu_display_blend_begin(video_info); + menu_display_set_viewport(video_info->width, video_info->height); + +#ifdef HAVE_SHADERPIPELINE + if (video_info->menu_shader_pipeline > XMB_SHADER_PIPELINE_WALLPAPER + && + (video_info->xmb_color_theme != XMB_THEME_WALLPAPER)) + { + draw.color = stripes_gradient_ident(video_info); + + if (running) + menu_display_set_alpha(draw.color, stripes_coord_black[3]); + else + menu_display_set_alpha(draw.color, stripes_coord_white[3]); + + menu_display_draw_gradient(&draw, video_info); + + draw.pipeline.id = VIDEO_SHADER_MENU_2; + + switch (video_info->menu_shader_pipeline) + { + case XMB_SHADER_PIPELINE_RIBBON: + draw.pipeline.id = VIDEO_SHADER_MENU; + break; + case XMB_SHADER_PIPELINE_SIMPLE_SNOW: + draw.pipeline.id = VIDEO_SHADER_MENU_3; + break; + case XMB_SHADER_PIPELINE_SNOW: + draw.pipeline.id = VIDEO_SHADER_MENU_4; + break; + case XMB_SHADER_PIPELINE_BOKEH: + draw.pipeline.id = VIDEO_SHADER_MENU_5; + break; + case XMB_SHADER_PIPELINE_SNOWFLAKE: + draw.pipeline.id = VIDEO_SHADER_MENU_6; + break; + default: + break; + } + + menu_display_draw_pipeline(&draw, video_info); + } + else +#endif + { + uintptr_t texture = draw.texture; + + if (video_info->xmb_color_theme != XMB_THEME_WALLPAPER) + draw.color = stripes_gradient_ident(video_info); + + if (running) + menu_display_set_alpha(draw.color, stripes_coord_black[3]); + else + menu_display_set_alpha(draw.color, stripes_coord_white[3]); + + if (video_info->xmb_color_theme != XMB_THEME_WALLPAPER) + menu_display_draw_gradient(&draw, video_info); + + { + float override_opacity = video_info->menu_wallpaper_opacity; + bool add_opacity = false; + + draw.texture = texture; + menu_display_set_alpha(draw.color, stripes_coord_white[3]); + + if (draw.texture) + draw.color = &stripes_coord_white[0]; + + if (running || video_info->xmb_color_theme == XMB_THEME_WALLPAPER) + add_opacity = true; + + menu_display_draw_bg(&draw, video_info, add_opacity, override_opacity); + } + } + + 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; + 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, + stripes->alpha, + stripes->textures.bg, + stripes_coord_black, + stripes_coord_white); + + selection = menu_navigation_get_selection(); + + strlcpy(title_truncated, + stripes->title_name, sizeof(title_truncated)); + + if (selection > 1) + { + /* skip 25 utf8 multi-byte chars */ + char *end = title_truncated; + + for(i = 0; i < 25 && *end; i++) + { + end++; + while((*end & 0xC0) == 0x80) + end++; + } + + *end = '\0'; + } + + /* Title text */ + stripes_draw_text(video_info, stripes, + title_truncated, stripes->margins_title_left, + stripes->margins_title_top, + 1, 1, TEXT_ALIGN_LEFT, + width, height, stripes->font); + + if (settings->bools.menu_core_enable && + menu_entries_get_core_title(title_msg, sizeof(title_msg)) == 0) + stripes_draw_text(video_info, stripes, title_msg, stripes->margins_title_left, + height - stripes->margins_title_bottom, 1, 1, TEXT_ALIGN_LEFT, + width, height, stripes->font); + + 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); + + /* Save State thumbnail, right side */ + if (stripes->savestate_thumbnail) + { + stripes_draw_thumbnail(video_info, + stripes, &stripes_coord_white[0], width, height, + stripes->margins_screen_left * stripes_scale_mod[5] + + stripes->icon_spacing_horizontal + pseudo_font_length, + stripes->margins_screen_top + stripes->icon_size + + stripes->savestate_thumbnail_height * stripes_scale_mod[4], + stripes->savestate_thumbnail_width * stripes_scale_mod[4], + stripes->savestate_thumbnail_height * stripes_scale_mod[4], + stripes->savestate_thumbnail); + } + + /* Right thumbnail big size */ + if (!settings->bools.menu_xmb_vertical_thumbnails || + (settings->bools.menu_xmb_vertical_thumbnails && !stripes->left_thumbnail)) + { + /* Do not draw the right thumbnail if there is no space available */ + + if (((stripes->margins_screen_top + + stripes->icon_size + min_thumb_size) <= height) && + ((stripes->margins_screen_left * stripes_scale_mod[5] + + stripes->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) + { + if (stripes->thumbnail + && !string_is_equal(stripes_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + /* Limit thumbnail width */ + + float thumb_width = 0.0f; + float thumb_height = 0.0f; + float thumb_max_width = (float)width - (stripes->icon_size / 6) + - (stripes->margins_screen_left * stripes_scale_mod[5]) - + stripes->icon_spacing_horizontal - pseudo_font_length; + + #ifdef STRIPES_DEBUG + RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", + stripes->thumbnail_width, stripes->thumbnail_height); + RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + if (stripes->thumbnail_width * stripes_scale_mod[4] > thumb_max_width) + { + thumb_width = (stripes->thumbnail_width * stripes_scale_mod[4]) * + (thumb_max_width / (stripes->thumbnail_width * stripes_scale_mod[4])); + thumb_height = (stripes->thumbnail_height * stripes_scale_mod[4]) * + (thumb_max_width / (stripes->thumbnail_width * stripes_scale_mod[4])); + } + else + { + thumb_width = stripes->thumbnail_width * stripes_scale_mod[4]; + thumb_height = stripes->thumbnail_height * stripes_scale_mod[4]; + } + + /* Limit thumbnail height to screen height + margin. */ + + if (stripes->margins_screen_top + stripes->icon_size + thumb_height >= + ((float)height * under_thumb_margin)) + { + thumb_width = thumb_width * + ((((float)height * under_thumb_margin) - + stripes->margins_screen_top - stripes->icon_size) / + thumb_height); + thumb_height = thumb_height * + ((((float)height * under_thumb_margin) - + stripes->margins_screen_top - stripes->icon_size) / + thumb_height); + } + + stripes_draw_thumbnail(video_info, + stripes, &stripes_coord_white[0], width, height, + (float)width - (stripes->icon_size / 6) - thumb_max_width + + ((thumb_max_width - thumb_width) / 2), + stripes->margins_screen_top + stripes->icon_size + thumb_height, + thumb_width, thumb_height, + stripes->thumbnail); + } + } + } + + /* Left thumbnail in the left margin */ + /* Do not draw the left thumbnail if there is no space available */ + if (!settings->bools.menu_xmb_vertical_thumbnails && + (stripes->margins_screen_top + stripes->icon_size * + (!(stripes->depth == 1)? 2.1 : 1) + min_thumb_size) + <= (float)height) + { + /* Left Thumbnail in the left margin */ + + if (stripes->left_thumbnail + && !string_is_equal(stripes_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + /* Limit left thumbnail width */ + + float left_thumb_width = 0.0f; + float left_thumb_height = 0.0f; + float thumb_max_width = stripes->icon_size * 3.4; + + #ifdef STRIPES_DEBUG + RARCH_LOG("[XMB left thumbnail] width: %.2f, height: %.2f\n", + stripes->left_thumbnail_width, stripes->left_thumbnail_height); + RARCH_LOG("[XMB left thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + if (stripes->left_thumbnail_width * stripes_scale_mod[4] > thumb_max_width) + { + left_thumb_width = (stripes->left_thumbnail_width * stripes_scale_mod[4]) * + (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); + left_thumb_height = (stripes->left_thumbnail_height * stripes_scale_mod[4]) * + (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); + } + else + { + left_thumb_width = stripes->left_thumbnail_width * stripes_scale_mod[4]; + left_thumb_height = stripes->left_thumbnail_height * stripes_scale_mod[4]; + } + + /* Limit left thumbnail height to screen height + margin. */ + if (stripes->margins_screen_top + stripes->icon_size * + (!(stripes->depth == 1)? 2.1 : 1) + + left_thumb_height >= + ((float)height - (96.0 * scale_factor))) + { + left_thumb_width = left_thumb_width * + ((((float)height - (96.0 * scale_factor)) + - stripes->margins_screen_top - + (stripes->icon_size * (!(stripes->depth == 1)? 2.1 : 1))) / + left_thumb_height); + + left_thumb_height = left_thumb_height * + ((((float)height - (96.0 * scale_factor)) + - stripes->margins_screen_top - + (stripes->icon_size * (!(stripes->depth == 1)? 2.1 : 1))) / + left_thumb_height); + } + else + { + left_thumb_width = left_thumb_width; + left_thumb_height = left_thumb_height; + } + + stripes_draw_thumbnail(video_info, + stripes, &stripes_coord_white[0], width, height, + (stripes->icon_size / 6) + + ((thumb_max_width - left_thumb_width) / 2), + stripes->margins_screen_top + stripes->icon_size * + (!(stripes->depth == 1)? 2.1 : 1) + left_thumb_height, + left_thumb_width, left_thumb_height, + stripes->left_thumbnail); + } + } + + /* No Right Thumbnail, draw only the left one big size */ + if (settings->bools.menu_xmb_vertical_thumbnails && !stripes->thumbnail) + { + /* Do not draw the left thumbnail if there is no space available */ + + if (((stripes->margins_screen_top + + stripes->icon_size + min_thumb_size) <= height) && + ((stripes->margins_screen_left * stripes_scale_mod[5] + + stripes->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) + { + if (stripes->left_thumbnail + && !string_is_equal(stripes_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + /* Limit left thumbnail width */ + + float left_thumb_width = 0.0f; + float left_thumb_height = 0.0f; + float thumb_max_width = (float)width - (stripes->icon_size / 6) + - (stripes->margins_screen_left * stripes_scale_mod[5]) - + stripes->icon_spacing_horizontal - pseudo_font_length; + + #ifdef STRIPES_DEBUG + RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", + stripes->thumbnail_width, stripes->thumbnail_height); + RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + if (stripes->left_thumbnail_width * stripes_scale_mod[4] > thumb_max_width) + { + left_thumb_width = (stripes->left_thumbnail_width * stripes_scale_mod[4]) * + (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); + left_thumb_height = (stripes->left_thumbnail_height * stripes_scale_mod[4]) * + (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); + } + else + { + left_thumb_width = stripes->left_thumbnail_width * stripes_scale_mod[4]; + left_thumb_height = stripes->left_thumbnail_height * stripes_scale_mod[4]; + } + + /* Limit left thumbnail height to screen height + margin. */ + + if (stripes->margins_screen_top + stripes->icon_size + left_thumb_height >= + ((float)height * under_thumb_margin)) + { + left_thumb_width = left_thumb_width * + ((((float)height * under_thumb_margin) - + stripes->margins_screen_top - stripes->icon_size) / + left_thumb_height); + left_thumb_height = left_thumb_height * + ((((float)height * under_thumb_margin) - + stripes->margins_screen_top - stripes->icon_size) / + left_thumb_height); + } + + stripes_draw_thumbnail(video_info, + stripes, &stripes_coord_white[0], width, height, + (float)width - (stripes->icon_size / 6) - thumb_max_width + + ((thumb_max_width - left_thumb_width) / 2), + stripes->margins_screen_top + stripes->icon_size + left_thumb_height, + left_thumb_width, left_thumb_height, + stripes->left_thumbnail); + } + } + } + + /* Clock image */ + menu_display_set_alpha(stripes_coord_white, MIN(stripes->alpha, 1.00f)); + + if (video_info->battery_level_enable) + { + char msg[12]; + static retro_time_t last_time = 0; + bool charging = false; + retro_time_t current_time = cpu_features_get_time_usec(); + int percent = 0; + enum frontend_powerstate state = get_last_powerstate(&percent); + + if (state == FRONTEND_POWERSTATE_CHARGING) + charging = true; + + if (current_time - last_time >= BATTERY_LEVEL_CHECK_INTERVAL) + { + last_time = current_time; + task_push_get_powerstate(); + } + + *msg = '\0'; + + if (percent > 0) + { + size_t x_pos = stripes->icon_size / 6; + size_t x_pos_icon = stripes->margins_title_left; + + if (stripes_coord_white[3] != 0) + stripes_draw_icon(video_info, + stripes->icon_size, + &mymat, + stripes->textures.list[charging + ? STRIPES_TEXTURE_BATTERY_CHARGING : STRIPES_TEXTURE_BATTERY_FULL], + width - (stripes->icon_size / 2) - x_pos_icon, + stripes->icon_size, + width, + height, + 1, + 0, + 1, + &stripes_coord_white[0], + stripes->shadow_offset); + + snprintf(msg, sizeof(msg), "%d%%", percent); + + percent_width = (unsigned) + font_driver_get_message_width( + stripes->font, msg, (unsigned)strlen(msg), 1); + + stripes_draw_text(video_info, stripes, msg, + width - stripes->margins_title_left - x_pos, + stripes->margins_title_top, 1, 1, TEXT_ALIGN_RIGHT, + width, height, stripes->font); + } + } + + if (video_info->timedate_enable) + { + menu_display_ctx_datetime_t datetime; + char timedate[255]; + int x_pos = 0; + + if (stripes_coord_white[3] != 0) + { + int x_pos = 0; + + if (percent_width) + x_pos = percent_width + (stripes->icon_size / 2.5); + + stripes_draw_icon(video_info, + stripes->icon_size, + &mymat, + stripes->textures.list[STRIPES_TEXTURE_CLOCK], + width - stripes->icon_size - x_pos, + stripes->icon_size, + width, + height, + 1, + 0, + 1, + &stripes_coord_white[0], + stripes->shadow_offset); + } + + timedate[0] = '\0'; + + datetime.s = timedate; + datetime.len = sizeof(timedate); + datetime.time_mode = 4; + + menu_display_timedate(&datetime); + + if (percent_width) + x_pos = percent_width + (stripes->icon_size / 2.5); + + stripes_draw_text(video_info, stripes, timedate, + width - stripes->margins_title_left - stripes->icon_size / 4 - x_pos, + stripes->margins_title_top, 1, 1, TEXT_ALIGN_RIGHT, + width, height, stripes->font); + } + + /* Arrow image */ + menu_display_set_alpha(stripes_coord_white, + MIN(stripes->textures_arrow_alpha, stripes->alpha)); + + if (stripes_coord_white[3] != 0) + stripes_draw_icon(video_info, + stripes->icon_size, + &mymat, + stripes->textures.list[STRIPES_TEXTURE_ARROW], + stripes->x + stripes->margins_screen_left + + stripes->icon_spacing_horizontal - + stripes->icon_size / 2.0 + stripes->icon_size, + stripes->margins_screen_top + + stripes->icon_size / 2.0 + stripes->icon_spacing_vertical + * stripes->active_item_factor, + width, + height, + stripes->textures_arrow_alpha, + 0, + 1, + &stripes_coord_white[0], + stripes->shadow_offset); + + menu_display_blend_begin(video_info); + + /* 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->x + stripes->categories_x_pos + + stripes->margins_screen_left + + stripes->icon_spacing_horizontal + * (i + 1) - stripes->icon_size / 2.0; + float y = stripes->margins_screen_top + + 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); + } + } + + /* Right side 2 thumbnails on top of each other */ + /* here to be displayed above the horizontal icons */ + if (stripes->left_thumbnail && stripes->thumbnail && settings->bools.menu_xmb_vertical_thumbnails) + { + /* Do not draw the right thumbnail if there is no space available */ + if (((stripes->margins_screen_top + + stripes->icon_size + min_thumb_size) <= height) && + ((stripes->margins_screen_left * stripes_scale_mod[5] + + stripes->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) + { + if (stripes->thumbnail && + !string_is_equal(stripes_thumbnails_ident('R'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + /* Limit right thumbnail width */ + + float thumb_width = 0.0f; + float thumb_height = 0.0f; + float thumb_max_width = (float)width - (stripes->icon_size / 6) - + (stripes->margins_screen_left * stripes_scale_mod[5]) - + stripes->icon_spacing_horizontal - pseudo_font_length; + + #ifdef STRIPES_DEBUG + RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", + stripes->thumbnail_width, stripes->thumbnail_height); + RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + if (stripes->thumbnail_width * stripes_scale_mod[4] > thumb_max_width) + { + thumb_width = (stripes->thumbnail_width * stripes_scale_mod[4]) * + (thumb_max_width / (stripes->thumbnail_width * stripes_scale_mod[4])); + thumb_height = (stripes->thumbnail_height * stripes_scale_mod[4]) * + (thumb_max_width / (stripes->thumbnail_width * stripes_scale_mod[4])); + } + else + { + thumb_width = stripes->thumbnail_width * stripes_scale_mod[4]; + thumb_height = stripes->thumbnail_height * stripes_scale_mod[4]; + } + + /* Limit right thumbnail height to usable area. */ + + if (thumb_height >= + ((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) + { + thumb_width = thumb_width * + ((((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) / + thumb_height); + thumb_height = thumb_height * + ((((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) / + thumb_height); + } + + stripes_draw_thumbnail(video_info, + stripes, &stripes_coord_white[0], width, height, + (float)width - (stripes->icon_size / 6) - thumb_max_width + + ((thumb_max_width - thumb_width) / 2), + stripes->icon_size + ((((float)height / 2 - + (stripes->icon_size + (stripes->icon_size/12))) - thumb_height) / 2) + + thumb_height, + thumb_width, thumb_height, + stripes->thumbnail); + } + } + + /* Do not draw the left thumbnail if there is no space available */ + + if (((stripes->margins_screen_top + + stripes->icon_size + min_thumb_size) <= height) && + ((stripes->margins_screen_left * stripes_scale_mod[5] + + stripes->icon_spacing_horizontal + + pseudo_font_length + min_thumb_size) <= width)) + { + if (stripes->left_thumbnail && + !string_is_equal(stripes_thumbnails_ident('L'), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + /* Limit left thumbnail width */ + + float left_thumb_width = 0.0f; + float left_thumb_height = 0.0f; + float thumb_max_width = (float)width - (stripes->icon_size / 6) - + (stripes->margins_screen_left * stripes_scale_mod[5]) - + stripes->icon_spacing_horizontal - pseudo_font_length; + + #ifdef STRIPES_DEBUG + RARCH_LOG("[XMB left thumbnail] width: %.2f, height: %.2f\n", + stripes->left_thumbnail_width, stripes->left_thumbnail_height); + RARCH_LOG("[XMB left thumbnail] w: %.2f, h: %.2f\n", width, height); + #endif + + if (stripes->left_thumbnail_width * stripes_scale_mod[4] > thumb_max_width) + { + left_thumb_width = (stripes->left_thumbnail_width * stripes_scale_mod[4]) * + (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); + left_thumb_height = (stripes->left_thumbnail_height * stripes_scale_mod[4]) * + (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); + } + else + { + left_thumb_width = stripes->left_thumbnail_width * stripes_scale_mod[4]; + left_thumb_height = stripes->left_thumbnail_height * stripes_scale_mod[4]; + } + + /* Limit left thumbnail height to usable area. */ + + if (left_thumb_height >= + ((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) + { + left_thumb_width = left_thumb_width * + ((((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) / + left_thumb_height); + left_thumb_height = left_thumb_height * + ((((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) / + left_thumb_height); + } + + stripes_draw_thumbnail(video_info, + stripes, &stripes_coord_white[0], width, height, + (float)width - (stripes->icon_size / 6) - thumb_max_width + + ((thumb_max_width - left_thumb_width) / 2), + stripes->icon_size + + (((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) + + (((((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) - + left_thumb_height) / 2) + left_thumb_height, + left_thumb_width, left_thumb_height, + stripes->left_thumbnail); + } + } + } + + 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) +{ + 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.5; + stripes->items_active_zoom = 1.0; + stripes->items_passive_zoom = 0.5; + + 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 = 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 = 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_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); + 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_ribbon_set_vertex(float *ribbon_verts, + unsigned idx, unsigned row, unsigned col) +{ + ribbon_verts[idx++] = ((float)col) / (STRIPES_RIBBON_COLS-1) * 2.0f - 1.0f; + ribbon_verts[idx++] = ((float)row) / (STRIPES_RIBBON_ROWS-1) * 2.0f - 1.0f; +} + +static void stripes_init_ribbon(stripes_handle_t * stripes) +{ + video_coords_t coords; + unsigned r, c, col; + unsigned i = 0; + video_coord_array_t *ca = menu_display_get_coords_array(); + unsigned vertices_total = STRIPES_RIBBON_VERTICES; + float *dummy = (float*)calloc(4 * vertices_total, sizeof(float)); + float *ribbon_verts = (float*)calloc(2 * vertices_total, sizeof(float)); + + /* Set up vertices */ + for (r = 0; r < STRIPES_RIBBON_ROWS - 1; r++) + { + for (c = 0; c < STRIPES_RIBBON_COLS; c++) + { + col = r % 2 ? STRIPES_RIBBON_COLS - c - 1 : c; + stripes_ribbon_set_vertex(ribbon_verts, i, r, col); + stripes_ribbon_set_vertex(ribbon_verts, i + 2, r + 1, col); + i += 4; + } + } + + coords.color = dummy; + coords.vertex = ribbon_verts; + coords.tex_coord = dummy; + coords.lut_tex_coord = dummy; + coords.vertices = vertices_total; + + video_coord_array_append(ca, &coords, coords.vertices); + + free(dummy); + free(ribbon_verts); +} + + + +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); + + stripes_init_ribbon(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/menu_driver.c b/menu/menu_driver.c index dde5ee212b..0912114c17 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -81,6 +81,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 diff --git a/menu/menu_driver.h b/menu/menu_driver.h index b61131a33d..d36214da0a 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -771,6 +771,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/pkg/apple/RetroArch.xcodeproj/project.pbxproj b/pkg/apple/RetroArch.xcodeproj/project.pbxproj index 3b4f0dd4be..6c0637f444 100644 --- a/pkg/apple/RetroArch.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch.xcodeproj/project.pbxproj @@ -539,7 +539,7 @@ "-DHAVE_MATERIALUI", "-DHAVE_HID", "-DHAVE_XMB", - "-DHAVE_SEGA", + "-DHAVE_STRIPES", "-DHAVE_SHADERPIPELINE", "-DHAVE_MMAP", "-DHAVE_LIBRETRODB", @@ -600,7 +600,7 @@ "-DHAVE_MATERIALUI", "-DHAVE_HID", "-DHAVE_XMB", - "-DHAVE_SEGA", + "-DHAVE_STRIPES", "-DHAVE_SHADERPIPELINE", "-DHAVE_MMAP", "-DHAVE_LIBRETRODB", diff --git a/qb/config.libs.sh b/qb/config.libs.sh index e0c3382d5b..d5894d2fc6 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -477,6 +477,7 @@ if [ "$HAVE_MATERIALUI" != 'no' ] || [ "$HAVE_XMB" != 'no' ] || [ "$HAVE_ZARCH" if [ "$HAVE_RGUI" = 'no' ]; then HAVE_MATERIALUI=no HAVE_XMB=no + HAVE_STRIPES=no HAVE_ZARCH=no die : 'Notice: RGUI not available, MaterialUI, XMB and ZARCH will also be disabled.' elif [ "$HAVE_OPENGL" = 'no' ] && [ "$HAVE_OPENGLES" = 'no' ] && [ "$HAVE_VULKAN" = 'no' ]; then @@ -489,6 +490,7 @@ if [ "$HAVE_MATERIALUI" != 'no' ] || [ "$HAVE_XMB" != 'no' ] || [ "$HAVE_ZARCH" else HAVE_MATERIALUI=no HAVE_XMB=no + HAVE_STRIPES=no HAVE_ZARCH=no die : 'Notice: Hardware rendering context not available, XMB, MaterialUI and ZARCH will also be disabled.' fi diff --git a/qb/config.params.sh b/qb/config.params.sh index 94135f94f6..6c120234b5 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -9,6 +9,7 @@ HAVE_LIBRETRODB=yes # Libretrodb support HAVE_RGUI=yes # RGUI menu HAVE_MATERIALUI=auto # MaterialUI menu HAVE_XMB=auto # XMB menu +HAVE_STRIPES=auto # Stripes menu HAVE_ZARCH=no # Zarch menu HAVE_NUKLEAR=no # Nuklear menu HAVE_RUNAHEAD=yes # Runahead support From fcda6f7d9628fd467f046150e8c0756158e43184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 10:01:14 +0700 Subject: [PATCH 002/106] [stripes] Remove gradients --- menu/drivers/stripes.c | 121 +---------------------------------------- 1 file changed, 2 insertions(+), 119 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 520cf87812..e1f0a2c01a 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -315,85 +315,6 @@ static float stripes_item_color[] = { 1, 1, 1, 1 }; - -float stripes_stripes_gradient_dark_purple[16] = { - 20/255.0, 13/255.0, 20/255.0, 1.0, - 20/255.0, 13/255.0, 20/255.0, 1.0, - 92/255.0, 44/255.0, 92/255.0, 1.0, - 148/255.0, 90/255.0, 148/255.0, 1.0, -}; - -float stripes_gradient_midnight_blue[16] = { - 44/255.0, 62/255.0, 80/255.0, 1.0, - 44/255.0, 62/255.0, 80/255.0, 1.0, - 44/255.0, 62/255.0, 80/255.0, 1.0, - 44/255.0, 62/255.0, 80/255.0, 1.0, -}; - -float stripes_gradient_golden[16] = { - 174/255.0, 123/255.0, 44/255.0, 1.0, - 205/255.0, 174/255.0, 84/255.0, 1.0, - 58/255.0, 43/255.0, 24/255.0, 1.0, - 58/255.0, 43/255.0, 24/255.0, 1.0, -}; - -float stripes_gradient_legacy_red[16] = { - 171/255.0, 70/255.0, 59/255.0, 1.0, - 171/255.0, 70/255.0, 59/255.0, 1.0, - 190/255.0, 80/255.0, 69/255.0, 1.0, - 190/255.0, 80/255.0, 69/255.0, 1.0, -}; - -float stripes_gradient_electric_blue[16] = { - 1/255.0, 2/255.0, 67/255.0, 1.0, - 1/255.0, 73/255.0, 183/255.0, 1.0, - 1/255.0, 93/255.0, 194/255.0, 1.0, - 3/255.0, 162/255.0, 254/255.0, 1.0, -}; - -float stripes_gradient_apple_green[16] = { - 102/255.0, 134/255.0, 58/255.0, 1.0, - 122/255.0, 131/255.0, 52/255.0, 1.0, - 82/255.0, 101/255.0, 35/255.0, 1.0, - 63/255.0, 95/255.0, 30/255.0, 1.0, -}; - -float stripes_gradient_undersea[16] = { - 23/255.0, 18/255.0, 41/255.0, 1.0, - 30/255.0, 72/255.0, 114/255.0, 1.0, - 52/255.0, 88/255.0, 110/255.0, 1.0, - 69/255.0, 125/255.0, 140/255.0, 1.0, - -}; - -float stripes_gradient_volcanic_red[16] = { - 1.0, 0.0, 0.1, 1.00, - 1.0, 0.1, 0.0, 1.00, - 0.1, 0.0, 0.1, 1.00, - 0.1, 0.0, 0.1, 1.00, -}; - -float stripes_gradient_dark[16] = { - 0.1, 0.1, 0.1, 1.00, - 0.1, 0.1, 0.1, 1.00, - 0.0, 0.0, 0.0, 1.00, - 0.0, 0.0, 0.0, 1.00, -}; - -float stripes_gradient_light[16] = { - 1.0, 1.0, 1.0, 1.00, - 1.0, 1.0, 1.0, 1.00, - 1.0, 1.0, 1.0, 1.00, - 1.0, 1.0, 1.0, 1.00, -}; - -float stripes_gradient_morning_blue[16] = { - 221/255.0, 241/255.0, 254/255.0, 1.00, - 135/255.0, 206/255.0, 250/255.0, 1.00, - 1.0, 1.0, 1.0, 1.00, - 170/255.0, 200/255.0, 252/255.0, 1.00, -}; - static void stripes_calculate_visible_range(const stripes_handle_t *stripes, unsigned height, size_t list_size, unsigned current, unsigned *first, unsigned *last); @@ -518,38 +439,6 @@ static const char *stripes_thumbnails_ident(char pos) return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF); } -static float *stripes_gradient_ident(video_frame_info_t *video_info) -{ - switch (video_info->xmb_color_theme) - { - case XMB_THEME_DARK_PURPLE: - return &stripes_stripes_gradient_dark_purple[0]; - case XMB_THEME_MIDNIGHT_BLUE: - return &stripes_gradient_midnight_blue[0]; - case XMB_THEME_GOLDEN: - return &stripes_gradient_golden[0]; - case XMB_THEME_ELECTRIC_BLUE: - return &stripes_gradient_electric_blue[0]; - case XMB_THEME_APPLE_GREEN: - return &stripes_gradient_apple_green[0]; - case XMB_THEME_UNDERSEA: - return &stripes_gradient_undersea[0]; - case XMB_THEME_VOLCANIC_RED: - return &stripes_gradient_volcanic_red[0]; - case XMB_THEME_DARK: - return &stripes_gradient_dark[0]; - case XMB_THEME_LIGHT: - return &stripes_gradient_light[0]; - case XMB_THEME_MORNING_BLUE: - return &stripes_gradient_morning_blue[0]; - case XMB_THEME_LEGACY_RED: - default: - break; - } - - return &stripes_gradient_legacy_red[0]; -} - static size_t stripes_list_get_selection(void *data) { stripes_handle_t *stripes = (stripes_handle_t*)data; @@ -2922,15 +2811,13 @@ static void stripes_draw_bg( && (video_info->xmb_color_theme != XMB_THEME_WALLPAPER)) { - draw.color = stripes_gradient_ident(video_info); + draw.color = &stripes_coord_white[0]; if (running) menu_display_set_alpha(draw.color, stripes_coord_black[3]); else menu_display_set_alpha(draw.color, stripes_coord_white[3]); - menu_display_draw_gradient(&draw, video_info); - draw.pipeline.id = VIDEO_SHADER_MENU_2; switch (video_info->menu_shader_pipeline) @@ -2961,17 +2848,13 @@ static void stripes_draw_bg( { uintptr_t texture = draw.texture; - if (video_info->xmb_color_theme != XMB_THEME_WALLPAPER) - draw.color = stripes_gradient_ident(video_info); + draw.color = &stripes_coord_white[0]; if (running) menu_display_set_alpha(draw.color, stripes_coord_black[3]); else menu_display_set_alpha(draw.color, stripes_coord_white[3]); - if (video_info->xmb_color_theme != XMB_THEME_WALLPAPER) - menu_display_draw_gradient(&draw, video_info); - { float override_opacity = video_info->menu_wallpaper_opacity; bool add_opacity = false; From 7601b740cc8a9fcd01a7e97a6f3c0210a000099e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 10:41:02 +0700 Subject: [PATCH 003/106] [stripes] Remove ribbon --- menu/drivers/xmb.c | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 362709187e..88b0044c9e 100755 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3960,42 +3960,6 @@ static void xmb_ribbon_set_vertex(float *ribbon_verts, ribbon_verts[idx++] = ((float)row) / (XMB_RIBBON_ROWS-1) * 2.0f - 1.0f; } -static void xmb_init_ribbon(xmb_handle_t * xmb) -{ - video_coords_t coords; - unsigned r, c, col; - unsigned i = 0; - video_coord_array_t *ca = menu_display_get_coords_array(); - unsigned vertices_total = XMB_RIBBON_VERTICES; - float *dummy = (float*)calloc(4 * vertices_total, sizeof(float)); - float *ribbon_verts = (float*)calloc(2 * vertices_total, sizeof(float)); - - /* Set up vertices */ - for (r = 0; r < XMB_RIBBON_ROWS - 1; r++) - { - for (c = 0; c < XMB_RIBBON_COLS; c++) - { - col = r % 2 ? XMB_RIBBON_COLS - c - 1 : c; - xmb_ribbon_set_vertex(ribbon_verts, i, r, col); - xmb_ribbon_set_vertex(ribbon_verts, i + 2, r + 1, col); - i += 4; - } - } - - coords.color = dummy; - coords.vertex = ribbon_verts; - coords.tex_coord = dummy; - coords.lut_tex_coord = dummy; - coords.vertices = vertices_total; - - video_coord_array_append(ca, &coords, coords.vertices); - - free(dummy); - free(ribbon_verts); -} - - - static void *xmb_init(void **userdata, bool video_is_threaded) { unsigned width, height; @@ -4104,8 +4068,6 @@ static void *xmb_init(void **userdata, bool video_is_threaded) if (xmb->horizontal_list) xmb_init_horizontal_list(xmb); - xmb_init_ribbon(xmb); - return menu; error: From 061fe25c33d441d0d9d9da3ee025fe4cb476e75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 13:16:01 +0700 Subject: [PATCH 004/106] [stripes] Compute y position for the tabs icons --- menu/drivers/stripes.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index e1f0a2c01a..0367ba254c 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -234,6 +234,9 @@ typedef struct stripes_handle float margins_slice; float textures_arrow_alpha; float categories_x_pos; + float categories_active_y; + float categories_before_y; + float categories_after_y; float categories_passive_alpha; float categories_passive_zoom; float categories_active_zoom; @@ -1702,6 +1705,7 @@ static void stripes_list_switch_horizontal_list(stripes_handle_t *stripes) menu_animation_ctx_entry_t entry; float ia = stripes->categories_passive_alpha; float iz = stripes->categories_passive_zoom; + float iy = stripes->categories_before_y; stripes_node_t *node = stripes_get_node(stripes, j); if (!node) @@ -1711,6 +1715,15 @@ static void stripes_list_switch_horizontal_list(stripes_handle_t *stripes) { ia = stripes->categories_active_alpha; iz = stripes->categories_active_zoom; + iy = stripes->categories_active_y; + } + else if (j < stripes->categories_active_idx) + { + iy = stripes->categories_before_y; + } + else if (j > stripes->categories_active_idx) + { + iy = stripes->categories_after_y; } entry.duration = STRIPES_DELAY; @@ -1727,6 +1740,11 @@ static void stripes_list_switch_horizontal_list(stripes_handle_t *stripes) entry.subject = &node->zoom; menu_animation_push(&entry); + + entry.target_value = iy; + entry.subject = &node->y; + + menu_animation_push(&entry); } } @@ -3381,7 +3399,7 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) stripes->margins_screen_left + stripes->icon_spacing_horizontal * (i + 1) - stripes->icon_size / 2.0; - float y = stripes->margins_screen_top + float y = node->y + stripes->icon_size / 2.0; float rotation = 0; float scale_factor = node->zoom; @@ -3624,7 +3642,7 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) menu_display_unset_viewport(video_info->width, video_info->height); } -static void stripes_layout_ps3(stripes_handle_t *stripes, int width) +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(); @@ -3639,6 +3657,11 @@ static void stripes_layout_ps3(stripes_handle_t *stripes, int width) stripes->categories_active_zoom = 1.0; stripes->categories_passive_zoom = 0.5; + + stripes->categories_active_y = height / 2; + stripes->categories_before_y = 64; + stripes->categories_after_y = height - 64; + stripes->items_active_zoom = 1.0; stripes->items_passive_zoom = 0.5; @@ -3777,7 +3800,7 @@ static void stripes_layout(stripes_handle_t *stripes) /* Mimic the layout of the PSP instead of the PS3 on tiny screens */ if (width > 320 && height > 240) - stripes_layout_ps3(stripes, width); + stripes_layout_ps3(stripes, width, height); else stripes_layout_psp(stripes, width); From 5a22d1cd8ecf2e0cde16e8743e601711e6628a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 13:21:33 +0700 Subject: [PATCH 005/106] [stripes] Comment out draw_items for now --- menu/drivers/stripes.c | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 0367ba254c..06dc09c601 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -3565,31 +3565,31 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) 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); +// 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); +// 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); +// 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); From 7a7cb730c54c783b5bb7ada3e0d501695ded871f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 13:26:20 +0700 Subject: [PATCH 006/106] [stripes] Remove pipeline --- menu/drivers/stripes.c | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 06dc09c601..f32d778504 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -2824,45 +2824,6 @@ static void stripes_draw_bg( menu_display_blend_begin(video_info); menu_display_set_viewport(video_info->width, video_info->height); -#ifdef HAVE_SHADERPIPELINE - if (video_info->menu_shader_pipeline > XMB_SHADER_PIPELINE_WALLPAPER - && - (video_info->xmb_color_theme != XMB_THEME_WALLPAPER)) - { - draw.color = &stripes_coord_white[0]; - - if (running) - menu_display_set_alpha(draw.color, stripes_coord_black[3]); - else - menu_display_set_alpha(draw.color, stripes_coord_white[3]); - - draw.pipeline.id = VIDEO_SHADER_MENU_2; - - switch (video_info->menu_shader_pipeline) - { - case XMB_SHADER_PIPELINE_RIBBON: - draw.pipeline.id = VIDEO_SHADER_MENU; - break; - case XMB_SHADER_PIPELINE_SIMPLE_SNOW: - draw.pipeline.id = VIDEO_SHADER_MENU_3; - break; - case XMB_SHADER_PIPELINE_SNOW: - draw.pipeline.id = VIDEO_SHADER_MENU_4; - break; - case XMB_SHADER_PIPELINE_BOKEH: - draw.pipeline.id = VIDEO_SHADER_MENU_5; - break; - case XMB_SHADER_PIPELINE_SNOWFLAKE: - draw.pipeline.id = VIDEO_SHADER_MENU_6; - break; - default: - break; - } - - menu_display_draw_pipeline(&draw, video_info); - } - else -#endif { uintptr_t texture = draw.texture; From 65626c188af2246c4856705cc78c995655902e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 13:31:19 +0700 Subject: [PATCH 007/106] [stripes] Remove most of draw_frame --- menu/drivers/stripes.c | 515 ----------------------------------------- 1 file changed, 515 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index f32d778504..8987c29dd2 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -2948,37 +2948,6 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) selection = menu_navigation_get_selection(); - strlcpy(title_truncated, - stripes->title_name, sizeof(title_truncated)); - - if (selection > 1) - { - /* skip 25 utf8 multi-byte chars */ - char *end = title_truncated; - - for(i = 0; i < 25 && *end; i++) - { - end++; - while((*end & 0xC0) == 0x80) - end++; - } - - *end = '\0'; - } - - /* Title text */ - stripes_draw_text(video_info, stripes, - title_truncated, stripes->margins_title_left, - stripes->margins_title_top, - 1, 1, TEXT_ALIGN_LEFT, - width, height, stripes->font); - - if (settings->bools.menu_core_enable && - menu_entries_get_core_title(title_msg, sizeof(title_msg)) == 0) - stripes_draw_text(video_info, stripes, title_msg, stripes->margins_title_left, - height - stripes->margins_title_bottom, 1, 1, TEXT_ALIGN_LEFT, - width, height, stripes->font); - rotate_draw.matrix = &mymat; rotate_draw.rotation = 0; rotate_draw.scale_x = 1; @@ -2989,357 +2958,6 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) menu_display_rotate_z(&rotate_draw, video_info); menu_display_blend_begin(video_info); - /* Save State thumbnail, right side */ - if (stripes->savestate_thumbnail) - { - stripes_draw_thumbnail(video_info, - stripes, &stripes_coord_white[0], width, height, - stripes->margins_screen_left * stripes_scale_mod[5] + - stripes->icon_spacing_horizontal + pseudo_font_length, - stripes->margins_screen_top + stripes->icon_size + - stripes->savestate_thumbnail_height * stripes_scale_mod[4], - stripes->savestate_thumbnail_width * stripes_scale_mod[4], - stripes->savestate_thumbnail_height * stripes_scale_mod[4], - stripes->savestate_thumbnail); - } - - /* Right thumbnail big size */ - if (!settings->bools.menu_xmb_vertical_thumbnails || - (settings->bools.menu_xmb_vertical_thumbnails && !stripes->left_thumbnail)) - { - /* Do not draw the right thumbnail if there is no space available */ - - if (((stripes->margins_screen_top + - stripes->icon_size + min_thumb_size) <= height) && - ((stripes->margins_screen_left * stripes_scale_mod[5] + - stripes->icon_spacing_horizontal + - pseudo_font_length + min_thumb_size) <= width)) - { - if (stripes->thumbnail - && !string_is_equal(stripes_thumbnails_ident('R'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - /* Limit thumbnail width */ - - float thumb_width = 0.0f; - float thumb_height = 0.0f; - float thumb_max_width = (float)width - (stripes->icon_size / 6) - - (stripes->margins_screen_left * stripes_scale_mod[5]) - - stripes->icon_spacing_horizontal - pseudo_font_length; - - #ifdef STRIPES_DEBUG - RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", - stripes->thumbnail_width, stripes->thumbnail_height); - RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif - - if (stripes->thumbnail_width * stripes_scale_mod[4] > thumb_max_width) - { - thumb_width = (stripes->thumbnail_width * stripes_scale_mod[4]) * - (thumb_max_width / (stripes->thumbnail_width * stripes_scale_mod[4])); - thumb_height = (stripes->thumbnail_height * stripes_scale_mod[4]) * - (thumb_max_width / (stripes->thumbnail_width * stripes_scale_mod[4])); - } - else - { - thumb_width = stripes->thumbnail_width * stripes_scale_mod[4]; - thumb_height = stripes->thumbnail_height * stripes_scale_mod[4]; - } - - /* Limit thumbnail height to screen height + margin. */ - - if (stripes->margins_screen_top + stripes->icon_size + thumb_height >= - ((float)height * under_thumb_margin)) - { - thumb_width = thumb_width * - ((((float)height * under_thumb_margin) - - stripes->margins_screen_top - stripes->icon_size) / - thumb_height); - thumb_height = thumb_height * - ((((float)height * under_thumb_margin) - - stripes->margins_screen_top - stripes->icon_size) / - thumb_height); - } - - stripes_draw_thumbnail(video_info, - stripes, &stripes_coord_white[0], width, height, - (float)width - (stripes->icon_size / 6) - thumb_max_width + - ((thumb_max_width - thumb_width) / 2), - stripes->margins_screen_top + stripes->icon_size + thumb_height, - thumb_width, thumb_height, - stripes->thumbnail); - } - } - } - - /* Left thumbnail in the left margin */ - /* Do not draw the left thumbnail if there is no space available */ - if (!settings->bools.menu_xmb_vertical_thumbnails && - (stripes->margins_screen_top + stripes->icon_size * - (!(stripes->depth == 1)? 2.1 : 1) + min_thumb_size) - <= (float)height) - { - /* Left Thumbnail in the left margin */ - - if (stripes->left_thumbnail - && !string_is_equal(stripes_thumbnails_ident('L'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - /* Limit left thumbnail width */ - - float left_thumb_width = 0.0f; - float left_thumb_height = 0.0f; - float thumb_max_width = stripes->icon_size * 3.4; - - #ifdef STRIPES_DEBUG - RARCH_LOG("[XMB left thumbnail] width: %.2f, height: %.2f\n", - stripes->left_thumbnail_width, stripes->left_thumbnail_height); - RARCH_LOG("[XMB left thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif - - if (stripes->left_thumbnail_width * stripes_scale_mod[4] > thumb_max_width) - { - left_thumb_width = (stripes->left_thumbnail_width * stripes_scale_mod[4]) * - (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); - left_thumb_height = (stripes->left_thumbnail_height * stripes_scale_mod[4]) * - (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); - } - else - { - left_thumb_width = stripes->left_thumbnail_width * stripes_scale_mod[4]; - left_thumb_height = stripes->left_thumbnail_height * stripes_scale_mod[4]; - } - - /* Limit left thumbnail height to screen height + margin. */ - if (stripes->margins_screen_top + stripes->icon_size * - (!(stripes->depth == 1)? 2.1 : 1) + - left_thumb_height >= - ((float)height - (96.0 * scale_factor))) - { - left_thumb_width = left_thumb_width * - ((((float)height - (96.0 * scale_factor)) - - stripes->margins_screen_top - - (stripes->icon_size * (!(stripes->depth == 1)? 2.1 : 1))) / - left_thumb_height); - - left_thumb_height = left_thumb_height * - ((((float)height - (96.0 * scale_factor)) - - stripes->margins_screen_top - - (stripes->icon_size * (!(stripes->depth == 1)? 2.1 : 1))) / - left_thumb_height); - } - else - { - left_thumb_width = left_thumb_width; - left_thumb_height = left_thumb_height; - } - - stripes_draw_thumbnail(video_info, - stripes, &stripes_coord_white[0], width, height, - (stripes->icon_size / 6) + - ((thumb_max_width - left_thumb_width) / 2), - stripes->margins_screen_top + stripes->icon_size * - (!(stripes->depth == 1)? 2.1 : 1) + left_thumb_height, - left_thumb_width, left_thumb_height, - stripes->left_thumbnail); - } - } - - /* No Right Thumbnail, draw only the left one big size */ - if (settings->bools.menu_xmb_vertical_thumbnails && !stripes->thumbnail) - { - /* Do not draw the left thumbnail if there is no space available */ - - if (((stripes->margins_screen_top + - stripes->icon_size + min_thumb_size) <= height) && - ((stripes->margins_screen_left * stripes_scale_mod[5] + - stripes->icon_spacing_horizontal + - pseudo_font_length + min_thumb_size) <= width)) - { - if (stripes->left_thumbnail - && !string_is_equal(stripes_thumbnails_ident('L'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - /* Limit left thumbnail width */ - - float left_thumb_width = 0.0f; - float left_thumb_height = 0.0f; - float thumb_max_width = (float)width - (stripes->icon_size / 6) - - (stripes->margins_screen_left * stripes_scale_mod[5]) - - stripes->icon_spacing_horizontal - pseudo_font_length; - - #ifdef STRIPES_DEBUG - RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", - stripes->thumbnail_width, stripes->thumbnail_height); - RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif - - if (stripes->left_thumbnail_width * stripes_scale_mod[4] > thumb_max_width) - { - left_thumb_width = (stripes->left_thumbnail_width * stripes_scale_mod[4]) * - (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); - left_thumb_height = (stripes->left_thumbnail_height * stripes_scale_mod[4]) * - (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); - } - else - { - left_thumb_width = stripes->left_thumbnail_width * stripes_scale_mod[4]; - left_thumb_height = stripes->left_thumbnail_height * stripes_scale_mod[4]; - } - - /* Limit left thumbnail height to screen height + margin. */ - - if (stripes->margins_screen_top + stripes->icon_size + left_thumb_height >= - ((float)height * under_thumb_margin)) - { - left_thumb_width = left_thumb_width * - ((((float)height * under_thumb_margin) - - stripes->margins_screen_top - stripes->icon_size) / - left_thumb_height); - left_thumb_height = left_thumb_height * - ((((float)height * under_thumb_margin) - - stripes->margins_screen_top - stripes->icon_size) / - left_thumb_height); - } - - stripes_draw_thumbnail(video_info, - stripes, &stripes_coord_white[0], width, height, - (float)width - (stripes->icon_size / 6) - thumb_max_width + - ((thumb_max_width - left_thumb_width) / 2), - stripes->margins_screen_top + stripes->icon_size + left_thumb_height, - left_thumb_width, left_thumb_height, - stripes->left_thumbnail); - } - } - } - - /* Clock image */ - menu_display_set_alpha(stripes_coord_white, MIN(stripes->alpha, 1.00f)); - - if (video_info->battery_level_enable) - { - char msg[12]; - static retro_time_t last_time = 0; - bool charging = false; - retro_time_t current_time = cpu_features_get_time_usec(); - int percent = 0; - enum frontend_powerstate state = get_last_powerstate(&percent); - - if (state == FRONTEND_POWERSTATE_CHARGING) - charging = true; - - if (current_time - last_time >= BATTERY_LEVEL_CHECK_INTERVAL) - { - last_time = current_time; - task_push_get_powerstate(); - } - - *msg = '\0'; - - if (percent > 0) - { - size_t x_pos = stripes->icon_size / 6; - size_t x_pos_icon = stripes->margins_title_left; - - if (stripes_coord_white[3] != 0) - stripes_draw_icon(video_info, - stripes->icon_size, - &mymat, - stripes->textures.list[charging - ? STRIPES_TEXTURE_BATTERY_CHARGING : STRIPES_TEXTURE_BATTERY_FULL], - width - (stripes->icon_size / 2) - x_pos_icon, - stripes->icon_size, - width, - height, - 1, - 0, - 1, - &stripes_coord_white[0], - stripes->shadow_offset); - - snprintf(msg, sizeof(msg), "%d%%", percent); - - percent_width = (unsigned) - font_driver_get_message_width( - stripes->font, msg, (unsigned)strlen(msg), 1); - - stripes_draw_text(video_info, stripes, msg, - width - stripes->margins_title_left - x_pos, - stripes->margins_title_top, 1, 1, TEXT_ALIGN_RIGHT, - width, height, stripes->font); - } - } - - if (video_info->timedate_enable) - { - menu_display_ctx_datetime_t datetime; - char timedate[255]; - int x_pos = 0; - - if (stripes_coord_white[3] != 0) - { - int x_pos = 0; - - if (percent_width) - x_pos = percent_width + (stripes->icon_size / 2.5); - - stripes_draw_icon(video_info, - stripes->icon_size, - &mymat, - stripes->textures.list[STRIPES_TEXTURE_CLOCK], - width - stripes->icon_size - x_pos, - stripes->icon_size, - width, - height, - 1, - 0, - 1, - &stripes_coord_white[0], - stripes->shadow_offset); - } - - timedate[0] = '\0'; - - datetime.s = timedate; - datetime.len = sizeof(timedate); - datetime.time_mode = 4; - - menu_display_timedate(&datetime); - - if (percent_width) - x_pos = percent_width + (stripes->icon_size / 2.5); - - stripes_draw_text(video_info, stripes, timedate, - width - stripes->margins_title_left - stripes->icon_size / 4 - x_pos, - stripes->margins_title_top, 1, 1, TEXT_ALIGN_RIGHT, - width, height, stripes->font); - } - - /* Arrow image */ - menu_display_set_alpha(stripes_coord_white, - MIN(stripes->textures_arrow_alpha, stripes->alpha)); - - if (stripes_coord_white[3] != 0) - stripes_draw_icon(video_info, - stripes->icon_size, - &mymat, - stripes->textures.list[STRIPES_TEXTURE_ARROW], - stripes->x + stripes->margins_screen_left + - stripes->icon_spacing_horizontal - - stripes->icon_size / 2.0 + stripes->icon_size, - stripes->margins_screen_top + - stripes->icon_size / 2.0 + stripes->icon_spacing_vertical - * stripes->active_item_factor, - width, - height, - stripes->textures_arrow_alpha, - 0, - 1, - &stripes_coord_white[0], - stripes->shadow_offset); - - menu_display_blend_begin(video_info); - /* Horizontal tab icons */ for (i = 0; i <= stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL) + stripes->system_tab_end; i++) @@ -3390,139 +3008,6 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) } } - /* Right side 2 thumbnails on top of each other */ - /* here to be displayed above the horizontal icons */ - if (stripes->left_thumbnail && stripes->thumbnail && settings->bools.menu_xmb_vertical_thumbnails) - { - /* Do not draw the right thumbnail if there is no space available */ - if (((stripes->margins_screen_top + - stripes->icon_size + min_thumb_size) <= height) && - ((stripes->margins_screen_left * stripes_scale_mod[5] + - stripes->icon_spacing_horizontal + - pseudo_font_length + min_thumb_size) <= width)) - { - if (stripes->thumbnail && - !string_is_equal(stripes_thumbnails_ident('R'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - /* Limit right thumbnail width */ - - float thumb_width = 0.0f; - float thumb_height = 0.0f; - float thumb_max_width = (float)width - (stripes->icon_size / 6) - - (stripes->margins_screen_left * stripes_scale_mod[5]) - - stripes->icon_spacing_horizontal - pseudo_font_length; - - #ifdef STRIPES_DEBUG - RARCH_LOG("[XMB thumbnail] width: %.2f, height: %.2f\n", - stripes->thumbnail_width, stripes->thumbnail_height); - RARCH_LOG("[XMB thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif - - if (stripes->thumbnail_width * stripes_scale_mod[4] > thumb_max_width) - { - thumb_width = (stripes->thumbnail_width * stripes_scale_mod[4]) * - (thumb_max_width / (stripes->thumbnail_width * stripes_scale_mod[4])); - thumb_height = (stripes->thumbnail_height * stripes_scale_mod[4]) * - (thumb_max_width / (stripes->thumbnail_width * stripes_scale_mod[4])); - } - else - { - thumb_width = stripes->thumbnail_width * stripes_scale_mod[4]; - thumb_height = stripes->thumbnail_height * stripes_scale_mod[4]; - } - - /* Limit right thumbnail height to usable area. */ - - if (thumb_height >= - ((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) - { - thumb_width = thumb_width * - ((((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) / - thumb_height); - thumb_height = thumb_height * - ((((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) / - thumb_height); - } - - stripes_draw_thumbnail(video_info, - stripes, &stripes_coord_white[0], width, height, - (float)width - (stripes->icon_size / 6) - thumb_max_width + - ((thumb_max_width - thumb_width) / 2), - stripes->icon_size + ((((float)height / 2 - - (stripes->icon_size + (stripes->icon_size/12))) - thumb_height) / 2) + - thumb_height, - thumb_width, thumb_height, - stripes->thumbnail); - } - } - - /* Do not draw the left thumbnail if there is no space available */ - - if (((stripes->margins_screen_top + - stripes->icon_size + min_thumb_size) <= height) && - ((stripes->margins_screen_left * stripes_scale_mod[5] + - stripes->icon_spacing_horizontal + - pseudo_font_length + min_thumb_size) <= width)) - { - if (stripes->left_thumbnail && - !string_is_equal(stripes_thumbnails_ident('L'), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - /* Limit left thumbnail width */ - - float left_thumb_width = 0.0f; - float left_thumb_height = 0.0f; - float thumb_max_width = (float)width - (stripes->icon_size / 6) - - (stripes->margins_screen_left * stripes_scale_mod[5]) - - stripes->icon_spacing_horizontal - pseudo_font_length; - - #ifdef STRIPES_DEBUG - RARCH_LOG("[XMB left thumbnail] width: %.2f, height: %.2f\n", - stripes->left_thumbnail_width, stripes->left_thumbnail_height); - RARCH_LOG("[XMB left thumbnail] w: %.2f, h: %.2f\n", width, height); - #endif - - if (stripes->left_thumbnail_width * stripes_scale_mod[4] > thumb_max_width) - { - left_thumb_width = (stripes->left_thumbnail_width * stripes_scale_mod[4]) * - (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); - left_thumb_height = (stripes->left_thumbnail_height * stripes_scale_mod[4]) * - (thumb_max_width / (stripes->left_thumbnail_width * stripes_scale_mod[4])); - } - else - { - left_thumb_width = stripes->left_thumbnail_width * stripes_scale_mod[4]; - left_thumb_height = stripes->left_thumbnail_height * stripes_scale_mod[4]; - } - - /* Limit left thumbnail height to usable area. */ - - if (left_thumb_height >= - ((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) - { - left_thumb_width = left_thumb_width * - ((((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) / - left_thumb_height); - left_thumb_height = left_thumb_height * - ((((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) / - left_thumb_height); - } - - stripes_draw_thumbnail(video_info, - stripes, &stripes_coord_white[0], width, height, - (float)width - (stripes->icon_size / 6) - thumb_max_width + - ((thumb_max_width - left_thumb_width) / 2), - stripes->icon_size + - (((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) + - (((((float)height - ((stripes->icon_size / 6) * 2) - stripes->icon_size) / 2) - - left_thumb_height) / 2) + left_thumb_height, - left_thumb_width, left_thumb_height, - stripes->left_thumbnail); - } - } - } - menu_display_blend_end(video_info); /* Vertical icons */ From d1695c043c834f9e736f673befffdffda995bb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 13:38:51 +0700 Subject: [PATCH 008/106] [stripes] Compute categories y position --- menu/drivers/stripes.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 8987c29dd2..246bc9106c 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -234,9 +234,13 @@ typedef struct stripes_handle 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_active_zoom; @@ -1705,6 +1709,7 @@ static void stripes_list_switch_horizontal_list(stripes_handle_t *stripes) menu_animation_ctx_entry_t entry; float ia = stripes->categories_passive_alpha; float iz = stripes->categories_passive_zoom; + float ix = stripes->categories_before_x; float iy = stripes->categories_before_y; stripes_node_t *node = stripes_get_node(stripes, j); @@ -1715,14 +1720,17 @@ static void stripes_list_switch_horizontal_list(stripes_handle_t *stripes) { ia = stripes->categories_active_alpha; iz = stripes->categories_active_zoom; + 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; } @@ -3104,10 +3112,16 @@ static void stripes_layout_ps3(stripes_handle_t *stripes, int width, int height) stripes->categories_active_zoom = 1.0; stripes->categories_passive_zoom = 0.5; + stripes->categories_angle = 400; + stripes->categories_active_y = height / 2; stripes->categories_before_y = 64; stripes->categories_after_y = height - 64; + stripes->categories_active_x = stripes->categories_angle / 2; + stripes->categories_before_x = stripes->categories_angle - 22; + stripes->categories_after_x = 22; + stripes->items_active_zoom = 1.0; stripes->items_passive_zoom = 0.5; From 34966e2ffcb45a5591317c7f728a828d88785ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 15:22:32 +0700 Subject: [PATCH 009/106] [stripes] Compute and animate category width --- menu/drivers/stripes.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 246bc9106c..777b53a476 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -87,6 +87,7 @@ typedef struct float zoom; float x; float y; + float width; uintptr_t icon; uintptr_t content_icon; char *fullpath; @@ -243,8 +244,10 @@ typedef struct stripes_handle 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; @@ -1709,6 +1712,7 @@ static void stripes_list_switch_horizontal_list(stripes_handle_t *stripes) 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); @@ -1720,6 +1724,7 @@ static void stripes_list_switch_horizontal_list(stripes_handle_t *stripes) { 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; } @@ -1753,6 +1758,11 @@ static void stripes_list_switch_horizontal_list(stripes_handle_t *stripes) entry.subject = &node->y; menu_animation_push(&entry); + + entry.target_value = iw; + entry.subject = &node->width; + + menu_animation_push(&entry); } } @@ -1772,7 +1782,7 @@ static void stripes_list_switch(stripes_handle_t *stripes) stripes_list_switch_horizontal_list(stripes); anim_entry.duration = STRIPES_DELAY; - anim_entry.target_value = stripes->icon_spacing_horizontal + 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; @@ -1952,7 +1962,7 @@ static void stripes_context_reset_horizontal_list( stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL); stripes->categories_x_pos = - stripes->icon_spacing_horizontal * + stripes->categories_passive_width * -(float)stripes->categories_selection_ptr; depth = (stripes->depth > 1) ? 2 : 1; @@ -2919,6 +2929,7 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) 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(); @@ -2982,12 +2993,9 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) menu_display_ctx_rotate_draw_t rotate_draw; math_matrix_4x4 mymat; uintptr_t texture = node->icon; - float x = stripes->x + stripes->categories_x_pos + - stripes->margins_screen_left + - stripes->icon_spacing_horizontal - * (i + 1) - stripes->icon_size / 2.0; - float y = node->y - + stripes->icon_size / 2.0; + float x = stack_width + stripes->categories_x_pos + 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; @@ -3014,6 +3022,8 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) &stripes_item_color[0], stripes->shadow_offset); } + + stack_width += node->width; } menu_display_blend_end(video_info); @@ -3122,6 +3132,9 @@ static void stripes_layout_ps3(stripes_handle_t *stripes, int width, int height) stripes->categories_before_x = stripes->categories_angle - 22; stripes->categories_after_x = 22; + stripes->categories_passive_width = 128; + stripes->categories_active_width = 1200; + stripes->items_active_zoom = 1.0; stripes->items_passive_zoom = 0.5; From 3f32a7f1b7582e4062de5032613dac7bee4c9b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 15:37:29 +0700 Subject: [PATCH 010/106] [stripes] Remove useless ribbon stuff --- menu/drivers/stripes.c | 45 ------------------------------------------ 1 file changed, 45 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 777b53a476..fe40341016 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -3332,49 +3332,6 @@ static void stripes_layout(stripes_handle_t *stripes) } } -static void stripes_ribbon_set_vertex(float *ribbon_verts, - unsigned idx, unsigned row, unsigned col) -{ - ribbon_verts[idx++] = ((float)col) / (STRIPES_RIBBON_COLS-1) * 2.0f - 1.0f; - ribbon_verts[idx++] = ((float)row) / (STRIPES_RIBBON_ROWS-1) * 2.0f - 1.0f; -} - -static void stripes_init_ribbon(stripes_handle_t * stripes) -{ - video_coords_t coords; - unsigned r, c, col; - unsigned i = 0; - video_coord_array_t *ca = menu_display_get_coords_array(); - unsigned vertices_total = STRIPES_RIBBON_VERTICES; - float *dummy = (float*)calloc(4 * vertices_total, sizeof(float)); - float *ribbon_verts = (float*)calloc(2 * vertices_total, sizeof(float)); - - /* Set up vertices */ - for (r = 0; r < STRIPES_RIBBON_ROWS - 1; r++) - { - for (c = 0; c < STRIPES_RIBBON_COLS; c++) - { - col = r % 2 ? STRIPES_RIBBON_COLS - c - 1 : c; - stripes_ribbon_set_vertex(ribbon_verts, i, r, col); - stripes_ribbon_set_vertex(ribbon_verts, i + 2, r + 1, col); - i += 4; - } - } - - coords.color = dummy; - coords.vertex = ribbon_verts; - coords.tex_coord = dummy; - coords.lut_tex_coord = dummy; - coords.vertices = vertices_total; - - video_coord_array_append(ca, &coords, coords.vertices); - - free(dummy); - free(ribbon_verts); -} - - - static void *stripes_init(void **userdata, bool video_is_threaded) { unsigned width, height; @@ -3483,8 +3440,6 @@ static void *stripes_init(void **userdata, bool video_is_threaded) if (stripes->horizontal_list) stripes_init_horizontal_list(stripes); - stripes_init_ribbon(stripes); - return menu; error: From f2e71615fb0ddcfe22a1361b6b0c8a03c8ee04c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 15:42:26 +0700 Subject: [PATCH 011/106] [stripes] Remove useless thumbnail stuff --- menu/drivers/stripes.c | 124 ----------------------------------------- 1 file changed, 124 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index fe40341016..b2ff0ed6bb 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -602,60 +602,6 @@ static void stripes_draw_icon( menu_display_draw(&draw, video_info); } -static void stripes_draw_thumbnail( - video_frame_info_t *video_info, - stripes_handle_t *stripes, float *color, - unsigned width, unsigned height, - float x, float y, - float w, float h, uintptr_t texture) -{ - menu_display_ctx_rotate_draw_t rotate_draw; - menu_display_ctx_draw_t draw; - struct video_coords coords; - math_matrix_4x4 mymat; - - 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); - - coords.vertices = 4; - coords.vertex = NULL; - coords.tex_coord = NULL; - coords.lut_tex_coord = NULL; - - draw.width = w; - draw.height = h; - 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 + stripes->shadow_offset; - draw.y = height - y - stripes->shadow_offset; - - menu_display_draw(&draw, video_info); - } - - coords.color = (const float*)color; - draw.x = x; - draw.y = height - y; - - menu_display_set_alpha(color, 1.0f); - - menu_display_draw(&draw, video_info); -} - static void stripes_draw_text( video_frame_info_t *video_info, stripes_handle_t *stripes, @@ -1211,76 +1157,6 @@ static void stripes_selection_pointer_changed( iy = stripes_item_y(stripes, i, selection); real_iy = iy + stripes->margins_screen_top; - if (i == selection) - { - unsigned depth = (unsigned)stripes_list_get_size(stripes, MENU_LIST_PLAIN); - unsigned stripes_system_tab = stripes_get_system_tab(stripes, (unsigned)stripes->categories_selection_ptr); - unsigned entry_type = menu_entry_get_type_new(&entry); - - ia = stripes->items_active_alpha; - iz = stripes->items_active_zoom; - if (!string_is_equal(thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) || !string_is_equal(lft_thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - if ((stripes_system_tab > STRIPES_SYSTEM_TAB_SETTINGS && depth == 1) || - (stripes_system_tab < STRIPES_SYSTEM_TAB_SETTINGS && depth == 4)) - { - if (!string_is_empty(entry.path)) - stripes_set_thumbnail_content(stripes, entry.path, 0 /* will be ignored */); - if (!string_is_equal(thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - stripes_update_thumbnail_path(stripes, i, 'R'); - stripes_update_thumbnail_image(stripes); - } - if (!string_is_equal(lft_thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - stripes_update_thumbnail_path(stripes, i, 'L'); - stripes_update_thumbnail_image(stripes); - } - } - else if (((entry_type == FILE_TYPE_IMAGE || entry_type == FILE_TYPE_IMAGEVIEWER || - entry_type == FILE_TYPE_RDB || entry_type == FILE_TYPE_RDB_ENTRY) - && stripes_system_tab <= STRIPES_SYSTEM_TAB_SETTINGS)) - { - if (!string_is_empty(entry.path)) - stripes_set_thumbnail_content(stripes, entry.path, 0 /* will be ignored */); - if (!string_is_equal(thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - stripes_update_thumbnail_path(stripes, i, 'R'); - stripes_update_thumbnail_image(stripes); - } - else if (!string_is_equal(lft_thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - stripes_update_thumbnail_path(stripes, i, 'L'); - stripes_update_thumbnail_image(stripes); - } - } - else if (filebrowser_get_type() != FILEBROWSER_NONE) - { - stripes_reset_thumbnail_content(stripes); - if (!string_is_equal(thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - stripes_update_thumbnail_path(stripes, i, 'R'); - stripes_update_thumbnail_image(stripes); - } - else if (!string_is_equal(lft_thumb_ident, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) - { - stripes_update_thumbnail_path(stripes, i, 'L'); - stripes_update_thumbnail_image(stripes); - } - } - } - stripes_update_savestate_thumbnail_path(stripes, i); - stripes_update_savestate_thumbnail_image(stripes); - } - if ( (!allow_animations) || (real_iy < -threshold || real_iy > height+threshold)) From a6e9107bd042b77e3b9ca0033eb010ddf448520d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 18:58:11 +0700 Subject: [PATCH 012/106] Add menu_display_draw_polygon --- menu/menu_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++ menu/menu_driver.h | 8 ++++++++ 2 files changed, 58 insertions(+) diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 0912114c17..d19d97ef03 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -711,6 +711,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 d36214da0a..1bfaab22bb 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -692,6 +692,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, From 2ef382455457299884c6209432ad3d81a6201556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 18:58:57 +0700 Subject: [PATCH 013/106] [stripe] Draw the stripes --- menu/drivers/stripes.c | 44 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index b2ff0ed6bb..da90b28521 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -1635,6 +1635,11 @@ static void stripes_list_switch_horizontal_list(stripes_handle_t *stripes) menu_animation_push(&entry); + entry.target_value = ix; + entry.subject = &node->x; + + menu_animation_push(&entry); + entry.target_value = iw; entry.subject = &node->width; @@ -2853,6 +2858,43 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) menu_display_rotate_z(&rotate_draw, video_info); menu_display_blend_begin(video_info); + float mycolor[16]= { + 0.50, 1.00, 1.00, 1.00, + 0.00, 0.50, 1.00, 1.00, + 0.50, 1.00, 1.00, 1.00, + 0.00, 0.50, 1.00, 1.00, + }; + + /* 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; + + 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, + &mycolor[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++) @@ -2869,7 +2911,7 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) menu_display_ctx_rotate_draw_t rotate_draw; math_matrix_4x4 mymat; uintptr_t texture = node->icon; - float x = stack_width + stripes->categories_x_pos + node->x + node->width / 2.0 + 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; From cef847721542158c490eb9c37812c7c9209ba3d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 20:47:49 +0700 Subject: [PATCH 014/106] [stripes] Rainbow colors --- menu/drivers/stripes.c | 55 +++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index da90b28521..02394096e0 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -325,6 +325,42 @@ static float stripes_item_color[] = { 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); @@ -2858,13 +2894,6 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) menu_display_rotate_z(&rotate_draw, video_info); menu_display_blend_begin(video_info); - float mycolor[16]= { - 0.50, 1.00, 1.00, 1.00, - 0.00, 0.50, 1.00, 1.00, - 0.50, 1.00, 1.00, 1.00, - 0.00, 0.50, 1.00, 1.00, - }; - /* Horizontal stripes */ for (i = 0; i <= stripes_list_get_size(stripes, MENU_LIST_HORIZONTAL) + stripes->system_tab_end; i++) @@ -2874,6 +2903,15 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) if (!node) continue; + float rgb[3]; + HSLToRGB(0.05*(float)i,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, + }; + menu_display_draw_polygon( video_info, stripes->categories_x_pos + stack_width, @@ -2884,9 +2922,8 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) video_info->height, stripes->categories_x_pos + stack_width + stripes->categories_angle + node->width, video_info->height, - video_info->width, video_info->height, - &mycolor[0]); + &color[0]); menu_display_blend_begin(video_info); From 4af704babe892e64fc3c14cc6f9023b6fd26457a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 20:55:53 +0700 Subject: [PATCH 015/106] [stripe] Use scale_factor to look nice of different screens --- menu/drivers/stripes.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 02394096e0..58bcb8b8c3 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -3077,24 +3077,24 @@ static void stripes_layout_ps3(stripes_handle_t *stripes, int width, int height) stripes->categories_active_zoom = 1.0; stripes->categories_passive_zoom = 0.5; - stripes->categories_angle = 400; + stripes->categories_angle = 400 * scale_factor; stripes->categories_active_y = height / 2; - stripes->categories_before_y = 64; - stripes->categories_after_y = height - 64; + 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; - stripes->categories_after_x = 22; + stripes->categories_before_x = stripes->categories_angle - 22 * scale_factor; + stripes->categories_after_x = 22 * scale_factor; - stripes->categories_passive_width = 128; - stripes->categories_active_width = 1200; + 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 = 0.85; + stripes->categories_passive_alpha = 1.0; stripes->items_active_alpha = 1.0; stripes->items_passive_alpha = 0.85; From 1189d84543ee5e219d14441b03eb33dd4b524533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 21:48:55 +0700 Subject: [PATCH 016/106] [stripes] Implement draw_bg --- menu/drivers/stripes.c | 78 ++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 52 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 58bcb8b8c3..6353ecf824 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -2733,59 +2733,37 @@ static void stripes_draw_bg( stripes_handle_t *stripes, video_frame_info_t *video_info, unsigned width, - unsigned height, - float alpha, - uintptr_t texture_id, - float *stripes_coord_black, - float *stripes_coord_white) + unsigned height) { menu_display_ctx_draw_t draw; + struct video_coords coords; - bool running = video_info->libretro_running; + 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, + }; - draw.x = 0; - draw.y = 0; - draw.texture = texture_id; - draw.width = width; - draw.height = height; - draw.color = &stripes_coord_black[0]; - draw.vertex = NULL; - draw.tex_coord = NULL; - draw.vertex_count = 4; - draw.prim_type = MENU_DISPLAY_PRIM_TRIANGLESTRIP; - draw.pipeline.id = 0; - draw.pipeline.active = stripes_shader_pipeline_active(video_info); + 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_set_viewport(video_info->width, video_info->height); - - { - uintptr_t texture = draw.texture; - - draw.color = &stripes_coord_white[0]; - - if (running) - menu_display_set_alpha(draw.color, stripes_coord_black[3]); - else - menu_display_set_alpha(draw.color, stripes_coord_white[3]); - - { - float override_opacity = video_info->menu_wallpaper_opacity; - bool add_opacity = false; - - draw.texture = texture; - menu_display_set_alpha(draw.color, stripes_coord_white[3]); - - if (draw.texture) - draw.color = &stripes_coord_white[0]; - - if (running || video_info->xmb_color_theme == XMB_THEME_WALLPAPER) - add_opacity = true; - - menu_display_draw_bg(&draw, video_info, add_opacity, override_opacity); - } - } - menu_display_draw(&draw, video_info); menu_display_blend_end(video_info); } @@ -2876,11 +2854,7 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) stripes, video_info, width, - height, - stripes->alpha, - stripes->textures.bg, - stripes_coord_black, - stripes_coord_white); + height); selection = menu_navigation_get_selection(); From 3a1e95428417b469e4d1ec599b93f5a780afbeb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Sun, 8 Apr 2018 21:55:11 +0700 Subject: [PATCH 017/106] [stripe] Fix icon sizes --- menu/drivers/stripes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 6353ecf824..adce62e543 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -3049,7 +3049,7 @@ static void stripes_layout_ps3(stripes_handle_t *stripes, int width, int height) stripes->under_item_offset = 5.0; stripes->categories_active_zoom = 1.0; - stripes->categories_passive_zoom = 0.5; + stripes->categories_passive_zoom = 0.25; stripes->categories_angle = 400 * scale_factor; @@ -3102,7 +3102,7 @@ static void stripes_layout_ps3(stripes_handle_t *stripes, int width, int height) stripes->margins_slice = 16; - stripes->icon_size = 128.0 * scale_factor; + stripes->icon_size = 256.0 * scale_factor; stripes->font_size = new_font_size; #ifdef STRIPES_DEBUG From 617793df40b704500a71acef7481741e893525b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Wed, 11 Apr 2018 21:17:37 +0700 Subject: [PATCH 018/106] [stripes] Better tab colors --- .vscode/c_cpp_properties.json | 3 ++- menu/drivers/stripes.c | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index c6f4ffd0eb..561af49173 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -5,7 +5,8 @@ "includePath": [ "/usr/include", "/usr/local/include", - "${workspaceRoot}" + "${workspaceRoot}", + "${workspaceRoot}/libretro-common/include" ], "defines": [], "intelliSenseMode": "clang-x64", diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index adce62e543..a66e092e23 100755 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -2878,12 +2878,12 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) continue; float rgb[3]; - HSLToRGB(0.05*(float)i,0.5,0.5, &rgb[0]) ; + HSLToRGB(0.07*(float)i,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, + 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( From 7c50550fc39822e99b9379b9bb761829ca7124ac Mon Sep 17 00:00:00 2001 From: alphanu1 <37101891+alphanu1@users.noreply.github.com> Date: Fri, 25 May 2018 12:26:11 +0100 Subject: [PATCH 019/106] Black screen fix --- gfx/display_servers/dispserv_x11.c | 5 ----- 1 file changed, 5 deletions(-) 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; From f8f620f2686e3d00992ad3f29386efbc63f147b6 Mon Sep 17 00:00:00 2001 From: DEX357 Date: Mon, 2 Jul 2018 20:01:03 +0200 Subject: [PATCH 020/106] Update msg_hash_pl.h --- intl/msg_hash_pl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index 9f672e1484..ee293ea0dc 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -1321,6 +1321,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, From 7a001a07aa88ddf07dd870755fad396a21f490e3 Mon Sep 17 00:00:00 2001 From: DEX357 Date: Mon, 2 Jul 2018 20:13:19 +0200 Subject: [PATCH 021/106] Update msg_hash_pl.h --- intl/msg_hash_pl.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index 9f672e1484..02bfce906f 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -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, @@ -1959,6 +1961,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 +2493,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." @@ -3576,6 +3584,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, From 4135300b0435923b7a0cdf48bcf2dff6a451630a Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Fri, 29 Jun 2018 16:19:41 -0700 Subject: [PATCH 022/106] fix(materialui): Assign scale_factor and rotation to draw --- menu/drivers/materialui.c | 2 ++ 1 file changed, 2 insertions(+) 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; From 5e1f991e32b397a85375ba840196251c0fa61567 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Fri, 29 Jun 2018 22:57:48 -0700 Subject: [PATCH 023/106] feat(Metal): initial xmb and glui menu support TODOs * menu shader pipeline effects * refactor / optimize graphics rendering --- gfx/common/metal/Context.h | 19 +- gfx/common/metal/Context.m | 187 +++- gfx/common/metal/MenuDisplay.h | 27 + gfx/common/metal/MenuDisplay.m | 257 +++++ gfx/common/metal/MetalRenderer.h | 10 - gfx/common/metal/PixelConverter+private.h | 13 - gfx/common/metal/PixelConverter.h | 20 - gfx/common/metal/PixelConverter.m | 49 - gfx/common/metal/PixelConverter.metal | 49 - gfx/common/metal/Renderer.h | 37 - gfx/common/metal/Renderer.m | 263 ----- gfx/common/metal/RendererCommon.m | 76 +- gfx/common/metal/ShaderTypes.h | 35 +- gfx/common/metal/Shaders.metal | 81 +- gfx/common/metal/TexturedView.h | 6 +- gfx/common/metal/TexturedView.m | 12 +- gfx/common/metal/View.h | 15 - gfx/common/metal/View.m | 1 + gfx/common/metal/metal_common.h | 3 +- gfx/common/metal_common.h | 22 +- gfx/common/metal_common.m | 949 ++++++++++++------ gfx/drivers/metal.m | 170 ++-- gfx/drivers_font/metal_raster_font.m | 236 +++-- griffin/griffin_objc.m | 3 +- menu/drivers_display/menu_display_metal.m | 102 +- .../RetroArch_Metal.xcodeproj/project.pbxproj | 246 ++++- 26 files changed, 1753 insertions(+), 1135 deletions(-) create mode 100644 gfx/common/metal/MenuDisplay.h create mode 100644 gfx/common/metal/MenuDisplay.m delete mode 100644 gfx/common/metal/MetalRenderer.h delete mode 100644 gfx/common/metal/PixelConverter+private.h delete mode 100644 gfx/common/metal/PixelConverter.h delete mode 100644 gfx/common/metal/PixelConverter.m delete mode 100644 gfx/common/metal/PixelConverter.metal delete mode 100644 gfx/common/metal/Renderer.h delete mode 100644 gfx/common/metal/Renderer.m diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index b410d8eff8..98f0101842 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -8,9 +8,14 @@ #import #import +#import "RendererCommon.h" -NS_ASSUME_NONNULL_BEGIN +@interface Texture : NSObject +@property (readonly) id texture; +@property (readonly) id sampler; +@end +/*! @brief Context contains the render state used by various components */ @interface Context : NSObject @property (readonly) id device; @@ -21,10 +26,12 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly) id nextDrawable; @property (readonly) id renderTexture; -+ (instancetype)newContextWithDevice:(id)d - layer:(CAMetalLayer *)layer - library:(id)l - commandQueue:(id)q; +- (instancetype)initWithDevice:(id)d + layer:(CAMetalLayer *)layer + library:(id)l; + +- (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter; +- (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst; /*! @brief begin marks the beginning of a frame */ - (void)begin; @@ -33,5 +40,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..f525617841 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -7,43 +7,178 @@ // #import "Context.h" +#import "Filter.h" #import -@interface Context() -{ - CAMetalLayer *_layer; - id _drawable; -} +@interface Texture() +@property (readwrite) id texture; +@property (readwrite) id sampler; +@end +@interface Context() +@property (readonly) id blitCommandBuffer; +- (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; + id _commandQueue; + CAMetalLayer *_layer; + id _drawable; + id _samplers[TEXTURE_FILTER_MIPMAP_NEAREST + 1]; + Filter *_filters[RPixelFormatCount]; // convert to bgra8888 + + id _blitCommandBuffer; } -- (id)nextDrawable { - if (_drawable == nil) { +- (instancetype)initWithDevice:(id)d + layer:(CAMetalLayer *)layer + library:(id)l +{ + if (self = [super init]) + { + _device = d; + _layer = layer; + _library = l; + _commandQueue = [_device newCommandQueue]; + + 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; + } + return self; +} + +- (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 +{ + 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; + } + + // TODO(sgc): mipmapping is not working + BOOL mipmapped = filter == TEXTURE_FILTER_MIPMAP_LINEAR || filter == TEXTURE_FILTER_MIPMAP_NEAREST; + + 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]; + } + + Texture *tex = [Texture new]; + tex.texture = t; + tex.sampler = _samplers[filter]; + + return tex; +} + +- (id)nextDrawable +{ + if (_drawable == nil) + { _drawable = _layer.nextDrawable; } return _drawable; } -- (id)renderTexture { +- (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.commandBuffer inBuf:src outTex:dst]; +} + +- (id)blitCommandBuffer +{ + if (!_blitCommandBuffer) + _blitCommandBuffer = [_commandQueue commandBuffer]; + return _blitCommandBuffer; +} + - (void)begin { assert(_commandBuffer == nil); @@ -53,9 +188,21 @@ - (void)end { assert(self->_commandBuffer != nil); + + if (_blitCommandBuffer) + { + // pending blits for mipmaps + [_blitCommandBuffer commit]; + [_blitCommandBuffer waitUntilCompleted]; + _blitCommandBuffer = nil; + } + [_commandBuffer commit]; _commandBuffer = nil; _drawable = nil; } @end + +@implementation Texture +@end diff --git a/gfx/common/metal/MenuDisplay.h b/gfx/common/metal/MenuDisplay.h new file mode 100644 index 0000000000..fb8bef3c62 --- /dev/null +++ b/gfx/common/metal/MenuDisplay.h @@ -0,0 +1,27 @@ +// +// Created by Stuart Carnie on 6/24/18. +// + +#import +#import "ShaderTypes.h" + +@class Context; +@class MetalDriver; + +@interface MenuDisplay : NSObject + +@property (readwrite) BOOL blend; +@property (readwrite) MTLClearColor clearColor; + +- (instancetype)initWithDriver:(MetalDriver *)driver; +- (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; ++ (const float *)defaultMatrix; + + +@end diff --git a/gfx/common/metal/MenuDisplay.m b/gfx/common/metal/MenuDisplay.m new file mode 100644 index 0000000000..0510f655ce --- /dev/null +++ b/gfx/common/metal/MenuDisplay.m @@ -0,0 +1,257 @@ +// +// 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 +{ + __weak MetalDriver *_driver; + Context *_context; + MTLClearColor _clearColor; + bool _clearNextRender; + Uniforms _uniforms; +} + +- (instancetype)initWithDriver:(MetalDriver *)driver +{ + if (self = [super init]) + { + _driver = driver; + _context = driver.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, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.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]; +} + ++ (const float *)defaultMatrix +{ + static matrix_float4x4 dummy; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + dummy = matrix_proj_ortho(0, 1, 0, 1); + }); + return &dummy; +} + +- (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; + } +} + +static INLINE void write_quad4(SpriteVertex *pv, + float x, float y, float width, float height, float scale, + float tex_x, float tex_y, float tex_width, float tex_height, + const float *color) +{ + unsigned i; + static const float strip[2 * 4] = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + }; + + float swidth = width * scale; + float sheight = height * scale; + + x += (width - swidth) / 2; + y += (height - sheight) / 2; + + for (i = 0; i < 4; i++) + { + pv[i].position = simd_make_float2(x + strip[2 * i + 0] * swidth, + y + strip[2 * i + 1] * sheight); + 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 = simd_make_float4(color[0], color[1], color[2], color[3]); + color += 4; + } +} + +static INLINE void write_quad4a(SpriteVertex *pv, + float x, float y, float width, float height, float scale, + float tex_x, float tex_y, float tex_width, float tex_height, + const float *color) +{ + unsigned i; + static const float vert[2 * 4] = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + }; + static const float tex[2 * 4] = { + 0.0f, 0.0f, + 1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f, + }; + + float swidth = width * scale; + float sheight = height * scale; + + x += (width - swidth) / 2; + y += (height - sheight) / 2; + + for (i = 0; i < 4; i++) + { + pv[i].position = simd_make_float2(x + vert[2 * i + 0] * swidth, + y + vert[2 * i + 1] * sheight); + pv[i].texCoord = simd_make_float2(tex_x + tex[2 * i + 0] * tex_width, + tex_y + tex[2 * i + 1] * tex_height); + pv[i].color = simd_make_float4(color[0], color[1], color[2], color[3]); + color += 4; + } +} + +- (void)draw:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video +{ + Texture *tex = (__bridge Texture *)(void *)draw->texture; + const float *vertex = draw->coords->vertex; + const float *tex_coord = draw->coords->tex_coord; + const float *color = draw->coords->color; + + if (!vertex) + vertex = MenuDisplay.defaultVertices; + if (!tex_coord) + tex_coord = MenuDisplay.defaultTexCoords; + if (!draw->coords->lut_tex_coord) + draw->coords->lut_tex_coord = MenuDisplay.defaultTexCoords; + + // TODO(sgc): is this necessary? + // if (!texture) + // texture = &vk->display.blank_texture; + if (!color) + color = MenuDisplay.defaultColor; + + assert(draw->coords->vertices <= 4); + SpriteVertex buf[4]; + SpriteVertex *pv = buf; + Uniforms *uniforms; + if (draw->coords->vertex == NULL) + { + write_quad4a(pv, + draw->x, + draw->y, + draw->width, + draw->height, + draw->scale_factor, + 0.0, 0.0, 1.0, 1.0, color); + + uniforms = _driver.viewportMVP; + } + else + { + for (unsigned i = 0; i < draw->coords->vertices; i++, pv++) + { + /* Y-flip. We're using top-left coordinates */ + pv->position = simd_make_float2(vertex[0], 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; + } + uniforms = &_uniforms; + } + + switch (draw->pipeline.id) + { +#ifdef HAVE_SHADERPIPELINE +#endif + default: + { + MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; + if (_clearNextRender) + { + rpd.colorAttachments[0].clearColor = _clearColor; + rpd.colorAttachments[0].loadAction = MTLLoadActionClear; + _clearNextRender = NO; + } + else + { + rpd.colorAttachments[0].loadAction = MTLLoadActionLoad; + } + rpd.colorAttachments[0].storeAction = MTLStoreActionStore; + rpd.colorAttachments[0].texture = _context.nextDrawable.texture; + + id cb = _context.commandBuffer; + + id rce = [cb renderCommandEncoderWithDescriptor:rpd]; + [rce setRenderPipelineState:[_driver getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; + [rce setVertexBytes:uniforms length:sizeof(*uniforms) atIndex:BufferIndexUniforms]; + [rce setVertexBytes:buf length:sizeof(buf) atIndex:BufferIndexPositions]; + [rce setFragmentTexture:tex.texture atIndex:TextureIndexColor]; + [rce setFragmentSamplerState:tex.sampler atIndex:SamplerIndexDraw]; + [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + [rce endEncoding]; + + break; + } + } +} +@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.m b/gfx/common/metal/RendererCommon.m index 4491e8ea44..b914c108d3 100644 --- a/gfx/common/metal/RendererCommon.m +++ b/gfx/common/metal/RendererCommon.m @@ -11,64 +11,62 @@ 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 matrix_proj_ortho(float left, float right, float top, float bottom) { float near = 0; float far = 1; - + float sx = 2 / (right - left); float sy = 2 / (top - bottom); float sz = 1 / (far - near); float tx = (right + left) / (left - right); 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 = {sx, 0, 0, 0}; + simd_float4 Q = {0, sy, 0, 0}; + simd_float4 R = {0, 0, sz, 0}; + simd_float4 S = {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..619442f50b 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,67 @@ 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; +} + +#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..5ed51728b3 100644 --- a/gfx/common/metal/TexturedView.h +++ b/gfx/common/metal/TexturedView.h @@ -4,9 +4,7 @@ #import "View.h" -@class Renderer; - -@interface TexturedView : NSObject +@interface TexturedView : NSObject @property (readonly) RPixelFormat format; @property (readonly) RTextureFilter filter; @@ -15,7 +13,7 @@ @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..830e8728b1 100644 --- a/gfx/common/metal/TexturedView.m +++ b/gfx/common/metal/TexturedView.m @@ -4,16 +4,13 @@ #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]; @@ -25,15 +22,14 @@ bool _pixelsDirty; } -- (instancetype)initWithDescriptor:(ViewDescriptor *)d renderer:(Renderer *)r +- (instancetype)initWithDescriptor:(ViewDescriptor *)d context:(Context *)c { self = [super init]; if (self) { - _renderer = r; _format = d.format; _bpp = RPixelFormatToBPP(_format); _filter = d.filter; - _context = r.context; + _context = c; _visible = YES; if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) { _drawState = ViewDrawStateEncoder; @@ -114,7 +110,7 @@ if (!_pixelsDirty) return; - [_renderer.conv convertFormat:_format from:_pixels to:_texture]; + [_context convertFormat:_format from:_pixels to:_texture]; _pixelsDirty = NO; } @@ -134,7 +130,7 @@ 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 { void *dst = _pixels.contents; diff --git a/gfx/common/metal/View.h b/gfx/common/metal/View.h index 5f390cbc34..7345a6bd7b 100644 --- a/gfx/common/metal/View.h +++ b/gfx/common/metal/View.h @@ -19,21 +19,6 @@ typedef NS_ENUM(NSInteger, ViewDrawState) 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; diff --git a/gfx/common/metal/View.m b/gfx/common/metal/View.m index 72cfeaca2c..db7c29bb32 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 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..821c405bcf 100644 --- a/gfx/common/metal_common.h +++ b/gfx/common/metal_common.h @@ -26,7 +26,7 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); #pragma mark - Classes -@interface FrameView : NSObject +@interface FrameView : NSObject @property (readonly) RPixelFormat format; @property (readonly) RTextureFilter filter; @@ -35,7 +35,6 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @property (readwrite) CGSize size; @property (readonly) ViewDrawState drawState; @property (readonly) struct video_shader* shader; - @property (readwrite) uint64_t frameCount; - (void)setFilteringIndex:(int)index smooth:(bool)smooth; @@ -63,17 +62,26 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @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) MenuDisplay* display; @property (readonly) Context* context; +@property (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)setShaderIndex:(NSUInteger)index; +- (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; -- (void)beginFrame; -- (void)drawViews; -- (void)endFrame; +- (id)getStockShader:(int)index blend:(bool)blend; /*! @brief setNeedsResize triggers a display resize */ - (void)setNeedsResize; diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 55e24128bb..7f84ecaf65 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,115 @@ } \ if (__y != nil) \ x = (__bridge __typeof__(x))(__bridge_retained void *)((NSObject *)__y); \ -} + } + +#pragma mark - private categories @interface FrameView() @property (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 (readonly) TexturedView *view; +- (instancetype)initWithContext:(Context *)context; @end @implementation MetalDriver { - id _device; - - Renderer *_renderer; FrameView *_frameView; - + MetalMenu *_menu; + video_info_t _video; + + dispatch_semaphore_t _inflightSemaphore; + id _device; + id _library; + Context *_context; + + CAMetalLayer *_layer; + + // render target layer state + id _t_pipelineState; + id _t_pipelineStateNoAlpha; + MTLRenderPassDescriptor *_t_rpd; + + id _samplerStateLinear; + id _samplerStateNearest; + + // + id _states[GFX_MAX_SHADERS][2]; + + // other state + Uniforms _uniforms; + Uniforms _viewportMVP; + BOOL _begin, _end; } -- (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]) + { + _inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT); + _device = MTLCreateSystemDefaultDevice(); + MetalView *view = (MetalView *)apple_platform.renderView; + view.device = _device; + view.delegate = self; + _layer = (CAMetalLayer *)view.layer; + + if (![self _initMetal]) + { + return nil; + } + + if (![self _initStates]) + { + return nil; + } + + _begin = NO; + _end = NO; + + _video = *video; _viewport = (video_viewport_t *)calloc(1, sizeof(video_viewport_t)); - _menu = [MetalMenu new]; + + _keepAspect = _video.force_aspect; + + gfx_ctx_mode_t mode = { + .width = _video.width, + .height = _video.height, + .fullscreen = _video.fullscreen, + }; + [apple_platform setVideoMode:mode]; + + *input = NULL; + *inputData = NULL; + + // menu display + _display = [[MenuDisplay alloc] initWithDriver:self]; + + // 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]; + } + + font_driver_init_osd((__bridge void *)self, false, video->is_threaded, FONT_DRIVER_RENDER_METAL_API); } return self; } @@ -66,60 +142,271 @@ - (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; + } + } + + { + 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]; + } + return YES; +} + +- (bool)_initStates +{ + 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); + + { + MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; + psd.label = @"stock_blend"; + + MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; + ca.pixelFormat = _layer.pixelFormat; + ca.blendingEnabled = NO; + ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + ca.destinationRGBBlendFactor = 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; + } + + 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; + } + } + return YES; +} + +- (void)_updateUniforms +{ + _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); +} + +#pragma mark - shaders + +- (id)getStockShader:(int)index blend:(bool)blend +{ + assert(index > 0 && index < GFX_MAX_SHADERS); + + return _states[index][blend ? 1 : 0]; +} + +- (void)setShaderIndex:(NSUInteger)index +{ + } #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); + } + } + + if (msg && *msg) + { + font_driver_render_msg(video_info, NULL, msg, NULL); + } + + [self _endFrame]; + } + + return YES; +} + +- (void)_beginFrame { video_driver_update_viewport(_viewport, NO, _keepAspect); - - [_renderer beginFrame]; + + assert(!_begin && !_end); + _begin = YES; + dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER); + [_context begin]; + [self _updateUniforms]; } -- (void)drawViews { - [_renderer drawViews]; -} - -- (void)endFrame +- (void)_drawViews:(video_frame_info_t *)video_info { - [_renderer endFrame]; + assert(_begin && !_end); + _begin = NO; + _end = YES; + + id cb = _context.commandBuffer; + cb.label = @"renderer cb"; + + // draw back buffer + [_frameView drawWithContext:_context]; + + id drawable = _context.nextDrawable; + _t_rpd.colorAttachments[0].texture = drawable.texture; + + if ((_frameView.drawState & ViewDrawStateEncoder) != 0) + { + id rce = [cb renderCommandEncoderWithDescriptor:_t_rpd]; + [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 endEncoding]; + } + +#if defined(HAVE_MENU) + if (_menu.enabled) + { + menu_driver_frame(video_info); + [_menu.view drawWithContext:_context]; + id rce = [cb renderCommandEncoderWithDescriptor:_t_rpd]; + [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]; + [rce endEncoding]; + } +#endif +} + +- (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)setNeedsResize @@ -127,30 +414,48 @@ // TODO(sgc): resize all drawables } +- (Uniforms *)viewportMVP +{ + return &_viewportMVP; +} + #pragma mark - MTKViewDelegate -- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { - RARCH_LOG("[MetalDriver] drawableSizeWillChange: %s\n", NSStringFromSize(size).UTF8String); +- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size +{ _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]; + _layer.drawableSize = size; video_driver_update_viewport(_viewport, NO, _keepAspect); + + _viewportMVP.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height); + _viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, _viewport->full_height, 0); } -- (void)drawInMTKView:(MTKView *)view { - +- (void)drawInMTKView:(MTKView *)view +{ + } @end @implementation MetalMenu { - Renderer *_renderer; + Context *_context; TexturedView *_view; BOOL _enabled; } +- (instancetype)initWithContext:(Context *)context +{ + if (self = [super init]) + { + _context = context; + } + return self; +} + - (void)setEnabled:(BOOL)enabled { if (_enabled == enabled) return; @@ -169,23 +474,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; } } @@ -218,14 +524,14 @@ typedef struct texture typedef struct ALIGN(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 +542,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 +600,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 +629,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 +643,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 +654,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 +677,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 +711,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 +739,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 +756,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 +771,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:(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; } @@ -503,8 +827,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 +850,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,104 +863,111 @@ 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++) { + for (unsigned i = 0; i < _shader->passes; i++) + { BOOL backBuffer = (_engine.pass[i].rt.view == nil); - - if (backBuffer) { + + if (backBuffer) + { rpd.colorAttachments[0].texture = _context.nextDrawable.texture; } - else { + else + { rpd.colorAttachments[0].texture = _engine.pass[i].rt.view; } - + id rce = [cb renderCommandEncoderWithDescriptor:rpd]; - if (firstPass) { - firstPass = NO; - } else { - [rce waitForFence:_fence beforeStages:MTLRenderStageVertex]; - } [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]; _texture = _engine.pass[i].rt.view; } - if (_texture == nil) { + if (_texture == nil) + { _drawState = ViewDrawStateContext; - } else { + } + else + { _drawState = ViewDrawStateAll; } } @@ -642,67 +975,74 @@ static vertex_t vertex_bytes[] = { - (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 +1052,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 +1082,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 +1159,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 +1183,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 +1215,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 +1232,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,60 +1275,65 @@ 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; } @@ -979,44 +1342,44 @@ static vertex_t vertex_bytes[] = { 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 +1394,10 @@ MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt) { case MTLPixelFormatRGBA8Unorm: return MTLPixelFormatBGRA8Unorm; - + case MTLPixelFormatRGBA8Unorm_sRGB: return MTLPixelFormatBGRA8Unorm_sRGB; - + default: return fmt; } diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index eb18c55670..8ba2ae7b5b 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -50,32 +50,13 @@ 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); - return (__bridge_retained void *)md; } @@ -85,42 +66,13 @@ 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) @@ -141,7 +93,7 @@ static bool metal_suppress_screensaver(void *data, bool enable) { bool enabled = enable; (void)data; - + return video_context_driver_suppress_screensaver(&enabled); } @@ -154,12 +106,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 +123,12 @@ 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 %dx%d\n", viewport_width, viewport_height); } static void metal_set_rotation(void *data, unsigned rotation) @@ -197,12 +149,12 @@ static bool metal_read_viewport(void *data, uint8_t *buffer, bool is_idle) #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, + // 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, @@ -218,36 +170,23 @@ 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, @@ -260,7 +199,7 @@ static void metal_set_video_mode(void *data, .height = height, .fullscreen = fullscreen, }; - + //[md setVideoMode:mode]; } @@ -268,7 +207,7 @@ static float metal_get_refresh_rate(void *data) { MetalDriver *md = (__bridge MetalDriver *)data; (void)md; - + return 0.0f; } @@ -281,27 +220,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 +258,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 +272,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 +303,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 +322,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, }; diff --git a/gfx/drivers_font/metal_raster_font.m b/gfx/drivers_font/metal_raster_font.m index f04ccb4b74..9123af24dc 100644 --- a/gfx/drivers_font/metal_raster_font.m +++ b/gfx/drivers_font/metal_raster_font.m @@ -40,10 +40,12 @@ Uniforms _uniforms; id _vert; + unsigned _capacity; + unsigned _offset; unsigned _vertices; } -@property (readwrite) MetalDriver *metal; +@property (weak, readwrite) MetalDriver *metal; @property (readonly) struct font_atlas *atlas; @property (readwrite) bool needsUpdate; @@ -55,26 +57,54 @@ @implementation MetalRaster +/* 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 + +#define ALIGN_CONSTANTS(size) ((size + kConstantAlignment - 1) & (~(kConstantAlignment - 1))) + - (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size { - if (self = [super init]) { + if (self = [super init]) + { if (metal == nil) return nil; _metal = metal; _context = metal.context; if (!font_renderer_create_default((const void **)&_font_driver, - &_font_data, font_path, font_size)) { + &_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 = ALIGN_CONSTANTS(_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 +113,12 @@ _texture = [_buffer newTextureWithDescriptor:td offset:0 bytesPerRow:_stride]; - _vert = [_context.device newBufferWithLength:sizeof(FontVertex) * 500 options:MTLResourceStorageModeManaged]; + _capacity = 12000; + _vert = [_context.device newBufferWithLength:sizeof(SpriteVertex) * + _capacity options:MTLResourceStorageModeManaged]; _needsUpdate = true; - if (![self _initializeState]) { + if (![self _initializeState]) + { return nil; } } @@ -98,11 +131,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,12 +151,13 @@ 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; } @@ -146,9 +180,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); @@ -167,19 +203,21 @@ { int delta_x = 0; - for (unsigned i = 0; i < length; i++) { + for (unsigned 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 +226,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 +249,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 +268,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 = roundf(posX * _metal.viewport->width); + int y = roundf((1.0f - posY) * _metal.viewport->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_width = 1.0f / _metal.viewport->width; float inv_win_height = 1.0f / _metal.viewport->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,33 +309,36 @@ 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)]; +- (void)_flush +{ + NSUInteger start = _offset * sizeof(SpriteVertex); + [_vert didModifyRange:NSMakeRange(start, sizeof(SpriteVertex) * _vertices)]; _rpd.colorAttachments[0].texture = _context.nextDrawable.texture; id cb = _context.commandBuffer; @@ -305,12 +346,14 @@ static INLINE void write_quad(FontVertex *pv, [rce pushDebugGroup:@"render fonts"]; [rce setRenderPipelineState:_state]; [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; - [rce setVertexBuffer:_vert offset:0 atIndex:BufferIndexPositions]; + [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 +366,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,11 +375,13 @@ 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) { + if (delim) + { unsigned msg_len = delim - msg; [self _renderLine:msg video:video @@ -348,7 +394,8 @@ static INLINE void write_quad(FontVertex *pv, msg += msg_len + 1; lines++; } - else { + else + { unsigned msg_len = strlen(msg); [self _renderLine:msg video:video @@ -378,7 +425,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 +435,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 +462,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 +510,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,16 +532,16 @@ 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]; 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/menu/drivers_display/menu_display_metal.m b/menu/drivers_display/menu_display_metal.m index 9e92e9b744..5184aefa51 100644 --- a/menu/drivers_display/menu_display_metal.m +++ b/menu/drivers_display/menu_display_metal.m @@ -17,31 +17,60 @@ #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; } 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) { } static void menu_display_metal_viewport(menu_display_ctx_draw_t *draw, - video_frame_info_t *video_info) + video_frame_info_t *video_info) { } @@ -49,56 +78,47 @@ static void menu_display_metal_restore_clear_color(void) { } -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/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj index 47e0a24049..8fd0f4dee9 100644 --- a/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 05269A6220ABF20500C29F1E /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05269A6120ABF20500C29F1E /* MetalKit.framework */; }; 05A8C7B420DB75A500FF7857 /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 05A8C74E20DB72F100FF7857 /* Shaders.metal */; }; - 05A8C7B520DB75A800FF7857 /* PixelConverter.metal in Sources */ = {isa = PBXBuildFile; fileRef = 05A8C75920DB72F100FF7857 /* PixelConverter.metal */; }; 05A8E23820A63CB40084ABDA /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A8E23720A63CB40084ABDA /* Metal.framework */; }; 05A8E23A20A63CED0084ABDA /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A8E23920A63CED0084ABDA /* IOSurface.framework */; }; 05A8E23C20A63CF50084ABDA /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A8E23B20A63CF50084ABDA /* QuartzCore.framework */; }; @@ -60,6 +59,17 @@ 055312AE20DE130A00C4D7F4 /* pixconv.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pixconv.c; sourceTree = ""; }; 055312AF20DE130A00C4D7F4 /* scaler_filter.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = scaler_filter.c; sourceTree = ""; }; 055312B020DE130A00C4D7F4 /* scaler.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = scaler.c; sourceTree = ""; }; + 0566C78420E49E6800BC768F /* video_frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video_frame.h; sourceTree = ""; }; + 0566C78620E49E6800BC768F /* vector_4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector_4.h; sourceTree = ""; }; + 0566C78720E49E6800BC768F /* vector_3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector_3.h; sourceTree = ""; }; + 0566C78820E49E6800BC768F /* vector_2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector_2.h; sourceTree = ""; }; + 0566C78920E49E6800BC768F /* matrix_3x3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = matrix_3x3.h; sourceTree = ""; }; + 0566C78A20E49E6800BC768F /* matrix_4x4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = matrix_4x4.h; sourceTree = ""; }; + 0566C78C20E49E6800BC768F /* scaler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scaler.h; sourceTree = ""; }; + 0566C78D20E49E6800BC768F /* pixconv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pixconv.h; sourceTree = ""; }; + 0566C78E20E49E6800BC768F /* scaler_int.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scaler_int.h; sourceTree = ""; }; + 0566C78F20E49E6800BC768F /* filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filter.h; sourceTree = ""; }; + 0566C79020E49E6800BC768F /* gl_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gl_capabilities.h; sourceTree = ""; }; 05A8C51B20DB72F000FF7857 /* menu_shader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = menu_shader.h; sourceTree = ""; }; 05A8C51D20DB72F000FF7857 /* menu_cbs_get_value.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = menu_cbs_get_value.c; sourceTree = ""; }; 05A8C51E20DB72F000FF7857 /* menu_cbs_sublabel.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = menu_cbs_sublabel.c; sourceTree = ""; }; @@ -279,26 +289,19 @@ 05A8C73920DB72F100FF7857 /* font_driver.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = font_driver.c; sourceTree = ""; }; 05A8C73C20DB72F100FF7857 /* vulkan_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vulkan_common.c; sourceTree = ""; }; 05A8C74420DB72F100FF7857 /* metal_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metal_common.h; sourceTree = ""; }; - 05A8C74620DB72F100FF7857 /* MetalRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetalRenderer.h; sourceTree = ""; }; - 05A8C74720DB72F100FF7857 /* PixelConverter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PixelConverter.h; sourceTree = ""; }; 05A8C74820DB72F100FF7857 /* metal_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metal_common.h; sourceTree = ""; }; 05A8C74920DB72F100FF7857 /* TexturedView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TexturedView.h; sourceTree = ""; }; - 05A8C74A20DB72F100FF7857 /* Renderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Renderer.h; sourceTree = ""; }; 05A8C74B20DB72F100FF7857 /* Context.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Context.h; sourceTree = ""; }; 05A8C74C20DB72F100FF7857 /* RendererCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RendererCommon.h; sourceTree = ""; }; - 05A8C74D20DB72F100FF7857 /* PixelConverter+private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PixelConverter+private.h"; sourceTree = ""; }; 05A8C74E20DB72F100FF7857 /* Shaders.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Shaders.metal; sourceTree = ""; }; 05A8C74F20DB72F100FF7857 /* View.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = View.h; sourceTree = ""; }; 05A8C75020DB72F100FF7857 /* Filter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Filter.m; sourceTree = ""; }; 05A8C75120DB72F100FF7857 /* ShaderTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShaderTypes.h; sourceTree = ""; }; - 05A8C75220DB72F100FF7857 /* PixelConverter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PixelConverter.m; sourceTree = ""; }; 05A8C75320DB72F100FF7857 /* Context.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Context.m; sourceTree = ""; }; 05A8C75420DB72F100FF7857 /* RendererCommon.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RendererCommon.m; sourceTree = ""; }; - 05A8C75520DB72F100FF7857 /* Renderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Renderer.m; sourceTree = ""; }; 05A8C75620DB72F100FF7857 /* TexturedView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TexturedView.m; sourceTree = ""; }; 05A8C75720DB72F100FF7857 /* Filter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Filter.h; sourceTree = ""; }; 05A8C75820DB72F100FF7857 /* View.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = View.m; sourceTree = ""; }; - 05A8C75920DB72F100FF7857 /* PixelConverter.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = PixelConverter.metal; sourceTree = ""; }; 05A8C75D20DB72F100FF7857 /* gl_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gl_common.c; sourceTree = ""; }; 05A8C75E20DB72F100FF7857 /* d3d_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = d3d_common.c; sourceTree = ""; }; 05A8C76320DB72F100FF7857 /* d3d10_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = d3d10_common.h; sourceTree = ""; }; @@ -352,6 +355,59 @@ 05A8E23720A63CB40084ABDA /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; 05A8E23920A63CED0084ABDA /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; }; 05A8E23B20A63CF50084ABDA /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 05C5D53320E3DD0900654EE4 /* input_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_types.h; sourceTree = ""; }; + 05C5D53420E3DD0900654EE4 /* input_remote.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_remote.c; sourceTree = ""; }; + 05C5D53720E3DD0900654EE4 /* nullinput.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nullinput.c; sourceTree = ""; }; + 05C5D53820E3DD0900654EE4 /* cocoa_input.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cocoa_input.h; sourceTree = ""; }; + 05C5D54120E3DD0900654EE4 /* sdl_input.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sdl_input.c; sourceTree = ""; }; + 05C5D54220E3DD0900654EE4 /* cocoa_input.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cocoa_input.c; sourceTree = ""; }; + 05C5D54C20E3DD0900654EE4 /* input_keymaps.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_keymaps.h; sourceTree = ""; }; + 05C5D54E20E3DD0900654EE4 /* blissbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = blissbox.h; sourceTree = ""; }; + 05C5D55420E3DD0900654EE4 /* GCExtendedGamepadSnapshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCExtendedGamepadSnapshot.h; sourceTree = ""; }; + 05C5D55520E3DD0900654EE4 /* GCControllerButtonInput.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCControllerButtonInput.h; sourceTree = ""; }; + 05C5D55620E3DD0900654EE4 /* GCGamepad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCGamepad.h; sourceTree = ""; }; + 05C5D55720E3DD0900654EE4 /* GCExtendedGamepad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCExtendedGamepad.h; sourceTree = ""; }; + 05C5D55820E3DD0900654EE4 /* GCGamepadSnapshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCGamepadSnapshot.h; sourceTree = ""; }; + 05C5D55920E3DD0900654EE4 /* GCControllerAxisInput.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCControllerAxisInput.h; sourceTree = ""; }; + 05C5D55A20E3DD0900654EE4 /* GCControllerDirectionPad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCControllerDirectionPad.h; sourceTree = ""; }; + 05C5D55B20E3DD0900654EE4 /* GameController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GameController.h; sourceTree = ""; }; + 05C5D55C20E3DD0900654EE4 /* GCController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCController.h; sourceTree = ""; }; + 05C5D55D20E3DD0900654EE4 /* GCControllerElement.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCControllerElement.h; sourceTree = ""; }; + 05C5D55E20E3DD0900654EE4 /* hid_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hid_types.h; sourceTree = ""; }; + 05C5D55F20E3DD0900654EE4 /* hid_driver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hid_driver.h; sourceTree = ""; }; + 05C5D56020E3DD0900654EE4 /* gamepad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gamepad.h; sourceTree = ""; }; + 05C5D56120E3DD0900654EE4 /* input_driver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_driver.h; sourceTree = ""; }; + 05C5D56320E3DD0900654EE4 /* keyboard_event_apple.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = keyboard_event_apple.c; sourceTree = ""; }; + 05C5D56620E3DD0900654EE4 /* keyboard_event_apple.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = keyboard_event_apple.h; sourceTree = ""; }; + 05C5D56A20E3DD0900654EE4 /* input_remapping.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_remapping.h; sourceTree = ""; }; + 05C5D56B20E3DD0900654EE4 /* input_mapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_mapper.h; sourceTree = ""; }; + 05C5D56C20E3DD0900654EE4 /* input_overlay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_overlay.h; sourceTree = ""; }; + 05C5D56D20E3DD0900654EE4 /* input_defines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_defines.h; sourceTree = ""; }; + 05C5D56F20E3DD0900654EE4 /* btstack_hid.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = btstack_hid.c; sourceTree = ""; }; + 05C5D57020E3DD0900654EE4 /* null_hid.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = null_hid.c; sourceTree = ""; }; + 05C5D57320E3DD0900654EE4 /* iohidmanager_hid.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = iohidmanager_hid.c; sourceTree = ""; }; + 05C5D57620E3DD0900654EE4 /* input_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_common.c; sourceTree = ""; }; + 05C5D57720E3DD0900654EE4 /* input_x11_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_x11_common.c; sourceTree = ""; }; + 05C5D57820E3DD0900654EE4 /* linux_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = linux_common.c; sourceTree = ""; }; + 05C5D57A20E3DD0900654EE4 /* hid_device_driver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hid_device_driver.h; sourceTree = ""; }; + 05C5D57B20E3DD0900654EE4 /* device_ds3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = device_ds3.c; sourceTree = ""; }; + 05C5D57C20E3DD0900654EE4 /* device_ds4.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = device_ds4.c; sourceTree = ""; }; + 05C5D57D20E3DD0900654EE4 /* hid_device_driver.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hid_device_driver.c; sourceTree = ""; }; + 05C5D57E20E3DD0900654EE4 /* device_wiiu_gca.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = device_wiiu_gca.c; sourceTree = ""; }; + 05C5D57F20E3DD0900654EE4 /* device_null.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = device_null.c; sourceTree = ""; }; + 05C5D58020E3DD0900654EE4 /* linux_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = linux_common.h; sourceTree = ""; }; + 05C5D58120E3DD0900654EE4 /* input_x11_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_x11_common.h; sourceTree = ""; }; + 05C5D58220E3DD0900654EE4 /* input_autodetect_builtin.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_autodetect_builtin.c; sourceTree = ""; }; + 05C5D58320E3DD0900654EE4 /* input_keymaps.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_keymaps.c; sourceTree = ""; }; + 05C5D58720E3DD0900654EE4 /* sdl_joypad.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sdl_joypad.c; sourceTree = ""; }; + 05C5D58820E3DD0900654EE4 /* null_joypad.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = null_joypad.c; sourceTree = ""; }; + 05C5D58D20E3DD0900654EE4 /* mfi_joypad.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = mfi_joypad.m; sourceTree = ""; }; + 05C5D59820E3DD0A00654EE4 /* hid_joypad.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hid_joypad.c; sourceTree = ""; }; + 05C5D59F20E3DD0A00654EE4 /* input_remote.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_remote.h; sourceTree = ""; }; + 05C5D5A020E3DD0A00654EE4 /* input_overlay.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_overlay.c; sourceTree = ""; }; + 05C5D5A120E3DD0A00654EE4 /* input_mapper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_mapper.c; sourceTree = ""; }; + 05C5D5A220E3DD0A00654EE4 /* input_remapping.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_remapping.c; sourceTree = ""; }; + 05C5D5A320E3DD0A00654EE4 /* input_driver.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_driver.c; sourceTree = ""; }; 05D7753120A55D2700646447 /* BaseConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = BaseConfig.xcconfig; sourceTree = ""; }; 05D7753320A5678300646447 /* griffin_cpp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = griffin_cpp.cpp; path = ../../griffin/griffin_cpp.cpp; sourceTree = ""; }; 05D7753420A5678400646447 /* griffin_glslang.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = griffin_glslang.cpp; path = ../../griffin/griffin_glslang.cpp; sourceTree = ""; }; @@ -373,6 +429,8 @@ 84DD5EB71A89F1C7007336C1 /* retroarch.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = retroarch.icns; path = ../../media/retroarch.icns; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = OSX/Info.plist; sourceTree = ""; }; 8D1107320486CEB800E47090 /* RetroArch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RetroArch.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A902040DE66D42F9EE47BFE3 /* MenuDisplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MenuDisplay.h; sourceTree = ""; }; + A902070F2C43F222FD56A95A /* MenuDisplay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MenuDisplay.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -410,6 +468,7 @@ 0538875020DE11A800769232 /* include */ = { isa = PBXGroup; children = ( + 0566C78320E49E6800BC768F /* gfx */, 0538875920DE11D300769232 /* libretro.h */, 0538875720DE11D300769232 /* retro_assert.h */, 0538875120DE11D200769232 /* retro_common_api.h */, @@ -443,6 +502,40 @@ path = scaler; sourceTree = ""; }; + 0566C78320E49E6800BC768F /* gfx */ = { + isa = PBXGroup; + children = ( + 0566C78520E49E6800BC768F /* math */, + 0566C78B20E49E6800BC768F /* scaler */, + 0566C78420E49E6800BC768F /* video_frame.h */, + 0566C79020E49E6800BC768F /* gl_capabilities.h */, + ); + path = gfx; + sourceTree = ""; + }; + 0566C78520E49E6800BC768F /* math */ = { + isa = PBXGroup; + children = ( + 0566C78620E49E6800BC768F /* vector_4.h */, + 0566C78720E49E6800BC768F /* vector_3.h */, + 0566C78820E49E6800BC768F /* vector_2.h */, + 0566C78920E49E6800BC768F /* matrix_3x3.h */, + 0566C78A20E49E6800BC768F /* matrix_4x4.h */, + ); + path = math; + sourceTree = ""; + }; + 0566C78B20E49E6800BC768F /* scaler */ = { + isa = PBXGroup; + children = ( + 0566C78C20E49E6800BC768F /* scaler.h */, + 0566C78D20E49E6800BC768F /* pixconv.h */, + 0566C78E20E49E6800BC768F /* scaler_int.h */, + 0566C78F20E49E6800BC768F /* filter.h */, + ); + path = scaler; + sourceTree = ""; + }; 05A8C51920DB72C200FF7857 /* Sources */ = { isa = PBXGroup; children = ( @@ -450,6 +543,7 @@ 05A8C59520DB72F000FF7857 /* frontend */, 05A8C5D420DB72F000FF7857 /* gfx */, 05A8C57120DB72F000FF7857 /* intl */, + 05C5D53220E3DD0900654EE4 /* input */, 05A8C51A20DB72F000FF7857 /* menu */, 05A8C5AD20DB72F000FF7857 /* ui */, ); @@ -861,13 +955,8 @@ 05A8C75720DB72F100FF7857 /* Filter.h */, 05A8C75020DB72F100FF7857 /* Filter.m */, 05A8C74820DB72F100FF7857 /* metal_common.h */, - 05A8C74620DB72F100FF7857 /* MetalRenderer.h */, - 05A8C74720DB72F100FF7857 /* PixelConverter.h */, - 05A8C75220DB72F100FF7857 /* PixelConverter.m */, - 05A8C75920DB72F100FF7857 /* PixelConverter.metal */, - 05A8C74D20DB72F100FF7857 /* PixelConverter+private.h */, - 05A8C74A20DB72F100FF7857 /* Renderer.h */, - 05A8C75520DB72F100FF7857 /* Renderer.m */, + A902040DE66D42F9EE47BFE3 /* MenuDisplay.h */, + A902070F2C43F222FD56A95A /* MenuDisplay.m */, 05A8C74C20DB72F100FF7857 /* RendererCommon.h */, 05A8C75420DB72F100FF7857 /* RendererCommon.m */, 05A8C74E20DB72F100FF7857 /* Shaders.metal */, @@ -947,6 +1036,131 @@ path = metal_shaders; sourceTree = ""; }; + 05C5D53220E3DD0900654EE4 /* input */ = { + isa = PBXGroup; + children = ( + 05C5D57520E3DD0900654EE4 /* common */, + 05C5D53520E3DD0900654EE4 /* drivers */, + 05C5D56E20E3DD0900654EE4 /* drivers_hid */, + 05C5D58420E3DD0900654EE4 /* drivers_joypad */, + 05C5D56220E3DD0900654EE4 /* drivers_keyboard */, + 05C5D54D20E3DD0900654EE4 /* include */, + 05C5D58220E3DD0900654EE4 /* input_autodetect_builtin.c */, + 05C5D56D20E3DD0900654EE4 /* input_defines.h */, + 05C5D5A320E3DD0A00654EE4 /* input_driver.c */, + 05C5D56120E3DD0900654EE4 /* input_driver.h */, + 05C5D58320E3DD0900654EE4 /* input_keymaps.c */, + 05C5D54C20E3DD0900654EE4 /* input_keymaps.h */, + 05C5D5A120E3DD0A00654EE4 /* input_mapper.c */, + 05C5D56B20E3DD0900654EE4 /* input_mapper.h */, + 05C5D5A020E3DD0A00654EE4 /* input_overlay.c */, + 05C5D56C20E3DD0900654EE4 /* input_overlay.h */, + 05C5D5A220E3DD0A00654EE4 /* input_remapping.c */, + 05C5D56A20E3DD0900654EE4 /* input_remapping.h */, + 05C5D53420E3DD0900654EE4 /* input_remote.c */, + 05C5D59F20E3DD0A00654EE4 /* input_remote.h */, + 05C5D53320E3DD0900654EE4 /* input_types.h */, + ); + name = input; + path = ../../input; + sourceTree = ""; + }; + 05C5D53520E3DD0900654EE4 /* drivers */ = { + isa = PBXGroup; + children = ( + 05C5D54220E3DD0900654EE4 /* cocoa_input.c */, + 05C5D53820E3DD0900654EE4 /* cocoa_input.h */, + 05C5D53720E3DD0900654EE4 /* nullinput.c */, + 05C5D54120E3DD0900654EE4 /* sdl_input.c */, + ); + path = drivers; + sourceTree = ""; + }; + 05C5D54D20E3DD0900654EE4 /* include */ = { + isa = PBXGroup; + children = ( + 05C5D55320E3DD0900654EE4 /* GameController */, + 05C5D54E20E3DD0900654EE4 /* blissbox.h */, + 05C5D56020E3DD0900654EE4 /* gamepad.h */, + 05C5D55F20E3DD0900654EE4 /* hid_driver.h */, + 05C5D55E20E3DD0900654EE4 /* hid_types.h */, + ); + path = include; + sourceTree = ""; + }; + 05C5D55320E3DD0900654EE4 /* GameController */ = { + isa = PBXGroup; + children = ( + 05C5D55B20E3DD0900654EE4 /* GameController.h */, + 05C5D55C20E3DD0900654EE4 /* GCController.h */, + 05C5D55920E3DD0900654EE4 /* GCControllerAxisInput.h */, + 05C5D55520E3DD0900654EE4 /* GCControllerButtonInput.h */, + 05C5D55A20E3DD0900654EE4 /* GCControllerDirectionPad.h */, + 05C5D55D20E3DD0900654EE4 /* GCControllerElement.h */, + 05C5D55720E3DD0900654EE4 /* GCExtendedGamepad.h */, + 05C5D55420E3DD0900654EE4 /* GCExtendedGamepadSnapshot.h */, + 05C5D55620E3DD0900654EE4 /* GCGamepad.h */, + 05C5D55820E3DD0900654EE4 /* GCGamepadSnapshot.h */, + ); + path = GameController; + sourceTree = ""; + }; + 05C5D56220E3DD0900654EE4 /* drivers_keyboard */ = { + isa = PBXGroup; + children = ( + 05C5D56320E3DD0900654EE4 /* keyboard_event_apple.c */, + 05C5D56620E3DD0900654EE4 /* keyboard_event_apple.h */, + ); + path = drivers_keyboard; + sourceTree = ""; + }; + 05C5D56E20E3DD0900654EE4 /* drivers_hid */ = { + isa = PBXGroup; + children = ( + 05C5D56F20E3DD0900654EE4 /* btstack_hid.c */, + 05C5D57020E3DD0900654EE4 /* null_hid.c */, + 05C5D57320E3DD0900654EE4 /* iohidmanager_hid.c */, + ); + path = drivers_hid; + sourceTree = ""; + }; + 05C5D57520E3DD0900654EE4 /* common */ = { + isa = PBXGroup; + children = ( + 05C5D57920E3DD0900654EE4 /* hid */, + 05C5D57620E3DD0900654EE4 /* input_common.c */, + 05C5D57720E3DD0900654EE4 /* input_x11_common.c */, + 05C5D58120E3DD0900654EE4 /* input_x11_common.h */, + 05C5D57820E3DD0900654EE4 /* linux_common.c */, + 05C5D58020E3DD0900654EE4 /* linux_common.h */, + ); + path = common; + sourceTree = ""; + }; + 05C5D57920E3DD0900654EE4 /* hid */ = { + isa = PBXGroup; + children = ( + 05C5D57B20E3DD0900654EE4 /* device_ds3.c */, + 05C5D57C20E3DD0900654EE4 /* device_ds4.c */, + 05C5D57F20E3DD0900654EE4 /* device_null.c */, + 05C5D57E20E3DD0900654EE4 /* device_wiiu_gca.c */, + 05C5D57D20E3DD0900654EE4 /* hid_device_driver.c */, + 05C5D57A20E3DD0900654EE4 /* hid_device_driver.h */, + ); + path = hid; + sourceTree = ""; + }; + 05C5D58420E3DD0900654EE4 /* drivers_joypad */ = { + isa = PBXGroup; + children = ( + 05C5D59820E3DD0A00654EE4 /* hid_joypad.c */, + 05C5D58D20E3DD0900654EE4 /* mfi_joypad.m */, + 05C5D58820E3DD0900654EE4 /* null_joypad.c */, + 05C5D58720E3DD0900654EE4 /* sdl_joypad.c */, + ); + path = drivers_joypad; + sourceTree = ""; + }; 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( @@ -1104,7 +1318,6 @@ buildActionMask = 2147483647; files = ( 05D7753720A567A700646447 /* griffin_glslang.cpp in Sources */, - 05A8C7B520DB75A800FF7857 /* PixelConverter.metal in Sources */, 05D7753520A567A400646447 /* griffin_cpp.cpp in Sources */, 509F0C9D1AA23AFC00619ECC /* griffin_objc.m in Sources */, 840222FC1A889EE2009AB261 /* griffin.c in Sources */, @@ -1146,6 +1359,7 @@ GCC_OPTIMIZATION_LEVEL = 0; INFOPLIST_FILE = "$(SRCROOT)/OSX/Info.plist"; INSTALL_PATH = "$(HOME)/Applications"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = YES; PRODUCT_BUNDLE_IDENTIFIER = "libretro.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = RetroArch; From 1c5d94aca19e447c0fd4bf7d813e7c7dab9361a4 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Fri, 29 Jun 2018 23:35:27 -0700 Subject: [PATCH 024/106] Improve performance; don't care about previous state ofcolor attachment Color attachment is being completely overwritten. --- gfx/common/metal/Context.h | 1 - gfx/common/metal_common.m | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index 98f0101842..d1ae25b9e7 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -20,7 +20,6 @@ @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; diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 7f84ecaf65..472b2703ca 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -882,8 +882,8 @@ static vertex_t vertex_bytes[] = { 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].clearColor = MTLClearColorMake(0, 0, 0, 1.0); + rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare; rpd.colorAttachments[0].storeAction = MTLStoreActionStore; for (unsigned i = 0; i < _shader->passes; i++) @@ -900,6 +900,9 @@ static vertex_t vertex_bytes[] = { } id rce = [cb renderCommandEncoderWithDescriptor:rpd]; +#if METAL_DEBUG + rce.label = [NSString stringWithFormat:@"pass %d", i]; +#endif [rce setRenderPipelineState:_engine.pass[i]._state]; From 4a101734a19a69d9d690159c6c144f2e0269d4ec Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Sat, 30 Jun 2018 10:30:43 -0700 Subject: [PATCH 025/106] More cleanup * use single render command encoder for display for better performance * Context handles all rendering and presentation --- gfx/common/metal/Context.h | 10 ++- gfx/common/metal/Context.m | 47 +++++++++-- gfx/common/metal/MenuDisplay.h | 1 + gfx/common/metal/MenuDisplay.m | 21 ++--- gfx/common/metal_common.h | 3 +- gfx/common/metal_common.m | 96 +++++++++++------------ gfx/drivers_font/metal_raster_font.m | 12 +-- menu/drivers_display/menu_display_metal.m | 7 ++ 8 files changed, 112 insertions(+), 85 deletions(-) diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index d1ae25b9e7..79577dfd22 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -20,10 +20,18 @@ @property (readonly) id device; @property (readonly) id library; + +/*! @brief Returns the command buffer used for pre-render work, + * such as mip maps for applying filters + * */ +@property (readonly) id blitCommandBuffer; + /*! @brief Returns the command buffer for the current frame */ @property (readonly) id commandBuffer; @property (readonly) id nextDrawable; -@property (readonly) id renderTexture; + +/*! @brief Main render encoder to back buffer */ +@property (readonly) id rce; - (instancetype)initWithDevice:(id)d layer:(CAMetalLayer *)layer diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index f525617841..cc77a484b2 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -16,18 +16,21 @@ @end @interface Context() -@property (readonly) id blitCommandBuffer; - (bool)_initConversionFilters; @end @implementation Context { + dispatch_semaphore_t _inflightSemaphore; id _commandQueue; CAMetalLayer *_layer; id _drawable; id _samplers[TEXTURE_FILTER_MIPMAP_NEAREST + 1]; Filter *_filters[RPixelFormatCount]; // convert to bgra8888 + // main render pass state + id _rce; + id _blitCommandBuffer; } @@ -37,6 +40,7 @@ { if (self = [super init]) { + _inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT); _device = d; _layer = layer; _library = l; @@ -63,10 +67,18 @@ if (![self _initConversionFilters]) return nil; + + if (![self _initMainState]) + return nil; } return self; } +- (bool)_initMainState +{ + return YES; +} + - (bool)_initConversionFilters { NSError *err = nil; @@ -158,18 +170,13 @@ 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.commandBuffer inBuf:src outTex:dst]; + [conv apply:self.blitCommandBuffer inBuf:src outTex:dst]; } - (id)blitCommandBuffer @@ -182,9 +189,22 @@ - (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].texture = self.nextDrawable.texture; + _rce = [_commandBuffer renderCommandEncoderWithDescriptor:rpd]; + } + return _rce; +} + - (void)end { assert(self->_commandBuffer != nil); @@ -197,7 +217,20 @@ _blitCommandBuffer = nil; } + if (_rce) + { + [_rce endEncoding]; + _rce = nil; + } + + __block dispatch_semaphore_t inflight = _inflightSemaphore; + [_commandBuffer addCompletedHandler:^(id _) { + dispatch_semaphore_signal(inflight); + }]; + + [_commandBuffer presentDrawable:self.nextDrawable]; [_commandBuffer commit]; + _commandBuffer = nil; _drawable = nil; } diff --git a/gfx/common/metal/MenuDisplay.h b/gfx/common/metal/MenuDisplay.h index fb8bef3c62..ff5347e38f 100644 --- a/gfx/common/metal/MenuDisplay.h +++ b/gfx/common/metal/MenuDisplay.h @@ -14,6 +14,7 @@ @property (readwrite) MTLClearColor clearColor; - (instancetype)initWithDriver:(MetalDriver *)driver; +- (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 diff --git a/gfx/common/metal/MenuDisplay.m b/gfx/common/metal/MenuDisplay.m index 0510f655ce..7bc93f2a1e 100644 --- a/gfx/common/metal/MenuDisplay.m +++ b/gfx/common/metal/MenuDisplay.m @@ -166,6 +166,11 @@ static INLINE void write_quad4a(SpriteVertex *pv, } } +- (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 { Texture *tex = (__bridge Texture *)(void *)draw->texture; @@ -225,30 +230,20 @@ static INLINE void write_quad4a(SpriteVertex *pv, #endif default: { - MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; if (_clearNextRender) { - rpd.colorAttachments[0].clearColor = _clearColor; - rpd.colorAttachments[0].loadAction = MTLLoadActionClear; + // TODO(sgc): draw quad to clear _clearNextRender = NO; } - else - { - rpd.colorAttachments[0].loadAction = MTLLoadActionLoad; - } - rpd.colorAttachments[0].storeAction = MTLStoreActionStore; - rpd.colorAttachments[0].texture = _context.nextDrawable.texture; + + id rce = _context.rce; - id cb = _context.commandBuffer; - - id rce = [cb renderCommandEncoderWithDescriptor:rpd]; [rce setRenderPipelineState:[_driver getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; [rce setVertexBytes:uniforms length:sizeof(*uniforms) atIndex:BufferIndexUniforms]; [rce setVertexBytes:buf length:sizeof(buf) atIndex:BufferIndexPositions]; [rce setFragmentTexture:tex.texture atIndex:TextureIndexColor]; [rce setFragmentSamplerState:tex.sampler atIndex:SamplerIndexDraw]; [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; - [rce endEncoding]; break; } diff --git a/gfx/common/metal_common.h b/gfx/common/metal_common.h index 821c405bcf..723ecab8da 100644 --- a/gfx/common/metal_common.h +++ b/gfx/common/metal_common.h @@ -46,7 +46,8 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @interface MetalMenu : NSObject -@property (nonatomic, readwrite) BOOL enabled; +@property (readonly) bool hasFrame; +@property (readwrite) bool enabled; @property (readwrite) float alpha; - (void)updateFrame:(void const *)source; diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 472b2703ca..bde736be08 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -52,7 +52,6 @@ video_info_t _video; - dispatch_semaphore_t _inflightSemaphore; id _device; id _library; Context *_context; @@ -62,7 +61,6 @@ // render target layer state id _t_pipelineState; id _t_pipelineStateNoAlpha; - MTLRenderPassDescriptor *_t_rpd; id _samplerStateLinear; id _samplerStateNearest; @@ -82,7 +80,6 @@ { if (self = [super init]) { - _inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT); _device = MTLCreateSystemDefaultDevice(); MetalView *view = (MetalView *)apple_platform.renderView; view.device = _device; @@ -199,13 +196,6 @@ } } - { - 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]; @@ -214,6 +204,7 @@ sd.magFilter = MTLSamplerMinMagFilterLinear; _samplerStateLinear = [_device newSamplerStateWithDescriptor:sd]; } + return YES; } @@ -230,7 +221,7 @@ { MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; - psd.label = @"stock_blend"; + psd.label = @"stock_no_blend"; MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; ca.pixelFormat = _layer.pixelFormat; @@ -252,7 +243,8 @@ 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) @@ -335,7 +327,6 @@ assert(!_begin && !_end); _begin = YES; - dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER); [_context begin]; [self _updateUniforms]; } @@ -346,18 +337,14 @@ _begin = NO; _end = YES; - id cb = _context.commandBuffer; - cb.label = @"renderer cb"; + id rce = _context.rce; // draw back buffer + [rce pushDebugGroup:@"core frame"]; [_frameView drawWithContext:_context]; - id drawable = _context.nextDrawable; - _t_rpd.colorAttachments[0].texture = drawable.texture; - if ((_frameView.drawState & ViewDrawStateEncoder) != 0) { - id rce = [cb renderCommandEncoderWithDescriptor:_t_rpd]; [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; [rce setRenderPipelineState:_t_pipelineStateNoAlpha]; if (_frameView.filter == RTextureFilterNearest) @@ -369,27 +356,31 @@ [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; } [_frameView drawWithEncoder:rce]; - [rce endEncoding]; } + [rce popDebugGroup]; #if defined(HAVE_MENU) if (_menu.enabled) { + [rce pushDebugGroup:@"menu"]; menu_driver_frame(video_info); - [_menu.view drawWithContext:_context]; - id rce = [cb renderCommandEncoderWithDescriptor:_t_rpd]; - [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; - [rce setRenderPipelineState:_t_pipelineState]; - if (_menu.view.filter == RTextureFilterNearest) + + if (_menu.hasFrame) { - [rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw]; + [_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]; } - else - { - [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; - } - [_menu.view drawWithEncoder:rce]; - [rce endEncoding]; + [rce popDebugGroup]; } #endif } @@ -398,14 +389,6 @@ { 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]; } @@ -444,7 +427,7 @@ { Context *_context; TexturedView *_view; - BOOL _enabled; + bool _enabled; } - (instancetype)initWithContext:(Context *)context @@ -456,14 +439,19 @@ return self; } -- (void)setEnabled:(BOOL)enabled +- (bool)hasFrame +{ + return _view != nil; +} + +- (void)setEnabled:(bool)enabled { if (_enabled == enabled) return; _enabled = enabled; _view.visible = enabled; } -- (BOOL)enabled +- (bool)enabled { return _enabled; } @@ -879,7 +867,7 @@ static vertex_t vertex_bytes[] = { } } - id cb = ctx.commandBuffer; + id cb = ctx.blitCommandBuffer; MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; // rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0); @@ -888,18 +876,20 @@ static vertex_t vertex_bytes[] = { 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; + rce = _context.rce; } else { rpd.colorAttachments[0].texture = _engine.pass[i].rt.view; + rce = [cb renderCommandEncoderWithDescriptor:rpd]; } - - id rce = [cb renderCommandEncoderWithDescriptor:rpd]; + #if METAL_DEBUG rce.label = [NSString stringWithFormat:@"pass %d", i]; #endif @@ -962,17 +952,19 @@ static vertex_t vertex_bytes[] = { [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 endEncoding]; + + if (!backBuffer) + { + [rce endEncoding]; + } + _texture = _engine.pass[i].rt.view; } + if (_texture == nil) - { _drawState = ViewDrawStateContext; - } else - { _drawState = ViewDrawStateAll; - } } - (void)_updateRenderTargets diff --git a/gfx/drivers_font/metal_raster_font.m b/gfx/drivers_font/metal_raster_font.m index 9123af24dc..dcf5e98e3e 100644 --- a/gfx/drivers_font/metal_raster_font.m +++ b/gfx/drivers_font/metal_raster_font.m @@ -32,7 +32,6 @@ id _buffer; id _texture; - MTLRenderPassDescriptor *_rpd; id _state; id _sampler; @@ -163,12 +162,6 @@ static const NSUInteger kConstantAlignment = 4; } } - { - _rpd = [MTLRenderPassDescriptor renderPassDescriptor]; - _rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare; - _rpd.colorAttachments[0].storeAction = MTLStoreActionStore; - } - { MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; sd.minFilter = MTLSamplerMinMagFilterLinear; @@ -339,10 +332,8 @@ static INLINE void write_quad6(SpriteVertex *pv, { NSUInteger start = _offset * sizeof(SpriteVertex); [_vert didModifyRange:NSMakeRange(start, sizeof(SpriteVertex) * _vertices)]; - _rpd.colorAttachments[0].texture = _context.nextDrawable.texture; - id cb = _context.commandBuffer; - id rce = [cb renderCommandEncoderWithDescriptor:_rpd]; + id rce = _context.rce; [rce pushDebugGroup:@"render fonts"]; [rce setRenderPipelineState:_state]; [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; @@ -351,7 +342,6 @@ static INLINE void write_quad6(SpriteVertex *pv, [rce setFragmentSamplerState:_sampler atIndex:SamplerIndexDraw]; [rce drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_vertices]; [rce popDebugGroup]; - [rce endEncoding]; _offset += _vertices; _vertices = 0; diff --git a/menu/drivers_display/menu_display_metal.m b/menu/drivers_display/menu_display_metal.m index 5184aefa51..b80702a72b 100644 --- a/menu/drivers_display/menu_display_metal.m +++ b/menu/drivers_display/menu_display_metal.m @@ -34,6 +34,7 @@ static void *menu_display_metal_get_default_mvp(video_frame_info_t *video_info) MetalDriver *md = GET_DRIVER(video_info); if (!md) return NULL; + return (void *)md.viewportMVP; } @@ -67,6 +68,11 @@ static void menu_display_metal_draw(menu_display_ctx_draw_t *draw, 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, @@ -76,6 +82,7 @@ static void menu_display_metal_viewport(menu_display_ctx_draw_t *draw, 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, From a0900ec433b0bf688850284a69685e750eb16f90 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Tue, 3 Jul 2018 22:28:33 -0700 Subject: [PATCH 026/106] feat(Metal): full xmb pipeline support Menu TODOs: * understand why ribbon does not look the same as GL * add clear support to `MenuDisplay` for glui --- gfx/common/metal/Context.h | 26 +- gfx/common/metal/Context.m | 208 +++++++++++-- gfx/common/metal/Filter.h | 4 +- gfx/common/metal/MenuDisplay.h | 5 +- gfx/common/metal/MenuDisplay.m | 223 ++++++-------- gfx/common/metal/RendererCommon.h | 10 + gfx/common/metal/RendererCommon.m | 8 +- gfx/common/metal/TexturedView.h | 12 +- gfx/common/metal/View.h | 6 +- gfx/common/metal/menu_pipeline.metal | 273 ++++++++++++++++++ gfx/common/metal_common.h | 38 +-- gfx/common/metal_common.m | 226 +++++++++++---- gfx/drivers_font/metal_raster_font.m | 55 ++-- menu/drivers_display/menu_display_metal.m | 2 +- pkg/apple/BaseConfig.xcconfig | 2 +- .../RetroArch_Metal.xcodeproj/project.pbxproj | 56 ++-- ui/drivers/cocoa/cocoa_common.m | 5 + ui/drivers/ui_cocoa.m | 4 +- 18 files changed, 858 insertions(+), 305 deletions(-) create mode 100644 gfx/common/metal/menu_pipeline.metal diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index 79577dfd22..d335e80510 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -11,27 +11,35 @@ #import "RendererCommon.h" @interface Texture : NSObject -@property (readonly) id texture; -@property (readonly) id sampler; +@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 (nonatomic, readonly) id device; +@property (nonatomic, readonly) id library; +@property (nonatomic, readwrite) MTLClearColor clearColor; /*! @brief Returns the command buffer used for pre-render work, * such as mip maps for applying filters * */ -@property (readonly) id blitCommandBuffer; +@property (nonatomic, readonly) id blitCommandBuffer; /*! @brief Returns the command buffer for the current frame */ -@property (readonly) id commandBuffer; -@property (readonly) id nextDrawable; +@property (nonatomic, readonly) id commandBuffer; +@property (nonatomic, readonly) id nextDrawable; /*! @brief Main render encoder to back buffer */ -@property (readonly) id rce; +@property (nonatomic, readonly) id rce; - (instancetype)initWithDevice:(id)d layer:(CAMetalLayer *)layer @@ -40,6 +48,8 @@ - (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter; - (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst; +- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length; + /*! @brief begin marks the beginning of a frame */ - (void)begin; diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index cc77a484b2..16e9d8142d 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -10,9 +10,22 @@ #import "Filter.h" #import +@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 (readwrite) id texture; -@property (readwrite) id sampler; +@property (nonatomic, readwrite) id texture; +@property (nonatomic, readwrite) id sampler; @end @interface Context() @@ -32,6 +45,10 @@ id _rce; id _blitCommandBuffer; + + NSUInteger _currentChain; + BufferChain *_chain[CHAIN_LENGTH]; + MTLClearColor _clearColor; } - (instancetype)initWithDevice:(id)d @@ -45,31 +62,39 @@ _layer = layer; _library = l; _commandQueue = [_device newCommandQueue]; + _clearColor = MTLClearColorMake(0, 0, 0, 1); + + { + MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; - MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; + sd.label = @"NEAREST"; + _samplers[TEXTURE_FILTER_NEAREST] = [d newSamplerStateWithDescriptor:sd]; - 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 = 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]; + 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 _initMainState]) return nil; + + for (int i = 0; i < CHAIN_LENGTH; i++) + { + _chain[i] = [[BufferChain alloc] initWithDevice:_device blockLen:65536]; + } } return self; } @@ -186,6 +211,12 @@ return _blitCommandBuffer; } +- (void)_nextChain +{ + _currentChain = (_currentChain + 1) % CHAIN_LENGTH; + [_chain[_currentChain] discard]; +} + - (void)begin { assert(_commandBuffer == nil); @@ -199,6 +230,8 @@ 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]; } @@ -207,7 +240,9 @@ - (void)end { - assert(self->_commandBuffer != nil); + assert(_commandBuffer != nil); + + [_chain[_currentChain] commitRanges]; if (_blitCommandBuffer) { @@ -233,9 +268,144 @@ _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..d40f0b8798 100644 --- a/gfx/common/metal/Filter.h +++ b/gfx/common/metal/Filter.h @@ -15,8 +15,8 @@ @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; diff --git a/gfx/common/metal/MenuDisplay.h b/gfx/common/metal/MenuDisplay.h index ff5347e38f..f467029d6e 100644 --- a/gfx/common/metal/MenuDisplay.h +++ b/gfx/common/metal/MenuDisplay.h @@ -10,8 +10,8 @@ @interface MenuDisplay : NSObject -@property (readwrite) BOOL blend; -@property (readwrite) MTLClearColor clearColor; +@property (nonatomic, readwrite) BOOL blend; +@property (nonatomic, readwrite) MTLClearColor clearColor; - (instancetype)initWithDriver:(MetalDriver *)driver; - (void)drawPipeline:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video; @@ -22,7 +22,6 @@ + (const float *)defaultVertices; + (const float *)defaultTexCoords; + (const float *)defaultColor; -+ (const float *)defaultMatrix; @end diff --git a/gfx/common/metal/MenuDisplay.m b/gfx/common/metal/MenuDisplay.m index 7bc93f2a1e..08b858364a 100644 --- a/gfx/common/metal/MenuDisplay.m +++ b/gfx/common/metal/MenuDisplay.m @@ -45,10 +45,10 @@ + (const float *)defaultTexCoords { static float dummy[] = { - 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f, }; return &dummy[0]; } @@ -64,17 +64,6 @@ return &dummy[0]; } -+ (const float *)defaultMatrix -{ - static matrix_float4x4 dummy; - - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - dummy = matrix_proj_ortho(0, 1, 0, 1); - }); - return &dummy; -} - - (void)setClearColor:(MTLClearColor)clearColor { _clearColor = clearColor; @@ -100,133 +89,103 @@ } } -static INLINE void write_quad4(SpriteVertex *pv, - float x, float y, float width, float height, float scale, - float tex_x, float tex_y, float tex_width, float tex_height, - const float *color) -{ - unsigned i; - static const float strip[2 * 4] = { - 0.0f, 1.0f, - 1.0f, 1.0f, - 0.0f, 0.0f, - 1.0f, 0.0f, - }; - - float swidth = width * scale; - float sheight = height * scale; - - x += (width - swidth) / 2; - y += (height - sheight) / 2; - - for (i = 0; i < 4; i++) - { - pv[i].position = simd_make_float2(x + strip[2 * i + 0] * swidth, - y + strip[2 * i + 1] * sheight); - 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 = simd_make_float4(color[0], color[1], color[2], color[3]); - color += 4; - } -} - -static INLINE void write_quad4a(SpriteVertex *pv, - float x, float y, float width, float height, float scale, - float tex_x, float tex_y, float tex_width, float tex_height, - const float *color) -{ - unsigned i; - static const float vert[2 * 4] = { - 0.0f, 1.0f, - 1.0f, 1.0f, - 0.0f, 0.0f, - 1.0f, 0.0f, - }; - static const float tex[2 * 4] = { - 0.0f, 0.0f, - 1.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, 1.0f, - }; - - float swidth = width * scale; - float sheight = height * scale; - - x += (width - swidth) / 2; - y += (height - sheight) / 2; - - for (i = 0; i < 4; i++) - { - pv[i].position = simd_make_float2(x + vert[2 * i + 0] * swidth, - y + vert[2 * i + 1] * sheight); - pv[i].texCoord = simd_make_float2(tex_x + tex[2 * i + 0] * tex_width, - tex_y + tex[2 * i + 1] * tex_height); - pv[i].color = simd_make_float4(color[0], color[1], color[2], color[3]); - color += 4; - } -} - - (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(_driver.viewport->full_width, _driver.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 { Texture *tex = (__bridge Texture *)(void *)draw->texture; - const float *vertex = draw->coords->vertex; - const float *tex_coord = draw->coords->tex_coord; - const float *color = draw->coords->color; + 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; - if (!vertex) - vertex = MenuDisplay.defaultVertices; - if (!tex_coord) - tex_coord = MenuDisplay.defaultTexCoords; - if (!draw->coords->lut_tex_coord) - draw->coords->lut_tex_coord = MenuDisplay.defaultTexCoords; - - // TODO(sgc): is this necessary? - // if (!texture) - // texture = &vk->display.blank_texture; - if (!color) - color = MenuDisplay.defaultColor; - - assert(draw->coords->vertices <= 4); - SpriteVertex buf[4]; - SpriteVertex *pv = buf; - Uniforms *uniforms; - if (draw->coords->vertex == NULL) + NSUInteger needed = draw->coords->vertices * sizeof(SpriteVertex); + BufferRange range; + if (![_context allocRange:&range length:needed]) { - write_quad4a(pv, - draw->x, - draw->y, - draw->width, - draw->height, - draw->scale_factor, - 0.0, 0.0, 1.0, 1.0, color); + 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.0 - vertex[1]); + vertex += 2; - uniforms = _driver.viewportMVP; - } - else - { - for (unsigned i = 0; i < draw->coords->vertices; i++, pv++) - { - /* Y-flip. We're using top-left coordinates */ - pv->position = simd_make_float2(vertex[0], 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; - } - uniforms = &_uniforms; + 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; + MTLViewport vp = { + .originX = draw->x, + .originY = _driver.viewport->full_height - draw->y - draw->height, + .width = draw->width, + .height = draw->height, + .znear = 0, + .zfar = 1, + }; + [rce setViewport:vp]; + switch (draw->pipeline.id) { -#ifdef HAVE_SHADERPIPELINE +#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:[_driver 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]; + break; + } #endif default: { @@ -235,15 +194,17 @@ static INLINE void write_quad4a(SpriteVertex *pv, // TODO(sgc): draw quad to clear _clearNextRender = NO; } - - id rce = _context.rce; [rce setRenderPipelineState:[_driver getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; - [rce setVertexBytes:uniforms length:sizeof(*uniforms) atIndex:BufferIndexUniforms]; - [rce setVertexBytes:buf length:sizeof(buf) atIndex:BufferIndexPositions]; + Uniforms uniforms = { + .projectionMatrix = draw->matrix_data ? *(matrix_float4x4 *)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:4]; + [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:vertexCount]; break; } diff --git a/gfx/common/metal/RendererCommon.h b/gfx/common/metal/RendererCommon.h index 8a443a2161..2661ace833 100644 --- a/gfx/common/metal/RendererCommon.h +++ b/gfx/common/metal/RendererCommon.h @@ -14,6 +14,16 @@ // 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 diff --git a/gfx/common/metal/RendererCommon.m b/gfx/common/metal/RendererCommon.m index b914c108d3..3751d20e2d 100644 --- a/gfx/common/metal/RendererCommon.m +++ b/gfx/common/metal/RendererCommon.m @@ -62,10 +62,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); - simd_float4 P = {sx, 0, 0, 0}; - simd_float4 Q = {0, sy, 0, 0}; - simd_float4 R = {0, 0, sz, 0}; - simd_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/TexturedView.h b/gfx/common/metal/TexturedView.h index 5ed51728b3..750dd6d7e5 100644 --- a/gfx/common/metal/TexturedView.h +++ b/gfx/common/metal/TexturedView.h @@ -6,12 +6,12 @@ @interface TexturedView : 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 (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; - (instancetype)initWithDescriptor:(ViewDescriptor *)td context:(Context *)c; diff --git a/gfx/common/metal/View.h b/gfx/common/metal/View.h index 7345a6bd7b..b8f62439a1 100644 --- a/gfx/common/metal/View.h +++ b/gfx/common/metal/View.h @@ -20,9 +20,9 @@ typedef NS_ENUM(NSInteger, ViewDrawState) }; @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/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_common.h b/gfx/common/metal_common.h index 723ecab8da..7b4dacda0d 100644 --- a/gfx/common/metal_common.h +++ b/gfx/common/metal_common.h @@ -28,14 +28,14 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @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; @@ -46,9 +46,9 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @interface MetalMenu : NSObject -@property (readonly) bool hasFrame; -@property (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; @@ -60,20 +60,20 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @interface MetalDriver : NSObject -@property (readonly) video_viewport_t* viewport; -@property (readwrite) bool keepAspect; -@property (readonly) MetalMenu* menu; -@property (readonly) FrameView* frameView; -@property (readonly) MenuDisplay* display; -@property (readonly) Context* context; -@property (readonly) Uniforms* viewportMVP; +@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) Context* context; +@property (nonatomic, readonly) Uniforms* viewportMVP; +@property (nonatomic, readonly) Uniforms* viewportMVPNormalized; - (instancetype)initWithVideo:(const video_info_t *)video input:(const input_driver_t **)input inputData:(void **)inputData; - (void)setVideo:(const video_info_t *)video; -- (void)setShaderIndex:(NSUInteger)index; - (bool)renderFrame:(const void *)data width:(unsigned)width height:(unsigned)height diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index bde736be08..01fb722b8d 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -32,7 +32,7 @@ @interface FrameView() -@property (readwrite) video_viewport_t *viewport; +@property (nonatomic, readwrite) video_viewport_t *viewport; - (instancetype)initWithDescriptor:(ViewDescriptor *)td context:(Context *)context; - (void)drawWithContext:(Context *)ctx; @@ -41,7 +41,7 @@ @end @interface MetalMenu() -@property (readonly) TexturedView *view; +@property (nonatomic, readonly) TexturedView *view; - (instancetype)initWithContext:(Context *)context; @end @@ -71,7 +71,7 @@ // other state Uniforms _uniforms; Uniforms _viewportMVP; - BOOL _begin, _end; + Uniforms _viewportMVPNormalized; } - (instancetype)initWithVideo:(const video_info_t *)video @@ -96,9 +96,6 @@ return nil; } - _begin = NO; - _end = NO; - _video = *video; _viewport = (video_viewport_t *)calloc(1, sizeof(video_viewport_t)); @@ -221,15 +218,15 @@ { MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; - psd.label = @"stock_no_blend"; + psd.label = @"stock"; MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; ca.pixelFormat = _layer.pixelFormat; ca.blendingEnabled = NO; - ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; - ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; psd.sampleCount = 1; psd.vertexDescriptor = vd; @@ -243,7 +240,7 @@ 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]; @@ -252,6 +249,114 @@ 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; } @@ -261,18 +366,44 @@ _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); } +- (void)_updateViewport:(CGSize)size +{ + _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); + + _viewportMVP.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height); + _viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, _viewport->full_height, 0); + _viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, 0, _viewport->full_height); + + _viewportMVPNormalized.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height); + _viewportMVPNormalized.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); +} + #pragma mark - shaders - (id)getStockShader:(int)index blend:(bool)blend { assert(index > 0 && index < GFX_MAX_SHADERS); - return _states[index][blend ? 1 : 0]; -} - -- (void)setShaderIndex:(NSUInteger)index -{ + 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]; } #pragma mark - video @@ -324,19 +455,12 @@ - (void)_beginFrame { video_driver_update_viewport(_viewport, NO, _keepAspect); - - assert(!_begin && !_end); - _begin = YES; [_context begin]; [self _updateUniforms]; } - (void)_drawViews:(video_frame_info_t *)video_info { - assert(_begin && !_end); - _begin = NO; - _end = YES; - id rce = _context.rce; // draw back buffer @@ -358,28 +482,37 @@ [_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); - - if (_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]; - } [rce popDebugGroup]; } #endif @@ -387,8 +520,6 @@ - (void)_endFrame { - assert(!_begin && _end); - _end = NO; [_context end]; } @@ -402,18 +533,16 @@ return &_viewportMVP; } +- (Uniforms *)viewportMVPNormalized +{ + return &_viewportMVPNormalized; +} + #pragma mark - MTKViewDelegate - (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { - _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); - - _viewportMVP.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height); - _viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, _viewport->full_height, 0); + [self _updateViewport:size]; } - (void)drawInMTKView:(MTKView *)view @@ -870,7 +999,6 @@ static vertex_t vertex_bytes[] = { id cb = ctx.blitCommandBuffer; MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; - // rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0); rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare; rpd.colorAttachments[0].storeAction = MTLStoreActionStore; diff --git a/gfx/drivers_font/metal_raster_font.m b/gfx/drivers_font/metal_raster_font.m index dcf5e98e3e..848e5970cb 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; @@ -44,36 +45,25 @@ unsigned _vertices; } -@property (weak, 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 -/* 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 - -#define ALIGN_CONSTANTS(size) ((size + kConstantAlignment - 1) & (~(kConstantAlignment - 1))) - -- (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 (driver == nil) return nil; - _metal = metal; - _context = metal.context; + _driver = driver; + _context = driver.context; if (!font_renderer_create_default((const void **)&_font_driver, &_font_data, font_path, font_size)) { @@ -83,7 +73,7 @@ static const NSUInteger kConstantAlignment = 4; _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); _atlas = _font_driver->get_atlas(_font_data); - _stride = ALIGN_CONSTANTS(_atlas->width); + _stride = MTL_ALIGN_BUFFER(_atlas->width); if (_stride == _atlas->width) { _buffer = [_context.device newBufferWithBytes:_atlas->buffer @@ -115,7 +105,6 @@ static const NSUInteger kConstantAlignment = 4; _capacity = 12000; _vert = [_context.device newBufferWithLength:sizeof(SpriteVertex) * _capacity options:MTLResourceStorageModeManaged]; - _needsUpdate = true; if (![self _initializeState]) { return nil; @@ -188,15 +177,14 @@ static const NSUInteger kConstantAlignment = 4; [_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 ... */ @@ -262,14 +250,14 @@ static INLINE void write_quad6(SpriteVertex *pv, 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 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) { @@ -335,8 +323,19 @@ static INLINE void write_quad6(SpriteVertex *pv, id rce = _context.rce; [rce pushDebugGroup:@"render fonts"]; + + MTLViewport vp = { + .originX = 0, + .originY = 0, + .width = _driver.viewport->full_width, + .height = _driver.viewport->full_height, + .znear = 0, + .zfar = 1, + }; + [rce setViewport:vp]; + [rce setRenderPipelineState:_state]; - [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; + [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]; @@ -386,7 +385,7 @@ static INLINE void write_quad6(SpriteVertex *pv, } else { - unsigned msg_len = strlen(msg); + NSUInteger msg_len = strlen(msg); [self _renderLine:msg video:video length:msg_len diff --git a/menu/drivers_display/menu_display_metal.m b/menu/drivers_display/menu_display_metal.m index b80702a72b..beb57553ee 100644 --- a/menu/drivers_display/menu_display_metal.m +++ b/menu/drivers_display/menu_display_metal.m @@ -35,7 +35,7 @@ static void *menu_display_metal_get_default_mvp(video_frame_info_t *video_info) if (!md) return NULL; - return (void *)md.viewportMVP; + return (void *)&md.viewportMVPNormalized->projectionMatrix; } static void menu_display_metal_blend_begin(video_frame_info_t *video_info) 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/RetroArch_Metal.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj index 8fd0f4dee9..e8bf2b08b3 100644 --- a/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 05269A6220ABF20500C29F1E /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05269A6120ABF20500C29F1E /* MetalKit.framework */; }; + 05770B9920E805160013DABC /* menu_pipeline.metal in Sources */ = {isa = PBXBuildFile; fileRef = 05770B9820E805160013DABC /* menu_pipeline.metal */; }; 05A8C7B420DB75A500FF7857 /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 05A8C74E20DB72F100FF7857 /* Shaders.metal */; }; 05A8E23820A63CB40084ABDA /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A8E23720A63CB40084ABDA /* Metal.framework */; }; 05A8E23A20A63CED0084ABDA /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A8E23920A63CED0084ABDA /* IOSurface.framework */; }; @@ -70,6 +71,7 @@ 0566C78E20E49E6800BC768F /* scaler_int.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scaler_int.h; sourceTree = ""; }; 0566C78F20E49E6800BC768F /* filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filter.h; sourceTree = ""; }; 0566C79020E49E6800BC768F /* gl_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gl_capabilities.h; sourceTree = ""; }; + 05770B9820E805160013DABC /* menu_pipeline.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = menu_pipeline.metal; sourceTree = ""; }; 05A8C51B20DB72F000FF7857 /* menu_shader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = menu_shader.h; sourceTree = ""; }; 05A8C51D20DB72F000FF7857 /* menu_cbs_get_value.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = menu_cbs_get_value.c; sourceTree = ""; }; 05A8C51E20DB72F000FF7857 /* menu_cbs_sublabel.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = menu_cbs_sublabel.c; sourceTree = ""; }; @@ -826,7 +828,6 @@ children = ( 05A8C64120DB72F000FF7857 /* d3d_shaders */, 05A8C60020DB72F000FF7857 /* gl_shaders */, - 05A8C7B320DB756F00FF7857 /* metal_shaders */, 05A8C62220DB72F000FF7857 /* vulkan_shaders */, 05A8C63E20DB72F000FF7857 /* d3d10.c */, 05A8C5F020DB72F000FF7857 /* gl.c */, @@ -876,33 +877,33 @@ 05A8C62220DB72F000FF7857 /* vulkan_shaders */ = { isa = PBXGroup; children = ( - 05A8C62320DB72F000FF7857 /* pipeline_snow_simple.vert */, - 05A8C62420DB72F000FF7857 /* opaque.vert */, - 05A8C62520DB72F000FF7857 /* alpha_blend.frag */, - 05A8C62620DB72F000FF7857 /* pipeline_snow.frag */, - 05A8C62720DB72F000FF7857 /* pipeline_ribbon.vert */, - 05A8C62820DB72F000FF7857 /* opaque.vert.inc */, - 05A8C62920DB72F000FF7857 /* alpha_blend.frag.inc */, - 05A8C62A20DB72F000FF7857 /* pipeline_ribbon.frag.inc */, 05A8C62B20DB72F000FF7857 /* Makefile */, - 05A8C62C20DB72F000FF7857 /* pipeline_bokeh.frag.inc */, - 05A8C62D20DB72F000FF7857 /* font.frag.inc */, - 05A8C62E20DB72F000FF7857 /* pipeline_snow.frag.inc */, - 05A8C62F20DB72F000FF7857 /* pipeline_ribbon_simple.vert */, - 05A8C63020DB72F000FF7857 /* pipeline_snow_simple.frag.inc */, - 05A8C63120DB72F000FF7857 /* pipeline_ribbon_simple.frag.inc */, - 05A8C63220DB72F000FF7857 /* pipeline_ribbon_simple.vert.inc */, - 05A8C63320DB72F000FF7857 /* pipeline_ribbon.frag */, - 05A8C63420DB72F000FF7857 /* pipeline_snow_simple.vert.inc */, + 05A8C62520DB72F000FF7857 /* alpha_blend.frag */, 05A8C63520DB72F000FF7857 /* font.frag */, - 05A8C63620DB72F000FF7857 /* alpha_blend.vert */, - 05A8C63720DB72F000FF7857 /* pipeline_snow_simple.frag */, 05A8C63820DB72F000FF7857 /* opaque.frag */, - 05A8C63920DB72F000FF7857 /* pipeline_ribbon.vert.inc */, - 05A8C63A20DB72F000FF7857 /* alpha_blend.vert.inc */, - 05A8C63B20DB72F000FF7857 /* opaque.frag.inc */, 05A8C63C20DB72F000FF7857 /* pipeline_bokeh.frag */, 05A8C63D20DB72F000FF7857 /* pipeline_ribbon_simple.frag */, + 05A8C63320DB72F000FF7857 /* pipeline_ribbon.frag */, + 05A8C63720DB72F000FF7857 /* pipeline_snow_simple.frag */, + 05A8C62620DB72F000FF7857 /* pipeline_snow.frag */, + 05A8C62920DB72F000FF7857 /* alpha_blend.frag.inc */, + 05A8C63A20DB72F000FF7857 /* alpha_blend.vert.inc */, + 05A8C62D20DB72F000FF7857 /* font.frag.inc */, + 05A8C63B20DB72F000FF7857 /* opaque.frag.inc */, + 05A8C62820DB72F000FF7857 /* opaque.vert.inc */, + 05A8C62C20DB72F000FF7857 /* pipeline_bokeh.frag.inc */, + 05A8C63120DB72F000FF7857 /* pipeline_ribbon_simple.frag.inc */, + 05A8C63220DB72F000FF7857 /* pipeline_ribbon_simple.vert.inc */, + 05A8C62A20DB72F000FF7857 /* pipeline_ribbon.frag.inc */, + 05A8C63920DB72F000FF7857 /* pipeline_ribbon.vert.inc */, + 05A8C63020DB72F000FF7857 /* pipeline_snow_simple.frag.inc */, + 05A8C63420DB72F000FF7857 /* pipeline_snow_simple.vert.inc */, + 05A8C62E20DB72F000FF7857 /* pipeline_snow.frag.inc */, + 05A8C63620DB72F000FF7857 /* alpha_blend.vert */, + 05A8C62420DB72F000FF7857 /* opaque.vert */, + 05A8C62F20DB72F000FF7857 /* pipeline_ribbon_simple.vert */, + 05A8C62720DB72F000FF7857 /* pipeline_ribbon.vert */, + 05A8C62320DB72F000FF7857 /* pipeline_snow_simple.vert */, ); path = vulkan_shaders; sourceTree = ""; @@ -960,6 +961,7 @@ 05A8C74C20DB72F100FF7857 /* RendererCommon.h */, 05A8C75420DB72F100FF7857 /* RendererCommon.m */, 05A8C74E20DB72F100FF7857 /* Shaders.metal */, + 05770B9820E805160013DABC /* menu_pipeline.metal */, 05A8C75120DB72F100FF7857 /* ShaderTypes.h */, 05A8C74920DB72F100FF7857 /* TexturedView.h */, 05A8C75620DB72F100FF7857 /* TexturedView.m */, @@ -1029,13 +1031,6 @@ path = drivers_font_renderer; sourceTree = ""; }; - 05A8C7B320DB756F00FF7857 /* metal_shaders */ = { - isa = PBXGroup; - children = ( - ); - path = metal_shaders; - sourceTree = ""; - }; 05C5D53220E3DD0900654EE4 /* input */ = { isa = PBXGroup; children = ( @@ -1318,6 +1313,7 @@ buildActionMask = 2147483647; files = ( 05D7753720A567A700646447 /* griffin_glslang.cpp in Sources */, + 05770B9920E805160013DABC /* menu_pipeline.metal in Sources */, 05D7753520A567A400646447 /* griffin_cpp.cpp in Sources */, 509F0C9D1AA23AFC00619ECC /* griffin_objc.m in Sources */, 840222FC1A889EE2009AB261 /* griffin.c in Sources */, diff --git a/ui/drivers/cocoa/cocoa_common.m b/ui/drivers/cocoa/cocoa_common.m index 451b8a412e..8ce30fe99a 100644 --- a/ui/drivers/cocoa/cocoa_common.m +++ b/ui/drivers/cocoa/cocoa_common.m @@ -55,6 +55,11 @@ { return YES; } + +- (BOOL)isFlipped +{ + return YES; +} @end #endif diff --git a/ui/drivers/ui_cocoa.m b/ui/drivers/ui_cocoa.m index b3173fe66d..b1d19e8000 100644 --- a/ui/drivers/ui_cocoa.m +++ b/ui/drivers/ui_cocoa.m @@ -297,7 +297,9 @@ static char** waiting_argv; } - (void)setVideoMode:(gfx_ctx_mode_t)mode { - // TODO(sgc): handle full screen + // TODO(sgc): handle full screen? + // cheap hack to ensure MTKView posts triggers a drawable resize event + [self.window setContentSize:NSMakeSize(mode.width-1, mode.height)]; [self.window setContentSize:NSMakeSize(mode.width, mode.height)]; } From 44ca1062b0f321407a53856748fe5487749001de Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Tue, 3 Jul 2018 22:31:51 -0700 Subject: [PATCH 027/106] formatting --- gfx/common/metal/Context.m | 12 +-- gfx/common/metal/Filter.h | 8 +- gfx/common/metal/Filter.m | 134 ++++++++++++++++-------------- gfx/common/metal/RendererCommon.h | 6 +- gfx/common/metal/RendererCommon.m | 22 ++--- gfx/common/metal/TexturedView.m | 61 ++++++++------ gfx/common/metal/View.h | 4 +- gfx/common/metal/View.m | 3 +- gfx/common/metal_common.h | 20 ++--- gfx/common/metal_common.m | 12 +-- 10 files changed, 154 insertions(+), 128 deletions(-) diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index 16e9d8142d..3e1ac87deb 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -63,28 +63,28 @@ _library = l; _commandQueue = [_device newCommandQueue]; _clearColor = MTLClearColorMake(0, 0, 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; diff --git a/gfx/common/metal/Filter.h b/gfx/common/metal/Filter.h index d40f0b8798..82094ad67f 100644 --- a/gfx/common/metal/Filter.h +++ b/gfx/common/metal/Filter.h @@ -10,7 +10,7 @@ #import @protocol FilterDelegate --(void)configure:(id)encoder; +- (void)configure:(id)encoder; @end @interface Filter : NSObject @@ -18,9 +18,9 @@ @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/RendererCommon.h b/gfx/common/metal/RendererCommon.h index 2661ace833..49c3f74c66 100644 --- a/gfx/common/metal/RendererCommon.h +++ b/gfx/common/metal/RendererCommon.h @@ -27,7 +27,8 @@ #pragma mark - Pixel Formats -typedef NS_ENUM(NSUInteger, RPixelFormat) { +typedef NS_ENUM(NSUInteger, RPixelFormat) +{ RPixelFormatInvalid, @@ -44,7 +45,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, diff --git a/gfx/common/metal/RendererCommon.m b/gfx/common/metal/RendererCommon.m index 3751d20e2d..aca474a401 100644 --- a/gfx/common/metal/RendererCommon.m +++ b/gfx/common/metal/RendererCommon.m @@ -11,15 +11,16 @@ NSUInteger RPixelFormatToBPP(RPixelFormat format) { - switch (format) { + 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; @@ -40,13 +41,14 @@ NSString *NSStringFromRPixelFormat(RPixelFormat format) STRING(RPixelFormatBGRA8Unorm); STRING(RPixelFormatBGRX8Unorm); #undef STRING - + }); - - if (format >= RPixelFormatCount) { + + if (format >= RPixelFormatCount) + { format = RPixelFormatInvalid; } - + return RPixelStrings[format]; } @@ -54,19 +56,19 @@ matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bott { float near = 0; float far = 1; - + float sx = 2 / (right - left); float sy = 2 / (top - bottom); float sz = 1 / (far - near); float tx = (right + left) / (left - right); float ty = (top + bottom) / (bottom - top); float tz = near / (far - near); - + 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/TexturedView.m b/gfx/common/metal/TexturedView.m index 830e8728b1..22ff31cf31 100644 --- a/gfx/common/metal/TexturedView.m +++ b/gfx/common/metal/TexturedView.m @@ -17,7 +17,7 @@ CGSize _size; // size of view in pixels CGRect _frame; NSUInteger _bpp; - + id _pixels; // frame buffer in _srcFmt bool _pixelsDirty; } @@ -25,15 +25,19 @@ - (instancetype)initWithDescriptor:(ViewDescriptor *)d context:(Context *)c { self = [super init]; - if (self) { + if (self) + { _format = d.format; _bpp = RPixelFormatToBPP(_format); _filter = d.filter; _context = c; _visible = YES; - if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) { + if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) + { _drawState = ViewDrawStateEncoder; - } else { + } + else + { _drawState = ViewDrawStateAll; } self.size = d.size; @@ -44,12 +48,13 @@ - (void)setSize:(CGSize)size { - if (CGSizeEqualToSize(_size, size)) { + if (CGSizeEqualToSize(_size, size)) + { return; } - + _size = size; - + // create new texture { MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm @@ -59,8 +64,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]; } @@ -73,21 +79,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}}, @@ -106,15 +113,16 @@ { if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) return; - + if (!_pixelsDirty) return; - + [_context convertFormat:_format from:_pixels to:_texture]; _pixelsDirty = NO; } -- (void)drawWithContext:(Context *)ctx { +- (void)drawWithContext:(Context *)ctx +{ [self _convertFormat]; } @@ -127,27 +135,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 * 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 b8f62439a1..ce1485a31a 100644 --- a/gfx/common/metal/View.h +++ b/gfx/common/metal/View.h @@ -12,11 +12,11 @@ typedef NS_ENUM(NSInteger, ViewDrawState) { - ViewDrawStateNone = 0x00, + ViewDrawStateNone = 0x00, ViewDrawStateContext = 0x01, ViewDrawStateEncoder = 0x02, - ViewDrawStateAll = 0x03, + ViewDrawStateAll = 0x03, }; @interface ViewDescriptor : NSObject diff --git a/gfx/common/metal/View.m b/gfx/common/metal/View.m index db7c29bb32..475983bc86 100644 --- a/gfx/common/metal/View.m +++ b/gfx/common/metal/View.m @@ -14,7 +14,8 @@ - (instancetype)init { self = [super init]; - if (self) { + if (self) + { _format = RPixelFormatBGRA8Unorm; } return self; diff --git a/gfx/common/metal_common.h b/gfx/common/metal_common.h index 7b4dacda0d..1ba4d73c77 100644 --- a/gfx/common/metal_common.h +++ b/gfx/common/metal_common.h @@ -34,8 +34,8 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @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; +@property (nonatomic, readonly) struct video_shader *shader; +@property (nonatomic, readwrite) uint64_t frameCount; - (void)setFilteringIndex:(int)index smooth:(bool)smooth; - (BOOL)setShaderFromPath:(NSString *)path; @@ -60,14 +60,14 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @interface MetalDriver : NSObject -@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) Context* context; -@property (nonatomic, readonly) Uniforms* viewportMVP; -@property (nonatomic, readonly) Uniforms* viewportMVPNormalized; +@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) Context *context; +@property (nonatomic, readonly) Uniforms *viewportMVP; +@property (nonatomic, readonly) Uniforms *viewportMVPNormalized; - (instancetype)initWithVideo:(const video_info_t *)video input:(const input_driver_t **)input diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 01fb722b8d..a975c06f7f 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -249,9 +249,9 @@ RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); return NO; } - + MTLFunctionConstantValues *vals; - + psd.label = @"snow_simple"; ca.blendingEnabled = YES; { @@ -272,7 +272,7 @@ RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); return NO; } - + psd.label = @"snow"; ca.blendingEnabled = YES; { @@ -293,7 +293,7 @@ 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"]; @@ -303,7 +303,7 @@ 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"]; @@ -313,7 +313,7 @@ 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"]; From 905e9e83c035549c119f540705fbdc571c586420 Mon Sep 17 00:00:00 2001 From: theheroGAC Date: Wed, 4 Jul 2018 08:27:33 +0200 Subject: [PATCH 028/106] Update msg_hash_it.h --- intl/msg_hash_it.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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, From 30d21923387612a4616a4bc667b295bff058506f Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Wed, 4 Jul 2018 00:16:14 -0700 Subject: [PATCH 029/106] fix(Metal): Fix release build crash for misaligned read --- gfx/common/metal/MenuDisplay.m | 5 +++-- gfx/common/metal/RendererCommon.h | 1 + gfx/common/metal/RendererCommon.m | 11 +++++++++++ gfx/common/metal_common.m | 4 ++-- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/gfx/common/metal/MenuDisplay.m b/gfx/common/metal/MenuDisplay.m index 08b858364a..9296a1e31a 100644 --- a/gfx/common/metal/MenuDisplay.m +++ b/gfx/common/metal/MenuDisplay.m @@ -148,7 +148,7 @@ SpriteVertex *pv = (SpriteVertex *)range.data; for (unsigned i = 0; i < draw->coords->vertices; i++, pv++) { - pv->position = simd_make_float2(vertex[0], 1.0 - vertex[1]); + pv->position = simd_make_float2(vertex[0], 1.0f - vertex[1]); vertex += 2; pv->texCoord = simd_make_float2(tex_coord[0], tex_coord[1]); @@ -196,8 +196,9 @@ } [rce setRenderPipelineState:[_driver getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; + Uniforms uniforms = { - .projectionMatrix = draw->matrix_data ? *(matrix_float4x4 *)draw->matrix_data + .projectionMatrix = draw->matrix_data ? make_matrix_float4x4((const float *)draw->matrix_data) : _uniforms.projectionMatrix }; [rce setVertexBytes:&uniforms length:sizeof(uniforms) atIndex:BufferIndexUniforms]; diff --git a/gfx/common/metal/RendererCommon.h b/gfx/common/metal/RendererCommon.h index 49c3f74c66..320a9cc8b0 100644 --- a/gfx/common/metal/RendererCommon.h +++ b/gfx/common/metal/RendererCommon.h @@ -54,5 +54,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 aca474a401..f3ed51db97 100644 --- a/gfx/common/metal/RendererCommon.m +++ b/gfx/common/metal/RendererCommon.m @@ -52,6 +52,17 @@ NSString *NSStringFromRPixelFormat(RPixelFormat format) return RPixelStrings[format]; } +matrix_float4x4 make_matrix_float4x4(const float *v) +{ + simd_float4 P = simd_make_float4(*v++, *v++, *v++, *v++); + simd_float4 Q = simd_make_float4(*v++, *v++, *v++, *v++); + simd_float4 R = simd_make_float4(*v++, *v++, *v++, *v++); + simd_float4 S = simd_make_float4(*v++, *v++, *v++, *v++); + + matrix_float4x4 mat = {P, Q, R, S}; + return mat; +} + matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bottom) { float near = 0; diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index a975c06f7f..b52fef8542 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -622,7 +622,7 @@ #pragma mark - FrameView -#define ALIGN(x) __attribute__((aligned(x))) +#define MTLALIGN(x) __attribute__((aligned(x))) typedef struct { @@ -638,7 +638,7 @@ typedef struct texture float4_t size_data; } texture_t; -typedef struct ALIGN(16) +typedef struct MTLALIGN(16) { matrix_float4x4 mvp; From 5240efc857df3a041147a78f9e4ab0cb78f20897 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Wed, 4 Jul 2018 00:24:04 -0700 Subject: [PATCH 030/106] chore(Metal): fix compiler warnings when targeting newer macOS versions --- gfx/drivers_font/metal_raster_font.m | 2 +- ui/drivers/cocoa/ui_cocoa_application.m | 8 ++- ui/drivers/cocoa/ui_cocoa_msg_window.m | 24 ++++++-- ui/drivers/ui_cocoa.m | 73 +++++++++++++++++-------- 4 files changed, 76 insertions(+), 31 deletions(-) diff --git a/gfx/drivers_font/metal_raster_font.m b/gfx/drivers_font/metal_raster_font.m index 848e5970cb..7ebf92f276 100644 --- a/gfx/drivers_font/metal_raster_font.m +++ b/gfx/drivers_font/metal_raster_font.m @@ -371,7 +371,7 @@ static INLINE void write_quad6(SpriteVertex *pv, /* Draw the line */ if (delim) { - unsigned msg_len = delim - msg; + NSUInteger msg_len = delim - msg; [self _renderLine:msg video:video length:msg_len diff --git a/ui/drivers/cocoa/ui_cocoa_application.m b/ui/drivers/cocoa/ui_cocoa_application.m index 2654db2c02..1f51257476 100644 --- a/ui/drivers/cocoa/ui_cocoa_application.m +++ b/ui/drivers/cocoa/ui_cocoa_application.m @@ -23,6 +23,10 @@ #include "cocoa_common.h" #include "../../ui_companion_driver.h" +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 +static const NSEventMask NSEventMaskAny = NSAnyEventMask; +#endif + static void* ui_application_cocoa_initialize(void) { return NULL; @@ -30,7 +34,7 @@ static void* ui_application_cocoa_initialize(void) static bool ui_application_cocoa_pending_events(void) { - NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; + NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; if (!event) return false; return true; @@ -40,7 +44,7 @@ static void ui_application_cocoa_process_events(void) { while (1) { - NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; + NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; if (!event) break; #if __has_feature(objc_arc) diff --git a/ui/drivers/cocoa/ui_cocoa_msg_window.m b/ui/drivers/cocoa/ui_cocoa_msg_window.m index 3164f6d672..ab856ca520 100644 --- a/ui/drivers/cocoa/ui_cocoa_msg_window.m +++ b/ui/drivers/cocoa/ui_cocoa_msg_window.m @@ -25,6 +25,12 @@ #include "../../ui_companion_driver.h" +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 +static const NSAlertStyle NSAlertStyleCritical = NSCriticalAlertStyle; +static const NSAlertStyle NSAlertStyleWarning = NSWarningAlertStyle; +static const NSAlertStyle NSAlertStyleInformational = NSInformationalAlertStyle; +#endif + static enum ui_msg_window_response ui_msg_window_cocoa_dialog(ui_msg_window_state *state, enum ui_msg_window_type type) { NSInteger response; @@ -61,24 +67,32 @@ static enum ui_msg_window_response ui_msg_window_cocoa_dialog(ui_msg_window_stat switch (type) { case UI_MSG_WINDOW_TYPE_ERROR: - [alert setAlertStyle:NSCriticalAlertStyle]; + [alert setAlertStyle:NSAlertStyleCritical]; break; case UI_MSG_WINDOW_TYPE_WARNING: - [alert setAlertStyle:NSWarningAlertStyle]; + [alert setAlertStyle:NSAlertStyleWarning]; break; case UI_MSG_WINDOW_TYPE_QUESTION: - [alert setAlertStyle:NSInformationalAlertStyle]; + [alert setAlertStyle:NSAlertStyleInformational]; break; case UI_MSG_WINDOW_TYPE_INFORMATION: - [alert setAlertStyle:NSInformationalAlertStyle]; + [alert setAlertStyle:NSAlertStyleInformational]; break; } - + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 + [alert beginSheetModalForWindow:(BRIDGE NSWindow *)ui_companion_driver_get_main_window() + completionHandler:^(NSModalResponse returnCode) { + [[NSApplication sharedApplication] stopModalWithCode:returnCode]; + }]; + response = [alert runModal]; +#else [alert beginSheetModalForWindow:(BRIDGE NSWindow *)ui_companion_driver_get_main_window() modalDelegate:apple_platform didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:nil]; response = [[NSApplication sharedApplication] runModalForWindow:[alert window]]; +#endif switch (state->buttons) { diff --git a/ui/drivers/ui_cocoa.m b/ui/drivers/ui_cocoa.m index b1d19e8000..24540d2e70 100644 --- a/ui/drivers/ui_cocoa.m +++ b/ui/drivers/ui_cocoa.m @@ -69,6 +69,31 @@ static void app_terminate(void) @implementation RApplication +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 +static const NSEventType NSEventTypeKeyDown = NSKeyDown; +static const NSEventType NSEventTypeKeyUp = NSKeyUp; +static const NSEventType NSEventTypeFlagsChanged = NSFlagsChanged; +static const NSEventType NSEventTypeMouseMoved = NSMouseMoved; +static const NSEventType NSEventTypeLeftMouseDragged = NSLeftMouseDragged; +static const NSEventType NSEventTypeRightMouseDragged = NSRightMouseDragged; +static const NSEventType NSEventTypeOtherMouseDragged = NSOtherMouseDragged; +static const NSEventType NSEventTypeLeftMouseDown = NSLeftMouseDown; +static const NSEventType NSEventTypeRightMouseDown = NSRightMouseDown; +static const NSEventType NSEventTypeOtherMouseDown = NSOtherMouseDown; +static const NSEventType NSEventTypeLeftMouseUp = NSLeftMouseUp; +static const NSEventType NSEventTypeRightMouseUp = NSRightMouseUp; +static const NSEventType NSEventTypeOtherMouseUp = NSOtherMouseUp; +static const NSEventType NSEventTypeScrollWheel = NSScrollWheel; + +// modifier flags +static const NSEventModifierFlags NSEventModifierFlagCapsLock = NSAlphaShiftKeyMask; +static const NSEventModifierFlags NSEventModifierFlagShift = NSShiftKeyMask; +static const NSEventModifierFlags NSEventModifierFlagControl = NSControlKeyMask; +static const NSEventModifierFlags NSEventModifierFlagOption = NSAlternateKeyMask; +static const NSEventModifierFlags NSEventModifierFlagCommand = NSCommandKeyMask; +static const NSEventModifierFlags NSEventModifierFlagNumericPad= NSNumericPadKeyMask; +#endif + - (void)sendEvent:(NSEvent *)event { NSEventType event_type; @@ -77,10 +102,10 @@ static void app_terminate(void) event_type = event.type; - switch ((int32_t)event_type) + switch (event_type) { - case NSKeyDown: - case NSKeyUp: + case NSEventTypeKeyDown: + case NSEventTypeKeyUp: { NSString* ch = (NSString*)event.characters; uint32_t character = 0; @@ -91,29 +116,29 @@ static void app_terminate(void) uint32_t i; character = [ch characterAtIndex:0]; - if (event.modifierFlags & NSAlphaShiftKeyMask) + if (event.modifierFlags & NSEventModifierFlagCapsLock) mod |= RETROKMOD_CAPSLOCK; - if (event.modifierFlags & NSShiftKeyMask) + if (event.modifierFlags & NSEventModifierFlagShift) mod |= RETROKMOD_SHIFT; - if (event.modifierFlags & NSControlKeyMask) + if (event.modifierFlags & NSEventModifierFlagControl) mod |= RETROKMOD_CTRL; - if (event.modifierFlags & NSAlternateKeyMask) + if (event.modifierFlags & NSEventModifierFlagOption) mod |= RETROKMOD_ALT; - if (event.modifierFlags & NSCommandKeyMask) + if (event.modifierFlags & NSEventModifierFlagCommand) mod |= RETROKMOD_META; - if (event.modifierFlags & NSNumericPadKeyMask) + if (event.modifierFlags & NSEventModifierFlagNumericPad) mod |= RETROKMOD_NUMLOCK; for (i = 1; i < ch.length; i++) - apple_input_keyboard_event(event_type == NSKeyDown, + apple_input_keyboard_event(event_type == NSEventTypeKeyDown, 0, [ch characterAtIndex:i], mod, RETRO_DEVICE_KEYBOARD); } - apple_input_keyboard_event(event_type == NSKeyDown, + apple_input_keyboard_event(event_type == NSEventTypeKeyDown, event.keyCode, character, mod, RETRO_DEVICE_KEYBOARD); } break; - case NSFlagsChanged: + case NSEventTypeFlagsChanged: { static uint32_t old_flags = 0; uint32_t new_flags = event.modifierFlags; @@ -125,10 +150,10 @@ static void app_terminate(void) 0, event.modifierFlags, RETRO_DEVICE_KEYBOARD); } break; - case NSMouseMoved: - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: + case NSEventTypeMouseMoved: + case NSEventTypeLeftMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeOtherMouseDragged: { NSPoint pos; NSPoint mouse_pos; @@ -150,12 +175,12 @@ static void app_terminate(void) apple->window_pos_y = (int16_t)mouse_pos.y; } break; - case NSScrollWheel: + case NSEventTypeScrollWheel: /* TODO/FIXME - properly implement. */ break; - case NSLeftMouseDown: - case NSRightMouseDown: - case NSOtherMouseDown: + case NSEventTypeLeftMouseDown: + case NSEventTypeRightMouseDown: + case NSEventTypeOtherMouseDown: { NSPoint pos = [apple_platform.renderView convertPoint:[event locationInWindow] fromView:nil]; apple = (cocoa_input_data_t*)input_driver_get_data(); @@ -166,9 +191,9 @@ static void app_terminate(void) apple->touch_count = 1; } break; - case NSLeftMouseUp: - case NSRightMouseUp: - case NSOtherMouseUp: + case NSEventTypeLeftMouseUp: + case NSEventTypeRightMouseUp: + case NSEventTypeOtherMouseUp: { NSPoint pos = [apple_platform.renderView convertPoint:[event locationInWindow] fromView:nil]; apple = (cocoa_input_data_t*)input_driver_get_data(); @@ -178,6 +203,8 @@ static void app_terminate(void) apple->touch_count = 0; } break; + default: + break; } } From 8a298616aa84dbfb123af58c694baaa323264746 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Wed, 4 Jul 2018 00:24:47 -0700 Subject: [PATCH 031/106] chore(cocoa): reformat (3 spaces, etc) --- ui/drivers/cocoa/ui_cocoa_msg_window.m | 166 ++++++++++++------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/ui/drivers/cocoa/ui_cocoa_msg_window.m b/ui/drivers/cocoa/ui_cocoa_msg_window.m index ab856ca520..b166c693d7 100644 --- a/ui/drivers/cocoa/ui_cocoa_msg_window.m +++ b/ui/drivers/cocoa/ui_cocoa_msg_window.m @@ -33,96 +33,96 @@ static const NSAlertStyle NSAlertStyleInformational = NSInformationalAlertStyle; static enum ui_msg_window_response ui_msg_window_cocoa_dialog(ui_msg_window_state *state, enum ui_msg_window_type type) { - NSInteger response; + NSInteger response; #if __has_feature(objc_arc) - NSAlert* alert = [NSAlert new]; + NSAlert *alert = [NSAlert new]; #else - NSAlert* alert = [[NSAlert new] autorelease]; + NSAlert* alert = [[NSAlert new] autorelease]; #endif - - if (!string_is_empty(state->title)) - [alert setMessageText:BOXSTRING(state->title)]; - [alert setInformativeText:BOXSTRING(state->text)]; - - switch (state->buttons) - { - case UI_MSG_WINDOW_OK: - [alert addButtonWithTitle:BOXSTRING("OK")]; - break; - case UI_MSG_WINDOW_YESNO: - [alert addButtonWithTitle:BOXSTRING("Yes")]; - [alert addButtonWithTitle:BOXSTRING("No")]; - break; - case UI_MSG_WINDOW_OKCANCEL: - [alert addButtonWithTitle:BOXSTRING("OK")]; - [alert addButtonWithTitle:BOXSTRING("Cancel")]; - break; - case UI_MSG_WINDOW_YESNOCANCEL: - [alert addButtonWithTitle:BOXSTRING("Yes")]; - [alert addButtonWithTitle:BOXSTRING("No")]; - [alert addButtonWithTitle:BOXSTRING("Cancel")]; - break; - } - - switch (type) - { - case UI_MSG_WINDOW_TYPE_ERROR: - [alert setAlertStyle:NSAlertStyleCritical]; - break; - case UI_MSG_WINDOW_TYPE_WARNING: - [alert setAlertStyle:NSAlertStyleWarning]; - break; - case UI_MSG_WINDOW_TYPE_QUESTION: - [alert setAlertStyle:NSAlertStyleInformational]; - break; - case UI_MSG_WINDOW_TYPE_INFORMATION: - [alert setAlertStyle:NSAlertStyleInformational]; - break; - } + + if (!string_is_empty(state->title)) + [alert setMessageText:BOXSTRING(state->title)]; + [alert setInformativeText:BOXSTRING(state->text)]; + + switch (state->buttons) + { + case UI_MSG_WINDOW_OK: + [alert addButtonWithTitle:BOXSTRING("OK")]; + break; + case UI_MSG_WINDOW_YESNO: + [alert addButtonWithTitle:BOXSTRING("Yes")]; + [alert addButtonWithTitle:BOXSTRING("No")]; + break; + case UI_MSG_WINDOW_OKCANCEL: + [alert addButtonWithTitle:BOXSTRING("OK")]; + [alert addButtonWithTitle:BOXSTRING("Cancel")]; + break; + case UI_MSG_WINDOW_YESNOCANCEL: + [alert addButtonWithTitle:BOXSTRING("Yes")]; + [alert addButtonWithTitle:BOXSTRING("No")]; + [alert addButtonWithTitle:BOXSTRING("Cancel")]; + break; + } + + switch (type) + { + case UI_MSG_WINDOW_TYPE_ERROR: + [alert setAlertStyle:NSAlertStyleCritical]; + break; + case UI_MSG_WINDOW_TYPE_WARNING: + [alert setAlertStyle:NSAlertStyleWarning]; + break; + case UI_MSG_WINDOW_TYPE_QUESTION: + [alert setAlertStyle:NSAlertStyleInformational]; + break; + case UI_MSG_WINDOW_TYPE_INFORMATION: + [alert setAlertStyle:NSAlertStyleInformational]; + break; + } #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 - [alert beginSheetModalForWindow:(BRIDGE NSWindow *)ui_companion_driver_get_main_window() - completionHandler:^(NSModalResponse returnCode) { - [[NSApplication sharedApplication] stopModalWithCode:returnCode]; - }]; - response = [alert runModal]; + [alert beginSheetModalForWindow:(BRIDGE NSWindow *)ui_companion_driver_get_main_window() + completionHandler:^(NSModalResponse returnCode) { + [[NSApplication sharedApplication] stopModalWithCode:returnCode]; + }]; + response = [alert runModal]; #else - [alert beginSheetModalForWindow:(BRIDGE NSWindow *)ui_companion_driver_get_main_window() - modalDelegate:apple_platform - didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) - contextInfo:nil]; - response = [[NSApplication sharedApplication] runModalForWindow:[alert window]]; + [alert beginSheetModalForWindow:(BRIDGE NSWindow *)ui_companion_driver_get_main_window() + modalDelegate:apple_platform + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) + contextInfo:nil]; + response = [[NSApplication sharedApplication] runModalForWindow:[alert window]]; #endif - - switch (state->buttons) - { - case UI_MSG_WINDOW_OK: - if (response == NSAlertFirstButtonReturn) - return UI_MSG_RESPONSE_OK; - break; - case UI_MSG_WINDOW_OKCANCEL: - if (response == NSAlertFirstButtonReturn) - return UI_MSG_RESPONSE_OK; - if (response == NSAlertSecondButtonReturn) - return UI_MSG_RESPONSE_CANCEL; - break; - case UI_MSG_WINDOW_YESNO: - if (response == NSAlertFirstButtonReturn) - return UI_MSG_RESPONSE_YES; - if (response == NSAlertSecondButtonReturn) - return UI_MSG_RESPONSE_NO; - break; - case UI_MSG_WINDOW_YESNOCANCEL: - if (response == NSAlertFirstButtonReturn) - return UI_MSG_RESPONSE_YES; - if (response == NSAlertSecondButtonReturn) - return UI_MSG_RESPONSE_NO; - if (response == NSAlertThirdButtonReturn) - return UI_MSG_RESPONSE_CANCEL; - break; - } - - return UI_MSG_RESPONSE_NA; + + switch (state->buttons) + { + case UI_MSG_WINDOW_OK: + if (response == NSAlertFirstButtonReturn) + return UI_MSG_RESPONSE_OK; + break; + case UI_MSG_WINDOW_OKCANCEL: + if (response == NSAlertFirstButtonReturn) + return UI_MSG_RESPONSE_OK; + if (response == NSAlertSecondButtonReturn) + return UI_MSG_RESPONSE_CANCEL; + break; + case UI_MSG_WINDOW_YESNO: + if (response == NSAlertFirstButtonReturn) + return UI_MSG_RESPONSE_YES; + if (response == NSAlertSecondButtonReturn) + return UI_MSG_RESPONSE_NO; + break; + case UI_MSG_WINDOW_YESNOCANCEL: + if (response == NSAlertFirstButtonReturn) + return UI_MSG_RESPONSE_YES; + if (response == NSAlertSecondButtonReturn) + return UI_MSG_RESPONSE_NO; + if (response == NSAlertThirdButtonReturn) + return UI_MSG_RESPONSE_CANCEL; + break; + } + + return UI_MSG_RESPONSE_NA; } static enum ui_msg_window_response ui_msg_window_cocoa_error(ui_msg_window_state *state) From 17f0ef7dd88121eba597b419d6913b10c188f5c5 Mon Sep 17 00:00:00 2001 From: DEX357 Date: Wed, 4 Jul 2018 12:10:39 +0200 Subject: [PATCH 032/106] Update msg_hash_pl.h --- intl/msg_hash_pl.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index 00ead311da..26689136a4 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -2856,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." @@ -3591,7 +3595,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_INPUT, MSG_HASH(MENU_ENUM_SUBLABEL_MIDI_INPUT, "Wybierz urządzenie wejściowe.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_OUTPUT, - "wyjście") + "Wyjście") MSG_HASH(MENU_ENUM_SUBLABEL_MIDI_OUTPUT, "Wybierz urządzenie wyjściowe.") MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_VOLUME, From c0dba89d439e5f230d290e4965d24785bc139450 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Wed, 4 Jul 2018 15:30:07 +0200 Subject: [PATCH 033/106] Update CHANGES.md --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 3674186a41..d3878dfe46 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,7 +14,8 @@ - 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. - PS3: Add audio mixer support for FLAC and MP3. - PSP: Use proper button labels, fix inverted R-Stick Y axis. From 844e5e15d0a686df334210dad5dfae2041322c25 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Wed, 4 Jul 2018 08:50:46 -0700 Subject: [PATCH 034/106] fix(Metal): fix XRGBA rendering; no nil texture; undefined stuff bad --- gfx/common/metal/Context.m | 2 ++ gfx/common/metal/MenuDisplay.m | 47 +++++++++++++++---------------- gfx/common/metal/RendererCommon.m | 11 +++++--- gfx/common/metal_common.m | 2 +- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index 3e1ac87deb..1610d1c56a 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -134,6 +134,8 @@ - (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. */ diff --git a/gfx/common/metal/MenuDisplay.m b/gfx/common/metal/MenuDisplay.m index 9296a1e31a..7581631fb7 100644 --- a/gfx/common/metal/MenuDisplay.m +++ b/gfx/common/metal/MenuDisplay.m @@ -131,7 +131,6 @@ - (void)draw:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video { - Texture *tex = (__bridge Texture *)(void *)draw->texture; 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; @@ -178,37 +177,37 @@ case VIDEO_SHADER_MENU_4: case VIDEO_SHADER_MENU_5: case VIDEO_SHADER_MENU_6: - { [rce setRenderPipelineState:[_driver 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]; - break; - } + return; #endif default: - { - if (_clearNextRender) - { - // TODO(sgc): draw quad to clear - _clearNextRender = NO; - } - - [rce setRenderPipelineState:[_driver 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]; - break; - } } + + Texture *tex = (__bridge Texture *)(void *)draw->texture; + if (tex == nil) + return; + + if (_clearNextRender) + { + // TODO(sgc): draw quad to clear + _clearNextRender = NO; + } + + [rce setRenderPipelineState:[_driver 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/RendererCommon.m b/gfx/common/metal/RendererCommon.m index f3ed51db97..dce930c162 100644 --- a/gfx/common/metal/RendererCommon.m +++ b/gfx/common/metal/RendererCommon.m @@ -54,10 +54,13 @@ NSString *NSStringFromRPixelFormat(RPixelFormat format) matrix_float4x4 make_matrix_float4x4(const float *v) { - simd_float4 P = simd_make_float4(*v++, *v++, *v++, *v++); - simd_float4 Q = simd_make_float4(*v++, *v++, *v++, *v++); - simd_float4 R = simd_make_float4(*v++, *v++, *v++, *v++); - simd_float4 S = simd_make_float4(*v++, *v++, *v++, *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; diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index b52fef8542..0a3b3a3836 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -901,7 +901,7 @@ typedef struct MTLALIGN(16) 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 * pitch)]; + bytesPerRow:pitch]; } else { From 048aa1659961c8f4dcde4c24401dad5ec24c2960 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Wed, 4 Jul 2018 09:50:09 -0700 Subject: [PATCH 035/106] feat(Metal): Support vsync enable / disable --- gfx/common/metal/Context.h | 3 +++ gfx/common/metal/Context.m | 11 +++++++++++ gfx/drivers/metal.m | 5 ++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index d335e80510..eeb687fc10 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -29,6 +29,9 @@ typedef struct @property (nonatomic, readonly) id library; @property (nonatomic, readwrite) MTLClearColor clearColor; +/*! @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 for applying filters * */ diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index 1610d1c56a..91d5ac4c27 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -60,6 +60,7 @@ _inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT); _device = d; _layer = layer; + _layer.displaySyncEnabled = YES; _library = l; _commandQueue = [_device newCommandQueue]; _clearColor = MTLClearColorMake(0, 0, 0, 1); @@ -99,6 +100,16 @@ return self; } +- (void)setDisplaySyncEnabled:(bool)displaySyncEnabled +{ + _layer.displaySyncEnabled = displaySyncEnabled; +} + +- (bool)displaySyncEnabled +{ + return _layer.displaySyncEnabled; +} + - (bool)_initMainState { return YES; diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index 8ba2ae7b5b..773848546e 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -75,8 +75,11 @@ static bool metal_frame(void *data, const void *frame, info:video_info]; } -static void metal_set_nonblock_state(void *data, bool state) +static void metal_set_nonblock_state(void *data, bool non_block) { + RARCH_LOG("[Metal]: set non block: %s\n", non_block ? "ON" : "OFF"); + MetalDriver *md = (__bridge MetalDriver *)data; + md.context.displaySyncEnabled = !non_block; } static bool metal_alive(void *data) From f1d2dac2133d64ddbebdc003d19fe088456b20cb Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Wed, 4 Jul 2018 19:45:54 +0200 Subject: [PATCH 036/106] Vulkan: Fix various stability issues with WSI. Added an ifdef to trigger "hardening" which will return spurious errors for critical WSI things like acquire next image, queue submit and surface dimension queries. --- gfx/common/vulkan_common.c | 157 ++++++++++++++++++++++++------------- 1 file changed, 101 insertions(+), 56 deletions(-) diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 5725ed6883..19afaa2490 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; +} +#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. */ @@ -2445,48 +2486,58 @@ void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk) } } -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 +2562,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 +2665,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 +2707,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 +2809,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; } From 52cb0fe37576f4d399d3932508d4120394f1494f Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Wed, 4 Jul 2018 20:25:03 +0200 Subject: [PATCH 037/106] Vulkan: Fix crash when we get two create_swapchain errors in a row. --- gfx/common/vulkan_common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 19afaa2490..31c7f3d620 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -62,7 +62,7 @@ static void trigger_spurious_error_vkresult(VkResult *res) static bool trigger_spurious_error(void) { ++wsi_harden_counter2; - return (wsi_harden_counter2 & 15) == 9; + return ((wsi_harden_counter2 & 15) == 9) || ((wsi_harden_counter2 & 15) == 10); } #endif @@ -2482,6 +2482,7 @@ retry: vk->context.current_swapchain_index = 0; vulkan_acquire_clear_fences(vk); vulkan_acquire_wait_fences(vk); + vk->context.invalid_swapchain = true; return; } } From 3deb56690a00c7d4c804334fe2b2a0be2ac588ce Mon Sep 17 00:00:00 2001 From: Dwedit Date: Wed, 4 Jul 2018 14:14:01 -0500 Subject: [PATCH 038/106] Fix missing BOM on msg_hash_pl.h --- intl/msg_hash_pl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index 26689136a4..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" ) From 71086283691f0247459df827de41de472a4874a2 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Wed, 4 Jul 2018 14:20:38 -0500 Subject: [PATCH 039/106] Moved WIN32 message loop out of win32_check_window, and into just before the call to runloop_iterate --- frontend/frontend.c | 7 ++++++- gfx/common/win32_common.c | 6 +----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/frontend/frontend.c b/frontend/frontend.c index b144761490..1ae4abc3f0 100644 --- a/frontend/frontend.c +++ b/frontend/frontend.c @@ -138,7 +138,12 @@ int rarch_main(int argc, char *argv[], void *data) do { unsigned sleep_ms = 0; - int ret = runloop_iterate(&sleep_ms); + int ret; + const ui_application_t *application = + ui_companion_driver_get_application_ptr(); + if (application) + application->process_events(); + ret = runloop_iterate(&sleep_ms); if (ret == 1 && sleep_ms > 0) retro_sleep(sleep_ms); diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index a95ee155c9..b4bcbc0481 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -1117,12 +1117,7 @@ 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(); *quit = g_win32_quit; -#endif if (g_win32_resized) { @@ -1131,6 +1126,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) From a19ea2ed0787bb3918ab2199e5e80debeaefe6da Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Wed, 4 Jul 2018 13:12:40 -0700 Subject: [PATCH 040/106] feat(Metal): Add support for overlays --- gfx/common/metal/Context.h | 1 + gfx/common/metal/Context.m | 16 +++-- gfx/common/metal_common.h | 12 ++++ gfx/common/metal_common.m | 131 +++++++++++++++++++++++++++++++++++++ gfx/drivers/metal.m | 104 ++++++++++++++++++++++------- 5 files changed, 233 insertions(+), 31 deletions(-) diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index eeb687fc10..5b5b380cf8 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -49,6 +49,7 @@ typedef struct 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; - (bool)allocRange:(BufferRange *)range length:(NSUInteger)length; diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index 91d5ac4c27..5e3fae6012 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -170,9 +170,17 @@ image.height = 8; } - // TODO(sgc): mipmapping is not working 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 @@ -192,11 +200,7 @@ [bce endEncoding]; } - Texture *tex = [Texture new]; - tex.texture = t; - tex.sampler = _samplers[filter]; - - return tex; + return t; } - (id)nextDrawable diff --git a/gfx/common/metal_common.h b/gfx/common/metal_common.h index 1ba4d73c77..e14902649b 100644 --- a/gfx/common/metal_common.h +++ b/gfx/common/metal_common.h @@ -58,6 +58,17 @@ 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 (nonatomic, readonly) video_viewport_t *viewport; @@ -65,6 +76,7 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @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; @property (nonatomic, readonly) Uniforms *viewportMVPNormalized; diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 0a3b3a3836..0777a51bb7 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -45,10 +45,16 @@ - (instancetype)initWithContext:(Context *)context; @end +@interface Overlay() +- (instancetype)initWithContext:(Context *)context; +- (void)drawWithEncoder:(id)rce; +@end + @implementation MetalDriver { FrameView *_frameView; MetalMenu *_menu; + Overlay *_overlay; video_info_t _video; @@ -128,6 +134,9 @@ [_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; @@ -440,6 +449,19 @@ 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:[self 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) { @@ -1462,6 +1484,115 @@ static vertex_t vertex_bytes[] = { @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 diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index 773848546e..fbfb3c48e4 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -92,12 +92,10 @@ 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, @@ -149,26 +147,6 @@ 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) { @@ -337,6 +315,82 @@ 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, From c7b21d5bd6cdc803035d97a484160a3d6b4ff32c Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Wed, 4 Jul 2018 13:13:03 -0700 Subject: [PATCH 041/106] feat(Metal): Add support to disable display sleep (no screen saver) --- ui/drivers/cocoa/cocoa_common.h | 4 ++++ ui/drivers/ui_cocoa.m | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/ui/drivers/cocoa/cocoa_common.h b/ui/drivers/cocoa/cocoa_common.h index 8e0cc1a0dd..a291b769b3 100644 --- a/ui/drivers/cocoa/cocoa_common.h +++ b/ui/drivers/cocoa/cocoa_common.h @@ -60,6 +60,10 @@ typedef enum apple_view_type { /*! @brief setCursorVisible specifies whether the cursor is visible */ - (void)setCursorVisible:(bool)v; +/*! @brief controls whether the screen saver should be disabled and + * the displays should not sleep. + */ +- (bool)setDisableDisplaySleep:(bool)disable; @end extern id apple_platform; diff --git a/ui/drivers/ui_cocoa.m b/ui/drivers/ui_cocoa.m index 24540d2e70..4070cfe873 100644 --- a/ui/drivers/ui_cocoa.m +++ b/ui/drivers/ui_cocoa.m @@ -53,6 +53,8 @@ id apple_platform; NSWindow* _window; apple_view_type_t _vt; NSView* _renderView; + id _sleepActivity; + } @property (nonatomic, retain) NSWindow IBOutlet* window; @@ -337,6 +339,26 @@ static char** waiting_argv; [NSCursor hide]; } +- (bool)setDisableDisplaySleep:(bool)disable +{ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 + if (disable && _sleepActivity == nil) + { + _sleepActivity = [NSProcessInfo.processInfo beginActivityWithOptions:NSActivityIdleDisplaySleepDisabled reason:@"disable screen saver"]; + } + else if (!disable && _sleepActivity != nil) + { + [NSProcessInfo.processInfo endActivity:_sleepActivity]; + _sleepActivity = nil; + } + return YES; +#else + return NO; +#endif + +} + + - (void) rarch_main { do From 2da12dca5803c3dcde920a1cff838fd98aecb36f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 4 Jul 2018 22:50:02 +0200 Subject: [PATCH 042/106] Squashed 'deps/SPIRV-Cross/' changes from e59cc24495..f96c9f9fb4 f96c9f9fb4 Merge pull request #635 from KhronosGroup/fix-631 e044732896 Support OpTypeImage with depth == 2 (unknown) properly. a6814a405a Merge pull request #630 from KhronosGroup/fix-628 af2d3abd03 Fail more gracefully with some unsupported opcodes. 26107ba200 Fix os.errno issues on Travis. ee44f6027b Changed OpTypeImage to only flag depth if the op is 1 c863f53cac Merge pull request #627 from KhronosGroup/macro-namespace-fix 9ddbd5aff6 Run format_all.sh. f1752e58e1 Add basic namespace to internal macros. d67e586b2e Merge pull request #626 from billhollings/master 9bf226cb05 Fixes for code review of PR 626. 4c5142b9d3 CompilerMSL support larger texel buffers by using 2D Metal textures. 314f39a7c4 Merge pull request #621 from billhollings/master 4beefe756c Fixes from PR 621 code review. f66507a701 Merge branch 'master' of https://github.com/KhronosGroup/SPIRV-Cross 0ea5e0549e Merge pull request #615 from JustSid/master 5ac55ee735 Fixed emission of some legacy texture ops without requiring the appropriate extensions ceec708b89 Added better fallbacks for legacy textureProjLod() and textureProjLodOffset() generation 994f789465 Merge pull request #624 from KhronosGroup/fix-619 33c61d2abe Support branch/loop hints in HLSL. 327fb03677 Merge pull request #623 from KhronosGroup/fix-618 2077478651 Merge pull request #622 from KhronosGroup/fix-620 10dfaf79d5 Support globallycoherent in HLSL. ffa9133d77 Support ternary expressions in OpSpecConstantOp. e091031613 CompilerMSL pass builtin struct members into functions. 7607eb6923 Merge pull request #617 from KhronosGroup/fix-612 d94d20f4f3 Deal with some builtins being declared with wrong signedness. 0f62b5dc1e Moved check for depth texture and shadowXY emission completely to legacy_tex_op() 447a253ce7 Simplified check for depth texture 76c8e3c1c4 Merge pull request #616 from KhronosGroup/fix-614 b29629fd46 Add support to remove SPIRV_Cross_BaseInstance uniform. 809631ce21 Mention JSON backend in README. 040204d65c Fix warnings and run format_all.sh. b4c8c3b9b2 Merge branch 'reflection' of git://github.com/jherico/SPIRV-Cross f6dad78c99 Added support for shadowXY() sample instructions in legacy GLSL 9ad432463c Prefix integer types with underscore 0ad0f948e1 More PR feedback 3b30202bee Add reflection specific test cases, add reflection testing to test_shaders.sh 8d84a541ac Add specialization constant output in reflection 762040084d More feedback d0a67ba6a7 Code consolidation, const correctness, faster regression testing 3a825349bc More cleanup 6c88b0048b PR feedback ee86000529 Cleanup code 709d3c60f2 Working on reflection output 0039cb86fc Merge pull request #613 from KhronosGroup/fix-609 9d31154917 Deal with switch case labels which share a block. git-subtree-dir: deps/SPIRV-Cross git-subtree-split: f96c9f9fb4fc7d17991cecb2b2294dce06d08d9c --- CMakeLists.txt | 6 +- README.md | 1 + main.cpp | 41 +- msvc/SPIRV-Cross.vcxproj | 2 + msvc/SPIRV-Cross.vcxproj.filters | 6 + .../asm/comp/control-flow-hints.asm.comp | 32 + .../asm/frag/unknown-depth-state.asm.frag | 31 + .../vert/uint-vertex-id-instance-id.asm.vert | 28 + .../shaders-hlsl/comp/globallycoherent.comp | 16 + .../frag/spec-constant-ternary.frag | 23 + .../asm/frag/unknown-depth-state.asm.frag | 22 + .../vert/uint-vertex-id-instance-id.asm.vert | 17 + .../frag/spec-constant-ternary.frag | 22 + .../shaders-msl/vert/set_builtin_in_func.vert | 19 + .../opt/shaders-msl/vert/texture_buffer.vert | 10 +- .../frag/switch-label-shared-block.asm.frag | 33 + .../asm/frag/unknown-depth-state.asm.vk.frag | 13 + .../frag/unknown-depth-state.asm.vk.frag.vk | 14 + .../geom/store-uint-layer.invalid.asm.geom | 41 + .../vert/uint-vertex-id-instance-id.asm.vert | 9 + .../vulkan/frag/spec-constant-ternary.vk.frag | 9 + .../frag/spec-constant-ternary.vk.frag.vk | 13 + .../asm/comp/control-flow-hints.asm.comp | 41 + .../asm/frag/unknown-depth-state.asm.frag | 41 + .../vert/uint-vertex-id-instance-id.asm.vert | 37 + .../shaders-hlsl/comp/globallycoherent.comp | 18 + .../frag/spec-constant-ternary.frag | 23 + .../vert/functions_nested.vert | 20 +- .../asm/frag/unknown-depth-state.asm.frag | 34 + .../vert/uint-vertex-id-instance-id.asm.vert | 28 + .../frag/spec-constant-ternary.frag | 22 + .../shaders-msl/vert/set_builtin_in_func.vert | 26 + .../shaders-msl/vert/texture_buffer.vert | 10 +- .../aliased-entry-point-names.asm.multi.json | 49 ++ .../comp/struct-layout.comp.json | 64 ++ .../comp/struct-packing.comp.json | 474 ++++++++++++ ...mbined-texture-sampler-shadow.vk.frag.json | 37 + .../combined-texture-sampler.vk.frag.json | 50 ++ .../image-load-store-uint-coord.asm.frag.json | 47 ++ .../frag/input-attachment-ms.vk.frag.json | 31 + .../frag/input-attachment.vk.frag.json | 31 + .../frag/push-constant.vk.frag.json | 46 ++ ...eparate-sampler-texture-array.vk.frag.json | 73 ++ .../frag/spec-constant.vk.frag.json | 71 ++ .../vert/read-from-row-major-array.vert.json | 61 ++ .../vert/texture_buffer.vert.json | 40 + .../frag/switch-label-shared-block.asm.frag | 33 + .../asm/frag/unknown-depth-state.asm.vk.frag | 23 + .../frag/unknown-depth-state.asm.vk.frag.vk | 24 + .../geom/store-uint-layer.invalid.asm.geom | 41 + .../vert/uint-vertex-id-instance-id.asm.vert | 18 + .../vulkan/frag/spec-constant-ternary.vk.frag | 9 + .../frag/spec-constant-ternary.vk.frag.vk | 13 + .../asm/comp/control-flow-hints.asm.comp | 146 ++++ .../asm/frag/unknown-depth-state.asm.frag | 71 ++ .../vert/uint-vertex-id-instance-id.asm.vert | 65 ++ shaders-hlsl/comp/globallycoherent.comp | 25 + shaders-hlsl/frag/spec-constant-ternary.frag | 9 + .../asm/frag/unknown-depth-state.asm.frag | 71 ++ .../vert/uint-vertex-id-instance-id.asm.vert | 65 ++ shaders-msl/frag/spec-constant-ternary.frag | 9 + shaders-msl/vert/set_builtin_in_func.vert | 12 + .../asm/aliased-entry-point-names.asm.multi | 60 ++ shaders-reflection/comp/struct-layout.comp | 24 + shaders-reflection/comp/struct-packing.comp | 87 +++ .../combined-texture-sampler-shadow.vk.frag | 29 + .../frag/combined-texture-sampler.vk.frag | 47 ++ .../frag/image-load-store-uint-coord.asm.frag | 103 +++ .../frag/input-attachment-ms.vk.frag | 10 + .../frag/input-attachment.vk.frag | 11 + shaders-reflection/frag/push-constant.vk.frag | 16 + .../separate-sampler-texture-array.vk.frag | 42 ++ shaders-reflection/frag/spec-constant.vk.frag | 78 ++ .../vert/read-from-row-major-array.vert | 20 + shaders-reflection/vert/texture_buffer.vert | 10 + .../frag/switch-label-shared-block.asm.frag | 45 ++ .../asm/frag/unknown-depth-state.asm.vk.frag | 71 ++ .../geom/store-uint-layer.invalid.asm.geom | 130 ++++ .../vert/uint-vertex-id-instance-id.asm.vert | 65 ++ .../vulkan/frag/spec-constant-ternary.vk.frag | 9 + spirv_common.hpp | 10 + spirv_cross.cpp | 180 ++++- spirv_cross.hpp | 41 +- spirv_glsl.cpp | 699 +++++++++++------- spirv_glsl.hpp | 19 +- spirv_hlsl.cpp | 162 ++-- spirv_hlsl.hpp | 7 +- spirv_msl.cpp | 186 +++-- spirv_msl.hpp | 5 + spirv_reflect.cpp | 573 ++++++++++++++ spirv_reflect.hpp | 72 ++ test_shaders.py | 163 +++- test_shaders.sh | 1 + update_test_shaders.sh | 1 + 94 files changed, 4898 insertions(+), 494 deletions(-) create mode 100644 reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp create mode 100644 reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag create mode 100644 reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/opt/shaders-hlsl/comp/globallycoherent.comp create mode 100644 reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag create mode 100644 reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag create mode 100644 reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/opt/shaders-msl/frag/spec-constant-ternary.frag create mode 100644 reference/opt/shaders-msl/vert/set_builtin_in_func.vert create mode 100644 reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag create mode 100644 reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag create mode 100644 reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk create mode 100644 reference/opt/shaders/asm/geom/store-uint-layer.invalid.asm.geom create mode 100644 reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag create mode 100644 reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk create mode 100644 reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp create mode 100644 reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag create mode 100644 reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/shaders-hlsl/comp/globallycoherent.comp create mode 100644 reference/shaders-hlsl/frag/spec-constant-ternary.frag create mode 100644 reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag create mode 100644 reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/shaders-msl/frag/spec-constant-ternary.frag create mode 100644 reference/shaders-msl/vert/set_builtin_in_func.vert create mode 100644 reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json create mode 100644 reference/shaders-reflection/comp/struct-layout.comp.json create mode 100644 reference/shaders-reflection/comp/struct-packing.comp.json create mode 100644 reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json create mode 100644 reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json create mode 100644 reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json create mode 100644 reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json create mode 100644 reference/shaders-reflection/frag/input-attachment.vk.frag.json create mode 100644 reference/shaders-reflection/frag/push-constant.vk.frag.json create mode 100644 reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json create mode 100644 reference/shaders-reflection/frag/spec-constant.vk.frag.json create mode 100644 reference/shaders-reflection/vert/read-from-row-major-array.vert.json create mode 100644 reference/shaders-reflection/vert/texture_buffer.vert.json create mode 100644 reference/shaders/asm/frag/switch-label-shared-block.asm.frag create mode 100644 reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag create mode 100644 reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk create mode 100644 reference/shaders/asm/geom/store-uint-layer.invalid.asm.geom create mode 100644 reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag create mode 100644 reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk create mode 100644 shaders-hlsl/asm/comp/control-flow-hints.asm.comp create mode 100644 shaders-hlsl/asm/frag/unknown-depth-state.asm.frag create mode 100644 shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 shaders-hlsl/comp/globallycoherent.comp create mode 100644 shaders-hlsl/frag/spec-constant-ternary.frag create mode 100644 shaders-msl/asm/frag/unknown-depth-state.asm.frag create mode 100644 shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 shaders-msl/frag/spec-constant-ternary.frag create mode 100644 shaders-msl/vert/set_builtin_in_func.vert create mode 100644 shaders-reflection/asm/aliased-entry-point-names.asm.multi create mode 100644 shaders-reflection/comp/struct-layout.comp create mode 100644 shaders-reflection/comp/struct-packing.comp create mode 100644 shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag create mode 100644 shaders-reflection/frag/combined-texture-sampler.vk.frag create mode 100644 shaders-reflection/frag/image-load-store-uint-coord.asm.frag create mode 100644 shaders-reflection/frag/input-attachment-ms.vk.frag create mode 100644 shaders-reflection/frag/input-attachment.vk.frag create mode 100644 shaders-reflection/frag/push-constant.vk.frag create mode 100644 shaders-reflection/frag/separate-sampler-texture-array.vk.frag create mode 100644 shaders-reflection/frag/spec-constant.vk.frag create mode 100644 shaders-reflection/vert/read-from-row-major-array.vert create mode 100644 shaders-reflection/vert/texture_buffer.vert create mode 100644 shaders/asm/frag/switch-label-shared-block.asm.frag create mode 100644 shaders/asm/frag/unknown-depth-state.asm.vk.frag create mode 100644 shaders/asm/geom/store-uint-layer.invalid.asm.geom create mode 100644 shaders/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 shaders/vulkan/frag/spec-constant-ternary.vk.frag create mode 100644 spirv_reflect.cpp create mode 100644 spirv_reflect.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9226afd46f..2849b6957d 100644 --- a/CMakeLists.txt +++ b/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/README.md b/README.md index 5f92698602..d3c1a93a07 100644 --- a/README.md +++ b/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/main.cpp b/main.cpp index b309a82ac3..69bd135c1a 100644 --- a/main.cpp +++ b/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/msvc/SPIRV-Cross.vcxproj b/msvc/SPIRV-Cross.vcxproj index 6040e2e116..d2287dcbe9 100644 --- a/msvc/SPIRV-Cross.vcxproj +++ b/msvc/SPIRV-Cross.vcxproj @@ -127,6 +127,7 @@ + @@ -138,6 +139,7 @@ + diff --git a/msvc/SPIRV-Cross.vcxproj.filters b/msvc/SPIRV-Cross.vcxproj.filters index f853c08b5b..3ff2c29a58 100644 --- a/msvc/SPIRV-Cross.vcxproj.filters +++ b/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/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000000..142ef5efa8 --- /dev/null +++ b/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/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..5b894de831 --- /dev/null +++ b/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/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..0d1e8cc534 --- /dev/null +++ b/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/reference/opt/shaders-hlsl/comp/globallycoherent.comp b/reference/opt/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000000..1637727deb --- /dev/null +++ b/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/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag b/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..12e0f5bd79 --- /dev/null +++ b/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/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..e8a88623a2 --- /dev/null +++ b/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/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..d453aadef0 --- /dev/null +++ b/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/reference/opt/shaders-msl/frag/spec-constant-ternary.frag b/reference/opt/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..5ab6b4fcb1 --- /dev/null +++ b/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/reference/opt/shaders-msl/vert/set_builtin_in_func.vert b/reference/opt/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000000..51a858af1e --- /dev/null +++ b/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/reference/opt/shaders-msl/vert/texture_buffer.vert b/reference/opt/shaders-msl/vert/texture_buffer.vert index f7bcb7918b..c45d298134 100644 --- a/reference/opt/shaders-msl/vert/texture_buffer.vert +++ b/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/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag b/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000000..ade9044e35 --- /dev/null +++ b/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/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000000..6953ec61d0 --- /dev/null +++ b/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/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk b/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk new file mode 100644 index 0000000000..2f997036f5 --- /dev/null +++ b/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/reference/opt/shaders/asm/geom/store-uint-layer.invalid.asm.geom b/reference/opt/shaders/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000000..c768d5da86 --- /dev/null +++ b/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/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..c25e9bbe5b --- /dev/null +++ b/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/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000000..91b0331b79 --- /dev/null +++ b/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/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk b/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk new file mode 100644 index 0000000000..59d3b99b9c --- /dev/null +++ b/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/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000000..9700100348 --- /dev/null +++ b/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/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..027dd00c5a --- /dev/null +++ b/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/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..a18c6e7056 --- /dev/null +++ b/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/reference/shaders-hlsl/comp/globallycoherent.comp b/reference/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000000..69886256f8 --- /dev/null +++ b/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/reference/shaders-hlsl/frag/spec-constant-ternary.frag b/reference/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..12e0f5bd79 --- /dev/null +++ b/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/reference/shaders-msl-no-opt/vert/functions_nested.vert b/reference/shaders-msl-no-opt/vert/functions_nested.vert index 43a508613b..037ead3922 100644 --- a/reference/shaders-msl-no-opt/vert/functions_nested.vert +++ b/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/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..005af22e3a --- /dev/null +++ b/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/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..89ca17f98b --- /dev/null +++ b/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/reference/shaders-msl/frag/spec-constant-ternary.frag b/reference/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..5ab6b4fcb1 --- /dev/null +++ b/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/reference/shaders-msl/vert/set_builtin_in_func.vert b/reference/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000000..2952748dc0 --- /dev/null +++ b/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/reference/shaders-msl/vert/texture_buffer.vert b/reference/shaders-msl/vert/texture_buffer.vert index f7bcb7918b..c45d298134 100644 --- a/reference/shaders-msl/vert/texture_buffer.vert +++ b/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/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json b/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json new file mode 100644 index 0000000000..45adf50b14 --- /dev/null +++ b/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/reference/shaders-reflection/comp/struct-layout.comp.json b/reference/shaders-reflection/comp/struct-layout.comp.json new file mode 100644 index 0000000000..3004454b80 --- /dev/null +++ b/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/reference/shaders-reflection/comp/struct-packing.comp.json b/reference/shaders-reflection/comp/struct-packing.comp.json new file mode 100644 index 0000000000..22a41584d9 --- /dev/null +++ b/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/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json b/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json new file mode 100644 index 0000000000..5b4d3c6f7b --- /dev/null +++ b/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/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json b/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json new file mode 100644 index 0000000000..8b6a184299 --- /dev/null +++ b/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/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json b/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json new file mode 100644 index 0000000000..527ea2bfee --- /dev/null +++ b/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/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json b/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json new file mode 100644 index 0000000000..5f381911ac --- /dev/null +++ b/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/reference/shaders-reflection/frag/input-attachment.vk.frag.json b/reference/shaders-reflection/frag/input-attachment.vk.frag.json new file mode 100644 index 0000000000..16ae6a4683 --- /dev/null +++ b/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/reference/shaders-reflection/frag/push-constant.vk.frag.json b/reference/shaders-reflection/frag/push-constant.vk.frag.json new file mode 100644 index 0000000000..f72a8fd654 --- /dev/null +++ b/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/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json b/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json new file mode 100644 index 0000000000..9216d93e5d --- /dev/null +++ b/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/reference/shaders-reflection/frag/spec-constant.vk.frag.json b/reference/shaders-reflection/frag/spec-constant.vk.frag.json new file mode 100644 index 0000000000..0add298666 --- /dev/null +++ b/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/reference/shaders-reflection/vert/read-from-row-major-array.vert.json b/reference/shaders-reflection/vert/read-from-row-major-array.vert.json new file mode 100644 index 0000000000..d92fb67fb5 --- /dev/null +++ b/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/reference/shaders-reflection/vert/texture_buffer.vert.json b/reference/shaders-reflection/vert/texture_buffer.vert.json new file mode 100644 index 0000000000..3c69e24cbc --- /dev/null +++ b/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/reference/shaders/asm/frag/switch-label-shared-block.asm.frag b/reference/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000000..ade9044e35 --- /dev/null +++ b/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/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000000..7729d30c09 --- /dev/null +++ b/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/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk b/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk new file mode 100644 index 0000000000..711fa27763 --- /dev/null +++ b/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/reference/shaders/asm/geom/store-uint-layer.invalid.asm.geom b/reference/shaders/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000000..c768d5da86 --- /dev/null +++ b/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/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..31f13bd777 --- /dev/null +++ b/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/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000000..91b0331b79 --- /dev/null +++ b/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/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk b/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk new file mode 100644 index 0000000000..59d3b99b9c --- /dev/null +++ b/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/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000000..74a15955c2 --- /dev/null +++ b/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/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..89036f0eb2 --- /dev/null +++ b/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/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..29b0076a1e --- /dev/null +++ b/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/shaders-hlsl/comp/globallycoherent.comp b/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000000..168b9404ea --- /dev/null +++ b/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/shaders-hlsl/frag/spec-constant-ternary.frag b/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..78dccbf044 --- /dev/null +++ b/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/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..89036f0eb2 --- /dev/null +++ b/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/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..29b0076a1e --- /dev/null +++ b/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/shaders-msl/frag/spec-constant-ternary.frag b/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..78dccbf044 --- /dev/null +++ b/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/shaders-msl/vert/set_builtin_in_func.vert b/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000000..dd991e3545 --- /dev/null +++ b/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/shaders-reflection/asm/aliased-entry-point-names.asm.multi b/shaders-reflection/asm/aliased-entry-point-names.asm.multi new file mode 100644 index 0000000000..d60cf3039c --- /dev/null +++ b/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/shaders-reflection/comp/struct-layout.comp b/shaders-reflection/comp/struct-layout.comp new file mode 100644 index 0000000000..5a2b7802df --- /dev/null +++ b/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/shaders-reflection/comp/struct-packing.comp b/shaders-reflection/comp/struct-packing.comp new file mode 100644 index 0000000000..d2ffbaef50 --- /dev/null +++ b/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/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag b/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag new file mode 100644 index 0000000000..2fabb5ea8a --- /dev/null +++ b/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/shaders-reflection/frag/combined-texture-sampler.vk.frag b/shaders-reflection/frag/combined-texture-sampler.vk.frag new file mode 100644 index 0000000000..b7de8d47e9 --- /dev/null +++ b/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/shaders-reflection/frag/image-load-store-uint-coord.asm.frag b/shaders-reflection/frag/image-load-store-uint-coord.asm.frag new file mode 100644 index 0000000000..a9bf1a7497 --- /dev/null +++ b/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/shaders-reflection/frag/input-attachment-ms.vk.frag b/shaders-reflection/frag/input-attachment-ms.vk.frag new file mode 100644 index 0000000000..e060738846 --- /dev/null +++ b/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/shaders-reflection/frag/input-attachment.vk.frag b/shaders-reflection/frag/input-attachment.vk.frag new file mode 100644 index 0000000000..f082d15b2a --- /dev/null +++ b/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/shaders-reflection/frag/push-constant.vk.frag b/shaders-reflection/frag/push-constant.vk.frag new file mode 100644 index 0000000000..6180faba31 --- /dev/null +++ b/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/shaders-reflection/frag/separate-sampler-texture-array.vk.frag b/shaders-reflection/frag/separate-sampler-texture-array.vk.frag new file mode 100644 index 0000000000..b3501c1d8d --- /dev/null +++ b/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/shaders-reflection/frag/spec-constant.vk.frag b/shaders-reflection/frag/spec-constant.vk.frag new file mode 100644 index 0000000000..e62a26059b --- /dev/null +++ b/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/shaders-reflection/vert/read-from-row-major-array.vert b/shaders-reflection/vert/read-from-row-major-array.vert new file mode 100644 index 0000000000..792fb8e36c --- /dev/null +++ b/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/shaders-reflection/vert/texture_buffer.vert b/shaders-reflection/vert/texture_buffer.vert new file mode 100644 index 0000000000..6bc7ddfae2 --- /dev/null +++ b/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/shaders/asm/frag/switch-label-shared-block.asm.frag b/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000000..8f55bcf536 --- /dev/null +++ b/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/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000000..89036f0eb2 --- /dev/null +++ b/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/shaders/asm/geom/store-uint-layer.invalid.asm.geom b/shaders/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000000..550fc4e990 --- /dev/null +++ b/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/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..29b0076a1e --- /dev/null +++ b/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/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000000..78dccbf044 --- /dev/null +++ b/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/spirv_common.hpp b/spirv_common.hpp index c4716a2388..e3fb2b7238 100644 --- a/spirv_common.hpp +++ b/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; diff --git a/spirv_cross.cpp b/spirv_cross.cpp index 0402562e0b..3e460e9f61 100644 --- a/spirv_cross.cpp +++ b/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; } @@ -4243,14 +4287,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 +4367,43 @@ 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; } bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint32_t *args, uint32_t length) @@ -4354,20 +4424,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 +4449,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 +4462,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 +4637,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/spirv_cross.hpp b/spirv_cross.hpp index 12dcae34e9..b25e1c19cb 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -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; @@ -817,8 +826,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,23 +835,37 @@ 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; }; @@ -856,6 +878,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/spirv_glsl.cpp b/spirv_glsl.cpp index 500c78b9da..5b71ab1d55 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -689,22 +689,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 +770,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 +789,6 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) return "rg32f"; case ImageFormatRg16f: return "rg16f"; - case ImageFormatRgba32i: return "rgba32i"; case ImageFormatRgba16i: @@ -820,7 +801,6 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) return "rg32i"; case ImageFormatRg16i: return "rg16i"; - case ImageFormatRgba32ui: return "rgba32ui"; case ImageFormatRgba16ui: @@ -833,71 +813,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; @@ -2246,7 +2201,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; @@ -2544,48 +2500,48 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop) op = type_to_glsl_constructor(type); break; -#define BOP(opname, x) \ - case Op##opname: \ - binary = true; \ - op = x; \ +#define GLSL_BOP(opname, x) \ + case Op##opname: \ + binary = true; \ + op = x; \ break -#define UOP(opname, x) \ - case Op##opname: \ - unary = true; \ - op = x; \ +#define GLSL_UOP(opname, x) \ + case Op##opname: \ + unary = true; \ + op = x; \ break - UOP(SNegate, "-"); - UOP(Not, "~"); - BOP(IAdd, "+"); - BOP(ISub, "-"); - BOP(IMul, "*"); - BOP(SDiv, "/"); - BOP(UDiv, "/"); - BOP(UMod, "%"); - BOP(SMod, "%"); - BOP(ShiftRightLogical, ">>"); - BOP(ShiftRightArithmetic, ">>"); - BOP(ShiftLeftLogical, "<<"); - BOP(BitwiseOr, "|"); - BOP(BitwiseXor, "^"); - BOP(BitwiseAnd, "&"); - BOP(LogicalOr, "||"); - BOP(LogicalAnd, "&&"); - UOP(LogicalNot, "!"); - BOP(LogicalEqual, "=="); - BOP(LogicalNotEqual, "!="); - BOP(IEqual, "=="); - BOP(INotEqual, "!="); - BOP(ULessThan, "<"); - BOP(SLessThan, "<"); - BOP(ULessThanEqual, "<="); - BOP(SLessThanEqual, "<="); - BOP(UGreaterThan, ">"); - BOP(SGreaterThan, ">"); - BOP(UGreaterThanEqual, ">="); - BOP(SGreaterThanEqual, ">="); + GLSL_UOP(SNegate, "-"); + GLSL_UOP(Not, "~"); + GLSL_BOP(IAdd, "+"); + GLSL_BOP(ISub, "-"); + GLSL_BOP(IMul, "*"); + GLSL_BOP(SDiv, "/"); + GLSL_BOP(UDiv, "/"); + GLSL_BOP(UMod, "%"); + GLSL_BOP(SMod, "%"); + GLSL_BOP(ShiftRightLogical, ">>"); + GLSL_BOP(ShiftRightArithmetic, ">>"); + GLSL_BOP(ShiftLeftLogical, "<<"); + GLSL_BOP(BitwiseOr, "|"); + GLSL_BOP(BitwiseXor, "^"); + GLSL_BOP(BitwiseAnd, "&"); + GLSL_BOP(LogicalOr, "||"); + GLSL_BOP(LogicalAnd, "&&"); + GLSL_UOP(LogicalNot, "!"); + GLSL_BOP(LogicalEqual, "=="); + GLSL_BOP(LogicalNotEqual, "!="); + GLSL_BOP(IEqual, "=="); + GLSL_BOP(INotEqual, "!="); + GLSL_BOP(ULessThan, "<"); + GLSL_BOP(SLessThan, "<"); + GLSL_BOP(ULessThanEqual, "<="); + GLSL_BOP(SLessThanEqual, "<="); + GLSL_BOP(UGreaterThan, ">"); + GLSL_BOP(SGreaterThan, ">"); + GLSL_BOP(UGreaterThanEqual, ">="); + GLSL_BOP(SGreaterThanEqual, ">="); case OpSelect: { @@ -2597,11 +2553,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; } @@ -2661,8 +2620,8 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop) break; } -#undef BOP -#undef UOP +#undef GLSL_BOP +#undef GLSL_UOP if (binary) { if (cop.arguments.size() < 2) @@ -3501,7 +3460,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 +3490,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 +3501,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 +3620,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 +3681,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 +3764,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 +3929,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 +3953,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 +3965,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 +3995,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 +4080,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 +4494,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 +4822,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 +4838,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 +4981,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 +5144,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 +5590,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. @@ -6081,17 +6094,17 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) auto opcode = static_cast(instruction.op); uint32_t length = instruction.length; -#define BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) -#define BOP_CAST(op, type) \ +#define GLSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) +#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 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 GLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #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_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 BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) -#define UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) +#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) switch (opcode) { @@ -6125,6 +6138,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; @@ -6182,11 +6198,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. @@ -6617,45 +6637,45 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // ALU case OpIsNan: - UFOP(isnan); + GLSL_UFOP(isnan); break; case OpIsInf: - UFOP(isinf); + GLSL_UFOP(isinf); break; case OpSNegate: case OpFNegate: - UOP(-); + GLSL_UOP(-); break; case OpIAdd: { // For simple arith ops, prefer the output type if there's a mismatch to avoid extra bitcasts. auto type = get(ops[0]).basetype; - BOP_CAST(+, type); + GLSL_BOP_CAST(+, type); break; } case OpFAdd: - BOP(+); + GLSL_BOP(+); break; case OpISub: { auto type = get(ops[0]).basetype; - BOP_CAST(-, type); + GLSL_BOP_CAST(-, type); break; } case OpFSub: - BOP(-); + GLSL_BOP(-); break; case OpIMul: { auto type = get(ops[0]).basetype; - BOP_CAST(*, type); + GLSL_BOP_CAST(*, type); break; } @@ -6671,7 +6691,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) e->need_transpose = true; } else - BOP(*); + GLSL_BOP(*); break; } @@ -6679,19 +6699,19 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpMatrixTimesScalar: case OpVectorTimesScalar: case OpMatrixTimesMatrix: - BOP(*); + GLSL_BOP(*); break; case OpOuterProduct: - BFOP(outerProduct); + GLSL_BFOP(outerProduct); break; case OpDot: - BFOP(dot); + GLSL_BFOP(dot); break; case OpTranspose: - UFOP(transpose); + GLSL_UFOP(transpose); break; case OpSRem: @@ -6713,67 +6733,67 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) } case OpSDiv: - BOP_CAST(/, SPIRType::Int); + GLSL_BOP_CAST(/, SPIRType::Int); break; case OpUDiv: - BOP_CAST(/, SPIRType::UInt); + GLSL_BOP_CAST(/, SPIRType::UInt); break; case OpFDiv: - BOP(/); + GLSL_BOP(/); break; case OpShiftRightLogical: - BOP_CAST(>>, SPIRType::UInt); + GLSL_BOP_CAST(>>, SPIRType::UInt); break; case OpShiftRightArithmetic: - BOP_CAST(>>, SPIRType::Int); + GLSL_BOP_CAST(>>, SPIRType::Int); break; case OpShiftLeftLogical: { auto type = get(ops[0]).basetype; - BOP_CAST(<<, type); + GLSL_BOP_CAST(<<, type); break; } case OpBitwiseOr: { auto type = get(ops[0]).basetype; - BOP_CAST(|, type); + GLSL_BOP_CAST(|, type); break; } case OpBitwiseXor: { auto type = get(ops[0]).basetype; - BOP_CAST (^, type); + GLSL_BOP_CAST (^, type); break; } case OpBitwiseAnd: { auto type = get(ops[0]).basetype; - BOP_CAST(&, type); + GLSL_BOP_CAST(&, type); break; } case OpNot: - UOP(~); + GLSL_UOP(~); break; case OpUMod: - BOP_CAST(%, SPIRType::UInt); + GLSL_BOP_CAST(%, SPIRType::UInt); break; case OpSMod: - BOP_CAST(%, SPIRType::Int); + GLSL_BOP_CAST(%, SPIRType::Int); break; case OpFMod: - BFOP(mod); + GLSL_BFOP(mod); break; case OpFRem: @@ -6800,11 +6820,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // Relational case OpAny: - UFOP(any); + GLSL_UFOP(any); break; case OpAll: - UFOP(all); + GLSL_UFOP(all); break; case OpSelect: @@ -6821,7 +6841,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (type.vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "||"); else - BOP(||); + GLSL_BOP(||); break; } @@ -6835,7 +6855,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (type.vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "&&"); else - BOP(&&); + GLSL_BOP(&&); break; } @@ -6843,18 +6863,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto &type = get(ops[0]); if (type.vecsize > 1) - UFOP(not); + GLSL_UFOP(not); else - UOP(!); + GLSL_UOP(!); break; } case OpIEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(equal, SPIRType::Int); + GLSL_BFOP_CAST(equal, SPIRType::Int); else - BOP_CAST(==, SPIRType::Int); + GLSL_BOP_CAST(==, SPIRType::Int); break; } @@ -6862,18 +6882,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpFOrdEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP(equal); + GLSL_BFOP(equal); else - BOP(==); + GLSL_BOP(==); break; } case OpINotEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(notEqual, SPIRType::Int); + GLSL_BFOP_CAST(notEqual, SPIRType::Int); else - BOP_CAST(!=, SPIRType::Int); + GLSL_BOP_CAST(!=, SPIRType::Int); break; } @@ -6881,9 +6901,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpFOrdNotEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP(notEqual); + GLSL_BFOP(notEqual); else - BOP(!=); + GLSL_BOP(!=); break; } @@ -6892,18 +6912,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpUGreaterThan ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(greaterThan, type); + GLSL_BFOP_CAST(greaterThan, type); else - BOP_CAST(>, type); + GLSL_BOP_CAST(>, type); break; } case OpFOrdGreaterThan: { if (expression_type(ops[2]).vecsize > 1) - BFOP(greaterThan); + GLSL_BFOP(greaterThan); else - BOP(>); + GLSL_BOP(>); break; } @@ -6912,18 +6932,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpUGreaterThanEqual ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(greaterThanEqual, type); + GLSL_BFOP_CAST(greaterThanEqual, type); else - BOP_CAST(>=, type); + GLSL_BOP_CAST(>=, type); break; } case OpFOrdGreaterThanEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP(greaterThanEqual); + GLSL_BFOP(greaterThanEqual); else - BOP(>=); + GLSL_BOP(>=); break; } @@ -6932,18 +6952,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpULessThan ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(lessThan, type); + GLSL_BFOP_CAST(lessThan, type); else - BOP_CAST(<, type); + GLSL_BOP_CAST(<, type); break; } case OpFOrdLessThan: { if (expression_type(ops[2]).vecsize > 1) - BFOP(lessThan); + GLSL_BFOP(lessThan); else - BOP(<); + GLSL_BOP(<); break; } @@ -6952,18 +6972,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpULessThanEqual ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(lessThanEqual, type); + GLSL_BFOP_CAST(lessThanEqual, type); else - BOP_CAST(<=, type); + GLSL_BOP_CAST(<=, type); break; } case OpFOrdLessThanEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP(lessThanEqual); + GLSL_BFOP(lessThanEqual); else - BOP(<=); + GLSL_BOP(<=); break; } @@ -7037,21 +7057,21 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // Derivatives case OpDPdx: - UFOP(dFdx); + GLSL_UFOP(dFdx); if (is_legacy_es()) require_extension_internal("GL_OES_standard_derivatives"); register_control_dependent_expression(ops[1]); break; case OpDPdy: - UFOP(dFdy); + GLSL_UFOP(dFdy); if (is_legacy_es()) require_extension_internal("GL_OES_standard_derivatives"); register_control_dependent_expression(ops[1]); break; case OpDPdxFine: - UFOP(dFdxFine); + GLSL_UFOP(dFdxFine); if (options.es) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); @@ -7062,7 +7082,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) break; case OpDPdyFine: - UFOP(dFdyFine); + GLSL_UFOP(dFdyFine); if (options.es) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); @@ -7077,14 +7097,14 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); } - UFOP(dFdxCoarse); + GLSL_UFOP(dFdxCoarse); if (options.version < 450) require_extension_internal("GL_ARB_derivative_control"); register_control_dependent_expression(ops[1]); break; case OpDPdyCoarse: - UFOP(dFdyCoarse); + GLSL_UFOP(dFdyCoarse); if (options.es) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); @@ -7095,14 +7115,14 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) break; case OpFwidth: - UFOP(fwidth); + GLSL_UFOP(fwidth); if (is_legacy_es()) require_extension_internal("GL_OES_standard_derivatives"); register_control_dependent_expression(ops[1]); break; case OpFwidthCoarse: - UFOP(fwidthCoarse); + GLSL_UFOP(fwidthCoarse); if (options.es) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); @@ -7113,7 +7133,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) break; case OpFwidthFine: - UFOP(fwidthFine); + GLSL_UFOP(fwidthFine); if (options.es) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); @@ -7126,21 +7146,21 @@ 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: case OpBitFieldUExtract: // TODO: The signedness of inputs is strict in GLSL, but not in SPIR-V, bitcast if necessary. - TFOP(bitfieldExtract); + GLSL_TFOP(bitfieldExtract); break; case OpBitReverse: - UFOP(bitfieldReverse); + GLSL_UFOP(bitfieldReverse); break; case OpBitCount: - UFOP(bitCount); + GLSL_UFOP(bitCount); break; // Atomics @@ -7177,7 +7197,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) flush_all_atomic_capable_variables(); // FIXME: Image? // OpAtomicLoad seems to only be relevant for atomic counters. - UFOP(atomicCounter); + GLSL_UFOP(atomicCounter); register_read(ops[1], ops[2], should_forward(ops[2])); break; @@ -7187,7 +7207,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpAtomicIIncrement: forced_temporaries.insert(ops[1]); // FIXME: Image? - UFOP(atomicCounterIncrement); + GLSL_UFOP(atomicCounterIncrement); flush_all_atomic_capable_variables(); register_read(ops[1], ops[2], should_forward(ops[2])); break; @@ -7195,7 +7215,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpAtomicIDecrement: forced_temporaries.insert(ops[1]); // FIXME: Image? - UFOP(atomicCounterDecrement); + GLSL_UFOP(atomicCounterDecrement); flush_all_atomic_capable_variables(); register_read(ops[1], ops[2], should_forward(ops[2])); break; @@ -7326,12 +7346,12 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { require_extension_internal("GL_ARB_texture_query_lod"); // For some reason, the ARB spec is all-caps. - BFOP(textureQueryLOD); + GLSL_BFOP(textureQueryLOD); } else if (options.es) SPIRV_CROSS_THROW("textureQueryLod not supported in ES profile."); else - BFOP(textureQueryLod); + GLSL_BFOP(textureQueryLod); register_control_dependent_expression(ops[1]); break; } @@ -7987,6 +8007,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; @@ -8375,7 +8419,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 +8492,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 +8542,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 +8760,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 +8769,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; } @@ -9123,6 +9165,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 +9189,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 +9418,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 +9437,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 +9485,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 +9702,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 +9952,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/spirv_glsl.hpp b/spirv_glsl.hpp index e02ef34802..7205def759 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -105,6 +105,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 @@ -276,7 +281,6 @@ protected: { for (uint32_t i = 0; i < indent; i++) (*buffer) << " "; - statement_inner(std::forward(ts)...); (*buffer) << '\n'; } @@ -404,6 +408,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, @@ -451,11 +458,11 @@ 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); bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, uint32_t start_offset = 0, uint32_t end_offset = std::numeric_limits::max()); @@ -484,7 +491,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; @@ -562,6 +569,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/spirv_hlsl.cpp b/spirv_hlsl.cpp index fffdf3129a..aaa05cc07e 100644 --- a/spirv_hlsl.cpp +++ b/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: @@ -3835,17 +3834,17 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) auto ops = stream(instruction); auto opcode = static_cast(instruction.op); -#define BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) -#define BOP_CAST(op, type) \ +#define HLSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) +#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 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 HLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #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) \ emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, hlsl_opcode_is_sign_invariant(opcode)) -#define BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) -#define UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) +#define HLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define HLSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) switch (opcode) { @@ -3914,39 +3913,39 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) } case OpDPdx: - UFOP(ddx); + HLSL_UFOP(ddx); register_control_dependent_expression(ops[1]); break; case OpDPdy: - UFOP(ddy); + HLSL_UFOP(ddy); register_control_dependent_expression(ops[1]); break; case OpDPdxFine: - UFOP(ddx_fine); + HLSL_UFOP(ddx_fine); register_control_dependent_expression(ops[1]); break; case OpDPdyFine: - UFOP(ddy_fine); + HLSL_UFOP(ddy_fine); register_control_dependent_expression(ops[1]); break; case OpDPdxCoarse: - UFOP(ddx_coarse); + HLSL_UFOP(ddx_coarse); register_control_dependent_expression(ops[1]); break; case OpDPdyCoarse: - UFOP(ddy_coarse); + HLSL_UFOP(ddy_coarse); register_control_dependent_expression(ops[1]); break; case OpFwidth: case OpFwidthCoarse: case OpFwidthFine: - UFOP(fwidth); + HLSL_UFOP(fwidth); register_control_dependent_expression(ops[1]); break; @@ -3959,7 +3958,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (type.vecsize > 1) emit_unrolled_unary_op(result_type, id, ops[2], "!"); else - UOP(!); + HLSL_UOP(!); break; } @@ -3971,7 +3970,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "=="); else - BOP_CAST(==, SPIRType::Int); + HLSL_BOP_CAST(==, SPIRType::Int); break; } @@ -3984,7 +3983,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "=="); else - BOP(==); + HLSL_BOP(==); break; } @@ -3996,7 +3995,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!="); else - BOP_CAST(!=, SPIRType::Int); + HLSL_BOP_CAST(!=, SPIRType::Int); break; } @@ -4009,7 +4008,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!="); else - BOP(!=); + HLSL_BOP(!=); break; } @@ -4023,7 +4022,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">"); else - BOP_CAST(>, type); + HLSL_BOP_CAST(>, type); break; } @@ -4035,7 +4034,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">"); else - BOP(>); + HLSL_BOP(>); break; } @@ -4049,7 +4048,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">="); else - BOP_CAST(>=, type); + HLSL_BOP_CAST(>=, type); break; } @@ -4061,7 +4060,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">="); else - BOP(>=); + HLSL_BOP(>=); break; } @@ -4075,7 +4074,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<"); else - BOP_CAST(<, type); + HLSL_BOP_CAST(<, type); break; } @@ -4087,7 +4086,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<"); else - BOP(<); + HLSL_BOP(<); break; } @@ -4101,7 +4100,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<="); else - BOP_CAST(<=, type); + HLSL_BOP_CAST(<=, type); break; } @@ -4113,7 +4112,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<="); else - BOP(<=); + HLSL_BOP(<=); break; } @@ -4418,18 +4417,18 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) } if (opcode == OpBitFieldSExtract) - TFOP(SPIRV_Cross_bitfieldSExtract); + HLSL_TFOP(SPIRV_Cross_bitfieldSExtract); else - TFOP(SPIRV_Cross_bitfieldUExtract); + HLSL_TFOP(SPIRV_Cross_bitfieldUExtract); break; } case OpBitCount: - UFOP(countbits); + HLSL_UFOP(countbits); break; case OpBitReverse: - UFOP(reversebits); + HLSL_UFOP(reversebits); break; default: @@ -4605,3 +4604,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/spirv_hlsl.hpp b/spirv_hlsl.hpp index df330d0588..bcc82b1043 100644 --- a/spirv_hlsl.hpp +++ b/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/spirv_msl.cpp b/spirv_msl.cpp index 984323b0d8..4f69c57150 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -534,19 +534,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 +1173,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)"); @@ -1506,16 +1543,16 @@ void CompilerMSL::emit_specialization_constants() void CompilerMSL::emit_instruction(const Instruction &instruction) { -#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); @@ -1527,81 +1564,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 @@ -1654,7 +1691,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]; \ @@ -1665,45 +1702,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 @@ -1827,7 +1864,7 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) break; } -#define ImgQry(qrytype) \ +#define MSL_ImgQry(qrytype) \ do \ { \ uint32_t rslt_type_id = ops[0]; \ @@ -1840,11 +1877,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 @@ -1926,7 +1963,7 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) e->need_transpose = true; } else - BOP(*); + MSL_BOP(*); break; } @@ -2429,8 +2466,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"; @@ -3553,17 +3591,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"; @@ -3586,25 +3620,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) { @@ -4035,8 +4064,8 @@ 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: @@ -4054,14 +4083,24 @@ 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)) 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; } @@ -4179,3 +4218,12 @@ 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 spirv_cross::SPIRType &) +{ +} + +void CompilerMSL::bitcast_to_builtin_store(uint32_t, std::string &, const spirv_cross::SPIRType &) +{ +} diff --git a/spirv_msl.hpp b/spirv_msl.hpp index 61f5eb2995..fe3401d91b 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -152,6 +152,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; @@ -215,6 +216,7 @@ public: SPVFuncImplFindSMsb, SPVFuncImplFindUMsb, SPVFuncImplArrayCopy, + SPVFuncImplTexelBufferCoords, SPVFuncImplInverse4x4, SPVFuncImplInverse3x3, SPVFuncImplInverse2x2, @@ -356,6 +358,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/spirv_reflect.cpp b/spirv_reflect.cpp new file mode 100644 index 0000000000..c322d972bd --- /dev/null +++ b/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/spirv_reflect.hpp b/spirv_reflect.hpp new file mode 100644 index 0000000000..e402fa8fb9 --- /dev/null +++ b/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/test_shaders.py b/test_shaders.py index 3efcf923d1..40e1c2e2c3 100755 --- a/test_shaders.py +++ b/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/test_shaders.sh b/test_shaders.sh index e96978a1e6..8a43afb4df 100755 --- a/test_shaders.sh +++ b/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/update_test_shaders.sh b/update_test_shaders.sh index 4bc87a1564..1582c6c3f3 100755 --- a/update_test_shaders.sh +++ b/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 From d8061ac0e95a97d284e5442e8f87b850e70e6ca8 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Wed, 4 Jul 2018 14:19:05 -0700 Subject: [PATCH 043/106] fix(macOS): Use correct availability macro check --- ui/drivers/cocoa/ui_cocoa_application.m | 4 +-- ui/drivers/cocoa/ui_cocoa_msg_window.m | 8 ++--- ui/drivers/ui_cocoa.m | 42 ++++++++++++------------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/ui/drivers/cocoa/ui_cocoa_application.m b/ui/drivers/cocoa/ui_cocoa_application.m index 1f51257476..54ecf46ef1 100644 --- a/ui/drivers/cocoa/ui_cocoa_application.m +++ b/ui/drivers/cocoa/ui_cocoa_application.m @@ -23,8 +23,8 @@ #include "cocoa_common.h" #include "../../ui_companion_driver.h" -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 -static const NSEventMask NSEventMaskAny = NSAnyEventMask; +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 +#define NSEventMaskAny NSAnyEventMask #endif static void* ui_application_cocoa_initialize(void) diff --git a/ui/drivers/cocoa/ui_cocoa_msg_window.m b/ui/drivers/cocoa/ui_cocoa_msg_window.m index b166c693d7..d85bc8e99e 100644 --- a/ui/drivers/cocoa/ui_cocoa_msg_window.m +++ b/ui/drivers/cocoa/ui_cocoa_msg_window.m @@ -25,10 +25,10 @@ #include "../../ui_companion_driver.h" -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 -static const NSAlertStyle NSAlertStyleCritical = NSCriticalAlertStyle; -static const NSAlertStyle NSAlertStyleWarning = NSWarningAlertStyle; -static const NSAlertStyle NSAlertStyleInformational = NSInformationalAlertStyle; +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 +#define NSAlertStyleCritical NSCriticalAlertStyle +#define NSAlertStyleWarning NSWarningAlertStyle +#define NSAlertStyleInformational NSInformationalAlertStyle #endif static enum ui_msg_window_response ui_msg_window_cocoa_dialog(ui_msg_window_state *state, enum ui_msg_window_type type) diff --git a/ui/drivers/ui_cocoa.m b/ui/drivers/ui_cocoa.m index 4070cfe873..c318af6e0a 100644 --- a/ui/drivers/ui_cocoa.m +++ b/ui/drivers/ui_cocoa.m @@ -71,29 +71,29 @@ static void app_terminate(void) @implementation RApplication -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 -static const NSEventType NSEventTypeKeyDown = NSKeyDown; -static const NSEventType NSEventTypeKeyUp = NSKeyUp; -static const NSEventType NSEventTypeFlagsChanged = NSFlagsChanged; -static const NSEventType NSEventTypeMouseMoved = NSMouseMoved; -static const NSEventType NSEventTypeLeftMouseDragged = NSLeftMouseDragged; -static const NSEventType NSEventTypeRightMouseDragged = NSRightMouseDragged; -static const NSEventType NSEventTypeOtherMouseDragged = NSOtherMouseDragged; -static const NSEventType NSEventTypeLeftMouseDown = NSLeftMouseDown; -static const NSEventType NSEventTypeRightMouseDown = NSRightMouseDown; -static const NSEventType NSEventTypeOtherMouseDown = NSOtherMouseDown; -static const NSEventType NSEventTypeLeftMouseUp = NSLeftMouseUp; -static const NSEventType NSEventTypeRightMouseUp = NSRightMouseUp; -static const NSEventType NSEventTypeOtherMouseUp = NSOtherMouseUp; -static const NSEventType NSEventTypeScrollWheel = NSScrollWheel; +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 +#define NSEventTypeKeyDown NSKeyDown +#define NSEventTypeKeyUp NSKeyUp +#define NSEventTypeFlagsChanged NSFlagsChanged +#define NSEventTypeMouseMoved NSMouseMoved +#define NSEventTypeLeftMouseDragged NSLeftMouseDragged +#define NSEventTypeRightMouseDragged NSRightMouseDragged +#define NSEventTypeOtherMouseDragged NSOtherMouseDragged +#define NSEventTypeLeftMouseDown NSLeftMouseDown +#define NSEventTypeRightMouseDown NSRightMouseDown +#define NSEventTypeOtherMouseDown NSOtherMouseDown +#define NSEventTypeLeftMouseUp NSLeftMouseUp +#define NSEventTypeRightMouseUp NSRightMouseUp +#define NSEventTypeOtherMouseUp NSOtherMouseUp +#define NSEventTypeScrollWheel NSScrollWheel // modifier flags -static const NSEventModifierFlags NSEventModifierFlagCapsLock = NSAlphaShiftKeyMask; -static const NSEventModifierFlags NSEventModifierFlagShift = NSShiftKeyMask; -static const NSEventModifierFlags NSEventModifierFlagControl = NSControlKeyMask; -static const NSEventModifierFlags NSEventModifierFlagOption = NSAlternateKeyMask; -static const NSEventModifierFlags NSEventModifierFlagCommand = NSCommandKeyMask; -static const NSEventModifierFlags NSEventModifierFlagNumericPad= NSNumericPadKeyMask; +#define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask +#define NSEventModifierFlagShift NSShiftKeyMask +#define NSEventModifierFlagControl NSControlKeyMask +#define NSEventModifierFlagOption NSAlternateKeyMask +#define NSEventModifierFlagCommand NSCommandKeyMask +#define NSEventModifierFlagNumericPad NSNumericPadKeyMask #endif - (void)sendEvent:(NSEvent *)event From d1712d7c5ad3388201d0068b0be703a4bbcb9207 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Wed, 4 Jul 2018 15:26:49 -0700 Subject: [PATCH 044/106] fix(macOS): Fix older targets --- ui/drivers/cocoa/ui_cocoa_application.m | 2 +- ui/drivers/cocoa/ui_cocoa_msg_window.m | 2 +- ui/drivers/ui_cocoa.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/drivers/cocoa/ui_cocoa_application.m b/ui/drivers/cocoa/ui_cocoa_application.m index 54ecf46ef1..afb6740d43 100644 --- a/ui/drivers/cocoa/ui_cocoa_application.m +++ b/ui/drivers/cocoa/ui_cocoa_application.m @@ -23,7 +23,7 @@ #include "cocoa_common.h" #include "../../ui_companion_driver.h" -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 #define NSEventMaskAny NSAnyEventMask #endif diff --git a/ui/drivers/cocoa/ui_cocoa_msg_window.m b/ui/drivers/cocoa/ui_cocoa_msg_window.m index d85bc8e99e..faf6739b99 100644 --- a/ui/drivers/cocoa/ui_cocoa_msg_window.m +++ b/ui/drivers/cocoa/ui_cocoa_msg_window.m @@ -25,7 +25,7 @@ #include "../../ui_companion_driver.h" -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 #define NSAlertStyleCritical NSCriticalAlertStyle #define NSAlertStyleWarning NSWarningAlertStyle #define NSAlertStyleInformational NSInformationalAlertStyle diff --git a/ui/drivers/ui_cocoa.m b/ui/drivers/ui_cocoa.m index c318af6e0a..6f607c041b 100644 --- a/ui/drivers/ui_cocoa.m +++ b/ui/drivers/ui_cocoa.m @@ -71,7 +71,7 @@ static void app_terminate(void) @implementation RApplication -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 #define NSEventTypeKeyDown NSKeyDown #define NSEventTypeKeyUp NSKeyUp #define NSEventTypeFlagsChanged NSFlagsChanged From aa9ebf5f29e1a22a5050572353b122915db1451d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 5 Jul 2018 02:10:00 +0200 Subject: [PATCH 045/106] Squashed 'libretro-common/' changes from 1548bee835..b857ce7fc5 b857ce7fc5 Cleanups git-subtree-dir: libretro-common git-subtree-split: b857ce7fc55d725246a095f766e2bb82bb1d4ce6 --- streams/file_stream.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/streams/file_stream.c b/streams/file_stream.c index 2718fbc49e..06b587b005 100644 --- a/streams/file_stream.c +++ b/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++; } From c3d469dd8eb2048bde593169f6318f36943aaeeb Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 5 Jul 2018 02:42:16 +0200 Subject: [PATCH 046/106] Revert "Moved WIN32 message loop out of win32_check_window, and into just before the call to runloop_iterate" This reverts commit 71086283691f0247459df827de41de472a4874a2. --- frontend/frontend.c | 7 +------ gfx/common/win32_common.c | 6 +++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/frontend/frontend.c b/frontend/frontend.c index 1ae4abc3f0..b144761490 100644 --- a/frontend/frontend.c +++ b/frontend/frontend.c @@ -138,12 +138,7 @@ int rarch_main(int argc, char *argv[], void *data) do { unsigned sleep_ms = 0; - int ret; - const ui_application_t *application = - ui_companion_driver_get_application_ptr(); - if (application) - application->process_events(); - ret = runloop_iterate(&sleep_ms); + int ret = runloop_iterate(&sleep_ms); if (ret == 1 && sleep_ms > 0) retro_sleep(sleep_ms); diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index b4bcbc0481..a95ee155c9 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -1117,7 +1117,12 @@ 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(); *quit = g_win32_quit; +#endif if (g_win32_resized) { @@ -1126,7 +1131,6 @@ 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) From 544802e1a82ba90e58cfe82929beb4868d8a84a3 Mon Sep 17 00:00:00 2001 From: Zoran Vuckovic Date: Thu, 5 Jul 2018 20:01:23 +0200 Subject: [PATCH 047/106] Add dinput rumble support --- input/drivers_joypad/dinput_joypad.c | 82 +++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) 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", }; From 838dcc8a5a3b57f7d9e737ba264428422d336c99 Mon Sep 17 00:00:00 2001 From: Dwedit Date: Thu, 5 Jul 2018 20:07:01 -0500 Subject: [PATCH 048/106] Move Message loop call to right before `video_driver_get_status` call --- gfx/common/win32_common.c | 6 +----- retroarch.c | 9 ++++++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index a95ee155c9..b4bcbc0481 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -1117,12 +1117,7 @@ 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(); *quit = g_win32_quit; -#endif if (g_win32_resized) { @@ -1131,6 +1126,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) diff --git a/retroarch.c b/retroarch.c index f95d909f62..a20fa064a2 100644 --- a/retroarch.c +++ b/retroarch.c @@ -2535,6 +2535,13 @@ static enum runloop_state runloop_check_state( } } + { + const ui_application_t *application = + ui_companion_driver_get_application_ptr(); + if (application) + application->process_events(); + } + video_driver_get_status(&frame_count, &is_alive, &is_focused); #ifdef HAVE_MENU @@ -2687,7 +2694,7 @@ static enum runloop_state runloop_check_state( if (focused || !runloop_idle) { bool libretro_running = menu_display_libretro_running( - rarch_is_inited, + rarch_is_inited, (current_core_type == CORE_TYPE_DUMMY)); menu_driver_render(runloop_idle, rarch_is_inited, From 22d6930488c3556e5423579be1d3fa3d2adcc51b Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 6 Jul 2018 04:45:52 +0200 Subject: [PATCH 049/106] Some MSVC 2010 360 buildfixes --- gfx/drivers_font/xdk360_fonts.cpp | 4 ++-- gfx/drivers_renderchain/d3d9_hlsl_renderchain.c | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) 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_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) From c8366b0b0f6ab996adcbcf1ee09a3ed6298e7d4e Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 6 Jul 2018 05:17:39 +0200 Subject: [PATCH 050/106] Squashed 'deps/SPIRV-Cross/' changes from f96c9f9fb4..0f59016635 0f59016635 Merge pull request #636 from KhronosGroup/cfg-refactor 2bf57d6dff Deal with composite constants in variable initializer. dcddd5326e Add LUT test cases for OpVariable with initializer. af290ede87 Remove some redundant spvArrayCopy declarations. 8c314112b4 Run format_all.sh. 5582523d9a Add some tests for LUT promotion. 5143695080 Don't need to enclose expression for arrays. d29f48ef06 Deduce constant LUTs from read-write variables. b5ed706860 Hoist out variable scope analysis. c26c41b26b Make the CFGs for all active functions available. 6fdadb9218 Track partial writes as well. 7216129377 Hoist out the inline VariableAccessHandler class. git-subtree-dir: deps/SPIRV-Cross git-subtree-split: 0f590166359f4ee35d80040f0965417f482cd6b1 --- .../frag/lut-promotion-initializer.asm.frag | 57 ++ .../frag/array-lut-no-loop-variable.frag | 3 +- .../frag/constant-composites.frag | 2 +- .../opt/shaders-hlsl/frag/lut-promotion.frag | 57 ++ .../frag/lut-promotion-initializer.asm.frag | 69 ++ .../frag/array-lut-no-loop-variable.frag | 19 +- .../opt/shaders-msl/frag/constant-array.frag | 6 +- .../shaders-msl/frag/constant-composites.frag | 2 +- .../opt/shaders-msl/frag/lut-promotion.frag | 69 ++ .../frag/lut-promotion-initializer.asm.frag | 42 + .../opt/shaders/comp/generate_height.comp | 4 +- .../opt/shaders/flatten/copy.flatten.vert | 2 +- .../opt/shaders/flatten/dynamic.flatten.vert | 4 +- ...multi-dimensional.desktop.flatten_dim.frag | 2 +- .../frag/array-lut-no-loop-variable.frag | 5 +- .../opt/shaders/frag/constant-array.frag | 9 +- .../opt/shaders/frag/constant-composites.frag | 2 +- reference/opt/shaders/frag/lut-promotion.frag | 42 + .../frag/lut-promotion-initializer.asm.frag | 55 ++ .../frag/array-lut-no-loop-variable.frag | 3 +- .../frag/constant-composites.frag | 2 +- .../shaders-hlsl/frag/lut-promotion.frag | 55 ++ .../frag/lut-promotion-initializer.asm.frag | 67 ++ .../asm/frag/op-constant-null.asm.frag | 17 - .../shaders-msl/comp/composite-construct.comp | 3 +- .../frag/array-lut-no-loop-variable.frag | 19 +- .../shaders-msl/frag/constant-array.frag | 8 +- .../shaders-msl/frag/constant-composites.frag | 2 +- reference/shaders-msl/frag/lut-promotion.frag | 67 ++ .../frag/lut-promotion-initializer.asm.frag | 40 + .../asm/frag/op-constant-null.asm.frag | 3 +- .../shaders/comp/composite-construct.comp | 7 +- reference/shaders/flatten/copy.flatten.vert | 2 +- .../shaders/flatten/dynamic.flatten.vert | 4 +- ...multi-dimensional.desktop.flatten_dim.frag | 2 +- .../frag/array-lut-no-loop-variable.frag | 5 +- reference/shaders/frag/constant-array.frag | 11 +- .../shaders/frag/constant-composites.frag | 2 +- reference/shaders/frag/lut-promotion.frag | 40 + .../frag/lut-promotion-initializer.asm.frag | 195 +++++ shaders-hlsl/frag/lut-promotion.frag | 44 ++ .../frag/lut-promotion-initializer.asm.frag | 195 +++++ shaders-msl/frag/lut-promotion.frag | 44 ++ .../frag/lut-promotion-initializer.asm.frag | 195 +++++ shaders/frag/lut-promotion.frag | 44 ++ spirv_common.hpp | 4 +- spirv_cpp.cpp | 1 + spirv_cross.cpp | 734 +++++++++++------- spirv_cross.hpp | 53 +- spirv_glsl.cpp | 84 +- spirv_glsl.hpp | 3 +- spirv_hlsl.cpp | 1 + spirv_msl.cpp | 33 +- spirv_msl.hpp | 1 + 54 files changed, 2019 insertions(+), 422 deletions(-) create mode 100644 reference/opt/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 reference/opt/shaders-hlsl/frag/lut-promotion.frag create mode 100644 reference/opt/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 reference/opt/shaders-msl/frag/lut-promotion.frag create mode 100644 reference/opt/shaders/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 reference/opt/shaders/frag/lut-promotion.frag create mode 100644 reference/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 reference/shaders-hlsl/frag/lut-promotion.frag create mode 100644 reference/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 reference/shaders-msl/frag/lut-promotion.frag create mode 100644 reference/shaders/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 reference/shaders/frag/lut-promotion.frag create mode 100644 shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 shaders-hlsl/frag/lut-promotion.frag create mode 100644 shaders-msl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 shaders-msl/frag/lut-promotion.frag create mode 100644 shaders/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 shaders/frag/lut-promotion.frag diff --git a/reference/opt/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag b/reference/opt/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..5deae3a569 --- /dev/null +++ b/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/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag b/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag index 8cb52f0a4d..3adf7d9852 100644 --- a/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag +++ b/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/reference/opt/shaders-hlsl/frag/constant-composites.frag b/reference/opt/shaders-hlsl/frag/constant-composites.frag index 0514eef1ee..2613e1c2c5 100644 --- a/reference/opt/shaders-hlsl/frag/constant-composites.frag +++ b/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/reference/opt/shaders-hlsl/frag/lut-promotion.frag b/reference/opt/shaders-hlsl/frag/lut-promotion.frag new file mode 100644 index 0000000000..aae0d39de2 --- /dev/null +++ b/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/reference/opt/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag b/reference/opt/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..9db6b5470d --- /dev/null +++ b/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/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag b/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag index 6c8299fa91..60868acd87 100644 --- a/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag +++ b/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/reference/opt/shaders-msl/frag/constant-array.frag b/reference/opt/shaders-msl/frag/constant-array.frag index 63576f109c..bb55934c6d 100644 --- a/reference/opt/shaders-msl/frag/constant-array.frag +++ b/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/reference/opt/shaders-msl/frag/constant-composites.frag b/reference/opt/shaders-msl/frag/constant-composites.frag index ec5d66e86d..cb3e592337 100644 --- a/reference/opt/shaders-msl/frag/constant-composites.frag +++ b/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/reference/opt/shaders-msl/frag/lut-promotion.frag b/reference/opt/shaders-msl/frag/lut-promotion.frag new file mode 100644 index 0000000000..4efdf4a196 --- /dev/null +++ b/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/reference/opt/shaders/asm/frag/lut-promotion-initializer.asm.frag b/reference/opt/shaders/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..d88c0e36d9 --- /dev/null +++ b/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/reference/opt/shaders/comp/generate_height.comp b/reference/opt/shaders/comp/generate_height.comp index 17d3073d2e..ff96e7505a 100644 --- a/reference/opt/shaders/comp/generate_height.comp +++ b/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/reference/opt/shaders/flatten/copy.flatten.vert b/reference/opt/shaders/flatten/copy.flatten.vert index 27ce450d62..33caec4f78 100644 --- a/reference/opt/shaders/flatten/copy.flatten.vert +++ b/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/reference/opt/shaders/flatten/dynamic.flatten.vert b/reference/opt/shaders/flatten/dynamic.flatten.vert index 8fc8ff6eed..7129af2869 100644 --- a/reference/opt/shaders/flatten/dynamic.flatten.vert +++ b/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/reference/opt/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag b/reference/opt/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag index 3657298f18..2ecee89377 100644 --- a/reference/opt/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag +++ b/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/reference/opt/shaders/frag/array-lut-no-loop-variable.frag b/reference/opt/shaders/frag/array-lut-no-loop-variable.frag index 7bdfe543e4..869a76e4e7 100644 --- a/reference/opt/shaders/frag/array-lut-no-loop-variable.frag +++ b/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/reference/opt/shaders/frag/constant-array.frag b/reference/opt/shaders/frag/constant-array.frag index 2af87ad80d..749fc80980 100644 --- a/reference/opt/shaders/frag/constant-array.frag +++ b/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/reference/opt/shaders/frag/constant-composites.frag b/reference/opt/shaders/frag/constant-composites.frag index b105dbd26c..c65c60613d 100644 --- a/reference/opt/shaders/frag/constant-composites.frag +++ b/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/reference/opt/shaders/frag/lut-promotion.frag b/reference/opt/shaders/frag/lut-promotion.frag new file mode 100644 index 0000000000..21c925796e --- /dev/null +++ b/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/reference/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag b/reference/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..a7aec01bad --- /dev/null +++ b/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/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag b/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag index 04d4d7fa75..407fa2bda4 100644 --- a/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag +++ b/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/reference/shaders-hlsl/frag/constant-composites.frag b/reference/shaders-hlsl/frag/constant-composites.frag index 0514eef1ee..2613e1c2c5 100644 --- a/reference/shaders-hlsl/frag/constant-composites.frag +++ b/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/reference/shaders-hlsl/frag/lut-promotion.frag b/reference/shaders-hlsl/frag/lut-promotion.frag new file mode 100644 index 0000000000..d148bc12cb --- /dev/null +++ b/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/reference/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag b/reference/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..72d41f3e70 --- /dev/null +++ b/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/reference/shaders-msl/asm/frag/op-constant-null.asm.frag b/reference/shaders-msl/asm/frag/op-constant-null.asm.frag index 1d9d11c978..670cb5f371 100644 --- a/reference/shaders-msl/asm/frag/op-constant-null.asm.frag +++ b/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/reference/shaders-msl/comp/composite-construct.comp b/reference/shaders-msl/comp/composite-construct.comp index fb4ed1f101..ef0412b1dc 100644 --- a/reference/shaders-msl/comp/composite-construct.comp +++ b/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/reference/shaders-msl/frag/array-lut-no-loop-variable.frag b/reference/shaders-msl/frag/array-lut-no-loop-variable.frag index 8943a8e5a1..37f83aae26 100644 --- a/reference/shaders-msl/frag/array-lut-no-loop-variable.frag +++ b/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/reference/shaders-msl/frag/constant-array.frag b/reference/shaders-msl/frag/constant-array.frag index 09f123b29a..212d4f0803 100644 --- a/reference/shaders-msl/frag/constant-array.frag +++ b/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/reference/shaders-msl/frag/constant-composites.frag b/reference/shaders-msl/frag/constant-composites.frag index ec5d66e86d..cb3e592337 100644 --- a/reference/shaders-msl/frag/constant-composites.frag +++ b/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/reference/shaders-msl/frag/lut-promotion.frag b/reference/shaders-msl/frag/lut-promotion.frag new file mode 100644 index 0000000000..a8eda3e13a --- /dev/null +++ b/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/reference/shaders/asm/frag/lut-promotion-initializer.asm.frag b/reference/shaders/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..c08bc2c781 --- /dev/null +++ b/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/reference/shaders/asm/frag/op-constant-null.asm.frag b/reference/shaders/asm/frag/op-constant-null.asm.frag index c4ae981f64..970b4c4a66 100644 --- a/reference/shaders/asm/frag/op-constant-null.asm.frag +++ b/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/reference/shaders/comp/composite-construct.comp b/reference/shaders/comp/composite-construct.comp index 91bb5348f5..3018be8f1b 100644 --- a/reference/shaders/comp/composite-construct.comp +++ b/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/reference/shaders/flatten/copy.flatten.vert b/reference/shaders/flatten/copy.flatten.vert index 2bdd723886..6416a4f425 100644 --- a/reference/shaders/flatten/copy.flatten.vert +++ b/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/reference/shaders/flatten/dynamic.flatten.vert b/reference/shaders/flatten/dynamic.flatten.vert index 6214ca450a..8be397ea3f 100644 --- a/reference/shaders/flatten/dynamic.flatten.vert +++ b/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/reference/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag b/reference/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag index 21c3363ca6..ef6bb526ab 100644 --- a/reference/shaders/flatten/multi-dimensional.desktop.flatten_dim.frag +++ b/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/reference/shaders/frag/array-lut-no-loop-variable.frag b/reference/shaders/frag/array-lut-no-loop-variable.frag index 54d7bf774c..baf2302519 100644 --- a/reference/shaders/frag/array-lut-no-loop-variable.frag +++ b/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/reference/shaders/frag/constant-array.frag b/reference/shaders/frag/constant-array.frag index 4da9b8948b..be033f3873 100644 --- a/reference/shaders/frag/constant-array.frag +++ b/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/reference/shaders/frag/constant-composites.frag b/reference/shaders/frag/constant-composites.frag index b105dbd26c..c65c60613d 100644 --- a/reference/shaders/frag/constant-composites.frag +++ b/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/reference/shaders/frag/lut-promotion.frag b/reference/shaders/frag/lut-promotion.frag new file mode 100644 index 0000000000..019393f9f3 --- /dev/null +++ b/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/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag b/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..320e5ebfbd --- /dev/null +++ b/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/shaders-hlsl/frag/lut-promotion.frag b/shaders-hlsl/frag/lut-promotion.frag new file mode 100644 index 0000000000..0cdc8148f9 --- /dev/null +++ b/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/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag b/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..320e5ebfbd --- /dev/null +++ b/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/shaders-msl/frag/lut-promotion.frag b/shaders-msl/frag/lut-promotion.frag new file mode 100644 index 0000000000..0cdc8148f9 --- /dev/null +++ b/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/shaders/asm/frag/lut-promotion-initializer.asm.frag b/shaders/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000000..320e5ebfbd --- /dev/null +++ b/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/shaders/frag/lut-promotion.frag b/shaders/frag/lut-promotion.frag new file mode 100644 index 0000000000..0cdc8148f9 --- /dev/null +++ b/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/spirv_common.hpp b/spirv_common.hpp index e3fb2b7238..0ea94fcb0c 100644 --- a/spirv_common.hpp +++ b/spirv_common.hpp @@ -764,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 @@ -1102,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/spirv_cpp.cpp b/spirv_cpp.cpp index 9302a07441..7d781e9172 100644 --- a/spirv_cpp.cpp +++ b/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/spirv_cross.cpp b/spirv_cross.cpp index 3e460e9f61..dd2a44ca33 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -3647,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, @@ -3951,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()) @@ -3978,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; } } @@ -4006,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; @@ -4027,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) @@ -4040,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); } } @@ -4051,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; @@ -4066,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; @@ -4077,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. @@ -4133,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; } } @@ -4406,6 +4552,72 @@ bool Compiler::CombinedImageSamplerDrefHandler::handle(spv::Op opcode, const uin 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) { if (length < 3) diff --git a/spirv_cross.hpp b/spirv_cross.hpp index b25e1c19cb..159e1988be 100644 --- a/spirv_cross.hpp +++ b/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. @@ -676,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); @@ -869,6 +867,55 @@ protected: 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; diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 5b71ab1d55..a973274616 100644 --- a/spirv_glsl.cpp +++ b/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(); @@ -1650,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); @@ -2071,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) @@ -2298,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--; @@ -2424,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) @@ -6190,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]); @@ -8289,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. @@ -8306,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; } @@ -8908,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); @@ -8969,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); diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index 7205def759..f5cc6ceb86 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -374,7 +374,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); @@ -463,6 +463,7 @@ protected: 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 = std::numeric_limits::max()); diff --git a/spirv_hlsl.cpp b/spirv_hlsl.cpp index aaa05cc07e..34e1d19fb5 100644 --- a/spirv_hlsl.cpp +++ b/spirv_hlsl.cpp @@ -4572,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(); diff --git a/spirv_msl.cpp b/spirv_msl.cpp index 4f69c57150..3f8de118b1 100644 --- a/spirv_msl.cpp +++ b/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(); @@ -2109,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, @@ -4072,6 +4078,7 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o { // 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; @@ -4088,7 +4095,13 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o 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; break; @@ -4209,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) @@ -4220,10 +4233,22 @@ void CompilerMSL::remap_constexpr_sampler(uint32_t id, const spirv_cross::MSLCon } // MSL always declares builtins with their SPIR-V type. -void CompilerMSL::bitcast_from_builtin_load(uint32_t, std::string &, const spirv_cross::SPIRType &) +void CompilerMSL::bitcast_from_builtin_load(uint32_t, std::string &, const SPIRType &) { } -void CompilerMSL::bitcast_to_builtin_store(uint32_t, std::string &, const spirv_cross::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/spirv_msl.hpp b/spirv_msl.hpp index fe3401d91b..7997cd30fb 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -293,6 +293,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; From 41ce61974d3ea92acda90d122574a477c2a6aecc Mon Sep 17 00:00:00 2001 From: Dwedit Date: Fri, 6 Jul 2018 09:53:31 -0500 Subject: [PATCH 051/106] A hack to invalidate the `frame_cache_data` pointer during the following events: Load Game, Reset, Init, Unload, Unload Game, Video Driver Destroy, Video Driver Free, Video Driver Init This prevents an invalid pointer from being read if the first frame starts out paused. --- core_impl.c | 11 +++++++++++ gfx/video_driver.c | 3 +++ 2 files changed, 14 insertions(+) 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/gfx/video_driver.c b/gfx/video_driver.c index 56f4bd3a90..5d0d50452d 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -1595,6 +1595,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 +1780,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 +1794,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) From 0f39ef424760ac05a3acbfdf79940ef5ed881c23 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Fri, 6 Jul 2018 23:03:15 -0400 Subject: [PATCH 052/106] [#6955] Update hexspeak magic pointer code Fixes #6955 --- retroarch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/retroarch.c b/retroarch.c index a20fa064a2..81e8057f96 100644 --- a/retroarch.c +++ b/retroarch.c @@ -194,7 +194,7 @@ static char runtime_shader_preset[255] = {0}; #ifdef HAVE_THREAD_STORAGE static sthread_tls_t rarch_tls; -const void *MAGIC_POINTER = (void*)(uintptr_t)0xB16B00B5; +const void *MAGIC_POINTER = (void*)(uintptr_t)0x0DEFACED; #endif static retro_bits_t has_set_libretro_device; From 01df2c5e88480b781aaab6717c4a38eb408774ee Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Fri, 6 Jul 2018 23:37:15 -0700 Subject: [PATCH 053/106] fix(Metal): Check for nil drawable --- gfx/common/metal/Context.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index 5e3fae6012..59066fccf6 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -280,7 +280,11 @@ dispatch_semaphore_signal(inflight); }]; - [_commandBuffer presentDrawable:self.nextDrawable]; + if (self.nextDrawable) + { + [_commandBuffer presentDrawable:self.nextDrawable]; + } + [_commandBuffer commit]; _commandBuffer = nil; From 74e1f24e9070537ba917c48b507bb44e47e4f085 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Fri, 6 Jul 2018 23:38:34 -0700 Subject: [PATCH 054/106] feat(Metal): Full screen support * I believe there is a race condition where the viewport is not set correctly on launch --- gfx/drivers/metal.m | 15 +++++++- .../RetroArch_Metal.xcodeproj/project.pbxproj | 38 +++++++++++++++++++ ui/drivers/ui_cocoa.m | 20 ++++++++-- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index fbfb3c48e4..644b33a60e 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -77,7 +77,6 @@ static bool metal_frame(void *data, const void *frame, static void metal_set_nonblock_state(void *data, bool non_block) { - RARCH_LOG("[Metal]: set non block: %s\n", non_block ? "ON" : "OFF"); MetalDriver *md = (__bridge MetalDriver *)data; md.context.displaySyncEnabled = !non_block; } @@ -87,6 +86,11 @@ 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; @@ -129,7 +133,10 @@ static void metal_free(void *data) static void metal_set_viewport(void *data, unsigned viewport_width, unsigned viewport_height, bool force_full, bool allow_rotate) { - //RARCH_LOG("[Metal]: set_viewport %dx%d\n", viewport_width, viewport_height); +// 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) @@ -174,6 +181,9 @@ static void metal_set_video_mode(void *data, unsigned width, unsigned height, bool fullscreen) { + RARCH_LOG("[Metal]: set_video_mode res=%dx%d fullscreen=%s\n", + width, height, + fullscreen ? "YES" : "NO"); MetalDriver *md = (__bridge MetalDriver *)data; gfx_ctx_mode_t mode = { .width = width, @@ -397,6 +407,7 @@ video_driver_t video_metal = { .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/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj index e8bf2b08b3..827745f550 100644 --- a/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj @@ -357,6 +357,21 @@ 05A8E23720A63CB40084ABDA /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; 05A8E23920A63CED0084ABDA /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; }; 05A8E23B20A63CF50084ABDA /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 05B5F90D20ED6A03009C521F /* content.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = content.h; path = ../../content.h; sourceTree = ""; }; + 05B5F90E20ED6A03009C521F /* core_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = core_type.h; path = ../../core_type.h; sourceTree = ""; }; + 05B5F90F20ED6A03009C521F /* command.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = command.c; path = ../../command.c; sourceTree = ""; }; + 05B5F91120ED6AAE009C521F /* retroarch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = retroarch.c; path = ../../retroarch.c; sourceTree = ""; }; + 05B5F91220ED6AAF009C521F /* retroarch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = retroarch.h; path = ../../retroarch.h; sourceTree = ""; }; + 05BF821420ED69D100D95B19 /* config.def.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = config.def.h; path = ../../config.def.h; sourceTree = ""; }; + 05BF821520ED69D100D95B19 /* core_impl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = core_impl.c; path = ../../core_impl.c; sourceTree = ""; }; + 05BF821620ED69D100D95B19 /* config.features.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = config.features.h; path = ../../config.features.h; sourceTree = ""; }; + 05BF821720ED69D100D95B19 /* command.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = command.h; path = ../../command.h; sourceTree = ""; }; + 05BF821820ED69D100D95B19 /* core_info.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = core_info.c; path = ../../core_info.c; sourceTree = ""; }; + 05BF821920ED69D100D95B19 /* core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = core.h; path = ../../core.h; sourceTree = ""; }; + 05BF821A20ED69D100D95B19 /* configuration.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = configuration.c; path = ../../configuration.c; sourceTree = ""; }; + 05BF821B20ED69D100D95B19 /* core_info.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = core_info.h; path = ../../core_info.h; sourceTree = ""; }; + 05BF821C20ED69D100D95B19 /* config.def.keybinds.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = config.def.keybinds.h; path = ../../config.def.keybinds.h; sourceTree = ""; }; + 05BF821D20ED69D100D95B19 /* configuration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = configuration.h; path = ../../configuration.h; sourceTree = ""; }; 05C5D53320E3DD0900654EE4 /* input_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_types.h; sourceTree = ""; }; 05C5D53420E3DD0900654EE4 /* input_remote.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_remote.c; sourceTree = ""; }; 05C5D53720E3DD0900654EE4 /* nullinput.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nullinput.c; sourceTree = ""; }; @@ -542,6 +557,7 @@ isa = PBXGroup; children = ( 0538874F20DE117B00769232 /* libretro-common */, + 05BF821320ED69A500D95B19 /* core */, 05A8C59520DB72F000FF7857 /* frontend */, 05A8C5D420DB72F000FF7857 /* gfx */, 05A8C57120DB72F000FF7857 /* intl */, @@ -1031,6 +1047,28 @@ path = drivers_font_renderer; sourceTree = ""; }; + 05BF821320ED69A500D95B19 /* core */ = { + isa = PBXGroup; + children = ( + 05B5F91120ED6AAE009C521F /* retroarch.c */, + 05B5F91220ED6AAF009C521F /* retroarch.h */, + 05B5F90F20ED6A03009C521F /* command.c */, + 05BF821720ED69D100D95B19 /* command.h */, + 05BF821420ED69D100D95B19 /* config.def.h */, + 05BF821C20ED69D100D95B19 /* config.def.keybinds.h */, + 05BF821620ED69D100D95B19 /* config.features.h */, + 05BF821A20ED69D100D95B19 /* configuration.c */, + 05BF821D20ED69D100D95B19 /* configuration.h */, + 05B5F90D20ED6A03009C521F /* content.h */, + 05BF821520ED69D100D95B19 /* core_impl.c */, + 05BF821820ED69D100D95B19 /* core_info.c */, + 05BF821B20ED69D100D95B19 /* core_info.h */, + 05B5F90E20ED6A03009C521F /* core_type.h */, + 05BF821920ED69D100D95B19 /* core.h */, + ); + name = core; + sourceTree = ""; + }; 05C5D53220E3DD0900654EE4 /* input */ = { isa = PBXGroup; children = ( diff --git a/ui/drivers/ui_cocoa.m b/ui/drivers/ui_cocoa.m index 6f607c041b..6b84e3e857 100644 --- a/ui/drivers/ui_cocoa.m +++ b/ui/drivers/ui_cocoa.m @@ -326,9 +326,23 @@ static char** waiting_argv; } - (void)setVideoMode:(gfx_ctx_mode_t)mode { - // TODO(sgc): handle full screen? - // cheap hack to ensure MTKView posts triggers a drawable resize event - [self.window setContentSize:NSMakeSize(mode.width-1, mode.height)]; + BOOL isFullScreen = (self.window.styleMask & NSFullScreenWindowMask) == NSFullScreenWindowMask; + if (mode.fullscreen && !isFullScreen) + { + [self.window toggleFullScreen:self]; + return; + } + + if (!mode.fullscreen && isFullScreen) + { + [self.window toggleFullScreen:self]; + } + + if (mode.width > 0) + { + // HACK(sgc): ensure MTKView posts a drawable resize event + [self.window setContentSize:NSMakeSize(mode.width-1, mode.height)]; + } [self.window setContentSize:NSMakeSize(mode.width, mode.height)]; } From 40a1446c950e8fbd42ddf58a4b33e8c7082049b9 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Sat, 7 Jul 2018 07:27:17 -0700 Subject: [PATCH 055/106] feat(Metal): Reload shader preset when Metal driver reinitialized --- gfx/drivers/metal.m | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index 644b33a60e..ef99b59030 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -46,6 +46,9 @@ #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) @@ -57,6 +60,15 @@ static void *metal_init(const video_info_t *video, { return NULL; } + + 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; } From 045606ce445b660890fbe53d041901e04b473e68 Mon Sep 17 00:00:00 2001 From: alphanu1 <37101891+alphanu1@users.noreply.github.com> Date: Sun, 8 Jul 2018 18:17:23 +0100 Subject: [PATCH 056/106] Restore refresh fix for windows The refresh rate is now detect to restore the original desktop resolution. --- gfx/display_servers/dispserv_win32.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gfx/display_servers/dispserv_win32.c b/gfx/display_servers/dispserv_win32.c index 243b0ecf2b..87100e8bdb 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 , 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; From 755a128d95beed9fdce4c79456952055aaa54964 Mon Sep 17 00:00:00 2001 From: alphanu1 <37101891+alphanu1@users.noreply.github.com> Date: Sun, 8 Jul 2018 19:15:24 +0100 Subject: [PATCH 057/106] cast float --- gfx/display_servers/dispserv_win32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/display_servers/dispserv_win32.c b/gfx/display_servers/dispserv_win32.c index 87100e8bdb..87f0253409 100644 --- a/gfx/display_servers/dispserv_win32.c +++ b/gfx/display_servers/dispserv_win32.c @@ -107,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, - win32_orig_refresh , win32_orig_refresh ); + win32_orig_refresh , (float)win32_orig_refresh ); #ifdef HAS_TASKBAR_EXT if (g_taskbarList && win32_taskbar_is_created()) From dc55bd13aa47bb0efbccb67aaa82690a4c025ac9 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 11 Jul 2018 03:42:42 +0200 Subject: [PATCH 058/106] Add HAVE_METAL --- Makefile.common | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Makefile.common b/Makefile.common index 4eaabdc31d..ad3109b9c9 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1105,6 +1105,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 From cb137954f93533ee5049eb32b3b27acc6f047b1e Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Wed, 11 Jul 2018 11:22:43 +0200 Subject: [PATCH 059/106] Update wiiu_audio.c - audio should no longer clip --- audio/drivers/wiiu_audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 7adce0050ebdbb852aeb8c7578777261e006d299 Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Wed, 11 Jul 2018 11:23:21 +0200 Subject: [PATCH 060/106] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index d3878dfe46..d69ed500f9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -28,6 +28,7 @@ - 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! From c81aab345c1e5930b3e8e9889ac0bcb771ad473a Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Wed, 11 Jul 2018 16:37:37 -0500 Subject: [PATCH 061/106] Lock sizes of enum fields at 32 bits. --- gfx/common/win32_common.c | 112 +++++++------------------------------- 1 file changed, 20 insertions(+), 92 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index b4bcbc0481..bc38d81da8 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -92,78 +92,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 +116,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 +145,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 +170,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 +187,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; @@ -1454,23 +1382,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); From 14e79e0a43e0ffa011994cebdfa48baea622380d Mon Sep 17 00:00:00 2001 From: Yoshi Sugawara Date: Wed, 11 Jul 2018 15:52:11 -1000 Subject: [PATCH 062/106] support viewing/opening files in Files app --- pkg/apple/iOS/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/apple/iOS/Info.plist b/pkg/apple/iOS/Info.plist index 50f6eafff0..8afa2ae3ad 100644 --- a/pkg/apple/iOS/Info.plist +++ b/pkg/apple/iOS/Info.plist @@ -40,6 +40,8 @@ 1.7.2 LSRequiresIPhoneOS + LSSupportsOpeningDocumentsInPlace + NSCameraUsageDescription YES UIApplicationExitsOnSuspend From 140e657d51e36ff679f814f77c28e1e9247d53b9 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Jul 2018 21:25:21 +0200 Subject: [PATCH 063/106] (GLSL) Add logging for compiling menu shaders --- gfx/drivers_shader/shader_glsl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gfx/drivers_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index ad12be1116..0430f0eacd 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -816,6 +816,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, @@ -827,6 +828,7 @@ static void gl_glsl_init_menu_shaders(void *data) 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; + RARCH_LOG("[GLSL]: Compiling simple ribbon shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_2, @@ -842,6 +844,7 @@ static void gl_glsl_init_menu_shaders(void *data) #endif shader_prog_info.fragment = stock_fragment_xmb_simple_snow; + RARCH_LOG("[GLSL]: Compiling snow shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_3, @@ -857,6 +860,7 @@ static void gl_glsl_init_menu_shaders(void *data) #endif shader_prog_info.fragment = stock_fragment_xmb_snow; + RARCH_LOG("[GLSL]: Compiling modern snow shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_4, @@ -872,6 +876,7 @@ static void gl_glsl_init_menu_shaders(void *data) #endif shader_prog_info.fragment = stock_fragment_xmb_bokeh; + RARCH_LOG("[GLSL]: Compiling bokeh shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_5, @@ -887,6 +892,7 @@ static void gl_glsl_init_menu_shaders(void *data) #endif shader_prog_info.fragment = stock_fragment_xmb_snowflake; + RARCH_LOG("[GLSL]: Compiling snowflake shader..\n"); gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_6, From 60d7d1c7936fd041a937bfc0c11a291ca214689a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Jul 2018 21:34:17 +0200 Subject: [PATCH 064/106] Add xmb ribbon simple core shader --- .../core_pipeline_xmb_ribbon_simple.glsl.frag.h | 11 +++++++++++ gfx/drivers_shader/shader_glsl.c | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 gfx/drivers/gl_shaders/core_pipeline_xmb_ribbon_simple.glsl.frag.h 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_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index 0430f0eacd..026e2da5f8 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -118,6 +118,7 @@ static const char *glsl_prefixes[] = { #ifdef HAVE_SHADERPIPELINE #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/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" @@ -826,7 +827,7 @@ 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( From fab08c9cfb29003c14f31aa03990df8a8c7993c1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Jul 2018 21:39:18 +0200 Subject: [PATCH 065/106] Only load Xinput DLL once --- input/drivers_joypad/xinput_joypad.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) 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). From 6b5ade4ac3b98ce03a08a6a1bd9c385726b43e8f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 12 Jul 2018 22:03:37 +0200 Subject: [PATCH 066/106] Should be no more deprecated warnings with core context GL for menu shaders --- .../core_pipeline_bokeh.glsl.frag.h | 33 ++++++++ .../gl_shaders/core_pipeline_snow.glsl.frag.h | 72 +++++++++++++++++ .../core_pipeline_snow_simple.glsl.frag.h | 72 +++++++++++++++++ .../core_pipeline_snowflake.glsl.frag.h | 77 +++++++++++++++++++ .../modern_pipeline_snow.glsl.vert.h | 8 +- gfx/drivers_shader/shader_glsl.c | 40 ++++++---- 6 files changed, 282 insertions(+), 20 deletions(-) create mode 100644 gfx/drivers/gl_shaders/core_pipeline_bokeh.glsl.frag.h create mode 100644 gfx/drivers/gl_shaders/core_pipeline_snow.glsl.frag.h create mode 100644 gfx/drivers/gl_shaders/core_pipeline_snow_simple.glsl.frag.h create mode 100644 gfx/drivers/gl_shaders/core_pipeline_snowflake.glsl.frag.h 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/modern_pipeline_snow.glsl.vert.h b/gfx/drivers/gl_shaders/modern_pipeline_snow.glsl.vert.h index 405ab02e96..7e7719fdad 100644 --- a/gfx/drivers/gl_shaders/modern_pipeline_snow.glsl.vert.h +++ b/gfx/drivers/gl_shaders/modern_pipeline_snow.glsl.vert.h @@ -3,11 +3,11 @@ /* 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( - attribute vec2 TexCoord; - attribute vec2 VertexCoord; - attribute vec4 Color; + in vec2 TexCoord; + in vec2 VertexCoord; + in vec4 Color; uniform mat4 MVPMatrix; - varying vec2 tex_coord; + out vec2 tex_coord; void main() { gl_Position = MVPMatrix * vec4(VertexCoord, 0.0, 1.0); diff --git a/gfx/drivers_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index 026e2da5f8..3a0683422d 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -117,8 +117,12 @@ 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" @@ -839,11 +843,12 @@ 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_modern; shader_prog_info.fragment = stock_fragment_xmb_simple_snow; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : 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( @@ -855,11 +860,12 @@ 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_modern; shader_prog_info.fragment = stock_fragment_xmb_snow; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : 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( @@ -871,11 +877,12 @@ 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_modern; shader_prog_info.fragment = stock_fragment_xmb_bokeh; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : 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( @@ -887,11 +894,12 @@ 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_modern; shader_prog_info.fragment = stock_fragment_xmb_snowflake; +#else + shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : 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( From ab27b7c85fd99f6b37bae1a20b2789d77315e09e Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 12 Jul 2018 16:55:08 -0400 Subject: [PATCH 067/106] initial SIXEL video driver --- Makefile.common | 12 + gfx/common/sixel_common.h | 34 ++ gfx/drivers/caca_gfx.c | 1 + gfx/drivers/sixel_gfx.c | 668 ++++++++++++++++++++++++ gfx/drivers_context/sixel_ctx.c | 202 +++++++ gfx/drivers_font/sixel_font.c | 149 ++++++ gfx/font_driver.c | 36 ++ gfx/font_driver.h | 1 + gfx/video_defines.h | 1 + gfx/video_driver.c | 12 +- gfx/video_driver.h | 3 + menu/drivers_display/menu_display_gdi.c | 12 - menu/menu_driver.c | 13 +- menu/menu_driver.h | 2 + qb/config.libs.sh | 3 +- qb/config.params.sh | 1 + 16 files changed, 1131 insertions(+), 19 deletions(-) create mode 100644 gfx/common/sixel_common.h create mode 100644 gfx/drivers/sixel_gfx.c create mode 100644 gfx/drivers_context/sixel_ctx.c create mode 100644 gfx/drivers_font/sixel_font.c diff --git a/Makefile.common b/Makefile.common index ad3109b9c9..2aca4eb425 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1044,6 +1044,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 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/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/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_context/sixel_ctx.c b/gfx/drivers_context/sixel_ctx.c new file mode 100644 index 0000000000..39926c5f06 --- /dev/null +++ b/gfx/drivers_context/sixel_ctx.c @@ -0,0 +1,202 @@ +/* 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 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 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_bind_api, + gfx_ctx_sixel_swap_interval, + gfx_ctx_sixel_set_video_mode, + gfx_ctx_sixel_get_video_size, + 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/sixel_font.c b/gfx/drivers_font/sixel_font.c new file mode 100644 index 0000000000..f97b732ca5 --- /dev/null +++ b/gfx/drivers_font/sixel_font.c @@ -0,0 +1,149 @@ +/* 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((const void**)&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 */ +} + +static void sixel_font_flush_block(unsigned width, unsigned height, + void* data) +{ + (void)data; +} + +static void sixel_font_bind_block(void* data, void* userdata) +{ + (void)data; +} + +font_renderer_t sixel_font = { + sixel_init_font, + sixel_render_free_font, + sixel_render_msg, + "sixel font", + sixel_font_get_glyph, /* get_glyph */ + sixel_font_bind_block, /* bind_block */ + sixel_font_flush_block, /* flush */ + sixel_get_message_width /* get_message_width */ +}; diff --git a/gfx/font_driver.c b/gfx/font_driver.c index 4d57e0162b..d9ed697afd 100644 --- a/gfx/font_driver.c +++ b/gfx/font_driver.c @@ -217,6 +217,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 +626,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..54876a0e87 100644 --- a/gfx/font_driver.h +++ b/gfx/font_driver.h @@ -170,6 +170,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..3ef84d919e 100644 --- a/gfx/video_defines.h +++ b/gfx/video_defines.h @@ -93,6 +93,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 5d0d50452d..908e25aedb 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -339,11 +339,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 +411,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 diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 2077ec383e..ca3dca38ad 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, @@ -1273,6 +1274,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 +1296,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/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/menu_driver.c b/menu/menu_driver.c index fdb7025b4a..4bc0a32ec1 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -130,14 +130,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 +302,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; diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 205197328f..b3c363afb7 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 }; @@ -814,6 +815,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; diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 93c2a34434..b6d29c9b6e 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -233,6 +233,7 @@ fi check_pkgconf ALSA alsa check_val '' ALSA -lasound alsa check_lib '' CACA -lcaca +check_lib '' SIXEL -lsixel if [ "$HAVE_OSS" != 'no' ]; then check_header OSS sys/soundcard.h @@ -516,7 +517,7 @@ if [ "$HAVE_MATERIALUI" != 'no' ] || [ "$HAVE_XMB" != 'no' ] || [ "$HAVE_ZARCH" HAVE_SHADERPIPELINE=no HAVE_VULKAN=no die : 'Notice: Hardware rendering context not available.' - elif [ "$HAVE_CACA" = 'yes' ]; then + elif [ "$HAVE_CACA" = 'yes' ] || [ "$HAVE_SIXEL" = 'yes' ]; then die : 'Notice: Hardware rendering context not available.' else HAVE_MATERIALUI=no diff --git a/qb/config.params.sh b/qb/config.params.sh index cd3e06d9dc..db514977d7 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -5,6 +5,7 @@ HAVE_MAN_DIR= # Manpage install directory (Deprecated) HAVE_OPENGLES_LIBS= # Link flags for custom GLES library HAVE_OPENGLES_CFLAGS= # C-flags for custom GLES library HAVE_CACA=no # Libcaca support +HAVE_SIXEL=no # Libsixel support HAVE_LIBRETRODB=yes # Libretrodb support HAVE_RGUI=yes # RGUI menu HAVE_MATERIALUI=auto # MaterialUI menu From 7e1074c40a3504166a0510469b5571c04210be2a Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 12 Jul 2018 17:23:48 -0400 Subject: [PATCH 068/106] sixel: add missing menu display driver --- menu/drivers_display/menu_display_sixel.c | 106 ++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 menu/drivers_display/menu_display_sixel.c 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", +}; From f8ea5cfd8dcc12e1f2036fee9f5cba69c59ebdad Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 12 Jul 2018 17:24:44 -0400 Subject: [PATCH 069/106] update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index d69ed500f9..a0ce3418b5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ - ANDROID: Powerstate/battery level support. - 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. - 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. From dc807f25cf33c192ec30d85d378725e7e3b40abe Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Tue, 1 May 2018 13:01:03 -0500 Subject: [PATCH 070/106] Fix android armeabi and x86_64 core updater urls --- config.def.h | 6 +++++- pkg/android/phoenix/jni/Android.mk | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 546e7bebfd..950bca391d 100644 --- a/config.def.h +++ b/config.def.h @@ -701,12 +701,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/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) From dbfccb25dd1d538d860c9c7ec4d87331d45affd9 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 12 Jul 2018 22:58:57 -0400 Subject: [PATCH 071/106] sixel: fix context init --- gfx/drivers_context/sixel_ctx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gfx/drivers_context/sixel_ctx.c b/gfx/drivers_context/sixel_ctx.c index 39926c5f06..cee11e0027 100644 --- a/gfx/drivers_context/sixel_ctx.c +++ b/gfx/drivers_context/sixel_ctx.c @@ -31,6 +31,8 @@ #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) { @@ -131,6 +133,11 @@ static bool gfx_ctx_sixel_get_metrics(void *data, 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) { @@ -171,10 +178,12 @@ static void gfx_ctx_sixel_swap_buffers(void *data, void *data2) 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 */ From 167ad3b57dd6e1989e86d2b009b8a70a63b91bce Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Thu, 12 Jul 2018 21:33:18 -0700 Subject: [PATCH 072/106] fix(Metal): Various Metal fixes * OSD message background * clear glui background * add Metal to configuration * added optional config to compile Metal build without OpenGL * fixed fill_pathname_join_delim; if out_path and dir are same, don't strlcpy, as the results are undefined. --- configuration.c | 6 + gfx/common/metal/Context.h | 12 +- gfx/common/metal/Context.m | 270 +++++++++++++++++- gfx/common/metal/MenuDisplay.h | 5 +- gfx/common/metal/MenuDisplay.m | 41 +-- gfx/common/metal/RendererCommon.h | 1 + gfx/common/metal/RendererCommon.m | 6 +- gfx/common/metal/Shaders.metal | 5 + gfx/common/metal/TexturedView.m | 2 - gfx/common/metal_common.h | 3 - gfx/common/metal_common.m | 247 +++------------- gfx/drivers/metal.m | 8 - gfx/drivers_font/metal_raster_font.m | 13 +- griffin/griffin.c | 2 +- libretro-common/file/file_path.c | 7 +- menu/drivers_display/menu_display_metal.m | 2 +- pkg/apple/{IntelliJ.xml => Project.xml} | 8 +- .../RetroArch_Metal.xcodeproj/project.pbxproj | 47 +++ 18 files changed, 424 insertions(+), 261 deletions(-) rename pkg/apple/{IntelliJ.xml => Project.xml} (91%) diff --git a/configuration.c b/configuration.c index 13da1997f1..953f4b4524 100644 --- a/configuration.c +++ b/configuration.c @@ -125,6 +125,7 @@ enum video_driver_enum { VIDEO_GL = 0, VIDEO_VULKAN, + VIDEO_METAL, VIDEO_DRM, VIDEO_XVIDEO, VIDEO_SDL, @@ -291,6 +292,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) @@ -711,6 +714,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: @@ -2357,6 +2362,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")) diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index 5b5b380cf8..497da8e44d 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -28,12 +28,14 @@ typedef struct @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; /*! @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 for applying filters + * such as mip maps and shader effects * */ @property (nonatomic, readonly) id blitCommandBuffer; @@ -51,6 +53,14 @@ typedef struct - (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; diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index 59066fccf6..f610320c24 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -38,6 +38,7 @@ id _commandQueue; CAMetalLayer *_layer; id _drawable; + video_viewport_t _viewport; id _samplers[TEXTURE_FILTER_MIPMAP_NEAREST + 1]; Filter *_filters[RPixelFormatCount]; // convert to bgra8888 @@ -49,6 +50,10 @@ NSUInteger _currentChain; BufferChain *_chain[CHAIN_LENGTH]; MTLClearColor _clearColor; + + id _states[GFX_MAX_SHADERS][2]; + id _clearState; + Uniforms _uniforms; } - (instancetype)initWithDevice:(id)d @@ -64,6 +69,7 @@ _library = l; _commandQueue = [_device newCommandQueue]; _clearColor = MTLClearColorMake(0, 0, 0, 1); + _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); { MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; @@ -89,7 +95,10 @@ if (![self _initConversionFilters]) return nil; - if (![self _initMainState]) + if (![self _initClearState]) + return nil; + + if (![self _initMenuStates]) return nil; for (int i = 0; i < CHAIN_LENGTH; i++) @@ -100,21 +109,241 @@ 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; } -- (bool)_initMainState +- (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; @@ -255,6 +484,41 @@ 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(_commandBuffer != nil); @@ -263,7 +527,7 @@ if (_blitCommandBuffer) { - // pending blits for mipmaps + // pending blits for mipmaps or render passes for slang shaders [_blitCommandBuffer commit]; [_blitCommandBuffer waitUntilCompleted]; _blitCommandBuffer = nil; diff --git a/gfx/common/metal/MenuDisplay.h b/gfx/common/metal/MenuDisplay.h index f467029d6e..12d5bbd6fd 100644 --- a/gfx/common/metal/MenuDisplay.h +++ b/gfx/common/metal/MenuDisplay.h @@ -3,17 +3,15 @@ // #import -#import "ShaderTypes.h" @class Context; -@class MetalDriver; @interface MenuDisplay : NSObject @property (nonatomic, readwrite) BOOL blend; @property (nonatomic, readwrite) MTLClearColor clearColor; -- (instancetype)initWithDriver:(MetalDriver *)driver; +- (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; @@ -23,5 +21,4 @@ + (const float *)defaultTexCoords; + (const float *)defaultColor; - @end diff --git a/gfx/common/metal/MenuDisplay.m b/gfx/common/metal/MenuDisplay.m index 7581631fb7..69a299c3c7 100644 --- a/gfx/common/metal/MenuDisplay.m +++ b/gfx/common/metal/MenuDisplay.m @@ -12,19 +12,17 @@ @implementation MenuDisplay { - __weak MetalDriver *_driver; Context *_context; MTLClearColor _clearColor; bool _clearNextRender; Uniforms _uniforms; } -- (instancetype)initWithDriver:(MetalDriver *)driver +- (instancetype)initWithContext:(Context *)context { if (self = [super init]) { - _driver = driver; - _context = driver.context; + _context = context; _clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); } @@ -45,10 +43,10 @@ + (const float *)defaultTexCoords { static float dummy[] = { - 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, }; return &dummy[0]; } @@ -97,7 +95,7 @@ draw->y = 0; draw->matrix_data = NULL; - _uniforms.outputSize = simd_make_float2(_driver.viewport->full_width, _driver.viewport->full_height); + _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); @@ -147,7 +145,7 @@ 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]); + pv->position = simd_make_float2(vertex[0], vertex[1]); vertex += 2; pv->texCoord = simd_make_float2(tex_coord[0], tex_coord[1]); @@ -158,9 +156,24 @@ } 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 = _driver.viewport->full_height - draw->y - draw->height, + .originY = _context.viewport->full_height - draw->y - draw->height, .width = draw->width, .height = draw->height, .znear = 0, @@ -177,7 +190,7 @@ case VIDEO_SHADER_MENU_4: case VIDEO_SHADER_MENU_5: case VIDEO_SHADER_MENU_6: - [rce setRenderPipelineState:[_driver getStockShader:draw->pipeline.id blend:_blend]]; + [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]; @@ -192,13 +205,7 @@ if (tex == nil) return; - if (_clearNextRender) - { - // TODO(sgc): draw quad to clear - _clearNextRender = NO; - } - - [rce setRenderPipelineState:[_driver getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; + [rce setRenderPipelineState:[_context getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; Uniforms uniforms = { .projectionMatrix = draw->matrix_data ? make_matrix_float4x4((const float *)draw->matrix_data) diff --git a/gfx/common/metal/RendererCommon.h b/gfx/common/metal/RendererCommon.h index 320a9cc8b0..977cf7b607 100644 --- a/gfx/common/metal/RendererCommon.h +++ b/gfx/common/metal/RendererCommon.h @@ -10,6 +10,7 @@ #define RendererCommon_h #import +#import "ShaderTypes.h" // TODO(sgc): implement triple buffering /*! @brief maximum inflight frames */ diff --git a/gfx/common/metal/RendererCommon.m b/gfx/common/metal/RendererCommon.m index dce930c162..c3be58dc86 100644 --- a/gfx/common/metal/RendererCommon.m +++ b/gfx/common/metal/RendererCommon.m @@ -55,11 +55,11 @@ NSString *NSStringFromRPixelFormat(RPixelFormat 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; + v += 4; simd_float4 Q = simd_make_float4(v[0], v[1], v[2], v[3]); - v+=4; + v += 4; simd_float4 R = simd_make_float4(v[0], v[1], v[2], v[3]); - v+=4; + v += 4; simd_float4 S = simd_make_float4(v[0], v[1], v[2], v[3]); matrix_float4x4 mat = {P, Q, R, S}; diff --git a/gfx/common/metal/Shaders.metal b/gfx/common/metal/Shaders.metal index 619442f50b..c5449b2989 100644 --- a/gfx/common/metal/Shaders.metal +++ b/gfx/common/metal/Shaders.metal @@ -74,6 +74,11 @@ fragment float4 stock_fragment(FontFragmentIn in [[ stage_in ]], 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) ]], diff --git a/gfx/common/metal/TexturedView.m b/gfx/common/metal/TexturedView.m index 22ff31cf31..f89346a654 100644 --- a/gfx/common/metal/TexturedView.m +++ b/gfx/common/metal/TexturedView.m @@ -7,8 +7,6 @@ #import "View.h" #import "Filter.h" -#import "ShaderTypes.h" - @implementation TexturedView { Context *_context; diff --git a/gfx/common/metal_common.h b/gfx/common/metal_common.h index e14902649b..c7271cee01 100644 --- a/gfx/common/metal_common.h +++ b/gfx/common/metal_common.h @@ -79,7 +79,6 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @property (nonatomic, readonly) Overlay *overlay; @property (nonatomic, readonly) Context *context; @property (nonatomic, readonly) Uniforms *viewportMVP; -@property (nonatomic, readonly) Uniforms *viewportMVPNormalized; - (instancetype)initWithVideo:(const video_info_t *)video input:(const input_driver_t **)input @@ -94,8 +93,6 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); msg:(const char *)msg info:(video_frame_info_t *)video_info; -- (id)getStockShader:(int)index blend:(bool)blend; - /*! @brief setNeedsResize triggers a display resize */ - (void)setNeedsResize; diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 0777a51bb7..efdb0853d2 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -71,13 +71,9 @@ id _samplerStateLinear; id _samplerStateNearest; - // - id _states[GFX_MAX_SHADERS][2]; - // other state Uniforms _uniforms; Uniforms _viewportMVP; - Uniforms _viewportMVPNormalized; } - (instancetype)initWithVideo:(const video_info_t *)video @@ -97,13 +93,9 @@ return nil; } - if (![self _initStates]) - { - return nil; - } - _video = *video; _viewport = (video_viewport_t *)calloc(1, sizeof(video_viewport_t)); + _viewportMVP.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); _keepAspect = _video.force_aspect; @@ -118,7 +110,7 @@ *inputData = NULL; // menu display - _display = [[MenuDisplay alloc] initWithDriver:self]; + _display = [[MenuDisplay alloc] initWithContext:_context]; // menu view _menu = [[MetalMenu alloc] initWithContext:_context]; @@ -214,162 +206,6 @@ return YES; } -- (bool)_initStates -{ - 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); - - { - 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; -} - - (void)_updateUniforms { _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); @@ -383,36 +219,9 @@ _layer.drawableSize = size; video_driver_update_viewport(_viewport, NO, _keepAspect); + _context.viewport = _viewport; + _viewportMVP.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height); - _viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, _viewport->full_height, 0); - _viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, 0, _viewport->full_height); - - _viewportMVPNormalized.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height); - _viewportMVPNormalized.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); -} - -#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]; } #pragma mark - video @@ -455,7 +264,7 @@ { id rce = _context.rce; [rce pushDebugGroup:@"overlay"]; - [rce setRenderPipelineState:[self getStockShader:VIDEO_SHADER_STOCK_BLEND blend:YES]]; + [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]; @@ -464,9 +273,7 @@ #endif if (msg && *msg) - { - font_driver_render_msg(video_info, NULL, msg, NULL); - } + [self _renderMessage:msg info:video_info]; [self _endFrame]; } @@ -474,6 +281,43 @@ 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); @@ -555,11 +399,6 @@ return &_viewportMVP; } -- (Uniforms *)viewportMVPNormalized -{ - return &_viewportMVPNormalized; -} - #pragma mark - MTKViewDelegate - (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index ef99b59030..d788d09bfa 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -196,14 +196,6 @@ static void metal_set_video_mode(void *data, RARCH_LOG("[Metal]: set_video_mode res=%dx%d fullscreen=%s\n", width, height, fullscreen ? "YES" : "NO"); - MetalDriver *md = (__bridge MetalDriver *)data; - gfx_ctx_mode_t mode = { - .width = width, - .height = height, - .fullscreen = fullscreen, - }; - - //[md setVideoMode:mode]; } static float metal_get_refresh_rate(void *data) diff --git a/gfx/drivers_font/metal_raster_font.m b/gfx/drivers_font/metal_raster_font.m index 7ebf92f276..c5c7fa94bd 100644 --- a/gfx/drivers_font/metal_raster_font.m +++ b/gfx/drivers_font/metal_raster_font.m @@ -323,17 +323,8 @@ static INLINE void write_quad6(SpriteVertex *pv, id rce = _context.rce; [rce pushDebugGroup:@"render fonts"]; - - MTLViewport vp = { - .originX = 0, - .originY = 0, - .width = _driver.viewport->full_width, - .height = _driver.viewport->full_height, - .znear = 0, - .zfar = 1, - }; - [rce setViewport:vp]; - + + [_context resetRenderViewport]; [rce setRenderPipelineState:_state]; [rce setVertexBytes:&_uniforms length:sizeof(Uniforms) atIndex:BufferIndexUniforms]; [rce setVertexBuffer:_vert offset:start atIndex:BufferIndexPositions]; diff --git a/griffin/griffin.c b/griffin/griffin.c index d2700ce1bf..77584d9839 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1235,7 +1235,7 @@ 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 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/menu/drivers_display/menu_display_metal.m b/menu/drivers_display/menu_display_metal.m index beb57553ee..7ff5a5bfa7 100644 --- a/menu/drivers_display/menu_display_metal.m +++ b/menu/drivers_display/menu_display_metal.m @@ -35,7 +35,7 @@ static void *menu_display_metal_get_default_mvp(video_frame_info_t *video_info) if (!md) return NULL; - return (void *)&md.viewportMVPNormalized->projectionMatrix; + return (void *)&md.viewportMVP->projectionMatrix; } static void menu_display_metal_blend_begin(video_frame_info_t *video_info) diff --git a/pkg/apple/IntelliJ.xml b/pkg/apple/Project.xml similarity index 91% 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 @@