diff options
723 files changed, 25833 insertions, 16214 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index 9ec6c4a4e9..84fdb2180b 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -198,20 +198,13 @@ jobs: submodules: 'recursive' path: 'godot-cpp' - # Check extension API - - name: Check for extension api updates + # Dump GDExtension interface and API + - name: Dump GDExtension interface and API for godot-cpp build if: ${{ matrix.godot-cpp-test }} run: | - echo "Running --dump-extension-api to create extensions api." - VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run ${{ matrix.bin }} --audio-driver Dummy --dump-extension-api 2>&1 > /dev/null || true - misc/scripts/compare_extension_api.py godot-cpp/godot-headers/extension_api.json extension_api.json - - # Copy new extension API files into place - - name: Copy new extension API files into place - if: ${{ matrix.godot-cpp-test }} - run: | - cp -f extension_api.json godot-cpp/godot-headers/ - cp -f core/extension/gdextension_interface.h godot-cpp/godot-headers/godot/ + ${{ matrix.bin }} --headless --dump-gdextension-interface --dump-extension-api + cp -f gdextension_interface.h godot-cpp/gdextension/ + cp -f extension_api.json godot-cpp/gdextension/ # Build godot-cpp test extension - name: Build godot-cpp test extension diff --git a/.gitignore b/.gitignore index 1292be06fd..ca96220570 100644 --- a/.gitignore +++ b/.gitignore @@ -242,8 +242,10 @@ xcuserdata/ [Rr]eleases/ x64/ x86/ -# Not build results, this is Theora source code. -!thirdparty/libtheora/x86/ + +# Do not ignore x86 folders anywhere under thirdparty libraries +!thirdparty/**/x86/ + [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ @@ -2,6 +2,7 @@ Aaron Record <aaronjrecord@gmail.com> Alexander Holland <alexander.holland@live.de> Alexander Holland <alexander.holland@live.de> <alexander.holland@haw-hamburg.de> Alexander Holland <alexander.holland@live.de> <AlexHolly> +Alfred Reinold Baudisch <alfred@alfredbaudisch.com> Andrea Catania <info@andreacatania.com> Anish Bhobe <anishbhobe@hotmail.com> Anutrix <numaanzaheerahmed@yahoo.com> @@ -26,6 +27,7 @@ dankan1890 <mewuidev2@gmail.com> Daniel J. Ramirez <djrmuv@gmail.com> David Cambré <david.cambre@gmail.com> <David.Cambre@gmail.com> Dominik 'dreamsComeTrue' Jasiński <dominikjasinski@o2.pl> +DeeJayLSP <djlsplays@gmail.com> <60024671+DeeJayLSP@users.noreply.github.com> Emmanuel Barroga <emmanuelbarroga@gmail.com> Eric M <itsjusteza@gmail.com> Eric Rybicki <info@ericrybicki.com> <stratos695@googlemail.com> @@ -41,6 +43,7 @@ foxydevloper <12120644+foxydevloper@users.noreply.github.com> Fredia Huya-Kouadio <fhuyakou@gmail.com> Fredia Huya-Kouadio <fhuyakou@gmail.com> <fhuya@google.com> Fredia Huya-Kouadio <fhuyakou@gmail.com> <fhuya@fb.com> +Fredia Huya-Kouadio <fhuyakou@gmail.com> <fhuya@meta.com> Geequlim <geequlim@gmail.com> Gilles Roudiere <gilles.roudiere@gmail.com> Gilles Roudiere <gilles.roudiere@gmail.com> <gilles.roudiere@laas.fr> @@ -112,6 +115,7 @@ Nils ANDRÉ-CHANG <nils@nilsand.re> <nils.andre.chang@gmail.com> Nuno Donato <nunodonato@gmail.com> <n.donato@estrelasustentavel.pt> Pawel Kowal <pkowal1982@gmail.com> Pedro J. Estébanez <pedrojrulez@gmail.com> <RandomShaper@users.noreply.github.com> +Patrick <firefly2442@gmail.com> Paul Batty <p_batty@hotmail.co.uk> Paul Batty <p_batty@hotmail.co.uk> <Paulb23@users.noreply.github.com> Pawel Kowal <pkowal1982@gmail.com> <pawel.kowal@javart.eu> @@ -131,14 +135,18 @@ Rémi Verschelde <rverschelde@gmail.com> <remi@verschelde.fr> Rhody Lugo <rhodylugo@gmail.com> <rhodylugo@me.com> Ricardo Subtil <ricasubtil@gmail.com> Rindbee <idleman@yeah.net> +Riteo Siuga <riteo@posteo.net> Robin Hübner <profan@prfn.se> <robinhubner@gmail.com> romulox_x <romulox_x@yahoo.com> +rune-scape <allie.smith.epic@gmail.com> +rune-scape <allie.smith.epic@gmail.com> <spartacrafter@gmail.com> Ruslan Mustakov <r.mustakov@gmail.com> <ruslan.mustakov@xored.com> Saracen <SaracenOne@gmail.com> sheepandshepherd <sheepandshepherd@hotmail.com> <sheepandshepherd@users.noreply.github.com> Silc 'Tokage' Renew <tokage.it.lab@gmail.com> Silc 'Tokage' Renew <tokage.it.lab@gmail.com> <61938263+TokageItLab@users.noreply.github.com> Swarnim Arun <swarnimarun11@gmail.com> +TechnoPorg <jonah.janzen@gmail.com> <69441745+TechnoPorg@users.noreply.github.com> Theo Hallenius <redsymbzone@hotmail.com> Tomasz Chabora <kobewi4e@gmail.com> Twarit <wtwarit@gmail.com> diff --git a/AUTHORS.md b/AUTHORS.md index 6f6ac48db2..7079180274 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -29,8 +29,10 @@ name is available. Aaron Franke (aaronfranke) Aaron Record (LightningAA) + Adam Scott (adamscott) Alexander Holland (AlexHolly) Alexey Khoroshavin (allkhor) + Alfred Reinold Baudisch (alfredbaudisch) Alket Rexhepi (alketii) Andrea Catania (AndreaCatania) Andrii Doroshenko (Xrayez) @@ -64,6 +66,7 @@ name is available. Dana Olson (adolson) Daniel J. Ramirez (djrm) Daniel Rakos (aqnuep) + Daniel Zilberleyb (dzil123) Danil Alexeev (dalexeev) dankan1890 David Cambré (Gallilus) @@ -72,6 +75,7 @@ name is available. Dharkael (lupoDharkael) Dmitry Koteroff (Krakean) Dominik Jasiński (dreamsComeTrue) + Douglas Leão (DeeJayLSP) DualMatrix Ellen Poe (ellenhp) Emmanuel Barroga (codecustard) @@ -118,13 +122,16 @@ name is available. Jake Young (Duroxxigar) Jakub Grzesik (kubecz3k) James Buck (jbuck3) + Jason Knight (jasonwinterpixel) Jean-Michel Bernard (jmb462) Jérôme Gully (Nutriz) Jia Jun Chai (SkyLucilfer) Joan Fons Sanchez (JFonS) Johan Manuel (29jm) Johannes Witt (HaSa1002) + Jonathan Nicholl (jtnicholl) Jordan Schidlowsky (winterpixelgames) + Josh Jones (DarkKilauea) Joshua Grams (JoshuaGrams) Juan Linietsky (reduz) Julian Murgia (StraToN) @@ -180,6 +187,7 @@ name is available. Nathan Lovato (NathanLovato) Nathan Warden (NathanWarden) Nicholas Huelin (SirQuartz) + Nikita Lita (nikitalita) Nils André-Chang (NilsIrl) Noah Beard (TwistedTwigleg) Nuno Donato (nunodonato) @@ -210,9 +218,11 @@ name is available. Rhody Lugo (rraallvv) Ricardo Buring (rburing) Ricardo Subtil (Ev1lbl0w) + Riteo Siuga (Riteo) Roberto F. Arroyo (robfram) Robin Hübner (profan) romulox-x + Rune Smith (rune-scape) Ruslan Mustakov (endragor) Ryan Roden-Corrent (rrcore) Saniko (sanikoyes) @@ -227,6 +237,7 @@ name is available. Stijn Hinlopen (hinlopen) Swarnim Arun (minraws) TC (floppyhammer) + TechnoPorg Thakee Nathees (ThakeeNathees) thebestnom Theo Hallenius (TheoXD) @@ -13,6 +13,7 @@ generous deed immortalized in the next stable release of Godot Engine. ## Platinum sponsors Gamblify <https://www.gamblify.com> + Heroic Labs <https://heroiclabs.com> Spiffcode <http://spiffcode.com> ## Gold sponsors @@ -25,20 +26,24 @@ generous deed immortalized in the next stable release of Godot Engine. ASIFA-Hollywood <https://www.asifa-hollywood.org> Playful Studios <https://playfulstudios.com> Robot Gentleman <http://robotgentleman.com> + Striked <https://developers.join-striked.com> ## Bronze sponsors Basically Games + Bippinbits <https://bippinbits.com> Brandon Lamb Bri Garry Newman Gordon MacPherson Hunter Dickson Isaiah smith + Jb Evain Keepsake Games <https://keepsake.games> Kenney <https://kenney.nl> Kitcat490 Kyle Szklenski + Matthew Campbell Maxim Karsten Moonwards <https://www.moonwards.com> Nik Rudenko @@ -49,12 +54,12 @@ generous deed immortalized in the next stable release of Godot Engine. AD Ford Albin Lundahl Andres Hernandez - Andrew Bowen Andrew Dunai anti666 Christian Baune Christopher Montesano Christopher Shifflett + Christoph Woinke Daniel Edwards Darrin Massena David Mydlarz @@ -68,6 +73,7 @@ generous deed immortalized in the next stable release of Godot Engine. Justin Arnold Justo Delgado Baudí Kossi Selom Banybah + Lloyd Bond Marcel Kräml Marek Belski Markus Ort @@ -75,13 +81,13 @@ generous deed immortalized in the next stable release of Godot Engine. Michael Mike King Nassor Paulino da Silva + nate-wilkins Nathan Warden Neal Gompa (Conan Kudo) Ninja_5tyl3 Patrick Horn Patrick Schmidt Rami - René Habermann Ronnie Cheng Ryan Heath Samantha @@ -106,13 +112,14 @@ generous deed immortalized in the next stable release of Godot Engine. Benito Carlo Cabanilla Daniel James + David Fedorchak David Gehrig David Hubber David Snopek Ed Morley First Last Frank Kurka - Hunter Jones + Guy Kay Jay Jacobus Dens Javier Roman Joan Fons @@ -124,19 +131,17 @@ generous deed immortalized in the next stable release of Godot Engine. Manuele Finocchiaro Markus Wiesner Mateo Navarrete - Mathieu Matthew Hillier - Mert Kasar Officine Pixel S.n.c. Pedro Silva Retro Village Rob Messick Roland Fredenhagen Ronan Zeegers + Rudi Sarksus Sean Sergey - Sergio Airaldi Sofox Stephan Kessler Stephen Molyneaux @@ -148,11 +153,9 @@ generous deed immortalized in the next stable release of Godot Engine. Victor Xeno Coliseum + ␣ 23BLUENINJA - Adam Mill - Adam Nakonieczny - Adam Nelson - Adam Stankiewicz + AdamRatai Alexander Erlemann Alexandre VALIN Alexej Kowalew @@ -164,6 +167,7 @@ generous deed immortalized in the next stable release of Godot Engine. Andriy Antanas Paskauskas Antoni Batchelli + Ari Arthur S. Muszynski BasicIncomePlz Brandon Hawkinson @@ -178,14 +182,14 @@ generous deed immortalized in the next stable release of Godot Engine. Craig Smith Cristopher CT - CzechBlueBear + CzechBlueBea + CzłowiekImadło Daniel Reed Daniel Tebbutt Darrian Little - David Thomason + DATSAGE Daylon J Williams Dennis Belfrage - Dimitri Nüscheler Donn Eddy Duncan Eric Brand @@ -194,9 +198,7 @@ generous deed immortalized in the next stable release of Godot Engine. flesk foxydevloper Fransiska - FroggEater Gabrielius Vaiškūnas - Garpur Gary Hulst Geoffroy Warin George Venizelos @@ -209,8 +211,6 @@ generous deed immortalized in the next stable release of Godot Engine. Heath Hayes Horváth-Lázár Péter Hu Hund - Hunter Barabas - ihereou illuxxium Jaap Marsman Jamal Bencharki @@ -225,22 +225,17 @@ generous deed immortalized in the next stable release of Godot Engine. Johnathan Kupferer John Stinson Josef Stumpfegger - Jose Manuel Muñoz Perez Joshie Sparks Joshua Flores Joshua Lesperance Juan Velandia Judd Julian Todd - Juraj Móza JUSTIN CARROLL - Justin Palmer Kelteseth - Kevan Khora kickmaniac kinfox - Lakshaya Goel Laszlo Kiss leetNightshade Leo Fidel R Liban @@ -264,26 +259,24 @@ generous deed immortalized in the next stable release of Godot Engine. Michael Policastro MikadoSC nate etan + Nicola Cocchiaro Nikita Blizniuk Oliver Dick - Oscar Campos Otis Clark + Patrick Wuttke Paul Hocker Paul Von Zimmerman - Pedro Pete Goodwin Petr Malac Petrus Prinsloo Philip Woods - R - RaiRu RAMupgrade + red1939 Reilt Rene Tailleur Rhodochrone Rickard Hermanson Rob - Robert McDermott Rob McInroy RodZilla Romeo Disca @@ -297,11 +290,8 @@ generous deed immortalized in the next stable release of Godot Engine. Samuel Hummerstone Samuel Judd schroedinger's possum - Serban Serafimescu - Sergey Fonaryov Shishir Tandale Sing Chun Lee - Skides SKison Song Junwoo spacechase0 @@ -309,6 +299,7 @@ generous deed immortalized in the next stable release of Godot Engine. Steven Landow Stoned Xander Sven Carstensen + Talii Teslatech Thomas Bjarnelöf Thomas Kurz @@ -321,17 +312,17 @@ generous deed immortalized in the next stable release of Godot Engine. Turntsnaco UltyX Valryia - Vincent Cloutier VoidPointer + whatever Winston - Wojciech Chojnacki Yifan Lai Yuancheng Zhang + zkip lan ## Silver donors + Aaron Mayfield Aaron Oldenburg - A. B. Adam Brunnmeier Adam Carr Adam Long @@ -353,18 +344,17 @@ generous deed immortalized in the next stable release of Godot Engine. Alejandro Saucedo AleMax Alessandro Senese - Alex Chan Alex Clavelle + Alex de la Mare alex raeside Allan Davis - Allen Schade Amar Šahinović Andre Altmueller Andrei Pufu Andre Stackhouse + Andrew Andrew Groot andrew james morris - Andrew Thomas angrykoala Ano Nim Anthony Avina @@ -373,28 +363,23 @@ generous deed immortalized in the next stable release of Godot Engine. Arda Erol Arthur Brainville Arturo Rosales - Ashley Claymore Aubrey Falconer Auré Franky aurelien condomines - Austin Finlinson Austin Miller Azar Gurbanov AzulCrescent Balázs Batári Beau Seymour - Behzad Ghaffari Benedikt Ben Vercammen Ben Visness - Bernd Jänichen Bernhard Werner Bill Thibault bitbrain Bjarne Voigtländer Black Block Blunderjack - BoiLudens Bram Brian Brian Ford @@ -402,15 +387,16 @@ generous deed immortalized in the next stable release of Godot Engine. Bronson Zgeb bugcaptor Burney Waring - Caleb Gartner Caleb Makela Caliburn Cameron Meyer Carlos Rios Carl van der Geest Casey + Cesar Ruiz Chad Steadman Checkpoint Charlie + Chris Cavalluzzi Chris Jagusch Chris Langford Chris Ridenour @@ -420,8 +406,6 @@ generous deed immortalized in the next stable release of Godot Engine. Christian Winter Christoffer Dahlblom Christophe Gagnier - Christoph Woinke - codedius Codex404 Cody Parker Conall O @@ -429,26 +413,23 @@ generous deed immortalized in the next stable release of Godot Engine. Corchari Corey W Craig Post + CrimsonZA CrispyPin - Cullen Canejo cynwav Dakota Watkins Daniel H. Bahr - Danielle Cheney + Danielle Dare Looks Daren Scot Wilson - Dave Walker + Davesnothere David Baker David Bôle David May David Maziarka - David Rapisarda Devin Carraway Devin R Dimitri Roche - Dmytro Korchynskyi Dominik Wetzel - Don B Donovan Hutcheon Douglas Plumley Dragontrapper @@ -460,7 +441,6 @@ generous deed immortalized in the next stable release of Godot Engine. Edward Herbert Edward Swartz Egon Elbre - Elgenzay Elias Nykrem Elijah Anderson Emerson MX @@ -469,20 +449,15 @@ generous deed immortalized in the next stable release of Godot Engine. Eric Stokes Eric Walkingshaw Eric Williams - Erika Sanders Erkki Seppälä Ewan Holmes - EZL games Faisal Alkubaisi Fault Boy fby - Fekinox Felix Adam - Felix Bohmann Fer DC Filip Lundby Frank - FrostMarble fumangy Game Endeavor Gary Thomas @@ -497,18 +472,14 @@ generous deed immortalized in the next stable release of Godot Engine. Guo Hongci Hans Jorgensen Haplo - Hayden Foley Heribert Hirth Hylian Ensemble Ian Richard Kunert Ian Williams - IndustrialRobot - Inki Crow Interstice iveks Jacob D Jaguar - JAJAJA JA Jake D Jako Danar James @@ -518,40 +489,33 @@ generous deed immortalized in the next stable release of Godot Engine. James Thomas Jamie Massey Janis Skuja - JanPaul Bergeson Jan Vetulani JARKKO PARVIAINEN - Jason Bolton Jason Evans Jason Uechi Jeff Hungerford Jeffrey Berube Jennifer Graves - Jeramie Jesse Dubay João Pedro Braz Joe Hurdle Joe Klemmer - John Anders Stav John Barlex John Bruce + John Palgut Jonas Jonas Arndt Jonas Bernemann + Jonas LHOSTE Jonas Rudlang Jonas Yamazaki - Jonatan R Jonathan Bieber Jonathan Ellis Jonathan G - Jon Oakes Jon Sully - Jordan West Jordy Goodridge - Jorge Antunes Jose Francisco 'Yiro' Vera Girona Joseph Catrambone - Josh P Josh Segall Josh Taylor Joshua Heidrich @@ -563,12 +527,11 @@ generous deed immortalized in the next stable release of Godot Engine. Julian Murgia Justin Hamilton Justin Spedding - KaDokta + Justin Zander Kalydi Balázs Katsuomi Kobayashi Keedong Park Keegan Scott - Keinan Powers Keith Bradner Kenji Kawabata Ken Minardo @@ -576,22 +539,19 @@ generous deed immortalized in the next stable release of Godot Engine. Kerotasma Ketafuki killaQueen + kimbring2 Kodera Software Kolandrious - Kristian Nygaard Jensen KsyTek Games kycho Kyle Burnett Kyle Jacobs - Kyle Szklenski Lasse le Dous Laurent CHEA Laurent Dethoor Laxman Pradhan Lech Rozanski Leland Vakarian - Lemin - LEMMiNO Leonardo Baumle Levi Lindsey LF @@ -605,7 +565,7 @@ generous deed immortalized in the next stable release of Godot Engine. Luke Kasz Major Haul Malcolm - Marco Lardelli + Mara Huldra Marcos Heitor Carvalho Markie Music Mark Jad @@ -614,7 +574,6 @@ generous deed immortalized in the next stable release of Godot Engine. Markus Michael Egger Markus Strompen Martin Holas - Martin Linklater Martin Liška Martin Trbola Martin Zabinski @@ -623,14 +582,11 @@ generous deed immortalized in the next stable release of Godot Engine. Matthew Booe Maverick Maxime Blade - Maxime Santerre Maxwell McStuffings - Melchor Morales + Melchor Melissa Mears - Merlyn Morgan-Graham Metal Demon 2000 - mhilbrunner Michael Michael Haney Michael Morrison @@ -640,6 +596,7 @@ generous deed immortalized in the next stable release of Godot Engine. Mikayla Mike Birkhead Mike Copley + Mike McRoberts Miss Mitchell J. Wagner MJacred @@ -652,23 +609,17 @@ generous deed immortalized in the next stable release of Godot Engine. MrAZIE Mrjemandem Nathaniel - neighty + neguse Neil Blakey-Milner Neil Wang Nerdforge Nerdyninja Nicholas La Roux - Nicholas Orlowski Nick Eldrenkamp Nick Macholl Nico Greve - Nicolas Goll-Perrier Nicolas Rosset - Nicolò Brigadoi Calamari Nils Nordmark - Nima Farid - Noah Shomette - Noel Billig Noesis obscuresteel Okatima @@ -676,13 +627,10 @@ generous deed immortalized in the next stable release of Godot Engine. Oliver Ambrose oscar1000108 Oscar Domingo - Panagiotis Xynos Parth Patel - Pascal Patrick Indermühle Patrickm Patrick Nafarrete - Patrick Wuttke Paul Gieske Paul Mozet Paweł Kowal @@ -690,6 +638,7 @@ generous deed immortalized in the next stable release of Godot Engine. Pedro Henrique Martins Garcia Peter Höglund Philip Ludington (MrPhil) + Phoenix Jauregui Pierre Caye Pille Pixel Archipel @@ -697,23 +646,20 @@ generous deed immortalized in the next stable release of Godot Engine. Point08 Portponky Preethi Vaidyanathan + Price Comstock PsycHead Puntigames pwab Quincy Quincy Quinn Morrison - RackBar Dingum Rafa Laguna + RagingRoosevelt Ragnar Pettersson Rainer Amler Rammeow - red1939 Relintai Remi Rampin - Remtaine Reneator - Riccardo Marini - Richard Hayes Richard Ivánek Riley Robin Ward @@ -725,7 +671,6 @@ generous deed immortalized in the next stable release of Godot Engine. Roka Roland Rząsa Roman Papush - Ronald Ho Hip (CrimsonZA) Roy Scayged Russ Ryan Groom @@ -733,6 +678,7 @@ generous deed immortalized in the next stable release of Godot Engine. Rykk Sammy Fischer Sangeeth Pavithran + Sasha Schwartz Sean Dee Sebastian Michailidis SeongWan Kim @@ -742,7 +688,6 @@ generous deed immortalized in the next stable release of Godot Engine. Shane Shane Lillie Shane Spoor - Silver1063 simdee Simon Jonas Larsen Simon Schoenenberger @@ -757,22 +702,19 @@ generous deed immortalized in the next stable release of Godot Engine. Soheib El-Harrache Solene Waked Sophie Winter - Sparky Squidgy Squirrel - Stéphane Roussel + SSebigo Stephen Rice Stephen Schlie Steven Drovie summerblind Sung soo Choi - Svenne Krap SxP tadashi endo Tarch Techwizz Terry - Theodore Lindsey TheVoiceInMyHead TheWint Thibaut DECROMBECQUE @@ -789,10 +731,8 @@ generous deed immortalized in the next stable release of Godot Engine. Timothy B. MacDonald Tim Riley Toadile - Tobias Bradtke Tom Coxon Tom Webster - Torgeir Lilleskog Torsten Crass travis f w Travis O'Brien @@ -808,13 +748,15 @@ generous deed immortalized in the next stable release of Godot Engine. v01tech Vaida Vaughan Ling + Vavin.X VikFro Vincent Barkmann + Vincent Cloutier Vincent Foulon Vitaliy Sapronenko - Vi Watch Vladimir Savin - Vuli Nux + volklobo@live.com + Vulinux Wapiti . Wiley Thompson William Bodin @@ -826,6 +768,7 @@ generous deed immortalized in the next stable release of Godot Engine. Yan Shi Zekim Zher Huei Lee + Zoee Silcock ケルベロス 貴宏 小松 郝晨煜 diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index d0de39a79d..385cd2b2fd 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1124,8 +1124,12 @@ void ProjectSettings::set_setting(const String &p_setting, const Variant &p_valu set(p_setting, p_value); } -Variant ProjectSettings::get_setting(const String &p_setting) const { - return get(p_setting); +Variant ProjectSettings::get_setting(const String &p_setting, const Variant &p_default_value) const { + if (has_setting(p_setting)) { + return get(p_setting); + } else { + return p_default_value; + } } bool ProjectSettings::has_custom_feature(const String &p_feature) const { @@ -1158,7 +1162,7 @@ ProjectSettings::AutoloadInfo ProjectSettings::get_autoload(const StringName &p_ void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting); ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting); - ClassDB::bind_method(D_METHOD("get_setting", "name"), &ProjectSettings::get_setting); + ClassDB::bind_method(D_METHOD("get_setting", "name", "default_value"), &ProjectSettings::get_setting, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("set_order", "name", "position"), &ProjectSettings::set_order); ClassDB::bind_method(D_METHOD("get_order", "name"), &ProjectSettings::get_order); ClassDB::bind_method(D_METHOD("set_initial_value", "name", "value"), &ProjectSettings::set_initial_value); @@ -1277,7 +1281,6 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("physics/2d/run_on_separate_thread", false); GLOBAL_DEF("physics/3d/run_on_separate_thread", false); - GLOBAL_DEF("debug/disable_touch", false); GLOBAL_DEF("debug/settings/profiler/max_functions", 16384); custom_prop_info["debug/settings/profiler/max_functions"] = PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 960dfe0395..2ffbda9cea 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -141,7 +141,7 @@ public: static const int CONFIG_VERSION = 5; void set_setting(const String &p_setting, const Variant &p_value); - Variant get_setting(const String &p_setting) const; + Variant get_setting(const String &p_setting, const Variant &p_default_value = Variant()) const; bool has_setting(String p_var) const; String localize_path(const String &p_path) const; diff --git a/core/core_constants.cpp b/core/core_constants.cpp index e0772cd43d..c72b6212d7 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -153,6 +153,7 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TOP_TO); BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_CENTER_TO); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_BASELINE_TO); BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_BOTTOM_TO); BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TO_TOP); diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 5812a24c4f..a0e3778dcc 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -252,63 +252,140 @@ Dictionary GDExtensionAPIDump::generate_extension_api() { } { - // Member offsets sizes. + // Member offsets, meta types and sizes. + +#define REAL_MEMBER_OFFSET(type, member) \ + { \ + type, \ + member, \ + "float", \ + sizeof(float), \ + "float", \ + sizeof(float), \ + "double", \ + sizeof(double), \ + "double", \ + sizeof(double), \ + } + +#define INT32_MEMBER_OFFSET(type, member) \ + { \ + type, \ + member, \ + "int32", \ + sizeof(int32_t), \ + "int32", \ + sizeof(int32_t), \ + "int32", \ + sizeof(int32_t), \ + "int32", \ + sizeof(int32_t), \ + } + +#define INT32_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \ + { \ + type, \ + member, \ + member_type, \ + sizeof(int32_t) * member_elems, \ + member_type, \ + sizeof(int32_t) * member_elems, \ + member_type, \ + sizeof(int32_t) * member_elems, \ + member_type, \ + sizeof(int32_t) * member_elems, \ + } + +#define REAL_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \ + { \ + type, \ + member, \ + member_type, \ + sizeof(float) * member_elems, \ + member_type, \ + sizeof(float) * member_elems, \ + member_type, \ + sizeof(double) * member_elems, \ + member_type, \ + sizeof(double) * member_elems, \ + } + struct { Variant::Type type; const char *member; - uint32_t offset_32_bits_real_float; - uint32_t offset_64_bits_real_float; - uint32_t offset_32_bits_real_double; - uint32_t offset_64_bits_real_double; + const char *member_meta_32_bits_real_float; + const uint32_t member_size_32_bits_real_float; + const char *member_meta_64_bits_real_float; + const uint32_t member_size_64_bits_real_float; + const char *member_meta_32_bits_real_double; + const uint32_t member_size_32_bits_real_double; + const char *member_meta_64_bits_real_double; + const uint32_t member_size_64_bits_real_double; } member_offset_array[] = { - { Variant::VECTOR2, "x", 0, 0, 0, 0 }, - { Variant::VECTOR2, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) }, - { Variant::VECTOR2I, "x", 0, 0, 0, 0 }, - { Variant::VECTOR2I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) }, - { Variant::RECT2, "position", 0, 0, 0, 0 }, - { Variant::RECT2, "size", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, - { Variant::RECT2I, "position", 0, 0, 0, 0 }, - { Variant::RECT2I, "size", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) }, - { Variant::VECTOR3, "x", 0, 0, 0, 0 }, - { Variant::VECTOR3, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) }, - { Variant::VECTOR3, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, - { Variant::VECTOR3I, "x", 0, 0, 0, 0 }, - { Variant::VECTOR3I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) }, - { Variant::VECTOR3I, "z", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) }, - { Variant::TRANSFORM2D, "x", 0, 0, 0, 0 }, - { Variant::TRANSFORM2D, "y", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, - { Variant::TRANSFORM2D, "origin", 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) }, - { Variant::VECTOR4, "x", 0, 0, 0, 0 }, - { Variant::VECTOR4, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) }, - { Variant::VECTOR4, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, - { Variant::VECTOR4, "w", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(double), 3 * sizeof(double) }, - { Variant::VECTOR4I, "x", 0, 0, 0, 0 }, - { Variant::VECTOR4I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) }, - { Variant::VECTOR4I, "z", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) }, - { Variant::VECTOR4I, "w", 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t) }, - { Variant::PLANE, "normal", 0, 0, 0, 0 }, - { Variant::PLANE, "d", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, - { Variant::QUATERNION, "x", 0, 0, 0, 0 }, - { Variant::QUATERNION, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) }, - { Variant::QUATERNION, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, - { Variant::QUATERNION, "w", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(double), 3 * sizeof(double) }, - { Variant::AABB, "position", 0, 0, 0, 0 }, - { Variant::AABB, "size", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, - // Remember that basis vectors are flipped! - { Variant::BASIS, "x", 0, 0, 0, 0 }, - { Variant::BASIS, "y", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, - { Variant::BASIS, "z", vec3_elems * 2 * sizeof(float), vec3_elems * 2 * sizeof(float), vec3_elems * 2 * sizeof(double), vec3_elems * 2 * sizeof(double) }, - { Variant::TRANSFORM3D, "basis", 0, 0, 0, 0 }, - { Variant::TRANSFORM3D, "origin", (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(double), (vec3_elems * 3) * sizeof(double) }, - { Variant::PROJECTION, "x", 0, 0, 0, 0 }, - { Variant::PROJECTION, "y", vec4_elems * sizeof(float), vec4_elems * sizeof(float), vec4_elems * sizeof(double), vec4_elems * sizeof(double) }, - { Variant::PROJECTION, "z", vec4_elems * 2 * sizeof(float), vec4_elems * 2 * sizeof(float), vec4_elems * 2 * sizeof(double), vec4_elems * 2 * sizeof(double) }, - { Variant::PROJECTION, "w", vec4_elems * 3 * sizeof(float), vec4_elems * 3 * sizeof(float), vec4_elems * 3 * sizeof(double), vec4_elems * 3 * sizeof(double) }, - { Variant::COLOR, "r", 0, 0, 0, 0 }, - { Variant::COLOR, "g", sizeof(float), sizeof(float), sizeof(float), sizeof(float) }, - { Variant::COLOR, "b", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(float) }, - { Variant::COLOR, "a", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(float) }, - { Variant::NIL, nullptr, 0, 0, 0, 0 }, + // Vector2 + REAL_MEMBER_OFFSET(Variant::VECTOR2, "x"), + REAL_MEMBER_OFFSET(Variant::VECTOR2, "y"), + // Vector2i + INT32_MEMBER_OFFSET(Variant::VECTOR2I, "x"), + INT32_MEMBER_OFFSET(Variant::VECTOR2I, "y"), + // Rect2 + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2, "position", "Vector2", 2), + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2, "size", "Vector2", 2), + // Rect2i + INT32_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2I, "position", "Vector2i", 2), + INT32_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2I, "size", "Vector2i", 2), + // Vector3 + REAL_MEMBER_OFFSET(Variant::VECTOR3, "x"), + REAL_MEMBER_OFFSET(Variant::VECTOR3, "y"), + REAL_MEMBER_OFFSET(Variant::VECTOR3, "z"), + // Vector3i + INT32_MEMBER_OFFSET(Variant::VECTOR3I, "x"), + INT32_MEMBER_OFFSET(Variant::VECTOR3I, "y"), + INT32_MEMBER_OFFSET(Variant::VECTOR3I, "z"), + // Transform2D + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM2D, "x", "Vector2", 2), + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM2D, "y", "Vector2", 2), + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM2D, "origin", "Vector2", 2), + // Vector4 + REAL_MEMBER_OFFSET(Variant::VECTOR4, "x"), + REAL_MEMBER_OFFSET(Variant::VECTOR4, "y"), + REAL_MEMBER_OFFSET(Variant::VECTOR4, "z"), + REAL_MEMBER_OFFSET(Variant::VECTOR4, "w"), + // Vector4i + INT32_MEMBER_OFFSET(Variant::VECTOR4I, "x"), + INT32_MEMBER_OFFSET(Variant::VECTOR4I, "y"), + INT32_MEMBER_OFFSET(Variant::VECTOR4I, "z"), + INT32_MEMBER_OFFSET(Variant::VECTOR4I, "w"), + // Plane + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PLANE, "normal", "Vector3", vec3_elems), + REAL_MEMBER_OFFSET(Variant::PLANE, "d"), + // Quaternion + REAL_MEMBER_OFFSET(Variant::QUATERNION, "x"), + REAL_MEMBER_OFFSET(Variant::QUATERNION, "y"), + REAL_MEMBER_OFFSET(Variant::QUATERNION, "z"), + REAL_MEMBER_OFFSET(Variant::QUATERNION, "w"), + // AABB + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::AABB, "position", "Vector3", vec3_elems), + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::AABB, "size", "Vector3", vec3_elems), + // Basis (remember that basis vectors are flipped!) + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::BASIS, "x", "Vector3", vec3_elems), + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::BASIS, "y", "Vector3", vec3_elems), + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::BASIS, "z", "Vector3", vec3_elems), + // Transform3D + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM3D, "basis", "Basis", vec3_elems * 3), + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM3D, "origin", "Vector3", vec3_elems), + // Projection + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "x", "Vector4", vec4_elems), + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "y", "Vector4", vec4_elems), + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "z", "Vector4", vec4_elems), + REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "w", "Vector4", vec4_elems), + // Color (always composed of 4bytes floats) + { Variant::COLOR, "r", "float", sizeof(float), "float", sizeof(float), "float", sizeof(float), "float", sizeof(float) }, + { Variant::COLOR, "g", "float", sizeof(float), "float", sizeof(float), "float", sizeof(float), "float", sizeof(float) }, + { Variant::COLOR, "b", "float", sizeof(float), "float", sizeof(float), "float", sizeof(float), "float", sizeof(float) }, + { Variant::COLOR, "a", "float", sizeof(float), "float", sizeof(float), "float", sizeof(float), "float", sizeof(float) }, + // End marker, must stay last + { Variant::NIL, nullptr, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0 }, }; Array core_type_member_offsets; @@ -319,15 +396,16 @@ Dictionary GDExtensionAPIDump::generate_extension_api() { Array type_offsets; uint32_t idx = 0; - Variant::Type last_type = Variant::NIL; + Variant::Type previous_type = Variant::NIL; Dictionary d2; Array members; + uint32_t offset = 0; while (true) { Variant::Type t = member_offset_array[idx].type; - if (t != last_type) { - if (last_type != Variant::NIL) { + if (t != previous_type) { + if (previous_type != Variant::NIL) { d2["members"] = members; type_offsets.push_back(d2); } @@ -338,27 +416,35 @@ Dictionary GDExtensionAPIDump::generate_extension_api() { String name = t == Variant::VARIANT_MAX ? String("Variant") : Variant::get_type_name(t); d2 = Dictionary(); members = Array(); + offset = 0; d2["name"] = name; - last_type = t; + previous_type = t; } Dictionary d3; - uint32_t offset = 0; + const char *member_meta = nullptr; + uint32_t member_size = 0; switch (i) { case 0: - offset = member_offset_array[idx].offset_32_bits_real_float; + member_meta = member_offset_array[idx].member_meta_32_bits_real_float; + member_size = member_offset_array[idx].member_size_32_bits_real_float; break; case 1: - offset = member_offset_array[idx].offset_64_bits_real_float; + member_meta = member_offset_array[idx].member_meta_64_bits_real_float; + member_size = member_offset_array[idx].member_size_64_bits_real_float; break; case 2: - offset = member_offset_array[idx].offset_32_bits_real_double; + member_meta = member_offset_array[idx].member_meta_32_bits_real_double; + member_size = member_offset_array[idx].member_size_32_bits_real_double; break; case 3: - offset = member_offset_array[idx].offset_64_bits_real_double; + member_meta = member_offset_array[idx].member_meta_64_bits_real_double; + member_size = member_offset_array[idx].member_size_64_bits_real_double; break; } d3["member"] = member_offset_array[idx].member; d3["offset"] = offset; + d3["meta"] = member_meta; + offset += member_size; members.push_back(d3); idx++; } diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 197e9db1e7..2074495aa9 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -80,7 +80,7 @@ static void gdextension_variant_destroy(GDExtensionVariantPtr p_self) { // variant type -static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { +static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { Variant *self = (Variant *)p_self; const StringName method = *reinterpret_cast<const StringName *>(p_method); const Variant **args = (const Variant **)p_args; @@ -96,7 +96,7 @@ static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionCo } } -static void gdextension_variant_call_static(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { +static void gdextension_variant_call_static(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { Variant::Type type = (Variant::Type)p_type; const StringName method = *reinterpret_cast<const StringName *>(p_method); const Variant **args = (const Variant **)p_args; @@ -486,7 +486,7 @@ static GDExtensionPtrConstructor gdextension_variant_get_ptr_constructor(GDExten static GDExtensionPtrDestructor gdextension_variant_get_ptr_destructor(GDExtensionVariantType p_type) { return (GDExtensionPtrDestructor)Variant::get_ptr_destructor(Variant::Type(p_type)); } -static void gdextension_variant_construct(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error) { +static void gdextension_variant_construct(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error) { memnew_placement(p_base, Variant); Callable::CallError error; @@ -814,7 +814,7 @@ static GDExtensionVariantPtr gdextension_dictionary_operator_index_const(GDExten /* OBJECT API */ -static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { +static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { const MethodBind *mb = reinterpret_cast<const MethodBind *>(p_method_bind); Object *o = (Object *)p_instance; const Variant **args = (const Variant **)p_args; @@ -830,7 +830,7 @@ static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_metho } } -static void gdextension_object_method_bind_ptrcall(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, GDExtensionConstTypePtr *p_args, GDExtensionTypePtr p_ret) { +static void gdextension_object_method_bind_ptrcall(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr p_ret) { const MethodBind *mb = reinterpret_cast<const MethodBind *>(p_method_bind); Object *o = (Object *)p_instance; mb->ptrcall(o, (const void **)p_args, p_ret); @@ -879,6 +879,23 @@ static GDObjectInstanceID gdextension_object_get_instance_id(GDExtensionConstObj return (GDObjectInstanceID)o->get_instance_id(); } +static GDExtensionObjectPtr gdextension_ref_get_object(GDExtensionConstRefPtr p_ref) { + const Ref<RefCounted> *ref = (const Ref<RefCounted> *)p_ref; + if (ref == nullptr || ref->is_null()) { + return (GDExtensionObjectPtr) nullptr; + } else { + return (GDExtensionObjectPtr)ref->ptr(); + } +} + +static void gdextension_ref_set_object(GDExtensionRefPtr p_ref, GDExtensionObjectPtr p_object) { + Ref<RefCounted> *ref = (Ref<RefCounted> *)p_ref; + ERR_FAIL_NULL(ref); + + Object *o = (RefCounted *)p_object; + ref->reference_ptr(o); +} + static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) { ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension); script_instance_extension->instance = p_instance_data; @@ -1057,6 +1074,11 @@ void gdextension_setup_interface(GDExtensionInterface *p_interface) { gde_interface.object_get_instance_from_id = gdextension_object_get_instance_from_id; gde_interface.object_get_instance_id = gdextension_object_get_instance_id; + /* REFERENCE */ + + gde_interface.ref_get_object = gdextension_ref_get_object; + gde_interface.ref_set_object = gdextension_ref_set_object; + /* SCRIPT INSTANCE */ gde_interface.script_instance_create = gdextension_script_instance_create; diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index b59a6b5ca4..0b7615ffdd 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -154,6 +154,8 @@ typedef const void *GDExtensionMethodBindPtr; typedef int64_t GDExtensionInt; typedef uint8_t GDExtensionBool; typedef uint64_t GDObjectInstanceID; +typedef void *GDExtensionRefPtr; +typedef const void *GDExtensionConstRefPtr; /* VARIANT DATA I/O */ @@ -176,8 +178,8 @@ typedef struct { typedef void (*GDExtensionVariantFromTypeConstructorFunc)(GDExtensionVariantPtr, GDExtensionTypePtr); typedef void (*GDExtensionTypeFromVariantConstructorFunc)(GDExtensionTypePtr, GDExtensionVariantPtr); typedef void (*GDExtensionPtrOperatorEvaluator)(GDExtensionConstTypePtr p_left, GDExtensionConstTypePtr p_right, GDExtensionTypePtr r_result); -typedef void (*GDExtensionPtrBuiltInMethod)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return, int p_argument_count); -typedef void (*GDExtensionPtrConstructor)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr *p_args); +typedef void (*GDExtensionPtrBuiltInMethod)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return, int p_argument_count); +typedef void (*GDExtensionPtrConstructor)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args); typedef void (*GDExtensionPtrDestructor)(GDExtensionTypePtr p_base); typedef void (*GDExtensionPtrSetter)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr p_value); typedef void (*GDExtensionPtrGetter)(GDExtensionConstTypePtr p_base, GDExtensionTypePtr r_value); @@ -186,7 +188,7 @@ typedef void (*GDExtensionPtrIndexedGetter)(GDExtensionConstTypePtr p_base, GDEx typedef void (*GDExtensionPtrKeyedSetter)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr p_key, GDExtensionConstTypePtr p_value); typedef void (*GDExtensionPtrKeyedGetter)(GDExtensionConstTypePtr p_base, GDExtensionConstTypePtr p_key, GDExtensionTypePtr r_value); typedef uint32_t (*GDExtensionPtrKeyedChecker)(GDExtensionConstVariantPtr p_base, GDExtensionConstVariantPtr p_key); -typedef void (*GDExtensionPtrUtilityFunction)(GDExtensionTypePtr r_return, GDExtensionConstTypePtr *p_arguments, int p_argument_count); +typedef void (*GDExtensionPtrUtilityFunction)(GDExtensionTypePtr r_return, const GDExtensionConstTypePtr *p_args, int p_argument_count); typedef GDExtensionObjectPtr (*GDExtensionClassConstructor)(); @@ -240,7 +242,7 @@ typedef void (*GDExtensionClassNotification)(GDExtensionClassInstancePtr p_insta typedef void (*GDExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr p_out); typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance); typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance); -typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); +typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_userdata); typedef void (*GDExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance); typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_userdata, GDExtensionConstStringNamePtr p_name); @@ -293,8 +295,8 @@ typedef enum { GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE } GDExtensionClassMethodArgumentMetadata; -typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); -typedef void (*GDExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); +typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); +typedef void (*GDExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); typedef struct { GDExtensionStringNamePtr name; @@ -342,7 +344,7 @@ typedef void (*GDExtensionScriptInstanceFreeMethodList)(GDExtensionScriptInstanc typedef GDExtensionBool (*GDExtensionScriptInstanceHasMethod)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name); -typedef void (*GDExtensionScriptInstanceCall)(GDExtensionScriptInstanceDataPtr p_self, GDExtensionConstStringNamePtr p_method, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); +typedef void (*GDExtensionScriptInstanceCall)(GDExtensionScriptInstanceDataPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); typedef void (*GDExtensionScriptInstanceNotification)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what); typedef void (*GDExtensionScriptInstanceToString)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out); @@ -427,8 +429,8 @@ typedef struct { void (*variant_destroy)(GDExtensionVariantPtr p_self); /* variant type */ - void (*variant_call)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); - void (*variant_call_static)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); + void (*variant_call)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); + void (*variant_call_static)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); void (*variant_evaluate)(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionVariantPtr r_return, GDExtensionBool *r_valid); void (*variant_set)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); void (*variant_set_named)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); @@ -463,7 +465,7 @@ typedef struct { GDExtensionPtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionInt p_hash); GDExtensionPtrConstructor (*variant_get_ptr_constructor)(GDExtensionVariantType p_type, int32_t p_constructor); GDExtensionPtrDestructor (*variant_get_ptr_destructor)(GDExtensionVariantType p_type); - void (*variant_construct)(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error); + void (*variant_construct)(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error); GDExtensionPtrSetter (*variant_get_ptr_setter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); GDExtensionPtrGetter (*variant_get_ptr_getter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); GDExtensionPtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDExtensionVariantType p_type); @@ -537,8 +539,8 @@ typedef struct { /* OBJECT */ - void (*object_method_bind_call)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_ret, GDExtensionCallError *r_error); - void (*object_method_bind_ptrcall)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); + void (*object_method_bind_call)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_ret, GDExtensionCallError *r_error); + void (*object_method_bind_ptrcall)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); void (*object_destroy)(GDExtensionObjectPtr p_o); GDExtensionObjectPtr (*global_get_singleton)(GDExtensionConstStringNamePtr p_name); @@ -551,6 +553,11 @@ typedef struct { GDExtensionObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id); GDObjectInstanceID (*object_get_instance_id)(GDExtensionConstObjectPtr p_object); + /* REFERENCE */ + + GDExtensionObjectPtr (*ref_get_object)(GDExtensionConstRefPtr p_ref); + void (*ref_set_object)(GDExtensionRefPtr p_ref, GDExtensionObjectPtr p_object); + /* SCRIPT INSTANCE */ GDExtensionScriptInstancePtr (*script_instance_create)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data); diff --git a/core/io/image.cpp b/core/io/image.cpp index 1b9538794a..a2e1bc22be 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -173,6 +173,14 @@ int Image::get_format_pixel_size(Format p_format) { return 1; case FORMAT_DXT5_RA_AS_RG: return 1; + case FORMAT_ASTC_4x4: + return 1; + case FORMAT_ASTC_4x4_HDR: + return 1; + case FORMAT_ASTC_8x8: + return 1; + case FORMAT_ASTC_8x8_HDR: + return 1; case FORMAT_MAX: { } } @@ -213,7 +221,18 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) { r_h = 4; } break; + case FORMAT_ASTC_4x4: + case FORMAT_ASTC_4x4_HDR: { + r_w = 4; + r_h = 4; + + } break; + case FORMAT_ASTC_8x8: + case FORMAT_ASTC_8x8_HDR: { + r_w = 8; + r_h = 8; + } break; default: { r_w = 1; r_h = 1; @@ -222,7 +241,9 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) { } int Image::get_format_pixel_rshift(Format p_format) { - if (p_format == FORMAT_DXT1 || p_format == FORMAT_RGTC_R || p_format == FORMAT_ETC || p_format == FORMAT_ETC2_R11 || p_format == FORMAT_ETC2_R11S || p_format == FORMAT_ETC2_RGB8 || p_format == FORMAT_ETC2_RGB8A1) { + if (p_format == FORMAT_ASTC_8x8) { + return 2; + } else if (p_format == FORMAT_DXT1 || p_format == FORMAT_RGTC_R || p_format == FORMAT_ETC || p_format == FORMAT_ETC2_R11 || p_format == FORMAT_ETC2_R11S || p_format == FORMAT_ETC2_RGB8 || p_format == FORMAT_ETC2_RGB8A1) { return 1; } else { return 0; @@ -260,6 +281,14 @@ int Image::get_format_block_size(Format p_format) { { return 4; } + case FORMAT_ASTC_4x4: + case FORMAT_ASTC_4x4_HDR: { + return 4; + } + case FORMAT_ASTC_8x8: + case FORMAT_ASTC_8x8_HDR: { + return 8; + } default: { } } @@ -2581,19 +2610,23 @@ Error Image::decompress() { _image_decompress_etc1(this); } else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RA_AS_RG && _image_decompress_etc2) { _image_decompress_etc2(this); + } else if (format >= FORMAT_ASTC_4x4 && format <= FORMAT_ASTC_8x8_HDR && _image_decompress_astc) { + _image_decompress_astc(this); } else { return ERR_UNAVAILABLE; } return OK; } -Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) { +Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality, ASTCFormat p_astc_format) { ERR_FAIL_INDEX_V_MSG(p_mode, COMPRESS_MAX, ERR_INVALID_PARAMETER, "Invalid compress mode."); ERR_FAIL_INDEX_V_MSG(p_source, COMPRESS_SOURCE_MAX, ERR_INVALID_PARAMETER, "Invalid compress source."); - return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality); + return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality, p_astc_format); } -Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality) { +Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality, ASTCFormat p_astc_format) { + ERR_FAIL_COND_V(data.is_empty(), ERR_INVALID_DATA); + switch (p_mode) { case COMPRESS_S3TC: { ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); @@ -2611,6 +2644,10 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); _image_compress_bptc_func(this, p_lossy_quality, p_channels); } break; + case COMPRESS_ASTC: { + ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); + _image_compress_astc_func(this, p_lossy_quality, p_astc_format); + } break; case COMPRESS_MAX: { ERR_FAIL_V(ERR_INVALID_PARAMETER); } break; @@ -2967,10 +3004,12 @@ void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = nu void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = nullptr; void (*Image::_image_compress_etc1_func)(Image *, float) = nullptr; void (*Image::_image_compress_etc2_func)(Image *, float, Image::UsedChannels) = nullptr; +void (*Image::_image_compress_astc_func)(Image *, float, Image::ASTCFormat) = nullptr; void (*Image::_image_decompress_bc)(Image *) = nullptr; void (*Image::_image_decompress_bptc)(Image *) = nullptr; void (*Image::_image_decompress_etc1)(Image *) = nullptr; void (*Image::_image_decompress_etc2)(Image *) = nullptr; +void (*Image::_image_decompress_astc)(Image *) = nullptr; Vector<uint8_t> (*Image::webp_lossy_packer)(const Ref<Image> &, float) = nullptr; Vector<uint8_t> (*Image::webp_lossless_packer)(const Ref<Image> &) = nullptr; @@ -3387,8 +3426,8 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible); ClassDB::bind_method(D_METHOD("detect_used_channels", "source"), &Image::detect_used_channels, DEFVAL(COMPRESS_SOURCE_GENERIC)); - ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(0.7)); - ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "lossy_quality"), &Image::compress_from_channels, DEFVAL(0.7)); + ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality", "astc_format"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(0.7), DEFVAL(ASTC_FORMAT_4x4)); + ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "lossy_quality", "astc_format"), &Image::compress_from_channels, DEFVAL(0.7), DEFVAL(ASTC_FORMAT_4x4)); ClassDB::bind_method(D_METHOD("decompress"), &Image::decompress); ClassDB::bind_method(D_METHOD("is_compressed"), &Image::is_compressed); @@ -3472,6 +3511,10 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(FORMAT_ETC2_RGB8A1); BIND_ENUM_CONSTANT(FORMAT_ETC2_RA_AS_RG); BIND_ENUM_CONSTANT(FORMAT_DXT5_RA_AS_RG); + BIND_ENUM_CONSTANT(FORMAT_ASTC_4x4); + BIND_ENUM_CONSTANT(FORMAT_ASTC_4x4_HDR); + BIND_ENUM_CONSTANT(FORMAT_ASTC_8x8); + BIND_ENUM_CONSTANT(FORMAT_ASTC_8x8_HDR); BIND_ENUM_CONSTANT(FORMAT_MAX); BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST); @@ -3499,6 +3542,9 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(COMPRESS_SOURCE_GENERIC); BIND_ENUM_CONSTANT(COMPRESS_SOURCE_SRGB); BIND_ENUM_CONSTANT(COMPRESS_SOURCE_NORMAL); + + BIND_ENUM_CONSTANT(ASTC_FORMAT_4x4); + BIND_ENUM_CONSTANT(ASTC_FORMAT_8x8); } void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)) { diff --git a/core/io/image.h b/core/io/image.h index ad5c0b4a04..8bcee4404b 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -109,6 +109,10 @@ public: FORMAT_ETC2_RGB8A1, FORMAT_ETC2_RA_AS_RG, //used to make basis universal happy FORMAT_DXT5_RA_AS_RG, //used to make basis universal happy + FORMAT_ASTC_4x4, + FORMAT_ASTC_4x4_HDR, + FORMAT_ASTC_8x8, + FORMAT_ASTC_8x8_HDR, FORMAT_MAX }; @@ -134,6 +138,11 @@ public: }; //some functions provided by something else + enum ASTCFormat { + ASTC_FORMAT_4x4, + ASTC_FORMAT_8x8, + }; + static ImageMemLoadFunc _png_mem_loader_func; static ImageMemLoadFunc _jpg_mem_loader_func; static ImageMemLoadFunc _webp_mem_loader_func; @@ -144,11 +153,13 @@ public: static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels); static void (*_image_compress_etc1_func)(Image *, float); static void (*_image_compress_etc2_func)(Image *, float, UsedChannels p_channels); + static void (*_image_compress_astc_func)(Image *, float, ASTCFormat p_format); static void (*_image_decompress_bc)(Image *); static void (*_image_decompress_bptc)(Image *); static void (*_image_decompress_etc1)(Image *); static void (*_image_decompress_etc2)(Image *); + static void (*_image_decompress_astc)(Image *); static Vector<uint8_t> (*webp_lossy_packer)(const Ref<Image> &p_image, float p_quality); static Vector<uint8_t> (*webp_lossless_packer)(const Ref<Image> &p_image); @@ -347,6 +358,7 @@ public: COMPRESS_ETC, COMPRESS_ETC2, COMPRESS_BPTC, + COMPRESS_ASTC, COMPRESS_MAX, }; enum CompressSource { @@ -356,8 +368,8 @@ public: COMPRESS_SOURCE_MAX, }; - Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7); - Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality = 0.7); + Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7, ASTCFormat p_astc_format = ASTC_FORMAT_4x4); + Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality = 0.7, ASTCFormat p_astc_format = ASTC_FORMAT_4x4); Error decompress(); bool is_compressed() const; @@ -429,5 +441,6 @@ VARIANT_ENUM_CAST(Image::CompressSource) VARIANT_ENUM_CAST(Image::UsedChannels) VARIANT_ENUM_CAST(Image::AlphaMode) VARIANT_ENUM_CAST(Image::RoughnessChannel) +VARIANT_ENUM_CAST(Image::ASTCFormat) #endif // IMAGE_H diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index c30acf32bb..968102e323 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -155,6 +155,19 @@ bool AStarGrid2D::is_point_solid(const Vector2i &p_id) const { return points[p_id.y][p_id.x].solid; } +void AStarGrid2D::set_point_weight_scale(const Vector2i &p_id, real_t p_weight_scale) { + ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set point's weight scale. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height)); + ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale)); + points[p_id.y][p_id.x].weight_scale = p_weight_scale; +} + +real_t AStarGrid2D::get_point_weight_scale(const Vector2i &p_id) const { + ERR_FAIL_COND_V_MSG(dirty, 0, "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), 0, vformat("Can't get point's weight scale. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height)); + return points[p_id.y][p_id.x].weight_scale; +} + AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) { if (!p_to || p_to->solid) { return nullptr; @@ -388,7 +401,10 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) { _get_nbors(p, nbors); for (List<Point *>::Element *E = nbors.front(); E; E = E->next()) { Point *e = E->get(); // The neighbour point. + real_t weight_scale = 1.0; + if (jumping_enabled) { + // TODO: Make it works with weight_scale. e = _jump(p, e); if (!e || e->closed_pass == pass) { continue; @@ -397,9 +413,10 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) { if (e->solid || e->closed_pass == pass) { continue; } + weight_scale = e->weight_scale; } - real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id); + real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * weight_scale; bool new_point = false; if (e->open_pass != pass) { // The point wasn't inside the open list. @@ -446,6 +463,12 @@ void AStarGrid2D::clear() { size = Vector2i(); } +Vector2 AStarGrid2D::get_point_position(const Vector2i &p_id) const { + ERR_FAIL_COND_V_MSG(dirty, Vector2(), "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), Vector2(), vformat("Can't get point's position. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height)); + return points[p_id.y][p_id.x].pos; +} + Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method."); ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height)); @@ -559,8 +582,11 @@ void AStarGrid2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_default_heuristic"), &AStarGrid2D::get_default_heuristic); ClassDB::bind_method(D_METHOD("set_point_solid", "id", "solid"), &AStarGrid2D::set_point_solid, DEFVAL(true)); ClassDB::bind_method(D_METHOD("is_point_solid", "id"), &AStarGrid2D::is_point_solid); + ClassDB::bind_method(D_METHOD("set_point_weight_scale", "id", "weight_scale"), &AStarGrid2D::set_point_weight_scale); + ClassDB::bind_method(D_METHOD("get_point_weight_scale", "id"), &AStarGrid2D::get_point_weight_scale); ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear); + ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStarGrid2D::get_point_position); ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::get_point_path); ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::get_id_path); diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h index 1002f18738..3b39d46a20 100644 --- a/core/math/a_star_grid_2d.h +++ b/core/math/a_star_grid_2d.h @@ -72,6 +72,7 @@ private: bool solid = false; Vector2 pos; + real_t weight_scale = 1.0; // Used for pathfinding. Point *prev_point = nullptr; @@ -166,8 +167,12 @@ public: void set_point_solid(const Vector2i &p_id, bool p_solid = true); bool is_point_solid(const Vector2i &p_id) const; + void set_point_weight_scale(const Vector2i &p_id, real_t p_weight_scale); + real_t get_point_weight_scale(const Vector2i &p_id) const; + void clear(); + Vector2 get_point_position(const Vector2i &p_id) const; Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to); TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to); }; diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 561970d2ee..51d88d8ea0 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -2274,8 +2274,7 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3 // Copy the edges over. There's two "half-edges" for every edge, so we pick only one of them. r_mesh.edges.resize(ch.edges.size() / 2); - OAHashMap<uint64_t, int32_t> edge_map; - edge_map.reserve(ch.edges.size() * 4); // The higher the capacity, the faster the insert + OAHashMap<uint64_t, int32_t> edge_map(ch.edges.size() * 4); // The higher the capacity, the faster the insert uint32_t edges_copied = 0; for (uint32_t i = 0; i < ch.edges.size(); i++) { diff --git a/core/math/math_defs.h b/core/math/math_defs.h index 759667e2d5..1058a47c1a 100644 --- a/core/math/math_defs.h +++ b/core/math/math_defs.h @@ -86,6 +86,7 @@ enum InlineAlignment { // Image alignment points. INLINE_ALIGNMENT_TOP_TO = 0b0000, INLINE_ALIGNMENT_CENTER_TO = 0b0001, + INLINE_ALIGNMENT_BASELINE_TO = 0b0011, INLINE_ALIGNMENT_BOTTOM_TO = 0b0010, INLINE_ALIGNMENT_IMAGE_MASK = 0b0011, diff --git a/core/math/projection.cpp b/core/math/projection.cpp index 9af388b081..c135e1946a 100644 --- a/core/math/projection.cpp +++ b/core/math/projection.cpp @@ -134,7 +134,7 @@ Projection Projection::create_for_hmd(int p_eye, real_t p_aspect, real_t p_intra Projection Projection::create_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { Projection proj; - proj.set_orthogonal(p_left, p_right, p_bottom, p_top, p_zfar, p_zfar); + proj.set_orthogonal(p_left, p_right, p_bottom, p_top, p_znear, p_zfar); return proj; } diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h index 71790fb825..411c589739 100644 --- a/core/object/ref_counted.h +++ b/core/object/ref_counted.h @@ -253,12 +253,14 @@ public: template <class T> struct PtrToArg<Ref<T>> { _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) { + // p_ptr points to a RefCounted object return Ref<T>(const_cast<T *>(reinterpret_cast<const T *>(p_ptr))); } typedef Ref<T> EncodeT; _FORCE_INLINE_ static void encode(Ref<T> p_val, const void *p_ptr) { + // p_ptr points to an EncodeT object which is a Ref<T> object. *(const_cast<Ref<RefCounted> *>(reinterpret_cast<const Ref<RefCounted> *>(p_ptr))) = p_val; } }; @@ -268,6 +270,7 @@ struct PtrToArg<const Ref<T> &> { typedef Ref<T> EncodeT; _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) { + // p_ptr points to a RefCounted object return Ref<T>((T *)p_ptr); } }; diff --git a/core/os/os.cpp b/core/os/os.cpp index 6d567ffd43..6b199e883f 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -155,10 +155,6 @@ int OS::get_process_id() const { return -1; } -void OS::vibrate_handheld(int p_duration_ms) { - WARN_PRINT("vibrate_handheld() only works with Android, iOS and Web"); -} - bool OS::is_stdout_verbose() const { return _verbose_stdout; } diff --git a/core/os/os.h b/core/os/os.h index 72a91f318a..07e9020a51 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -159,7 +159,7 @@ public: virtual Error kill(const ProcessID &p_pid) = 0; virtual int get_process_id() const; virtual bool is_process_running(const ProcessID &p_pid) const = 0; - virtual void vibrate_handheld(int p_duration_ms = 500); + virtual void vibrate_handheld(int p_duration_ms = 500) {} virtual Error shell_open(String p_uri); virtual Error set_cwd(const String &p_cwd); diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 9c4fc4e1b7..64d49b8b93 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -169,6 +169,10 @@ bool StringName::operator!=(const String &p_name) const { return !(operator==(p_name)); } +bool StringName::operator!=(const char *p_name) const { + return !(operator==(p_name)); +} + bool StringName::operator!=(const StringName &p_name) const { // the real magic of all this mess happens here. // this is why path comparisons are very fast diff --git a/core/string/string_name.h b/core/string/string_name.h index ff4c41af94..6a2420e02a 100644 --- a/core/string/string_name.h +++ b/core/string/string_name.h @@ -102,6 +102,7 @@ public: bool operator==(const String &p_name) const; bool operator==(const char *p_name) const; bool operator!=(const String &p_name) const; + bool operator!=(const char *p_name) const; _FORCE_INLINE_ bool is_node_unique_name() const { if (!_data) { diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 4e26b61334..adab6d07c7 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -220,37 +220,6 @@ void CharString::copy_from(const char *p_cstr) { /* String */ /*************************************************************************/ -//kind of poor should be rewritten properly -String String::word_wrap(int p_chars_per_line) const { - int from = 0; - int last_space = 0; - String ret; - for (int i = 0; i < length(); i++) { - if (i - from >= p_chars_per_line) { - if (last_space == -1) { - ret += substr(from, i - from + 1) + "\n"; - } else { - ret += substr(from, last_space - from) + "\n"; - i = last_space; //rewind - } - from = i + 1; - last_space = -1; - } else if (operator[](i) == ' ' || operator[](i) == '\t') { - last_space = i; - } else if (operator[](i) == '\n') { - ret += substr(from, i - from) + "\n"; - from = i + 1; - last_space = -1; - } - } - - if (from < length()) { - ret += substr(from, length()); - } - - return ret; -} - Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const { // Splits the URL into scheme, host, port, path. Strip credentials when present. String base = *this; diff --git a/core/string/ustring.h b/core/string/ustring.h index ed3848fb8a..559f679f0f 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -425,7 +425,6 @@ public: String c_escape_multiline() const; String c_unescape() const; String json_escape() const; - String word_wrap(int p_chars_per_line) const; Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const; String property_name_encode() const; diff --git a/core/variant/type_info.h b/core/variant/type_info.h index e355053296..8e2d150430 100644 --- a/core/variant/type_info.h +++ b/core/variant/type_info.h @@ -290,6 +290,7 @@ public: _FORCE_INLINE_ void set_flag(T p_flag) { value |= p_flag; } _FORCE_INLINE_ bool has_flag(T p_flag) const { return value & p_flag; } _FORCE_INLINE_ void clear_flag(T p_flag) { return value &= ~p_flag; } + _FORCE_INLINE_ BitField() = default; _FORCE_INLINE_ BitField(int64_t p_value) { value = p_value; } _FORCE_INLINE_ operator int64_t() const { return value; } _FORCE_INLINE_ operator Variant() const { return value; } diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 684baff348..f3b9bd235c 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -1314,7 +1314,7 @@ struct VariantZeroAssigner<float> { template <> struct VariantZeroAssigner<String> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string(v) = String(); } }; template <> @@ -1399,12 +1399,12 @@ struct VariantZeroAssigner<Color> { template <> struct VariantZeroAssigner<StringName> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string_name(v) = StringName(); } }; template <> struct VariantZeroAssigner<NodePath> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_node_path(v) = NodePath(); } }; template <> @@ -1414,12 +1414,12 @@ struct VariantZeroAssigner<::RID> { template <> struct VariantZeroAssigner<Callable> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_callable(v) = Callable(); } }; template <> struct VariantZeroAssigner<Signal> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_signal(v) = Signal(); } }; template <> @@ -1434,47 +1434,47 @@ struct VariantZeroAssigner<Array> { template <> struct VariantZeroAssigner<PackedByteArray> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_byte_array(v) = PackedByteArray(); } }; template <> struct VariantZeroAssigner<PackedInt32Array> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int32_array(v) = PackedInt32Array(); } }; template <> struct VariantZeroAssigner<PackedInt64Array> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int64_array(v) = PackedInt64Array(); } }; template <> struct VariantZeroAssigner<PackedFloat32Array> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float32_array(v) = PackedFloat32Array(); } }; template <> struct VariantZeroAssigner<PackedFloat64Array> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float64_array(v) = PackedFloat64Array(); } }; template <> struct VariantZeroAssigner<PackedStringArray> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string_array(v) = PackedStringArray(); } }; template <> struct VariantZeroAssigner<PackedVector2Array> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2_array(v) = PackedVector2Array(); } }; template <> struct VariantZeroAssigner<PackedVector3Array> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3_array(v) = PackedVector3Array(); } }; template <> struct VariantZeroAssigner<PackedColorArray> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color_array(v) = PackedColorArray(); } }; template <class T> diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 50c1ed847a..6b0dccd7fc 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1479,6 +1479,9 @@ <constant name="INLINE_ALIGNMENT_CENTER_TO" value="1" enum="InlineAlignment"> Aligns the center of the inline object (e.g. image, table) to the position of the text specified by [code]INLINE_ALIGNMENT_TO_*[/code] constant. </constant> + <constant name="INLINE_ALIGNMENT_BASELINE_TO" value="3" enum="InlineAlignment"> + Aligns the baseline (user defined) of the inline object (e.g. image, table) to the position of the text specified by [code]INLINE_ALIGNMENT_TO_*[/code] constant. + </constant> <constant name="INLINE_ALIGNMENT_BOTTOM_TO" value="2" enum="InlineAlignment"> Aligns the bottom of the inline object (e.g. image, table) to the position of the text specified by [code]INLINE_ALIGNMENT_TO_*[/code] constant. </constant> diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml index 8dde3748d7..cba783246f 100644 --- a/doc/classes/AStarGrid2D.xml +++ b/doc/classes/AStarGrid2D.xml @@ -69,6 +69,20 @@ [b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector3Array] and will print an error message. </description> </method> + <method name="get_point_position" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="id" type="Vector2i" /> + <description> + Returns the position of the point associated with the given [param id]. + </description> + </method> + <method name="get_point_weight_scale" qualifiers="const"> + <return type="float" /> + <param index="0" name="id" type="Vector2i" /> + <description> + Returns the weight scale of the point associated with the given [param id]. + </description> + </method> <method name="is_dirty" qualifiers="const"> <return type="bool" /> <description> @@ -103,6 +117,16 @@ <param index="1" name="solid" type="bool" default="true" /> <description> Disables or enables the specified point for pathfinding. Useful for making an obstacle. By default, all points are enabled. + [b]Note:[/b] Calling [method update] is not needed after the call of this function. + </description> + </method> + <method name="set_point_weight_scale"> + <return type="void" /> + <param index="0" name="id" type="Vector2i" /> + <param index="1" name="weight_scale" type="float" /> + <description> + Sets the [param weight_scale] for the point with the given [param id]. The [param weight_scale] is multiplied by the result of [method _compute_cost] when determining the overall cost of traveling across a segment from a neighboring point to this point. + [b]Note:[/b] Calling [method update] is not needed after the call of this function. </description> </method> <method name="update"> @@ -124,6 +148,7 @@ </member> <member name="jumping_enabled" type="bool" setter="set_jumping_enabled" getter="is_jumping_enabled" default="false"> Enables or disables jumping to skip up the intermediate points and speeds up the searching algorithm. + [b]Note:[/b] Currently, toggling it on disables the consideration of weight scaling in pathfinding. </member> <member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2(0, 0)"> The offset of the grid which will be applied to calculate the resulting point position returned by [method get_point_path]. If changed, [method update] needs to be called before finding the next path. @@ -134,20 +159,22 @@ </members> <constants> <constant name="HEURISTIC_EUCLIDEAN" value="0" enum="Heuristic"> - The Euclidean heuristic to be used for the pathfinding using the following formula: + The [url=https://en.wikipedia.org/wiki/Euclidean_distance]Euclidean heuristic[/url] to be used for the pathfinding using the following formula: [codeblock] dx = abs(to_id.x - from_id.x) dy = abs(to_id.y - from_id.y) result = sqrt(dx * dx + dy * dy) [/codeblock] + [b]Note:[/b] This is also the internal heuristic used in [AStar3D] and [AStar2D] by default (with the inclusion of possible z-axis coordinate). </constant> <constant name="HEURISTIC_MANHATTAN" value="1" enum="Heuristic"> - The Manhattan heuristic to be used for the pathfinding using the following formula: + The [url=https://en.wikipedia.org/wiki/Taxicab_geometry]Manhattan heuristic[/url] to be used for the pathfinding using the following formula: [codeblock] dx = abs(to_id.x - from_id.x) dy = abs(to_id.y - from_id.y) result = dx + dy [/codeblock] + [b]Note:[/b] This heuristic is intended to be used with 4-side orthogonal movements, provided by setting the [member diagonal_mode] to [constant DIAGONAL_MODE_NEVER]. </constant> <constant name="HEURISTIC_OCTILE" value="2" enum="Heuristic"> The Octile heuristic to be used for the pathfinding using the following formula: @@ -159,7 +186,7 @@ [/codeblock] </constant> <constant name="HEURISTIC_CHEBYSHEV" value="3" enum="Heuristic"> - The Chebyshev heuristic to be used for the pathfinding using the following formula: + The [url=https://en.wikipedia.org/wiki/Chebyshev_distance]Chebyshev heuristic[/url] to be used for the pathfinding using the following formula: [codeblock] dx = abs(to_id.x - from_id.x) dy = abs(to_id.y - from_id.y) diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index d9a1f896f1..c0626dcfe4 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -305,9 +305,9 @@ <return type="int" /> <param index="0" name="track_idx" type="int" /> <param index="1" name="time" type="float" /> - <param index="2" name="exact" type="bool" default="false" /> + <param index="2" name="find_mode" type="int" enum="Animation.FindMode" default="0" /> <description> - Finds the key index by time in a given track. Optionally, only find it if the exact time is given. + Finds the key index by time in a given track. Optionally, only find it if the approx/exact time is given. </description> </method> <method name="track_get_interpolation_loop_wrap" qualifiers="const"> @@ -622,5 +622,14 @@ <constant name="LOOPED_FLAG_START" value="2" enum="LoopedFlag"> This flag indicates that the animation has reached the start of the animation and just after loop processed. </constant> + <constant name="FIND_MODE_NEAREST" value="0" enum="FindMode"> + Finds the nearest time key. + </constant> + <constant name="FIND_MODE_APPROX" value="1" enum="FindMode"> + Finds only the key with approximating the time. + </constant> + <constant name="FIND_MODE_EXACT" value="2" enum="FindMode"> + Finds only the key with matching the time. + </constant> </constants> </class> diff --git a/doc/classes/AnimationNodeStateMachineTransition.xml b/doc/classes/AnimationNodeStateMachineTransition.xml index 4c2a30030b..814b2d0052 100644 --- a/doc/classes/AnimationNodeStateMachineTransition.xml +++ b/doc/classes/AnimationNodeStateMachineTransition.xml @@ -22,14 +22,11 @@ <member name="advance_expression" type="String" setter="set_advance_expression" getter="get_advance_expression" default=""""> Use an expression as a condition for state machine transitions. It is possible to create complex animation advance conditions for switching between states and gives much greater flexibility for creating complex state machines by directly interfacing with the script code. </member> - <member name="auto_advance" type="bool" setter="set_auto_advance" getter="has_auto_advance" default="false"> - Turn on the transition automatically when this state is reached. This works best with [constant SWITCH_MODE_AT_END]. - </member> - <member name="disabled" type="bool" setter="set_disabled" getter="is_disabled" default="false"> - Don't use this transition during [method AnimationNodeStateMachinePlayback.travel] or [member auto_advance]. + <member name="advance_mode" type="int" setter="set_advance_mode" getter="get_advance_mode" enum="AnimationNodeStateMachineTransition.AdvanceMode" default="1"> + Determines whether the transition should disabled, enabled when using [method AnimationNodeStateMachinePlayback.travel], or traversed automatically if the [member advance_condition] and [member advance_expression] checks are true (if assigned). </member> <member name="priority" type="int" setter="set_priority" getter="get_priority" default="1"> - Lower priority transitions are preferred when travelling through the tree via [method AnimationNodeStateMachinePlayback.travel] or [member auto_advance]. + Lower priority transitions are preferred when travelling through the tree via [method AnimationNodeStateMachinePlayback.travel] or [member advance_mode] is set to [constant ADVANCE_MODE_AUTO]. </member> <member name="switch_mode" type="int" setter="set_switch_mode" getter="get_switch_mode" enum="AnimationNodeStateMachineTransition.SwitchMode" default="0"> The transition type. @@ -58,5 +55,14 @@ <constant name="SWITCH_MODE_AT_END" value="2" enum="SwitchMode"> Wait for the current state playback to end, then switch to the beginning of the next state animation. </constant> + <constant name="ADVANCE_MODE_DISABLED" value="0" enum="AdvanceMode"> + Don't use this transition. + </constant> + <constant name="ADVANCE_MODE_ENABLED" value="1" enum="AdvanceMode"> + Only use this transition during [method AnimationNodeStateMachinePlayback.travel]. + </constant> + <constant name="ADVANCE_MODE_AUTO" value="2" enum="AdvanceMode"> + Automatically use this transition if the [member advance_condition] and [member advance_expression] checks are true (if assigned). + </constant> </constants> </class> diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index 21f4b37741..a17a727d7e 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -111,11 +111,25 @@ </member> </members> <signals> + <signal name="animation_finished"> + <param index="0" name="anim_name" type="StringName" /> + <description> + Notifies when an animation finished playing. + [b]Note:[/b] This signal is not emitted if an animation is looping or aborted. Also be aware of the possibility of unseen playback by sync and xfade. + </description> + </signal> <signal name="animation_player_changed"> <description> Emitted when the [member anim_player] is changed. </description> </signal> + <signal name="animation_started"> + <param index="0" name="anim_name" type="StringName" /> + <description> + Notifies when an animation starts playing. + [b]Note:[/b] This signal is not emitted if an animation is looping or playbacked from the middle. Also be aware of the possibility of unseen playback by sync and xfade. + </description> + </signal> </signals> <constants> <constant name="ANIMATION_PROCESS_PHYSICS" value="0" enum="AnimationProcessCallback"> diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index 29592f133d..3f76cc16ec 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -114,15 +114,13 @@ <signal name="area_entered"> <param index="0" name="area" type="Area2D" /> <description> - Emitted when another Area2D enters this Area2D. Requires [member monitoring] to be set to [code]true[/code]. - [param area] the other Area2D. + Emitted when the received [param area] enters this area. Requires [member monitoring] to be set to [code]true[/code]. </description> </signal> <signal name="area_exited"> <param index="0" name="area" type="Area2D" /> <description> - Emitted when another Area2D exits this Area2D. Requires [member monitoring] to be set to [code]true[/code]. - [param area] the other Area2D. + Emitted when the received [param area] exits this area. Requires [member monitoring] to be set to [code]true[/code]. </description> </signal> <signal name="area_shape_entered"> @@ -131,11 +129,18 @@ <param index="2" name="area_shape_index" type="int" /> <param index="3" name="local_shape_index" type="int" /> <description> - Emitted when one of another Area2D's [Shape2D]s enters one of this Area2D's [Shape2D]s. Requires [member monitoring] to be set to [code]true[/code]. - [param area_rid] the [RID] of the other Area2D's [CollisionObject2D] used by the [PhysicsServer2D]. - [param area] the other Area2D. - [param area_shape_index] the index of the [Shape2D] of the other Area2D used by the [PhysicsServer2D]. Get the [CollisionShape2D] node with [code]area.shape_owner_get_owner(area.shape_find_owner(area_shape_index))[/code]. - [param local_shape_index] the index of the [Shape2D] of this Area2D used by the [PhysicsServer2D]. Get the [CollisionShape2D] node with [code]self.shape_owner_get_owner(self.shape_find_owner(local_shape_index))[/code]. + Emitted when a [Shape2D] of the received [param area] enters a shape of this area. Requires [member monitoring] to be set to [code]true[/code]. + [param local_shape_index] and [param area_shape_index] contain indices of the interacting shapes from this area and the other area, respectively. [param area_rid] contains the [RID] of the other area. These values can be used with the [PhysicsServer2D]. + [b]Example of getting the[/b] [CollisionShape2D] [b]node from the shape index:[/b] + [codeblocks] + [gdscript] + var other_shape_owner = area.shape_find_owner(area_shape_index) + var other_shape_node = area.shape_owner_get_owner(other_shape_owner) + + var local_shape_owner = shape_find_owner(local_shape_index) + var local_shape_node = shape_owner_get_owner(local_shape_owner) + [/gdscript] + [/codeblocks] </description> </signal> <signal name="area_shape_exited"> @@ -144,25 +149,20 @@ <param index="2" name="area_shape_index" type="int" /> <param index="3" name="local_shape_index" type="int" /> <description> - Emitted when one of another Area2D's [Shape2D]s exits one of this Area2D's [Shape2D]s. Requires [member monitoring] to be set to [code]true[/code]. - [param area_rid] the [RID] of the other Area2D's [CollisionObject2D] used by the [PhysicsServer2D]. - [param area] the other Area2D. - [param area_shape_index] the index of the [Shape2D] of the other Area2D used by the [PhysicsServer2D]. Get the [CollisionShape2D] node with [code]area.shape_owner_get_owner(area.shape_find_owner(area_shape_index))[/code]. - [param local_shape_index] the index of the [Shape2D] of this Area2D used by the [PhysicsServer2D]. Get the [CollisionShape2D] node with [code]self.shape_owner_get_owner(self.shape_find_owner(local_shape_index))[/code]. + Emitted when a [Shape2D] of the received [param area] exits a shape of this area. Requires [member monitoring] to be set to [code]true[/code]. + See also [signal area_shape_entered]. </description> </signal> <signal name="body_entered"> <param index="0" name="body" type="Node2D" /> <description> - Emitted when a [PhysicsBody2D] or [TileMap] enters this Area2D. Requires [member monitoring] to be set to [code]true[/code]. [TileMap]s are detected if the [TileSet] has Collision [Shape2D]s. - [param body] the [Node], if it exists in the tree, of the other [PhysicsBody2D] or [TileMap]. + Emitted when the received [param body] enters this area. [param body] can be a [PhysicsBody2D] or a [TileMap]. [TileMap]s are detected if their [TileSet] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code]. </description> </signal> <signal name="body_exited"> <param index="0" name="body" type="Node2D" /> <description> - Emitted when a [PhysicsBody2D] or [TileMap] exits this Area2D. Requires [member monitoring] to be set to [code]true[/code]. [TileMap]s are detected if the [TileSet] has Collision [Shape2D]s. - [param body] the [Node], if it exists in the tree, of the other [PhysicsBody2D] or [TileMap]. + Emitted when the received [param body] exits this area. [param body] can be a [PhysicsBody2D] or a [TileMap]. [TileMap]s are detected if their [TileSet] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code]. </description> </signal> <signal name="body_shape_entered"> @@ -171,11 +171,18 @@ <param index="2" name="body_shape_index" type="int" /> <param index="3" name="local_shape_index" type="int" /> <description> - Emitted when one of a [PhysicsBody2D] or [TileMap]'s [Shape2D]s enters one of this Area2D's [Shape2D]s. Requires [member monitoring] to be set to [code]true[/code]. [TileMap]s are detected if the [TileSet] has Collision [Shape2D]s. - [param body_rid] the [RID] of the [PhysicsBody2D] or [TileSet]'s [CollisionObject2D] used by the [PhysicsServer2D]. - [param body] the [Node], if it exists in the tree, of the [PhysicsBody2D] or [TileMap]. - [param body_shape_index] the index of the [Shape2D] of the [PhysicsBody2D] or [TileMap] used by the [PhysicsServer2D]. Get the [CollisionShape2D] node with [code]body.shape_owner_get_owner(body.shape_find_owner(body_shape_index))[/code]. - [param local_shape_index] the index of the [Shape2D] of this Area2D used by the [PhysicsServer2D]. Get the [CollisionShape2D] node with [code]self.shape_owner_get_owner(self.shape_find_owner(local_shape_index))[/code]. + Emitted when a [Shape2D] of the received [param body] enters a shape of this area. [param body] can be a [PhysicsBody2D] or a [TileMap]. [TileMap]s are detected if their [TileSet] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code]. + [param local_shape_index] and [param body_shape_index] contain indices of the interacting shapes from this area and the interacting body, respectively. [param body_rid] contains the [RID] of the body. These values can be used with the [PhysicsServer2D]. + [b]Example of getting the[/b] [CollisionShape2D] [b]node from the shape index:[/b] + [codeblocks] + [gdscript] + var body_shape_owner = body.shape_find_owner(body_shape_index) + var body_shape_node = body.shape_owner_get_owner(body_shape_owner) + + var local_shape_owner = shape_find_owner(local_shape_index) + var local_shape_node = shape_owner_get_owner(local_shape_owner) + [/gdscript] + [/codeblocks] </description> </signal> <signal name="body_shape_exited"> @@ -184,11 +191,8 @@ <param index="2" name="body_shape_index" type="int" /> <param index="3" name="local_shape_index" type="int" /> <description> - Emitted when one of a [PhysicsBody2D] or [TileMap]'s [Shape2D]s exits one of this Area2D's [Shape2D]s. Requires [member monitoring] to be set to [code]true[/code]. [TileMap]s are detected if the [TileSet] has Collision [Shape2D]s. - [param body_rid] the [RID] of the [PhysicsBody2D] or [TileSet]'s [CollisionObject2D] used by the [PhysicsServer2D]. - [param body] the [Node], if it exists in the tree, of the [PhysicsBody2D] or [TileMap]. - [param body_shape_index] the index of the [Shape2D] of the [PhysicsBody2D] or [TileMap] used by the [PhysicsServer2D]. Get the [CollisionShape2D] node with [code]body.shape_owner_get_owner(body.shape_find_owner(body_shape_index))[/code]. - [param local_shape_index] the index of the [Shape2D] of this Area2D used by the [PhysicsServer2D]. Get the [CollisionShape2D] node with [code]self.shape_owner_get_owner(self.shape_find_owner(local_shape_index))[/code]. + Emitted when a [Shape2D] of the received [param body] exits a shape of this area. [param body] can be a [PhysicsBody2D] or a [TileMap]. [TileMap]s are detected if their [TileSet] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code]. + See also [signal body_shape_entered]. </description> </signal> </signals> diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index ea8cab324d..8923ac8aae 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -133,15 +133,13 @@ <signal name="area_entered"> <param index="0" name="area" type="Area3D" /> <description> - Emitted when another Area3D enters this Area3D. Requires [member monitoring] to be set to [code]true[/code]. - [param area] the other Area3D. + Emitted when the received [param area] enters this area. Requires [member monitoring] to be set to [code]true[/code]. </description> </signal> <signal name="area_exited"> <param index="0" name="area" type="Area3D" /> <description> - Emitted when another Area3D exits this Area3D. Requires [member monitoring] to be set to [code]true[/code]. - [param area] the other Area3D. + Emitted when the received [param area] exits this area. Requires [member monitoring] to be set to [code]true[/code]. </description> </signal> <signal name="area_shape_entered"> @@ -150,11 +148,18 @@ <param index="2" name="area_shape_index" type="int" /> <param index="3" name="local_shape_index" type="int" /> <description> - Emitted when one of another Area3D's [Shape3D]s enters one of this Area3D's [Shape3D]s. Requires [member monitoring] to be set to [code]true[/code]. - [param area_rid] the [RID] of the other Area3D's [CollisionObject3D] used by the [PhysicsServer3D]. - [param area] the other Area3D. - [param area_shape_index] the index of the [Shape3D] of the other Area3D used by the [PhysicsServer3D]. Get the [CollisionShape3D] node with [code]area.shape_owner_get_owner(area.shape_find_owner(area_shape_index))[/code]. - [param local_shape_index] the index of the [Shape3D] of this Area3D used by the [PhysicsServer3D]. Get the [CollisionShape3D] node with [code]self.shape_owner_get_owner(self.shape_find_owner(local_shape_index))[/code]. + Emitted when a [Shape3D] of the received [param area] enters a shape of this area. Requires [member monitoring] to be set to [code]true[/code]. + [param local_shape_index] and [param area_shape_index] contain indices of the interacting shapes from this area and the other area, respectively. [param area_rid] contains the [RID] of the other area. These values can be used with the [PhysicsServer3D]. + [b]Example of getting the[/b] [CollisionShape3D] [b]node from the shape index:[/b] + [codeblocks] + [gdscript] + var other_shape_owner = area.shape_find_owner(area_shape_index) + var other_shape_node = area.shape_owner_get_owner(other_shape_owner) + + var local_shape_owner = shape_find_owner(local_shape_index) + var local_shape_node = shape_owner_get_owner(local_shape_owner) + [/gdscript] + [/codeblocks] </description> </signal> <signal name="area_shape_exited"> @@ -163,25 +168,20 @@ <param index="2" name="area_shape_index" type="int" /> <param index="3" name="local_shape_index" type="int" /> <description> - Emitted when one of another Area3D's [Shape3D]s exits one of this Area3D's [Shape3D]s. Requires [member monitoring] to be set to [code]true[/code]. - [param area_rid] the [RID] of the other Area3D's [CollisionObject3D] used by the [PhysicsServer3D]. - [param area] the other Area3D. - [param area_shape_index] the index of the [Shape3D] of the other Area3D used by the [PhysicsServer3D]. Get the [CollisionShape3D] node with [code]area.shape_owner_get_owner(area.shape_find_owner(area_shape_index))[/code]. - [param local_shape_index] the index of the [Shape3D] of this Area3D used by the [PhysicsServer3D]. Get the [CollisionShape3D] node with [code]self.shape_owner_get_owner(self.shape_find_owner(local_shape_index))[/code]. + Emitted when a [Shape3D] of the received [param area] exits a shape of this area. Requires [member monitoring] to be set to [code]true[/code]. + See also [signal area_shape_entered]. </description> </signal> <signal name="body_entered"> <param index="0" name="body" type="Node3D" /> <description> - Emitted when a [PhysicsBody3D] or [GridMap] enters this Area3D. Requires [member monitoring] to be set to [code]true[/code]. [GridMap]s are detected if the [MeshLibrary] has Collision [Shape3D]s. - [param body] the [Node], if it exists in the tree, of the other [PhysicsBody3D] or [GridMap]. + Emitted when the received [param body] enters this area. [param body] can be a [PhysicsBody3D] or a [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code]. </description> </signal> <signal name="body_exited"> <param index="0" name="body" type="Node3D" /> <description> - Emitted when a [PhysicsBody3D] or [GridMap] exits this Area3D. Requires [member monitoring] to be set to [code]true[/code]. [GridMap]s are detected if the [MeshLibrary] has Collision [Shape3D]s. - [param body] the [Node], if it exists in the tree, of the other [PhysicsBody3D] or [GridMap]. + Emitted when the received [param body] exits this area. [param body] can be a [PhysicsBody3D] or a [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code]. </description> </signal> <signal name="body_shape_entered"> @@ -190,11 +190,18 @@ <param index="2" name="body_shape_index" type="int" /> <param index="3" name="local_shape_index" type="int" /> <description> - Emitted when one of a [PhysicsBody3D] or [GridMap]'s [Shape3D]s enters one of this Area3D's [Shape3D]s. Requires [member monitoring] to be set to [code]true[/code]. [GridMap]s are detected if the [MeshLibrary] has Collision [Shape3D]s. - [param body_rid] the [RID] of the [PhysicsBody3D] or [MeshLibrary]'s [CollisionObject3D] used by the [PhysicsServer3D]. - [param body] the [Node], if it exists in the tree, of the [PhysicsBody3D] or [GridMap]. - [param body_shape_index] the index of the [Shape3D] of the [PhysicsBody3D] or [GridMap] used by the [PhysicsServer3D]. Get the [CollisionShape3D] node with [code]body.shape_owner_get_owner(body.shape_find_owner(body_shape_index))[/code]. - [param local_shape_index] the index of the [Shape3D] of this Area3D used by the [PhysicsServer3D]. Get the [CollisionShape3D] node with [code]self.shape_owner_get_owner(self.shape_find_owner(local_shape_index))[/code]. + Emitted when a [Shape3D] of the received [param body] enters a shape of this area. [param body] can be a [PhysicsBody3D] or a [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code]. + [param local_shape_index] and [param body_shape_index] contain indices of the interacting shapes from this area and the interacting body, respectively. [param body_rid] contains the [RID] of the body. These values can be used with the [PhysicsServer3D]. + [b]Example of getting the[/b] [CollisionShape3D] [b]node from the shape index:[/b] + [codeblocks] + [gdscript] + var body_shape_owner = body.shape_find_owner(body_shape_index) + var body_shape_node = body.shape_owner_get_owner(body_shape_owner) + + var local_shape_owner = shape_find_owner(local_shape_index) + var local_shape_node = shape_owner_get_owner(local_shape_owner) + [/gdscript] + [/codeblocks] </description> </signal> <signal name="body_shape_exited"> @@ -203,11 +210,8 @@ <param index="2" name="body_shape_index" type="int" /> <param index="3" name="local_shape_index" type="int" /> <description> - Emitted when one of a [PhysicsBody3D] or [GridMap]'s [Shape3D]s enters one of this Area3D's [Shape3D]s. Requires [member monitoring] to be set to [code]true[/code]. [GridMap]s are detected if the [MeshLibrary] has Collision [Shape3D]s. - [param body_rid] the [RID] of the [PhysicsBody3D] or [MeshLibrary]'s [CollisionObject3D] used by the [PhysicsServer3D]. - [param body] the [Node], if it exists in the tree, of the [PhysicsBody3D] or [GridMap]. - [param body_shape_index] the index of the [Shape3D] of the [PhysicsBody3D] or [GridMap] used by the [PhysicsServer3D]. Get the [CollisionShape3D] node with [code]body.shape_owner_get_owner(body.shape_find_owner(body_shape_index))[/code]. - [param local_shape_index] the index of the [Shape3D] of this Area3D used by the [PhysicsServer3D]. Get the [CollisionShape3D] node with [code]self.shape_owner_get_owner(self.shape_find_owner(local_shape_index))[/code]. + Emitted when a [Shape3D] of the received [param body] exits a shape of this area. [param body] can be a [PhysicsBody3D] or a [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code]. + See also [signal body_shape_entered]. </description> </signal> </signals> diff --git a/doc/classes/AudioStreamRandomizer.xml b/doc/classes/AudioStreamRandomizer.xml index 9b58d78af5..d93f853c89 100644 --- a/doc/classes/AudioStreamRandomizer.xml +++ b/doc/classes/AudioStreamRandomizer.xml @@ -12,8 +12,10 @@ <method name="add_stream"> <return type="void" /> <param index="0" name="index" type="int" /> + <param index="1" name="stream" type="AudioStream" /> + <param index="2" name="weight" type="float" default="1.0" /> <description> - Insert a stream at the specified index. + Insert a stream at the specified index. If the index is less than zero, the insertion occurs at the end of the underlying pool. </description> </method> <method name="get_stream" qualifiers="const"> diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index d2c9a77486..961f3c495a 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -118,6 +118,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> [Font] of the [Button]'s text. diff --git a/doc/classes/ButtonGroup.xml b/doc/classes/ButtonGroup.xml index 277bda2836..8195669b10 100644 --- a/doc/classes/ButtonGroup.xml +++ b/doc/classes/ButtonGroup.xml @@ -4,8 +4,8 @@ Group of Buttons. </brief_description> <description> - Group of [Button]. All direct and indirect children buttons become radios. Only one allows being pressed. - [member BaseButton.toggle_mode] should be [code]true[/code]. + Group of [BaseButton]. The members of this group are treated like radio buttons in the sense that only one button can be pressed at the same time. + Every member of the ButtonGroup should have [member BaseButton.toggle_mode] set to [code]true[/code]. </description> <tutorials> </tutorials> diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 499d53e63c..5171e0acbe 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -153,6 +153,7 @@ <param index="3" name="modulate" type="Color" default="Color(1, 1, 1, 1)" /> <param index="4" name="outline" type="float" default="0.0" /> <param index="5" name="pixel_range" type="float" default="4.0" /> + <param index="6" name="scale" type="float" default="1.0" /> <description> Draws a textured rectangle region of the multi-channel signed distance field texture at a given position, optionally modulated by a color. See [member FontFile.multichannel_signed_distance_field] for more information and caveats about MSDF font rendering. If [param outline] is positive, each alpha channel value of pixel in region is set to maximum value of true distance in the [param outline] radius. @@ -404,7 +405,7 @@ <method name="get_canvas_transform" qualifiers="const"> <return type="Transform2D" /> <description> - Returns the transform matrix of this item's canvas. + Returns the transform from the coordinate system of the canvas, this item is in, to the [Viewport]s coordinate system. </description> </method> <method name="get_global_mouse_position" qualifiers="const"> @@ -422,7 +423,7 @@ <method name="get_global_transform_with_canvas" qualifiers="const"> <return type="Transform2D" /> <description> - Returns the global transform matrix of this item in relation to the canvas. + Returns the transform from the local coordinate system of this [CanvasItem] to the [Viewport]s coordinate system. </description> </method> <method name="get_local_mouse_position" qualifiers="const"> @@ -453,7 +454,7 @@ <method name="get_viewport_transform" qualifiers="const"> <return type="Transform2D" /> <description> - Returns this item's transform in relation to the viewport. + Returns the transform from the coordinate system of the canvas, this item is in, to the [Viewport]s embedders coordinate system. </description> </method> <method name="get_visibility_layer_bit" qualifiers="const"> diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml index 7c1b19b961..ce999c06d3 100644 --- a/doc/classes/CanvasLayer.xml +++ b/doc/classes/CanvasLayer.xml @@ -19,6 +19,12 @@ Returns the RID of the canvas used by this layer. </description> </method> + <method name="get_final_transform" qualifiers="const"> + <return type="Transform2D" /> + <description> + Returns the transform from the [CanvasLayer]s coordinate system to the [Viewport]s coordinate system. + </description> + </method> <method name="hide"> <return type="void" /> <description> diff --git a/doc/classes/CheckBox.xml b/doc/classes/CheckBox.xml index 699e872e99..3ee18cf351 100644 --- a/doc/classes/CheckBox.xml +++ b/doc/classes/CheckBox.xml @@ -43,6 +43,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> The [Font] to use for the [CheckBox] text. diff --git a/doc/classes/CheckButton.xml b/doc/classes/CheckButton.xml index dfbaa7cbee..03578cafd0 100644 --- a/doc/classes/CheckButton.xml +++ b/doc/classes/CheckButton.xml @@ -43,6 +43,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> The [Font] to use for the [CheckButton] text. diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml index e7ef5fb289..c313e6c875 100644 --- a/doc/classes/CodeEdit.xml +++ b/doc/classes/CodeEdit.xml @@ -651,6 +651,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> Sets the default [Font]. diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index 4d78433915..d1387d088d 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -205,11 +205,18 @@ <description> Returns the [Color] associated with the provided [param hex] integer in 32-bit ARGB format (8 bits per channel, alpha channel first). In GDScript and C#, the [int] is best visualized with hexadecimal notation ([code]"0x"[/code] prefix). - [codeblock] + [codeblocks] + [gdscript] var red = Color.hex(0xffff0000) var dark_cyan = Color.hex(0xff008b8b) var my_color = Color.hex(0xa4bbefd2) - [/codeblock] + [/gdscript] + [csharp] + var red = new Color(0xffff0000); + var dark_cyan = new Color(0xff008b8b); + var my_color = new Color(0xa4bbefd2); + [/csharp] + [/codeblocks] </description> </method> <method name="hex64" qualifiers="static"> @@ -234,9 +241,9 @@ var col = Color.html("663399cc") # col is Color(0.4, 0.2, 0.6, 0.8) [/gdscript] [csharp] - var blue = new Color("#0000ff"); // blue is Color(0.0, 0.0, 1.0, 1.0) - var green = new Color("#0F0"); // green is Color(0.0, 1.0, 0.0, 1.0) - var col = new Color("663399cc"); // col is Color(0.4, 0.2, 0.6, 0.8) + var blue = Color.FromHtml("#0000ff"); // blue is Color(0.0, 0.0, 1.0, 1.0) + var green = Color.FromHtml("#0F0"); // green is Color(0.0, 1.0, 0.0, 1.0) + var col = Color.FromHtml("663399cc"); // col is Color(0.4, 0.2, 0.6, 0.8) [/csharp] [/codeblocks] </description> @@ -257,14 +264,13 @@ Color.html_is_valid("#55aaFF5") # Returns false [/gdscript] [csharp] - // This method is not available in C#. Use `StringExtensions.IsValidHtmlColor()`, instead. - "#55AAFF".IsValidHtmlColor(); // Returns true - "#55AAFF20".IsValidHtmlColor(); // Returns true - "55AAFF".IsValidHtmlColor(); // Returns true - "#F2C".IsValidHtmlColor(); // Returns true + Color.IsHtmlValid("#55AAFF"); // Returns true + Color.IsHtmlValid("#55AAFF20"); // Returns true + Color.IsHtmlValid("55AAFF"); // Returns true + Color.IsHtmlValid("#F2C"); // Returns true - "#AABBC".IsValidHtmlColor(); // Returns false - "#55aaFF5".IsValidHtmlColor(); // Returns false + Color.IsHtmlValid("#AABBC"); // Returns false + Color.IsHtmlValid("#55aaFF5"); // Returns false [/csharp] [/codeblocks] </description> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index 2b287d7546..823433c2df 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ColorPicker" inherits="BoxContainer" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="ColorPicker" inherits="VBoxContainer" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Color picker control. </brief_description> @@ -88,7 +88,6 @@ <member name="sliders_visible" type="bool" setter="set_sliders_visible" getter="are_sliders_visible" default="true"> If [code]true[/code], the color sliders are visible. </member> - <member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" overrides="BoxContainer" default="true" /> </members> <signals> <signal name="color_changed"> diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index b7a0bdfb0c..2a37cc162a 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -79,6 +79,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> [Font] of the [ColorPickerButton]'s text. diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index fd6aab6a49..b0351559ee 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -1050,6 +1050,9 @@ <member name="rotation" type="float" setter="set_rotation" getter="get_rotation" default="0.0"> The node's rotation around its pivot, in radians. See [member pivot_offset] to change the pivot's position. </member> + <member name="rotation_degrees" type="float" setter="set_rotation_degrees" getter="get_rotation_degrees"> + Helper property to access [member rotation] in degrees instead of radians. + </member> <member name="scale" type="Vector2" setter="set_scale" getter="get_scale" default="Vector2(1, 1)"> The node's scale, relative to its [member size]. Change this property to scale the node around its [member pivot_offset]. The Control's [member tooltip_text] will also scale according to this value. [b]Note:[/b] This property is mainly intended to be used for animation purposes. Text inside the Control will look pixelated or blurry when the Control is scaled. To support multiple resolutions in your project, use an appropriate viewport stretch mode as described in the [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]documentation[/url] instead of scaling Controls individually. diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml index cfe2036499..362d792b39 100644 --- a/doc/classes/Curve3D.xml +++ b/doc/classes/Curve3D.xml @@ -138,7 +138,7 @@ <param index="1" name="cubic" type="bool" default="false" /> <param index="2" name="apply_tilt" type="bool" default="false" /> <description> - Similar with [code]interpolate_baked()[/code]. The the return value is [code]Transform3D[/code], with [code]origin[/code] as point position, [code]basis.x[/code] as sideway vector, [code]basis.y[/code] as up vector, [code]basis.z[/code] as forward vector. When the curve length is 0, there is no reasonable way to caculate the rotation, all vectors aligned with global space axes. + Similar with [code]interpolate_baked()[/code]. The the return value is [code]Transform3D[/code], with [code]origin[/code] as point position, [code]basis.x[/code] as sideway vector, [code]basis.y[/code] as up vector, [code]basis.z[/code] as forward vector. When the curve length is 0, there is no reasonable way to calculate the rotation, all vectors aligned with global space axes. </description> </method> <method name="samplef" qualifiers="const"> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 92225b816f..5f99ba82b8 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -135,7 +135,7 @@ [/csharp] [/codeblocks] [b]Note:[/b] Erasing elements while iterating over dictionaries is [b]not[/b] supported and will result in unpredictable behavior. - [b]Note:[/b] When declaring a dictionary with [code]const[/code], the dictionary becomes read-only. A read-only Dictionary's entries cannot be overriden at run-time. This does [i]not[/i] affect nested [Array] and [Dictionary] values. + [b]Note:[/b] When declaring a dictionary with [code]const[/code], the dictionary becomes read-only. A read-only Dictionary's entries cannot be overridden at run-time. This does [i]not[/i] affect nested [Array] and [Dictionary] values. </description> <tutorials> <link title="GDScript basics: Dictionary">$DOCS_URL/tutorials/scripting/gdscript/gdscript_basics.html#dictionary</link> diff --git a/doc/classes/EditorSpinSlider.xml b/doc/classes/EditorSpinSlider.xml index de105b32e1..d270d32df7 100644 --- a/doc/classes/EditorSpinSlider.xml +++ b/doc/classes/EditorSpinSlider.xml @@ -28,4 +28,26 @@ The suffix to display after the value (in a faded color). This should generally be a plural word. You may have to use an abbreviation if the suffix is too long to be displayed. </member> </members> + <signals> + <signal name="grabbed"> + <description> + Emitted when the spinner/slider is grabbed. + </description> + </signal> + <signal name="ungrabbed"> + <description> + Emitted when the spinner/slider is ungrabbed. + </description> + </signal> + <signal name="value_focus_entered"> + <description> + Emitted when the value form gains focus. + </description> + </signal> + <signal name="value_focus_exited"> + <description> + Emitted when the value form loses focus. + </description> + </signal> + </signals> </class> diff --git a/doc/classes/EditorVCSInterface.xml b/doc/classes/EditorVCSInterface.xml index b766978c04..85c10fefd9 100644 --- a/doc/classes/EditorVCSInterface.xml +++ b/doc/classes/EditorVCSInterface.xml @@ -53,7 +53,7 @@ </description> </method> <method name="_get_branch_list" qualifiers="virtual"> - <return type="Dictionary[]" /> + <return type="String[]" /> <description> Gets an instance of an [Array] of [String]s containing available branch names in the VCS. </description> @@ -94,7 +94,7 @@ </description> </method> <method name="_get_remotes" qualifiers="virtual"> - <return type="Dictionary[]" /> + <return type="String[]" /> <description> Returns an [Array] of [String]s, each containing the name of a remote configured in the VCS. </description> diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml index 1019c271dc..69a7627774 100644 --- a/doc/classes/FontFile.xml +++ b/doc/classes/FontFile.xml @@ -570,7 +570,7 @@ Weight (boldness) of the font. A value in the [code]100...999[/code] range, normal font weight is [code]400[/code], bold font weight is [code]700[/code]. </member> <member name="force_autohinter" type="bool" setter="set_force_autohinter" getter="is_force_autohinter" default="false"> - If set to [code]true[/code], auto-hinting is supported and preferred over font built-in hinting. Used by dynamic fonts only. + If set to [code]true[/code], auto-hinting is supported and preferred over font built-in hinting. Used by dynamic fonts only (MSDF fonts don't support hinting). </member> <member name="generate_mipmaps" type="bool" setter="set_generate_mipmaps" getter="get_generate_mipmaps" default="false"> If set to [code]true[/code], generate mipmaps for the font textures. @@ -579,25 +579,27 @@ Font hinting mode. Used by dynamic fonts only. </member> <member name="msdf_pixel_range" type="int" setter="set_msdf_pixel_range" getter="get_msdf_pixel_range" default="16"> - The width of the range around the shape between the minimum and maximum representable signed distance. + The width of the range around the shape between the minimum and maximum representable signed distance. If using font outlines, [member msdf_pixel_range] must be set to at least [i]twice[/i] the size of the largest font outline. The default [member msdf_pixel_range] value of [code]16[/code] allows outline sizes up to [code]8[/code] to look correct. </member> <member name="msdf_size" type="int" setter="set_msdf_size" getter="get_msdf_size" default="48"> - Source font size used to generate MSDF textures. + Source font size used to generate MSDF textures. Higher values allow for more precision, but are slower to render and require more memory. Only increase this value if you notice a visible lack of precision in glyph rendering. </member> <member name="multichannel_signed_distance_field" type="bool" setter="set_multichannel_signed_distance_field" getter="is_multichannel_signed_distance_field" default="false"> - If set to [code]true[/code], glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data. + If set to [code]true[/code], glyphs of all sizes are rendered using single multichannel signed distance field (MSDF) generated from the dynamic font vector data. Since this approach does not rely on rasterizing the font every time its size changes, this allows for resizing the font in real-time without any performance penalty. Text will also not look grainy for [Control]s that are scaled down (or for [Label3D]s viewed from a long distance). As a downside, font hinting is not available with MSDF. The lack of font hinting may result in less crisp and less readable fonts at small sizes. + [b]Note:[/b] If using font outlines, [member msdf_pixel_range] must be set to at least [i]twice[/i] the size of the largest font outline. + [b]Note:[/b] MSDF font rendering does not render glyphs with overlapping shapes correctly. Overlapping shapes are not valid per the OpenType standard, but are still commonly found in many font files, especially those converted by Google Fonts. To avoid issues with overlapping glyphs, consider downloading the font file directly from the type foundry instead of relying on Google Fonts. </member> <member name="opentype_feature_overrides" type="Dictionary" setter="set_opentype_feature_overrides" getter="get_opentype_feature_overrides" default="{}"> Font OpenType feature set override. </member> <member name="oversampling" type="float" setter="set_oversampling" getter="get_oversampling" default="0.0"> - Font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only. + Font oversampling factor. If set to [code]0.0[/code], the global oversampling factor is used instead. Used by dynamic fonts only (MSDF fonts ignore oversampling). </member> <member name="style_name" type="String" setter="set_font_style_name" getter="get_font_style_name" default=""""> Font style name. </member> <member name="subpixel_positioning" type="int" setter="set_subpixel_positioning" getter="get_subpixel_positioning" enum="TextServer.SubpixelPositioning" default="1"> - Font glyph subpixel positioning mode. Subpixel positioning provides shaper text and better kerning for smaller font sizes, at the cost of memory usage and font rasterization speed. Use [constant TextServer.SUBPIXEL_POSITIONING_AUTO] to automatically enable it based on the font size. + Font glyph subpixel positioning mode. Subpixel positioning provides shaper text and better kerning for smaller font sizes, at the cost of higher memory usage and lower font rasterization speed. Use [constant TextServer.SUBPIXEL_POSITIONING_AUTO] to automatically enable it based on the font size. </member> </members> </class> diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml index 90a983d28b..1ae4718536 100644 --- a/doc/classes/GeometryInstance3D.xml +++ b/doc/classes/GeometryInstance3D.xml @@ -16,13 +16,6 @@ Get the value of a shader parameter as set on this instance. </description> </method> - <method name="set_custom_aabb"> - <return type="void" /> - <param index="0" name="aabb" type="AABB" /> - <description> - Overrides the bounding box of this node with a custom one. To remove it, set an [AABB] with all fields set to zero. - </description> - </method> <method name="set_instance_shader_parameter"> <return type="void" /> <param index="0" name="name" type="StringName" /> @@ -36,6 +29,9 @@ <member name="cast_shadow" type="int" setter="set_cast_shadows_setting" getter="get_cast_shadows_setting" enum="GeometryInstance3D.ShadowCastingSetting" default="1"> The selected shadow casting flag. See [enum ShadowCastingSetting] for possible values. </member> + <member name="custom_aabb" type="AABB" setter="set_custom_aabb" getter="get_custom_aabb" default="AABB(0, 0, 0, 0, 0, 0)"> + Overrides the bounding box of this node with a custom one. This can be used to avoid the expensive [AABB] recalculation that happens when a skeleton is used with a [MeshInstance3D] or to have fine control over the [MeshInstance3D]'s bounding box. To remove this, set value to an [AABB] with all fields set to zero. + </member> <member name="extra_cull_margin" type="float" setter="set_extra_cull_margin" getter="get_extra_cull_margin" default="0.0"> The extra distance added to the GeometryInstance3D's bounding box ([AABB]) to increase its cull box. </member> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index be66b8a7b9..ed1b40488e 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -76,8 +76,12 @@ <param index="0" name="mode" type="int" enum="Image.CompressMode" /> <param index="1" name="source" type="int" enum="Image.CompressSource" default="0" /> <param index="2" name="lossy_quality" type="float" default="0.7" /> + <param index="3" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" /> <description> - Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available. See [enum CompressMode] and [enum CompressSource] constants. + Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available. + The [param mode] parameter helps to pick the best compression method for DXT and ETC2 formats. It is ignored for ASTC compression. + The [param lossy_quality] parameter is optional for compressors that support it. + For ASTC compression, the [param astc_format] parameter must be supplied. </description> </method> <method name="compress_from_channels"> @@ -85,7 +89,12 @@ <param index="0" name="mode" type="int" enum="Image.CompressMode" /> <param index="1" name="channels" type="int" enum="Image.UsedChannels" /> <param index="2" name="lossy_quality" type="float" default="0.7" /> + <param index="3" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" /> <description> + Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available. + This is an alternative to [method compress] that lets the user supply the channels used in order for the compressor to pick the best DXT and ETC2 formats. For other formats (non DXT or ETC2), this argument is ignored. + The [param lossy_quality] parameter is optional for compressors that support it. + For ASTC compression, the [param astc_format] parameter must be supplied. </description> </method> <method name="compute_image_metrics"> @@ -658,7 +667,19 @@ </constant> <constant name="FORMAT_DXT5_RA_AS_RG" value="34" enum="Format"> </constant> - <constant name="FORMAT_MAX" value="35" enum="Format"> + <constant name="FORMAT_ASTC_4x4" value="35" enum="Format"> + [url=https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression]Adaptive Scalable Texutre Compression[/url]. This implements the 4x4 (high quality) mode. + </constant> + <constant name="FORMAT_ASTC_4x4_HDR" value="36" enum="Format"> + Same format as [constant FORMAT_ASTC_4x4], but with the hint to let the GPU know it is used for HDR. + </constant> + <constant name="FORMAT_ASTC_8x8" value="37" enum="Format"> + [url=https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression]Adaptive Scalable Texutre Compression[/url]. This implements the 8x8 (low quality) mode. + </constant> + <constant name="FORMAT_ASTC_8x8_HDR" value="38" enum="Format"> + Same format as [constant FORMAT_ASTC_8x8], but with the hint to let the GPU know it is used for HDR. + </constant> + <constant name="FORMAT_MAX" value="39" enum="Format"> Represents the size of the [enum Format] enum. </constant> <constant name="INTERPOLATE_NEAREST" value="0" enum="Interpolation"> @@ -722,5 +743,11 @@ <constant name="COMPRESS_SOURCE_NORMAL" value="2" enum="CompressSource"> Source texture (before compression) is a normal texture (e.g. it can be compressed into two channels). </constant> + <constant name="ASTC_FORMAT_4x4" value="0" enum="ASTCFormat"> + Hint to indicate that the high quality 4x4 ASTC compression format should be used. + </constant> + <constant name="ASTC_FORMAT_8x8" value="1" enum="ASTCFormat"> + Hint to indicate that the low quality 8x8 ASTC compression format should be used. + </constant> </constants> </class> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index d246e64251..be8c8ff83f 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -339,7 +339,7 @@ <param index="2" name="strong_magnitude" type="float" /> <param index="3" name="duration" type="float" default="0" /> <description> - Starts to vibrate the joypad. Joypads usually come with two rumble motors, a strong and a weak one. [param weak_magnitude] is the strength of the weak motor (between 0 and 1) and [param strong_magnitude] is the strength of the strong motor (between 0 and 1). [param duration] is the duration of the effect in seconds (a duration of 0 will try to play the vibration indefinitely). + Starts to vibrate the joypad. Joypads usually come with two rumble motors, a strong and a weak one. [param weak_magnitude] is the strength of the weak motor (between 0 and 1) and [param strong_magnitude] is the strength of the strong motor (between 0 and 1). [param duration] is the duration of the effect in seconds (a duration of 0 will try to play the vibration indefinitely). The vibration can be stopped early by calling [method stop_joy_vibration]. [b]Note:[/b] Not every hardware is compatible with long effect durations; it is recommended to restart an effect if it has to be played for more than a few seconds. </description> </method> @@ -347,18 +347,18 @@ <return type="void" /> <param index="0" name="device" type="int" /> <description> - Stops the vibration of the joypad. + Stops the vibration of the joypad started with [method start_joy_vibration]. </description> </method> <method name="vibrate_handheld"> <return type="void" /> <param index="0" name="duration_ms" type="int" default="500" /> <description> - Vibrate handheld devices. - [b]Note:[/b] This method is implemented on Android, iOS, and Web. - [b]Note:[/b] For Android, it requires enabling the [code]VIBRATE[/code] permission in the export preset. - [b]Note:[/b] For iOS, specifying the duration is supported in iOS 13 and later. - [b]Note:[/b] Some web browsers such as Safari and Firefox for Android do not support this method. + Vibrate the handheld device for the specified duration in milliseconds. + [b]Note:[/b] This method is implemented on Android, iOS, and Web. It has no effect on other platforms. + [b]Note:[/b] For Android, [method vibrate_handheld] requires enabling the [code]VIBRATE[/code] permission in the export preset. Otherwise, [method vibrate_handheld] will have no effect. + [b]Note:[/b] For iOS, specifying the duration is only supported in iOS 13 and later. + [b]Note:[/b] Some web browsers such as Safari and Firefox for Android do not support [method vibrate_handheld]. </description> </method> <method name="warp_mouse"> diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index 844f260971..c485d26e0c 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -459,6 +459,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the item text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="v_separation" data_type="constant" type="int" default="2"> The vertical spacing between items. diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml index cd87b4558f..1a8cbf0584 100644 --- a/doc/classes/Label.xml +++ b/doc/classes/Label.xml @@ -111,6 +111,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> Text outline size. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="shadow_offset_x" data_type="constant" type="int" default="1"> The horizontal offset of the text's shadow. diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index e57cc68b36..b8383aaed9 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -424,6 +424,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> Font used for the text. diff --git a/doc/classes/LinkButton.xml b/doc/classes/LinkButton.xml index d7701ea184..b3938999f3 100644 --- a/doc/classes/LinkButton.xml +++ b/doc/classes/LinkButton.xml @@ -28,7 +28,23 @@ Base text writing direction. </member> <member name="underline" type="int" setter="set_underline_mode" getter="get_underline_mode" enum="LinkButton.UnderlineMode" default="0"> - Determines when to show the underline. See [enum UnderlineMode] for options. + The underline mode to use for the text. See [enum LinkButton.UnderlineMode] for the available modes. + </member> + <member name="uri" type="String" setter="set_uri" getter="get_uri" default=""""> + The [url=https://en.wikipedia.org/wiki/Uniform_Resource_Identifier]URI[/url] for this [LinkButton]. If set to a valid URI, pressing the button opens the URI using the operating system's default program for the protocol (via [method OS.shell_open]). HTTP and HTTPS URLs open the default web browser. + [b]Examples:[/b] + [codeblocks] + [gdscript] + uri = "https://godotengine.org" # Opens the URL in the default web browser. + uri = "C:\SomeFolder" # Opens the file explorer at the given path. + uri = "C:\SomeImage.png" # Opens the given image in the default viewing app. + [/gdscript] + [csharp] + Uri = "https://godotengine.org"; // Opens the URL in the default web browser. + Uri = "C:\SomeFolder"; // Opens the file explorer at the given path. + Uri = "C:\SomeImage.png"; // Opens the given image in the default viewing app. + [/csharp] + [/codeblocks] </member> </members> <constants> @@ -60,6 +76,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="underline_spacing" data_type="constant" type="int" default="2"> The vertical space between the baseline of text and the underline. diff --git a/doc/classes/MenuBar.xml b/doc/classes/MenuBar.xml index e8505937ff..79876e56a3 100644 --- a/doc/classes/MenuBar.xml +++ b/doc/classes/MenuBar.xml @@ -143,6 +143,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> [Font] of the menu item's text. diff --git a/doc/classes/MenuButton.xml b/doc/classes/MenuButton.xml index 4d5d5a011b..3061149570 100644 --- a/doc/classes/MenuButton.xml +++ b/doc/classes/MenuButton.xml @@ -75,6 +75,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> [Font] of the [MenuButton]'s text. diff --git a/doc/classes/MeshLibrary.xml b/doc/classes/MeshLibrary.xml index 85e57dd0f3..7c93ba5dce 100644 --- a/doc/classes/MeshLibrary.xml +++ b/doc/classes/MeshLibrary.xml @@ -59,14 +59,21 @@ Returns the item's name. </description> </method> - <method name="get_item_navmesh" qualifiers="const"> + <method name="get_item_navigation_layers" qualifiers="const"> + <return type="int" /> + <param index="0" name="id" type="int" /> + <description> + Returns the item's navigation layers bitmask. + </description> + </method> + <method name="get_item_navigation_mesh" qualifiers="const"> <return type="NavigationMesh" /> <param index="0" name="id" type="int" /> <description> Returns the item's navigation mesh. </description> </method> - <method name="get_item_navmesh_transform" qualifiers="const"> + <method name="get_item_navigation_mesh_transform" qualifiers="const"> <return type="Transform3D" /> <param index="0" name="id" type="int" /> <description> @@ -126,18 +133,26 @@ This name is shown in the editor. It can also be used to look up the item later using [method find_item_by_name]. </description> </method> - <method name="set_item_navmesh"> + <method name="set_item_navigation_layers"> + <return type="void" /> + <param index="0" name="id" type="int" /> + <param index="1" name="navigation_layers" type="int" /> + <description> + Sets the item's navigation layers bitmask. + </description> + </method> + <method name="set_item_navigation_mesh"> <return type="void" /> <param index="0" name="id" type="int" /> - <param index="1" name="navmesh" type="NavigationMesh" /> + <param index="1" name="navigation_mesh" type="NavigationMesh" /> <description> Sets the item's navigation mesh. </description> </method> - <method name="set_item_navmesh_transform"> + <method name="set_item_navigation_mesh_transform"> <return type="void" /> <param index="0" name="id" type="int" /> - <param index="1" name="navmesh" type="Transform3D" /> + <param index="1" name="navigation_mesh" type="Transform3D" /> <description> Sets the transform to apply to the item's navigation mesh. </description> diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml index 8cf7824ff7..31d347d76c 100644 --- a/doc/classes/NavigationAgent2D.xml +++ b/doc/classes/NavigationAgent2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationAgent2D" inherits="Node" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationAgent2D" inherits="Node" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> 2D Agent used in navigation for collision avoidance. </brief_description> @@ -16,24 +16,30 @@ Returns the distance to the target location, using the agent's global position. The user must set [member target_location] in order for this to be accurate. </description> </method> - <method name="get_final_location"> - <return type="Vector2" /> - <description> - Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. - </description> - </method> - <method name="get_nav_path" qualifiers="const"> + <method name="get_current_navigation_path" qualifiers="const"> <return type="PackedVector2Array" /> <description> Returns this agent's current path from start to finish in global coordinates. The path only updates when the target location is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_location] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic. </description> </method> - <method name="get_nav_path_index" qualifiers="const"> + <method name="get_current_navigation_path_index" qualifiers="const"> <return type="int" /> <description> Returns which index the agent is currently on in the navigation path's [PackedVector2Array]. </description> </method> + <method name="get_current_navigation_result" qualifiers="const"> + <return type="NavigationPathQueryResult2D" /> + <description> + Returns the path query result for the path the agent is currently following. + </description> + </method> + <method name="get_final_location"> + <return type="Vector2" /> + <description> + Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. + </description> + </method> <method name="get_navigation_layer_value" qualifiers="const"> <return type="bool" /> <param index="0" name="layer_number" type="int" /> @@ -122,6 +128,9 @@ <member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="3.0"> The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. </member> + <member name="path_metadata_flags" type="int" setter="set_path_metadata_flags" getter="get_path_metadata_flags" enum="NavigationPathQueryParameters2D.PathMetadataFlags" default="7"> + Additional information to return with the navigation path. + </member> <member name="radius" type="float" setter="set_radius" getter="get_radius" default="10.0"> The radius of the avoidance agent. This is the "body" of the avoidance agent and not the avoidance maneuver starting radius (which is controlled by [member neighbor_distance]). Does not affect normal pathfinding. To change an actor's pathfinding radius bake [NavigationMesh] resources with a different [member NavigationMesh.agent_radius] property and use different navigation maps for each actor size. @@ -137,6 +146,17 @@ </member> </members> <signals> + <signal name="link_reached"> + <param index="0" name="details" type="Dictionary" /> + <description> + Notifies when a navigation link has been reached. + The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: + - [code]location[/code]: The start location of the link that was reached. + - [code]type[/code]: Always [constant NavigationPathQueryResult2D.PATH_SEGMENT_TYPE_LINK]. + - [code]rid[/code]: The [RID] of the link. + - [code]owner[/code]: The object which manages the link (usually [NavigationLink2D]). + </description> + </signal> <signal name="navigation_finished"> <description> Notifies when the final location is reached. @@ -158,5 +178,16 @@ Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity]. Only emitted when [member avoidance_enabled] is true. </description> </signal> + <signal name="waypoint_reached"> + <param index="0" name="details" type="Dictionary" /> + <description> + Notifies when a waypoint along the path has been reached. + The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: + - [code]location[/code]: The location of the waypoint that was reached. + - [code]type[/code]: The type of navigation primitive (region or link) that contains this waypoint. + - [code]rid[/code]: The [RID] of the containing navigation primitive (region or link). + - [code]owner[/code]: The object which manages the containing navigation primitive (region or link). + </description> + </signal> </signals> </class> diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml index 44c17647f7..c3f4809b5e 100644 --- a/doc/classes/NavigationAgent3D.xml +++ b/doc/classes/NavigationAgent3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationAgent3D" inherits="Node" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationAgent3D" inherits="Node" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> 3D Agent used in navigation for collision avoidance. </brief_description> @@ -16,24 +16,30 @@ Returns the distance to the target location, using the agent's global position. The user must set [member target_location] in order for this to be accurate. </description> </method> - <method name="get_final_location"> - <return type="Vector3" /> - <description> - Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. - </description> - </method> - <method name="get_nav_path" qualifiers="const"> + <method name="get_current_navigation_path" qualifiers="const"> <return type="PackedVector3Array" /> <description> Returns this agent's current path from start to finish in global coordinates. The path only updates when the target location is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_location] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic. </description> </method> - <method name="get_nav_path_index" qualifiers="const"> + <method name="get_current_navigation_path_index" qualifiers="const"> <return type="int" /> <description> Returns which index the agent is currently on in the navigation path's [PackedVector3Array]. </description> </method> + <method name="get_current_navigation_result" qualifiers="const"> + <return type="NavigationPathQueryResult3D" /> + <description> + Returns the path query result for the path the agent is currently following. + </description> + </method> + <method name="get_final_location"> + <return type="Vector3" /> + <description> + Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. + </description> + </method> <method name="get_navigation_layer_value" qualifiers="const"> <return type="bool" /> <param index="0" name="layer_number" type="int" /> @@ -128,6 +134,9 @@ <member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="3.0"> The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. </member> + <member name="path_metadata_flags" type="int" setter="set_path_metadata_flags" getter="get_path_metadata_flags" enum="NavigationPathQueryParameters3D.PathMetadataFlags" default="7"> + Additional information to return with the navigation path. + </member> <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0"> The radius of the avoidance agent. This is the "body" of the avoidance agent and not the avoidance maneuver starting radius (which is controlled by [member neighbor_distance]). Does not affect normal pathfinding. To change an actor's pathfinding radius bake [NavigationMesh] resources with a different [member NavigationMesh.agent_radius] property and use different navigation maps for each actor size. @@ -143,6 +152,17 @@ </member> </members> <signals> + <signal name="link_reached"> + <param index="0" name="details" type="Dictionary" /> + <description> + Notifies when a navigation link has been reached. + The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: + - [code]location[/code]: The start location of the link that was reached. + - [code]type[/code]: Always [constant NavigationPathQueryResult3D.PATH_SEGMENT_TYPE_LINK]. + - [code]rid[/code]: The [RID] of the link. + - [code]owner[/code]: The object which manages the link (usually [NavigationLink3D]). + </description> + </signal> <signal name="navigation_finished"> <description> Notifies when the final location is reached. @@ -164,5 +184,16 @@ Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity]. Only emitted when [member avoidance_enabled] is true. </description> </signal> + <signal name="waypoint_reached"> + <param index="0" name="details" type="Dictionary" /> + <description> + Notifies when a waypoint along the path has been reached. + The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: + - [code]location[/code]: The location of the waypoint that was reached. + - [code]type[/code]: The type of navigation primitive (region or link) that contains this waypoint. + - [code]rid[/code]: The [RID] of the containing navigation primitive (region or link). + - [code]owner[/code]: The object which manages the containing navigation primitive (region or link). + </description> + </signal> </signals> </class> diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml index 1e086fb730..9d75694360 100644 --- a/doc/classes/NavigationLink2D.xml +++ b/doc/classes/NavigationLink2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationLink2D" inherits="Node2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationLink2D" inherits="Node2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Creates a link between two locations that [NavigationServer2D] can route agents through. </brief_description> @@ -38,7 +38,7 @@ The distance the link will search is controlled by [method NavigationServer2D.map_set_link_connection_radius]. </member> <member name="enter_cost" type="float" setter="set_enter_cost" getter="get_enter_cost" default="0.0"> - When pathfinding enters this link from another regions navmesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path. + When pathfinding enters this link from another regions navigation mesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path. </member> <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> A bitfield determining all navigation layers the link belongs to. These navigation layers will be checked when requesting a path with [method NavigationServer2D.map_get_path]. diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml index 4d5d81bec5..730c43c5a8 100644 --- a/doc/classes/NavigationLink3D.xml +++ b/doc/classes/NavigationLink3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationLink3D" inherits="Node3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationLink3D" inherits="Node3D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Creates a link between two locations that [NavigationServer3D] can route agents through. </brief_description> @@ -38,7 +38,7 @@ The distance the link will search is controlled by [method NavigationServer3D.map_set_link_connection_radius]. </member> <member name="enter_cost" type="float" setter="set_enter_cost" getter="get_enter_cost" default="0.0"> - When pathfinding enters this link from another regions navmesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path. + When pathfinding enters this link from another regions navigation mesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path. </member> <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> A bitfield determining all navigation layers the link belongs to. These navigation layers will be checked when requesting a path with [method NavigationServer3D.map_get_path]. diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml index c86bc47e04..ff898551d4 100644 --- a/doc/classes/NavigationMesh.xml +++ b/doc/classes/NavigationMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationMesh" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationMesh" inherits="Resource" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A mesh to approximate the walkable areas and obstacles. </brief_description> @@ -133,13 +133,10 @@ <member name="geometry_source_geometry_mode" type="int" setter="set_source_geometry_mode" getter="get_source_geometry_mode" enum="NavigationMesh.SourceGeometryMode" default="0"> The source of the geometry used when baking. See [enum SourceGeometryMode] for possible values. </member> - <member name="geometry_source_group_name" type="StringName" setter="set_source_group_name" getter="get_source_group_name" default="&"navmesh""> + <member name="geometry_source_group_name" type="StringName" setter="set_source_group_name" getter="get_source_group_name" default="&"navigation_mesh_source_group""> The name of the group to scan for geometry. Only used when [member geometry_source_geometry_mode] is [constant SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN] or [constant SOURCE_GEOMETRY_GROUPS_EXPLICIT]. </member> - <member name="polygon_verts_per_poly" type="float" setter="set_verts_per_poly" getter="get_verts_per_poly" default="6.0"> - The maximum number of vertices allowed for polygons generated during the contour to polygon conversion process. - </member> <member name="region_merge_size" type="float" setter="set_region_merge_size" getter="get_region_merge_size" default="20.0"> Any regions with a size smaller than this will be merged with larger regions if possible. [b]Note:[/b] This value will be squared to calculate the number of cells. For example, a value of 20 will set the number of cells to 400. @@ -151,6 +148,9 @@ <member name="sample_partition_type" type="int" setter="set_sample_partition_type" getter="get_sample_partition_type" enum="NavigationMesh.SamplePartitionType" default="0"> Partitioning algorithm for creating the navigation mesh polys. See [enum SamplePartitionType] for possible values. </member> + <member name="vertices_per_polygon" type="float" setter="set_vertices_per_polygon" getter="get_vertices_per_polygon" default="6.0"> + The maximum number of vertices allowed for polygons generated during the contour to polygon conversion process. + </member> </members> <constants> <constant name="SAMPLE_PARTITION_WATERSHED" value="0" enum="SamplePartitionType"> @@ -177,8 +177,8 @@ <constant name="PARSED_GEOMETRY_MAX" value="3" enum="ParsedGeometryType"> Represents the size of the [enum ParsedGeometryType] enum. </constant> - <constant name="SOURCE_GEOMETRY_NAVMESH_CHILDREN" value="0" enum="SourceGeometryMode"> - Scans the child nodes of [NavigationRegion3D] recursively for geometry. + <constant name="SOURCE_GEOMETRY_ROOT_NODE_CHILDREN" value="0" enum="SourceGeometryMode"> + Scans the child nodes of the root node recursively for geometry. </constant> <constant name="SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN" value="1" enum="SourceGeometryMode"> Scans nodes in a group and their child nodes recursively for geometry. The group is specified by [member geometry_source_group_name]. diff --git a/doc/classes/NavigationMeshGenerator.xml b/doc/classes/NavigationMeshGenerator.xml index 4c337db90f..15d149a229 100644 --- a/doc/classes/NavigationMeshGenerator.xml +++ b/doc/classes/NavigationMeshGenerator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationMeshGenerator" inherits="Object" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationMeshGenerator" inherits="Object" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Helper class for creating and clearing navigation meshes. </brief_description> @@ -15,17 +15,17 @@ <methods> <method name="bake"> <return type="void" /> - <param index="0" name="nav_mesh" type="NavigationMesh" /> + <param index="0" name="navigation_mesh" type="NavigationMesh" /> <param index="1" name="root_node" type="Node" /> <description> - Bakes navigation data to the provided [param nav_mesh] by parsing child nodes under the provided [param root_node] or a specific group of nodes for potential source geometry. The parse behavior can be controlled with the [member NavigationMesh.geometry_parsed_geometry_type] and [member NavigationMesh.geometry_source_geometry_mode] properties on the [NavigationMesh] resource. + Bakes navigation data to the provided [param navigation_mesh] by parsing child nodes under the provided [param root_node] or a specific group of nodes for potential source geometry. The parse behavior can be controlled with the [member NavigationMesh.geometry_parsed_geometry_type] and [member NavigationMesh.geometry_source_geometry_mode] properties on the [NavigationMesh] resource. </description> </method> <method name="clear"> <return type="void" /> - <param index="0" name="nav_mesh" type="NavigationMesh" /> + <param index="0" name="navigation_mesh" type="NavigationMesh" /> <description> - Removes all polygons and vertices from the provided [param nav_mesh] resource. + Removes all polygons and vertices from the provided [param navigation_mesh] resource. </description> </method> </methods> diff --git a/doc/classes/NavigationObstacle2D.xml b/doc/classes/NavigationObstacle2D.xml index 68d9691ccf..ce0f05ad28 100644 --- a/doc/classes/NavigationObstacle2D.xml +++ b/doc/classes/NavigationObstacle2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationObstacle2D" inherits="Node" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationObstacle2D" inherits="Node" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> 2D Obstacle used in navigation for collision avoidance. </brief_description> diff --git a/doc/classes/NavigationObstacle3D.xml b/doc/classes/NavigationObstacle3D.xml index 90eb55887f..78bbb788d9 100644 --- a/doc/classes/NavigationObstacle3D.xml +++ b/doc/classes/NavigationObstacle3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationObstacle3D" inherits="Node" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationObstacle3D" inherits="Node" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> 3D Obstacle used in navigation for collision avoidance. </brief_description> diff --git a/doc/classes/NavigationPathQueryParameters2D.xml b/doc/classes/NavigationPathQueryParameters2D.xml index 397ca8b713..511b2e7a8c 100644 --- a/doc/classes/NavigationPathQueryParameters2D.xml +++ b/doc/classes/NavigationPathQueryParameters2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationPathQueryParameters2D" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationPathQueryParameters2D" inherits="RefCounted" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Parameters to be sent to a 2D navigation path query. </brief_description> @@ -12,6 +12,9 @@ <member name="map" type="RID" setter="set_map" getter="get_map"> The navigation [code]map[/code] [RID] used in the path query. </member> + <member name="metadata_flags" type="int" setter="set_metadata_flags" getter="get_metadata_flags" enum="NavigationPathQueryParameters2D.PathMetadataFlags" default="7"> + Additional information to include with the navigation path. + </member> <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> The navigation layers the query will use (as a bitmask). </member> @@ -33,10 +36,25 @@ The path query uses the default A* pathfinding algorithm. </constant> <constant name="PATH_POSTPROCESSING_CORRIDORFUNNEL" value="0" enum="PathPostProcessing"> - Applies a funnel algorithm to the raw path corridor found by the pathfinding algorithm. This will result in the shortest path possible inside the path corridor. This postprocessing very much depends on the navmesh polygon layout and the created corridor. Especially tile- or gridbased layouts can face artificial corners with diagonal movement due to a jagged path corridor imposed by the cell shapes. + Applies a funnel algorithm to the raw path corridor found by the pathfinding algorithm. This will result in the shortest path possible inside the path corridor. This postprocessing very much depends on the navigation mesh polygon layout and the created corridor. Especially tile- or gridbased layouts can face artificial corners with diagonal movement due to a jagged path corridor imposed by the cell shapes. </constant> <constant name="PATH_POSTPROCESSING_EDGECENTERED" value="1" enum="PathPostProcessing"> - Centers every path position in the middle of the traveled navmesh polygon edge. This creates better paths for tile- or gridbased layouts that restrict the movement to the cells center. + Centers every path position in the middle of the traveled navigation mesh polygon edge. This creates better paths for tile- or gridbased layouts that restrict the movement to the cells center. + </constant> + <constant name="PATH_METADATA_INCLUDE_NONE" value="0" enum="PathMetadataFlags" is_bitfield="true"> + Don't include any additional metadata about the returned path. + </constant> + <constant name="PATH_METADATA_INCLUDE_TYPES" value="1" enum="PathMetadataFlags" is_bitfield="true"> + Include the type of navigation primitive (region or link) that each point of the path goes through. + </constant> + <constant name="PATH_METADATA_INCLUDE_RIDS" value="2" enum="PathMetadataFlags" is_bitfield="true"> + Include the [RID]s of the regions and links that each point of the path goes through. + </constant> + <constant name="PATH_METADATA_INCLUDE_OWNERS" value="4" enum="PathMetadataFlags" is_bitfield="true"> + Include the [code]ObjectID[/code]s of the [Object]s which manage the regions and links each point of the path goes through. + </constant> + <constant name="PATH_METADATA_INCLUDE_ALL" value="7" enum="PathMetadataFlags" is_bitfield="true"> + Include all available metadata about the returned path. </constant> </constants> </class> diff --git a/doc/classes/NavigationPathQueryParameters3D.xml b/doc/classes/NavigationPathQueryParameters3D.xml index 29faa5561b..b5031f60f2 100644 --- a/doc/classes/NavigationPathQueryParameters3D.xml +++ b/doc/classes/NavigationPathQueryParameters3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationPathQueryParameters3D" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationPathQueryParameters3D" inherits="RefCounted" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Parameters to be sent to a 3D navigation path query. </brief_description> @@ -12,6 +12,9 @@ <member name="map" type="RID" setter="set_map" getter="get_map"> The navigation [code]map[/code] [RID] used in the path query. </member> + <member name="metadata_flags" type="int" setter="set_metadata_flags" getter="get_metadata_flags" enum="NavigationPathQueryParameters3D.PathMetadataFlags" default="7"> + Additional information to include with the navigation path. + </member> <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> The navigation layers the query will use (as a bitmask). </member> @@ -33,10 +36,25 @@ The path query uses the default A* pathfinding algorithm. </constant> <constant name="PATH_POSTPROCESSING_CORRIDORFUNNEL" value="0" enum="PathPostProcessing"> - Applies a funnel algorithm to the raw path corridor found by the pathfinding algorithm. This will result in the shortest path possible inside the path corridor. This postprocessing very much depends on the navmesh polygon layout and the created corridor. Especially tile- or gridbased layouts can face artificial corners with diagonal movement due to a jagged path corridor imposed by the cell shapes. + Applies a funnel algorithm to the raw path corridor found by the pathfinding algorithm. This will result in the shortest path possible inside the path corridor. This postprocessing very much depends on the navigation mesh polygon layout and the created corridor. Especially tile- or gridbased layouts can face artificial corners with diagonal movement due to a jagged path corridor imposed by the cell shapes. </constant> <constant name="PATH_POSTPROCESSING_EDGECENTERED" value="1" enum="PathPostProcessing"> - Centers every path position in the middle of the traveled navmesh polygon edge. This creates better paths for tile- or gridbased layouts that restrict the movement to the cells center. + Centers every path position in the middle of the traveled navigation mesh polygon edge. This creates better paths for tile- or gridbased layouts that restrict the movement to the cells center. + </constant> + <constant name="PATH_METADATA_INCLUDE_NONE" value="0" enum="PathMetadataFlags" is_bitfield="true"> + Don't include any additional metadata about the returned path. + </constant> + <constant name="PATH_METADATA_INCLUDE_TYPES" value="1" enum="PathMetadataFlags" is_bitfield="true"> + Include the type of navigation primitive (region or link) that each point of the path goes through. + </constant> + <constant name="PATH_METADATA_INCLUDE_RIDS" value="2" enum="PathMetadataFlags" is_bitfield="true"> + Include the [RID]s of the regions and links that each point of the path goes through. + </constant> + <constant name="PATH_METADATA_INCLUDE_OWNERS" value="4" enum="PathMetadataFlags" is_bitfield="true"> + Include the [code]ObjectID[/code]s of the [Object]s which manage the regions and links each point of the path goes through. + </constant> + <constant name="PATH_METADATA_INCLUDE_ALL" value="7" enum="PathMetadataFlags" is_bitfield="true"> + Include all available metadata about the returned path. </constant> </constants> </class> diff --git a/doc/classes/NavigationPathQueryResult2D.xml b/doc/classes/NavigationPathQueryResult2D.xml index 95b90e9383..75f7cc47aa 100644 --- a/doc/classes/NavigationPathQueryResult2D.xml +++ b/doc/classes/NavigationPathQueryResult2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationPathQueryResult2D" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationPathQueryResult2D" inherits="RefCounted" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Result from a [NavigationPathQueryParameters2D] navigation path query. </brief_description> @@ -20,5 +20,22 @@ <member name="path" type="PackedVector2Array" setter="set_path" getter="get_path" default="PackedVector2Array()"> The resulting path array from the navigation query. All path array positions are in global coordinates. Without customized query parameters this is the same path as returned by [method NavigationServer2D.map_get_path]. </member> + <member name="path_owner_ids" type="PackedInt64Array" setter="set_path_owner_ids" getter="get_path_owner_ids" default="PackedInt64Array()"> + The [code]ObjectID[/code]s of the [Object]s which manage the regions and links each point of the path goes through. + </member> + <member name="path_rids" type="RID[]" setter="set_path_rids" getter="get_path_rids" default="[]"> + The [RID]s of the regions and links that each point of the path goes through. + </member> + <member name="path_types" type="PackedInt32Array" setter="set_path_types" getter="get_path_types" default="PackedInt32Array()"> + The type of navigation primitive (region or link) that each point of the path goes through. + </member> </members> + <constants> + <constant name="PATH_SEGMENT_TYPE_REGION" value="0" enum="PathSegmentType"> + This segment of the path goes through a region. + </constant> + <constant name="PATH_SEGMENT_TYPE_LINK" value="1" enum="PathSegmentType"> + This segment of the path goes through a link. + </constant> + </constants> </class> diff --git a/doc/classes/NavigationPathQueryResult3D.xml b/doc/classes/NavigationPathQueryResult3D.xml index b4ca8288db..03d41cb230 100644 --- a/doc/classes/NavigationPathQueryResult3D.xml +++ b/doc/classes/NavigationPathQueryResult3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationPathQueryResult3D" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationPathQueryResult3D" inherits="RefCounted" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Result from a [NavigationPathQueryParameters3D] navigation path query. </brief_description> @@ -20,5 +20,22 @@ <member name="path" type="PackedVector3Array" setter="set_path" getter="get_path" default="PackedVector3Array()"> The resulting path array from the navigation query. All path array positions are in global coordinates. Without customized query parameters this is the same path as returned by [method NavigationServer3D.map_get_path]. </member> + <member name="path_owner_ids" type="PackedInt64Array" setter="set_path_owner_ids" getter="get_path_owner_ids" default="PackedInt64Array()"> + The [code]ObjectID[/code]s of the [Object]s which manage the regions and links each point of the path goes through. + </member> + <member name="path_rids" type="RID[]" setter="set_path_rids" getter="get_path_rids" default="[]"> + The [RID]s of the regions and links that each point of the path goes through. + </member> + <member name="path_types" type="PackedInt32Array" setter="set_path_types" getter="get_path_types" default="PackedInt32Array()"> + The type of navigation primitive (region or link) that each point of the path goes through. + </member> </members> + <constants> + <constant name="PATH_SEGMENT_TYPE_REGION" value="0" enum="PathSegmentType"> + This segment of the path goes through a region. + </constant> + <constant name="PATH_SEGMENT_TYPE_LINK" value="1" enum="PathSegmentType"> + This segment of the path goes through a link. + </constant> + </constants> </class> diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml index 19466c171f..b30dd2703a 100644 --- a/doc/classes/NavigationPolygon.xml +++ b/doc/classes/NavigationPolygon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationPolygon" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationPolygon" inherits="Resource" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A node that has methods to draw outlines or use indices of vertices to create navigation polygons. </brief_description> @@ -12,14 +12,14 @@ var outline = PackedVector2Array([Vector2(0, 0), Vector2(0, 50), Vector2(50, 50), Vector2(50, 0)]) polygon.add_outline(outline) polygon.make_polygons_from_outlines() - $NavigationRegion2D.navpoly = polygon + $NavigationRegion2D.navigation_polygon = polygon [/gdscript] [csharp] var polygon = new NavigationPolygon(); var outline = new Vector2[] { new Vector2(0, 0), new Vector2(0, 50), new Vector2(50, 50), new Vector2(50, 0) }; polygon.AddOutline(outline); polygon.MakePolygonsFromOutlines(); - GetNode<NavigationRegion2D>("NavigationRegion2D").Navpoly = polygon; + GetNode<NavigationRegion2D>("NavigationRegion2D").NavigationPolygon = polygon; [/csharp] [/codeblocks] Using [method add_polygon] and indices of the vertices array. @@ -30,7 +30,7 @@ polygon.vertices = vertices var indices = PackedInt32Array([0, 1, 2, 3]) polygon.add_polygon(indices) - $NavigationRegion2D.navpoly = polygon + $NavigationRegion2D.navigation_polygon = polygon [/gdscript] [csharp] var polygon = new NavigationPolygon(); @@ -38,7 +38,7 @@ polygon.Vertices = vertices; var indices = new int[] { 0, 1, 2, 3 }; polygon.AddPolygon(indices); - GetNode<NavigationRegion2D>("NavigationRegion2D").Navpoly = polygon; + GetNode<NavigationRegion2D>("NavigationRegion2D").NavigationPolygon = polygon; [/csharp] [/codeblocks] </description> @@ -80,10 +80,10 @@ Clears the array of polygons, but it doesn't clear the array of outlines and vertices. </description> </method> - <method name="get_mesh"> + <method name="get_navigation_mesh"> <return type="NavigationMesh" /> <description> - Returns the [NavigationMesh] resulting from this navigation polygon. This navmesh can be used to update the navmesh of a region with the [method NavigationServer3D.region_set_navmesh] API directly (as 2D uses the 3D server behind the scene). + Returns the [NavigationMesh] resulting from this navigation polygon. This navigation mesh can be used to update the navigation mesh of a region with the [method NavigationServer3D.region_set_navigation_mesh] API directly (as 2D uses the 3D server behind the scene). </description> </method> <method name="get_outline" qualifiers="const"> diff --git a/doc/classes/NavigationRegion2D.xml b/doc/classes/NavigationRegion2D.xml index 0f28081201..8b8793b3b4 100644 --- a/doc/classes/NavigationRegion2D.xml +++ b/doc/classes/NavigationRegion2D.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationRegion2D" inherits="Node2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationRegion2D" inherits="Node2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A region of the 2D navigation map. </brief_description> <description> A region of the navigation map. It tells the [NavigationServer2D] what can be navigated and what cannot, based on its [NavigationPolygon] resource. Two regions can be connected to each other if they share a similar edge. You can set the minimum distance between two vertices required to connect two edges by using [method NavigationServer2D.map_set_edge_connection_margin]. - [b]Note:[/b] Overlapping two regions' polygons is not enough for connecting two regions. They must share a similar edge. + [b]Note:[/b] Overlapping two regions' navigation polygons is not enough for connecting two regions. They must share a similar edge. The pathfinding cost of entering this region from another region can be controlled with the [member enter_cost] value. [b]Note:[/b] This value is not added to the path cost when the start position is already inside this region. The pathfinding cost of traveling distances inside this region can be controlled with the [member travel_cost] multiplier. @@ -42,16 +42,16 @@ Determines if the [NavigationRegion2D] is enabled or disabled. </member> <member name="enter_cost" type="float" setter="set_enter_cost" getter="get_enter_cost" default="0.0"> - When pathfinding enters this region's navmesh from another regions navmesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path. + When pathfinding enters this region's navigation mesh from another regions navigation mesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path. </member> <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> A bitfield determining all navigation layers the region belongs to. These navigation layers can be checked upon when requesting a path with [method NavigationServer2D.map_get_path]. </member> - <member name="navpoly" type="NavigationPolygon" setter="set_navigation_polygon" getter="get_navigation_polygon"> + <member name="navigation_polygon" type="NavigationPolygon" setter="set_navigation_polygon" getter="get_navigation_polygon"> The [NavigationPolygon] resource to use. </member> <member name="travel_cost" type="float" setter="set_travel_cost" getter="get_travel_cost" default="1.0"> - When pathfinding moves inside this region's navmesh the traveled distances are multiplied with [code]travel_cost[/code] for determining the shortest path. + When pathfinding moves inside this region's navigation mesh the traveled distances are multiplied with [code]travel_cost[/code] for determining the shortest path. </member> </members> </class> diff --git a/doc/classes/NavigationRegion3D.xml b/doc/classes/NavigationRegion3D.xml index 85f163b3c2..10662db869 100644 --- a/doc/classes/NavigationRegion3D.xml +++ b/doc/classes/NavigationRegion3D.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationRegion3D" inherits="Node3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationRegion3D" inherits="Node3D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A region of the navigation map. </brief_description> <description> A region of the navigation map. It tells the [NavigationServer3D] what can be navigated and what cannot, based on its [NavigationMesh] resource. Two regions can be connected to each other if they share a similar edge. You can set the minimum distance between two vertices required to connect two edges by using [method NavigationServer3D.map_set_edge_connection_margin]. - [b]Note:[/b] Overlapping two regions' navmeshes is not enough for connecting two regions. They must share a similar edge. + [b]Note:[/b] Overlapping two regions' navigation meshes is not enough for connecting two regions. They must share a similar edge. The cost of entering this region from another region can be controlled with the [member enter_cost] value. [b]Note:[/b] This value is not added to the path cost when the start position is already inside this region. The cost of traveling distances inside this region can be controlled with the [member travel_cost] multiplier. @@ -49,16 +49,16 @@ Determines if the [NavigationRegion3D] is enabled or disabled. </member> <member name="enter_cost" type="float" setter="set_enter_cost" getter="get_enter_cost" default="0.0"> - When pathfinding enters this region's navmesh from another regions navmesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path. + When pathfinding enters this region's navigation mesh from another regions navigation mesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path. </member> <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> A bitfield determining all navigation layers the region belongs to. These navigation layers can be checked upon when requesting a path with [method NavigationServer3D.map_get_path]. </member> - <member name="navmesh" type="NavigationMesh" setter="set_navigation_mesh" getter="get_navigation_mesh"> + <member name="navigation_mesh" type="NavigationMesh" setter="set_navigation_mesh" getter="get_navigation_mesh"> The [NavigationMesh] resource to use. </member> <member name="travel_cost" type="float" setter="set_travel_cost" getter="get_travel_cost" default="1.0"> - When pathfinding moves inside this region's navmesh the traveled distances are multiplied with [code]travel_cost[/code] for determining the shortest path. + When pathfinding moves inside this region's navigation mesh the traveled distances are multiplied with [code]travel_cost[/code] for determining the shortest path. </member> </members> <signals> diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index 230e58a12a..32e48cde54 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationServer2D" inherits="Object" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationServer2D" inherits="Object" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Server interface for low-level 2D navigation access </brief_description> @@ -489,12 +489,12 @@ Set the region's navigation layers. This allows selecting regions from a path request (when using [method NavigationServer2D.map_get_path]). </description> </method> - <method name="region_set_navpoly" qualifiers="const"> + <method name="region_set_navigation_polygon" qualifiers="const"> <return type="void" /> <param index="0" name="region" type="RID" /> - <param index="1" name="nav_poly" type="NavigationPolygon" /> + <param index="1" name="navigation_polygon" type="NavigationPolygon" /> <description> - Sets the navigation mesh for the region. + Sets the [param navigation_polygon] for the region. </description> </method> <method name="region_set_owner_id" qualifiers="const"> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index c7da3b7b5b..c156dfac16 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationServer3D" inherits="Object" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationServer3D" inherits="Object" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Server interface for low-level 3D navigation access </brief_description> @@ -433,12 +433,12 @@ Queries a path in a given navigation map. Start and target position and other parameters are defined through [NavigationPathQueryParameters3D]. Updates the provided [NavigationPathQueryResult3D] result object with the path among other results requested by the query. </description> </method> - <method name="region_bake_navmesh" qualifiers="const"> + <method name="region_bake_navigation_mesh" qualifiers="const"> <return type="void" /> - <param index="0" name="mesh" type="NavigationMesh" /> - <param index="1" name="node" type="Node" /> + <param index="0" name="navigation_mesh" type="NavigationMesh" /> + <param index="1" name="root_node" type="Node" /> <description> - Bakes the navigation mesh. + Bakes the [param navigation_mesh] with bake source geometry collected starting from the [param root_node]. </description> </method> <method name="region_create" qualifiers="const"> @@ -539,10 +539,10 @@ Set the region's navigation layers. This allows selecting regions from a path request (when using [method NavigationServer3D.map_get_path]). </description> </method> - <method name="region_set_navmesh" qualifiers="const"> + <method name="region_set_navigation_mesh" qualifiers="const"> <return type="void" /> <param index="0" name="region" type="RID" /> - <param index="1" name="nav_mesh" type="NavigationMesh" /> + <param index="1" name="navigation_mesh" type="NavigationMesh" /> <description> Sets the navigation mesh for the region. </description> diff --git a/doc/classes/Node2D.xml b/doc/classes/Node2D.xml index 26efe37dce..9d224f09b1 100644 --- a/doc/classes/Node2D.xml +++ b/doc/classes/Node2D.xml @@ -99,6 +99,9 @@ <member name="global_rotation" type="float" setter="set_global_rotation" getter="get_global_rotation"> Global rotation in radians. </member> + <member name="global_rotation_degrees" type="float" setter="set_global_rotation_degrees" getter="get_global_rotation_degrees"> + Helper property to access [member global_rotation] in degrees instead of radians. + </member> <member name="global_scale" type="Vector2" setter="set_global_scale" getter="get_global_scale"> Global scale. </member> @@ -114,6 +117,9 @@ <member name="rotation" type="float" setter="set_rotation" getter="get_rotation" default="0.0"> Rotation in radians, relative to the node's parent. </member> + <member name="rotation_degrees" type="float" setter="set_rotation_degrees" getter="get_rotation_degrees"> + Helper property to access [member rotation] in degrees instead of radians. + </member> <member name="scale" type="Vector2" setter="set_scale" getter="get_scale" default="Vector2(1, 1)"> The node's scale. Unscaled value: [code](1, 1)[/code]. [b]Note:[/b] Negative X scales in 2D are not decomposable from the transformation matrix. Due to the way scale is represented with transformation matrices in Godot, negative scales on the X axis will be changed to negative scales on the Y axis and a rotation of 180 degrees when decomposed. diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index b6024b1887..1aa71113e9 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -274,6 +274,9 @@ Rotation part of the global transformation in radians, specified in terms of YXZ-Euler angles in the format (X angle, Y angle, Z angle). [b]Note:[/b] In the mathematical sense, rotation is a matrix and not a vector. The three Euler angles, which are the three independent parameters of the Euler-angle parametrization of the rotation matrix, are stored in a [Vector3] data structure not because the rotation is a vector, but only because [Vector3] exists as a convenient data-structure to store 3 floating-point numbers. Therefore, applying affine operations on the rotation "vector" is not meaningful. </member> + <member name="global_rotation_degrees" type="Vector3" setter="set_global_rotation_degrees" getter="get_global_rotation_degrees"> + Helper property to access [member global_rotation] in degrees instead of radians. + </member> <member name="global_transform" type="Transform3D" setter="set_global_transform" getter="get_global_transform"> World3D space (global) [Transform3D] of this node. </member> @@ -287,6 +290,9 @@ Rotation part of the local transformation in radians, specified in terms of Euler angles. The angles construct a rotaton in the order specified by the [member rotation_order] property. [b]Note:[/b] In the mathematical sense, rotation is a matrix and not a vector. The three Euler angles, which are the three independent parameters of the Euler-angle parametrization of the rotation matrix, are stored in a [Vector3] data structure not because the rotation is a vector, but only because [Vector3] exists as a convenient data-structure to store 3 floating-point numbers. Therefore, applying affine operations on the rotation "vector" is not meaningful. </member> + <member name="rotation_degrees" type="Vector3" setter="set_rotation_degrees" getter="get_rotation_degrees"> + Helper property to access [member rotation] in degrees instead of radians. + </member> <member name="rotation_edit_mode" type="int" setter="set_rotation_edit_mode" getter="get_rotation_edit_mode" enum="Node3D.RotationEditMode" default="0"> Specify how rotation (and scale) will be presented in the editor. </member> diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index 5e834b3d91..e015bec134 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -8,7 +8,7 @@ You can create new instances, using [code]Object.new()[/code] in GDScript, or [code]new Object[/code] in C#. To delete an Object instance, call [method free]. This is necessary for most classes inheriting Object, because they do not manage memory on their own, and will otherwise cause memory leaks when no longer in use. There are a few classes that perform memory management. For example, [RefCounted] (and by extension [Resource]) deletes itself when no longer referenced, and [Node] deletes its children when freed. Objects can have a [Script] attached to them. Once the [Script] is instantiated, it effectively acts as an extension to the base class, allowing it to define and inherit new properties, methods and signals. - Inside a [Script], [method _get_property_list] may be overriden to customize properties in several ways. This allows them to be available to the editor, display as lists of options, sub-divide into groups, save on disk, etc. Scripting languages offer easier ways to customize properties, such as with the [annotation @GDScript.@export] annotation. + Inside a [Script], [method _get_property_list] may be overridden to customize properties in several ways. This allows them to be available to the editor, display as lists of options, sub-divide into groups, save on disk, etc. Scripting languages offer easier ways to customize properties, such as with the [annotation @GDScript.@export] annotation. Godot is very dynamic. An object's script, and therefore its properties, methods and signals, can be changed at run-time. Because of this, there can be occasions where, for example, a property required by a method may not exist. To prevent run-time errors, see methods such as [method set], [method get], [method call], [method has_method], [method has_signal], etc. Note that these methods are [b]much[/b] slower than direct references. In GDScript, you can also check if a given property, method, or signal name exists in an object with the [code]in[/code] operator: [codeblock] diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml index e63e822648..a58a6249ae 100644 --- a/doc/classes/OptionButton.xml +++ b/doc/classes/OptionButton.xml @@ -259,6 +259,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> [Font] of the [OptionButton]'s text. diff --git a/doc/classes/PathFollow3D.xml b/doc/classes/PathFollow3D.xml index fa7580b7b6..01275471d0 100644 --- a/doc/classes/PathFollow3D.xml +++ b/doc/classes/PathFollow3D.xml @@ -15,7 +15,7 @@ <param index="0" name="transform" type="Transform3D" /> <param index="1" name="rotation_mode" type="int" enum="PathFollow3D.RotationMode" /> <description> - Correct the [code]transform[/code]. [code]rotation_mode[/code] implicitly specifies how posture (forward, up and sideway direction) is caculated. + Correct the [code]transform[/code]. [code]rotation_mode[/code] implicitly specifies how posture (forward, up and sideway direction) is calculated. </description> </method> </methods> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 6810b0e8e4..8dec4eaf86 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -584,6 +584,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the item text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="separator_outline_size" data_type="constant" type="int" default="0"> The size of the labeled separator text outline. diff --git a/doc/classes/ProgressBar.xml b/doc/classes/ProgressBar.xml index 510b8d5bd1..9f89a4e4c3 100644 --- a/doc/classes/ProgressBar.xml +++ b/doc/classes/ProgressBar.xml @@ -42,6 +42,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> Font used to draw the fill percentage if [member show_percentage] is [code]true[/code]. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 72f9a0e32f..67b692a7a0 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -70,15 +70,18 @@ <method name="get_setting" qualifiers="const"> <return type="Variant" /> <param index="0" name="name" type="String" /> + <param index="1" name="default_value" type="Variant" default="null" /> <description> - Returns the value of a setting. + Returns the value of the setting identified by [param name]. If the setting doesn't exist and [param default_value] is specified, the value of [param default_value] is returned. Otherwise, [code]null[/code] is returned. [b]Example:[/b] [codeblocks] [gdscript] print(ProjectSettings.get_setting("application/config/name")) + print(ProjectSettings.get_setting("application/config/custom_description", "No description specified.")) [/gdscript] [csharp] GD.Print(ProjectSettings.GetSetting("application/config/name")); + GD.Print(ProjectSettings.GetSetting("application/config/custom_description", "No description specified.")); [/csharp] [/codeblocks] </description> @@ -344,9 +347,6 @@ <member name="compression/formats/zstd/window_log_size" type="int" setter="" getter="" default="27"> Largest size limit (in power of 2) allowed when compressing using long-distance matching with Zstandard. Higher values can result in better compression, but will require more memory when compressing and decompressing. </member> - <member name="debug/disable_touch" type="bool" setter="" getter="" default="false"> - Disable touch input. Only has effect on iOS. - </member> <member name="debug/file_logging/enable_file_logging" type="bool" setter="" getter="" default="false"> If [code]true[/code], logs all output to files. </member> @@ -585,7 +585,7 @@ [b]Note:[/b] This setting has no effect on the home indicator if [code]hide_home_indicator[/code] is [code]true[/code]. </member> <member name="display/window/per_pixel_transparency/allowed" type="bool" setter="" getter="" default="false"> - If [code]true[/code], allows per-pixel transparency for the window background. This affects performance, so leave it on [code]false[/code] unless you need it. See also [member display/window/size/transparent] and [member rendering/transparent_background]. + If [code]true[/code], allows per-pixel transparency for the window background. This affects performance, so leave it on [code]false[/code] unless you need it. See also [member display/window/size/transparent] and [member rendering/viewport/transparent_background]. </member> <member name="display/window/size/always_on_top" type="bool" setter="" getter="" default="false"> Forces the main window to be always on top. @@ -610,7 +610,7 @@ [b]Note:[/b] This setting is ignored on iOS. </member> <member name="display/window/size/transparent" type="bool" setter="" getter="" default="false"> - If [code]true[/code], enables a window manager hint that the main window background [i]can[/i] be transparent. This does not make the background actually transparent. For the background to be transparent, the root viewport must also be made transparent by enabling [member rendering/transparent_background]. + If [code]true[/code], enables a window manager hint that the main window background [i]can[/i] be transparent. This does not make the background actually transparent. For the background to be transparent, the root viewport must also be made transparent by enabling [member rendering/viewport/transparent_background]. [b]Note:[/b] To use a transparent splash screen, set [member application/boot_splash/bg_color] to [code]Color(0, 0, 0, 0)[/code]. [b]Note:[/b] This setting has no effect if [member display/window/per_pixel_transparency/allowed] is set to [code]false[/code]. </member> @@ -2256,7 +2256,7 @@ <member name="rendering/textures/webp_compression/lossless_compression_factor" type="float" setter="" getter="" default="25"> The default compression factor for lossless WebP. Decompression speed is mostly unaffected by the compression factor. Supported values are 0 to 100. </member> - <member name="rendering/transparent_background" type="bool" setter="" getter="" default="false"> + <member name="rendering/viewport/transparent_background" type="bool" setter="" getter="" default="false"> If [code]true[/code], enables [member Viewport.transparent_bg] on the root viewport. This allows per-pixel transparency to be effective after also enabling [member display/window/size/transparent] and [member display/window/per_pixel_transparency/allowed]. </member> <member name="rendering/vrs/mode" type="int" setter="" getter="" default="0"> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 797231ac5e..f318430611 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -504,7 +504,7 @@ <return type="RID" /> <param index="0" name="size_bytes" type="int" /> <param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" /> - <param index="2" name="usage" type="int" default="0" /> + <param index="2" name="usage" type="int" enum="RenderingDevice.StorageBufferUsage" default="0" /> <description> </description> </method> @@ -1273,7 +1273,7 @@ </constant> <constant name="INDEX_BUFFER_FORMAT_UINT32" value="1" enum="IndexBufferFormat"> </constant> - <constant name="STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT" value="1" enum="StorageBufferUsage"> + <constant name="STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT" value="1" enum="StorageBufferUsage" is_bitfield="true"> </constant> <constant name="UNIFORM_TYPE_SAMPLER" value="0" enum="UniformType"> </constant> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 6536f8cb68..87e569ba20 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -241,6 +241,7 @@ <param index="4" name="modulate" type="Color" default="Color(1, 1, 1, 1)" /> <param index="5" name="outline_size" type="int" default="0" /> <param index="6" name="px_range" type="float" default="1.0" /> + <param index="7" name="scale" type="float" default="1.0" /> <description> </description> </method> @@ -1559,7 +1560,7 @@ <param index="0" name="instance" type="RID" /> <param index="1" name="aabb" type="AABB" /> <description> - Sets a custom AABB to use when culling objects from the view frustum. Equivalent to [method GeometryInstance3D.set_custom_aabb]. + Sets a custom AABB to use when culling objects from the view frustum. Equivalent to setting [member GeometryInstance3D.custom_aabb]. </description> </method> <method name="instance_set_extra_visibility_margin"> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index e222894647..dd291a425d 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -367,6 +367,7 @@ <return type="void" /> <param index="0" name="columns" type="int" /> <param index="1" name="inline_align" type="int" enum="InlineAlignment" default="0" /> + <param index="2" name="align_to_row" type="int" default="-1" /> <description> Adds a [code][table=columns,inline_align][/code] tag to the tag stack. </description> @@ -377,12 +378,12 @@ Adds a [code][u][/code] tag to the tag stack. </description> </method> - <method name="remove_line"> + <method name="remove_paragraph"> <return type="bool" /> - <param index="0" name="line" type="int" /> + <param index="0" name="paragraph" type="int" /> <description> - Removes a line of content from the label. Returns [code]true[/code] if the line exists. - The [param line] argument is the index of the line to remove, it can take values in the interval [code][0, get_line_count() - 1][/code]. + Removes a paragraph of content from the label. Returns [code]true[/code] if the paragraph exists. + The [param paragraph] argument is the index of the paragraph to remove, it can take values in the interval [code][0, get_paragraph_count() - 1][/code]. </description> </method> <method name="scroll_to_line"> @@ -655,6 +656,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="shadow_offset_x" data_type="constant" type="int" default="1"> The horizontal offset of the font's shadow. diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 653d53607e..97466e7860 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -973,11 +973,11 @@ [codeblocks] [gdscript] var url = "$DOCS_URL/?highlight=Godot%20Engine%3%docs" - print(url.uri_decode()) # Prints "$DOCS_URL/?hightlight=Godot Engine:docs" + print(url.uri_decode()) # Prints "$DOCS_URL/?highlight=Godot Engine:docs" [/gdscript] [csharp] var url = "$DOCS_URL/?highlight=Godot%20Engine%3%docs" - GD.Print(url.URIDecode()) // Prints "$DOCS_URL/?hightlight=Godot Engine:docs" + GD.Print(url.URIDecode()) // Prints "$DOCS_URL/?highlight=Godot Engine:docs" [/csharp] [/codeblocks] </description> @@ -988,13 +988,13 @@ Encodes the string to URL-friendly format. This method is meant to properly encode the parameters in a URL when sending an HTTP request. [codeblocks] [gdscript] - var prefix = "$DOCS_URL/?hightlight=" + var prefix = "$DOCS_URL/?highlight=" var url = prefix + "Godot Engine:docs".uri_encode() print(url) # Prints "$DOCS_URL/?highlight=Godot%20Engine%3%docs" [/gdscript] [csharp] - var prefix = "$DOCS_URL/?hightlight="; + var prefix = "$DOCS_URL/?highlight="; var url = prefix + "Godot Engine:docs".URIEncode(); GD.Print(url); // Prints "$DOCS_URL/?highlight=Godot%20Engine%3%docs" diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index 44d78a46fb..b46e39b8d7 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -880,11 +880,11 @@ [codeblocks] [gdscript] var url = "$DOCS_URL/?highlight=Godot%20Engine%3%docs" - print(url.uri_decode()) # Prints "$DOCS_URL/?hightlight=Godot Engine:docs" + print(url.uri_decode()) # Prints "$DOCS_URL/?highlight=Godot Engine:docs" [/gdscript] [csharp] var url = "$DOCS_URL/?highlight=Godot%20Engine%3%docs" - GD.Print(url.URIDecode()) // Prints "$DOCS_URL/?hightlight=Godot Engine:docs" + GD.Print(url.URIDecode()) // Prints "$DOCS_URL/?highlight=Godot Engine:docs" [/csharp] [/codeblocks] </description> @@ -895,13 +895,13 @@ Encodes the string to URL-friendly format. This method is meant to properly encode the parameters in a URL when sending an HTTP request. [codeblocks] [gdscript] - var prefix = "$DOCS_URL/?hightlight=" + var prefix = "$DOCS_URL/?highlight=" var url = prefix + "Godot Engine:docs".uri_encode() print(url) # Prints "$DOCS_URL/?highlight=Godot%20Engine%3%docs" [/gdscript] [csharp] - var prefix = "$DOCS_URL/?hightlight="; + var prefix = "$DOCS_URL/?highlight="; var url = prefix + "Godot Engine:docs".URIEncode(); GD.Print(url); // Prints "$DOCS_URL/?highlight=Godot%20Engine%3%docs" diff --git a/doc/classes/TabBar.xml b/doc/classes/TabBar.xml index 713c016651..8d56cbda13 100644 --- a/doc/classes/TabBar.xml +++ b/doc/classes/TabBar.xml @@ -319,6 +319,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the tab text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> The font used to draw tab names. diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index e2e7a0c37e..c744c9b439 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -214,6 +214,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the tab text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="side_margin" data_type="constant" type="int" default="8"> The space at the left or right edges of the tab bar, accordingly with the current [member tab_alignment]. diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index d4f5233438..1efd0f9326 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -1396,6 +1396,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="font" data_type="font" type="Font"> Sets the default [Font]. diff --git a/doc/classes/TextLine.xml b/doc/classes/TextLine.xml index d1dfdecbd2..1ebae9889d 100644 --- a/doc/classes/TextLine.xml +++ b/doc/classes/TextLine.xml @@ -15,6 +15,7 @@ <param index="1" name="size" type="Vector2" /> <param index="2" name="inline_align" type="int" enum="InlineAlignment" default="5" /> <param index="3" name="length" type="int" default="1" /> + <param index="4" name="baseline" type="float" default="0.0" /> <description> Adds inline object to the text buffer, [param key] must be unique. In the text, object is represented as [param length] object replacement characters. </description> @@ -122,6 +123,7 @@ <param index="0" name="key" type="Variant" /> <param index="1" name="size" type="Vector2" /> <param index="2" name="inline_align" type="int" enum="InlineAlignment" default="5" /> + <param index="3" name="baseline" type="float" default="0.0" /> <description> Sets new size and alignment of embedded object. </description> diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml index e0729ba844..38afd9b5f8 100644 --- a/doc/classes/TextParagraph.xml +++ b/doc/classes/TextParagraph.xml @@ -15,6 +15,7 @@ <param index="1" name="size" type="Vector2" /> <param index="2" name="inline_align" type="int" enum="InlineAlignment" default="5" /> <param index="3" name="length" type="int" default="1" /> + <param index="4" name="baseline" type="float" default="0.0" /> <description> Adds inline object to the text buffer, [param key] must be unique. In the text, object is represented as [param length] object replacement characters. </description> @@ -228,6 +229,7 @@ <param index="0" name="key" type="Variant" /> <param index="1" name="size" type="Vector2" /> <param index="2" name="inline_align" type="int" enum="InlineAlignment" default="5" /> + <param index="3" name="baseline" type="float" default="0.0" /> <description> Sets new size and alignment of embedded object. </description> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 4fc6ee3312..d2c6dee373 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -930,6 +930,7 @@ <param index="1" name="language" type="String" default="""" /> <description> Converts a number from the Western Arabic (0..9) to the numeral systems used in [param language]. + If [param language] is omitted, the active locale will be used. </description> </method> <method name="free_rid"> @@ -1097,6 +1098,7 @@ <param index="2" name="size" type="Vector2" /> <param index="3" name="inline_align" type="int" enum="InlineAlignment" default="5" /> <param index="4" name="length" type="int" default="1" /> + <param index="5" name="baseline" type="float" default="0.0" /> <description> Adds inline object to the text buffer, [param key] must be unique. In the text, object is represented as [param length] object replacement characters. </description> @@ -1438,6 +1440,7 @@ <param index="1" name="key" type="Variant" /> <param index="2" name="size" type="Vector2" /> <param index="3" name="inline_align" type="int" enum="InlineAlignment" default="5" /> + <param index="4" name="baseline" type="float" default="0.0" /> <description> Sets new size and alignment of embedded object. </description> @@ -1546,8 +1549,15 @@ <return type="PackedInt32Array" /> <param index="0" name="string" type="String" /> <param index="1" name="language" type="String" default="""" /> - <description> - Returns array of the word break character offsets. + <param index="2" name="chars_per_line" type="int" default="0" /> + <description> + Returns an array of the word break boundaries. Elements in the returned array are the offsets of the start and end of words. Therefore the length of the array is always even. + When [param chars_per_line] is greater than zero, line break boundaries are returned instead. + [codeblock] + var ts = TextServerManager.get_primary_interface() + print(ts.string_get_word_breaks("Godot Engine")) # Prints [0, 5, 6, 12] + print(ts.string_get_word_breaks("Godot Engine", "en", 5)) # Prints [0, 5, 6, 11, 11, 12] + [/codeblock] </description> </method> <method name="string_to_lower" qualifiers="const"> diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index 4c9817fcd4..e144b09eb6 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -945,6 +945,7 @@ <param index="2" name="size" type="Vector2" /> <param index="3" name="inline_align" type="int" enum="InlineAlignment" /> <param index="4" name="length" type="int" /> + <param index="5" name="baseline" type="float" /> <description> </description> </method> @@ -1242,6 +1243,7 @@ <param index="1" name="key" type="Variant" /> <param index="2" name="size" type="Vector2" /> <param index="3" name="inline_align" type="int" enum="InlineAlignment" /> + <param index="4" name="baseline" type="float" /> <description> </description> </method> @@ -1344,6 +1346,7 @@ <return type="PackedInt32Array" /> <param index="0" name="string" type="String" /> <param index="1" name="language" type="String" /> + <param index="2" name="chars_per_line" type="int" /> <description> </description> </method> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index e9e2a738d3..8b537545bc 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -257,8 +257,9 @@ <description> Sets the tile indentifiers for the cell on layer [param layer] at coordinates [param coords]. Each tile of the [TileSet] is identified using three parts: - The source identifier [param source_id] identifies a [TileSetSource] identifier. See [method TileSet.set_source_id], - - The atlas coordinates identifier [param atlas_coords] identifies a tile coordinates in the atlas (if the source is a [TileSetAtlasSource]. For [TileSetScenesCollectionSource] it should be 0), + - The atlas coordinates identifier [param atlas_coords] identifies a tile coordinates in the atlas (if the source is a [TileSetAtlasSource]. For [TileSetScenesCollectionSource] it should always be [code]Vector2i(0, 0)[/code]), - The alternative tile identifier [param alternative_tile] identifies a tile alternative the source is a [TileSetAtlasSource], and the scene for a [TileSetScenesCollectionSource]. + If [param source_id] is set to [code]-1[/code], [param atlas_coords] to [code]Vector2i(-1, -1)[/code] or [param alternative_tile] to [code]-1[/code], the cell will be erased. An erased cell gets [b]all[/b] its identifiers automatically set to their respective invalid values, namely [code]-1[/code], [code]Vector2i(-1, -1)[/code] and [code]-1[/code]. </description> </method> <method name="set_cells_terrain_connect"> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 629c271417..584a2a2a7b 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -522,6 +522,7 @@ </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. + [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. </theme_item> <theme_item name="parent_hl_line_margin" data_type="constant" type="int" default="0"> The space between the parent relationship lines for the selected [TreeItem] and the relationship lines to its siblings that are not selected. diff --git a/doc/classes/TubeTrailMesh.xml b/doc/classes/TubeTrailMesh.xml index ddc544dc97..7457aa4050 100644 --- a/doc/classes/TubeTrailMesh.xml +++ b/doc/classes/TubeTrailMesh.xml @@ -7,6 +7,12 @@ <tutorials> </tutorials> <members> + <member name="cap_bottom" type="bool" setter="set_cap_bottom" getter="is_cap_bottom" default="true"> + If [code]true[/code], generates a cap at the bottom of the tube. This can be set to [code]false[/code] to speed up generation and rendering when the cap is never seen by the camera. + </member> + <member name="cap_top" type="bool" setter="set_cap_top" getter="is_cap_top" default="true"> + If [code]true[/code], generates a cap at the top of the tube. This can be set to [code]false[/code] to speed up generation and rendering when the cap is never seen by the camera. + </member> <member name="curve" type="Curve" setter="set_curve" getter="get_curve"> </member> <member name="radial_steps" type="int" setter="set_radial_steps" getter="get_radial_steps" default="8"> diff --git a/drivers/SCsub b/drivers/SCsub index 6cfcb1d18c..276b99e10b 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -24,7 +24,6 @@ SConscript("winmidi/SCsub") # Graphics drivers if env["vulkan"]: - SConscript("spirv-reflect/SCsub") SConscript("vulkan/SCsub") if env["opengl3"]: SConscript("gl_context/SCsub") diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 7b39641029..ecad4b66a5 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -568,9 +568,8 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou _new_batch(batch_broken, index); // Override the start position and index as we want to start from where we finished off last time. - state.canvas_instance_batches[state.current_batch_index].start = r_last_index * sizeof(InstanceData); + state.canvas_instance_batches[state.current_batch_index].start = r_last_index; index = 0; - _align_instance_data_buffer(index); for (int i = 0; i < p_item_count; i++) { Item *ci = items[i]; @@ -630,14 +629,14 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou } // Copy over all data needed for rendering. - glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].ubo); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); #ifdef WEB_ENABLED - glBufferSubData(GL_UNIFORM_BUFFER, r_last_index * sizeof(InstanceData), sizeof(InstanceData) * index, state.instance_data_array); + glBufferSubData(GL_ARRAY_BUFFER, r_last_index * sizeof(InstanceData), sizeof(InstanceData) * index, state.instance_data_array); #else // On Desktop and mobile we map the memory without synchronizing for maximum speed. - void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, r_last_index * sizeof(InstanceData), index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); - memcpy(ubo, state.instance_data_array, index * sizeof(InstanceData)); - glUnmapBuffer(GL_UNIFORM_BUFFER); + void *buffer = glMapBufferRange(GL_ARRAY_BUFFER, r_last_index * sizeof(InstanceData), index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(buffer, state.instance_data_array, index * sizeof(InstanceData)); + glUnmapBuffer(GL_ARRAY_BUFFER); #endif glDisable(GL_SCISSOR_TEST); @@ -664,7 +663,17 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou uint64_t specialization = 0; specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled); specialization |= uint64_t(!GLES3::Config::get_singleton()->float_texture_supported) << 1; - bool success = _bind_material(material_data, variant, specialization); + RID shader_version = data.canvas_shader_default_version; + + if (material_data) { + if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { + // Bind uniform buffer and textures + material_data->bind_uniforms(); + shader_version = material_data->shader_data->version; + } + } + + bool success = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(shader_version, variant, specialization); if (!success) { continue; } @@ -1122,16 +1131,10 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend RID particles = pt->particles; state.canvas_instance_batches[state.current_batch_index].tex = pt->texture; state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED; - bool local_coords = particles_storage->particles_is_using_local_coords(particles); if (particles_storage->particles_has_collision(particles) && texture_storage->render_target_is_sdf_enabled(p_render_target)) { // Pass collision information. - Transform2D xform; - if (local_coords) { - xform = p_item->final_transform; - } else { - xform = p_canvas_transform_inverse; - } + Transform2D xform = p_item->final_transform; GLuint sdf_texture = texture_storage->render_target_get_sdf_texture(p_render_target); @@ -1217,26 +1220,15 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { _bind_canvas_texture(state.canvas_instance_batches[p_index].tex, state.canvas_instance_batches[p_index].filter, state.canvas_instance_batches[p_index].repeat); - // Bind the region of the UBO used by this batch. - // If region exceeds the boundary of the UBO, just ignore. - uint32_t range_bytes = data.max_instances_per_batch * sizeof(InstanceData); - if (state.canvas_instance_batches[p_index].start >= (data.max_instances_per_ubo - 1) * sizeof(InstanceData)) { - return; - } else if (state.canvas_instance_batches[p_index].start >= (data.max_instances_per_ubo - data.max_instances_per_batch) * sizeof(InstanceData)) { - // If we have less than a full batch at the end, we can just draw it anyway. - // OpenGL will complain about the UBO being smaller than expected, but it should render fine. - range_bytes = (data.max_instances_per_ubo - 1) * sizeof(InstanceData) - state.canvas_instance_batches[p_index].start; - } - - uint32_t range_start = state.canvas_instance_batches[p_index].start; - glBindBufferRange(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].ubo, range_start, range_bytes); - switch (state.canvas_instance_batches[p_index].command_type) { case Item::Command::TYPE_RECT: case Item::Command::TYPE_NINEPATCH: { glBindVertexArray(data.indexed_quad_array); - glDrawElements(GL_TRIANGLES, state.canvas_instance_batches[p_index].instance_count * 6, GL_UNSIGNED_INT, 0); - glBindBuffer(GL_UNIFORM_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); + _enable_attributes(range_start, false); + + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, state.canvas_instance_batches[p_index].instance_count); glBindVertexArray(0); } break; @@ -1248,18 +1240,21 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { ERR_FAIL_COND(!pb); glBindVertexArray(pb->vertex_array); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + + uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); + _enable_attributes(range_start, false); if (pb->color_disabled && pb->color != Color(1.0, 1.0, 1.0, 1.0)) { glVertexAttrib4f(RS::ARRAY_COLOR, pb->color.r, pb->color.g, pb->color.b, pb->color.a); } if (pb->index_buffer != 0) { - glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr); + glDrawElementsInstanced(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr, 1); } else { - glDrawArrays(prim[polygon->primitive], 0, pb->count); + glDrawArraysInstanced(prim[polygon->primitive], 0, pb->count, 1); } glBindVertexArray(0); - glBindBuffer(GL_UNIFORM_BUFFER, 0); if (pb->color_disabled && pb->color != Color(1.0, 1.0, 1.0, 1.0)) { // Reset so this doesn't pollute other draw calls. @@ -1269,14 +1264,16 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { case Item::Command::TYPE_PRIMITIVE: { glBindVertexArray(data.canvas_quad_array); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); + _enable_attributes(range_start, true); + const GLenum primitive[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLES }; int instance_count = state.canvas_instance_batches[p_index].instance_count; - if (instance_count > 1) { + ERR_FAIL_COND(instance_count <= 0); + if (instance_count >= 1) { glDrawArraysInstanced(primitive[state.canvas_instance_batches[p_index].primitive_points], 0, state.canvas_instance_batches[p_index].primitive_points, instance_count); - } else { - glDrawArrays(primitive[state.canvas_instance_batches[p_index].primitive_points], 0, state.canvas_instance_batches[p_index].primitive_points); } - glBindBuffer(GL_UNIFORM_BUFFER, 0); } break; @@ -1370,6 +1367,11 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0); bool use_index_buffer = false; glBindVertexArray(vertex_array_gl); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + + uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); + _enable_attributes(range_start, false, instance_count); + if (index_array_gl != 0) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl); use_index_buffer = true; @@ -1396,20 +1398,13 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { } GLenum primitive_gl = prim[int(primitive)]; - if (instance_count == 1) { - if (use_index_buffer) { - glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0); - } else { - glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface)); - } - } else if (instance_count > 1) { - if (use_index_buffer) { - glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count); - } else { - glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count); - } - } + if (use_index_buffer) { + glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count); + } else { + glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); if (instance_count > 1) { glDisableVertexAttribArray(5); @@ -1429,7 +1424,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { } void RasterizerCanvasGLES3::_add_to_batch(uint32_t &r_index, bool &r_batch_broken) { - if (r_index >= data.max_instances_per_ubo - 1) { + if (r_index >= data.max_instances_per_buffer - 1) { ERR_PRINT_ONCE("Trying to draw too many items. Please increase maximum number of items in the project settings 'rendering/gl_compatibility/item_buffer_size'"); return; } @@ -1457,27 +1452,25 @@ void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken, uint32_t &r_index) // Copy the properties of the current batch, we will manually update the things that changed. Batch new_batch = state.canvas_instance_batches[state.current_batch_index]; new_batch.instance_count = 0; - new_batch.start = state.canvas_instance_batches[state.current_batch_index].start + state.canvas_instance_batches[state.current_batch_index].instance_count * sizeof(InstanceData); + new_batch.start = state.canvas_instance_batches[state.current_batch_index].start + state.canvas_instance_batches[state.current_batch_index].instance_count; state.current_batch_index++; state.canvas_instance_batches.push_back(new_batch); - _align_instance_data_buffer(r_index); } -bool RasterizerCanvasGLES3::_bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization) { - if (p_material_data) { - if (p_material_data->shader_data->version.is_valid() && p_material_data->shader_data->valid) { - // Bind uniform buffer and textures - p_material_data->bind_uniforms(); - return GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(p_material_data->shader_data->version, p_variant, p_specialization); - } else { - return GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); - } - } else { - return GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); +void RasterizerCanvasGLES3::_enable_attributes(uint32_t p_start, bool p_primitive, uint32_t p_rate) { + uint32_t split = p_primitive ? 11 : 12; + for (uint32_t i = 6; i < split; i++) { + glEnableVertexAttribArray(i); + glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, sizeof(InstanceData), CAST_INT_TO_UCHAR_PTR(p_start + (i - 6) * 4 * sizeof(float))); + glVertexAttribDivisor(i, p_rate); + } + for (uint32_t i = split; i <= 13; i++) { + glEnableVertexAttribArray(i); + glVertexAttribIPointer(i, 4, GL_UNSIGNED_INT, sizeof(InstanceData), CAST_INT_TO_UCHAR_PTR(p_start + (i - 6) * 4 * sizeof(float))); + glVertexAttribDivisor(i, p_rate); } } - RID RasterizerCanvasGLES3::light_create() { CanvasLight canvas_light; return canvas_light_owner.make_rid(canvas_light); @@ -2102,6 +2095,9 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe if (t) { ERR_FAIL_COND(!t->canvas_texture); ct = t->canvas_texture; + if (t->render_target) { + t->render_target->used_in_frame = true; + } } else { ct = texture_storage->get_canvas_texture(p_texture); } @@ -2129,6 +2125,9 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe glBindTexture(GL_TEXTURE_2D, texture->tex_id); texture->gl_set_filter(filter); texture->gl_set_repeat(repeat); + if (texture->render_target) { + texture->render_target->used_in_frame = true; + } } GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map); @@ -2142,6 +2141,9 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe glBindTexture(GL_TEXTURE_2D, normal_map->tex_id); normal_map->gl_set_filter(filter); normal_map->gl_set_repeat(repeat); + if (normal_map->render_target) { + normal_map->render_target->used_in_frame = true; + } } GLES3::Texture *specular_map = texture_storage->get_texture(ct->specular); @@ -2155,6 +2157,9 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe glBindTexture(GL_TEXTURE_2D, specular_map->tex_id); specular_map->gl_set_filter(filter); specular_map->gl_set_repeat(repeat); + if (specular_map->render_target) { + specular_map->render_target->used_in_frame = true; + } } } @@ -2416,8 +2421,8 @@ void RasterizerCanvasGLES3::_allocate_instance_data_buffer() { GLuint new_buffers[3]; glGenBuffers(3, new_buffers); // Batch UBO. - glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[0]); - glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, new_buffers[0]); + glBufferData(GL_ARRAY_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); // Light uniform buffer. glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[1]); glBufferData(GL_UNIFORM_BUFFER, sizeof(LightUniform) * data.max_lights_per_render, nullptr, GL_STREAM_DRAW); @@ -2427,36 +2432,16 @@ void RasterizerCanvasGLES3::_allocate_instance_data_buffer() { state.current_buffer = (state.current_buffer + 1); DataBuffer db; - db.ubo = new_buffers[0]; + db.buffer = new_buffers[0]; db.light_ubo = new_buffers[1]; db.state_ubo = new_buffers[2]; db.last_frame_used = RSG::rasterizer->get_frame_number(); state.canvas_instance_data_buffers.insert(state.current_buffer, db); state.current_buffer = state.current_buffer % state.canvas_instance_data_buffers.size(); + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_UNIFORM_BUFFER, 0); } -// Batch start positions need to be aligned to the device's GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT. -// This needs to be called anytime a new batch is created. -void RasterizerCanvasGLES3::_align_instance_data_buffer(uint32_t &r_index) { - if (GLES3::Config::get_singleton()->uniform_buffer_offset_alignment > int(sizeof(InstanceData))) { - uint32_t offset = state.canvas_instance_batches[state.current_batch_index].start % GLES3::Config::get_singleton()->uniform_buffer_offset_alignment; - if (offset > 0) { - // uniform_buffer_offset_alignment can be 4, 16, 32, or 256. Our instance batches are 128 bytes. - // Accordingly, this branch is only triggered if we are 128 bytes off. - uint32_t offset_bytes = GLES3::Config::get_singleton()->uniform_buffer_offset_alignment - offset; - state.canvas_instance_batches[state.current_batch_index].start += offset_bytes; - // Offset the instance array so it stays in sync with batch start points. - // This creates gaps in the instance buffer with wasted space, but we can't help it. - r_index += offset_bytes / sizeof(InstanceData); - if (r_index > 0) { - // In this case we need to copy over the basic data. - state.instance_data_array[r_index] = state.instance_data_array[r_index - 1]; - } - } - } -} - void RasterizerCanvasGLES3::set_time(double p_time) { state.time = p_time; } @@ -2601,12 +2586,12 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { data.max_instances_per_batch = 128; } else { data.max_lights_per_render = 256; - data.max_instances_per_batch = 512; + data.max_instances_per_batch = 2048; } // Reserve 3 Uniform Buffers for instance data Frame N, N+1 and N+2 - data.max_instances_per_ubo = MAX(data.max_instances_per_batch, uint32_t(GLOBAL_GET("rendering/gl_compatibility/item_buffer_size"))); - data.max_instance_buffer_size = data.max_instances_per_ubo * sizeof(InstanceData); // 16,384 instances * 128 bytes = 2,097,152 bytes = 2,048 kb + data.max_instances_per_buffer = MAX(data.max_instances_per_batch, uint32_t(GLOBAL_GET("rendering/gl_compatibility/item_buffer_size"))); + data.max_instance_buffer_size = data.max_instances_per_buffer * sizeof(InstanceData); // 16,384 instances * 128 bytes = 2,097,152 bytes = 2,048 kb state.canvas_instance_data_buffers.resize(3); state.canvas_instance_batches.reserve(200); @@ -2614,8 +2599,8 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { GLuint new_buffers[3]; glGenBuffers(3, new_buffers); // Batch UBO. - glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[0]); - glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, new_buffers[0]); + glBufferData(GL_ARRAY_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); // Light uniform buffer. glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[1]); glBufferData(GL_UNIFORM_BUFFER, sizeof(LightUniform) * data.max_lights_per_render, nullptr, GL_STREAM_DRAW); @@ -2623,41 +2608,28 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); DataBuffer db; - db.ubo = new_buffers[0]; + db.buffer = new_buffers[0]; db.light_ubo = new_buffers[1]; db.state_ubo = new_buffers[2]; db.last_frame_used = 0; db.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); state.canvas_instance_data_buffers[i] = db; } + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_UNIFORM_BUFFER, 0); - state.instance_data_array = memnew_arr(InstanceData, data.max_instances_per_ubo); + state.instance_data_array = memnew_arr(InstanceData, data.max_instances_per_buffer); state.light_uniforms = memnew_arr(LightUniform, data.max_lights_per_render); { - const uint32_t no_of_instances = data.max_instances_per_batch; - + const uint32_t indices[6] = { 0, 2, 1, 3, 2, 0 }; glGenVertexArrays(1, &data.indexed_quad_array); glBindVertexArray(data.indexed_quad_array); glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices); - - const uint32_t num_indices = 6; - const uint32_t quad_indices[num_indices] = { 0, 2, 1, 3, 2, 0 }; - - const uint32_t total_indices = no_of_instances * num_indices; - uint32_t *indices = new uint32_t[total_indices]; - for (uint32_t i = 0; i < total_indices; i++) { - uint32_t quad = i / num_indices; - uint32_t quad_local = i % num_indices; - indices[i] = quad_indices[quad_local] + quad * num_indices; - } - glGenBuffers(1, &data.indexed_quad_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.indexed_quad_buffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * total_indices, indices, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * 6, indices, GL_STATIC_DRAW); glBindVertexArray(0); - delete[] indices; } String global_defines; @@ -2665,7 +2637,7 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { global_defines += "#define MAX_LIGHTS " + itos(data.max_lights_per_render) + "\n"; global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(data.max_instances_per_batch) + "\n"; - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines); + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines, 1); data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create(); shadow_render.shader.initialize(); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index bd87973404..d99a8414f7 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -245,7 +245,7 @@ public: uint32_t max_lights_per_render = 256; uint32_t max_lights_per_item = 16; uint32_t max_instances_per_batch = 512; - uint32_t max_instances_per_ubo = 16384; + uint32_t max_instances_per_buffer = 16384; uint32_t max_instance_buffer_size = 16384 * 128; } data; @@ -278,7 +278,7 @@ public: // We track them and ensure that they don't get reused until at least 2 frames have passed // to avoid the GPU stalling to wait for a resource to become available. struct DataBuffer { - GLuint ubo = 0; + GLuint buffer = 0; GLuint light_ubo = 0; GLuint state_ubo = 0; uint64_t last_frame_used = -3; @@ -359,6 +359,7 @@ public: void _add_to_batch(uint32_t &r_index, bool &r_batch_broken); void _allocate_instance_data_buffer(); void _align_instance_data_buffer(uint32_t &r_index); + void _enable_attributes(uint32_t p_start, bool p_primitive, uint32_t p_rate = 1); void set_time(double p_time); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 7b4131b3a3..75fc0c8c35 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -333,18 +333,14 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c return; } - Size2i win_size = DisplayServer::get_singleton()->screen_get_size(); + Size2i win_size = DisplayServer::get_singleton()->window_get_size(); glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, win_size.width, win_size.height); - glDisable(GL_BLEND); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); glDepthMask(GL_FALSE); - if (false) { - // if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { - glClearColor(0.0, 0.0, 0.0, 0.0); - } else { - glClearColor(p_color.r, p_color.g, p_color.b, 1.0); - } + glClearColor(p_color.r, p_color.g, p_color.b, 1.0); glClear(GL_COLOR_BUFFER_BIT); RID texture = texture_storage->texture_allocate(); @@ -370,14 +366,24 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor(); } + // Flip Y. + screenrect.position.y = win_size.y - screenrect.position.y; + screenrect.size.y = -screenrect.size.y; + + // Normalize texture coordinates to window size. + screenrect.position /= win_size; + screenrect.size /= win_size; + GLES3::Texture *t = texture_storage->get_texture(texture); - glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 1); + t->gl_set_filter(p_use_filter ? RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR : RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, t->tex_id); + copy_effects->copy_to_rect(screenrect); glBindTexture(GL_TEXTURE_2D, 0); - texture_storage->texture_free(texture); - end_frame(true); + + texture_storage->texture_free(texture); } #endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index b3c23cac47..1faecfd4d0 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1744,7 +1744,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ render_data.reflection_probes = ∅ } - bool reverse_cull = false; + bool reverse_cull = render_data.cam_transform.basis.determinant() < 0; /////////// // Fill Light lists here diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index c1c26ed963..9a90f33674 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -11,6 +11,7 @@ mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING DISABLE_LIGHTING = false USE_RGBA_SHADOWS = false +SINGLE_INSTANCE = false #[vertex] @@ -25,9 +26,74 @@ layout(location = 1) in highp vec4 instance_xform0; layout(location = 2) in highp vec4 instance_xform1; layout(location = 5) in highp uvec4 instance_color_custom_data; // Color packed into xy, custom_data packed into zw for compatibility with 3D +#endif // USE_INSTANCING + +#endif // USE_ATTRIBUTES + +#include "stdlib_inc.glsl" + +layout(location = 6) in highp vec4 attrib_A; +layout(location = 7) in highp vec4 attrib_B; +layout(location = 8) in highp vec4 attrib_C; +layout(location = 9) in highp vec4 attrib_D; +layout(location = 10) in highp vec4 attrib_E; +#ifdef USE_PRIMITIVE +layout(location = 11) in highp uvec4 attrib_F; +#else +layout(location = 11) in highp vec4 attrib_F; +#endif +layout(location = 12) in highp uvec4 attrib_G; +layout(location = 13) in highp uvec4 attrib_H; + +#define read_draw_data_world_x attrib_A.xy +#define read_draw_data_world_y attrib_A.zw +#define read_draw_data_world_ofs attrib_B.xy +#define read_draw_data_color_texture_pixel_size attrib_B.zw + +#ifdef USE_PRIMITIVE + +#define read_draw_data_point_a attrib_C.xy +#define read_draw_data_point_b attrib_C.zw +#define read_draw_data_point_c attrib_D.xy +#define read_draw_data_uv_a attrib_D.zw +#define read_draw_data_uv_b attrib_E.xy +#define read_draw_data_uv_c attrib_E.zw + +#define read_draw_data_color_a_rg attrib_F.x +#define read_draw_data_color_a_ba attrib_F.y +#define read_draw_data_color_b_rg attrib_F.z +#define read_draw_data_color_b_ba attrib_F.w +#define read_draw_data_color_c_rg attrib_G.x +#define read_draw_data_color_c_ba attrib_G.y + +#else + +#define read_draw_data_modulation attrib_C +#define read_draw_data_ninepatch_margins attrib_D +#define read_draw_data_dst_rect attrib_E +#define read_draw_data_src_rect attrib_F + #endif +#define read_draw_data_flags attrib_G.z +#define read_draw_data_specular_shininess attrib_G.w +#define read_draw_data_lights attrib_H + +// Varyings so the per-instance info can be used in the fragment shader +flat out vec4 varying_A; +flat out vec2 varying_B; +#ifndef USE_PRIMITIVE +flat out vec4 varying_C; +#ifndef USE_ATTRIBUTES +#ifdef USE_NINEPATCH + +flat out vec2 varying_D; +#endif +flat out vec4 varying_E; +#endif #endif +flat out uvec2 varying_F; +flat out uvec4 varying_G; // This needs to be outside clang-format so the ubo comment is in the right place #ifdef MATERIAL_UNIFORMS_USED @@ -39,12 +105,10 @@ layout(std140) uniform MaterialUniforms{ //ubo:4 #endif /* clang-format on */ #include "canvas_uniforms_inc.glsl" -#include "stdlib_inc.glsl" out vec2 uv_interp; out vec4 color_interp; out vec2 vertex_interp; -flat out int draw_data_instance; #ifdef USE_NINEPATCH @@ -55,35 +119,46 @@ out vec2 pixel_size_interp; #GLOBALS void main() { + varying_A = vec4(read_draw_data_world_x, read_draw_data_world_y); + varying_B = read_draw_data_color_texture_pixel_size; +#ifndef USE_PRIMITIVE + varying_C = read_draw_data_ninepatch_margins; + +#ifndef USE_ATTRIBUTES +#ifdef USE_NINEPATCH + varying_D = vec2(read_draw_data_dst_rect.z, read_draw_data_dst_rect.w); +#endif // USE_NINEPATCH + varying_E = read_draw_data_src_rect; +#endif // !USE_ATTRIBUTES +#endif // USE_PRIMITIVE + + varying_F = uvec2(read_draw_data_flags, read_draw_data_specular_shininess); + varying_G = read_draw_data_lights; + vec4 instance_custom = vec4(0.0); #ifdef USE_PRIMITIVE - draw_data_instance = gl_InstanceID; vec2 vertex; vec2 uv; vec4 color; if (gl_VertexID % 3 == 0) { - vertex = draw_data[draw_data_instance].point_a; - uv = draw_data[draw_data_instance].uv_a; - color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_a_rg), unpackHalf2x16(draw_data[draw_data_instance].color_a_ba)); + vertex = read_draw_data_point_a; + uv = read_draw_data_uv_a; + color = vec4(unpackHalf2x16(read_draw_data_color_a_rg), unpackHalf2x16(read_draw_data_color_a_ba)); } else if (gl_VertexID % 3 == 1) { - vertex = draw_data[draw_data_instance].point_b; - uv = draw_data[draw_data_instance].uv_b; - color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_b_rg), unpackHalf2x16(draw_data[draw_data_instance].color_b_ba)); + vertex = read_draw_data_point_b; + uv = read_draw_data_uv_b; + color = vec4(unpackHalf2x16(read_draw_data_color_b_rg), unpackHalf2x16(read_draw_data_color_b_ba)); } else { - vertex = draw_data[draw_data_instance].point_c; - uv = draw_data[draw_data_instance].uv_c; - color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_c_rg), unpackHalf2x16(draw_data[draw_data_instance].color_c_ba)); + vertex = read_draw_data_point_c; + uv = read_draw_data_uv_c; + color = vec4(unpackHalf2x16(read_draw_data_color_c_rg), unpackHalf2x16(read_draw_data_color_c_ba)); } #elif defined(USE_ATTRIBUTES) - draw_data_instance = gl_InstanceID; -#ifdef USE_INSTANCING - draw_data_instance = 0; -#endif vec2 vertex = vertex_attrib; - vec4 color = color_attrib * draw_data[draw_data_instance].modulation; + vec4 color = color_attrib * read_draw_data_modulation; vec2 uv = uv_attrib; #ifdef USE_INSTANCING @@ -93,30 +168,29 @@ void main() { #endif #else - draw_data_instance = gl_VertexID / 6; vec2 vertex_base_arr[6] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0)); vec2 vertex_base = vertex_base_arr[gl_VertexID % 6]; - vec2 uv = draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw) * ((draw_data[draw_data_instance].flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy); - vec4 color = draw_data[draw_data_instance].modulation; - vec2 vertex = draw_data[draw_data_instance].dst_rect.xy + abs(draw_data[draw_data_instance].dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data[draw_data_instance].src_rect.zw, vec2(0.0, 0.0))); + vec2 uv = read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) * ((read_draw_data_flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy); + vec4 color = read_draw_data_modulation; + vec2 vertex = read_draw_data_dst_rect.xy + abs(read_draw_data_dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(read_draw_data_src_rect.zw, vec2(0.0, 0.0))); #endif - mat4 model_matrix = mat4(vec4(draw_data[draw_data_instance].world_x, 0.0, 0.0), vec4(draw_data[draw_data_instance].world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data[draw_data_instance].world_ofs, 0.0, 1.0)); + mat4 model_matrix = mat4(vec4(read_draw_data_world_x, 0.0, 0.0), vec4(read_draw_data_world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(read_draw_data_world_ofs, 0.0, 1.0)); #ifdef USE_INSTANCING model_matrix = model_matrix * transpose(mat4(instance_xform0, instance_xform1, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))); #endif // USE_INSTANCING #if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) - if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_PARTICLES)) { + if (bool(read_draw_data_flags & FLAGS_USING_PARTICLES)) { //scale by texture size - vertex /= draw_data[draw_data_instance].color_texture_pixel_size; + vertex /= read_draw_data_color_texture_pixel_size; } #endif - vec2 color_texture_pixel_size = draw_data[draw_data_instance].color_texture_pixel_size.xy; + vec2 color_texture_pixel_size = read_draw_data_color_texture_pixel_size; #ifdef USE_POINT_SIZE float point_size = 1.0; @@ -126,7 +200,7 @@ void main() { } #ifdef USE_NINEPATCH - pixel_size_interp = abs(draw_data[draw_data_instance].dst_rect.zw) * vertex_base; + pixel_size_interp = abs(read_draw_data_dst_rect.zw) * vertex_base; #endif #if !defined(SKIP_TRANSFORM_USED) @@ -159,6 +233,46 @@ void main() { #include "canvas_uniforms_inc.glsl" #include "stdlib_inc.glsl" +in vec2 uv_interp; +in vec2 vertex_interp; +in vec4 color_interp; + +#ifdef USE_NINEPATCH + +in vec2 pixel_size_interp; + +#endif + +// Can all be flat as they are the same for the whole batched instance +flat in vec4 varying_A; +flat in vec2 varying_B; +#define read_draw_data_world_x varying_A.xy +#define read_draw_data_world_y varying_A.zw +#define read_draw_data_color_texture_pixel_size varying_B + +#ifndef USE_PRIMITIVE +flat in vec4 varying_C; +#define read_draw_data_ninepatch_margins varying_C + +#ifndef USE_ATTRIBUTES +#ifdef USE_NINEPATCH + +flat in vec2 varying_D; +#define read_draw_data_dst_rect_z varying_D.x +#define read_draw_data_dst_rect_w varying_D.y +#endif + +flat in vec4 varying_E; +#define read_draw_data_src_rect varying_E +#endif // USE_ATTRIBUTES +#endif // USE_PRIMITIVE + +flat in uvec2 varying_F; +flat in uvec4 varying_G; +#define read_draw_data_flags varying_F.x +#define read_draw_data_specular_shininess varying_F.y +#define read_draw_data_lights varying_G + #ifndef DISABLE_LIGHTING uniform sampler2D atlas_texture; //texunit:-2 uniform sampler2D shadow_atlas_texture; //texunit:-3 @@ -170,17 +284,6 @@ uniform sampler2D specular_texture; //texunit:-7 uniform sampler2D color_texture; //texunit:0 -in vec2 uv_interp; -in vec4 color_interp; -in vec2 vertex_interp; -flat in int draw_data_instance; - -#ifdef USE_NINEPATCH - -in vec2 pixel_size_interp; - -#endif - layout(location = 0) out vec4 frag_color; #ifdef MATERIAL_UNIFORMS_USED @@ -366,7 +469,7 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo } else if (pixel >= draw_size - margin_end) { return (tex_size - (draw_size - pixel)) * tex_pixel_size; } else { - if (!bool(draw_data[draw_data_instance].flags & FLAGS_NINEPACH_DRAW_CENTER)) { + if (!bool(read_draw_data_flags & FLAGS_NINEPACH_DRAW_CENTER)) { draw_center--; } @@ -414,28 +517,26 @@ void main() { int draw_center = 2; uv = vec2( - map_ninepatch_axis(pixel_size_interp.x, abs(draw_data[draw_data_instance].dst_rect.z), draw_data[draw_data_instance].color_texture_pixel_size.x, draw_data[draw_data_instance].ninepatch_margins.x, draw_data[draw_data_instance].ninepatch_margins.z, int(draw_data[draw_data_instance].flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center), - map_ninepatch_axis(pixel_size_interp.y, abs(draw_data[draw_data_instance].dst_rect.w), draw_data[draw_data_instance].color_texture_pixel_size.y, draw_data[draw_data_instance].ninepatch_margins.y, draw_data[draw_data_instance].ninepatch_margins.w, int(draw_data[draw_data_instance].flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center)); + map_ninepatch_axis(pixel_size_interp.x, abs(read_draw_data_dst_rect_z), read_draw_data_color_texture_pixel_size.x, read_draw_data_ninepatch_margins.x, read_draw_data_ninepatch_margins.z, int(read_draw_data_flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center), + map_ninepatch_axis(pixel_size_interp.y, abs(read_draw_data_dst_rect_w), read_draw_data_color_texture_pixel_size.y, read_draw_data_ninepatch_margins.y, read_draw_data_ninepatch_margins.w, int(read_draw_data_flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center)); if (draw_center == 0) { color.a = 0.0; } - uv = uv * draw_data[draw_data_instance].src_rect.zw + draw_data[draw_data_instance].src_rect.xy; //apply region if needed + uv = uv * read_draw_data_src_rect.zw + read_draw_data_src_rect.xy; //apply region if needed #endif - if (bool(draw_data[draw_data_instance].flags & FLAGS_CLIP_RECT_UV)) { - uv = clamp(uv, draw_data[draw_data_instance].src_rect.xy, draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw)); + if (bool(read_draw_data_flags & FLAGS_CLIP_RECT_UV)) { + uv = clamp(uv, read_draw_data_src_rect.xy, read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw)); } #endif #ifndef USE_PRIMITIVE - if (bool(draw_data[draw_data_instance].flags & FLAGS_USE_MSDF)) { - float px_range = draw_data[draw_data_instance].ninepatch_margins.x; - float outline_thickness = draw_data[draw_data_instance].ninepatch_margins.y; - //float reserved1 = draw_data[draw_data_instance].ninepatch_margins.z; - //float reserved2 = draw_data[draw_data_instance].ninepatch_margins.w; + if (bool(read_draw_data_flags & FLAGS_USE_MSDF)) { + float px_range = read_draw_data_ninepatch_margins.x; + float outline_thickness = read_draw_data_ninepatch_margins.y; vec4 msdf_sample = texture(color_texture, uv); vec2 msdf_size = vec2(textureSize(color_texture, 0)); @@ -451,7 +552,7 @@ void main() { float a = clamp(d * px_size + 0.5, 0.0, 1.0); color.a = a * color.a; } - } else if (bool(draw_data[draw_data_instance].flags & FLAGS_USE_LCD)) { + } else if (bool(read_draw_data_flags & FLAGS_USE_LCD)) { vec4 lcd_sample = texture(color_texture, uv); if (lcd_sample.a == 1.0) { color.rgb = lcd_sample.rgb * color.a; @@ -465,7 +566,7 @@ void main() { color *= texture(color_texture, uv); } - uint light_count = (draw_data[draw_data_instance].flags >> uint(FLAGS_LIGHT_COUNT_SHIFT)) & uint(0xF); //max 16 lights + uint light_count = (read_draw_data_flags >> uint(FLAGS_LIGHT_COUNT_SHIFT)) & uint(0xF); //max 16 lights bool using_light = light_count > 0u || directional_light_count > 0u; vec3 normal; @@ -476,7 +577,7 @@ void main() { bool normal_used = false; #endif - if (normal_used || (using_light && bool(draw_data[draw_data_instance].flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) { + if (normal_used || (using_light && bool(read_draw_data_flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) { normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0); normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); normal_used = true; @@ -493,9 +594,9 @@ void main() { bool specular_shininess_used = false; #endif - if (specular_shininess_used || (using_light && normal_used && bool(draw_data[draw_data_instance].flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) { + if (specular_shininess_used || (using_light && normal_used && bool(read_draw_data_flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) { specular_shininess = texture(specular_texture, uv); - specular_shininess *= godot_unpackUnorm4x8(draw_data[draw_data_instance].specular_shininess); + specular_shininess *= godot_unpackUnorm4x8(read_draw_data_specular_shininess); specular_shininess_used = true; } else { specular_shininess = vec4(1.0); @@ -507,7 +608,7 @@ void main() { vec2 screen_uv = vec2(0.0); #endif - vec2 color_texture_pixel_size = draw_data[draw_data_instance].color_texture_pixel_size.xy; + vec2 color_texture_pixel_size = read_draw_data_color_texture_pixel_size.xy; vec3 light_vertex = vec3(vertex, 0.0); vec2 shadow_vertex = vertex; @@ -529,7 +630,7 @@ void main() { if (normal_used) { //convert by item transform - normal.xy = mat2(normalize(draw_data[draw_data_instance].world_x), normalize(draw_data[draw_data_instance].world_y)) * normal.xy; + normal.xy = mat2(normalize(read_draw_data_world_x), normalize(read_draw_data_world_y)) * normal.xy; //convert by canvas transform normal = normalize((canvas_normal_transform * vec4(normal, 0.0)).xyz); } @@ -591,15 +692,15 @@ void main() { uint light_base; if (i < 8u) { if (i < 4u) { - light_base = draw_data[draw_data_instance].lights[0]; + light_base = read_draw_data_lights[0]; } else { - light_base = draw_data[draw_data_instance].lights[1]; + light_base = read_draw_data_lights[1]; } } else { if (i < 12u) { - light_base = draw_data[draw_data_instance].lights[2]; + light_base = read_draw_data_lights[2]; } else { - light_base = draw_data[draw_data_instance].lights[3]; + light_base = read_draw_data_lights[3]; } } light_base >>= (i & 3u) * 8u; diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl index dd5ebecb1a..f65558f042 100644 --- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl +++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl @@ -27,38 +27,6 @@ #define FLAGS_USE_MSDF uint(1 << 28) #define FLAGS_USE_LCD uint(1 << 29) -// must be always 128 bytes long -struct DrawData { - vec2 world_x; - vec2 world_y; - vec2 world_ofs; - vec2 color_texture_pixel_size; -#ifdef USE_PRIMITIVE - vec2 point_a; - vec2 point_b; - vec2 point_c; - vec2 uv_a; - vec2 uv_b; - vec2 uv_c; - uint color_a_rg; - uint color_a_ba; - uint color_b_rg; - uint color_b_ba; - uint color_c_rg; - uint color_c_ba; -#else - vec4 modulation; - vec4 ninepatch_margins; - vec4 dst_rect; //for built-in rect and UV - vec4 src_rect; - uint pad; - uint pad2; -#endif - uint flags; - uint specular_shininess; - uvec4 lights; -}; - layout(std140) uniform GlobalShaderUniformData { //ubo:1 vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; @@ -116,7 +84,3 @@ layout(std140) uniform LightData { //ubo:2 Light light_array[MAX_LIGHTS]; }; #endif // DISABLE_LIGHTING -layout(std140) uniform DrawDataInstances { //ubo:3 - - DrawData draw_data[MAX_DRAW_DATA_INSTANCES]; -}; diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index 9b496c0999..46dc2b806e 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -90,8 +90,6 @@ Config::Config() { glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_offset_alignment); - support_anisotropic_filter = extensions.has("GL_EXT_texture_filter_anisotropic"); if (support_anisotropic_filter) { glGetFloatv(_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic_level); diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index 87202fde84..1a93b4b8ef 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -67,8 +67,6 @@ public: int max_renderable_lights = 0; int max_lights_per_object = 0; - int uniform_buffer_offset_alignment = 0; - // TODO implement wireframe in OpenGL // bool generate_wireframes; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 19e841f466..ebafdc88d8 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -923,6 +923,104 @@ _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, } /////////////////////////////////////////////////////////////////////////// +// ShaderData + +void ShaderData::set_path_hint(const String &p_hint) { + path = p_hint; +} + +void ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { + if (!p_texture.is_valid()) { + if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { + default_texture_params[p_name].erase(p_index); + + if (default_texture_params[p_name].is_empty()) { + default_texture_params.erase(p_name); + } + } + } else { + if (!default_texture_params.has(p_name)) { + default_texture_params[p_name] = HashMap<int, RID>(); + } + default_texture_params[p_name][p_index] = p_texture; + } +} + +Variant ShaderData::get_default_parameter(const StringName &p_parameter) const { + if (uniforms.has(p_parameter)) { + ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); + } + return Variant(); +} + +void ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { + SortArray<Pair<StringName, int>, ShaderLanguage::UniformOrderComparator> sorter; + LocalVector<Pair<StringName, int>> filtered_uniforms; + + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { + continue; + } + if (E.value.texture_order >= 0) { + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000)); + } else { + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order)); + } + } + int uniform_count = filtered_uniforms.size(); + sorter.sort(filtered_uniforms.ptr(), uniform_count); + + String last_group; + for (int i = 0; i < uniform_count; i++) { + const StringName &uniform_name = filtered_uniforms[i].first; + const ShaderLanguage::ShaderNode::Uniform &uniform = uniforms[uniform_name]; + + String group = uniform.group; + if (!uniform.subgroup.is_empty()) { + group += "::" + uniform.subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniform); + pi.name = uniform_name; + p_param_list->push_back(pi); + } +} + +void ShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + RendererMaterialStorage::InstanceShaderParam p; + p.info = ShaderLanguage::uniform_to_property_info(E.value); + p.info.name = E.key; //supply name + p.index = E.value.instance_index; + p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); + p_param_list->push_back(p); + } +} + +bool ShaderData::is_parameter_texture(const StringName &p_param) const { + if (!uniforms.has(p_param)) { + return false; + } + + return uniforms[p_param].texture_order >= 0; +} + +/////////////////////////////////////////////////////////////////////////// // MaterialData // Look up table to translate ShaderLanguage::DataType to GL_TEXTURE_* @@ -2806,10 +2904,6 @@ void MaterialStorage::material_update_dependency(RID p_material, DependencyTrack /* Canvas Shader Data */ -void CanvasShaderData::set_path_hint(const String &p_path) { - path = p_path; -} - void CanvasShaderData::set_code(const String &p_code) { // compile the shader @@ -2890,85 +2984,6 @@ void CanvasShaderData::set_code(const String &p_code) { valid = true; } -void CanvasShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void CanvasShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - HashMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { - continue; - } - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void CanvasShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool CanvasShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool CanvasShaderData::is_animated() const { return false; } @@ -2977,15 +2992,6 @@ bool CanvasShaderData::casts_shadows() const { return false; } -Variant CanvasShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode CanvasShaderData::get_native_source_code() const { return MaterialStorage::get_singleton()->shaders.canvas_shader.version_get_native_source_code(version); } @@ -3021,6 +3027,9 @@ void CanvasMaterialData::bind_uniforms() { Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); glActiveTexture(GL_TEXTURE1 + ti); // Start at GL_TEXTURE1 because texture slot 0 is used by the base texture glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + if (texture->render_target) { + texture->render_target->used_in_frame = true; + } // Set sampler state here as the same texture can be used in multiple places with different flags // Need to convert sampler state from ShaderLanguage::Texture* to RS::CanvasItemTexture* @@ -3044,10 +3053,6 @@ GLES3::MaterialData *GLES3::_create_canvas_material_func(ShaderData *p_shader) { //////////////////////////////////////////////////////////////////////////////// // SKY SHADER -void SkyShaderData::set_path_hint(const String &p_path) { - path = p_path; -} - void SkyShaderData::set_code(const String &p_code) { //compile @@ -3138,83 +3143,6 @@ void SkyShaderData::set_code(const String &p_code) { valid = true; } -void SkyShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void SkyShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - RBMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void SkyShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool SkyShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool SkyShaderData::is_animated() const { return false; } @@ -3223,15 +3151,6 @@ bool SkyShaderData::casts_shadows() const { return false; } -Variant SkyShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode SkyShaderData::get_native_source_code() const { return MaterialStorage::get_singleton()->shaders.sky_shader.version_get_native_source_code(version); } @@ -3278,6 +3197,9 @@ void SkyMaterialData::bind_uniforms() { Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); glActiveTexture(GL_TEXTURE0 + ti); glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + if (texture->render_target) { + texture->render_target->used_in_frame = true; + } // Set sampler state here as the same texture can be used in multiple places with different flags // Need to convert sampler state from ShaderLanguage::Texture* to RS::CanvasItemTexture* @@ -3291,10 +3213,6 @@ void SkyMaterialData::bind_uniforms() { //////////////////////////////////////////////////////////////////////////////// // Scene SHADER -void SceneShaderData::set_path_hint(const String &p_path) { - path = p_path; -} - void SceneShaderData::set_code(const String &p_code) { //compile @@ -3471,86 +3389,6 @@ void SceneShaderData::set_code(const String &p_code) { valid = true; } -void SceneShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void SceneShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - RBMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { - continue; - } - - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void SceneShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool SceneShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool SceneShaderData::is_animated() const { return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex); } @@ -3563,15 +3401,6 @@ bool SceneShaderData::casts_shadows() const { return !has_alpha || (uses_depth_pre_pass && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED)); } -Variant SceneShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode SceneShaderData::get_native_source_code() const { return MaterialStorage::get_singleton()->shaders.scene_shader.version_get_native_source_code(version); } @@ -3624,6 +3453,9 @@ void SceneMaterialData::bind_uniforms() { Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); glActiveTexture(GL_TEXTURE0 + ti); glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + if (texture->render_target) { + texture->render_target->used_in_frame = true; + } // Set sampler state here as the same texture can be used in multiple places with different flags // Need to convert sampler state from ShaderLanguage::Texture* to RS::CanvasItemTexture* @@ -3636,10 +3468,6 @@ void SceneMaterialData::bind_uniforms() { /* Particles SHADER */ -void ParticlesShaderData::set_path_hint(const String &p_path) { - path = p_path; -} - void ParticlesShaderData::set_code(const String &p_code) { //compile @@ -3696,83 +3524,6 @@ void ParticlesShaderData::set_code(const String &p_code) { valid = true; } -void ParticlesShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void ParticlesShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - HashMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void ParticlesShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool ParticlesShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool ParticlesShaderData::is_animated() const { return false; } @@ -3781,15 +3532,6 @@ bool ParticlesShaderData::casts_shadows() const { return false; } -Variant ParticlesShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode ParticlesShaderData::get_native_source_code() const { return MaterialStorage::get_singleton()->shaders.particles_process_shader.version_get_native_source_code(version); } @@ -3827,8 +3569,11 @@ void ParticleProcessMaterialData::bind_uniforms() { ShaderCompiler::GeneratedCode::Texture *texture_uniforms = shader_data->texture_uniforms.ptrw(); for (int ti = 0; ti < texture_cache.size(); ti++) { Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); - glActiveTexture(GL_TEXTURE1 + ti); // Start at GL_TEXTURE1 becuase texture slot 0 is reserved for the heightmap texture. + glActiveTexture(GL_TEXTURE1 + ti); // Start at GL_TEXTURE1 because texture slot 0 is reserved for the heightmap texture. glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + if (texture->render_target) { + texture->render_target->used_in_frame = true; + } // Set sampler state here as the same texture can be used in multiple places with different flags // Need to convert sampler state from ShaderLanguage::Texture* to RS::CanvasItemTexture* diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 8ae5e5eb9c..9965d23070 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -53,16 +53,20 @@ namespace GLES3 { /* Shader Structs */ struct ShaderData { - virtual void set_code(const String &p_Code) = 0; - virtual void set_path_hint(const String &p_hint) = 0; - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) = 0; - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const = 0; + String path; + HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + HashMap<StringName, HashMap<int, RID>> default_texture_params; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const = 0; - virtual bool is_parameter_texture(const StringName &p_param) const = 0; + virtual void set_path_hint(const String &p_hint); + virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); + virtual Variant get_default_parameter(const StringName &p_parameter) const; + virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; + virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; + virtual bool is_parameter_texture(const StringName &p_param) const; + + virtual void set_code(const String &p_Code) = 0; virtual bool is_animated() const = 0; virtual bool casts_shadows() const = 0; - virtual Variant get_default_parameter(const StringName &p_parameter) const = 0; virtual RS::ShaderNativeSourceCode get_native_source_code() const { return RS::ShaderNativeSourceCode(); } virtual ~ShaderData() {} @@ -149,17 +153,14 @@ struct CanvasShaderData : public ShaderData { bool valid; RID version; - String path; BlendMode blend_mode = BLEND_MODE_MIX; - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; bool uses_screen_texture = false; bool uses_screen_texture_mipmaps = false; @@ -167,15 +168,8 @@ struct CanvasShaderData : public ShaderData { bool uses_time = false; virtual void set_code(const String &p_Code); - virtual void set_path_hint(const String &p_hint); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; CanvasShaderData(); @@ -202,15 +196,12 @@ struct SkyShaderData : public ShaderData { bool valid; RID version; - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size; - String path; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; bool uses_time; bool uses_position; @@ -219,14 +210,8 @@ struct SkyShaderData : public ShaderData { bool uses_light; virtual void set_code(const String &p_Code); - virtual void set_path_hint(const String &p_hint); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; SkyShaderData(); virtual ~SkyShaderData(); @@ -284,16 +269,12 @@ struct SceneShaderData : public ShaderData { bool valid; RID version; - String path; - - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; BlendMode blend_mode; AlphaAntiAliasing alpha_antialiasing_mode; @@ -343,15 +324,8 @@ struct SceneShaderData : public ShaderData { uint32_t index = 0; virtual void set_code(const String &p_Code); - virtual void set_path_hint(const String &p_hint); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; SceneShaderData(); @@ -386,15 +360,12 @@ struct ParticlesShaderData : public ShaderData { RID version; bool uses_collision = false; - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size = 0; - String path; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; bool uses_time = false; @@ -402,14 +373,8 @@ struct ParticlesShaderData : public ShaderData { uint32_t userdata_count = 0; virtual void set_code(const String &p_Code); - virtual void set_path_hint(const String &p_hint); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; ParticlesShaderData() {} diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 5bbbc7b91b..d31ac4d413 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -323,27 +323,8 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) } if (mesh->surface_count == 0) { - mesh->bone_aabbs = p_surface.bone_aabbs; mesh->aabb = p_surface.aabb; } else { - if (mesh->bone_aabbs.size() < p_surface.bone_aabbs.size()) { - // ArrayMesh::_surface_set_data only allocates bone_aabbs up to max_bone - // Each surface may affect different numbers of bones. - mesh->bone_aabbs.resize(p_surface.bone_aabbs.size()); - } - for (int i = 0; i < p_surface.bone_aabbs.size(); i++) { - const AABB &bone = p_surface.bone_aabbs[i]; - if (bone.has_volume()) { - AABB &mesh_bone = mesh->bone_aabbs.write[i]; - if (mesh_bone != AABB()) { - // Already initialized, merge AABBs. - mesh_bone.merge_with(bone); - } else { - // Not yet initialized, copy the bone AABB. - mesh_bone = bone; - } - } - } mesh->aabb.merge_with(p_surface.aabb); } @@ -528,7 +509,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); - if (!skeleton || skeleton->size == 0) { + if (!skeleton || skeleton->size == 0 || mesh->skeleton_aabb_version == skeleton->version) { return mesh->aabb; } @@ -622,6 +603,8 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { } } + mesh->aabb = aabb; + mesh->skeleton_aabb_version = skeleton->version; return aabb; } diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 0f30814928..cbbba0d03e 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -120,12 +120,11 @@ struct Mesh { Surface **surfaces = nullptr; uint32_t surface_count = 0; - Vector<AABB> bone_aabbs; - bool has_bone_weights = false; AABB aabb; AABB custom_aabb; + uint64_t skeleton_aabb_version = 0; Vector<RID> material_cache; diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp index 1a0d97df01..e158a3e90c 100644 --- a/drivers/gles3/storage/particles_storage.cpp +++ b/drivers/gles3/storage/particles_storage.cpp @@ -53,7 +53,7 @@ ParticlesStorage::ParticlesStorage() { { String global_defines; global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now - material_storage->shaders.particles_process_shader.initialize(global_defines); + material_storage->shaders.particles_process_shader.initialize(global_defines, 1); } { // default material and shader for particles shader @@ -528,6 +528,15 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta //2D collision Transform2D xform = p_particles->sdf_collision_transform; //will use dotproduct manually so invert beforehand + + if (!p_particles->use_local_coords) { + Transform2D emission; + emission.columns[0] = Vector2(p_particles->emission_transform.basis.get_column(0).x, p_particles->emission_transform.basis.get_column(0).y); + emission.columns[1] = Vector2(p_particles->emission_transform.basis.get_column(1).x, p_particles->emission_transform.basis.get_column(1).y); + emission.set_origin(Vector2(p_particles->emission_transform.origin.x, p_particles->emission_transform.origin.y)); + xform = xform * emission.affine_inverse(); + } + Transform2D revert = xform.affine_inverse(); frame_params.collider_count = 1; frame_params.colliders[0].transform[0] = xform.columns[0][0]; diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 99908d197a..e6aa6a39c1 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1606,9 +1606,9 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { return; } - if (rt->overridden.color.is_valid()) { - texture->is_render_target = true; - } else { + texture->is_render_target = true; + texture->render_target = rt; + if (rt->overridden.color.is_null()) { texture->format = rt->image_format; texture->real_format = rt->image_format; texture->target = texture_target; @@ -1717,9 +1717,12 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { tex->width = 0; tex->height = 0; tex->active = false; + tex->render_target = nullptr; + tex->is_render_target = false; } } else { Texture *tex = get_texture(rt->overridden.color); + tex->render_target = nullptr; tex->is_render_target = false; } @@ -1751,7 +1754,7 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { RID TextureStorage::render_target_create() { RenderTarget render_target; - //render_target.was_used = false; + render_target.used_in_frame = false; render_target.clear_requested = false; Texture t; diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index b02a100784..711fd7b962 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -152,12 +152,10 @@ Vector<String> OS_Unix::get_video_adapter_driver_info() const { String OS_Unix::get_stdin_string(bool p_block) { if (p_block) { char buff[1024]; - String ret = stdin_buf + fgets(buff, 1024, stdin); - stdin_buf = ""; - return ret; + return String::utf8(fgets(buff, 1024, stdin)); } - return ""; + return String(); } Error OS_Unix::get_entropy(uint8_t *r_buffer, int p_bytes) { diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index ce06a52a95..68d2dd0042 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -46,8 +46,6 @@ protected: virtual void finalize_core() override; - String stdin_buf; - public: OS_Unix(); diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 45f544c02d..b982ab3e40 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -40,7 +40,6 @@ #include "drivers/vulkan/vulkan_context.h" #include "thirdparty/misc/smolv.h" -#include "thirdparty/spirv-reflect/spirv_reflect.h" //#define FORCE_FULL_BARRIER @@ -996,8 +995,11 @@ void RenderingDeviceVulkan::get_compressed_image_format_block_dimensions(DataFor case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc. - case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: - case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: + case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: { + r_w = 4; + r_h = 4; + } break; + case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: // Unsupported case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK: case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK: @@ -1008,10 +1010,16 @@ void RenderingDeviceVulkan::get_compressed_image_format_block_dimensions(DataFor case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK: case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK: case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: { + r_w = 4; + r_h = 4; + } break; case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: - case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: - case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: { + r_w = 8; + r_h = 8; + } break; + case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: // Unsupported case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK: case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK: case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK: @@ -1101,7 +1109,7 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(Data case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: - return 8; // Wrong. + return 16; default: { } } @@ -1123,6 +1131,10 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_pixel_rshift(DataFor case DATA_FORMAT_EAC_R11_UNORM_BLOCK: case DATA_FORMAT_EAC_R11_SNORM_BLOCK: return 1; + case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: { + return 2; + } default: { } } @@ -4524,14 +4536,6 @@ RID RenderingDeviceVulkan::index_array_create(RID p_index_buffer, uint32_t p_ind /**** SHADER ****/ /****************/ -static const char *shader_stage_names[RenderingDevice::SHADER_STAGE_MAX] = { - "Vertex", - "Fragment", - "TesselationControl", - "TesselationEvaluation", - "Compute" -}; - static const char *shader_uniform_names[RenderingDevice::UNIFORM_TYPE_MAX] = { "Sampler", "CombinedSampler", "Texture", "Image", "TextureBuffer", "SamplerTextureBuffer", "ImageBuffer", "UniformBuffer", "StorageBuffer", "InputAttachment" }; @@ -4562,198 +4566,6 @@ String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) { } return ret; } -#if 0 -bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLayoutBinding> > &bindings, Vector<Vector<UniformInfo> > &uniform_infos, const glslang::TObjectReflection &reflection, RenderingDevice::ShaderStage p_stage, Shader::PushConstant &push_constant, String *r_error) { - VkDescriptorSetLayoutBinding layout_binding; - UniformInfo info; - - switch (reflection.getType()->getBasicType()) { - case glslang::EbtSampler: { - //print_line("DEBUG: IsSampler"); - if (reflection.getType()->getSampler().dim == glslang::EsdBuffer) { - // Texture buffers. - if (reflection.getType()->getSampler().isCombined()) { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; - info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER; - //print_line("DEBUG: SAMPLER: texel combined"); - } else if (reflection.getType()->getSampler().isTexture()) { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; - info.type = UNIFORM_TYPE_TEXTURE_BUFFER; - //print_line("DEBUG: SAMPLER: texel alone"); - } else if (reflection.getType()->getSampler().isImage()) { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; - info.type = UNIFORM_TYPE_IMAGE_BUFFER; - //print_line("DEBUG: SAMPLER: texel buffer"); - } else { - if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' is of unsupported buffer type."; - } - return false; - } - } else if (reflection.getType()->getSampler().isCombined()) { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; - //print_line("DEBUG: SAMPLER: combined"); - } else if (reflection.getType()->getSampler().isPureSampler()) { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; - info.type = UNIFORM_TYPE_SAMPLER; - //print_line("DEBUG: SAMPLER: sampler"); - } else if (reflection.getType()->getSampler().isTexture()) { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - info.type = UNIFORM_TYPE_TEXTURE; - //print_line("DEBUG: SAMPLER: image"); - } else if (reflection.getType()->getSampler().isImage()) { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - info.type = UNIFORM_TYPE_IMAGE; - //print_line("DEBUG: SAMPLER: storage image"); - } else { - //print_line("DEBUG: sampler unknown"); - if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' is of unsupported sampler type."; - } - return false; - } - - if (reflection.getType()->isArray()) { - layout_binding.descriptorCount = reflection.getType()->getArraySizes()->getCumulativeSize(); - //print_line("DEBUG: array of size: " + itos(layout_binding.descriptorCount)); - } else { - layout_binding.descriptorCount = 1; - } - - info.length = layout_binding.descriptorCount; - - } break; - /*case glslang::EbtStruct: { - print_line("DEBUG: Struct"); - - } break;*/ - case glslang::EbtBlock: { - //print_line("DEBUG: Block"); - if (reflection.getType()->getQualifier().storage == glslang::EvqUniform) { - if (reflection.getType()->getQualifier().layoutPushConstant) { - uint32_t len = reflection.size; - if (push_constant.push_constant_size != 0 && push_constant.push_constant_size != len) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' push constants for different stages should all be the same size."; - return false; - } - push_constant.push_constant_size = len; - push_constant.push_constants_vk_stage |= shader_stage_masks[p_stage]; - return true; - } - //print_line("DEBUG: Uniform buffer"); - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - info.type = UNIFORM_TYPE_UNIFORM_BUFFER; - } else if (reflection.getType()->getQualifier().storage == glslang::EvqBuffer) { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - info.type = UNIFORM_TYPE_STORAGE_BUFFER; - //print_line("DEBUG: Storage buffer"); - } else { - if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' is of unsupported block type: (" + itos(reflection.getType()->getQualifier().storage) + ")."; - } - return false; - } - - if (reflection.getType()->isArray()) { - layout_binding.descriptorCount = reflection.getType()->getArraySizes()->getCumulativeSize(); - //print_line("DEBUG: array of size: " + itos(layout_binding.descriptorCount)); - } else { - layout_binding.descriptorCount = 1; - } - - info.length = reflection.size; - - } break; - /*case glslang::EbtReference: { - } break;*/ - /*case glslang::EbtAtomicUint: { - } break;*/ - default: { - if (reflection.getType()->getQualifier().hasOffset() || reflection.name.find(".") != std::string::npos) { - // Member of uniform block? - return true; - } - - if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' unsupported uniform type."; - } - return false; - } - } - - if (!reflection.getType()->getQualifier().hasBinding()) { - if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' lacks a binding number."; - } - return false; - } - - uint32_t set = reflection.getType()->getQualifier().hasSet() ? reflection.getType()->getQualifier().layoutSet : 0; - - if (set >= MAX_UNIFORM_SETS) { - if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."; - } - return false; - } - - if (set >= limits.maxBoundDescriptorSets) { - if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' uses a set (" + itos(set) + ") index larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."; - } - return false; - } - - uint32_t binding = reflection.getType()->getQualifier().layoutBinding; - - if (set < (uint32_t)bindings.size()) { - // Check if this already exists. - for (int i = 0; i < bindings[set].size(); i++) { - if (bindings[set][i].binding == binding) { - // Already exists, verify that it's the same type. - if (bindings[set][i].descriptorType != layout_binding.descriptorType) { - if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(binding) + " with different uniform type."; - } - return false; - } - - // Also, verify that it's the same size. - if (bindings[set][i].descriptorCount != layout_binding.descriptorCount || uniform_infos[set][i].length != info.length) { - if (r_error) { - *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(binding) + " with different uniform size."; - } - return false; - } - - // Just append stage mask and return. - bindings.write[set].write[i].stageFlags |= shader_stage_masks[p_stage]; - uniform_infos.write[set].write[i].stages |= 1 << p_stage; - return true; - } - } - } - layout_binding.binding = binding; - layout_binding.stageFlags = shader_stage_masks[p_stage]; - layout_binding.pImmutableSamplers = nullptr; // No support for this yet. - - info.stages = 1 << p_stage; - info.binding = binding; - - if (set >= (uint32_t)bindings.size()) { - bindings.resize(set + 1); - uniform_infos.resize(set + 1); - } -#if 0 - print_line("stage: " + String(shader_stage_names[p_stage]) + " set: " + itos(set) + " binding: " + itos(info.binding) + " type:" + shader_uniform_names[info.type] + " length: " + itos(info.length)); -#endif - bindings.write[set].push_back(layout_binding); - uniform_infos.write[set].push_back(info); - - return true; -} -#endif // Version 1: initial. // Version 2: Added shader name. @@ -4786,346 +4598,68 @@ struct RenderingDeviceVulkanShaderBinarySpecializationConstant { struct RenderingDeviceVulkanShaderBinaryData { uint32_t vertex_input_mask; - uint32_t fragment_outputs; - uint32_t specialization_constant_count; + uint32_t fragment_output_mask; + uint32_t specialization_constants_count; uint32_t is_compute; uint32_t compute_local_size[3]; uint32_t set_count; uint32_t push_constant_size; - uint32_t push_constants_vk_stage; + uint32_t push_constant_vk_stages_mask; uint32_t stage_count; uint32_t shader_name_len; }; Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name) { - RenderingDeviceVulkanShaderBinaryData binary_data; - binary_data.vertex_input_mask = 0; - binary_data.fragment_outputs = 0; - binary_data.specialization_constant_count = 0; - binary_data.is_compute = 0; - binary_data.compute_local_size[0] = 0; - binary_data.compute_local_size[1] = 0; - binary_data.compute_local_size[2] = 0; - binary_data.set_count = 0; - binary_data.push_constant_size = 0; - binary_data.push_constants_vk_stage = 0; + SpirvReflectionData spirv_data; + if (_reflect_spirv(p_spirv, spirv_data) != OK) { + return Vector<uint8_t>(); + } + ERR_FAIL_COND_V_MSG((uint32_t)spirv_data.uniforms.size() > limits.maxBoundDescriptorSets, Vector<uint8_t>(), + "Number of uniform sets is larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."); + + // Collect reflection data into binary data. + RenderingDeviceVulkanShaderBinaryData binary_data; Vector<Vector<RenderingDeviceVulkanShaderBinaryDataBinding>> uniform_info; // Set bindings. Vector<RenderingDeviceVulkanShaderBinarySpecializationConstant> specialization_constants; - - uint32_t stages_processed = 0; - - for (int i = 0; i < p_spirv.size(); i++) { - if (p_spirv[i].shader_stage == SHADER_STAGE_COMPUTE) { - binary_data.is_compute = true; - ERR_FAIL_COND_V_MSG(p_spirv.size() != 1, Vector<uint8_t>(), - "Compute shaders can only receive one stage, dedicated to compute."); - } - ERR_FAIL_COND_V_MSG(stages_processed & (1 << p_spirv[i].shader_stage), Vector<uint8_t>(), - "Stage " + String(shader_stage_names[p_spirv[i].shader_stage]) + " submitted more than once."); - - { - SpvReflectShaderModule module; - const uint8_t *spirv = p_spirv[i].spir_v.ptr(); - SpvReflectResult result = spvReflectCreateShaderModule(p_spirv[i].spir_v.size(), spirv, &module); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed parsing shader."); - - if (binary_data.is_compute) { - binary_data.compute_local_size[0] = module.entry_points->local_size.x; - binary_data.compute_local_size[1] = module.entry_points->local_size.y; - binary_data.compute_local_size[2] = module.entry_points->local_size.z; - } - uint32_t binding_count = 0; - result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating descriptor bindings."); - - uint32_t stage = p_spirv[i].shader_stage; - - if (binding_count > 0) { - // Parse bindings. - - Vector<SpvReflectDescriptorBinding *> bindings; - bindings.resize(binding_count); - result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, bindings.ptrw()); - - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed getting descriptor bindings."); - - for (uint32_t j = 0; j < binding_count; j++) { - const SpvReflectDescriptorBinding &binding = *bindings[j]; - - RenderingDeviceVulkanShaderBinaryDataBinding info{}; - - bool need_array_dimensions = false; - bool need_block_size = false; - bool may_be_writable = false; - - switch (binding.descriptor_type) { - case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: { - info.type = UNIFORM_TYPE_SAMPLER; - need_array_dimensions = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { - info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; - need_array_dimensions = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { - info.type = UNIFORM_TYPE_TEXTURE; - need_array_dimensions = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: { - info.type = UNIFORM_TYPE_IMAGE; - need_array_dimensions = true; - may_be_writable = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { - info.type = UNIFORM_TYPE_TEXTURE_BUFFER; - need_array_dimensions = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { - info.type = UNIFORM_TYPE_IMAGE_BUFFER; - need_array_dimensions = true; - may_be_writable = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { - info.type = UNIFORM_TYPE_UNIFORM_BUFFER; - need_block_size = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: { - info.type = UNIFORM_TYPE_STORAGE_BUFFER; - need_block_size = true; - may_be_writable = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: { - ERR_PRINT("Dynamic uniform buffer not supported."); - continue; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: { - ERR_PRINT("Dynamic storage buffer not supported."); - continue; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { - info.type = UNIFORM_TYPE_INPUT_ATTACHMENT; - need_array_dimensions = true; - } break; - case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: { - ERR_PRINT("Acceleration structure not supported."); - continue; - } break; - } - - if (need_array_dimensions) { - if (binding.array.dims_count == 0) { - info.length = 1; - } else { - for (uint32_t k = 0; k < binding.array.dims_count; k++) { - if (k == 0) { - info.length = binding.array.dims[0]; - } else { - info.length *= binding.array.dims[k]; - } - } - } - - } else if (need_block_size) { - info.length = binding.block.size; - } else { - info.length = 0; - } - - if (may_be_writable) { - info.writable = !(binding.type_description->decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) && !(binding.block.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE); - } else { - info.writable = false; - } - - info.binding = binding.binding; - uint32_t set = binding.set; - - ERR_FAIL_COND_V_MSG(set >= MAX_UNIFORM_SETS, Vector<uint8_t>(), - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."); - - ERR_FAIL_COND_V_MSG(set >= limits.maxBoundDescriptorSets, Vector<uint8_t>(), - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."); - - if (set < (uint32_t)uniform_info.size()) { - // Check if this already exists. - bool exists = false; - for (int k = 0; k < uniform_info[set].size(); k++) { - if (uniform_info[set][k].binding == (uint32_t)info.binding) { - // Already exists, verify that it's the same type. - ERR_FAIL_COND_V_MSG(uniform_info[set][k].type != info.type, Vector<uint8_t>(), - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type."); - - // Also, verify that it's the same size. - ERR_FAIL_COND_V_MSG(uniform_info[set][k].length != info.length, Vector<uint8_t>(), - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size."); - - // Also, verify that it has the same writability. - ERR_FAIL_COND_V_MSG(uniform_info[set][k].writable != info.writable, Vector<uint8_t>(), - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different writability."); - - // Just append stage mask and return. - uniform_info.write[set].write[k].stages |= 1 << stage; - exists = true; - break; - } - } - - if (exists) { - continue; // Merged. - } - } - - info.stages = 1 << stage; - - if (set >= (uint32_t)uniform_info.size()) { - uniform_info.resize(set + 1); - } - - uniform_info.write[set].push_back(info); - } - } - - { - // Specialization constants. - - uint32_t sc_count = 0; - result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating specialization constants."); - - if (sc_count) { - Vector<SpvReflectSpecializationConstant *> spec_constants; - spec_constants.resize(sc_count); - - result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, spec_constants.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining specialization constants."); - - for (uint32_t j = 0; j < sc_count; j++) { - int32_t existing = -1; - RenderingDeviceVulkanShaderBinarySpecializationConstant sconst{}; - SpvReflectSpecializationConstant *spc = spec_constants[j]; - - sconst.constant_id = spc->constant_id; - sconst.int_value = 0.0; // Clear previous value JIC. - switch (spc->constant_type) { - case SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL: { - sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; - sconst.bool_value = spc->default_value.int_bool_value != 0; - } break; - case SPV_REFLECT_SPECIALIZATION_CONSTANT_INT: { - sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; - sconst.int_value = spc->default_value.int_bool_value; - } break; - case SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT: { - sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; - sconst.float_value = spc->default_value.float_value; - } break; - } - sconst.stage_flags = 1 << p_spirv[i].shader_stage; - - for (int k = 0; k < specialization_constants.size(); k++) { - if (specialization_constants[k].constant_id == sconst.constant_id) { - ERR_FAIL_COND_V_MSG(specialization_constants[k].type != sconst.type, Vector<uint8_t>(), "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their types differ."); - ERR_FAIL_COND_V_MSG(specialization_constants[k].int_value != sconst.int_value, Vector<uint8_t>(), "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their default values differ."); - existing = k; - break; - } - } - - if (existing > 0) { - specialization_constants.write[existing].stage_flags |= sconst.stage_flags; - } else { - specialization_constants.push_back(sconst); - } - } - } - } - - if (stage == SHADER_STAGE_VERTEX) { - uint32_t iv_count = 0; - result = spvReflectEnumerateInputVariables(&module, &iv_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating input variables."); - - if (iv_count) { - Vector<SpvReflectInterfaceVariable *> input_vars; - input_vars.resize(iv_count); - - result = spvReflectEnumerateInputVariables(&module, &iv_count, input_vars.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining input variables."); - - for (uint32_t j = 0; j < iv_count; j++) { - if (input_vars[j] && input_vars[j]->decoration_flags == 0) { // Regular input. - binary_data.vertex_input_mask |= (1 << uint32_t(input_vars[j]->location)); - } - } - } - } - - if (stage == SHADER_STAGE_FRAGMENT) { - uint32_t ov_count = 0; - result = spvReflectEnumerateOutputVariables(&module, &ov_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating output variables."); - - if (ov_count) { - Vector<SpvReflectInterfaceVariable *> output_vars; - output_vars.resize(ov_count); - - result = spvReflectEnumerateOutputVariables(&module, &ov_count, output_vars.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining output variables."); - - for (uint32_t j = 0; j < ov_count; j++) { - const SpvReflectInterfaceVariable *refvar = output_vars[j]; - if (refvar != nullptr && refvar->built_in != SpvBuiltInFragDepth) { - binary_data.fragment_outputs |= 1 << refvar->location; - } - } - } + { + binary_data.vertex_input_mask = spirv_data.vertex_input_mask; + binary_data.fragment_output_mask = spirv_data.fragment_output_mask; + binary_data.specialization_constants_count = spirv_data.specialization_constants.size(); + binary_data.is_compute = spirv_data.is_compute; + binary_data.compute_local_size[0] = spirv_data.compute_local_size[0]; + binary_data.compute_local_size[1] = spirv_data.compute_local_size[1]; + binary_data.compute_local_size[2] = spirv_data.compute_local_size[2]; + binary_data.set_count = spirv_data.uniforms.size(); + binary_data.push_constant_size = spirv_data.push_constant_size; + for (uint32_t i = 0; i < SHADER_STAGE_MAX; i++) { + if (spirv_data.push_constant_stages_mask.has_flag((ShaderStage)(1 << i))) { + binary_data.push_constant_vk_stages_mask |= shader_stage_masks[i]; } + } - uint32_t pc_count = 0; - result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating push constants."); - - if (pc_count) { - ERR_FAIL_COND_V_MSG(pc_count > 1, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "': Only one push constant is supported, which should be the same across shader stages."); - - Vector<SpvReflectBlockVariable *> pconstants; - pconstants.resize(pc_count); - result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, pconstants.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining push constants."); -#if 0 - if (pconstants[0] == nullptr) { - Ref<FileAccess> f = FileAccess::open("res://popo.spv", FileAccess::WRITE); - f->store_buffer((const uint8_t *)&SpirV[0], SpirV.size() * sizeof(uint32_t)); - } -#endif - - ERR_FAIL_COND_V_MSG(binary_data.push_constant_size && binary_data.push_constant_size != pconstants[0]->size, Vector<uint8_t>(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "': Push constant block must be the same across shader stages."); - - binary_data.push_constant_size = pconstants[0]->size; - binary_data.push_constants_vk_stage |= shader_stage_masks[stage]; - - //print_line("Stage: " + String(shader_stage_names[stage]) + " push constant of size=" + itos(push_constant.push_constant_size)); + for (const Vector<SpirvReflectionData::Uniform> &spirv_set : spirv_data.uniforms) { + Vector<RenderingDeviceVulkanShaderBinaryDataBinding> set_bindings; + for (const SpirvReflectionData::Uniform &spirv_uniform : spirv_set) { + RenderingDeviceVulkanShaderBinaryDataBinding binding{}; + binding.type = (uint32_t)spirv_uniform.type; + binding.binding = spirv_uniform.binding; + binding.stages = (uint32_t)spirv_uniform.stages_mask; + binding.length = spirv_uniform.length; + binding.writable = (uint32_t)spirv_uniform.writable; + set_bindings.push_back(binding); } - - // Destroy the reflection data when no longer required. - spvReflectDestroyShaderModule(&module); + uniform_info.push_back(set_bindings); } - stages_processed |= (1 << p_spirv[i].shader_stage); + for (const SpirvReflectionData::SpecializationConstant &spirv_sc : spirv_data.specialization_constants) { + RenderingDeviceVulkanShaderBinarySpecializationConstant spec_constant{}; + spec_constant.type = (uint32_t)spirv_sc.type; + spec_constant.constant_id = spirv_sc.constant_id; + spec_constant.int_value = spirv_sc.int_value; + spec_constant.stage_flags = (uint32_t)spirv_sc.stages_mask; + specialization_constants.push_back(spec_constant); + } } Vector<Vector<uint8_t>> compressed_stages; @@ -5167,7 +4701,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve stages_binary_size += s; } - binary_data.specialization_constant_count = specialization_constants.size(); + binary_data.specialization_constants_count = specialization_constants.size(); binary_data.set_count = uniform_info.size(); binary_data.stage_count = p_spirv.size(); @@ -5273,12 +4807,12 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ const RenderingDeviceVulkanShaderBinaryData &binary_data = *(reinterpret_cast<const RenderingDeviceVulkanShaderBinaryData *>(binptr + 12)); Shader::PushConstant push_constant; - push_constant.push_constant_size = binary_data.push_constant_size; - push_constant.push_constants_vk_stage = binary_data.push_constants_vk_stage; + push_constant.size = binary_data.push_constant_size; + push_constant.vk_stages_mask = binary_data.push_constant_vk_stages_mask; uint32_t vertex_input_mask = binary_data.vertex_input_mask; - uint32_t fragment_outputs = binary_data.fragment_outputs; + uint32_t fragment_output_mask = binary_data.fragment_output_mask; bool is_compute = binary_data.is_compute; @@ -5374,11 +4908,11 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ read_offset += set_size; } - ERR_FAIL_COND_V(read_offset + binary_data.specialization_constant_count * sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) >= binsize, RID()); + ERR_FAIL_COND_V(read_offset + binary_data.specialization_constants_count * sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) >= binsize, RID()); Vector<Shader::SpecializationConstant> specialization_constants; - for (uint32_t i = 0; i < binary_data.specialization_constant_count; i++) { + for (uint32_t i = 0; i < binary_data.specialization_constants_count; i++) { const RenderingDeviceVulkanShaderBinarySpecializationConstant &src_sc = *(reinterpret_cast<const RenderingDeviceVulkanShaderBinarySpecializationConstant *>(binptr + read_offset)); Shader::SpecializationConstant sc; sc.constant.int_value = src_sc.int_value; @@ -5444,7 +4978,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ Shader shader; shader.vertex_input_mask = vertex_input_mask; - shader.fragment_output_mask = fragment_outputs; + shader.fragment_output_mask = fragment_output_mask; shader.push_constant = push_constant; shader.is_compute = is_compute; shader.compute_local_size[0] = compute_local_size[0]; @@ -5474,19 +5008,11 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ break; } - const VkShaderStageFlagBits shader_stage_bits[SHADER_STAGE_MAX] = { - VK_SHADER_STAGE_VERTEX_BIT, - VK_SHADER_STAGE_FRAGMENT_BIT, - VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, - VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, - VK_SHADER_STAGE_COMPUTE_BIT, - }; - VkPipelineShaderStageCreateInfo shader_stage; shader_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stage.pNext = nullptr; shader_stage.flags = 0; - shader_stage.stage = shader_stage_bits[stage_type[i]]; + shader_stage.stage = shader_stage_masks[stage_type[i]]; shader_stage.module = module; shader_stage.pName = "main"; shader_stage.pSpecializationInfo = nullptr; @@ -5558,10 +5084,10 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ // Needs to be declared in this outer scope, otherwise it may not outlive its assignment // to pipeline_layout_create_info. VkPushConstantRange push_constant_range; - if (push_constant.push_constant_size) { - push_constant_range.stageFlags = push_constant.push_constants_vk_stage; + if (push_constant.size) { + push_constant_range.stageFlags = push_constant.vk_stages_mask; push_constant_range.offset = 0; - push_constant_range.size = push_constant.push_constant_size; + push_constant_range.size = push_constant.size; pipeline_layout_create_info.pushConstantRangeCount = 1; pipeline_layout_create_info.pPushConstantRanges = &push_constant_range; @@ -5635,7 +5161,7 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve return id; } -RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, uint32_t p_usage) { +RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, BitField<StorageBufferUsage> p_usage) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), "Creating buffers with data is forbidden during creation of a draw list"); @@ -5646,7 +5172,7 @@ RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Ve Buffer buffer; uint32_t flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - if (p_usage & STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT) { + if (p_usage.has_flag(STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT)) { flags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; } Error err = _buffer_allocate(&buffer, p_size_bytes, flags, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); @@ -5956,10 +5482,8 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, // Can also be used as storage, add to mutable sampled. mutable_sampled_textures.push_back(texture); } - if (texture->owner.is_valid()) { - texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. - } + + DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -6010,10 +5534,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, mutable_sampled_textures.push_back(texture); } - if (texture->owner.is_valid()) { - texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. - } + DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -6058,10 +5579,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, mutable_storage_textures.push_back(texture); } - if (texture->owner.is_valid()) { - texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. - } + DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); img_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; @@ -6223,10 +5741,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, img_info.sampler = VK_NULL_HANDLE; img_info.imageView = texture->view; - if (texture->owner.is_valid()) { - texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. - } + DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -6878,10 +6393,10 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateGraphicsPipelines failed with error " + itos(err) + " for shader '" + shader->name + "'."); pipeline.set_formats = shader->set_formats; - pipeline.push_constant_stages = shader->push_constant.push_constants_vk_stage; + pipeline.push_constant_stages_mask = shader->push_constant.vk_stages_mask; pipeline.pipeline_layout = shader->pipeline_layout; pipeline.shader = p_shader; - pipeline.push_constant_size = shader->push_constant.push_constant_size; + pipeline.push_constant_size = shader->push_constant.size; #ifdef DEBUG_ENABLED pipeline.validation.dynamic_state = p_dynamic_state_flags; @@ -6993,10 +6508,10 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<Pi ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateComputePipelines failed with error " + itos(err) + "."); pipeline.set_formats = shader->set_formats; - pipeline.push_constant_stages = shader->push_constant.push_constants_vk_stage; + pipeline.push_constant_stages_mask = shader->push_constant.vk_stages_mask; pipeline.pipeline_layout = shader->pipeline_layout; pipeline.shader = p_shader; - pipeline.push_constant_size = shader->push_constant.push_constant_size; + pipeline.push_constant_size = shader->push_constant.size; pipeline.local_group_size[0] = shader->compute_local_size[0]; pipeline.local_group_size[1] = shader->compute_local_size[1]; pipeline.local_group_size[2] = shader->compute_local_size[2]; @@ -7638,7 +7153,7 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI dl->state.set_count = pcount; // Update set count. if (pipeline->push_constant_size) { - dl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; + dl->state.pipeline_push_constant_stages = pipeline->push_constant_stages_mask; #ifdef DEBUG_ENABLED dl->validation.pipeline_push_constant_supplied = false; #endif @@ -8254,7 +7769,7 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l cl->state.set_count = pcount; // Update set count. if (pipeline->push_constant_size) { - cl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; + cl->state.pipeline_push_constant_stages = pipeline->push_constant_stages_mask; #ifdef DEBUG_ENABLED cl->validation.pipeline_push_constant_supplied = false; #endif diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 2321b95f18..3ccd7bb1d9 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -543,10 +543,6 @@ class RenderingDeviceVulkan : public RenderingDevice { // As a result, we need to figure out quickly when something is no longer "compatible". // in order to avoid costly rebinds. - enum { - MAX_UNIFORM_SETS = 16 - }; - struct UniformInfo { UniformType type = UniformType::UNIFORM_TYPE_MAX; bool writable = false; @@ -628,8 +624,8 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t fragment_output_mask = 0; struct PushConstant { - uint32_t push_constant_size = 0; - uint32_t push_constants_vk_stage = 0; + uint32_t size = 0; + uint32_t vk_stages_mask = 0; }; PushConstant push_constant; @@ -791,7 +787,7 @@ class RenderingDeviceVulkan : public RenderingDevice { VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // Not owned, needed for push constants. VkPipeline pipeline = VK_NULL_HANDLE; uint32_t push_constant_size = 0; - uint32_t push_constant_stages = 0; + uint32_t push_constant_stages_mask = 0; }; RID_Owner<RenderPipeline, true> render_pipeline_owner; @@ -802,7 +798,7 @@ class RenderingDeviceVulkan : public RenderingDevice { VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // Not owned, needed for push constants. VkPipeline pipeline = VK_NULL_HANDLE; uint32_t push_constant_size = 0; - uint32_t push_constant_stages = 0; + uint32_t push_constant_stages_mask = 0; uint32_t local_group_size[3] = { 0, 0, 0 }; }; @@ -1117,7 +1113,7 @@ public: /*****************/ virtual RID uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>()); - virtual RID storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>(), uint32_t p_usage = 0); + virtual RID storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>(), BitField<StorageBufferUsage> p_usage = 0); virtual RID texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>()); virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set); diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index c376d5434f..a1e4b5fde4 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -35,6 +35,7 @@ #include "editor/input_event_configuration_dialog.h" #include "scene/gui/check_button.h" #include "scene/gui/tree.h" +#include "scene/scene_string_names.h" static bool _is_action_name_valid(const String &p_name) { const char32_t *cstr = p_name.get_data(); @@ -362,6 +363,8 @@ void ActionMapEditor::_bind_methods() { ADD_SIGNAL(MethodInfo("action_removed", PropertyInfo(Variant::STRING, "name"))); ADD_SIGNAL(MethodInfo("action_renamed", PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name"))); ADD_SIGNAL(MethodInfo("action_reordered", PropertyInfo(Variant::STRING, "action_name"), PropertyInfo(Variant::STRING, "relative_to"), PropertyInfo(Variant::BOOL, "before"))); + ADD_SIGNAL(MethodInfo(SNAME("filter_focused"))); + ADD_SIGNAL(MethodInfo(SNAME("filter_unfocused"))); } LineEdit *ActionMapEditor::get_search_box() const { @@ -492,6 +495,14 @@ void ActionMapEditor::use_external_search_box(LineEdit *p_searchbox) { action_list_search->connect("text_changed", callable_mp(this, &ActionMapEditor::_search_term_updated)); } +void ActionMapEditor::_on_filter_focused() { + emit_signal(SNAME("filter_focused")); +} + +void ActionMapEditor::_on_filter_unfocused() { + emit_signal(SNAME("filter_unfocused")); +} + ActionMapEditor::ActionMapEditor() { // Main Vbox Container VBoxContainer *main_vbox = memnew(VBoxContainer); @@ -512,6 +523,8 @@ ActionMapEditor::ActionMapEditor() { action_list_search_by_event->set_h_size_flags(Control::SIZE_EXPAND_FILL); action_list_search_by_event->set_stretch_ratio(0.75); action_list_search_by_event->connect("event_changed", callable_mp(this, &ActionMapEditor::_search_by_event)); + action_list_search_by_event->connect(SceneStringNames::get_singleton()->focus_entered, callable_mp(this, &ActionMapEditor::_on_filter_focused)); + action_list_search_by_event->connect(SceneStringNames::get_singleton()->focus_exited, callable_mp(this, &ActionMapEditor::_on_filter_unfocused)); top_hbox->add_child(action_list_search_by_event); Button *clear_all_search = memnew(Button); diff --git a/editor/action_map_editor.h b/editor/action_map_editor.h index ad9980c4ef..1908805b17 100644 --- a/editor/action_map_editor.h +++ b/editor/action_map_editor.h @@ -106,6 +106,9 @@ private: bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + void _on_filter_focused(); + void _on_filter_unfocused(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 530708f3e5..7b5a7b7046 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -575,7 +575,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { ep.point_rect.size = bezier_icon->get_size(); if (selection.has(IntPair(i, j))) { draw_texture(selected_icon, ep.point_rect.position); - draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 8), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent); + draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 8), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.0001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent); draw_string(font, ep.point_rect.position + Vector2(8, -8), TTR("Value:") + " " + TS->format_number(rtos(Math::snapped(value, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent); } else { Color track_color = Color(1, 1, 1, 1); @@ -812,7 +812,7 @@ void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int return; } - int idx = animation->track_find_key(p_track, p_pos, true); + int idx = animation->track_find_key(p_track, p_pos, Animation::FIND_MODE_APPROX); ERR_FAIL_COND(idx < 0); selection.insert(IntPair(p_track, idx)); @@ -1168,8 +1168,8 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { new_point[4] = 0; real_t time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); - while (animation->track_find_key(selected_track, time, true) != -1) { - time += 0.001; + while (animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX) != -1) { + time += 0.0001; } Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); @@ -1179,7 +1179,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { undo_redo->commit_action(); //then attempt to move - int index = animation->track_find_key(selected_track, time, true); + int index = animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX); ERR_FAIL_COND(index == -1); _clear_selection(); selection.insert(IntPair(selected_track, index)); @@ -1283,7 +1283,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { real_t newtime = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x); - int idx = animation->track_find_key(E->get().first, newtime, true); + int idx = animation->track_find_key(E->get().first, newtime, Animation::FIND_MODE_APPROX); if (idx == -1) { continue; } @@ -1539,7 +1539,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { real_t time = ((menu_insert_key.x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); - while (animation->track_find_key(selected_track, time, true) != -1) { + while (animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX) != -1) { time += 0.001; } @@ -1599,7 +1599,7 @@ void AnimationBezierTrackEdit::duplicate_selection() { for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { real_t t = animation->track_get_key_time(E->get().first, E->get().second); real_t dst_time = t + (timeline->get_play_position() - top_time); - int existing_idx = animation->track_find_key(E->get().first, dst_time, true); + int existing_idx = animation->track_find_key(E->get().first, dst_time, Animation::FIND_MODE_APPROX); undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->get().first, dst_time, animation->track_get_key_value(E->get().first, E->get().second), animation->track_get_key_transition(E->get().first, E->get().second)); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, dst_time); @@ -1623,7 +1623,7 @@ void AnimationBezierTrackEdit::duplicate_selection() { int track = E.first; real_t time = E.second; - int existing_idx = animation->track_find_key(track, time, true); + int existing_idx = animation->track_find_key(track, time, Animation::FIND_MODE_APPROX); if (existing_idx == -1) { continue; diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 2ee06a0dbd..74e0db967d 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -48,1356 +48,1182 @@ #include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" -class AnimationTrackKeyEdit : public Object { - GDCLASS(AnimationTrackKeyEdit, Object); - -public: - bool setting = false; - bool animation_read_only = false; +void AnimationTrackKeyEdit::_bind_methods() { + ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj); + ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationTrackKeyEdit::_key_ofs_changed); + ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationTrackKeyEdit::_hide_script_from_inspector); + ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationTrackKeyEdit::_hide_metadata_from_inspector); + ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationTrackKeyEdit::get_root_path); + ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationTrackKeyEdit::_dont_undo_redo); + ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationTrackKeyEdit::_is_read_only); +} - bool _hide_script_from_inspector() { return true; } - bool _hide_metadata_from_inspector() { return true; } - bool _dont_undo_redo() { return true; } +void AnimationTrackKeyEdit::_fix_node_path(Variant &value) { + NodePath np = value; - bool _is_read_only() { - return animation_read_only; + if (np == NodePath()) { + return; } - static void _bind_methods() { - ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj); - ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationTrackKeyEdit::_key_ofs_changed); - ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationTrackKeyEdit::_hide_script_from_inspector); - ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationTrackKeyEdit::_hide_metadata_from_inspector); - ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationTrackKeyEdit::get_root_path); - ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationTrackKeyEdit::_dont_undo_redo); - ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationTrackKeyEdit::_is_read_only); - } + Node *root = EditorNode::get_singleton()->get_tree()->get_root(); - void _fix_node_path(Variant &value) { - NodePath np = value; + Node *np_node = root->get_node(np); + ERR_FAIL_COND(!np_node); - if (np == NodePath()) { - return; - } + Node *edited_node = root->get_node(base); + ERR_FAIL_COND(!edited_node); - Node *root = EditorNode::get_singleton()->get_tree()->get_root(); + value = edited_node->get_path_to(np_node); +} - Node *np_node = root->get_node(np); - ERR_FAIL_COND(!np_node); +void AnimationTrackKeyEdit::_update_obj(const Ref<Animation> &p_anim) { + if (setting || animation != p_anim) { + return; + } - Node *edited_node = root->get_node(base); - ERR_FAIL_COND(!edited_node); + notify_change(); +} - value = edited_node->get_path_to(np_node); +void AnimationTrackKeyEdit::_key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) { + if (animation != p_anim || from != key_ofs) { + return; } - void _update_obj(const Ref<Animation> &p_anim) { - if (setting || animation != p_anim) { - return; - } + key_ofs = to; - notify_change(); + if (setting) { + return; } - void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) { - if (animation != p_anim || from != key_ofs) { - return; - } + notify_change(); +} - key_ofs = to; +bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_value) { + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + ERR_FAIL_COND_V(key == -1, false); - if (setting) { - return; - } + String name = p_name; + if (name == "easing") { + float val = p_value; + float prev_val = animation->track_get_key_transition(track, key); + setting = true; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Animation Change Transition"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, val); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - notify_change(); + setting = false; + return true; } - bool _set(const StringName &p_name, const Variant &p_value) { - int key = animation->track_find_key(track, key_ofs, true); - ERR_FAIL_COND_V(key == -1, false); - - String name = p_name; - if (name == "time" || name == "frame") { - float new_time = p_value; - - if (name == "frame") { - float fps = animation->get_step(); - if (fps > 0) { - fps = 1.0 / fps; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + Variant old = animation->track_get_key_value(track, key); + setting = true; + String chan; + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + chan = "Position3D"; + break; + case Animation::TYPE_ROTATION_3D: + chan = "Rotation3D"; + break; + case Animation::TYPE_SCALE_3D: + chan = "Scale3D"; + break; + default: { + } } - new_time /= fps; - } - if (new_time == key_ofs) { + undo_redo->create_action(vformat(TTR("Animation Change %s"), chan)); + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); + + setting = false; return true; } - int existing = animation->track_find_key(track, new_time, true); + } break; + case Animation::TYPE_BLEND_SHAPE: + case Animation::TYPE_VALUE: { + if (name == "value") { + Variant value = p_value; - setting = true; - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - undo_redo->create_action(TTR("Animation Change Keyframe Time"), UndoRedo::MERGE_ENDS); + if (value.get_type() == Variant::NODE_PATH) { + _fix_node_path(value); + } - Variant val = animation->track_get_key_value(track, key); - float trans = animation->track_get_key_transition(track, key); + setting = true; + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + Variant prev = animation->track_get_key_value(track, key); + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, prev); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key); - undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans); - undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, new_time); - undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans); - undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs); + setting = false; + return true; + } + } break; + case Animation::TYPE_METHOD: { + Dictionary d_old = animation->track_get_key_value(track, key); + Dictionary d_new = d_old.duplicate(); + + bool change_notify_deserved = false; + bool mergeable = false; + + if (name == "name") { + d_new["method"] = p_value; + } else if (name == "arg_count") { + Vector<Variant> args = d_old["args"]; + args.resize(p_value); + d_new["args"] = args; + change_notify_deserved = true; + } else if (name.begins_with("args/")) { + Vector<Variant> args = d_old["args"]; + int idx = name.get_slice("/", 1).to_int(); + ERR_FAIL_INDEX_V(idx, args.size(), false); + + String what = name.get_slice("/", 2); + if (what == "type") { + Variant::Type t = Variant::Type(int(p_value)); + + if (t != args[idx].get_type()) { + Callable::CallError err; + if (Variant::can_convert(args[idx].get_type(), t)) { + Variant old = args[idx]; + Variant *ptrs[1] = { &old }; + Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); + } else { + Variant::construct(t, args.write[idx], nullptr, 0, err); + } + change_notify_deserved = true; + d_new["args"] = args; + } + } else if (what == "value") { + Variant value = p_value; + if (value.get_type() == Variant::NODE_PATH) { + _fix_node_path(value); + } - if (existing != -1) { - Variant v = animation->track_get_key_value(track, existing); - trans = animation->track_get_key_transition(track, existing); - undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, new_time, v, trans); + args.write[idx] = value; + d_new["args"] = args; + mergeable = true; + } } - undo_redo->commit_action(); - setting = false; - return true; - } + if (mergeable) { + undo_redo->create_action(TTR("Animation Change Call"), UndoRedo::MERGE_ENDS); + } else { + undo_redo->create_action(TTR("Animation Change Call")); + } - if (name == "easing") { - float val = p_value; - float prev_val = animation->track_get_key_transition(track, key); setting = true; - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - undo_redo->create_action(TTR("Animation Change Transition"), UndoRedo::MERGE_ENDS); - undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, val); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val); + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old); undo_redo->add_do_method(this, "_update_obj", animation); undo_redo->add_undo_method(this, "_update_obj", animation); undo_redo->commit_action(); setting = false; + if (change_notify_deserved) { + notify_change(); + } return true; - } - - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - case Animation::TYPE_ROTATION_3D: - case Animation::TYPE_SCALE_3D: { - if (name == "position" || name == "rotation" || name == "scale") { - Variant old = animation->track_get_key_value(track, key); - setting = true; - String chan; - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - chan = "Position3D"; - break; - case Animation::TYPE_ROTATION_3D: - chan = "Rotation3D"; - break; - case Animation::TYPE_SCALE_3D: - chan = "Scale3D"; - break; - default: { - } - } - - undo_redo->create_action(vformat(TTR("Anim Change %s"), chan)); - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); + } break; + case Animation::TYPE_BEZIER: { + if (name == "value") { + const Variant &value = p_value; - setting = false; - return true; - } + setting = true; + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + float prev = animation->bezier_track_get_key_value(track, key); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_value", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_value", track, key, prev); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - } break; - case Animation::TYPE_BLEND_SHAPE: - case Animation::TYPE_VALUE: { - if (name == "value") { - Variant value = p_value; + setting = false; + return true; + } - if (value.get_type() == Variant::NODE_PATH) { - _fix_node_path(value); - } + if (name == "in_handle") { + const Variant &value = p_value; - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - Variant prev = animation->track_get_key_value(track, key); - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, prev); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); + setting = true; + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + Vector2 prev = animation->bezier_track_get_key_in_handle(track, key); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, prev); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - setting = false; - return true; - } - } break; - case Animation::TYPE_METHOD: { - Dictionary d_old = animation->track_get_key_value(track, key); - Dictionary d_new = d_old.duplicate(); + setting = false; + return true; + } - bool change_notify_deserved = false; - bool mergeable = false; + if (name == "out_handle") { + const Variant &value = p_value; - if (name == "name") { - d_new["method"] = p_value; - } else if (name == "arg_count") { - Vector<Variant> args = d_old["args"]; - args.resize(p_value); - d_new["args"] = args; - change_notify_deserved = true; - } else if (name.begins_with("args/")) { - Vector<Variant> args = d_old["args"]; - int idx = name.get_slice("/", 1).to_int(); - ERR_FAIL_INDEX_V(idx, args.size(), false); - - String what = name.get_slice("/", 2); - if (what == "type") { - Variant::Type t = Variant::Type(int(p_value)); - - if (t != args[idx].get_type()) { - Callable::CallError err; - if (Variant::can_convert(args[idx].get_type(), t)) { - Variant old = args[idx]; - Variant *ptrs[1] = { &old }; - Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); - } else { - Variant::construct(t, args.write[idx], nullptr, 0, err); - } - change_notify_deserved = true; - d_new["args"] = args; - } - } else if (what == "value") { - Variant value = p_value; - if (value.get_type() == Variant::NODE_PATH) { - _fix_node_path(value); - } + setting = true; + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + Vector2 prev = animation->bezier_track_get_key_out_handle(track, key); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, prev); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - args.write[idx] = value; - d_new["args"] = args; - mergeable = true; - } - } + setting = false; + return true; + } - if (mergeable) { - undo_redo->create_action(TTR("Animation Change Call"), UndoRedo::MERGE_ENDS); - } else { - undo_redo->create_action(TTR("Animation Change Call")); - } + if (name == "handle_mode") { + const Variant &value = p_value; setting = true; - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old); + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + int prev = animation->bezier_track_get_key_handle_mode(track, key); + undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, prev); undo_redo->add_do_method(this, "_update_obj", animation); undo_redo->add_undo_method(this, "_update_obj", animation); undo_redo->commit_action(); setting = false; - if (change_notify_deserved) { - notify_change(); - } return true; - } break; - case Animation::TYPE_BEZIER: { - if (name == "value") { - const Variant &value = p_value; - - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - float prev = animation->bezier_track_get_key_value(track, key); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_value", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_value", track, key, prev); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); - - setting = false; - return true; - } + } + } break; + case Animation::TYPE_AUDIO: { + if (name == "stream") { + Ref<AudioStream> stream = p_value; - if (name == "in_handle") { - const Variant &value = p_value; + setting = true; + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + Ref<Resource> prev = animation->audio_track_get_key_stream(track, key); + undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_stream", track, key, stream); + undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_stream", track, key, prev); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - Vector2 prev = animation->bezier_track_get_key_in_handle(track, key); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, prev); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); - - setting = false; - return true; - } + setting = false; + return true; + } - if (name == "out_handle") { - const Variant &value = p_value; + if (name == "start_offset") { + float value = p_value; - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - Vector2 prev = animation->bezier_track_get_key_out_handle(track, key); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, prev); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); - - setting = false; - return true; - } + setting = true; + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + float prev = animation->audio_track_get_key_start_offset(track, key); + undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, prev); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - if (name == "handle_mode") { - const Variant &value = p_value; + setting = false; + return true; + } - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - int prev = animation->bezier_track_get_key_handle_mode(track, key); - undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, value); - undo_redo->add_undo_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, prev); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); - - setting = false; - return true; - } - } break; - case Animation::TYPE_AUDIO: { - if (name == "stream") { - Ref<AudioStream> stream = p_value; + if (name == "end_offset") { + float value = p_value; - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - Ref<Resource> prev = animation->audio_track_get_key_stream(track, key); - undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_stream", track, key, stream); - undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_stream", track, key, prev); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); - - setting = false; - return true; - } + setting = true; + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + float prev = animation->audio_track_get_key_end_offset(track, key); + undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, prev); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - if (name == "start_offset") { - float value = p_value; + setting = false; + return true; + } + } break; + case Animation::TYPE_ANIMATION: { + if (name == "animation") { + StringName anim_name = p_value; - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - float prev = animation->audio_track_get_key_start_offset(track, key); - undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, prev); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); - - setting = false; - return true; - } + setting = true; + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + StringName prev = animation->animation_track_get_key_animation(track, key); + undo_redo->add_do_method(animation.ptr(), "animation_track_set_key_animation", track, key, anim_name); + undo_redo->add_undo_method(animation.ptr(), "animation_track_set_key_animation", track, key, prev); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - if (name == "end_offset") { - float value = p_value; + setting = false; + return true; + } + } break; + } - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - float prev = animation->audio_track_get_key_end_offset(track, key); - undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, prev); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); - - setting = false; - return true; - } - } break; - case Animation::TYPE_ANIMATION: { - if (name == "animation") { - StringName anim_name = p_value; + return false; +} - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - StringName prev = animation->animation_track_get_key_animation(track, key); - undo_redo->add_do_method(animation.ptr(), "animation_track_set_key_animation", track, key, anim_name); - undo_redo->add_undo_method(animation.ptr(), "animation_track_set_key_animation", track, key, prev); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); - - setting = false; - return true; - } - } break; - } +bool AnimationTrackKeyEdit::_get(const StringName &p_name, Variant &r_ret) const { + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + ERR_FAIL_COND_V(key == -1, false); - return false; + String name = p_name; + if (name == "easing") { + r_ret = animation->track_get_key_transition(track, key); + return true; } - bool _get(const StringName &p_name, Variant &r_ret) const { - int key = animation->track_find_key(track, key_ofs, true); - ERR_FAIL_COND_V(key == -1, false); - - String name = p_name; - if (name == "time") { - r_ret = key_ofs; - return true; - } - - if (name == "frame") { - float fps = animation->get_step(); - if (fps > 0) { - fps = 1.0 / fps; + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + r_ret = animation->track_get_key_value(track, key); + return true; + } + } break; + case Animation::TYPE_BLEND_SHAPE: + case Animation::TYPE_VALUE: { + if (name == "value") { + r_ret = animation->track_get_key_value(track, key); + return true; } - r_ret = key_ofs * fps; - return true; - } - if (name == "easing") { - r_ret = animation->track_get_key_transition(track, key); - return true; - } + } break; + case Animation::TYPE_METHOD: { + Dictionary d = animation->track_get_key_value(track, key); - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - case Animation::TYPE_ROTATION_3D: - case Animation::TYPE_SCALE_3D: { - if (name == "position" || name == "rotation" || name == "scale") { - r_ret = animation->track_get_key_value(track, key); - return true; - } - } break; - case Animation::TYPE_BLEND_SHAPE: - case Animation::TYPE_VALUE: { - if (name == "value") { - r_ret = animation->track_get_key_value(track, key); - return true; - } + if (name == "name") { + ERR_FAIL_COND_V(!d.has("method"), false); + r_ret = d["method"]; + return true; + } - } break; - case Animation::TYPE_METHOD: { - Dictionary d = animation->track_get_key_value(track, key); + ERR_FAIL_COND_V(!d.has("args"), false); - if (name == "name") { - ERR_FAIL_COND_V(!d.has("method"), false); - r_ret = d["method"]; - return true; - } + Vector<Variant> args = d["args"]; - ERR_FAIL_COND_V(!d.has("args"), false); + if (name == "arg_count") { + r_ret = args.size(); + return true; + } - Vector<Variant> args = d["args"]; + if (name.begins_with("args/")) { + int idx = name.get_slice("/", 1).to_int(); + ERR_FAIL_INDEX_V(idx, args.size(), false); - if (name == "arg_count") { - r_ret = args.size(); + String what = name.get_slice("/", 2); + if (what == "type") { + r_ret = args[idx].get_type(); return true; } - if (name.begins_with("args/")) { - int idx = name.get_slice("/", 1).to_int(); - ERR_FAIL_INDEX_V(idx, args.size(), false); - - String what = name.get_slice("/", 2); - if (what == "type") { - r_ret = args[idx].get_type(); - return true; - } - - if (what == "value") { - r_ret = args[idx]; - return true; - } - } - - } break; - case Animation::TYPE_BEZIER: { - if (name == "value") { - r_ret = animation->bezier_track_get_key_value(track, key); + if (what == "value") { + r_ret = args[idx]; return true; } + } - if (name == "in_handle") { - r_ret = animation->bezier_track_get_key_in_handle(track, key); - return true; - } + } break; + case Animation::TYPE_BEZIER: { + if (name == "value") { + r_ret = animation->bezier_track_get_key_value(track, key); + return true; + } - if (name == "out_handle") { - r_ret = animation->bezier_track_get_key_out_handle(track, key); - return true; - } + if (name == "in_handle") { + r_ret = animation->bezier_track_get_key_in_handle(track, key); + return true; + } - if (name == "handle_mode") { - r_ret = animation->bezier_track_get_key_handle_mode(track, key); - return true; - } + if (name == "out_handle") { + r_ret = animation->bezier_track_get_key_out_handle(track, key); + return true; + } - } break; - case Animation::TYPE_AUDIO: { - if (name == "stream") { - r_ret = animation->audio_track_get_key_stream(track, key); - return true; - } + if (name == "handle_mode") { + r_ret = animation->bezier_track_get_key_handle_mode(track, key); + return true; + } - if (name == "start_offset") { - r_ret = animation->audio_track_get_key_start_offset(track, key); - return true; - } + } break; + case Animation::TYPE_AUDIO: { + if (name == "stream") { + r_ret = animation->audio_track_get_key_stream(track, key); + return true; + } - if (name == "end_offset") { - r_ret = animation->audio_track_get_key_end_offset(track, key); - return true; - } + if (name == "start_offset") { + r_ret = animation->audio_track_get_key_start_offset(track, key); + return true; + } - } break; - case Animation::TYPE_ANIMATION: { - if (name == "animation") { - r_ret = animation->animation_track_get_key_animation(track, key); - return true; - } + if (name == "end_offset") { + r_ret = animation->audio_track_get_key_end_offset(track, key); + return true; + } - } break; - } + } break; + case Animation::TYPE_ANIMATION: { + if (name == "animation") { + r_ret = animation->animation_track_get_key_animation(track, key); + return true; + } - return false; + } break; } - void _get_property_list(List<PropertyInfo> *p_list) const { - if (animation.is_null()) { - return; - } - ERR_FAIL_INDEX(track, animation->get_track_count()); - int key = animation->track_find_key(track, key_ofs, true); - ERR_FAIL_COND(key == -1); + return false; +} - if (use_fps && animation->get_step() > 0) { - float max_frame = animation->get_length() / animation->get_step(); - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("frame"), PROPERTY_HINT_RANGE, "0," + rtos(max_frame) + ",1")); - } else { - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("time"), PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01")); - } +void AnimationTrackKeyEdit::_get_property_list(List<PropertyInfo> *p_list) const { + if (animation.is_null()) { + return; + } - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, PNAME("position"))); - } break; - case Animation::TYPE_ROTATION_3D: { - p_list->push_back(PropertyInfo(Variant::QUATERNION, PNAME("rotation"))); - } break; - case Animation::TYPE_SCALE_3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, PNAME("scale"))); - } break; - case Animation::TYPE_BLEND_SHAPE: { - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("value"))); - } break; - case Animation::TYPE_VALUE: { - Variant v = animation->track_get_key_value(track, key); + ERR_FAIL_INDEX(track, animation->get_track_count()); + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + ERR_FAIL_COND(key == -1); - if (hint.type != Variant::NIL) { - PropertyInfo pi = hint; - pi.name = PNAME("value"); - p_list->push_back(pi); - } else { - PropertyHint val_hint = PROPERTY_HINT_NONE; - String val_hint_string; - - if (v.get_type() == Variant::OBJECT) { - // Could actually check the object property if exists..? Yes I will! - Ref<Resource> res = v; - if (res.is_valid()) { - val_hint = PROPERTY_HINT_RESOURCE_TYPE; - val_hint_string = res->get_class(); - } - } + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, PNAME("position"))); + } break; + case Animation::TYPE_ROTATION_3D: { + p_list->push_back(PropertyInfo(Variant::QUATERNION, PNAME("rotation"))); + } break; + case Animation::TYPE_SCALE_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, PNAME("scale"))); + } break; + case Animation::TYPE_BLEND_SHAPE: { + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("value"))); + } break; + case Animation::TYPE_VALUE: { + Variant v = animation->track_get_key_value(track, key); - if (v.get_type() != Variant::NIL) { - p_list->push_back(PropertyInfo(v.get_type(), PNAME("value"), val_hint, val_hint_string)); + if (hint.type != Variant::NIL) { + PropertyInfo pi = hint; + pi.name = PNAME("value"); + p_list->push_back(pi); + } else { + PropertyHint val_hint = PROPERTY_HINT_NONE; + String val_hint_string; + + if (v.get_type() == Variant::OBJECT) { + // Could actually check the object property if exists..? Yes I will! + Ref<Resource> res = v; + if (res.is_valid()) { + val_hint = PROPERTY_HINT_RESOURCE_TYPE; + val_hint_string = res->get_class(); } } - } break; - case Animation::TYPE_METHOD: { - p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("name"))); - p_list->push_back(PropertyInfo(Variant::INT, PNAME("arg_count"), PROPERTY_HINT_RANGE, "0,32,1,or_greater")); - - Dictionary d = animation->track_get_key_value(track, key); - ERR_FAIL_COND(!d.has("args")); - Vector<Variant> args = d["args"]; - String vtypes; - for (int i = 0; i < Variant::VARIANT_MAX; i++) { - if (i > 0) { - vtypes += ","; - } - vtypes += Variant::get_type_name(Variant::Type(i)); + if (v.get_type() != Variant::NIL) { + p_list->push_back(PropertyInfo(v.get_type(), PNAME("value"), val_hint, val_hint_string)); } + } - for (int i = 0; i < args.size(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%d/%s", PNAME("args"), i, PNAME("type")), PROPERTY_HINT_ENUM, vtypes)); - if (args[i].get_type() != Variant::NIL) { - p_list->push_back(PropertyInfo(args[i].get_type(), vformat("%s/%d/%s", PNAME("args"), i, PNAME("value")))); - } + } break; + case Animation::TYPE_METHOD: { + p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("name"))); + p_list->push_back(PropertyInfo(Variant::INT, PNAME("arg_count"), PROPERTY_HINT_RANGE, "0,32,1,or_greater")); + + Dictionary d = animation->track_get_key_value(track, key); + ERR_FAIL_COND(!d.has("args")); + Vector<Variant> args = d["args"]; + String vtypes; + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (i > 0) { + vtypes += ","; } + vtypes += Variant::get_type_name(Variant::Type(i)); + } - } break; - case Animation::TYPE_BEZIER: { - Animation::HandleMode hm = animation->bezier_track_get_key_handle_mode(track, key); - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("value"))); - if (hm == Animation::HANDLE_MODE_LINEAR) { - p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); - } else { - p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"))); - p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"))); + for (int i = 0; i < args.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%d/%s", PNAME("args"), i, PNAME("type")), PROPERTY_HINT_ENUM, vtypes)); + if (args[i].get_type() != Variant::NIL) { + p_list->push_back(PropertyInfo(args[i].get_type(), vformat("%s/%d/%s", PNAME("args"), i, PNAME("value")))); } - p_list->push_back(PropertyInfo(Variant::INT, PNAME("handle_mode"), PROPERTY_HINT_ENUM, "Free,Linear,Balanced,Mirrored")); - - } break; - case Animation::TYPE_AUDIO: { - p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("stream"), PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("start_offset"), PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater")); - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("end_offset"), PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater")); + } - } break; - case Animation::TYPE_ANIMATION: { - String animations; + } break; + case Animation::TYPE_BEZIER: { + Animation::HandleMode hm = animation->bezier_track_get_key_handle_mode(track, key); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("value"))); + if (hm == Animation::HANDLE_MODE_LINEAR) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); + } else { + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"))); + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"))); + } + p_list->push_back(PropertyInfo(Variant::INT, PNAME("handle_mode"), PROPERTY_HINT_ENUM, "Free,Linear,Balanced,Mirrored")); - if (root_path && root_path->has_node(animation->track_get_path(track))) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node(animation->track_get_path(track))); - if (ap) { - List<StringName> anims; - ap->get_animation_list(&anims); - for (const StringName &E : anims) { - if (!animations.is_empty()) { - animations += ","; - } + } break; + case Animation::TYPE_AUDIO: { + p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("stream"), PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("start_offset"), PROPERTY_HINT_RANGE, "0,3600,0.0001,or_greater")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("end_offset"), PROPERTY_HINT_RANGE, "0,3600,0.0001,or_greater")); - animations += String(E); + } break; + case Animation::TYPE_ANIMATION: { + String animations; + + if (root_path && root_path->has_node(animation->track_get_path(track))) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node(animation->track_get_path(track))); + if (ap) { + List<StringName> anims; + ap->get_animation_list(&anims); + for (const StringName &E : anims) { + if (!animations.is_empty()) { + animations += ","; } + + animations += String(E); } } + } - if (!animations.is_empty()) { - animations += ","; - } - animations += "[stop]"; + if (!animations.is_empty()) { + animations += ","; + } + animations += "[stop]"; - p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("animation"), PROPERTY_HINT_ENUM, animations)); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("animation"), PROPERTY_HINT_ENUM, animations)); - } break; - } + } break; + } - if (animation->track_get_type(track) == Animation::TYPE_VALUE) { - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("easing"), PROPERTY_HINT_EXP_EASING)); - } + if (animation->track_get_type(track) == Animation::TYPE_VALUE) { + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("easing"), PROPERTY_HINT_EXP_EASING)); } +} - Ref<Animation> animation; - int track = -1; - float key_ofs = 0; - Node *root_path = nullptr; +void AnimationTrackKeyEdit::notify_change() { + notify_property_list_changed(); +} - PropertyInfo hint; - NodePath base; - bool use_fps = false; +Node *AnimationTrackKeyEdit::get_root_path() { + return root_path; +} - void notify_change() { - notify_property_list_changed(); - } +void AnimationTrackKeyEdit::set_use_fps(bool p_enable) { + use_fps = p_enable; + notify_property_list_changed(); +} - Node *get_root_path() { - return root_path; - } +void AnimationMultiTrackKeyEdit::_bind_methods() { + ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationMultiTrackKeyEdit::_update_obj); + ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationMultiTrackKeyEdit::_key_ofs_changed); + ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_script_from_inspector); + ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_metadata_from_inspector); + ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationMultiTrackKeyEdit::get_root_path); + ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationMultiTrackKeyEdit::_dont_undo_redo); + ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationMultiTrackKeyEdit::_is_read_only); +} - void set_use_fps(bool p_enable) { - use_fps = p_enable; - notify_property_list_changed(); +void AnimationMultiTrackKeyEdit::_fix_node_path(Variant &value, NodePath &base) { + NodePath np = value; + + if (np == NodePath()) { + return; } -}; -class AnimationMultiTrackKeyEdit : public Object { - GDCLASS(AnimationMultiTrackKeyEdit, Object); + Node *root = EditorNode::get_singleton()->get_tree()->get_root(); -public: - bool setting = false; - bool animation_read_only = false; + Node *np_node = root->get_node(np); + ERR_FAIL_COND(!np_node); - bool _hide_script_from_inspector() { return true; } - bool _hide_metadata_from_inspector() { return true; } - bool _dont_undo_redo() { return true; } + Node *edited_node = root->get_node(base); + ERR_FAIL_COND(!edited_node); - bool _is_read_only() { - return animation_read_only; - } + value = edited_node->get_path_to(np_node); +} - static void _bind_methods() { - ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationMultiTrackKeyEdit::_update_obj); - ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationMultiTrackKeyEdit::_key_ofs_changed); - ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_script_from_inspector); - ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_metadata_from_inspector); - ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationMultiTrackKeyEdit::get_root_path); - ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationMultiTrackKeyEdit::_dont_undo_redo); - ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationMultiTrackKeyEdit::_is_read_only); +void AnimationMultiTrackKeyEdit::_update_obj(const Ref<Animation> &p_anim) { + if (setting || animation != p_anim) { + return; } - void _fix_node_path(Variant &value, NodePath &base) { - NodePath np = value; + notify_change(); +} - if (np == NodePath()) { - return; - } +void AnimationMultiTrackKeyEdit::_key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) { + if (animation != p_anim) { + return; + } - Node *root = EditorNode::get_singleton()->get_tree()->get_root(); + for (const KeyValue<int, List<float>> &E : key_ofs_map) { + int key = 0; + for (const float &key_ofs : E.value) { + if (from != key_ofs) { + key++; + continue; + } - Node *np_node = root->get_node(np); - ERR_FAIL_COND(!np_node); + int track = E.key; + key_ofs_map[track][key] = to; - Node *edited_node = root->get_node(base); - ERR_FAIL_COND(!edited_node); + if (setting) { + return; + } - value = edited_node->get_path_to(np_node); - } + notify_change(); - void _update_obj(const Ref<Animation> &p_anim) { - if (setting || animation != p_anim) { return; } - - notify_change(); } +} - void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) { - if (animation != p_anim) { - return; - } - - for (const KeyValue<int, List<float>> &E : key_ofs_map) { - int key = 0; - for (const float &key_ofs : E.value) { - if (from != key_ofs) { - key++; - continue; - } +bool AnimationMultiTrackKeyEdit::_set(const StringName &p_name, const Variant &p_value) { + bool update_obj = false; + bool change_notify_deserved = false; + for (const KeyValue<int, List<float>> &E : key_ofs_map) { + int track = E.key; + for (const float &key_ofs : E.value) { + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + ERR_FAIL_COND_V(key == -1, false); - int track = E.key; - key_ofs_map[track][key] = to; + String name = p_name; + if (name == "easing") { + float val = p_value; + float prev_val = animation->track_get_key_transition(track, key); - if (setting) { - return; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Transition"), UndoRedo::MERGE_ENDS); } - - notify_change(); - - return; + undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, val); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val); + update_obj = true; } - } - } - bool _set(const StringName &p_name, const Variant &p_value) { - bool update_obj = false; - bool change_notify_deserved = false; - for (const KeyValue<int, List<float>> &E : key_ofs_map) { - int track = E.key; - for (const float &key_ofs : E.value) { - int key = animation->track_find_key(track, key_ofs, true); - ERR_FAIL_COND_V(key == -1, false); - - String name = p_name; - if (name == "time" || name == "frame") { - float new_time = p_value; - - if (name == "frame") { - float fps = animation->get_step(); - if (fps > 0) { - fps = 1.0 / fps; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + Variant old = animation->track_get_key_value(track, key); + if (!setting) { + String chan; + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + chan = "Position3D"; + break; + case Animation::TYPE_ROTATION_3D: + chan = "Rotation3D"; + break; + case Animation::TYPE_SCALE_3D: + chan = "Scale3D"; + break; + default: { + } } - new_time /= fps; - } - - int existing = animation->track_find_key(track, new_time, true); - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - if (!setting) { setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Time"), UndoRedo::MERGE_ENDS); + undo_redo->create_action(vformat(TTR("Animation Multi Change %s"), chan)); } + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); + update_obj = true; + } break; + case Animation::TYPE_BLEND_SHAPE: + case Animation::TYPE_VALUE: { + if (name == "value") { + Variant value = p_value; - Variant val = animation->track_get_key_value(track, key); - float trans = animation->track_get_key_transition(track, key); - - undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key); - undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans); - undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, new_time); - undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans); - undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs); + if (value.get_type() == Variant::NODE_PATH) { + _fix_node_path(value, base_map[track]); + } - if (existing != -1) { - Variant v = animation->track_get_key_value(track, existing); - trans = animation->track_get_key_transition(track, existing); - undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, new_time, v, trans); + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); + } + Variant prev = animation->track_get_key_value(track, key); + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, prev); + update_obj = true; } - } else if (name == "easing") { - float val = p_value; - float prev_val = animation->track_get_key_transition(track, key); + } break; + case Animation::TYPE_METHOD: { + Dictionary d_old = animation->track_get_key_value(track, key); + Dictionary d_new = d_old.duplicate(); - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Transition"), UndoRedo::MERGE_ENDS); - } - undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, val); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val); - update_obj = true; - } + bool mergeable = false; - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - case Animation::TYPE_ROTATION_3D: - case Animation::TYPE_SCALE_3D: { - Variant old = animation->track_get_key_value(track, key); - if (!setting) { - String chan; - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - chan = "Position3D"; - break; - case Animation::TYPE_ROTATION_3D: - chan = "Rotation3D"; - break; - case Animation::TYPE_SCALE_3D: - chan = "Scale3D"; - break; - default: { + if (name == "name") { + d_new["method"] = p_value; + } else if (name == "arg_count") { + Vector<Variant> args = d_old["args"]; + args.resize(p_value); + d_new["args"] = args; + change_notify_deserved = true; + } else if (name.begins_with("args/")) { + Vector<Variant> args = d_old["args"]; + int idx = name.get_slice("/", 1).to_int(); + ERR_FAIL_INDEX_V(idx, args.size(), false); + + String what = name.get_slice("/", 2); + if (what == "type") { + Variant::Type t = Variant::Type(int(p_value)); + + if (t != args[idx].get_type()) { + Callable::CallError err; + if (Variant::can_convert(args[idx].get_type(), t)) { + Variant old = args[idx]; + Variant *ptrs[1] = { &old }; + Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); + } else { + Variant::construct(t, args.write[idx], nullptr, 0, err); } + change_notify_deserved = true; + d_new["args"] = args; } - - setting = true; - undo_redo->create_action(vformat(TTR("Animation Multi Change %s"), chan)); - } - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); - update_obj = true; - } break; - case Animation::TYPE_BLEND_SHAPE: - case Animation::TYPE_VALUE: { - if (name == "value") { + } else if (what == "value") { Variant value = p_value; - if (value.get_type() == Variant::NODE_PATH) { _fix_node_path(value, base_map[track]); } - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - Variant prev = animation->track_get_key_value(track, key); - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, prev); - update_obj = true; + args.write[idx] = value; + d_new["args"] = args; + mergeable = true; } - } break; - case Animation::TYPE_METHOD: { - Dictionary d_old = animation->track_get_key_value(track, key); - Dictionary d_new = d_old.duplicate(); + } - bool mergeable = false; + Variant prev = animation->track_get_key_value(track, key); - if (name == "name") { - d_new["method"] = p_value; - } else if (name == "arg_count") { - Vector<Variant> args = d_old["args"]; - args.resize(p_value); - d_new["args"] = args; - change_notify_deserved = true; - } else if (name.begins_with("args/")) { - Vector<Variant> args = d_old["args"]; - int idx = name.get_slice("/", 1).to_int(); - ERR_FAIL_INDEX_V(idx, args.size(), false); - - String what = name.get_slice("/", 2); - if (what == "type") { - Variant::Type t = Variant::Type(int(p_value)); - - if (t != args[idx].get_type()) { - Callable::CallError err; - if (Variant::can_convert(args[idx].get_type(), t)) { - Variant old = args[idx]; - Variant *ptrs[1] = { &old }; - Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); - } else { - Variant::construct(t, args.write[idx], nullptr, 0, err); - } - change_notify_deserved = true; - d_new["args"] = args; - } - } else if (what == "value") { - Variant value = p_value; - if (value.get_type() == Variant::NODE_PATH) { - _fix_node_path(value, base_map[track]); - } + if (!setting) { + if (mergeable) { + undo_redo->create_action(TTR("Animation Multi Change Call"), UndoRedo::MERGE_ENDS); + } else { + undo_redo->create_action(TTR("Animation Multi Change Call")); + } - args.write[idx] = value; - d_new["args"] = args; - mergeable = true; - } + setting = true; + } + + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old); + update_obj = true; + } break; + case Animation::TYPE_BEZIER: { + if (name == "value") { + const Variant &value = p_value; + + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } + float prev = animation->bezier_track_get_key_value(track, key); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_value", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_value", track, key, prev); + update_obj = true; + } else if (name == "in_handle") { + const Variant &value = p_value; - Variant prev = animation->track_get_key_value(track, key); + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); + } + Vector2 prev = animation->bezier_track_get_key_in_handle(track, key); + undo_redo->add_do_method(this, "_bezier_track_set_key_in_handle", track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_in_handle", track, key, prev); + update_obj = true; + } else if (name == "out_handle") { + const Variant &value = p_value; if (!setting) { - if (mergeable) { - undo_redo->create_action(TTR("Animation Multi Change Call"), UndoRedo::MERGE_ENDS); - } else { - undo_redo->create_action(TTR("Animation Multi Change Call")); - } + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); + } + Vector2 prev = animation->bezier_track_get_key_out_handle(track, key); + undo_redo->add_do_method(this, "_bezier_track_set_key_out_handle", track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_out_handle", track, key, prev); + update_obj = true; + } else if (name == "handle_mode") { + const Variant &value = p_value; + if (!setting) { setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } + int prev = animation->bezier_track_get_key_handle_mode(track, key); + undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, prev); + update_obj = true; + } + } break; + case Animation::TYPE_AUDIO: { + if (name == "stream") { + Ref<AudioStream> stream = p_value; - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old); + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); + } + Ref<Resource> prev = animation->audio_track_get_key_stream(track, key); + undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_stream", track, key, stream); + undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_stream", track, key, prev); update_obj = true; - } break; - case Animation::TYPE_BEZIER: { - if (name == "value") { - const Variant &value = p_value; + } else if (name == "start_offset") { + float value = p_value; - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - float prev = animation->bezier_track_get_key_value(track, key); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_value", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_value", track, key, prev); - update_obj = true; - } else if (name == "in_handle") { - const Variant &value = p_value; - - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - Vector2 prev = animation->bezier_track_get_key_in_handle(track, key); - undo_redo->add_do_method(this, "_bezier_track_set_key_in_handle", track, key, value); - undo_redo->add_undo_method(this, "_bezier_track_set_key_in_handle", track, key, prev); - update_obj = true; - } else if (name == "out_handle") { - const Variant &value = p_value; - - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - Vector2 prev = animation->bezier_track_get_key_out_handle(track, key); - undo_redo->add_do_method(this, "_bezier_track_set_key_out_handle", track, key, value); - undo_redo->add_undo_method(this, "_bezier_track_set_key_out_handle", track, key, prev); - update_obj = true; - } else if (name == "handle_mode") { - const Variant &value = p_value; - - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - int prev = animation->bezier_track_get_key_handle_mode(track, key); - undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, value); - undo_redo->add_undo_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, prev); - update_obj = true; + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } - } break; - case Animation::TYPE_AUDIO: { - if (name == "stream") { - Ref<AudioStream> stream = p_value; + float prev = animation->audio_track_get_key_start_offset(track, key); + undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, prev); + update_obj = true; + } else if (name == "end_offset") { + float value = p_value; - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - Ref<Resource> prev = animation->audio_track_get_key_stream(track, key); - undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_stream", track, key, stream); - undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_stream", track, key, prev); - update_obj = true; - } else if (name == "start_offset") { - float value = p_value; - - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - float prev = animation->audio_track_get_key_start_offset(track, key); - undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, prev); - update_obj = true; - } else if (name == "end_offset") { - float value = p_value; - - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - float prev = animation->audio_track_get_key_end_offset(track, key); - undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, prev); - update_obj = true; + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } - } break; - case Animation::TYPE_ANIMATION: { - if (name == "animation") { - StringName anim_name = p_value; + float prev = animation->audio_track_get_key_end_offset(track, key); + undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, prev); + update_obj = true; + } + } break; + case Animation::TYPE_ANIMATION: { + if (name == "animation") { + StringName anim_name = p_value; - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - StringName prev = animation->animation_track_get_key_animation(track, key); - undo_redo->add_do_method(animation.ptr(), "animation_track_set_key_animation", track, key, anim_name); - undo_redo->add_undo_method(animation.ptr(), "animation_track_set_key_animation", track, key, prev); - update_obj = true; + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } - } break; - } + StringName prev = animation->animation_track_get_key_animation(track, key); + undo_redo->add_do_method(animation.ptr(), "animation_track_set_key_animation", track, key, anim_name); + undo_redo->add_undo_method(animation.ptr(), "animation_track_set_key_animation", track, key, prev); + update_obj = true; + } + } break; } } + } - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - if (setting) { - if (update_obj) { - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - } - - undo_redo->commit_action(); - setting = false; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + if (setting) { + if (update_obj) { + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + } - if (change_notify_deserved) { - notify_change(); - } + undo_redo->commit_action(); + setting = false; - return true; + if (change_notify_deserved) { + notify_change(); } - return false; + return true; } - bool _get(const StringName &p_name, Variant &r_ret) const { - for (const KeyValue<int, List<float>> &E : key_ofs_map) { - int track = E.key; - for (const float &key_ofs : E.value) { - int key = animation->track_find_key(track, key_ofs, true); - ERR_CONTINUE(key == -1); + return false; +} - String name = p_name; - if (name == "time") { - r_ret = key_ofs; - return true; - } +bool AnimationMultiTrackKeyEdit::_get(const StringName &p_name, Variant &r_ret) const { + for (const KeyValue<int, List<float>> &E : key_ofs_map) { + int track = E.key; + for (const float &key_ofs : E.value) { + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + ERR_CONTINUE(key == -1); - if (name == "frame") { - float fps = animation->get_step(); - if (fps > 0) { - fps = 1.0 / fps; + String name = p_name; + if (name == "easing") { + r_ret = animation->track_get_key_transition(track, key); + return true; + } + + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + r_ret = animation->track_get_key_value(track, key); + return true; } - r_ret = key_ofs * fps; - return true; - } - if (name == "easing") { - r_ret = animation->track_get_key_transition(track, key); - return true; - } + } break; + case Animation::TYPE_BLEND_SHAPE: + case Animation::TYPE_VALUE: { + if (name == "value") { + r_ret = animation->track_get_key_value(track, key); + return true; + } - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - case Animation::TYPE_ROTATION_3D: - case Animation::TYPE_SCALE_3D: { - if (name == "position" || name == "rotation" || name == "scale") { - r_ret = animation->track_get_key_value(track, key); - return true; - } + } break; + case Animation::TYPE_METHOD: { + Dictionary d = animation->track_get_key_value(track, key); - } break; - case Animation::TYPE_BLEND_SHAPE: - case Animation::TYPE_VALUE: { - if (name == "value") { - r_ret = animation->track_get_key_value(track, key); - return true; - } + if (name == "name") { + ERR_FAIL_COND_V(!d.has("method"), false); + r_ret = d["method"]; + return true; + } - } break; - case Animation::TYPE_METHOD: { - Dictionary d = animation->track_get_key_value(track, key); + ERR_FAIL_COND_V(!d.has("args"), false); - if (name == "name") { - ERR_FAIL_COND_V(!d.has("method"), false); - r_ret = d["method"]; - return true; - } + Vector<Variant> args = d["args"]; - ERR_FAIL_COND_V(!d.has("args"), false); + if (name == "arg_count") { + r_ret = args.size(); + return true; + } - Vector<Variant> args = d["args"]; + if (name.begins_with("args/")) { + int idx = name.get_slice("/", 1).to_int(); + ERR_FAIL_INDEX_V(idx, args.size(), false); - if (name == "arg_count") { - r_ret = args.size(); + String what = name.get_slice("/", 2); + if (what == "type") { + r_ret = args[idx].get_type(); return true; } - if (name.begins_with("args/")) { - int idx = name.get_slice("/", 1).to_int(); - ERR_FAIL_INDEX_V(idx, args.size(), false); - - String what = name.get_slice("/", 2); - if (what == "type") { - r_ret = args[idx].get_type(); - return true; - } - - if (what == "value") { - r_ret = args[idx]; - return true; - } - } - - } break; - case Animation::TYPE_BEZIER: { - if (name == "value") { - r_ret = animation->bezier_track_get_key_value(track, key); + if (what == "value") { + r_ret = args[idx]; return true; } + } - if (name == "in_handle") { - r_ret = animation->bezier_track_get_key_in_handle(track, key); - return true; - } + } break; + case Animation::TYPE_BEZIER: { + if (name == "value") { + r_ret = animation->bezier_track_get_key_value(track, key); + return true; + } - if (name == "out_handle") { - r_ret = animation->bezier_track_get_key_out_handle(track, key); - return true; - } + if (name == "in_handle") { + r_ret = animation->bezier_track_get_key_in_handle(track, key); + return true; + } - if (name == "handle_mode") { - r_ret = animation->bezier_track_get_key_handle_mode(track, key); - return true; - } + if (name == "out_handle") { + r_ret = animation->bezier_track_get_key_out_handle(track, key); + return true; + } - } break; - case Animation::TYPE_AUDIO: { - if (name == "stream") { - r_ret = animation->audio_track_get_key_stream(track, key); - return true; - } + if (name == "handle_mode") { + r_ret = animation->bezier_track_get_key_handle_mode(track, key); + return true; + } - if (name == "start_offset") { - r_ret = animation->audio_track_get_key_start_offset(track, key); - return true; - } + } break; + case Animation::TYPE_AUDIO: { + if (name == "stream") { + r_ret = animation->audio_track_get_key_stream(track, key); + return true; + } - if (name == "end_offset") { - r_ret = animation->audio_track_get_key_end_offset(track, key); - return true; - } + if (name == "start_offset") { + r_ret = animation->audio_track_get_key_start_offset(track, key); + return true; + } - } break; - case Animation::TYPE_ANIMATION: { - if (name == "animation") { - r_ret = animation->animation_track_get_key_animation(track, key); - return true; - } + if (name == "end_offset") { + r_ret = animation->audio_track_get_key_end_offset(track, key); + return true; + } - } break; - } + } break; + case Animation::TYPE_ANIMATION: { + if (name == "animation") { + r_ret = animation->animation_track_get_key_animation(track, key); + return true; + } + + } break; } } + } - return false; + return false; +} + +void AnimationMultiTrackKeyEdit::_get_property_list(List<PropertyInfo> *p_list) const { + if (animation.is_null()) { + return; } - void _get_property_list(List<PropertyInfo> *p_list) const { - if (animation.is_null()) { - return; - } - int first_track = -1; - float first_key = -1.0; + int first_track = -1; + float first_key = -1.0; - bool show_time = true; - bool same_track_type = true; - bool same_key_type = true; - for (const KeyValue<int, List<float>> &E : key_ofs_map) { - int track = E.key; - ERR_FAIL_INDEX(track, animation->get_track_count()); + bool same_track_type = true; + bool same_key_type = true; + for (const KeyValue<int, List<float>> &E : key_ofs_map) { + int track = E.key; + ERR_FAIL_INDEX(track, animation->get_track_count()); - if (first_track < 0) { - first_track = track; - } + if (first_track < 0) { + first_track = track; + } - if (show_time && E.value.size() > 1) { - show_time = false; + if (same_track_type) { + if (animation->track_get_type(first_track) != animation->track_get_type(track)) { + same_track_type = false; + same_key_type = false; } - if (same_track_type) { - if (animation->track_get_type(first_track) != animation->track_get_type(track)) { - same_track_type = false; - same_key_type = false; + for (const float &F : E.value) { + int key = animation->track_find_key(track, F, Animation::FIND_MODE_APPROX); + ERR_FAIL_COND(key == -1); + if (first_key < 0) { + first_key = key; } - for (const float &F : E.value) { - int key = animation->track_find_key(track, F, true); - ERR_FAIL_COND(key == -1); - if (first_key < 0) { - first_key = key; - } - - if (animation->track_get_key_value(first_track, first_key).get_type() != animation->track_get_key_value(track, key).get_type()) { - same_key_type = false; - } + if (animation->track_get_key_value(first_track, first_key).get_type() != animation->track_get_key_value(track, key).get_type()) { + same_key_type = false; } } } + } - if (show_time) { - if (use_fps && animation->get_step() > 0) { - float max_frame = animation->get_length() / animation->get_step(); - p_list->push_back(PropertyInfo(Variant::FLOAT, "frame", PROPERTY_HINT_RANGE, "0," + rtos(max_frame) + ",1")); - } else { - p_list->push_back(PropertyInfo(Variant::FLOAT, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01")); - } - } - - if (same_track_type) { - switch (animation->track_get_type(first_track)) { - case Animation::TYPE_POSITION_3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); - } break; - case Animation::TYPE_ROTATION_3D: { - p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale")); - } break; - case Animation::TYPE_SCALE_3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale")); - } break; - case Animation::TYPE_BLEND_SHAPE: { - p_list->push_back(PropertyInfo(Variant::FLOAT, "value")); - } break; - case Animation::TYPE_VALUE: { - if (same_key_type) { - Variant v = animation->track_get_key_value(first_track, first_key); + if (same_track_type) { + switch (animation->track_get_type(first_track)) { + case Animation::TYPE_POSITION_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); + } break; + case Animation::TYPE_ROTATION_3D: { + p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale")); + } break; + case Animation::TYPE_SCALE_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale")); + } break; + case Animation::TYPE_BLEND_SHAPE: { + p_list->push_back(PropertyInfo(Variant::FLOAT, "value")); + } break; + case Animation::TYPE_VALUE: { + if (same_key_type) { + Variant v = animation->track_get_key_value(first_track, first_key); - if (hint.type != Variant::NIL) { - PropertyInfo pi = hint; - pi.name = "value"; - p_list->push_back(pi); - } else { - PropertyHint val_hint = PROPERTY_HINT_NONE; - String val_hint_string; - - if (v.get_type() == Variant::OBJECT) { - // Could actually check the object property if exists..? Yes I will! - Ref<Resource> res = v; - if (res.is_valid()) { - val_hint = PROPERTY_HINT_RESOURCE_TYPE; - val_hint_string = res->get_class(); - } + if (hint.type != Variant::NIL) { + PropertyInfo pi = hint; + pi.name = "value"; + p_list->push_back(pi); + } else { + PropertyHint val_hint = PROPERTY_HINT_NONE; + String val_hint_string; + + if (v.get_type() == Variant::OBJECT) { + // Could actually check the object property if exists..? Yes I will! + Ref<Resource> res = v; + if (res.is_valid()) { + val_hint = PROPERTY_HINT_RESOURCE_TYPE; + val_hint_string = res->get_class(); } + } - if (v.get_type() != Variant::NIL) { - p_list->push_back(PropertyInfo(v.get_type(), "value", val_hint, val_hint_string)); - } + if (v.get_type() != Variant::NIL) { + p_list->push_back(PropertyInfo(v.get_type(), "value", val_hint, val_hint_string)); } } + } - p_list->push_back(PropertyInfo(Variant::FLOAT, "easing", PROPERTY_HINT_EXP_EASING)); - } break; - case Animation::TYPE_METHOD: { - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "name")); + p_list->push_back(PropertyInfo(Variant::FLOAT, "easing", PROPERTY_HINT_EXP_EASING)); + } break; + case Animation::TYPE_METHOD: { + p_list->push_back(PropertyInfo(Variant::STRING_NAME, "name")); - p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,32,1,or_greater")); + p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,32,1,or_greater")); - Dictionary d = animation->track_get_key_value(first_track, first_key); - ERR_FAIL_COND(!d.has("args")); - Vector<Variant> args = d["args"]; - String vtypes; - for (int i = 0; i < Variant::VARIANT_MAX; i++) { - if (i > 0) { - vtypes += ","; - } - vtypes += Variant::get_type_name(Variant::Type(i)); + Dictionary d = animation->track_get_key_value(first_track, first_key); + ERR_FAIL_COND(!d.has("args")); + Vector<Variant> args = d["args"]; + String vtypes; + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (i > 0) { + vtypes += ","; } + vtypes += Variant::get_type_name(Variant::Type(i)); + } - for (int i = 0; i < args.size(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, "args/" + itos(i) + "/type", PROPERTY_HINT_ENUM, vtypes)); - if (args[i].get_type() != Variant::NIL) { - p_list->push_back(PropertyInfo(args[i].get_type(), "args/" + itos(i) + "/value")); - } - } - } break; - case Animation::TYPE_BEZIER: { - p_list->push_back(PropertyInfo(Variant::FLOAT, "value")); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "in_handle")); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "out_handle")); - p_list->push_back(PropertyInfo(Variant::INT, "handle_mode", PROPERTY_HINT_ENUM, "Free,Linear,Balanced,Mirrored")); - } break; - case Animation::TYPE_AUDIO: { - p_list->push_back(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "start_offset", PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "end_offset", PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater")); - } break; - case Animation::TYPE_ANIMATION: { - if (key_ofs_map.size() > 1) { - break; + for (int i = 0; i < args.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, "args/" + itos(i) + "/type", PROPERTY_HINT_ENUM, vtypes)); + if (args[i].get_type() != Variant::NIL) { + p_list->push_back(PropertyInfo(args[i].get_type(), "args/" + itos(i) + "/value")); } + } + } break; + case Animation::TYPE_BEZIER: { + p_list->push_back(PropertyInfo(Variant::FLOAT, "value")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "in_handle")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "out_handle")); + p_list->push_back(PropertyInfo(Variant::INT, "handle_mode", PROPERTY_HINT_ENUM, "Free,Linear,Balanced,Mirrored")); + } break; + case Animation::TYPE_AUDIO: { + p_list->push_back(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); + p_list->push_back(PropertyInfo(Variant::FLOAT, "start_offset", PROPERTY_HINT_RANGE, "0,3600,0.0001,or_greater")); + p_list->push_back(PropertyInfo(Variant::FLOAT, "end_offset", PROPERTY_HINT_RANGE, "0,3600,0.0001,or_greater")); + } break; + case Animation::TYPE_ANIMATION: { + if (key_ofs_map.size() > 1) { + break; + } - String animations; - - if (root_path && root_path->has_node(animation->track_get_path(first_track))) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node(animation->track_get_path(first_track))); - if (ap) { - List<StringName> anims; - ap->get_animation_list(&anims); - for (List<StringName>::Element *G = anims.front(); G; G = G->next()) { - if (!animations.is_empty()) { - animations += ","; - } + String animations; - animations += String(G->get()); + if (root_path && root_path->has_node(animation->track_get_path(first_track))) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node(animation->track_get_path(first_track))); + if (ap) { + List<StringName> anims; + ap->get_animation_list(&anims); + for (List<StringName>::Element *G = anims.front(); G; G = G->next()) { + if (!animations.is_empty()) { + animations += ","; } + + animations += String(G->get()); } } + } - if (!animations.is_empty()) { - animations += ","; - } - animations += "[stop]"; + if (!animations.is_empty()) { + animations += ","; + } + animations += "[stop]"; - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "animation", PROPERTY_HINT_ENUM, animations)); - } break; - } + p_list->push_back(PropertyInfo(Variant::STRING_NAME, "animation", PROPERTY_HINT_ENUM, animations)); + } break; } } +} - Ref<Animation> animation; - - RBMap<int, List<float>> key_ofs_map; - RBMap<int, NodePath> base_map; - PropertyInfo hint; - - Node *root_path = nullptr; - - bool use_fps = false; - - void notify_change() { - notify_property_list_changed(); - } +void AnimationMultiTrackKeyEdit::notify_change() { + notify_property_list_changed(); +} - Node *get_root_path() { - return root_path; - } +Node *AnimationMultiTrackKeyEdit::get_root_path() { + return root_path; +} - void set_use_fps(bool p_enable) { - use_fps = p_enable; - notify_property_list_changed(); - } -}; +void AnimationMultiTrackKeyEdit::set_use_fps(bool p_enable) { + use_fps = p_enable; + notify_property_list_changed(); +} void AnimationTimelineEdit::_zoom_changed(double) { queue_redraw(); @@ -1420,7 +1246,7 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) { return; } - p_new_len = MAX(0.001, p_new_len); + p_new_len = MAX(0.0001, p_new_len); if (use_fps && animation->get_step() > 0) { p_new_len *= animation->get_step(); } @@ -1539,7 +1365,7 @@ void AnimationTimelineEdit::_notification(int p_what) { float l = animation->get_length(); if (l <= 0) { - l = 0.001; // Avoid crashor. + l = 0.0001; // Avoid crashor. } Ref<Texture2D> hsize_icon = get_theme_icon(SNAME("Hsize"), SNAME("EditorIcons")); @@ -1600,7 +1426,7 @@ void AnimationTimelineEdit::_notification(int p_what) { end_px = zoomw; } - draw_rect(Rect2(Point2(get_name_limit() + begin_px, 0), Point2(end_px - begin_px - 1, h)), timecolor); + draw_rect(Rect2(Point2(get_name_limit() + begin_px, 0), Point2(end_px - begin_px, h)), timecolor); } } @@ -1759,7 +1585,7 @@ void AnimationTimelineEdit::update_values() { } } else { length->set_value(animation->get_length()); - length->set_step(0.001); + length->set_step(0.0001); length->set_tooltip_text(TTR("Animation length (seconds)")); time_icon->set_tooltip_text(TTR("Animation length (seconds)")); } @@ -1950,9 +1776,9 @@ AnimationTimelineEdit::AnimationTimelineEdit() { time_icon->set_tooltip_text(TTR("Animation length (seconds)")); len_hb->add_child(time_icon); length = memnew(EditorSpinSlider); - length->set_min(0.001); + length->set_min(0.0001); length->set_max(36000); - length->set_step(0.001); + length->set_step(0.0001); length->set_allow_greater(true); length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0)); length->set_hide_slider(true); @@ -2058,7 +1884,7 @@ void AnimationTrackEdit::_notification(int p_what) { } else if (animation->track_get_type(track) == Animation::TYPE_AUDIO) { text = TTR("Audio Clips:"); } else if (animation->track_get_type(track) == Animation::TYPE_ANIMATION) { - text = TTR("Anim Clips:"); + text = TTR("Animation Clips:"); } else { text += anim_path.get_concatenated_subnames(); } @@ -2670,7 +2496,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { } if (key_idx != -1) { - String text = TTR("Time (s):") + " " + rtos(animation->track_get_key_time(track, key_idx)) + "\n"; + String text = TTR("Time (s):") + " " + TS->format_number(rtos(Math::snapped(animation->track_get_key_time(track, key_idx), 0.0001))) + "\n"; switch (animation->track_get_type(track)) { case Animation::TYPE_POSITION_3D: { Vector3 t = animation->track_get_key_value(track, key_idx); @@ -4390,7 +4216,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD p_next_tracks.normal++; } else { undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_id.track_idx, time); - int existing = animation->track_find_key(p_id.track_idx, time, true); + int existing = animation->track_find_key(p_id.track_idx, time, Animation::FIND_MODE_APPROX); if (existing != -1) { Variant v = animation->track_get_key_value(p_id.track_idx, existing); float trans = animation->track_get_key_transition(p_id.track_idx, existing); @@ -5005,8 +4831,8 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { if (snap->is_pressed() && step->get_value() != 0) { p_ofs = snap_time(p_ofs); } - while (animation->track_find_key(p_track, p_ofs, true) != -1) { // Make sure insertion point is valid. - p_ofs += 0.001; + while (animation->track_find_key(p_track, p_ofs, Animation::FIND_MODE_APPROX) != -1) { // Make sure insertion point is valid. + p_ofs += 0.0001; } Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); @@ -5338,7 +5164,7 @@ void AnimationTrackEditor::_select_at_anim(const Ref<Animation> &p_anim, int p_t return; } - int idx = animation->track_find_key(p_track, p_pos, true); + int idx = animation->track_find_key(p_track, p_pos, Animation::FIND_MODE_APPROX); ERR_FAIL_COND(idx < 0); SelectedKey sk; @@ -5365,7 +5191,7 @@ void AnimationTrackEditor::_move_selection_commit() { // 2 - Remove overlapped keys. for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) { float newtime = snap_time(E->get().pos + motion); - int idx = animation->track_find_key(E->key().track, newtime, true); + int idx = animation->track_find_key(E->key().track, newtime, Animation::FIND_MODE_APPROX); if (idx == -1) { continue; } @@ -5625,7 +5451,7 @@ void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) { continue; } - int existing_idx = animation->track_find_key(dst_track, dst_time, true); + int existing_idx = animation->track_find_key(dst_track, dst_time, Animation::FIND_MODE_APPROX); undo_redo->add_do_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key)); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", dst_track, dst_time); @@ -5916,7 +5742,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { // 2 - Remove overlapped keys. for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) { float newtime = (E->get().pos - from_t) * s + from_t; - int idx = animation->track_find_key(E->key().track, newtime, true); + int idx = animation->track_find_key(E->key().track, newtime, Animation::FIND_MODE_APPROX); if (idx == -1) { continue; } @@ -6127,7 +5953,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { undo_redo->add_do_method(reset.ptr(), "track_set_path", dst_track, path); undo_redo->add_undo_method(reset.ptr(), "remove_track", dst_track); } else { - existing_idx = reset->track_find_key(dst_track, 0, true); + existing_idx = reset->track_find_key(dst_track, 0, Animation::FIND_MODE_APPROX); } undo_redo->add_do_method(reset.ptr(), "track_insert_key", dst_track, 0, animation->track_get_key_value(sk.track, sk.key), animation->track_get_key_transition(sk.track, sk.key)); @@ -6656,7 +6482,7 @@ AnimationTrackEditor::AnimationTrackEditor() { step = memnew(EditorSpinSlider); step->set_min(0); step->set_max(1000000); - step->set_step(0.001); + step->set_step(0.0001); step->set_hide_slider(true); step->set_custom_minimum_size(Size2(100, 0) * EDSCALE); step->set_tooltip_text(TTR("Animation step value.")); @@ -6946,3 +6772,103 @@ AnimationTrackEditor::~AnimationTrackEditor() { memdelete(multi_key_edit); } } + +// AnimationTrackKeyEditEditorPlugin + +void AnimationTrackKeyEditEditor::_time_edit_entered() { + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + if (key == -1) { + return; + } + key_data_cache.time = animation->track_get_key_time(track, key); + key_data_cache.transition = animation->track_get_key_transition(track, key); + key_data_cache.value = animation->track_get_key_value(track, key); +} + +void AnimationTrackKeyEditEditor::_time_edit_exited() { + real_t new_time = spinner->get_value(); + + if (use_fps) { + real_t fps = animation->get_step(); + if (fps > 0) { + fps = 1.0 / fps; + } + new_time /= fps; + } + + if (Math::is_equal_approx(new_time, key_data_cache.time)) { + return; // No change. + } + + int existing = animation->track_find_key(track, new_time, Animation::FIND_MODE_APPROX); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Animation Change Keyframe Time"), UndoRedo::MERGE_ENDS); + + if (existing != -1) { + undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, animation->track_get_key_time(track, existing)); + } + undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, key_data_cache.time); + undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, key_data_cache.value, key_data_cache.transition); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, new_time); + undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_data_cache.time, key_data_cache.value, key_data_cache.transition); + if (existing != -1) { + undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, animation->track_get_key_time(track, existing), animation->track_get_key_value(track, existing), animation->track_get_key_transition(track, existing)); + } + + // Reselect key. + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + AnimationTrackEditor *ate = ape->get_track_editor(); + if (ate) { + undo_redo->add_do_method(ate, "_clear_selection_for_anim", animation); + undo_redo->add_undo_method(ate, "_clear_selection_for_anim", animation); + undo_redo->add_do_method(ate, "_select_at_anim", animation, track, new_time); + undo_redo->add_undo_method(ate, "_select_at_anim", animation, track, key_data_cache.time); + } + } + + undo_redo->commit_action(); +} + +AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animation, int p_track, real_t p_key_ofs, bool p_use_fps) { + if (!p_animation.is_valid()) { + return; + } + + animation = p_animation; + track = p_track; + key_ofs = p_key_ofs; + use_fps = p_use_fps; + + set_label("Time"); + + spinner = memnew(EditorSpinSlider); + spinner->set_focus_mode(Control::FOCUS_CLICK); + spinner->set_min(0); + spinner->set_allow_greater(true); + spinner->set_allow_lesser(true); + + if (use_fps) { + spinner->set_step(1); + spinner->set_hide_slider(true); + real_t fps = animation->get_step(); + if (fps > 0) { + fps = 1.0 / fps; + } + spinner->set_value(key_ofs * fps); + } else { + spinner->set_step(0.0001); + spinner->set_value(key_ofs); + spinner->set_max(animation->get_length()); + } + + add_child(spinner); + + spinner->connect("grabbed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED); + spinner->connect("ungrabbed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED); + spinner->connect("value_focus_entered", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED); + spinner->connect("value_focus_exited", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED); +} + +AnimationTrackKeyEditEditor::~AnimationTrackKeyEditEditor() { +} diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 4b50424f39..5ae826bd5c 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -50,9 +50,83 @@ #include "scene/resources/animation.h" #include "scene_tree_editor.h" +class AnimationTrackEditor; class AnimationTrackEdit; class ViewPanner; +class AnimationTrackKeyEdit : public Object { + GDCLASS(AnimationTrackKeyEdit, Object); + +public: + bool setting = false; + bool animation_read_only = false; + + Ref<Animation> animation; + int track = -1; + float key_ofs = 0; + Node *root_path = nullptr; + + PropertyInfo hint; + NodePath base; + bool use_fps = false; + + bool _hide_script_from_inspector() { return true; } + bool _hide_metadata_from_inspector() { return true; } + bool _dont_undo_redo() { return true; } + + bool _is_read_only() { return animation_read_only; } + + void notify_change(); + Node *get_root_path(); + void set_use_fps(bool p_enable); + +protected: + static void _bind_methods(); + void _fix_node_path(Variant &value); + void _update_obj(const Ref<Animation> &p_anim); + void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; +}; + +class AnimationMultiTrackKeyEdit : public Object { + GDCLASS(AnimationMultiTrackKeyEdit, Object); + +public: + bool setting = false; + bool animation_read_only = false; + + Ref<Animation> animation; + + RBMap<int, List<float>> key_ofs_map; + RBMap<int, NodePath> base_map; + PropertyInfo hint; + + Node *root_path = nullptr; + + bool use_fps = false; + + bool _hide_script_from_inspector() { return true; } + bool _hide_metadata_from_inspector() { return true; } + bool _dont_undo_redo() { return true; } + + bool _is_read_only() { return animation_read_only; } + + void notify_change(); + Node *get_root_path(); + void set_use_fps(bool p_enable); + +protected: + static void _bind_methods(); + void _fix_node_path(Variant &value, NodePath &base); + void _update_obj(const Ref<Animation> &p_anim); + void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; +}; + class AnimationTimelineEdit : public Range { GDCLASS(AnimationTimelineEdit, Range); @@ -129,8 +203,6 @@ public: AnimationTimelineEdit(); }; -class AnimationTrackEditor; - class AnimationTrackEdit : public Control { GDCLASS(AnimationTrackEdit, Control); friend class AnimationTimelineEdit; @@ -592,4 +664,30 @@ public: ~AnimationTrackEditor(); }; +// AnimationTrackKeyEditEditorPlugin + +class AnimationTrackKeyEditEditor : public EditorProperty { + GDCLASS(AnimationTrackKeyEditEditor, EditorProperty); + + Ref<Animation> animation; + int track = -1; + real_t key_ofs = 0.0; + bool use_fps = false; + + EditorSpinSlider *spinner = nullptr; + + struct KeyDataCache { + real_t time = 0.0; + float transition = 0.0; + Variant value; + } key_data_cache; + + void _time_edit_entered(); + void _time_edit_exited(); + +public: + AnimationTrackKeyEditEditor(Ref<Animation> p_animation, int p_track, real_t p_key_ofs, bool p_use_fps); + ~AnimationTrackKeyEditEditor(); +}; + #endif // ANIMATION_TRACK_EDITOR_H diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp index 704935e163..0ad62710eb 100644 --- a/editor/animation_track_editor_plugins.cpp +++ b/editor/animation_track_editor_plugins.cpp @@ -831,8 +831,8 @@ Rect2 AnimationTrackEditTypeAudio::get_key_rect(int p_index, float p_pixels_sec) len -= end_ofs; len -= start_ofs; - if (len <= 0.001) { - len = 0.001; + if (len <= 0.0001) { + len = 0.0001; } if (get_animation()->track_get_key_count(get_track()) > p_index + 1) { @@ -887,8 +887,8 @@ void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int len -= end_ofs; len -= start_ofs; - if (len <= 0.001) { - len = 0.001; + if (len <= 0.0001) { + len = 0.0001; } int pixel_len = len * p_pixels_sec; @@ -1014,8 +1014,8 @@ void AnimationTrackEditTypeAudio::drop_data(const Point2 &p_point, const Variant ofs = get_editor()->snap_time(ofs); - while (get_animation()->track_find_key(get_track(), ofs, true) != -1) { //make sure insertion point is valid - ofs += 0.001; + while (get_animation()->track_find_key(get_track(), ofs, Animation::FIND_MODE_APPROX) != -1) { //make sure insertion point is valid + ofs += 0.0001; } Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index 35666a5566..0f8b2f36be 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -106,7 +106,7 @@ void EditorProfiler::clear() { seeking = false; // Ensure button text (start, stop) is correct - _set_button_text(); + _update_button_text(); emit_signal(SNAME("enable_profiling"), activate->is_pressed()); } @@ -376,7 +376,7 @@ void EditorProfiler::_update_frame() { updating_frame = false; } -void EditorProfiler::_set_button_text() { +void EditorProfiler::_update_button_text() { if (activate->is_pressed()) { activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); activate->set_text(TTR("Stop")); @@ -387,7 +387,7 @@ void EditorProfiler::_set_button_text() { } void EditorProfiler::_activate_pressed() { - _set_button_text(); + _update_button_text(); if (activate->is_pressed()) { _clear_pressed(); @@ -510,13 +510,17 @@ void EditorProfiler::_bind_methods() { } void EditorProfiler::set_enabled(bool p_enable, bool p_clear) { - activate->set_pressed(false); activate->set_disabled(!p_enable); if (p_clear) { clear(); } } +void EditorProfiler::set_pressed(bool p_pressed) { + activate->set_pressed(p_pressed); + _update_button_text(); +} + bool EditorProfiler::is_profiling() { return activate->is_pressed(); } @@ -595,6 +599,7 @@ EditorProfiler::EditorProfiler() { add_child(hb); activate = memnew(Button); activate->set_toggle_mode(true); + activate->set_disabled(true); activate->set_text(TTR("Start")); activate->connect("pressed", callable_mp(this, &EditorProfiler::_activate_pressed)); hb->add_child(activate); diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h index e9ecc285ed..d0dd67688b 100644 --- a/editor/debugger/editor_profiler.h +++ b/editor/debugger/editor_profiler.h @@ -122,7 +122,7 @@ private: Timer *frame_delay = nullptr; Timer *plot_delay = nullptr; - void _set_button_text(); + void _update_button_text(); void _update_frame(); void _activate_pressed(); @@ -155,6 +155,7 @@ protected: public: void add_frame_metric(const Metric &p_metric, bool p_final = false); void set_enabled(bool p_enable, bool p_clear = true); + void set_pressed(bool p_pressed); bool is_profiling(); bool is_seeking() { return seeking; } void disable_seeking(); diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index b8bc712ba6..fe425220c9 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -66,6 +66,7 @@ void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) { } updating_frame = true; + clear_button->set_disabled(false); cursor_metric_edit->set_max(frame_metrics[last_metric].frame_number); cursor_metric_edit->set_min(MAX(frame_metrics[last_metric].frame_number - frame_metrics.size(), 0u)); @@ -408,6 +409,7 @@ void EditorVisualProfiler::_activate_pressed() { activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); activate->set_text(TTR("Stop")); _clear_pressed(); //always clear on start + clear_button->set_disabled(false); } else { activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); activate->set_text(TTR("Start")); @@ -416,6 +418,7 @@ void EditorVisualProfiler::_activate_pressed() { } void EditorVisualProfiler::_clear_pressed() { + clear_button->set_disabled(true); clear(); _update_plot(); } @@ -647,10 +650,25 @@ void EditorVisualProfiler::_bind_methods() { ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable"))); } +void EditorVisualProfiler::_update_button_text() { + if (activate->is_pressed()) { + activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); + activate->set_text(TTR("Stop")); + } else { + activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); + activate->set_text(TTR("Start")); + } +} + void EditorVisualProfiler::set_enabled(bool p_enable) { activate->set_disabled(!p_enable); } +void EditorVisualProfiler::set_pressed(bool p_pressed) { + activate->set_pressed(p_pressed); + _update_button_text(); +} + bool EditorVisualProfiler::is_profiling() { return activate->is_pressed(); } @@ -714,12 +732,14 @@ EditorVisualProfiler::EditorVisualProfiler() { add_child(hb); activate = memnew(Button); activate->set_toggle_mode(true); + activate->set_disabled(true); activate->set_text(TTR("Start")); activate->connect("pressed", callable_mp(this, &EditorVisualProfiler::_activate_pressed)); hb->add_child(activate); clear_button = memnew(Button); clear_button->set_text(TTR("Clear")); + clear_button->set_disabled(true); clear_button->connect("pressed", callable_mp(this, &EditorVisualProfiler::_clear_pressed)); hb->add_child(clear_button); diff --git a/editor/debugger/editor_visual_profiler.h b/editor/debugger/editor_visual_profiler.h index 8aa9e7b308..8180d354e8 100644 --- a/editor/debugger/editor_visual_profiler.h +++ b/editor/debugger/editor_visual_profiler.h @@ -101,6 +101,8 @@ private: Timer *frame_delay = nullptr; Timer *plot_delay = nullptr; + void _update_button_text(); + void _update_frame(bool p_focus_selected = false); void _activate_pressed(); @@ -133,6 +135,7 @@ protected: public: void add_frame_metric(const Metric &p_metric); void set_enabled(bool p_enable); + void set_pressed(bool p_pressed); bool is_profiling(); bool is_seeking() { return seeking; } void disable_seeking(); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index deca638f3b..f6b00b83b0 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -319,6 +319,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da tabs->set_current_tab(0); } profiler->set_enabled(false, false); + visual_profiler->set_enabled(false); inspector->clear_cache(); // Take a chance to force remote objects update. } else if (p_msg == "debug_exit") { @@ -328,8 +329,12 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da _update_buttons_state(); _set_reason_text(TTR("Execution resumed."), MESSAGE_SUCCESS); emit_signal(SNAME("breaked"), false, false, "", false); + profiler->set_enabled(true, false); profiler->disable_seeking(); + + visual_profiler->set_enabled(true); + } else if (p_msg == "set_pid") { ERR_FAIL_COND(p_data.size() < 1); remote_pid = p_data[0]; @@ -751,7 +756,16 @@ void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType reason->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); } reason->set_text(p_reason); - reason->set_tooltip_text(p_reason.word_wrap(80)); + + const PackedInt32Array boundaries = TS->string_get_word_breaks(p_reason, "", 80); + PackedStringArray lines; + for (int i = 0; i < boundaries.size(); i += 2) { + const int start = boundaries[i]; + const int end = boundaries[i + 1]; + lines.append(p_reason.substr(start, end - start + 1)); + } + + reason->set_tooltip_text(String("\n").join(lines)); } void ScriptEditorDebugger::_notification(int p_what) { @@ -892,6 +906,7 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { stop(); profiler->set_enabled(true, true); + visual_profiler->set_enabled(true); peer = p_peer; ERR_FAIL_COND(p_peer.is_null()); @@ -948,7 +963,11 @@ void ScriptEditorDebugger::stop() { res_path_cache.clear(); profiler_signature.clear(); - profiler->set_enabled(true, false); + profiler->set_enabled(false, false); + profiler->set_pressed(false); + + visual_profiler->set_enabled(false); + visual_profiler->set_pressed(false); inspector->edit(nullptr); _update_buttons_state(); diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index f15b874c45..e593d652ad 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -947,9 +947,10 @@ Variant EditorData::script_class_instance(const String &p_class) { if (ScriptServer::is_global_class(p_class)) { Ref<Script> script = script_class_load_script(p_class); if (script.is_valid()) { - Object *obj = ClassDB::instantiate(script->get_instance_base_type()); + // Store in a variant to initialize the refcount if needed. + Variant obj = ClassDB::instantiate(script->get_instance_base_type()); if (obj) { - obj->set_script(script); + obj.operator Object *()->set_script(script); } return obj; } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index a5397a8e6a..88a1bbb4a9 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1172,6 +1172,36 @@ void EditorInspectorSection::_test_unfold() { } } +Ref<Texture2D> EditorInspectorSection::_get_arrow() { + Ref<Texture2D> arrow; + if (foldable) { + if (object->editor_is_section_unfolded(section)) { + arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree")); + } else { + if (is_layout_rtl()) { + arrow = get_theme_icon(SNAME("arrow_collapsed_mirrored"), SNAME("Tree")); + } else { + arrow = get_theme_icon(SNAME("arrow_collapsed"), SNAME("Tree")); + } + } + } + return arrow; +} + +int EditorInspectorSection::_get_header_height() { + Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); + int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); + + int header_height = font->get_height(font_size); + Ref<Texture2D> arrow = _get_arrow(); + if (arrow.is_valid()) { + header_height = MAX(header_height, arrow->get_height()); + } + header_height += get_theme_constant(SNAME("v_separation"), SNAME("Tree")); + + return header_height; +} + void EditorInspectorSection::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { @@ -1182,30 +1212,6 @@ void EditorInspectorSection::_notification(int p_what) { if (!vbox_added) { return; } - // Get the section header font. - Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); - int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); - - // Get the right direction arrow texture, if the section is foldable. - Ref<Texture2D> arrow; - if (foldable) { - if (object->editor_is_section_unfolded(section)) { - arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree")); - } else { - if (is_layout_rtl()) { - arrow = get_theme_icon(SNAME("arrow_collapsed_mirrored"), SNAME("Tree")); - } else { - arrow = get_theme_icon(SNAME("arrow_collapsed"), SNAME("Tree")); - } - } - } - - // Compute the height of the section header. - int header_height = font->get_height(font_size); - if (arrow.is_valid()) { - header_height = MAX(header_height, arrow->get_height()); - } - header_height += get_theme_constant(SNAME("v_separation"), SNAME("Tree")); int inspector_margin = get_theme_constant(SNAME("inspector_margin"), SNAME("Editor")); int section_indent_size = get_theme_constant(SNAME("indent_size"), SNAME("EditorInspectorSection")); @@ -1218,6 +1224,7 @@ void EditorInspectorSection::_notification(int p_what) { } Size2 size = get_size() - Vector2(inspector_margin, 0); + int header_height = _get_header_height(); Vector2 offset = Vector2(is_layout_rtl() ? 0 : inspector_margin, header_height); for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -1233,36 +1240,6 @@ void EditorInspectorSection::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { - // Get the section header font. - Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); - int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); - Color font_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); - - // Get the right direction arrow texture, if the section is foldable. - Ref<Texture2D> arrow; - bool folded = foldable; - if (foldable) { - if (object->editor_is_section_unfolded(section)) { - arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree")); - folded = false; - } else { - if (is_layout_rtl()) { - arrow = get_theme_icon(SNAME("arrow_collapsed_mirrored"), SNAME("Tree")); - } else { - arrow = get_theme_icon(SNAME("arrow_collapsed"), SNAME("Tree")); - } - } - } - - bool rtl = is_layout_rtl(); - - // Compute the height and width of the section header. - int header_height = font->get_height(font_size); - if (arrow.is_valid()) { - header_height = MAX(header_height, arrow->get_height()); - } - header_height += get_theme_constant(SNAME("v_separation"), SNAME("Tree")); - int section_indent = 0; int section_indent_size = get_theme_constant(SNAME("indent_size"), SNAME("EditorInspectorSection")); if (indent_depth > 0 && section_indent_size > 0) { @@ -1275,11 +1252,13 @@ void EditorInspectorSection::_notification(int p_what) { int header_width = get_size().width - section_indent; int header_offset_x = 0.0; + bool rtl = is_layout_rtl(); if (!rtl) { header_offset_x += section_indent; } // Draw header area. + int header_height = _get_header_height(); Rect2 header_rect = Rect2(Vector2(header_offset_x, 0.0), Vector2(header_width, header_height)); Color c = bg_color; c.a *= 0.4; @@ -1288,7 +1267,7 @@ void EditorInspectorSection::_notification(int p_what) { } draw_rect(header_rect, c); - // Draw header title, folding arrow and coutn of revertable properties. + // Draw header title, folding arrow and count of revertable properties. { int separation = Math::round(2 * EDSCALE); @@ -1296,6 +1275,7 @@ void EditorInspectorSection::_notification(int p_what) { int margin_end = separation; // - Arrow. + Ref<Texture2D> arrow = _get_arrow(); if (arrow.is_valid()) { Point2 arrow_position; if (rtl) { @@ -1313,6 +1293,13 @@ void EditorInspectorSection::_notification(int p_what) { // - Count of revertable properties. String num_revertable_str; int num_revertable_width = 0; + + bool folded = foldable && !object->editor_is_section_unfolded(section); + + Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); + int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); + Color font_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); + if (folded && revertable_properties.size()) { int label_width = font->get_string_size(label, HORIZONTAL_ALIGNMENT_LEFT, available, font_size, TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS).x; @@ -1481,10 +1468,12 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { - Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree")); - int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree")); - if (mb->get_position().y > font->get_height(font_size)) { //clicked outside - return; + if (object->editor_is_section_unfolded(section)) { + int header_height = _get_header_height(); + + if (mb->get_position().y >= header_height) { + return; + } } accept_event(); @@ -2597,7 +2586,7 @@ bool EditorInspector::_is_property_disabled_by_feature_profile(const StringName return false; } -void EditorInspector::update_tree() { +void EditorInspector::_update_tree() { //to update properly if all is refreshed StringName current_selected = property_selected; int current_focusable = -1; @@ -3316,6 +3305,10 @@ void EditorInspector::update_tree() { } } +void EditorInspector::update_tree() { + update_tree_pending = true; +} + void EditorInspector::update_property(const String &p_prop) { if (!editor_property_map.has(p_prop)) { return; @@ -3910,10 +3903,9 @@ void EditorInspector::_notification(int p_what) { changing++; if (update_tree_pending) { - update_tree(); update_tree_pending = false; pending.clear(); - + _update_tree(); } else { while (pending.size()) { StringName prop = *pending.begin(); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 41651494ce..53490f0880 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -280,6 +280,8 @@ class EditorInspectorSection : public Container { HashSet<StringName> revertable_properties; void _test_unfold(); + int _get_header_height(); + Ref<Texture2D> _get_arrow(); protected: Object *object = nullptr; @@ -540,6 +542,8 @@ class EditorInspector : public ScrollContainer { void _show_add_meta_dialog(); void _check_meta_name(const String &p_name); + void _update_tree(); + protected: static void _bind_methods(); void _notification(int p_what); diff --git a/editor/editor_layouts_dialog.cpp b/editor/editor_layouts_dialog.cpp index 886e29a504..19b137c7fd 100644 --- a/editor/editor_layouts_dialog.cpp +++ b/editor/editor_layouts_dialog.cpp @@ -33,6 +33,7 @@ #include "core/io/config_file.h" #include "core/object/class_db.h" #include "core/os/keyboard.h" +#include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "scene/gui/item_list.h" #include "scene/gui/line_edit.h" @@ -97,6 +98,11 @@ void EditorLayoutsDialog::_post_popup() { for (const String &E : layouts) { layout_names->add_item(E); } + if (name->is_visible()) { + name->grab_focus(); + } else { + layout_names->grab_focus(); + } } EditorLayoutsDialog::EditorLayoutsDialog() { @@ -106,7 +112,9 @@ EditorLayoutsDialog::EditorLayoutsDialog() { makevb->set_anchor_and_offset(SIDE_RIGHT, Control::ANCHOR_END, -5); layout_names = memnew(ItemList); - makevb->add_child(layout_names); + layout_names->set_auto_height(true); + makevb->add_margin_child(TTR("Select existing layout:"), layout_names); + layout_names->set_custom_minimum_size(Size2(300 * EDSCALE, 1)); layout_names->set_visible(true); layout_names->set_offset(SIDE_TOP, 5); layout_names->set_anchor_and_offset(SIDE_LEFT, Control::ANCHOR_BEGIN, 5); @@ -116,8 +124,10 @@ EditorLayoutsDialog::EditorLayoutsDialog() { layout_names->set_allow_rmb_select(true); name = memnew(LineEdit); + name->set_placeholder("Or enter new layout name"); makevb->add_child(name); name->set_offset(SIDE_TOP, 5); + name->set_custom_minimum_size(Size2(300 * EDSCALE, 1)); name->set_anchor_and_offset(SIDE_LEFT, Control::ANCHOR_BEGIN, 5); name->set_anchor_and_offset(SIDE_RIGHT, Control::ANCHOR_END, -5); name->connect("gui_input", callable_mp(this, &EditorLayoutsDialog::_line_gui_input)); diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 84284a7f31..a232c83783 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -286,7 +286,7 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) { // Remove last line if replacing, as it will be replace by the next added line. // Why "- 2"? RichTextLabel is weird. When you add a line with add_newline(), it also adds an element to the list of lines which is null/blank, // but it still counts as a line. So if you remove the last line (count - 1) you are actually removing nothing... - log->remove_line(log->get_paragraph_count() - 2); + log->remove_paragraph(log->get_paragraph_count() - 2); } switch (p_message.type) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 4c3c9973cb..01e605880c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -96,6 +96,7 @@ #include "editor/export/editor_export.h" #include "editor/export/export_template_manager.h" #include "editor/export/project_export.h" +#include "editor/fbx_importer_manager.h" #include "editor/filesystem_dock.h" #include "editor/history_dock.h" #include "editor/import/audio_stream_import_settings.h" @@ -2754,11 +2755,20 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { log->add_message(TTR("Can't undo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR); } else { String action = editor_data.get_undo_redo()->get_current_action_name(); - + int id = editor_data.get_undo_redo()->get_current_action_history_id(); if (!editor_data.get_undo_redo()->undo()) { log->add_message(TTR("Nothing to undo."), EditorLog::MSG_TYPE_EDITOR); } else if (!action.is_empty()) { - log->add_message(vformat(TTR("Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR); + switch (id) { + case EditorUndoRedoManager::GLOBAL_HISTORY: + log->add_message(vformat(TTR("Global Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR); + break; + case EditorUndoRedoManager::REMOTE_HISTORY: + log->add_message(vformat(TTR("Remote Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR); + break; + default: + log->add_message(vformat(TTR("Scene Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR); + } } } } break; @@ -2770,7 +2780,20 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { log->add_message(TTR("Nothing to redo."), EditorLog::MSG_TYPE_EDITOR); } else { String action = editor_data.get_undo_redo()->get_current_action_name(); - log->add_message(vformat(TTR("Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR); + if (action.is_empty()) { + break; + } + + switch (editor_data.get_undo_redo()->get_current_action_history_id()) { + case EditorUndoRedoManager::GLOBAL_HISTORY: + log->add_message(vformat(TTR("Global Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR); + break; + case EditorUndoRedoManager::REMOTE_HISTORY: + log->add_message(vformat(TTR("Remote Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR); + break; + default: + log->add_message(vformat(TTR("Scene Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR); + } } } } break; @@ -2959,7 +2982,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case SETTINGS_MANAGE_EXPORT_TEMPLATES: { export_template_manager->popup_manager(); - + } break; + case SETTINGS_MANAGE_FBX_IMPORTER: { +#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) + fbx_importer_manager->show_dialog(); +#endif } break; case SETTINGS_INSTALL_ANDROID_BUILD_TEMPLATE: { custom_build_manage_templates->hide(); @@ -5167,15 +5194,15 @@ void EditorNode::_layout_menu_option(int p_id) { current_menu_option = p_id; layout_dialog->set_title(TTR("Save Layout")); layout_dialog->set_ok_button_text(TTR("Save")); - layout_dialog->popup_centered(); layout_dialog->set_name_line_enabled(true); + layout_dialog->popup_centered(); } break; case SETTINGS_LAYOUT_DELETE: { current_menu_option = p_id; layout_dialog->set_title(TTR("Delete Layout")); layout_dialog->set_ok_button_text(TTR("Delete")); - layout_dialog->popup_centered(); layout_dialog->set_name_line_enabled(false); + layout_dialog->popup_centered(); } break; case SETTINGS_LAYOUT_DEFAULT: { _load_docks_from_config(default_layout, "docks"); @@ -6602,6 +6629,11 @@ EditorNode::EditorNode() { gui_base->add_child(about); feature_profile_manager->connect("current_feature_profile_changed", callable_mp(this, &EditorNode::_feature_profile_changed)); +#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) + fbx_importer_manager = memnew(FBXImporterManager); + gui_base->add_child(fbx_importer_manager); +#endif + warning = memnew(AcceptDialog); warning->add_button(TTR("Copy Text"), true, "copy"); gui_base->add_child(warning); @@ -6774,6 +6806,9 @@ EditorNode::EditorNode() { #ifndef ANDROID_ENABLED settings_menu->add_item(TTR("Manage Export Templates..."), SETTINGS_MANAGE_EXPORT_TEMPLATES); #endif +#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) + settings_menu->add_item(TTR("Configure FBX Importer..."), SETTINGS_MANAGE_FBX_IMPORTER); +#endif help_menu = memnew(PopupMenu); help_menu->set_name(TTR("Help")); @@ -7227,6 +7262,7 @@ EditorNode::EditorNode() { gui_base->add_child(disk_changed); add_editor_plugin(memnew(AnimationPlayerEditorPlugin)); + add_editor_plugin(memnew(AnimationTrackKeyEditEditorPlugin)); add_editor_plugin(memnew(CanvasItemEditorPlugin)); add_editor_plugin(memnew(Node3DEditorPlugin)); add_editor_plugin(memnew(ScriptEditorPlugin)); diff --git a/editor/editor_node.h b/editor/editor_node.h index c430662c7f..950970379d 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -77,6 +77,7 @@ class EditorSettingsDialog; class EditorToaster; class EditorUndoRedoManager; class ExportTemplateManager; +class FBXImporterManager; class FileDialog; class FileSystemDock; class HistoryDock; @@ -209,6 +210,7 @@ private: SETTINGS_EDITOR_DATA_FOLDER, SETTINGS_EDITOR_CONFIG_FOLDER, SETTINGS_MANAGE_EXPORT_TEMPLATES, + SETTINGS_MANAGE_FBX_IMPORTER, SETTINGS_MANAGE_FEATURE_PROFILES, SETTINGS_INSTALL_ANDROID_BUILD_TEMPLATE, SETTINGS_PICK_MAIN_SCENE, @@ -285,6 +287,8 @@ private: ProjectExportDialog *project_export = nullptr; ProjectSettingsEditor *project_settings_editor = nullptr; + FBXImporterManager *fbx_importer_manager = nullptr; + Vector<EditorPlugin *> editor_plugins; bool _initializing_plugins = false; HashMap<String, EditorPlugin *> addon_name_to_plugin; diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index fb3bf46c05..4c9b18efe7 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1384,10 +1384,11 @@ void EditorPropertyInteger::update_property() { void EditorPropertyInteger::_bind_methods() { } -void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) { +void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) { spin->set_min(p_min); spin->set_max(p_max); spin->set_step(p_step); + spin->set_hide_slider(p_hide_slider); spin->set_allow_greater(p_allow_greater); spin->set_allow_lesser(p_allow_lesser); spin->set_suffix(p_suffix); @@ -2242,12 +2243,11 @@ void EditorPropertyVector2i::_notification(int p_what) { } } -void EditorPropertyVector2i::setup(int p_min, int p_max, bool p_hide_slider, bool p_link, const String &p_suffix) { +void EditorPropertyVector2i::setup(int p_min, int p_max, bool p_link, const String &p_suffix) { for (int i = 0; i < 2; i++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); spin[i]->set_step(1); - spin[i]->set_hide_slider(p_hide_slider); spin[i]->set_allow_greater(true); spin[i]->set_allow_lesser(true); spin[i]->set_suffix(p_suffix); @@ -2352,12 +2352,11 @@ void EditorPropertyRect2i::_notification(int p_what) { void EditorPropertyRect2i::_bind_methods() { } -void EditorPropertyRect2i::setup(int p_min, int p_max, bool p_hide_slider, const String &p_suffix) { +void EditorPropertyRect2i::setup(int p_min, int p_max, const String &p_suffix) { for (int i = 0; i < 4; i++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); spin[i]->set_step(1); - spin[i]->set_hide_slider(p_hide_slider); spin[i]->set_allow_greater(true); spin[i]->set_allow_lesser(true); spin[i]->set_suffix(p_suffix); @@ -2496,12 +2495,12 @@ void EditorPropertyVector3i::_notification(int p_what) { void EditorPropertyVector3i::_bind_methods() { } -void EditorPropertyVector3i::setup(int p_min, int p_max, bool p_hide_slider, bool p_link, const String &p_suffix) { +void EditorPropertyVector3i::setup(int p_min, int p_max, bool p_link, const String &p_suffix) { for (int i = 0; i < 3; i++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); spin[i]->set_step(1); - spin[i]->set_hide_slider(p_hide_slider); + spin[i]->set_hide_slider(false); spin[i]->set_allow_greater(true); spin[i]->set_allow_lesser(true); spin[i]->set_suffix(p_suffix); @@ -3004,11 +3003,11 @@ void EditorPropertyVector4i::_notification(int p_what) { void EditorPropertyVector4i::_bind_methods() { } -void EditorPropertyVector4i::setup(double p_min, double p_max, bool p_hide_slider, const String &p_suffix) { +void EditorPropertyVector4i::setup(double p_min, double p_max, const String &p_suffix) { for (int i = 0; i < 4; i++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); - spin[i]->set_hide_slider(p_hide_slider); + spin[i]->set_step(1); spin[i]->set_allow_greater(true); spin[i]->set_allow_lesser(true); spin[i]->set_suffix(p_suffix); @@ -4347,7 +4346,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ EditorPropertyInteger *editor = memnew(EditorPropertyInteger); EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1); - editor->setup(hint.min, hint.max, hint.step, hint.or_greater, hint.or_less, hint.suffix); + editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.or_greater, hint.or_less, hint.suffix); return editor; } @@ -4475,7 +4474,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::VECTOR2I: { EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide)); EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1); - editor->setup(hint.min, hint.max, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix); + editor->setup(hint.min, hint.max, p_hint == PROPERTY_HINT_LINK, hint.suffix); return editor; } break; @@ -4488,7 +4487,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::RECT2I: { EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i(p_wide)); EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1); - editor->setup(hint.min, hint.max, hint.hide_slider, hint.suffix); + editor->setup(hint.min, hint.max, hint.suffix); return editor; } break; @@ -4502,7 +4501,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::VECTOR3I: { EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide)); EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1); - editor->setup(hint.min, hint.max, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix); + editor->setup(hint.min, hint.max, p_hint == PROPERTY_HINT_LINK, hint.suffix); return editor; } break; @@ -4516,7 +4515,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::VECTOR4I: { EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i); EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1); - editor->setup(hint.min, hint.max, hint.hide_slider, hint.suffix); + editor->setup(hint.min, hint.max, hint.suffix); return editor; } break; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index f38e33d9e3..042b94130b 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -378,7 +378,7 @@ protected: public: virtual void update_property() override; - void setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix = String()); + void setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix = String()); EditorPropertyInteger(); }; @@ -566,7 +566,7 @@ protected: public: virtual void update_property() override; - void setup(int p_min, int p_max, bool p_hide_slider, bool p_link = false, const String &p_suffix = String()); + void setup(int p_min, int p_max, bool p_link = false, const String &p_suffix = String()); EditorPropertyVector2i(bool p_force_wide = false); }; @@ -583,7 +583,7 @@ protected: public: virtual void update_property() override; - void setup(int p_min, int p_max, bool p_hide_slider, const String &p_suffix = String()); + void setup(int p_min, int p_max, const String &p_suffix = String()); EditorPropertyRect2i(bool p_force_wide = false); }; @@ -608,7 +608,7 @@ protected: public: virtual void update_property() override; - void setup(int p_min, int p_max, bool p_hide_slider, bool p_link = false, const String &p_suffix = String()); + void setup(int p_min, int p_max, bool p_link = false, const String &p_suffix = String()); EditorPropertyVector3i(bool p_force_wide = false); }; @@ -693,7 +693,7 @@ protected: public: virtual void update_property() override; - void setup(double p_min, double p_max, bool p_hide_slider, const String &p_suffix = String()); + void setup(double p_min, double p_max, const String &p_suffix = String()); EditorPropertyVector4i(); }; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index edda6c5d7b..451cb7cfee 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -919,7 +919,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::INT: { EditorPropertyInteger *editor = memnew(EditorPropertyInteger); - editor->setup(-100000, 100000, 1, true, true); + editor->setup(-100000, 100000, 1, false, true, true); prop = editor; } break; @@ -942,7 +942,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::VECTOR2I: { EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i); - editor->setup(-100000, 100000, true); + editor->setup(-100000, 100000); prop = editor; } break; @@ -954,7 +954,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::RECT2I: { EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i); - editor->setup(-100000, 100000, true); + editor->setup(-100000, 100000); prop = editor; } break; @@ -966,7 +966,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::VECTOR3I: { EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i); - editor->setup(-100000, 100000, true); + editor->setup(-100000, 100000); prop = editor; } break; @@ -978,7 +978,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::VECTOR4I: { EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i); - editor->setup(-100000, 100000, true); + editor->setup(-100000, 100000); prop = editor; } break; diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp index 9587e65cad..e8a0912d66 100644 --- a/editor/editor_property_name_processor.cpp +++ b/editor/editor_property_name_processor.cpp @@ -231,6 +231,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["tcp"] = "TCP"; capitalize_string_remaps["tls"] = "TLS"; capitalize_string_remaps["ui"] = "UI"; + capitalize_string_remaps["uri"] = "URI"; capitalize_string_remaps["url"] = "URL"; capitalize_string_remaps["urls"] = "URLs"; capitalize_string_remaps["us"] = String::utf8("(µs)"); // Unit. diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 9128143619..60c46835fe 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -76,6 +76,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) { pre_grab_value = get_value(); grabbing_spinner = false; grabbing_spinner_mouse_pos = get_global_mouse_position(); + emit_signal("grabbed"); } } else { if (grabbing_spinner_attempt) { @@ -83,6 +84,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) { Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); Input::get_singleton()->warp_mouse(grabbing_spinner_mouse_pos); queue_redraw(); + emit_signal("ungrabbed"); } else { _focus_entered(); } @@ -178,9 +180,11 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) { grabbing_ratio = get_as_ratio(); grabbing_from = grabber->get_transform().xform(mb->get_position()).x; } + emit_signal("grabbed"); } else { grabbing_grabber = false; mousewheel_over_grabber = false; + emit_signal("ungrabbed"); } } @@ -299,10 +303,6 @@ void EditorSpinSlider::_draw_spin_slider() { Ref<Texture2D> updown = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox")); - if (get_step() == 1) { - number_width -= updown->get_width(); - } - String numstr = get_text_value(); int vofs = (size.height - font->get_height(font_size)) / 2 + font->get_ascent(font_size); @@ -359,76 +359,79 @@ void EditorSpinSlider::_draw_spin_slider() { } TS->free_rid(num_rid); - if (get_step() == 1) { - Ref<Texture2D> updown2 = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox")); - int updown_vofs = (size.height - updown2->get_height()) / 2; - if (rtl) { - updown_offset = sb->get_margin(SIDE_LEFT); - } else { - updown_offset = size.width - sb->get_margin(SIDE_RIGHT) - updown2->get_width(); - } - Color c(1, 1, 1); - if (hover_updown) { - c *= Color(1.2, 1.2, 1.2); - } - draw_texture(updown2, Vector2(updown_offset, updown_vofs), c); - if (grabber->is_visible()) { - grabber->hide(); - } - } else if (!hide_slider) { - const int grabber_w = 4 * EDSCALE; - const int width = size.width - sb->get_minimum_size().width - grabber_w; - const int ofs = sb->get_offset().x; - const int svofs = (size.height + vofs) / 2 - 1; - Color c = fc; - - // Draw the horizontal slider's background. - c.a = 0.2; - draw_rect(Rect2(ofs, svofs + 1, width, 2 * EDSCALE), c); - - // Draw the horizontal slider's filled part on the left. - const int gofs = get_as_ratio() * width; - c.a = 0.45; - draw_rect(Rect2(ofs, svofs + 1, gofs, 2 * EDSCALE), c); - - // Draw the horizontal slider's grabber. - c.a = 0.9; - const Rect2 grabber_rect = Rect2(ofs + gofs, svofs, grabber_w, 4 * EDSCALE); - draw_rect(grabber_rect, c); - - grabbing_spinner_mouse_pos = get_global_position() + grabber_rect.get_center(); - - bool display_grabber = (grabbing_grabber || mouse_over_spin || mouse_over_grabber) && !grabbing_spinner && !(value_input_popup && value_input_popup->is_visible()); - if (grabber->is_visible() != display_grabber) { - if (display_grabber) { - grabber->show(); + if (!hide_slider) { + if (get_step() == 1) { + number_width -= updown->get_width(); + Ref<Texture2D> updown2 = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox")); + int updown_vofs = (size.height - updown2->get_height()) / 2; + if (rtl) { + updown_offset = sb->get_margin(SIDE_LEFT); } else { + updown_offset = size.width - sb->get_margin(SIDE_RIGHT) - updown2->get_width(); + } + Color c(1, 1, 1); + if (hover_updown) { + c *= Color(1.2, 1.2, 1.2); + } + draw_texture(updown2, Vector2(updown_offset, updown_vofs), c); + if (grabber->is_visible()) { grabber->hide(); } - } - - if (display_grabber) { - Ref<Texture2D> grabber_tex; - if (mouse_over_grabber) { - grabber_tex = get_theme_icon(SNAME("grabber_highlight"), SNAME("HSlider")); - } else { - grabber_tex = get_theme_icon(SNAME("grabber"), SNAME("HSlider")); + } else { + const int grabber_w = 4 * EDSCALE; + const int width = size.width - sb->get_minimum_size().width - grabber_w; + const int ofs = sb->get_offset().x; + const int svofs = (size.height + vofs) / 2 - 1; + Color c = fc; + + // Draw the horizontal slider's background. + c.a = 0.2; + draw_rect(Rect2(ofs, svofs + 1, width, 2 * EDSCALE), c); + + // Draw the horizontal slider's filled part on the left. + const int gofs = get_as_ratio() * width; + c.a = 0.45; + draw_rect(Rect2(ofs, svofs + 1, gofs, 2 * EDSCALE), c); + + // Draw the horizontal slider's grabber. + c.a = 0.9; + const Rect2 grabber_rect = Rect2(ofs + gofs, svofs, grabber_w, 4 * EDSCALE); + draw_rect(grabber_rect, c); + + grabbing_spinner_mouse_pos = get_global_position() + grabber_rect.get_center(); + + bool display_grabber = (grabbing_grabber || mouse_over_spin || mouse_over_grabber) && !grabbing_spinner && !(value_input_popup && value_input_popup->is_visible()); + if (grabber->is_visible() != display_grabber) { + if (display_grabber) { + grabber->show(); + } else { + grabber->hide(); + } } - if (grabber->get_texture() != grabber_tex) { - grabber->set_texture(grabber_tex); - } + if (display_grabber) { + Ref<Texture2D> grabber_tex; + if (mouse_over_grabber) { + grabber_tex = get_theme_icon(SNAME("grabber_highlight"), SNAME("HSlider")); + } else { + grabber_tex = get_theme_icon(SNAME("grabber"), SNAME("HSlider")); + } - Vector2 scale = get_global_transform_with_canvas().get_scale(); - grabber->set_scale(scale); - grabber->reset_size(); - grabber->set_position(get_global_position() + (grabber_rect.get_center() - grabber->get_size() * 0.5) * scale); + if (grabber->get_texture() != grabber_tex) { + grabber->set_texture(grabber_tex); + } - if (mousewheel_over_grabber) { - Input::get_singleton()->warp_mouse(grabber->get_position() + grabber_rect.size); - } + Vector2 scale = get_global_transform_with_canvas().get_scale(); + grabber->set_scale(scale); + grabber->reset_size(); + grabber->set_position(get_global_position() + (grabber_rect.get_center() - grabber->get_size() * 0.5) * scale); + + if (mousewheel_over_grabber) { + Input::get_singleton()->warp_mouse(grabber->get_position() + grabber_rect.size); + } - grabber_range = width; + grabber_range = width; + } } } } @@ -584,6 +587,8 @@ void EditorSpinSlider::_value_focus_exited() { //enter, click, esc grab_focus(); } + + emit_signal("value_focus_exited"); } void EditorSpinSlider::_grabber_mouse_entered() { @@ -627,6 +632,7 @@ void EditorSpinSlider::_focus_entered() { value_input->call_deferred(SNAME("select_all")); value_input->set_focus_next(find_next_valid_focus()->get_path()); value_input->set_focus_previous(find_prev_valid_focus()->get_path()); + emit_signal("value_focus_entered"); } void EditorSpinSlider::_bind_methods() { @@ -650,6 +656,11 @@ void EditorSpinSlider::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_only"), "set_read_only", "is_read_only"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_slider"), "set_hide_slider", "is_hiding_slider"); + + ADD_SIGNAL(MethodInfo("grabbed")); + ADD_SIGNAL(MethodInfo("ungrabbed")); + ADD_SIGNAL(MethodInfo("value_focus_entered")); + ADD_SIGNAL(MethodInfo("value_focus_exited")); } void EditorSpinSlider::_ensure_input_popup() { diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp index ab33b2d051..780ea2fd23 100644 --- a/editor/editor_undo_redo_manager.cpp +++ b/editor/editor_undo_redo_manager.cpp @@ -270,35 +270,9 @@ bool EditorUndoRedoManager::undo() { return false; } - int selected_history = INVALID_HISTORY; - double global_timestamp = 0; - - // Pick the history with greatest last action timestamp (either global or current scene). - { - History &history = get_or_create_history(GLOBAL_HISTORY); - if (!history.undo_stack.is_empty()) { - selected_history = history.id; - global_timestamp = history.undo_stack.back()->get().timestamp; - } - } - - { - History &history = get_or_create_history(REMOTE_HISTORY); - if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) { - selected_history = history.id; - global_timestamp = history.undo_stack.back()->get().timestamp; - } - } - - { - History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id()); - if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) { - selected_history = history.id; - } - } - - if (selected_history != INVALID_HISTORY) { - return undo_history(selected_history); + History *selected_history = _get_newest_undo(); + if (selected_history) { + return undo_history(selected_history->id); } return false; } @@ -427,33 +401,7 @@ void EditorUndoRedoManager::clear_history(bool p_increase_version, int p_idx) { String EditorUndoRedoManager::get_current_action_name() { if (has_undo()) { - History *selected_history = nullptr; - double global_timestamp = 0; - - // Pick the history with greatest last action timestamp (either global or current scene). - { - History &history = get_or_create_history(GLOBAL_HISTORY); - if (!history.undo_stack.is_empty()) { - selected_history = &history; - global_timestamp = history.undo_stack.back()->get().timestamp; - } - } - - { - History &history = get_or_create_history(REMOTE_HISTORY); - if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) { - selected_history = &history; - global_timestamp = history.undo_stack.back()->get().timestamp; - } - } - - { - History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id()); - if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) { - selected_history = &history; - } - } - + History *selected_history = _get_newest_undo(); if (selected_history) { return selected_history->undo_redo->get_current_action_name(); } @@ -461,6 +409,16 @@ String EditorUndoRedoManager::get_current_action_name() { return ""; } +int EditorUndoRedoManager::get_current_action_history_id() { + if (has_undo()) { + History *selected_history = _get_newest_undo(); + if (selected_history) { + return selected_history->id; + } + } + return INVALID_HISTORY; +} + void EditorUndoRedoManager::discard_history(int p_idx, bool p_erase_from_map) { ERR_FAIL_COND(!history_map.has(p_idx)); History &history = history_map[p_idx]; @@ -475,6 +433,37 @@ void EditorUndoRedoManager::discard_history(int p_idx, bool p_erase_from_map) { } } +EditorUndoRedoManager::History *EditorUndoRedoManager::_get_newest_undo() { + History *selected_history = nullptr; + double global_timestamp = 0; + + // Pick the history with greatest last action timestamp (either global or current scene). + { + History &history = get_or_create_history(GLOBAL_HISTORY); + if (!history.undo_stack.is_empty()) { + selected_history = &history; + global_timestamp = history.undo_stack.back()->get().timestamp; + } + } + + { + History &history = get_or_create_history(REMOTE_HISTORY); + if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) { + selected_history = &history; + global_timestamp = history.undo_stack.back()->get().timestamp; + } + } + + { + History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id()); + if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) { + selected_history = &history; + } + } + + return selected_history; +} + void EditorUndoRedoManager::_bind_methods() { ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode", "custom_context"), &EditorUndoRedoManager::create_action, DEFVAL(UndoRedo::MERGE_DISABLE), DEFVAL((Object *)nullptr)); ClassDB::bind_method(D_METHOD("commit_action", "execute"), &EditorUndoRedoManager::commit_action, DEFVAL(true)); diff --git a/editor/editor_undo_redo_manager.h b/editor/editor_undo_redo_manager.h index 08482b6e2d..74911180f0 100644 --- a/editor/editor_undo_redo_manager.h +++ b/editor/editor_undo_redo_manager.h @@ -66,6 +66,8 @@ private: bool is_committing = false; + History *_get_newest_undo(); + protected: static void _bind_methods(); @@ -127,6 +129,7 @@ public: bool has_redo(); String get_current_action_name(); + int get_current_action_history_id(); void discard_history(int p_idx, bool p_erase_from_map = true); ~EditorUndoRedoManager(); diff --git a/editor/editor_vcs_interface.cpp b/editor/editor_vcs_interface.cpp index d22c7bd149..3a6516fbfa 100644 --- a/editor/editor_vcs_interface.cpp +++ b/editor/editor_vcs_interface.cpp @@ -57,7 +57,7 @@ void EditorVCSInterface::set_credentials(String p_username, String p_password, S } List<String> EditorVCSInterface::get_remotes() { - TypedArray<Dictionary> result; + TypedArray<String> result; if (!GDVIRTUAL_CALL(_get_remotes, result)) { UNIMPLEMENTED(); return {}; @@ -137,7 +137,7 @@ List<EditorVCSInterface::Commit> EditorVCSInterface::get_previous_commits(int p_ } List<String> EditorVCSInterface::get_branch_list() { - TypedArray<Dictionary> result; + TypedArray<String> result; if (!GDVIRTUAL_CALL(_get_branch_list, result)) { UNIMPLEMENTED(); return {}; diff --git a/editor/editor_vcs_interface.h b/editor/editor_vcs_interface.h index 5d4901cefa..c93fe4a672 100644 --- a/editor/editor_vcs_interface.h +++ b/editor/editor_vcs_interface.h @@ -117,8 +117,8 @@ protected: GDVIRTUAL0R(bool, _shut_down); GDVIRTUAL0R(String, _get_vcs_name); GDVIRTUAL1R(TypedArray<Dictionary>, _get_previous_commits, int); - GDVIRTUAL0R(TypedArray<Dictionary>, _get_branch_list); - GDVIRTUAL0R(TypedArray<Dictionary>, _get_remotes); + GDVIRTUAL0R(TypedArray<String>, _get_branch_list); + GDVIRTUAL0R(TypedArray<String>, _get_remotes); GDVIRTUAL1(_create_branch, String); GDVIRTUAL1(_remove_branch, String); GDVIRTUAL2(_create_remote, String, String); diff --git a/editor/fbx_importer_manager.cpp b/editor/fbx_importer_manager.cpp new file mode 100644 index 0000000000..b258fe7e09 --- /dev/null +++ b/editor/fbx_importer_manager.cpp @@ -0,0 +1,158 @@ +/*************************************************************************/ +/* fbx_importer_manager.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "fbx_importer_manager.h" + +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "scene/gui/link_button.h" + +void FBXImporterManager::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: { + connect("confirmed", callable_mp(this, &FBXImporterManager::_path_confirmed)); + } break; + } +} + +void FBXImporterManager::show_dialog(bool p_exclusive) { + String fbx2gltf_path = EDITOR_GET("filesystem/import/fbx/fbx2gltf_path"); + fbx_path->set_text(fbx2gltf_path); + _validate_path(fbx2gltf_path); + + set_flag(Window::FLAG_BORDERLESS, p_exclusive); // Avoid closing accidentally . + set_close_on_escape(!p_exclusive); + + popup_centered(); +} + +void FBXImporterManager::_validate_path(const String &p_path) { + String error; + bool success = false; + + if (p_path == "") { + error = TTR("Path to FBX2glTF executable is empty."); + } else if (!FileAccess::exists(p_path)) { + error = TTR("Path to FBX2glTF executable is invalid."); + } else { + List<String> args; + args.push_back("--version"); + int exitcode; + Error err = OS::get_singleton()->execute(p_path, args, nullptr, &exitcode); + + if (err == OK && exitcode == 0) { + success = true; + } else { + error = TTR("Error executing this file (wrong version or architecture)."); + } + } + + if (success) { + path_status->set_text(TTR("FBX2glTF executable is valid.")); + path_status->add_theme_color_override("font_color", path_status->get_theme_color(SNAME("success_color"), SNAME("Editor"))); + get_ok_button()->set_disabled(false); + } else { + path_status->set_text(error); + path_status->add_theme_color_override("font_color", path_status->get_theme_color(SNAME("error_color"), SNAME("Editor"))); + get_ok_button()->set_disabled(true); + } +} + +void FBXImporterManager::_select_file(const String &p_path) { + fbx_path->set_text(p_path); + _validate_path(p_path); +} + +void FBXImporterManager::_path_confirmed() { + String path = fbx_path->get_text(); + EditorSettings::get_singleton()->set("filesystem/import/fbx/fbx2gltf_path", path); + EditorSettings::get_singleton()->save(); +} + +void FBXImporterManager::_browse_install() { + if (fbx_path->get_text() != String()) { + browse_dialog->set_current_file(fbx_path->get_text()); + } + + browse_dialog->popup_centered_ratio(); +} + +FBXImporterManager *FBXImporterManager::singleton = nullptr; + +FBXImporterManager::FBXImporterManager() { + singleton = this; + + set_title(TTR("Configure FBX Importer")); + + VBoxContainer *vb = memnew(VBoxContainer); + vb->add_child(memnew(Label(TTR("FBX2glTF is required for importing FBX files.\nPlease download it and provide a valid path to the binary:")))); + LinkButton *lb = memnew(LinkButton); + lb->set_text(TTR("Click this link to download FBX2glTF")); + lb->set_uri("https://godotengine.org/fbx-import"); + vb->add_child(lb); + + HBoxContainer *hb = memnew(HBoxContainer); + + fbx_path = memnew(LineEdit); + fbx_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hb->add_child(fbx_path); + fbx_path_browse = memnew(Button); + hb->add_child(fbx_path_browse); + fbx_path_browse->set_text(TTR("Browse")); + fbx_path_browse->connect("pressed", callable_mp(this, &FBXImporterManager::_browse_install)); + hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hb->set_custom_minimum_size(Size2(400 * EDSCALE, 0)); + + vb->add_child(hb); + + path_status = memnew(Label); + vb->add_child(path_status); + + add_child(vb); + + fbx_path->connect("text_changed", callable_mp(this, &FBXImporterManager::_validate_path)); + + get_ok_button()->set_text(TTR("Confirm Path")); + + browse_dialog = memnew(EditorFileDialog); + browse_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + browse_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); +#if defined(X11_ENABLED) + browse_dialog->add_filter("FBX2glTF-linux-x86_64"); +#elif defined(OSX_ENABLED) + browse_dialog->add_filter("FBX2glTF-macos-x86_64"); +#elif defined(WINDOWS_ENABLED) + browse_dialog->add_filter("FBX2glTF-windows-x86_64"); +#endif + + browse_dialog->connect("file_selected", callable_mp(this, &FBXImporterManager::_select_file)); + + add_child(browse_dialog); +} diff --git a/editor/fbx_importer_manager.h b/editor/fbx_importer_manager.h new file mode 100644 index 0000000000..28de761519 --- /dev/null +++ b/editor/fbx_importer_manager.h @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* fbx_importer_manager.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FBX_IMPORTER_MANAGER_H +#define FBX_IMPORTER_MANAGER_H + +#include "editor/editor_file_dialog.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/line_edit.h" + +class FBXImporterManager : public ConfirmationDialog { + GDCLASS(FBXImporterManager, ConfirmationDialog) + + Label *message = nullptr; + LineEdit *fbx_path = nullptr; + Button *fbx_path_browse = nullptr; + EditorFileDialog *browse_dialog = nullptr; + Label *path_status = nullptr; + + void _validate_path(const String &p_path); + void _select_file(const String &p_path); + void _path_confirmed(); + void _browse_install(); + void _link_clicked(); + + static FBXImporterManager *singleton; + +protected: + void _notification(int p_what); + +public: + static FBXImporterManager *get_singleton() { return singleton; } + + void show_dialog(bool p_exclusive = false); + + FBXImporterManager(); +}; + +#endif // FBX_IMPORTER_MANAGER_H diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index b7e7200b11..3a6c891142 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -569,8 +569,6 @@ FindInFilesPanel::FindInFilesPanel() { hbc->add_child(find_label); _search_text_label = memnew(Label); - _search_text_label->add_theme_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("source"), SNAME("EditorFonts"))); - _search_text_label->add_theme_font_size_override("font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("source_size"), SNAME("EditorFonts"))); hbc->add_child(_search_text_label); _progress_bar = memnew(ProgressBar); @@ -598,8 +596,6 @@ FindInFilesPanel::FindInFilesPanel() { } _results_display = memnew(Tree); - _results_display->add_theme_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("source"), SNAME("EditorFonts"))); - _results_display->add_theme_font_size_override("font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("source_size"), SNAME("EditorFonts"))); _results_display->set_v_size_flags(SIZE_EXPAND_FILL); _results_display->connect("item_selected", callable_mp(this, &FindInFilesPanel::_on_result_selected)); _results_display->connect("item_edited", callable_mp(this, &FindInFilesPanel::_on_item_edited)); @@ -688,13 +684,20 @@ void FindInFilesPanel::stop_search() { void FindInFilesPanel::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_PROCESS: { - _progress_bar->set_as_ratio(_finder->get_progress()); - } break; - case NOTIFICATION_THEME_CHANGED: { _search_text_label->add_theme_font_override("font", get_theme_font(SNAME("source"), SNAME("EditorFonts"))); + _search_text_label->add_theme_font_size_override("font_size", get_theme_font_size(SNAME("source_size"), SNAME("EditorFonts"))); _results_display->add_theme_font_override("font", get_theme_font(SNAME("source"), SNAME("EditorFonts"))); + _results_display->add_theme_font_size_override("font_size", get_theme_font_size(SNAME("source_size"), SNAME("EditorFonts"))); + + // Rebuild search tree. + if (!_finder->get_search_text().is_empty()) { + start_search(); + } + } break; + + case NOTIFICATION_PROCESS: { + _progress_bar->set_as_ratio(_finder->get_progress()); } break; } } diff --git a/editor/import/audio_stream_import_settings.cpp b/editor/import/audio_stream_import_settings.cpp index f635c74547..92d4397c68 100644 --- a/editor/import/audio_stream_import_settings.cpp +++ b/editor/import/audio_stream_import_settings.cpp @@ -465,12 +465,17 @@ void AudioStreamImportSettings::_settings_changed() { updating_settings = true; stream->call("set_loop", loop->is_pressed()); stream->call("set_loop_offset", loop_offset->get_value()); + if (loop->is_pressed()) { + loop_offset->set_editable(true); + } else { + loop_offset->set_editable(false); + } + if (bpm_enabled->is_pressed()) { stream->call("set_bpm", bpm_edit->get_value()); - beats_enabled->show(); - beats_edit->show(); - bar_beats_label->show(); - bar_beats_edit->show(); + beats_enabled->set_disabled(false); + beats_edit->set_editable(true); + bar_beats_edit->set_editable(true); double bpm = bpm_edit->get_value(); if (bpm > 0) { float beat_size = 60 / float(bpm); @@ -486,10 +491,9 @@ void AudioStreamImportSettings::_settings_changed() { } else { stream->call("set_bpm", 0); stream->call("set_bar_beats", 4); - beats_enabled->hide(); - beats_edit->hide(); - bar_beats_label->hide(); - bar_beats_edit->hide(); + beats_enabled->set_disabled(true); + beats_edit->set_editable(false); + bar_beats_edit->set_editable(false); } if (bpm_enabled->is_pressed() && beats_enabled->is_pressed()) { stream->call("set_beat_count", beats_edit->get_value()); @@ -552,17 +556,8 @@ AudioStreamImportSettings::AudioStreamImportSettings() { bpm_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); interactive_hb->add_child(bpm_edit); interactive_hb->add_spacer(); - bar_beats_label = memnew(Label(TTR("Beats/Bar:"))); - interactive_hb->add_child(bar_beats_label); - bar_beats_edit = memnew(SpinBox); - bar_beats_edit->set_tooltip_text(TTR("Configure the Beats Per Bar. This used for music-aware transitions between AudioStreams.")); - bar_beats_edit->set_min(2); - bar_beats_edit->set_max(32); - bar_beats_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); - interactive_hb->add_child(bar_beats_edit); - interactive_hb->add_spacer(); beats_enabled = memnew(CheckBox); - beats_enabled->set_text(TTR("Length (in beats):")); + beats_enabled->set_text(TTR("Beat Count:")); beats_enabled->connect("toggled", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); interactive_hb->add_child(beats_enabled); beats_edit = memnew(SpinBox); @@ -570,6 +565,15 @@ AudioStreamImportSettings::AudioStreamImportSettings() { beats_edit->set_max(99999); beats_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); interactive_hb->add_child(beats_edit); + bar_beats_label = memnew(Label(TTR("Bar Beats:"))); + interactive_hb->add_child(bar_beats_label); + bar_beats_edit = memnew(SpinBox); + bar_beats_edit->set_tooltip_text(TTR("Configure the Beats Per Bar. This used for music-aware transitions between AudioStreams.")); + bar_beats_edit->set_min(2); + bar_beats_edit->set_max(32); + bar_beats_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); + interactive_hb->add_child(bar_beats_edit); + interactive_hb->add_spacer(); main_vbox->add_margin_child(TTR("Music Playback:"), interactive_hb); color_rect = memnew(ColorRect); diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 730aa3bd61..380a4e370c 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -555,6 +555,7 @@ void SceneImportSettings::open_settings(const String &p_path, bool p_for_animati material_set.clear(); mesh_set.clear(); + animation_map.clear(); material_map.clear(); mesh_map.clear(); node_map.clear(); @@ -957,15 +958,15 @@ void SceneImportSettings::_notification(int p_what) { void SceneImportSettings::_menu_callback(int p_id) { switch (p_id) { case ACTION_EXTRACT_MATERIALS: { - save_path->set_text(TTR("Select folder to extract material resources")); + save_path->set_title(TTR("Select folder to extract material resources")); external_extension_type->select(0); } break; case ACTION_CHOOSE_MESH_SAVE_PATHS: { - save_path->set_text(TTR("Select folder where mesh resources will save on import")); + save_path->set_title(TTR("Select folder where mesh resources will save on import")); external_extension_type->select(1); } break; case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: { - save_path->set_text(TTR("Select folder where animations will save on import")); + save_path->set_title(TTR("Select folder where animations will save on import")); external_extension_type->select(1); } break; } diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 5183a738ae..344a800241 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -87,13 +87,13 @@ void AnimationPlayerEditor::_notification(int p_what) { } frame->set_value(player->get_current_animation_position()); track_editor->set_anim_pos(player->get_current_animation_position()); - } else if (!player->is_valid()) { // Reset timeline when the player has been stopped externally frame->set_value(0); } else if (last_active) { // Need the last frame after it stopped. frame->set_value(player->get_current_animation_position()); + track_editor->set_anim_pos(player->get_current_animation_position()); } last_active = player->is_playing(); @@ -423,7 +423,7 @@ void AnimationPlayerEditor::_select_anim_by_name(const String &p_anim) { _animation_selected(idx); } -double AnimationPlayerEditor::_get_editor_step() const { +float AnimationPlayerEditor::_get_editor_step() const { // Returns the effective snapping value depending on snapping modifiers, or 0 if snapping is disabled. if (track_editor->is_snap_enabled()) { const String current = player->get_assigned_animation(); @@ -434,7 +434,7 @@ double AnimationPlayerEditor::_get_editor_step() const { return Input::get_singleton()->is_key_pressed(Key::SHIFT) ? anim->get_step() * 0.25 : anim->get_step(); } - return 0.0; + return 0.0f; } void AnimationPlayerEditor::_animation_name_edited() { @@ -1922,10 +1922,12 @@ void AnimationPlayerEditorPlugin::_notification(int p_what) { } void AnimationPlayerEditorPlugin::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) { - if (!anim_editor->get_track_editor()->has_keying()) { + AnimationTrackEditor *te = anim_editor->get_track_editor(); + if (!te || !te->has_keying()) { return; } - anim_editor->get_track_editor()->insert_value_key(p_keyed, p_value, p_advance); + te->_clear_selection(); + te->insert_value_key(p_keyed, p_value, p_advance); } void AnimationPlayerEditorPlugin::_transform_key_request(Object *sp, const String &p_sub, const Transform3D &p_key) { @@ -1971,3 +1973,26 @@ AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin() { AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() { } + +// AnimationTrackKeyEditEditorPlugin + +bool EditorInspectorPluginAnimationTrackKeyEdit::can_handle(Object *p_object) { + return Object::cast_to<AnimationTrackKeyEdit>(p_object) != nullptr; +} + +void EditorInspectorPluginAnimationTrackKeyEdit::parse_begin(Object *p_object) { + AnimationTrackKeyEdit *atk = Object::cast_to<AnimationTrackKeyEdit>(p_object); + ERR_FAIL_COND(!atk); + + atk_editor = memnew(AnimationTrackKeyEditEditor(atk->animation, atk->track, atk->key_ofs, atk->use_fps)); + add_custom_control(atk_editor); +} + +AnimationTrackKeyEditEditorPlugin::AnimationTrackKeyEditEditorPlugin() { + atk_plugin = memnew(EditorInspectorPluginAnimationTrackKeyEdit); + EditorInspector::add_inspector_plugin(atk_plugin); +} + +bool AnimationTrackKeyEditEditorPlugin::handles(Object *p_object) const { + return p_object->is_class("AnimationTrackKeyEdit"); +} diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 53d460fc9e..8539d450e6 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -162,7 +162,7 @@ class AnimationPlayerEditor : public VBoxContainer { } onion; void _select_anim_by_name(const String &p_anim); - double _get_editor_step() const; + float _get_editor_step() const; void _play_pressed(); void _play_from_pressed(); void _play_bw_pressed(); @@ -272,4 +272,30 @@ public: ~AnimationPlayerEditorPlugin(); }; +// AnimationTrackKeyEditEditorPlugin + +class EditorInspectorPluginAnimationTrackKeyEdit : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginAnimationTrackKeyEdit, EditorInspectorPlugin); + + AnimationTrackKeyEditEditor *atk_editor = nullptr; + +public: + virtual bool can_handle(Object *p_object) override; + virtual void parse_begin(Object *p_object) override; +}; + +class AnimationTrackKeyEditEditorPlugin : public EditorPlugin { + GDCLASS(AnimationTrackKeyEditEditorPlugin, EditorPlugin); + + EditorInspectorPluginAnimationTrackKeyEdit *atk_plugin = nullptr; + +public: + bool has_main_screen() const override { return false; } + virtual bool handles(Object *p_object) const override; + + virtual String get_name() const override { return "AnimationTrackKeyEdit"; } + + AnimationTrackKeyEditEditorPlugin(); +}; + #endif // ANIMATION_PLAYER_EDITOR_PLUGIN_H diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 66a0c746d9..7b8a5d06f8 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -1097,7 +1097,8 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action Ref<AnimationNodeStateMachineTransition> tr; tr.instantiate(); - tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected())); + tr->set_advance_mode(auto_advance->is_pressed() ? AnimationNodeStateMachineTransition::AdvanceMode::ADVANCE_MODE_AUTO : AnimationNodeStateMachineTransition::AdvanceMode::ADVANCE_MODE_ENABLED); + tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected())); Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); if (!p_nested_action) { @@ -1326,7 +1327,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } - _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected()), true, false, false, false, false); + _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()), true, false, false, false, false); } Ref<Texture2D> tr_reference_icon = get_theme_icon(SNAME("TransitionImmediateBig"), SNAME("EditorIcons")); @@ -1349,8 +1350,8 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { tl.to = (state_machine->get_node_position(local_to) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i); - tl.disabled = tr->is_disabled(); - tl.auto_advance = tr->has_auto_advance(); + tl.disabled = bool(tr->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED); + tl.auto_advance = bool(tr->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO); tl.advance_condition_name = tr->get_advance_condition_name(); tl.advance_condition_state = false; tl.mode = tr->get_switch_mode(); @@ -1590,10 +1591,12 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { tool_create->set_icon(get_theme_icon(SNAME("ToolAddNode"), SNAME("EditorIcons"))); tool_connect->set_icon(get_theme_icon(SNAME("ToolConnect"), SNAME("EditorIcons"))); - transition_mode->clear(); - transition_mode->add_icon_item(get_theme_icon(SNAME("TransitionImmediate"), SNAME("EditorIcons")), TTR("Immediate")); - transition_mode->add_icon_item(get_theme_icon(SNAME("TransitionSync"), SNAME("EditorIcons")), TTR("Sync")); - transition_mode->add_icon_item(get_theme_icon(SNAME("TransitionEnd"), SNAME("EditorIcons")), TTR("At End")); + switch_mode->clear(); + switch_mode->add_icon_item(get_theme_icon(SNAME("TransitionImmediate"), SNAME("EditorIcons")), TTR("Immediate")); + switch_mode->add_icon_item(get_theme_icon(SNAME("TransitionSync"), SNAME("EditorIcons")), TTR("Sync")); + switch_mode->add_icon_item(get_theme_icon(SNAME("TransitionEnd"), SNAME("EditorIcons")), TTR("At End")); + + auto_advance->set_icon(get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons"))); tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); tool_group->set_icon(get_theme_icon(SNAME("Group"), SNAME("EditorIcons"))); @@ -1652,12 +1655,12 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { break; } - if (transition_lines[i].disabled != state_machine->get_transition(tidx)->is_disabled()) { + if (transition_lines[i].disabled != bool(state_machine->get_transition(tidx)->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED)) { state_machine_draw->queue_redraw(); break; } - if (transition_lines[i].auto_advance != state_machine->get_transition(tidx)->has_auto_advance()) { + if (transition_lines[i].auto_advance != bool(state_machine->get_transition(tidx)->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO)) { state_machine_draw->queue_redraw(); break; } @@ -1904,7 +1907,7 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action void AnimationNodeStateMachineEditor::_update_mode() { if (tool_select->is_pressed()) { - tool_erase_hb->show(); + selection_tools_hb->show(); bool nothing_selected = selected_nodes.is_empty() && selected_transition_from == StringName() && selected_transition_to == StringName(); bool start_end_selected = selected_nodes.size() == 1 && (*selected_nodes.begin() == state_machine->start_node || *selected_nodes.begin() == state_machine->end_node); tool_erase->set_disabled(nothing_selected || start_end_selected || read_only); @@ -1927,7 +1930,13 @@ void AnimationNodeStateMachineEditor::_update_mode() { } } } else { - tool_erase_hb->hide(); + selection_tools_hb->hide(); + } + + if (tool_connect->is_pressed()) { + transition_tools_hb->show(); + } else { + transition_tools_hb->hide(); } } @@ -1978,35 +1987,48 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { tool_connect->set_tooltip_text(TTR("Connect nodes.")); tool_connect->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_update_mode), CONNECT_DEFERRED); - tool_erase_hb = memnew(HBoxContainer); - top_hb->add_child(tool_erase_hb); - tool_erase_hb->add_child(memnew(VSeparator)); + // Context-sensitive selection tools: + selection_tools_hb = memnew(HBoxContainer); + top_hb->add_child(selection_tools_hb); + selection_tools_hb->add_child(memnew(VSeparator)); tool_group = memnew(Button); tool_group->set_flat(true); tool_group->set_tooltip_text(TTR("Group Selected Node(s)") + " (Ctrl+G)"); tool_group->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_group_selected_nodes)); tool_group->set_disabled(true); - tool_erase_hb->add_child(tool_group); + selection_tools_hb->add_child(tool_group); tool_ungroup = memnew(Button); tool_ungroup->set_flat(true); tool_ungroup->set_tooltip_text(TTR("Ungroup Selected Node") + " (Ctrl+Shift+G)"); tool_ungroup->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_ungroup_selected_nodes)); tool_ungroup->set_visible(false); - tool_erase_hb->add_child(tool_ungroup); + selection_tools_hb->add_child(tool_ungroup); tool_erase = memnew(Button); tool_erase->set_flat(true); tool_erase->set_tooltip_text(TTR("Remove selected node or transition.")); tool_erase->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_erase_selected).bind(false)); tool_erase->set_disabled(true); - tool_erase_hb->add_child(tool_erase); + selection_tools_hb->add_child(tool_erase); + + transition_tools_hb = memnew(HBoxContainer); + top_hb->add_child(transition_tools_hb); + transition_tools_hb->add_child(memnew(VSeparator)); + + transition_tools_hb->add_child(memnew(Label(TTR("Transition:")))); + switch_mode = memnew(OptionButton); + transition_tools_hb->add_child(switch_mode); + + auto_advance = memnew(Button); + auto_advance->set_flat(true); + auto_advance->set_tooltip_text(TTR("New Transitions Should Auto Advance")); + auto_advance->set_toggle_mode(true); + auto_advance->set_pressed(true); + transition_tools_hb->add_child(auto_advance); - top_hb->add_child(memnew(VSeparator)); - top_hb->add_child(memnew(Label(TTR("Transition:")))); - transition_mode = memnew(OptionButton); - top_hb->add_child(transition_mode); + // top_hb->add_spacer(); diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 5edf803c41..28b5f0cbcc 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -52,15 +52,18 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { Button *tool_select = nullptr; Button *tool_create = nullptr; Button *tool_connect = nullptr; - Button *tool_group = nullptr; - Button *tool_ungroup = nullptr; Popup *name_edit_popup = nullptr; LineEdit *name_edit = nullptr; - HBoxContainer *tool_erase_hb = nullptr; + HBoxContainer *selection_tools_hb = nullptr; + Button *tool_group = nullptr; + Button *tool_ungroup = nullptr; Button *tool_erase = nullptr; - OptionButton *transition_mode = nullptr; + HBoxContainer *transition_tools_hb = nullptr; + OptionButton *switch_mode = nullptr; + Button *auto_advance = nullptr; + OptionButton *play_mode = nullptr; PanelContainer *panel = nullptr; diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 7c3ecd5d4e..6483a18a4d 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -43,6 +43,11 @@ #include "editor/project_settings_editor.h" #include "scene/gui/menu_button.h" +#include "modules/modules_enabled.gen.h" // For svg. +#ifdef MODULE_SVG_ENABLED +#include "modules/svg/image_loader_svg.h" +#endif + static inline void setup_http_request(HTTPRequest *request) { request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); @@ -751,13 +756,30 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PackedB uint8_t png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; uint8_t jpg_signature[3] = { 255, 216, 255 }; + uint8_t webp_signature[4] = { 82, 73, 70, 70 }; + uint8_t bmp_signature[2] = { 66, 77 }; if (r) { if ((memcmp(&r[0], &png_signature[0], 8) == 0) && Image::_png_mem_loader_func) { image->copy_internals_from(Image::_png_mem_loader_func(r, len)); } else if ((memcmp(&r[0], &jpg_signature[0], 3) == 0) && Image::_jpg_mem_loader_func) { image->copy_internals_from(Image::_jpg_mem_loader_func(r, len)); + } else if ((memcmp(&r[0], &webp_signature[0], 4) == 0) && Image::_webp_mem_loader_func) { + image->copy_internals_from(Image::_webp_mem_loader_func(r, len)); + } else if ((memcmp(&r[0], &bmp_signature[0], 2) == 0) && Image::_bmp_mem_loader_func) { + image->copy_internals_from(Image::_bmp_mem_loader_func(r, len)); } +#ifdef MODULE_SVG_ENABLED + else { + ImageLoaderSVG svg_loader; + Ref<Image> img = Ref<Image>(memnew(Image)); + Error err = svg_loader.create_image_from_utf8_buffer(img, image_data, 1.0, false); + + if (err == OK) { + image->copy_internals_from(img); + } + } +#endif } if (!image->is_empty()) { diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp new file mode 100644 index 0000000000..60d949cdf0 --- /dev/null +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -0,0 +1,280 @@ +/*************************************************************************/ +/* audio_stream_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "audio_stream_editor_plugin.h" + +#include "core/core_string_names.h" +#include "editor/audio_stream_preview.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "scene/resources/audio_stream_wav.h" + +// AudioStreamEditor + +void AudioStreamEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: { + AudioStreamPreviewGenerator::get_singleton()->connect(SNAME("preview_updated"), callable_mp(this, &AudioStreamEditor::_preview_changed)); + } break; + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_ENTER_TREE: { + Ref<Font> font = get_theme_font(SNAME("status_source"), SNAME("EditorFonts")); + + _current_label->add_theme_font_override(SNAME("font"), font); + _duration_label->add_theme_font_override(SNAME("font"), font); + + _play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); + _stop_button->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); + _preview->set_color(get_theme_color(SNAME("dark_color_2"), SNAME("Editor"))); + + set_color(get_theme_color(SNAME("dark_color_1"), SNAME("Editor"))); + + _indicator->queue_redraw(); + _preview->queue_redraw(); + } break; + case NOTIFICATION_PROCESS: { + _current = _player->get_playback_position(); + _indicator->queue_redraw(); + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible_in_tree()) { + _stop(); + } + } break; + default: { + } break; + } +} + +void AudioStreamEditor::_draw_preview() { + Rect2 rect = _preview->get_rect(); + Size2 size = get_size(); + + Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream); + float preview_len = preview->get_length(); + + Vector<Vector2> lines; + lines.resize(size.width * 2); + + for (int i = 0; i < size.width; i++) { + float ofs = i * preview_len / size.width; + float ofs_n = (i + 1) * preview_len / size.width; + float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5; + float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5; + + int idx = i; + lines.write[idx * 2 + 0] = Vector2(i + 1, rect.position.y + min * rect.size.y); + lines.write[idx * 2 + 1] = Vector2(i + 1, rect.position.y + max * rect.size.y); + } + + RS::get_singleton()->canvas_item_add_multiline(_preview->get_canvas_item(), lines, { get_theme_color(SNAME("contrast_color_2"), SNAME("Editor")) }); +} + +void AudioStreamEditor::_preview_changed(ObjectID p_which) { + if (stream.is_valid() && stream->get_instance_id() == p_which) { + _preview->queue_redraw(); + } +} + +void AudioStreamEditor::_stream_changed() { + if (!is_visible()) { + return; + } + queue_redraw(); +} + +void AudioStreamEditor::_play() { + if (_player->is_playing()) { + _pausing = true; + _player->stop(); + _play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); + set_process(false); + } else { + _pausing = false; + _player->play(_current); + _play_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons"))); + set_process(true); + } +} + +void AudioStreamEditor::_stop() { + _player->stop(); + _play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); + _current = 0; + _indicator->queue_redraw(); + set_process(false); +} + +void AudioStreamEditor::_on_finished() { + _play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); + if (!_pausing) { + _current = 0; + _indicator->queue_redraw(); + } else { + _pausing = false; + } + set_process(false); +} + +void AudioStreamEditor::_draw_indicator() { + if (stream.is_null()) { + return; + } + + Rect2 rect = _preview->get_rect(); + float len = stream->get_length(); + float ofs_x = _current / len * rect.size.width; + const Color col = get_theme_color(SNAME("accent_color"), SNAME("Editor")); + Ref<Texture2D> icon = get_theme_icon(SNAME("TimelineIndicator"), SNAME("EditorIcons")); + _indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), col, Math::round(2 * EDSCALE)); + _indicator->draw_texture( + icon, + Point2(ofs_x - icon->get_width() * 0.5, 0), + col); + + _current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /"); +} + +void AudioStreamEditor::_on_input_indicator(Ref<InputEvent> p_event) { + const Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { + if (mb->is_pressed()) { + _seek_to(mb->get_position().x); + } + _dragging = mb->is_pressed(); + } + + const Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + if (_dragging) { + _seek_to(mm->get_position().x); + } + } +} + +void AudioStreamEditor::_seek_to(real_t p_x) { + _current = p_x / _preview->get_rect().size.x * stream->get_length(); + _current = CLAMP(_current, 0, stream->get_length()); + _player->seek(_current); + _indicator->queue_redraw(); +} + +void AudioStreamEditor::set_stream(const Ref<AudioStream> &p_stream) { + if (stream.is_valid()) { + stream->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &AudioStreamEditor::_stream_changed)); + } + + stream = p_stream; + if (stream.is_null()) { + hide(); + return; + } + stream->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &AudioStreamEditor::_stream_changed)); + + _player->set_stream(stream); + _current = 0; + + String text = String::num(stream->get_length(), 2).pad_decimals(2) + "s"; + _duration_label->set_text(text); + + queue_redraw(); +} + +AudioStreamEditor::AudioStreamEditor() { + set_custom_minimum_size(Size2(1, 100) * EDSCALE); + + _player = memnew(AudioStreamPlayer); + _player->connect(SNAME("finished"), callable_mp(this, &AudioStreamEditor::_on_finished)); + add_child(_player); + + VBoxContainer *vbox = memnew(VBoxContainer); + vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + add_child(vbox); + + _preview = memnew(ColorRect); + _preview->set_v_size_flags(SIZE_EXPAND_FILL); + _preview->connect(SNAME("draw"), callable_mp(this, &AudioStreamEditor::_draw_preview)); + vbox->add_child(_preview); + + _indicator = memnew(Control); + _indicator->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + _indicator->connect(SNAME("draw"), callable_mp(this, &AudioStreamEditor::_draw_indicator)); + _indicator->connect(SNAME("gui_input"), callable_mp(this, &AudioStreamEditor::_on_input_indicator)); + _preview->add_child(_indicator); + + HBoxContainer *hbox = memnew(HBoxContainer); + hbox->add_theme_constant_override("separation", 0); + vbox->add_child(hbox); + + _play_button = memnew(Button); + hbox->add_child(_play_button); + _play_button->set_flat(true); + _play_button->set_focus_mode(Control::FOCUS_NONE); + _play_button->connect(SNAME("pressed"), callable_mp(this, &AudioStreamEditor::_play)); + _play_button->set_shortcut(ED_SHORTCUT("audio_stream_editor/audio_preview_play_pause", TTR("Audio Preview Play/Pause"), Key::SPACE)); + + _stop_button = memnew(Button); + hbox->add_child(_stop_button); + _stop_button->set_flat(true); + _stop_button->set_focus_mode(Control::FOCUS_NONE); + _stop_button->connect(SNAME("pressed"), callable_mp(this, &AudioStreamEditor::_stop)); + + _current_label = memnew(Label); + _current_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); + _current_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + _current_label->set_modulate(Color(1, 1, 1, 0.5)); + hbox->add_child(_current_label); + + _duration_label = memnew(Label); + hbox->add_child(_duration_label); +} + +// EditorInspectorPluginAudioStream + +bool EditorInspectorPluginAudioStream::can_handle(Object *p_object) { + return Object::cast_to<AudioStreamWAV>(p_object) != nullptr; +} + +void EditorInspectorPluginAudioStream::parse_begin(Object *p_object) { + AudioStream *stream = Object::cast_to<AudioStream>(p_object); + + editor = memnew(AudioStreamEditor); + editor->set_stream(Ref<AudioStream>(stream)); + + add_custom_control(editor); +} + +// AudioStreamEditorPlugin + +AudioStreamEditorPlugin::AudioStreamEditorPlugin() { + Ref<EditorInspectorPluginAudioStream> plugin; + plugin.instantiate(); + add_inspector_plugin(plugin); +} diff --git a/editor/plugins/audio_stream_editor_plugin.h b/editor/plugins/audio_stream_editor_plugin.h new file mode 100644 index 0000000000..13e52929ee --- /dev/null +++ b/editor/plugins/audio_stream_editor_plugin.h @@ -0,0 +1,93 @@ +/*************************************************************************/ +/* audio_stream_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef AUDIO_STREAM_EDITOR_PLUGIN_H +#define AUDIO_STREAM_EDITOR_PLUGIN_H + +#include "editor/editor_inspector.h" +#include "editor/editor_plugin.h" +#include "scene/audio/audio_stream_player.h" +#include "scene/gui/button.h" +#include "scene/gui/color_rect.h" +#include "scene/gui/label.h" + +class AudioStreamEditor : public ColorRect { + GDCLASS(AudioStreamEditor, ColorRect); + + Ref<AudioStream> stream; + + AudioStreamPlayer *_player = nullptr; + ColorRect *_preview = nullptr; + Control *_indicator = nullptr; + Label *_current_label = nullptr; + Label *_duration_label = nullptr; + + Button *_play_button = nullptr; + Button *_stop_button = nullptr; + + float _current = 0; + bool _dragging = false; + bool _pausing = false; + +protected: + void _notification(int p_what); + void _preview_changed(ObjectID p_which); + void _play(); + void _stop(); + void _on_finished(); + void _draw_preview(); + void _draw_indicator(); + void _on_input_indicator(Ref<InputEvent> p_event); + void _seek_to(real_t p_x); + void _stream_changed(); + +public: + void set_stream(const Ref<AudioStream> &p_stream); + + AudioStreamEditor(); +}; + +class EditorInspectorPluginAudioStream : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginAudioStream, EditorInspectorPlugin); + AudioStreamEditor *editor = nullptr; + +public: + virtual bool can_handle(Object *p_object) override; + virtual void parse_begin(Object *p_object) override; +}; + +class AudioStreamEditorPlugin : public EditorPlugin { + GDCLASS(AudioStreamEditorPlugin, EditorPlugin); + +public: + AudioStreamEditorPlugin(); +}; + +#endif // AUDIO_STREAM_EDITOR_PLUGIN_H diff --git a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp index e21a50a434..61b7683a05 100644 --- a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp +++ b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp @@ -81,7 +81,7 @@ void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_und if (p_from_index < 0) { undo_redo_man->add_undo_method(randomizer, "remove_stream", p_to_pos < 0 ? randomizer->get_streams_count() : p_to_pos); } else if (p_to_pos < 0) { - undo_redo_man->add_undo_method(randomizer, "add_stream", p_from_index); + undo_redo_man->add_undo_method(randomizer, "add_stream", p_from_index, Ref<AudioStream>()); } List<PropertyInfo> properties; @@ -107,7 +107,7 @@ void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_und #undef ADD_UNDO if (p_from_index < 0) { - undo_redo_man->add_do_method(randomizer, "add_stream", p_to_pos); + undo_redo_man->add_do_method(randomizer, "add_stream", p_to_pos, Ref<AudioStream>()); } else if (p_to_pos < 0) { undo_redo_man->add_do_method(randomizer, "remove_stream", p_from_index); } else { diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index efb2c80cfd..0f8ed1c857 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -70,10 +70,12 @@ public: }; class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator { + GDCLASS(EditorPackedScenePreviewPlugin, EditorResourcePreviewGenerator); + public: - virtual bool handles(const String &p_type) const; - virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const; - virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const; + virtual bool handles(const String &p_type) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; + virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const override; EditorPackedScenePreviewPlugin(); }; @@ -107,17 +109,21 @@ public: }; class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator { + GDCLASS(EditorScriptPreviewPlugin, EditorResourcePreviewGenerator); + public: - virtual bool handles(const String &p_type) const; - virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const; + virtual bool handles(const String &p_type) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; EditorScriptPreviewPlugin(); }; class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator { + GDCLASS(EditorAudioStreamPreviewPlugin, EditorResourcePreviewGenerator); + public: - virtual bool handles(const String &p_type) const; - virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const; + virtual bool handles(const String &p_type) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; EditorAudioStreamPreviewPlugin(); }; diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp index 4370d013be..6b3db095d4 100644 --- a/editor/plugins/font_config_plugin.cpp +++ b/editor/plugins/font_config_plugin.cpp @@ -483,7 +483,7 @@ void EditorPropertyOTVariation::update_property() { Vector3i range = supported.get_value_at_index(i); EditorPropertyInteger *prop = memnew(EditorPropertyInteger); - prop->setup(range.x, range.y, 1, false, false); + prop->setup(range.x, range.y, false, 1, false, false); prop->set_object_and_property(object.ptr(), "keys/" + itos(name_tag)); String name = TS->tag_to_name(name_tag); @@ -762,7 +762,7 @@ void EditorPropertyOTFeatures::update_property() { } break; case Variant::INT: { EditorPropertyInteger *editor = memnew(EditorPropertyInteger); - editor->setup(0, 255, 1, false, false); + editor->setup(0, 255, 1, false, false, false); prop = editor; } break; default: { diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 4a8e5d9979..4b31e99f0e 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -160,23 +160,23 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, p_library->set_item_shapes(id, collisions); - Ref<NavigationMesh> navmesh; - Transform3D navmesh_transform; + Ref<NavigationMesh> navigation_mesh; + Transform3D navigation_mesh_transform; for (int j = 0; j < mi->get_child_count(); j++) { Node *child2 = mi->get_child(j); if (!Object::cast_to<NavigationRegion3D>(child2)) { continue; } NavigationRegion3D *sb = Object::cast_to<NavigationRegion3D>(child2); - navmesh = sb->get_navigation_mesh(); - navmesh_transform = sb->get_transform(); - if (!navmesh.is_null()) { + navigation_mesh = sb->get_navigation_mesh(); + navigation_mesh_transform = sb->get_transform(); + if (!navigation_mesh.is_null()) { break; } } - if (!navmesh.is_null()) { - p_library->set_item_navmesh(id, navmesh); - p_library->set_item_navmesh_transform(id, navmesh_transform); + if (!navigation_mesh.is_null()) { + p_library->set_item_navigation_mesh(id, navigation_mesh); + p_library->set_item_navigation_mesh_transform(id, navigation_mesh_transform); } } diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index c8b80db334..91c7fbc06a 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -1016,8 +1016,9 @@ Ref<StandardMaterial3D> EditorNode3DGizmoPlugin::get_material(const String &p_na } String EditorNode3DGizmoPlugin::get_gizmo_name() const { - if (get_script_instance() && get_script_instance()->has_method("_get_gizmo_name")) { - return get_script_instance()->call("_get_gizmo_name"); + String ret; + if (GDVIRTUAL_CALL(_get_gizmo_name, ret)) { + return ret; } WARN_PRINT_ONCE("A 3D editor gizmo has no name defined (it will appear as \"Unnamed Gizmo\" in the \"View > Gizmos\" menu). To resolve this, override the `_get_gizmo_name()` function to return a String in the script that extends EditorNode3DGizmoPlugin."); @@ -1025,8 +1026,9 @@ String EditorNode3DGizmoPlugin::get_gizmo_name() const { } int EditorNode3DGizmoPlugin::get_priority() const { - if (get_script_instance() && get_script_instance()->has_method("_get_priority")) { - return get_script_instance()->call("_get_priority"); + int ret; + if (GDVIRTUAL_CALL(_get_priority, ret)) { + return ret; } return 0; } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index f5dd893377..4976c8c750 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -463,6 +463,15 @@ void Node3DEditorViewport::_view_settings_confirmed(real_t p_interp_delta) { _update_camera(p_interp_delta); } +void Node3DEditorViewport::_update_navigation_controls_visibility() { + bool show_viewport_rotation_gizmo = EDITOR_GET("editors/3d/navigation/show_viewport_rotation_gizmo") && (!previewing_cinema && !previewing_camera); + rotation_control->set_visible(show_viewport_rotation_gizmo); + + bool show_viewport_navigation_gizmo = EDITOR_GET("editors/3d/navigation/show_viewport_navigation_gizmo") && (!previewing_cinema && !previewing_camera); + position_control->set_visible(show_viewport_navigation_gizmo); + look_control->set_visible(show_viewport_navigation_gizmo); +} + void Node3DEditorViewport::_update_camera(real_t p_interp_delta) { bool is_orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL; @@ -2597,7 +2606,7 @@ void Node3DEditorViewport::_project_settings_changed() { const bool use_taa = GLOBAL_GET("rendering/anti_aliasing/quality/use_taa"); viewport->set_use_taa(use_taa); - const bool transparent_background = GLOBAL_GET("rendering/transparent_background"); + const bool transparent_background = GLOBAL_GET("rendering/viewport/transparent_background"); viewport->set_transparent_background(transparent_background); const bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding"); @@ -2642,9 +2651,6 @@ void Node3DEditorViewport::_notification(int p_what) { set_freelook_active(false); } call_deferred(SNAME("update_transform_gizmo_view")); - rotation_control->set_visible(EDITOR_GET("editors/3d/navigation/show_viewport_rotation_gizmo")); - position_control->set_visible(EDITOR_GET("editors/3d/navigation/show_viewport_navigation_gizmo")); - look_control->set_visible(EDITOR_GET("editors/3d/navigation/show_viewport_navigation_gizmo")); } break; case NOTIFICATION_RESIZED: { @@ -2662,6 +2668,7 @@ void Node3DEditorViewport::_notification(int p_what) { } } + _update_navigation_controls_visibility(); _update_freelook(delta); Node *scene_root = SceneTreeDock::get_singleton()->get_editor_data()->get_edited_scene_root(); @@ -3566,9 +3573,8 @@ void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) { ERR_FAIL_COND(p_activate && !preview); ERR_FAIL_COND(!p_activate && !previewing); - rotation_control->set_visible(!p_activate); - position_control->set_visible(!p_activate); - look_control->set_visible(!p_activate); + previewing_camera = p_activate; + _update_navigation_controls_visibility(); if (!p_activate) { previewing->disconnect("tree_exiting", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); @@ -3589,9 +3595,7 @@ void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) { void Node3DEditorViewport::_toggle_cinema_preview(bool p_activate) { previewing_cinema = p_activate; - rotation_control->set_visible(!p_activate); - position_control->set_visible(!p_activate); - look_control->set_visible(!p_activate); + _update_navigation_controls_visibility(); if (!previewing_cinema) { if (previewing != nullptr) { @@ -8429,8 +8433,8 @@ Node3DEditor::Node3DEditor() { EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d/manipulator_gizmo_size", PROPERTY_HINT_RANGE, "16,160,1")); EDITOR_DEF("editors/3d/manipulator_gizmo_opacity", 0.9); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, "editors/3d/manipulator_gizmo_opacity", PROPERTY_HINT_RANGE, "0,1,0.01")); - EDITOR_DEF_RST("editors/3d/navigation/show_viewport_rotation_gizmo", true); - EDITOR_DEF_RST("editors/3d/navigation/show_viewport_navigation_gizmo", DisplayServer::get_singleton()->is_touchscreen_available()); + EDITOR_DEF("editors/3d/navigation/show_viewport_rotation_gizmo", true); + EDITOR_DEF("editors/3d/navigation/show_viewport_navigation_gizmo", DisplayServer::get_singleton()->is_touchscreen_available()); current_hover_gizmo_handle = -1; current_hover_gizmo_handle_secondary = false; diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index fc252822c4..53e167ace6 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -384,6 +384,7 @@ private: void _view_settings_confirmed(real_t p_interp_delta); void _update_camera(real_t p_interp_delta); + void _update_navigation_controls_visibility(); Transform3D to_camera_transform(const Cursor &p_cursor) const; void _draw(); @@ -399,6 +400,7 @@ private: Camera3D *previewing = nullptr; Camera3D *preview = nullptr; + bool previewing_camera; bool previewing_cinema; bool _is_node_locked(const Node *p_node); void _preview_exited_scene(); diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 63ca78d6c0..7a9e50e482 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -274,13 +274,10 @@ void Path3DGizmo::redraw() { // Fish Bone. v3p.push_back(p1); - v3p.push_back(p1 + (side - forward) * 0.06); + v3p.push_back(p1 + (side - forward + up * 0.3) * 0.06); v3p.push_back(p1); - v3p.push_back(p1 + (-side - forward) * 0.06); - - v3p.push_back(p1); - v3p.push_back(p1 + up * 0.03); + v3p.push_back(p1 + (-side - forward + up * 0.3) * 0.06); } add_lines(v3p, path_material); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index f79a001efb..d2dadd884b 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -153,9 +153,10 @@ void BoneTransformEditor::set_target(const String &p_prop) { void BoneTransformEditor::_property_keyed(const String &p_path, bool p_advance) { AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor(); - if (!te->has_keying()) { + if (!te || !te->has_keying()) { return; } + te->_clear_selection(); PackedStringArray split = p_path.split("/"); if (split.size() == 3 && split[0] == "bones") { int bone_idx = split[1].to_int(); diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 57f9b3135a..1fdf940a82 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -309,7 +309,22 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) { case ROTATE_LEFT: case FLIP_HORIZONTALLY: case FLIP_VERTICALLY: { - undo_redo->create_action(TTR("Rotate Polygons Left")); + switch (p_item_pressed) { + case ROTATE_RIGHT: { + undo_redo->create_action(TTR("Rotate Polygons Right")); + } break; + case ROTATE_LEFT: { + undo_redo->create_action(TTR("Rotate Polygons Left")); + } break; + case FLIP_HORIZONTALLY: { + undo_redo->create_action(TTR("Flip Polygons Horizontally")); + } break; + case FLIP_VERTICALLY: { + undo_redo->create_action(TTR("Flip Polygons Vertically")); + } break; + default: + break; + } for (unsigned int i = 0; i < polygons.size(); i++) { Vector<Point2> new_polygon; for (int point_index = 0; point_index < polygons[i].size(); point_index++) { diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 4131c06745..5a1f214933 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -924,6 +924,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over Color modulate = tile_data->get_modulate(); Color self_modulate = tile_map->get_self_modulate(); modulate *= self_modulate; + modulate *= tile_map->get_layer_modulate(tile_map_layer); // Draw the tile. p_overlay->draw_texture_rect_region(atlas_source->get_texture(), dest_rect, source_rect, modulate * Color(1.0, 1.0, 1.0, 0.5), transpose, tile_set->is_uv_clipping()); diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp index b31fb1aa58..2868c14452 100644 --- a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp +++ b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp @@ -398,7 +398,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() { source_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed)); source_from_property_editor->set_selectable(false); source_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); - source_from_property_editor->setup(-1, 99999, 1, true, false); + source_from_property_editor->setup(-1, 99999, 1, false, true, false); vboxcontainer_from->add_child(source_from_property_editor); coords_from_property_editor = memnew(EditorPropertyVector2i); @@ -417,7 +417,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() { alternative_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed)); alternative_from_property_editor->set_selectable(false); alternative_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); - alternative_from_property_editor->setup(-1, 99999, 1, true, false); + alternative_from_property_editor->setup(-1, 99999, 1, false, true, false); alternative_from_property_editor->hide(); vboxcontainer_from->add_child(alternative_from_property_editor); @@ -432,7 +432,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() { source_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed)); source_to_property_editor->set_selectable(false); source_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); - source_to_property_editor->setup(-1, 99999, 1, true, false); + source_to_property_editor->setup(-1, 99999, 1, false, true, false); vboxcontainer_to->add_child(source_to_property_editor); coords_to_property_editor = memnew(EditorPropertyVector2i); @@ -451,7 +451,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() { alternative_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed)); alternative_to_property_editor->set_selectable(false); alternative_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); - alternative_to_property_editor->setup(-1, 99999, 1, true, false); + alternative_to_property_editor->setup(-1, 99999, 1, false, true, false); alternative_to_property_editor->hide(); vboxcontainer_to->add_child(alternative_to_property_editor); diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 369a59c443..e9c6184d73 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -47,43 +47,7 @@ VersionControlEditorPlugin *VersionControlEditorPlugin::singleton = nullptr; void VersionControlEditorPlugin::_bind_methods() { - ClassDB::bind_method(D_METHOD("_initialize_vcs"), &VersionControlEditorPlugin::_initialize_vcs); - ClassDB::bind_method(D_METHOD("_set_credentials"), &VersionControlEditorPlugin::_set_credentials); - ClassDB::bind_method(D_METHOD("_update_set_up_warning"), &VersionControlEditorPlugin::_update_set_up_warning); - ClassDB::bind_method(D_METHOD("_commit"), &VersionControlEditorPlugin::_commit); - ClassDB::bind_method(D_METHOD("_refresh_stage_area"), &VersionControlEditorPlugin::_refresh_stage_area); - ClassDB::bind_method(D_METHOD("_move_all"), &VersionControlEditorPlugin::_move_all); - ClassDB::bind_method(D_METHOD("_load_diff"), &VersionControlEditorPlugin::_load_diff); - ClassDB::bind_method(D_METHOD("_display_diff"), &VersionControlEditorPlugin::_display_diff); - ClassDB::bind_method(D_METHOD("_item_activated"), &VersionControlEditorPlugin::_item_activated); - ClassDB::bind_method(D_METHOD("_update_branch_create_button"), &VersionControlEditorPlugin::_update_branch_create_button); - ClassDB::bind_method(D_METHOD("_update_remote_create_button"), &VersionControlEditorPlugin::_update_remote_create_button); - ClassDB::bind_method(D_METHOD("_update_commit_button"), &VersionControlEditorPlugin::_update_commit_button); - ClassDB::bind_method(D_METHOD("_refresh_branch_list"), &VersionControlEditorPlugin::_refresh_branch_list); - ClassDB::bind_method(D_METHOD("_set_commit_list_size"), &VersionControlEditorPlugin::_set_commit_list_size); - ClassDB::bind_method(D_METHOD("_refresh_commit_list"), &VersionControlEditorPlugin::_refresh_commit_list); - ClassDB::bind_method(D_METHOD("_refresh_remote_list"), &VersionControlEditorPlugin::_refresh_remote_list); - ClassDB::bind_method(D_METHOD("_ssh_public_key_selected"), &VersionControlEditorPlugin::_ssh_public_key_selected); - ClassDB::bind_method(D_METHOD("_ssh_private_key_selected"), &VersionControlEditorPlugin::_ssh_private_key_selected); - ClassDB::bind_method(D_METHOD("_commit_message_gui_input"), &VersionControlEditorPlugin::_commit_message_gui_input); - ClassDB::bind_method(D_METHOD("_cell_button_pressed"), &VersionControlEditorPlugin::_cell_button_pressed); - ClassDB::bind_method(D_METHOD("_discard_all"), &VersionControlEditorPlugin::_discard_all); - ClassDB::bind_method(D_METHOD("_create_branch"), &VersionControlEditorPlugin::_create_branch); - ClassDB::bind_method(D_METHOD("_create_remote"), &VersionControlEditorPlugin::_create_remote); - ClassDB::bind_method(D_METHOD("_remove_branch"), &VersionControlEditorPlugin::_remove_branch); - ClassDB::bind_method(D_METHOD("_remove_remote"), &VersionControlEditorPlugin::_remove_remote); - ClassDB::bind_method(D_METHOD("_branch_item_selected"), &VersionControlEditorPlugin::_branch_item_selected); - ClassDB::bind_method(D_METHOD("_remote_selected"), &VersionControlEditorPlugin::_remote_selected); - ClassDB::bind_method(D_METHOD("_fetch"), &VersionControlEditorPlugin::_fetch); - ClassDB::bind_method(D_METHOD("_pull"), &VersionControlEditorPlugin::_pull); - ClassDB::bind_method(D_METHOD("_push"), &VersionControlEditorPlugin::_push); - ClassDB::bind_method(D_METHOD("_extra_option_selected"), &VersionControlEditorPlugin::_extra_option_selected); - ClassDB::bind_method(D_METHOD("_update_extra_options"), &VersionControlEditorPlugin::_update_extra_options); - ClassDB::bind_method(D_METHOD("_popup_branch_remove_confirm"), &VersionControlEditorPlugin::_popup_branch_remove_confirm); - ClassDB::bind_method(D_METHOD("_popup_remote_remove_confirm"), &VersionControlEditorPlugin::_popup_remote_remove_confirm); - ClassDB::bind_method(D_METHOD("_popup_file_dialog"), &VersionControlEditorPlugin::_popup_file_dialog); - - ClassDB::bind_method(D_METHOD("popup_vcs_set_up_dialog"), &VersionControlEditorPlugin::popup_vcs_set_up_dialog); + // No binds required so far. } void VersionControlEditorPlugin::_create_vcs_metadata_files() { @@ -94,12 +58,10 @@ void VersionControlEditorPlugin::_create_vcs_metadata_files() { void VersionControlEditorPlugin::_notification(int p_what) { if (p_what == NOTIFICATION_READY) { String installed_plugin = GLOBAL_DEF("editor/version_control/plugin_name", ""); - String project_path = GLOBAL_DEF("editor/version_control/project_path", OS::get_singleton()->get_resource_dir()); - project_path_input->set_text(project_path); bool has_autoload_enable = GLOBAL_DEF("editor/version_control/autoload_on_startup", false); if (installed_plugin != "" && has_autoload_enable) { - if (_load_plugin(installed_plugin, project_path)) { + if (_load_plugin(installed_plugin)) { _set_credentials(); } } @@ -144,18 +106,15 @@ void VersionControlEditorPlugin::_initialize_vcs() { const int id = set_up_choice->get_selected_id(); String selected_plugin = set_up_choice->get_item_text(id); - if (_load_plugin(selected_plugin, project_path_input->get_text())) { + if (_load_plugin(selected_plugin)) { ProjectSettings::get_singleton()->set("editor/version_control/autoload_on_startup", true); ProjectSettings::get_singleton()->set("editor/version_control/plugin_name", selected_plugin); - ProjectSettings::get_singleton()->set("editor/version_control/project_path", project_path_input->get_text()); ProjectSettings::get_singleton()->save(); } } void VersionControlEditorPlugin::_set_vcs_ui_state(bool p_enabled) { - select_project_path_button->set_disabled(p_enabled); set_up_dialog->get_ok_button()->set_disabled(!p_enabled); - project_path_input->set_editable(!p_enabled); set_up_choice->set_disabled(p_enabled); toggle_vcs_choice->set_pressed_no_signal(p_enabled); } @@ -181,14 +140,14 @@ void VersionControlEditorPlugin::_set_credentials() { EditorSettings::get_singleton()->set_setting("version_control/ssh_private_key_path", ssh_private_key); } -bool VersionControlEditorPlugin::_load_plugin(String p_name, String p_project_path) { +bool VersionControlEditorPlugin::_load_plugin(String p_name) { Object *extension_instance = ClassDB::instantiate(p_name); ERR_FAIL_NULL_V_MSG(extension_instance, false, "Received a nullptr VCS extension instance during construction."); EditorVCSInterface *vcs_plugin = Object::cast_to<EditorVCSInterface>(extension_instance); ERR_FAIL_NULL_V_MSG(vcs_plugin, false, vformat("Could not cast VCS extension instance to %s.", EditorVCSInterface::get_class_static())); - String res_dir = project_path_input->get_text(); + String res_dir = OS::get_singleton()->get_resource_dir(); ERR_FAIL_COND_V_MSG(!vcs_plugin->initialize(res_dir), false, "Could not initialize " + p_name); @@ -435,6 +394,10 @@ void VersionControlEditorPlugin::_discard_file(String p_file_path, EditorVCSInte EditorFileSystem::get_singleton()->update_file(p_file_path); } +void VersionControlEditorPlugin::_confirm_discard_all() { + discard_all_confirm->popup_centered(); +} + void VersionControlEditorPlugin::_discard_all() { TreeItem *file_entry = unstaged_files->get_root()->get_first_child(); while (file_entry) { @@ -631,7 +594,6 @@ void VersionControlEditorPlugin::_display_diff(int p_idx) { diff->pop(); diff->pop(); - diff->add_newline(); diff->push_font(EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("status_source"), SNAME("EditorFonts"))); for (int j = 0; j < diff_file.diff_hunks.size(); j++) { EditorVCSInterface::DiffHunk hunk = diff_file.diff_hunks[j]; @@ -641,6 +603,7 @@ void VersionControlEditorPlugin::_display_diff(int p_idx) { String old_lines = String::num_int64(hunk.old_lines); String new_lines = String::num_int64(hunk.new_lines); + diff->add_newline(); diff->append_text("[center]@@ " + old_start + "," + old_lines + " " + new_start + "," + new_lines + " @@[/center]"); diff->add_newline(); @@ -653,7 +616,6 @@ void VersionControlEditorPlugin::_display_diff(int p_idx) { break; } diff->add_newline(); - diff->add_newline(); } diff->pop(); @@ -721,12 +683,10 @@ void VersionControlEditorPlugin::_display_diff_split_view(List<EditorVCSInterfac if (diff_line.old_line_no >= 0) { diff->push_cell(); - diff->push_indent(1); diff->push_color(has_change ? red : white); diff->add_text(String::num_int64(diff_line.old_line_no)); diff->pop(); diff->pop(); - diff->pop(); diff->push_cell(); diff->push_color(has_change ? red : white); @@ -753,12 +713,10 @@ void VersionControlEditorPlugin::_display_diff_split_view(List<EditorVCSInterfac if (diff_line.new_line_no >= 0) { diff->push_cell(); - diff->push_indent(1); diff->push_color(has_change ? green : white); diff->add_text(String::num_int64(diff_line.new_line_no)); diff->pop(); diff->pop(); - diff->pop(); diff->push_cell(); diff->push_color(has_change ? green : white); @@ -943,10 +901,6 @@ void VersionControlEditorPlugin::_toggle_vcs_integration(bool p_toggled) { } } -void VersionControlEditorPlugin::_project_path_selected(String p_project_path) { - project_path_input->set_text(p_project_path); -} - void VersionControlEditorPlugin::fetch_available_vcs_plugin_names() { available_plugins.clear(); ClassDB::get_direct_inheriters_from_class(EditorVCSInterface::get_class_static(), &available_plugins); @@ -1040,34 +994,6 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_choice->set_h_size_flags(Control::SIZE_EXPAND_FILL); set_up_hbc->add_child(set_up_choice); - HBoxContainer *project_path_hbc = memnew(HBoxContainer); - project_path_hbc->set_h_size_flags(Control::SIZE_FILL); - set_up_vbc->add_child(project_path_hbc); - - Label *project_path_label = memnew(Label); - project_path_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); - project_path_label->set_text(TTR("VCS Project Path")); - project_path_hbc->add_child(project_path_label); - - project_path_input = memnew(LineEdit); - project_path_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); - project_path_input->set_text(OS::get_singleton()->get_resource_dir()); - project_path_hbc->add_child(project_path_input); - - FileDialog *select_project_path_file_dialog = memnew(FileDialog); - select_project_path_file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM); - select_project_path_file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_DIR); - select_project_path_file_dialog->set_show_hidden_files(true); - select_project_path_file_dialog->set_current_dir(OS::get_singleton()->get_resource_dir()); - select_project_path_file_dialog->connect(SNAME("dir_selected"), callable_mp(this, &VersionControlEditorPlugin::_project_path_selected)); - project_path_hbc->add_child(select_project_path_file_dialog); - - select_project_path_button = memnew(Button); - select_project_path_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Folder", "EditorIcons")); - select_project_path_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_popup_file_dialog).bind(select_project_path_file_dialog)); - select_project_path_button->set_tooltip_text(TTR("Select VCS project path")); - project_path_hbc->add_child(select_project_path_button); - HBoxContainer *toggle_vcs_hbc = memnew(HBoxContainer); toggle_vcs_hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); set_up_vbc->add_child(toggle_vcs_hbc); @@ -1240,10 +1166,21 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { refresh_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_remote_list)); unstage_title->add_child(refresh_button); + discard_all_confirm = memnew(AcceptDialog); + discard_all_confirm->set_title(TTR("Discard all changes")); + discard_all_confirm->set_min_size(Size2i(400, 50)); + discard_all_confirm->set_text(TTR("This operation is IRREVERSIBLE. Your changes will be deleted FOREVER.")); + discard_all_confirm->set_hide_on_ok(true); + discard_all_confirm->set_ok_button_text(TTR("Permanentally delete my changes")); + discard_all_confirm->add_cancel_button(); + version_commit_dock->add_child(discard_all_confirm); + + discard_all_confirm->get_ok_button()->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_discard_all)); + discard_all_button = memnew(Button); discard_all_button->set_tooltip_text(TTR("Discard all changes")); discard_all_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Close"), SNAME("EditorIcons"))); - discard_all_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_discard_all)); + discard_all_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_confirm_discard_all)); discard_all_button->set_flat(true); unstage_title->add_child(discard_all_button); diff --git a/editor/plugins/version_control_editor_plugin.h b/editor/plugins/version_control_editor_plugin.h index 3340384a92..d73588a1bf 100644 --- a/editor/plugins/version_control_editor_plugin.h +++ b/editor/plugins/version_control_editor_plugin.h @@ -73,8 +73,6 @@ private: AcceptDialog *set_up_dialog = nullptr; CheckButton *toggle_vcs_choice = nullptr; OptionButton *set_up_choice = nullptr; - LineEdit *project_path_input = nullptr; - Button *select_project_path_button = nullptr; VBoxContainer *set_up_vbc = nullptr; VBoxContainer *set_up_settings_vbc = nullptr; LineEdit *set_up_username = nullptr; @@ -86,6 +84,8 @@ private: FileDialog *set_up_ssh_private_key_file_dialog = nullptr; Label *set_up_warning_text = nullptr; + AcceptDialog *discard_all_confirm = nullptr; + OptionButton *commit_list_size_button = nullptr; AcceptDialog *branch_create_confirm = nullptr; @@ -150,13 +150,14 @@ private: void _update_opened_tabs(); void _update_extra_options(); - bool _load_plugin(String p_name, String p_project_path); + bool _load_plugin(String p_name); void _pull(); void _push(); void _force_push(); void _fetch(); void _commit(); + void _confirm_discard_all(); void _discard_all(); void _refresh_stage_area(); void _refresh_branch_list(); @@ -193,7 +194,6 @@ private: void _create_vcs_metadata_files(); void _popup_file_dialog(Variant p_file_dialog_variant); void _toggle_vcs_integration(bool p_toggled); - void _project_path_selected(String p_project_path); friend class EditorVCSInterface; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index cf811067c9..c93b0019dc 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1263,7 +1263,7 @@ Dictionary VisualShaderEditor::get_custom_node_data(Ref<VisualShaderNodeCustom> void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { Ref<Script> scr = Ref<Script>(p_resource.ptr()); - if (scr.is_null() || scr->get_instance_base_type() != String("VisualShaderNodeCustom")) { + if (scr.is_null() || scr->get_instance_base_type() != "VisualShaderNodeCustom") { return; } diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index a1b2aa3089..0d93c8a95e 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -446,8 +446,8 @@ private: String::utf8("\n• ") + TTR("Fast rendering of simple scenes.")); } else if (renderer_type == "gl_compatibility") { renderer_info->set_text( - String::utf8("• ") + TTR("Supports desktop, mobile, + web platforms.") + - String::utf8("\n• ") + TTR("Least advanced 3D graphics.") + + String::utf8("• ") + TTR("Supports desktop, mobile + web platforms.") + + String::utf8("\n• ") + TTR("Least advanced 3D graphics (currently work-in-progress).") + String::utf8("\n• ") + TTR("Intended for low-end/older devices.") + String::utf8("\n• ") + TTR("Uses OpenGL 3 backend (OpenGL 3.3/ES 3.0/WebGL2).") + String::utf8("\n• ") + TTR("Fastest rendering of simple scenes.")); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index b406b2a1ce..b99a83a546 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -553,6 +553,14 @@ void ProjectSettingsEditor::_update_theme() { } } +void ProjectSettingsEditor::_input_filter_focused() { + set_close_on_escape(false); +} + +void ProjectSettingsEditor::_input_filter_unfocused() { + set_close_on_escape(true); +} + void ProjectSettingsEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { @@ -683,6 +691,8 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { action_map_editor->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed)); action_map_editor->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed)); action_map_editor->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered)); + action_map_editor->connect(SNAME("filter_focused"), callable_mp(this, &ProjectSettingsEditor::_input_filter_focused)); + action_map_editor->connect(SNAME("filter_unfocused"), callable_mp(this, &ProjectSettingsEditor::_input_filter_unfocused)); tab_container->add_child(action_map_editor); localization_editor = memnew(LocalizationEditor); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 7f6dd1b692..1687be47fb 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -107,6 +107,9 @@ class ProjectSettingsEditor : public AcceptDialog { void _update_action_map_editor(); void _update_theme(); + void _input_filter_focused(); + void _input_filter_unfocused(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index 247d5e1717..8ac0aa6fe3 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -48,6 +48,7 @@ #include "editor/import/editor_import_plugin.h" #include "editor/import/resource_importer_scene.h" #include "editor/plugins/animation_tree_editor_plugin.h" +#include "editor/plugins/audio_stream_editor_plugin.h" #include "editor/plugins/audio_stream_randomizer_editor_plugin.h" #include "editor/plugins/bit_map_editor_plugin.h" #include "editor/plugins/bone_map_editor_plugin.h" @@ -155,6 +156,7 @@ void register_editor_types() { // This list is alphabetized, and plugins that depend on Node2D are in their own section below. EditorPlugins::add_by_type<AnimationTreeEditorPlugin>(); + EditorPlugins::add_by_type<AudioStreamEditorPlugin>(); EditorPlugins::add_by_type<AudioStreamRandomizerEditorPlugin>(); EditorPlugins::add_by_type<BitMapEditorPlugin>(); EditorPlugins::add_by_type<BoneMapEditorPlugin>(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 96688a3614..f91b571118 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1965,37 +1965,21 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { } Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - if (selected.size() == 1) { - Node *node = selected.front()->get(); - Ref<Script> existing = node->get_script(); - - undo_redo->create_action(TTR("Attach Script"), UndoRedo::MERGE_DISABLE, node); - undo_redo->add_do_method(InspectorDock::get_singleton(), "store_script_properties", node); - undo_redo->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", node); - undo_redo->add_do_method(node, "set_script", p_script); - undo_redo->add_undo_method(node, "set_script", existing); - undo_redo->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", node); - undo_redo->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", node); + undo_redo->create_action(TTR("Attach Script"), UndoRedo::MERGE_DISABLE, selected.front()->get()); + for (Node *E : selected) { + Ref<Script> existing = E->get_script(); + undo_redo->add_do_method(InspectorDock::get_singleton(), "store_script_properties", E); + undo_redo->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", E); + undo_redo->add_do_method(E, "set_script", p_script); + undo_redo->add_undo_method(E, "set_script", existing); + undo_redo->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", E); + undo_redo->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", E); undo_redo->add_do_method(this, "_update_script_button"); undo_redo->add_undo_method(this, "_update_script_button"); - undo_redo->commit_action(); - } else { - undo_redo->create_action(TTR("Attach Script"), UndoRedo::MERGE_DISABLE, selected.front()->get()); - for (Node *E : selected) { - Ref<Script> existing = E->get_script(); - undo_redo->add_do_method(InspectorDock::get_singleton(), "store_script_properties", E); - undo_redo->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", E); - undo_redo->add_do_method(E, "set_script", p_script); - undo_redo->add_undo_method(E, "set_script", existing); - undo_redo->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", E); - undo_redo->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", E); - undo_redo->add_do_method(this, "_update_script_button"); - undo_redo->add_undo_method(this, "_update_script_button"); - } - undo_redo->commit_action(); } + undo_redo->commit_action(); - _push_item(p_script.operator->()); + _push_item(p_script.ptr()); _update_script_button(); } diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 092ef30678..30a9dc5bbf 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -132,8 +132,16 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i if (config_err.is_empty()) { return; } - config_err = config_err.word_wrap(80); - warning->set_text(config_err); + + const PackedInt32Array boundaries = TS->string_get_word_breaks(config_err, "", 80); + PackedStringArray lines; + for (int i = 0; i < boundaries.size(); i += 2) { + const int start = boundaries[i]; + const int end = boundaries[i + 1]; + lines.append(config_err.substr(start, end - start + 1)); + } + + warning->set_text(String("\n").join(lines)); warning->popup_centered(); } else if (p_id == BUTTON_SIGNALS) { diff --git a/main/main.cpp b/main/main.cpp index 8fa0a78480..7e1fd38e14 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2121,7 +2121,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { MAIN_PRINT("Main: Setup Logo"); -#if defined(WEB_ENABLED) || defined(ANDROID_ENABLED) +#if !defined(TOOLS_ENABLED) && (defined(WEB_ENABLED) || defined(ANDROID_ENABLED)) bool show_logo = false; #else bool show_logo = true; @@ -2594,6 +2594,7 @@ bool Main::start() { } if (dump_gdextension_interface || dump_extension_api) { + OS::get_singleton()->set_exit_code(EXIT_SUCCESS); return false; } diff --git a/misc/scripts/compare_extension_api.py b/misc/scripts/compare_extension_api.py deleted file mode 100755 index f96db4278c..0000000000 --- a/misc/scripts/compare_extension_api.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import sys - -# TODO: -# Add a process that compares the original godot-cpp/godot-headers/extension_api.json with the new extension_api.json (both passed as arguments) and reports any API calls that have been removed. -# If we only have additions or no changes to the file, we pass -# For now we deem this too early because the API isn't stable enough yet. - -sys.exit(0) diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp index 155f7809b0..8d21541341 100644 --- a/modules/basis_universal/register_types.cpp +++ b/modules/basis_universal/register_types.cpp @@ -277,6 +277,7 @@ void initialize_basis_universal_module(ModuleInitializationLevel p_level) { basisu_encoder_init(); Image::basis_universal_packer = basis_universal_packer; #endif + basist::basisu_transcoder_init(); Image::basis_universal_unpacker = basis_universal_unpacker; Image::basis_universal_unpacker_ptr = basis_universal_unpacker_ptr; } diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 4981750b7d..fd748ea569 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -106,6 +106,7 @@ <param index="0" name="instance" type="Object" /> <description> Returns the passed [param instance] converted to a Dictionary. Can be useful for serializing. + [b]Note:[/b] Cannot be used to serialize objects with built-in scripts attached or objects allocated within built-in scripts. [codeblock] var foo = "bar" func _ready(): diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 91f31174dd..1fe1561559 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1111,21 +1111,27 @@ bool GDScript::inherits_script(const Ref<Script> &p_script) const { GDScript *GDScript::find_class(const String &p_qualified_name) { String first = p_qualified_name.get_slice("::", 0); + Vector<String> class_names; GDScript *result = nullptr; + // Empty initial name means start here. if (first.is_empty() || first == name) { + class_names = p_qualified_name.split("::"); result = this; - } else if (first == get_root_script()->path) { + } else if (p_qualified_name.begins_with(get_root_script()->path)) { + // Script path could have a class path separator("::") in it. + class_names = p_qualified_name.trim_prefix(get_root_script()->path).split("::"); result = get_root_script(); } else if (HashMap<StringName, Ref<GDScript>>::Iterator E = subclasses.find(first)) { + class_names = p_qualified_name.split("::"); result = E->value.ptr(); } else if (_owner != nullptr) { // Check parent scope. return _owner->find_class(p_qualified_name); } - int name_count = p_qualified_name.get_slice_count("::"); - for (int i = 1; result != nullptr && i < name_count; i++) { - String current_name = p_qualified_name.get_slice("::", i); + // Starts at index 1 because index 0 was handled above. + for (int i = 1; result != nullptr && i < class_names.size(); i++) { + String current_name = class_names[i]; if (HashMap<StringName, Ref<GDScript>>::Iterator E = result->subclasses.find(current_name)) { result = E->value.ptr(); } else { @@ -1137,11 +1143,12 @@ GDScript *GDScript::find_class(const String &p_qualified_name) { return result; } -bool GDScript::is_subclass(const GDScript *p_script) { +bool GDScript::has_class(const GDScript *p_script) { String fqn = p_script->fully_qualified_name; - if (!fqn.is_empty() && fqn != fully_qualified_name && fqn.begins_with(fully_qualified_name)) { - String fqn_rest = fqn.substr(fully_qualified_name.length()); - return find_class(fqn_rest) == p_script; + if (fully_qualified_name.is_empty() && fqn.get_slice("::", 0).is_empty()) { + return p_script == this; + } else if (fqn.begins_with(fully_qualified_name)) { + return p_script == find_class(fqn.trim_prefix(fully_qualified_name)); } return false; } @@ -1288,15 +1295,10 @@ String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript) } GDScript *GDScript::_get_gdscript_from_variant(const Variant &p_variant) { - Variant::Type type = p_variant.get_type(); - if (type != Variant::Type::OBJECT) - return nullptr; - Object *obj = p_variant; - if (obj == nullptr) { + if (obj == nullptr || obj->get_instance_id().is_null()) { return nullptr; } - return Object::cast_to<GDScript>(obj); } @@ -1964,6 +1966,16 @@ void GDScriptLanguage::add_named_global_constant(const StringName &p_name, const named_globals[p_name] = p_value; } +Variant GDScriptLanguage::get_any_global_constant(const StringName &p_name) { + if (named_globals.has(p_name)) { + return named_globals[p_name]; + } + if (globals.has(p_name)) { + return _global_array[globals[p_name]]; + } + ERR_FAIL_V_MSG(Variant(), vformat("Could not find any global constant with name: %s.", p_name)); +} + void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) { ERR_FAIL_COND(!named_globals.has(p_name)); named_globals.erase(p_name); @@ -2597,8 +2609,7 @@ Ref<GDScript> GDScriptLanguage::get_script_by_fully_qualified_name(const String SelfList<GDScript> *elem = script_list.first(); while (elem) { GDScript *scr = elem->self(); - scr = scr->find_class(p_name); - if (scr != nullptr) { + if (scr->fully_qualified_name == p_name) { return scr; } elem = elem->next(); diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 7911ea47ec..39367e377b 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -187,7 +187,7 @@ public: bool inherits_script(const Ref<Script> &p_script) const override; GDScript *find_class(const String &p_qualified_name); - bool is_subclass(const GDScript *p_script); + bool has_class(const GDScript *p_script); GDScript *get_root_script(); bool is_root_script() const { return _owner == nullptr; } String get_fully_qualified_name() const { return fully_qualified_name; } @@ -455,6 +455,9 @@ public: _FORCE_INLINE_ Variant *get_global_array() { return _global_array; } _FORCE_INLINE_ const HashMap<StringName, int> &get_global_map() const { return globals; } _FORCE_INLINE_ const HashMap<StringName, Variant> &get_named_globals_map() const { return named_globals; } + // These two functions should be used when behavior needs to be consistent between in-editor and running the scene + bool has_any_global_constant(const StringName &p_name) { return named_globals.has(p_name) || globals.has(p_name); } + Variant get_any_global_constant(const StringName &p_name); _FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 1149749ef7..fc2e6e94f3 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -225,6 +225,8 @@ void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::Clas } p_list->push_back(p_node); + // TODO: Try to solve class inheritance if not yet resolving. + // Prioritize node base type over its outer class if (p_node->base_type.class_type != nullptr) { get_class_node_current_scope_classes(p_node->base_type.class_type, p_list); @@ -235,15 +237,58 @@ void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::Clas } } -Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) { - if (p_class->base_type.is_set()) { - // Already resolved +Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source) { + if (p_source == nullptr && parser->has_class(p_class)) { + p_source = p_class; + } + + if (p_class->base_type.is_resolving()) { + push_error(vformat(R"(Could not resolve class "%s": Cyclic reference.)", type_from_metatype(p_class->get_datatype()).to_string()), p_source); + return ERR_PARSE_ERROR; + } + + if (!p_class->base_type.has_no_type()) { + // Already resolved. + return OK; + } + + if (!parser->has_class(p_class)) { + String script_path = p_class->get_datatype().script_path; + Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path); + if (parser_ref.is_null()) { + push_error(vformat(R"(Could not find script "%s".)", script_path), p_source); + return ERR_PARSE_ERROR; + } + + Error err = parser_ref->raise_status(GDScriptParserRef::PARSED); + if (err) { + push_error(vformat(R"(Could not parse script "%s": %s.)", script_path, error_names[err]), p_source); + return ERR_PARSE_ERROR; + } + + ERR_FAIL_COND_V_MSG(!parser_ref->get_parser()->has_class(p_class), ERR_PARSE_ERROR, R"(Parser bug: Mismatched external parser.)"); + + GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer(); + GDScriptParser *other_parser = parser_ref->get_parser(); + + int error_count = other_parser->errors.size(); + other_analyzer->resolve_class_inheritance(p_class); + if (other_parser->errors.size() > error_count) { + push_error(vformat(R"(Could not resolve inheritance for class "%s".)", p_class->fqcn), p_source); + return ERR_PARSE_ERROR; + } + return OK; } + GDScriptParser::ClassNode *previous_class = parser->current_class; + parser->current_class = p_class; + if (p_class->identifier) { StringName class_name = p_class->identifier->name; - if (class_exists(class_name)) { + if (GDScriptParser::get_builtin_type(class_name) < Variant::VARIANT_MAX) { + push_error(vformat(R"(Class "%s" hides a built-in type.)", class_name), p_class->identifier); + } else if (class_exists(class_name)) { push_error(vformat(R"(Class "%s" hides a native class.)", class_name), p_class->identifier); } else if (ScriptServer::is_global_class(class_name) && (ScriptServer::get_global_class_path(class_name) != parser->script_path || p_class != parser->head)) { push_error(vformat(R"(Class "%s" hides a global script class.)", class_name), p_class->identifier); @@ -252,7 +297,9 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, } } - GDScriptParser::DataType result; + GDScriptParser::DataType resolving_datatype; + resolving_datatype.kind = GDScriptParser::DataType::RESOLVING; + p_class->base_type = resolving_datatype; // Set datatype for class. GDScriptParser::DataType class_type; @@ -265,6 +312,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, class_type.builtin_type = Variant::OBJECT; p_class->set_datatype(class_type); + GDScriptParser::DataType result; if (!p_class->extends_used) { result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; result.kind = GDScriptParser::DataType::NATIVE; @@ -286,7 +334,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, return ERR_PARSE_ERROR; } - Error err = ext_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + Error err = ext_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); if (err != OK) { push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", p_class->extends_path), p_class); return err; @@ -313,7 +361,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, return ERR_PARSE_ERROR; } - Error err = base_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + Error err = base_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); if (err != OK) { push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class); return err; @@ -322,7 +370,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, } } else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) { const ProjectSettings::AutoloadInfo &info = ProjectSettings::get_singleton()->get_autoload(name); - if (info.path.get_extension().to_lower() != ".gd") { + if (info.path.get_extension().to_lower() != GDScriptLanguage::get_singleton()->get_extension()) { push_error(vformat(R"(Singleton %s is not a GDScript.)", info.name), p_class); return ERR_PARSE_ERROR; } @@ -333,11 +381,12 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, return ERR_PARSE_ERROR; } - Error err = info_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + Error err = info_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); if (err != OK) { push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class); return err; } + base = info_parser->get_parser()->head->get_datatype(); } else if (class_exists(name) && ClassDB::can_instantiate(name)) { base.kind = GDScriptParser::DataType::NATIVE; base.native_type = name; @@ -349,7 +398,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, for (GDScriptParser::ClassNode *look_class : script_classes) { if (look_class->identifier && look_class->identifier->name == name) { if (!look_class->get_datatype().is_set()) { - Error err = resolve_inheritance(look_class, false); + Error err = resolve_class_inheritance(look_class, p_class); if (err) { return err; } @@ -358,15 +407,9 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, found = true; break; } - if (look_class->members_indices.has(name) && look_class->get_member(name).type == GDScriptParser::ClassNode::Member::CLASS) { - GDScriptParser::ClassNode::Member member = look_class->get_member(name); - if (!member.m_class->get_datatype().is_set()) { - Error err = resolve_inheritance(member.m_class, false); - if (err) { - return err; - } - } - base = member.m_class->get_datatype(); + if (look_class->has_member(name)) { + resolve_class_member(look_class, name, p_class); + base = look_class->get_member(name).get_datatype(); found = true; break; } @@ -402,7 +445,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, result = base; } - if (!result.is_set()) { + if (!result.is_set() || result.has_no_type()) { // TODO: More specific error messages. push_error(vformat(R"(Could not resolve inheritance for class "%s".)", p_class->identifier == nullptr ? "<main>" : p_class->identifier->name), p_class); return ERR_PARSE_ERROR; @@ -422,10 +465,21 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, class_type.native_type = result.native_type; p_class->set_datatype(class_type); + parser->current_class = previous_class; + + return OK; +} + +Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) { + Error err = resolve_class_inheritance(p_class); + if (err) { + return err; + } + if (p_recursive) { for (int i = 0; i < p_class->members.size(); i++) { if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) { - Error err = resolve_inheritance(p_class->members[i].m_class, true); + err = resolve_class_inheritance(p_class->members[i].m_class, true); if (err) { return err; } @@ -437,14 +491,29 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, } GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::TypeNode *p_type) { - GDScriptParser::DataType result; + GDScriptParser::DataType bad_type; + bad_type.kind = GDScriptParser::DataType::VARIANT; + bad_type.type_source = GDScriptParser::DataType::INFERRED; if (p_type == nullptr) { - result.kind = GDScriptParser::DataType::VARIANT; - return result; + return bad_type; + } + + if (p_type->get_datatype().is_resolving()) { + push_error(R"(Could not resolve datatype: Cyclic reference.)", p_type); + return bad_type; } - result.type_source = result.ANNOTATED_EXPLICIT; + if (!p_type->get_datatype().has_no_type()) { + return p_type->get_datatype(); + } + + GDScriptParser::DataType resolving_datatype; + resolving_datatype.kind = GDScriptParser::DataType::RESOLVING; + p_type->set_datatype(resolving_datatype); + + GDScriptParser::DataType result; + result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; result.builtin_type = Variant::OBJECT; if (p_type->type_chain.is_empty()) { @@ -458,29 +527,21 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type StringName first = p_type->type_chain[0]->name; if (first == SNAME("Variant")) { - result.kind = GDScriptParser::DataType::VARIANT; if (p_type->type_chain.size() > 1) { - push_error(R"("Variant" type don't contain nested types.)", p_type->type_chain[1]); - return GDScriptParser::DataType(); + // TODO: Variant does actually have a nested Type though. + push_error(R"(Variant doesn't contain nested types.)", p_type->type_chain[1]); + return bad_type; } - return result; - } - - if (first == SNAME("Object")) { + result.kind = GDScriptParser::DataType::VARIANT; + } else if (first == SNAME("Object")) { + // Object is treated like a native type, not a built-in. result.kind = GDScriptParser::DataType::NATIVE; result.native_type = SNAME("Object"); - if (p_type->type_chain.size() > 1) { - push_error(R"("Object" type don't contain nested types.)", p_type->type_chain[1]); - return GDScriptParser::DataType(); - } - return result; - } - - if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) { + } else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) { // Built-in types. if (p_type->type_chain.size() > 1) { push_error(R"(Built-in types don't contain nested types.)", p_type->type_chain[1]); - return GDScriptParser::DataType(); + return bad_type; } result.kind = GDScriptParser::DataType::BUILTIN; result.builtin_type = GDScriptParser::get_builtin_type(first); @@ -506,9 +567,9 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type String ext = path.get_extension(); if (ext == GDScriptLanguage::get_singleton()->get_extension()) { Ref<GDScriptParserRef> ref = get_parser_for(path); - if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) { + if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type); - return GDScriptParser::DataType(); + return bad_type; } result = ref->get_parser()->head->get_datatype(); } else { @@ -523,9 +584,9 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) { const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first); Ref<GDScriptParserRef> ref = get_parser_for(autoload.path); - if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) { + if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type); - return GDScriptParser::DataType(); + return bad_type; } result = ref->get_parser()->head->get_datatype(); } else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) { @@ -541,26 +602,28 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type break; } if (script_class->members_indices.has(first)) { - GDScriptParser::ClassNode::Member member = script_class->members[script_class->members_indices[first]]; + resolve_class_member(script_class, first, p_type); + + GDScriptParser::ClassNode::Member member = script_class->get_member(first); switch (member.type) { case GDScriptParser::ClassNode::Member::CLASS: - result = member.m_class->get_datatype(); + result = member.get_datatype(); break; case GDScriptParser::ClassNode::Member::ENUM: - result = member.m_enum->get_datatype(); + result = member.get_datatype(); break; case GDScriptParser::ClassNode::Member::CONSTANT: - if (member.constant->get_datatype().is_meta_type) { - result = member.constant->get_datatype(); + if (member.get_datatype().is_meta_type) { + result = member.get_datatype(); result.is_meta_type = false; break; } else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) { Ref<GDScript> gdscript = member.constant->initializer->reduced_value; if (gdscript.is_valid()) { Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path()); - if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) { + if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), p_type); - return GDScriptParser::DataType(); + return bad_type; } result = ref->get_parser()->head->get_datatype(); result.is_meta_type = false; @@ -578,15 +641,14 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type [[fallthrough]]; default: push_error(vformat(R"("%s" is a %s but does not contain a type.)", first, member.get_type_name()), p_type); - return GDScriptParser::DataType(); + return bad_type; } } } } if (!result.is_set()) { push_error(vformat(R"("%s" was not found in the current scope.)", first), p_type); - result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type. - return result; + return bad_type; } if (p_type->type_chain.size() > 1) { @@ -597,27 +659,26 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type result = p_type->type_chain[i]->get_datatype(); if (!result.is_set()) { push_error(vformat(R"(Could not find type "%s" under base "%s".)", p_type->type_chain[i]->name, base.to_string()), p_type->type_chain[1]); - result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type. - return result; + return bad_type; } else if (!result.is_meta_type) { push_error(vformat(R"(Member "%s" under base "%s" is not a valid type.)", p_type->type_chain[i]->name, base.to_string()), p_type->type_chain[1]); - result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type. - return result; + return bad_type; } } } else if (result.kind == GDScriptParser::DataType::NATIVE) { // Only enums allowed for native. - if (ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) { - if (p_type->type_chain.size() > 2) { - push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]); - } else { - result = make_native_enum_type(result.native_type, p_type->type_chain[1]->name); - } + if (!ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) { + push_error(vformat(R"(Could not find nested type "%s" under base "%s".)", p_type->type_chain[1]->name, result.to_string()), p_type->type_chain[1]); + return bad_type; + } + if (p_type->type_chain.size() > 2) { + push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]); + return bad_type; } + result = make_native_enum_type(result.native_type, p_type->type_chain[1]->name); } else { push_error(vformat(R"(Could not find nested type "%s" under base "%s".)", p_type->type_chain[1]->name, result.to_string()), p_type->type_chain[1]); - result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type. - return result; + return bad_type; } } @@ -629,22 +690,77 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type return result; } -void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_class) { - if (p_class->resolved_interface) { +void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source) { + ERR_FAIL_COND(!p_class->has_member(p_name)); + resolve_class_member(p_class, p_class->members_indices[p_name], p_source); +} + +void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source) { + ERR_FAIL_INDEX(p_index, p_class->members.size()); + + GDScriptParser::ClassNode::Member &member = p_class->members.write[p_index]; + if (p_source == nullptr && parser->has_class(p_class)) { + p_source = member.get_source_node(); + } + + if (member.get_datatype().is_resolving()) { + push_error(vformat(R"(Could not resolve member "%s": Cyclic reference.)", member.get_name()), p_source); return; } - p_class->resolved_interface = true; + + if (member.get_datatype().is_set()) { + return; + } + + if (!parser->has_class(p_class)) { + String script_path = p_class->get_datatype().script_path; + Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path); + if (parser_ref.is_null()) { + push_error(vformat(R"(Could not find script "%s" (While resolving "%s").)", script_path, member.get_name()), p_source); + return; + } + + Error err = parser_ref->raise_status(GDScriptParserRef::PARSED); + if (err) { + push_error(vformat(R"(Could not resolve script "%s": %s (While resolving "%s").)", script_path, error_names[err], member.get_name()), p_source); + return; + } + + ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)"); + + GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer(); + GDScriptParser *other_parser = parser_ref->get_parser(); + + int error_count = other_parser->errors.size(); + other_analyzer->resolve_class_member(p_class, p_index); + if (other_parser->errors.size() > error_count) { + push_error(vformat(R"(Could not resolve member "%s".)", member.get_name()), p_source); + } + + return; + } + + // If it's already resolving, that's ok. + if (!p_class->base_type.is_resolving()) { + Error err = resolve_class_inheritance(p_class); + if (err) { + return; + } + } GDScriptParser::ClassNode *previous_class = parser->current_class; parser->current_class = p_class; - for (int i = 0; i < p_class->members.size(); i++) { - GDScriptParser::ClassNode::Member member = p_class->members[i]; + GDScriptParser::DataType resolving_datatype; + resolving_datatype.kind = GDScriptParser::DataType::RESOLVING; + { switch (member.type) { case GDScriptParser::ClassNode::Member::VARIABLE: { check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable); + member.variable->set_datatype(resolving_datatype); + GDScriptParser::DataType datatype; datatype.kind = GDScriptParser::DataType::VARIANT; datatype.type_source = GDScriptParser::DataType::UNDETECTED; @@ -656,7 +772,6 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } if (member.variable->initializer != nullptr) { - member.variable->set_datatype(datatype); // Allow recursive usage. reduce_expression(member.variable->initializer); if ((member.variable->infer_datatype || (member.variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && member.variable->initializer->type == GDScriptParser::Node::ARRAY) { // Typed array. @@ -667,18 +782,14 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } datatype = member.variable->initializer->get_datatype(); + if (datatype.type_source != GDScriptParser::DataType::UNDETECTED) { datatype.type_source = GDScriptParser::DataType::INFERRED; } - } - // Check if initializer is an unset identifier (ie: a variable within scope, but declared below) - if (member.variable->initializer && !member.variable->initializer->get_datatype().is_set()) { - if (member.variable->initializer->type == GDScriptParser::Node::IDENTIFIER) { - GDScriptParser::IdentifierNode *initializer_identifier = static_cast<GDScriptParser::IdentifierNode *>(member.variable->initializer); - push_error(vformat(R"(Identifier "%s" must be declared above current variable.)", initializer_identifier->name), member.variable->initializer); - } else { - ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier."); + if (!datatype.is_set()) { + push_error(vformat(R"(Could not resolve initializer for member "%s".)", member.variable->identifier->name), member.variable->initializer); + datatype.kind = GDScriptParser::DataType::VARIANT; } } @@ -730,10 +841,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas case GDScriptParser::ClassNode::Member::CONSTANT: { check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant); - reduce_expression(member.constant->initializer); + member.constant->set_datatype(resolving_datatype); GDScriptParser::DataType specified_type; - if (member.constant->datatype_specifier != nullptr) { specified_type = resolve_datatype(member.constant->datatype_specifier); specified_type.is_meta_type = false; @@ -741,7 +851,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas GDScriptParser::DataType datatype; if (member.constant->initializer) { + reduce_expression(member.constant->initializer); datatype = member.constant->initializer->get_datatype(); + if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) { GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer); const_fold_array(array); @@ -754,6 +866,11 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer)); } + if (!datatype.is_set()) { + push_error(vformat(R"(Could not resolve initializer for member "%s".)", member.constant->identifier->name), member.constant->initializer); + datatype.kind = GDScriptParser::DataType::VARIANT; + } + if (!member.constant->initializer->is_constant) { push_error(R"(Initializer for a constant must be a constant expression.)", member.constant->initializer); } @@ -782,18 +899,21 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas case GDScriptParser::ClassNode::Member::SIGNAL: { check_class_member_name_conflict(p_class, member.signal->identifier->name, member.signal); + member.signal->set_datatype(resolving_datatype); + + // This is the _only_ way to declare a signal. Therefore, we can generate its + // MethodInfo inline so it's a tiny bit more efficient. + MethodInfo mi = MethodInfo(member.signal->identifier->name); + for (int j = 0; j < member.signal->parameters.size(); j++) { - GDScriptParser::DataType signal_type = resolve_datatype(member.signal->parameters[j]->datatype_specifier); - signal_type.is_meta_type = false; - member.signal->parameters[j]->set_datatype(signal_type); + GDScriptParser::ParameterNode *param = member.signal->parameters[j]; + GDScriptParser::DataType param_type = resolve_datatype(param->datatype_specifier); + param_type.is_meta_type = false; + param->set_datatype(param_type); + mi.arguments.push_back(PropertyInfo(param_type.builtin_type, param->identifier->name)); + // TODO: add signal parameter default values } - // TODO: Make MethodInfo from signal. - GDScriptParser::DataType signal_type; - signal_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - signal_type.kind = GDScriptParser::DataType::BUILTIN; - signal_type.builtin_type = Variant::SIGNAL; - - member.signal->set_datatype(signal_type); + member.signal->set_datatype(make_signal_type(mi)); // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) { @@ -803,6 +923,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas case GDScriptParser::ClassNode::Member::ENUM: { check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum); + member.m_enum->set_datatype(resolving_datatype); + GDScriptParser::DataType enum_type; enum_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; enum_type.kind = GDScriptParser::DataType::ENUM; @@ -812,7 +934,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas enum_type.is_meta_type = true; enum_type.is_constant = true; - // Enums can't be nested, so we can safely override this. + const GDScriptParser::EnumNode *prev_enum = current_enum; current_enum = member.m_enum; for (int j = 0; j < member.m_enum->values.size(); j++) { @@ -840,7 +962,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas enum_type.enum_values[element.identifier->name] = element.value; } - current_enum = nullptr; + current_enum = prev_enum; member.m_enum->set_datatype(enum_type); @@ -850,15 +972,18 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } break; case GDScriptParser::ClassNode::Member::FUNCTION: - resolve_function_signature(member.function); + resolve_function_signature(member.function, p_source); break; case GDScriptParser::ClassNode::Member::ENUM_VALUE: { if (member.enum_value.custom_value) { check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.custom_value); + member.enum_value.identifier->set_datatype(resolving_datatype); + + const GDScriptParser::EnumNode *prev_enum = current_enum; current_enum = member.enum_value.parent_enum; reduce_expression(member.enum_value.custom_value); - current_enum = nullptr; + current_enum = prev_enum; if (!member.enum_value.custom_value->is_constant) { push_error(R"(Enum values must be constant.)", member.enum_value.custom_value); @@ -872,18 +997,30 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.parent_enum); if (member.enum_value.index > 0) { - member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1; + const GDScriptParser::EnumNode::Value &prev_value = member.enum_value.parent_enum->values[member.enum_value.index - 1]; + resolve_class_member(p_class, prev_value.identifier->name, member.enum_value.identifier); + member.enum_value.value = prev_value.value + 1; } else { member.enum_value.value = 0; } member.enum_value.resolved = true; } + // Also update the original references. - member.enum_value.parent_enum->values.write[member.enum_value.index] = member.enum_value; - p_class->members.write[i].enum_value = member.enum_value; + member.enum_value.parent_enum->values.set(member.enum_value.index, member.enum_value); + + GDScriptParser::DataType datatype; + datatype.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + datatype.kind = GDScriptParser::DataType::BUILTIN; + datatype.builtin_type = Variant::INT; + member.enum_value.identifier->set_datatype(datatype); } break; case GDScriptParser::ClassNode::Member::CLASS: check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class); + // If it's already resolving, that's ok. + if (!member.m_class->base_type.is_resolving()) { + resolve_class_inheritance(member.m_class, p_source); + } break; case GDScriptParser::ClassNode::Member::GROUP: // No-op, but needed to silence warnings. @@ -894,28 +1031,123 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } - // Recurse nested classes. - for (int i = 0; i < p_class->members.size(); i++) { - GDScriptParser::ClassNode::Member member = p_class->members[i]; - if (member.type != GDScriptParser::ClassNode::Member::CLASS) { - continue; + parser->current_class = previous_class; +} + +void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source) { + if (p_source == nullptr && parser->has_class(p_class)) { + p_source = p_class; + } + + if (!p_class->resolved_interface) { + if (!parser->has_class(p_class)) { + String script_path = p_class->get_datatype().script_path; + Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path); + if (parser_ref.is_null()) { + push_error(vformat(R"(Could not find script "%s".)", script_path), p_source); + return; + } + + Error err = parser_ref->raise_status(GDScriptParserRef::PARSED); + if (err) { + push_error(vformat(R"(Could not resolve script "%s": %s.)", script_path, error_names[err]), p_source); + return; + } + + ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)"); + + GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer(); + GDScriptParser *other_parser = parser_ref->get_parser(); + + int error_count = other_parser->errors.size(); + other_analyzer->resolve_class_interface(p_class); + if (other_parser->errors.size() > error_count) { + push_error(vformat(R"(Could not resolve class "%s".)", p_class->fqcn), p_source); + } + + return; + } + p_class->resolved_interface = true; + + if (resolve_class_inheritance(p_class) != OK) { + return; + } + + GDScriptParser::DataType base_type = p_class->base_type; + if (base_type.kind == GDScriptParser::DataType::CLASS) { + GDScriptParser::ClassNode *base_class = base_type.class_type; + resolve_class_interface(base_class, p_class); } - resolve_class_interface(member.m_class); + for (int i = 0; i < p_class->members.size(); i++) { + resolve_class_member(p_class, i); + } } +} - parser->current_class = previous_class; +void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_class, bool p_recursive) { + resolve_class_interface(p_class); + + if (p_recursive) { + for (int i = 0; i < p_class->members.size(); i++) { + GDScriptParser::ClassNode::Member member = p_class->members[i]; + if (member.type == GDScriptParser::ClassNode::Member::CLASS) { + resolve_class_interface(member.m_class, true); + } + } + } } -void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { +void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source) { + if (p_source == nullptr && parser->has_class(p_class)) { + p_source = p_class; + } + if (p_class->resolved_body) { return; } + + if (!parser->has_class(p_class)) { + String script_path = p_class->get_datatype().script_path; + Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path); + if (parser_ref.is_null()) { + push_error(vformat(R"(Could not find script "%s".)", script_path), p_source); + return; + } + + Error err = parser_ref->raise_status(GDScriptParserRef::PARSED); + if (err) { + push_error(vformat(R"(Could not resolve script "%s": %s.)", script_path, error_names[err]), p_source); + return; + } + + ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)"); + + GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer(); + GDScriptParser *other_parser = parser_ref->get_parser(); + + int error_count = other_parser->errors.size(); + other_analyzer->resolve_class_body(p_class); + if (other_parser->errors.size() > error_count) { + push_error(vformat(R"(Could not resolve class "%s".)", p_class->fqcn), p_source); + } + + return; + } + p_class->resolved_body = true; GDScriptParser::ClassNode *previous_class = parser->current_class; parser->current_class = p_class; + resolve_class_interface(p_class, p_source); + + GDScriptParser::DataType base_type = p_class->base_type; + if (base_type.kind == GDScriptParser::DataType::CLASS) { + GDScriptParser::ClassNode *base_class = base_type.class_type; + resolve_class_body(base_class, p_class); + } + // Do functions and properties now. for (int i = 0; i < p_class->members.size(); i++) { GDScriptParser::ClassNode::Member member = p_class->members[i]; @@ -958,18 +1190,6 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { } } - parser->current_class = previous_class; - - // Recurse nested classes. - for (int i = 0; i < p_class->members.size(); i++) { - GDScriptParser::ClassNode::Member member = p_class->members[i]; - if (member.type != GDScriptParser::ClassNode::Member::CLASS) { - continue; - } - - resolve_class_body(member.m_class); - } - // Check unused variables and datatypes of property getters and setters. for (int i = 0; i < p_class->members.size(); i++) { GDScriptParser::ClassNode::Member member = p_class->members[i]; @@ -1057,6 +1277,21 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { } } } + + parser->current_class = previous_class; +} + +void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, bool p_recursive) { + resolve_class_body(p_class); + + if (p_recursive) { + for (int i = 0; i < p_class->members.size(); i++) { + GDScriptParser::ClassNode::Member member = p_class->members[i]; + if (member.type == GDScriptParser::ClassNode::Member::CLASS) { + resolve_class_body(member.m_class, true); + } + } + } } void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root) { @@ -1066,8 +1301,10 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root case GDScriptParser::Node::NONE: break; // Unreachable. case GDScriptParser::Node::CLASS: - resolve_class_interface(static_cast<GDScriptParser::ClassNode *>(p_node)); - resolve_class_body(static_cast<GDScriptParser::ClassNode *>(p_node)); + if (OK == resolve_class_inheritance(static_cast<GDScriptParser::ClassNode *>(p_node), true)) { + resolve_class_interface(static_cast<GDScriptParser::ClassNode *>(p_node), true); + resolve_class_body(static_cast<GDScriptParser::ClassNode *>(p_node), true); + } break; case GDScriptParser::Node::CONSTANT: resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node)); @@ -1149,7 +1386,16 @@ void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_anno // TODO: Add second validation function for annotations, so they can use checked types. } -void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function) { +void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source) { + if (p_source == nullptr) { + p_source = p_function; + } + + if (p_function->get_datatype().is_resolving()) { + push_error(vformat(R"(Could not resolve function "%s": Cyclic reference.)", p_function->identifier->name), p_source); + return; + } + if (p_function->resolved_signature) { return; } @@ -1158,6 +1404,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * GDScriptParser::FunctionNode *previous_function = parser->current_function; parser->current_function = p_function; + GDScriptParser::DataType prev_datatype = p_function->get_datatype(); + + GDScriptParser::DataType resolving_datatype; + resolving_datatype.kind = GDScriptParser::DataType::RESOLVING; + p_function->set_datatype(resolving_datatype); + #ifdef TOOLS_ENABLED int default_value_count = 0; #endif // TOOLS_ENABLED @@ -1262,6 +1514,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * #endif // TOOLS_ENABLED } + if (p_function->get_datatype().is_resolving()) { + p_function->set_datatype(prev_datatype); + } + parser->current_function = previous_function; } @@ -1492,6 +1748,12 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable type = p_variable->initializer->get_datatype(); +#ifdef DEBUG_ENABLED + if (p_variable->initializer->type == GDScriptParser::Node::CALL && type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) { + parser->push_warning(p_variable->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_variable->initializer)->function_name); + } +#endif + if (p_variable->infer_datatype) { type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; @@ -1505,11 +1767,6 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable } else { type.type_source = GDScriptParser::DataType::INFERRED; } -#ifdef DEBUG_ENABLED - if (p_variable->initializer->type == GDScriptParser::Node::CALL && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) { - parser->push_warning(p_variable->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_variable->initializer)->function_name); - } -#endif } if (p_variable->datatype_specifier != nullptr) { @@ -1588,7 +1845,7 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant type = p_constant->initializer->get_datatype(); #ifdef DEBUG_ENABLED - if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) { + if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) { parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name); } #endif @@ -2076,7 +2333,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig } #ifdef DEBUG_ENABLED - if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_value_type.kind == GDScriptParser::DataType::BUILTIN && assigned_value_type.builtin_type == Variant::NIL) { + if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_value_type.is_hard_type() && assigned_value_type.kind == GDScriptParser::DataType::BUILTIN && assigned_value_type.builtin_type == Variant::NIL) { parser->push_warning(p_assignment->assigned_value, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value)->function_name); } else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) { parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION); @@ -2745,7 +3002,7 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str return type; } - Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + Error err = ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); if (err) { push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source); type.type_source = GDScriptParser::DataType::UNDETECTED; @@ -2768,6 +3025,10 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str } void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base) { + if (!p_identifier->get_datatype().has_no_type()) { + return; + } + GDScriptParser::DataType base; if (p_base == nullptr) { base = type_from_metatype(parser->current_class->get_datatype()); @@ -2860,16 +3121,16 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->set_datatype(base_class->get_datatype()); return; } + if (base_class->has_member(name)) { - const GDScriptParser::ClassNode::Member &member = base_class->get_member(name); + resolve_class_member(base_class, name, p_identifier); + + GDScriptParser::ClassNode::Member member = base_class->get_member(name); p_identifier->set_datatype(member.get_datatype()); switch (member.type) { case GDScriptParser::ClassNode::Member::CONSTANT: - // For out-of-order resolution: - reduce_expression(member.constant->initializer); p_identifier->is_constant = true; p_identifier->reduced_value = member.constant->initializer->reduced_value; - p_identifier->set_datatype(member.constant->initializer->get_datatype()); p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; p_identifier->constant_source = member.constant; break; @@ -2878,6 +3139,12 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->reduced_value = member.enum_value.value; p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; break; + case GDScriptParser::ClassNode::Member::ENUM: + if (p_base != nullptr && p_base->is_constant) { + p_identifier->is_constant = true; + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; + } + break; case GDScriptParser::ClassNode::Member::VARIABLE: p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE; p_identifier->variable_source = member.variable; @@ -2887,13 +3154,19 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL; break; case GDScriptParser::ClassNode::Member::FUNCTION: - resolve_function_signature(member.function); p_identifier->set_datatype(make_callable_type(member.function->info)); break; case GDScriptParser::ClassNode::Member::CLASS: - // For out-of-order resolution: - resolve_class_interface(member.m_class); - p_identifier->set_datatype(member.m_class->get_datatype()); + if (p_base != nullptr && p_base->is_constant) { + p_identifier->is_constant = true; + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; + + Error err = OK; + GDScript *scr = GDScriptCache::get_full_script(base.script_path, err).ptr(); + ERR_FAIL_COND_MSG(err != OK, "Error while getting subscript full script."); + scr = scr->find_class(p_identifier->get_datatype().class_type->fqcn); + p_identifier->reduced_value = scr; + } break; default: break; // Type already set. @@ -2904,36 +3177,31 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod // TODO: Allow outer static functions. if (base_class->outer != nullptr) { List<GDScriptParser::ClassNode *> script_classes; - get_class_node_current_scope_classes(parser->current_class, &script_classes); + get_class_node_current_scope_classes(base_class->outer, &script_classes); for (GDScriptParser::ClassNode *script_class : script_classes) { if (script_class->has_member(name)) { - const GDScriptParser::ClassNode::Member &member = script_class->get_member(name); + resolve_class_member(script_class, name, p_identifier); + + GDScriptParser::ClassNode::Member member = script_class->get_member(name); switch (member.type) { - case GDScriptParser::ClassNode::Member::CONSTANT: { + case GDScriptParser::ClassNode::Member::CONSTANT: // TODO: Make sure loops won't cause problem. And make special error message for those. - // For out-of-order resolution: - reduce_expression(member.constant->initializer); p_identifier->set_datatype(member.get_datatype()); p_identifier->is_constant = true; p_identifier->reduced_value = member.constant->initializer->reduced_value; return; - } break; - case GDScriptParser::ClassNode::Member::ENUM_VALUE: { + case GDScriptParser::ClassNode::Member::ENUM_VALUE: p_identifier->set_datatype(member.get_datatype()); p_identifier->is_constant = true; p_identifier->reduced_value = member.enum_value.value; return; - } break; - case GDScriptParser::ClassNode::Member::ENUM: { + case GDScriptParser::ClassNode::Member::ENUM: p_identifier->set_datatype(member.get_datatype()); p_identifier->is_constant = false; return; - } break; - case GDScriptParser::ClassNode::Member::CLASS: { - resolve_class_interface(member.m_class); - p_identifier->set_datatype(member.m_class->get_datatype()); + case GDScriptParser::ClassNode::Member::CLASS: + p_identifier->set_datatype(member.get_datatype()); return; - } break; default: break; } @@ -3139,21 +3407,21 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident if (ResourceLoader::get_resource_type(autoload.path) == "GDScript") { Ref<GDScriptParserRef> singl_parser = get_parser_for(autoload.path); if (singl_parser.is_valid()) { - Error err = singl_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); if (err == OK) { result = type_from_metatype(singl_parser->get_parser()->head->get_datatype()); } } } else if (ResourceLoader::get_resource_type(autoload.path) == "PackedScene") { - if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(name)) { - Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name]; + if (GDScriptLanguage::get_singleton()->has_any_global_constant(name)) { + Variant constant = GDScriptLanguage::get_singleton()->get_any_global_constant(name); Node *node = Object::cast_to<Node>(constant); if (node != nullptr) { Ref<GDScript> scr = node->get_script(); if (scr.is_valid()) { Ref<GDScriptParserRef> singl_parser = get_parser_for(scr->get_script_path()); if (singl_parser.is_valid()) { - Error err = singl_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); if (err == OK) { result = type_from_metatype(singl_parser->get_parser()->head->get_datatype()); } @@ -3168,17 +3436,8 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } } - if (GDScriptLanguage::get_singleton()->get_global_map().has(name)) { - int idx = GDScriptLanguage::get_singleton()->get_global_map()[name]; - Variant constant = GDScriptLanguage::get_singleton()->get_global_array()[idx]; - p_identifier->set_datatype(type_from_variant(constant, p_identifier)); - p_identifier->is_constant = true; - p_identifier->reduced_value = constant; - return; - } - - if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(name)) { - Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name]; + if (GDScriptLanguage::get_singleton()->has_any_global_constant(name)) { + Variant constant = GDScriptLanguage::get_singleton()->get_any_global_constant(name); p_identifier->set_datatype(type_from_variant(constant, p_identifier)); p_identifier->is_constant = true; p_identifier->reduced_value = constant; @@ -3347,53 +3606,42 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } GDScriptParser::DataType base_type = p_subscript->base->get_datatype(); - // If base is a class metatype, use the analyzer instead. - if (p_subscript->base->is_constant && !(base_type.is_meta_type && base_type.kind == GDScriptParser::DataType::CLASS)) { + bool valid = false; + // If the base is a metatype, use the analyzer instead. + if (p_subscript->base->is_constant && !base_type.is_meta_type) { // Just try to get it. - bool valid = false; Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid); - - // If it's a GDScript instance, try to get the full script. Maybe it's not still completely loaded. - Ref<GDScript> gdscr = Ref<GDScript>(p_subscript->base->reduced_value); - if (!valid && gdscr.is_valid()) { - Error err = OK; - GDScriptCache::get_full_script(gdscr->get_script_path(), err); - if (err == OK) { - value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid); - } - } - - if (!valid) { - push_error(vformat(R"(Cannot get member "%s" from "%s".)", p_subscript->attribute->name, p_subscript->base->reduced_value), p_subscript->index); - result_type.kind = GDScriptParser::DataType::VARIANT; - } else { + if (valid) { p_subscript->is_constant = true; p_subscript->reduced_value = value; result_type = type_from_variant(value, p_subscript); } + } else if (base_type.is_variant() || !base_type.is_hard_type()) { + valid = true; + result_type.kind = GDScriptParser::DataType::VARIANT; + mark_node_unsafe(p_subscript); } else { - if (base_type.is_variant() || !base_type.is_hard_type()) { - result_type.kind = GDScriptParser::DataType::VARIANT; - mark_node_unsafe(p_subscript); - } else { - reduce_identifier_from_base(p_subscript->attribute, &base_type); - GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype(); - if (attr_type.is_set()) { - result_type = attr_type; - p_subscript->is_constant = p_subscript->attribute->is_constant; - p_subscript->reduced_value = p_subscript->attribute->reduced_value; - } else { - if (base_type.kind == GDScriptParser::DataType::BUILTIN) { - push_error(vformat(R"(Cannot find member "%s" in base "%s".)", p_subscript->attribute->name, base_type.to_string()), p_subscript->attribute); + reduce_identifier_from_base(p_subscript->attribute, &base_type); + GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype(); + if (attr_type.is_set()) { + valid = true; + result_type = attr_type; + p_subscript->is_constant = p_subscript->attribute->is_constant; + p_subscript->reduced_value = p_subscript->attribute->reduced_value; + } else if (!base_type.is_meta_type || !base_type.is_constant) { + valid = base_type.kind != GDScriptParser::DataType::BUILTIN; #ifdef DEBUG_ENABLED - } else { - parser->push_warning(p_subscript, GDScriptWarning::UNSAFE_PROPERTY_ACCESS, p_subscript->attribute->name, base_type.to_string()); -#endif - } - result_type.kind = GDScriptParser::DataType::VARIANT; + if (valid) { + parser->push_warning(p_subscript, GDScriptWarning::UNSAFE_PROPERTY_ACCESS, p_subscript->attribute->name, base_type.to_string()); } +#endif + result_type.kind = GDScriptParser::DataType::VARIANT; } } + if (!valid) { + push_error(vformat(R"(Cannot find member "%s" in base "%s".)", p_subscript->attribute->name, type_from_metatype(base_type).to_string()), p_subscript->attribute); + result_type.kind = GDScriptParser::DataType::VARIANT; + } } else { if (p_subscript->index == nullptr) { return; @@ -3640,7 +3888,6 @@ void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternar void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) { reduce_expression(p_unary_op->operand); - GDScriptParser::DataType operand_type = p_unary_op->operand->get_datatype(); GDScriptParser::DataType result; if (p_unary_op->operand == nullptr) { @@ -3649,6 +3896,8 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) return; } + GDScriptParser::DataType operand_type = p_unary_op->operand->get_datatype(); + if (p_unary_op->operand->is_constant) { p_unary_op->is_constant = true; p_unary_op->reduced_value = Variant::evaluate(p_unary_op->variant_op, p_unary_op->operand->reduced_value, Variant()); @@ -3731,10 +3980,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va result.builtin_type = p_value.get_type(); result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; // Constant has explicit type. - if (p_value.get_type() == Variant::NIL) { - // A null value is a variant, not void. - result.kind = GDScriptParser::DataType::VARIANT; - } else if (p_value.get_type() == Variant::OBJECT) { + if (p_value.get_type() == Variant::OBJECT) { // Object is treated as a native type, not a builtin type. result.kind = GDScriptParser::DataType::NATIVE; @@ -3752,43 +3998,42 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va scr = obj->get_script(); } if (scr.is_valid()) { - result.script_type = scr; result.script_path = scr->get_path(); Ref<GDScript> gds = scr; if (gds.is_valid()) { result.kind = GDScriptParser::DataType::CLASS; // This might be an inner class, so we want to get the parser for the root. // But still get the inner class from that tree. - GDScript *current = gds.ptr(); - List<StringName> class_chain; - while (current->_owner) { - // Push to front so it's in reverse. - class_chain.push_front(current->name); - current = current->_owner; - } - - Ref<GDScriptParserRef> ref = get_parser_for(current->get_path()); + String script_path = gds->get_script_path(); + Ref<GDScriptParserRef> ref = get_parser_for(script_path); if (ref.is_null()) { - push_error("Could not find script in path.", p_source); + push_error(vformat(R"(Could not find script "%s".)", script_path), p_source); GDScriptParser::DataType error_type; error_type.kind = GDScriptParser::DataType::VARIANT; return error_type; } - ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); - - GDScriptParser::ClassNode *found = ref->get_parser()->head; - - // It should be okay to assume this exists, since we have a complete script already. - for (const StringName &E : class_chain) { - found = found->get_member(E).m_class; + Error err = ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); + GDScriptParser::ClassNode *found = nullptr; + if (err == OK) { + found = ref->get_parser()->find_class(gds->fully_qualified_name); + if (found != nullptr) { + err = resolve_class_inheritance(found, p_source); + } + } + if (err || found == nullptr) { + push_error(vformat(R"(Could not resolve script "%s".)", script_path), p_source); + GDScriptParser::DataType error_type; + error_type.kind = GDScriptParser::DataType::VARIANT; + return error_type; } result.class_type = found; result.script_path = ref->get_parser()->script_path; } else { result.kind = GDScriptParser::DataType::SCRIPT; + result.native_type = scr->get_instance_base_type(); } - result.native_type = scr->get_instance_base_type(); + result.script_type = scr; } else { result.kind = GDScriptParser::DataType::NATIVE; if (result.native_type == GDScriptNativeClass::get_class_static()) { @@ -3912,8 +4157,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo push_error(vformat(R"(Member "%s" is not a function.)", function_name), p_source); return false; } + + resolve_class_member(base_class, function_name, p_source); found_function = base_class->get_member(function_name).function; } + + resolve_class_inheritance(base_class, p_source); base_class = base_class->base_type.class_type; } @@ -4082,12 +4331,10 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con base_class = base_class->base_type.class_type; } - StringName base_native = base.native_type; - - ERR_FAIL_COND_V_MSG(!class_exists(base_native), false, "Non-existent native base class."); - - StringName parent = base_native; + StringName parent = base.native_type; while (parent != StringName()) { + ERR_FAIL_COND_V_MSG(!class_exists(parent), false, "Non-existent native base class."); + if (ClassDB::has_method(parent, name, true)) { parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "method", parent); return true; @@ -4277,6 +4524,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ case GDScriptParser::DataType::VARIANT: case GDScriptParser::DataType::BUILTIN: case GDScriptParser::DataType::ENUM: + case GDScriptParser::DataType::RESOLVING: case GDScriptParser::DataType::UNRESOLVED: break; // Already solved before. } @@ -4313,6 +4561,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ case GDScriptParser::DataType::VARIANT: case GDScriptParser::DataType::BUILTIN: case GDScriptParser::DataType::ENUM: + case GDScriptParser::DataType::RESOLVING: case GDScriptParser::DataType::UNRESOLVED: break; // Already solved before. } @@ -4327,6 +4576,10 @@ void GDScriptAnalyzer::push_error(const String &p_message, const GDScriptParser: void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) { #ifdef DEBUG_ENABLED + if (p_node == nullptr) { + return; + } + for (int i = p_node->start_line; i <= p_node->end_line; i++) { parser->unsafe_lines.insert(i); } @@ -4359,39 +4612,46 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) { } Error GDScriptAnalyzer::resolve_inheritance() { - return resolve_inheritance(parser->head); + return resolve_class_inheritance(parser->head, true); } Error GDScriptAnalyzer::resolve_interface() { - resolve_class_interface(parser->head); + resolve_class_interface(parser->head, true); return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR; } Error GDScriptAnalyzer::resolve_body() { - resolve_class_body(parser->head); + resolve_class_body(parser->head, true); return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR; } -Error GDScriptAnalyzer::resolve_program() { - resolve_class_interface(parser->head); - resolve_class_body(parser->head); - +Error GDScriptAnalyzer::resolve_dependencies() { for (KeyValue<String, Ref<GDScriptParserRef>> &K : depended_parsers) { if (K.value.is_null()) { return ERR_PARSE_ERROR; } - K.value->raise_status(GDScriptParserRef::FULLY_SOLVED); + K.value->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); } + return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR; } Error GDScriptAnalyzer::analyze() { parser->errors.clear(); - Error err = resolve_inheritance(parser->head); + Error err = OK; + + err = resolve_inheritance(); if (err) { return err; } - return resolve_program(); + + resolve_interface(); + resolve_body(); + if (!parser->errors.is_empty()) { + return ERR_PARSE_ERROR; + } + + return resolve_dependencies(); } GDScriptAnalyzer::GDScriptAnalyzer(GDScriptParser *p_parser) { diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 44ca1593ed..a4d9efb094 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -52,18 +52,20 @@ class GDScriptAnalyzer { void get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list); - Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true); + Error resolve_class_inheritance(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr); + Error resolve_class_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive); GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type); void decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement); - // This traverses the tree to resolve all TypeNodes. - Error resolve_program(); - void resolve_annotation(GDScriptParser::AnnotationNode *p_annotation); - void resolve_class_interface(GDScriptParser::ClassNode *p_class); - void resolve_class_body(GDScriptParser::ClassNode *p_class); - void resolve_function_signature(GDScriptParser::FunctionNode *p_function); + void resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source = nullptr); + void resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source = nullptr); + void resolve_class_interface(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr); + void resolve_class_interface(GDScriptParser::ClassNode *p_class, bool p_recursive); + void resolve_class_body(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr); + void resolve_class_body(GDScriptParser::ClassNode *p_class, bool p_recursive); + void resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source = nullptr); void resolve_function_body(GDScriptParser::FunctionNode *p_function); void resolve_node(GDScriptParser::Node *p_node, bool p_is_root = true); void resolve_suite(GDScriptParser::SuiteNode *p_suite); @@ -115,7 +117,7 @@ class GDScriptAnalyzer { GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source); void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal); bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr); - void push_error(const String &p_message, const GDScriptParser::Node *p_origin); + void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr); void mark_node_unsafe(const GDScriptParser::Node *p_node); void mark_lambda_use_self(); bool class_exists(const StringName &p_class) const; @@ -128,6 +130,7 @@ public: Error resolve_inheritance(); Error resolve_interface(); Error resolve_body(); + Error resolve_dependencies(); Error analyze(); GDScriptAnalyzer(GDScriptParser *p_parser); diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index d1467eea95..6faf2dde73 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -50,6 +50,13 @@ GDScriptParser *GDScriptParserRef::get_parser() const { return parser; } +GDScriptAnalyzer *GDScriptParserRef::get_analyzer() { + if (analyzer == nullptr) { + analyzer = memnew(GDScriptAnalyzer(parser)); + } + return analyzer; +} + Error GDScriptParserRef::raise_status(Status p_new_status) { ERR_FAIL_COND_V(parser == nullptr, ERR_INVALID_DATA); @@ -64,23 +71,22 @@ Error GDScriptParserRef::raise_status(Status p_new_status) { result = parser->parse(GDScriptCache::get_source_code(path), path, false); break; case PARSED: { - analyzer = memnew(GDScriptAnalyzer(parser)); status = INHERITANCE_SOLVED; - Error inheritance_result = analyzer->resolve_inheritance(); + Error inheritance_result = get_analyzer()->resolve_inheritance(); if (result == OK) { result = inheritance_result; } } break; case INHERITANCE_SOLVED: { status = INTERFACE_SOLVED; - Error interface_result = analyzer->resolve_interface(); + Error interface_result = get_analyzer()->resolve_interface(); if (result == OK) { result = interface_result; } } break; case INTERFACE_SOLVED: { status = FULLY_SOLVED; - Error body_result = analyzer->resolve_body(); + Error body_result = get_analyzer()->resolve_body(); if (result == OK) { result = body_result; } diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h index 0ee269f96c..43a45bfef6 100644 --- a/modules/gdscript/gdscript_cache.h +++ b/modules/gdscript/gdscript_cache.h @@ -65,6 +65,7 @@ public: bool is_valid() const; Status get_status() const; GDScriptParser *get_parser() const; + GDScriptAnalyzer *get_analyzer(); Error raise_status(Status p_new_status); void clear(); diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 5972481c3a..6a94c25c88 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -59,7 +59,7 @@ public: type = p_type; } Address(AddressMode p_mode, uint32_t p_address, const GDScriptDataType &p_type = GDScriptDataType()) { - mode = p_mode, + mode = p_mode; address = p_address; type = p_type; } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 2a98b856ce..cf9e734b05 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -117,8 +117,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D result.builtin_type = p_datatype.builtin_type; result.native_type = p_datatype.native_type; - String root_name = p_datatype.class_type->fqcn.get_slice("::", 0); - bool is_local_class = !root_name.is_empty() && root_name == main_script->fully_qualified_name; + bool is_local_class = parser->has_class(p_datatype.class_type); Ref<GDScript> script; if (is_local_class) { @@ -157,6 +156,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D result.builtin_type = Variant::INT; } break; + case GDScriptParser::DataType::RESOLVING: case GDScriptParser::DataType::UNRESOLVED: { ERR_PRINT("Parser bug: converting unresolved type."); return GDScriptDataType(); @@ -490,24 +490,29 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } break; case GDScriptParser::Node::CAST: { const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression); - GDScriptParser::DataType og_cast_type = cn->cast_type->get_datatype(); + GDScriptParser::DataType og_cast_type = cn->get_datatype(); GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type, codegen.script); - if (og_cast_type.kind == GDScriptParser::DataType::ENUM) { - // Enum types are usually treated as dictionaries, but in this case we want to cast to an integer. - cast_type.kind = GDScriptDataType::BUILTIN; - cast_type.builtin_type = Variant::INT; - } + GDScriptCodeGenerator::Address result; + if (cast_type.has_type) { + if (og_cast_type.kind == GDScriptParser::DataType::ENUM) { + // Enum types are usually treated as dictionaries, but in this case we want to cast to an integer. + cast_type.kind = GDScriptDataType::BUILTIN; + cast_type.builtin_type = Variant::INT; + } - // Create temporary for result first since it will be deleted last. - GDScriptCodeGenerator::Address result = codegen.add_temporary(cast_type); + // Create temporary for result first since it will be deleted last. + result = codegen.add_temporary(cast_type); - GDScriptCodeGenerator::Address src = _parse_expression(codegen, r_error, cn->operand); + GDScriptCodeGenerator::Address src = _parse_expression(codegen, r_error, cn->operand); - gen->write_cast(result, src, cast_type); + gen->write_cast(result, src, cast_type); - if (src.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); + if (src.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); + } + } else { + result = _parse_expression(codegen, r_error, cn->operand); } return result; @@ -2299,7 +2304,7 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri return ERR_COMPILATION_FAILED; } - if (base.ptr() == main_script || main_script->is_subclass(base.ptr())) { + if (main_script->has_class(base.ptr())) { Error err = _populate_class_members(base.ptr(), p_class->base_type.class_type, p_keep_state); if (err) { return err; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 79387d1bf6..d84af0c63c 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2848,16 +2848,6 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c break; } - GDScriptParser::CompletionContext c = completion_context; - c.current_function = nullptr; - c.current_suite = nullptr; - c.base = base.value.get_type() == Variant::OBJECT ? base.value.operator Object *() : nullptr; - if (base.type.kind == GDScriptParser::DataType::CLASS) { - c.current_class = base.type.class_type; - } else { - c.current_class = nullptr; - } - _find_identifiers_in_base(base, false, options, 0); } break; case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index f2aafe9f0c..103269f0f5 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -642,6 +642,53 @@ void GDScriptParser::parse_program() { clear_unused_annotations(); } +GDScriptParser::ClassNode *GDScriptParser::find_class(const String &p_qualified_name) const { + String first = p_qualified_name.get_slice("::", 0); + + Vector<String> class_names; + GDScriptParser::ClassNode *result = nullptr; + // Empty initial name means start at the head. + if (first.is_empty() || (head->identifier && first == head->identifier->name)) { + class_names = p_qualified_name.split("::"); + result = head; + } else if (p_qualified_name.begins_with(script_path)) { + // Script path could have a class path separator("::") in it. + class_names = p_qualified_name.trim_prefix(script_path).split("::"); + result = head; + } else if (head->has_member(first)) { + class_names = p_qualified_name.split("::"); + GDScriptParser::ClassNode::Member member = head->get_member(first); + if (member.type == GDScriptParser::ClassNode::Member::CLASS) { + result = member.m_class; + } + } + + // Starts at index 1 because index 0 was handled above. + for (int i = 1; result != nullptr && i < class_names.size(); i++) { + String current_name = class_names[i]; + GDScriptParser::ClassNode *next = nullptr; + if (result->has_member(current_name)) { + GDScriptParser::ClassNode::Member member = result->get_member(current_name); + if (member.type == GDScriptParser::ClassNode::Member::CLASS) { + next = member.m_class; + } + } + result = next; + } + + return result; +} + +bool GDScriptParser::has_class(const GDScriptParser::ClassNode *p_class) const { + if (head->fqcn.is_empty() && p_class->fqcn.get_slice("::", 0).is_empty()) { + return p_class == head; + } else if (p_class->fqcn.begins_with(head->fqcn)) { + return find_class(p_class->fqcn.trim_prefix(head->fqcn)) == p_class; + } + + return false; +} + GDScriptParser::ClassNode *GDScriptParser::parse_class() { ClassNode *n_class = alloc_node<ClassNode>(); @@ -2240,7 +2287,7 @@ GDScriptParser::IdentifierNode *GDScriptParser::parse_identifier() { GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign) { if (!previous.is_identifier()) { - ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token."); + ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing identifier node without identifier token."); } IdentifierNode *identifier = alloc_node<IdentifierNode>(); complete_extents(identifier); @@ -4042,11 +4089,12 @@ String GDScriptParser::DataType::to_string() const { } case ENUM: return enum_type.operator String() + " (enum)"; + case RESOLVING: case UNRESOLVED: return "<unresolved type>"; } - ERR_FAIL_V_MSG("<unresolved type", "Kind set outside the enum range."); + ERR_FAIL_V_MSG("<unresolved type>", "Kind set outside the enum range."); } static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_type) { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index d092a2a5e9..540ef1c561 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -107,6 +107,7 @@ public: CLASS, // GDScript. ENUM, // Enumeration. VARIANT, // Can be any type. + RESOLVING, // Currently resolving. UNRESOLVED, }; Kind kind = UNRESOLVED; @@ -133,9 +134,10 @@ public: MethodInfo method_info; // For callable/signals. HashMap<StringName, int64_t> enum_values; // For enums. - _FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; } + _FORCE_INLINE_ bool is_set() const { return kind != RESOLVING && kind != UNRESOLVED; } + _FORCE_INLINE_ bool is_resolving() const { return kind == RESOLVING; } _FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; } - _FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == UNRESOLVED; } + _FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == RESOLVING || kind == UNRESOLVED; } _FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; } String to_string() const; @@ -188,6 +190,7 @@ public: return script_type == p_other.script_type; case CLASS: return class_type == p_other.class_type; + case RESOLVING: case UNRESOLVED: break; } @@ -516,6 +519,32 @@ public: }; EnumNode::Value enum_value; + String get_name() const { + switch (type) { + case UNDEFINED: + return "<undefined member>"; + case CLASS: + // All class-type members have an id. + return m_class->identifier->name; + case CONSTANT: + return constant->identifier->name; + case FUNCTION: + return function->identifier->name; + case SIGNAL: + return signal->identifier->name; + case VARIABLE: + return variable->identifier->name; + case ENUM: + // All enum-type members have an id. + return m_enum->identifier->name; + case ENUM_VALUE: + return enum_value.identifier->name; + case GROUP: + return annotation->export_info.name; + } + return ""; + } + String get_type_name() const { switch (type) { case UNDEFINED: @@ -576,31 +605,42 @@ public: return variable->get_datatype(); case ENUM: return m_enum->get_datatype(); - case ENUM_VALUE: { - // Always integer. - DataType out_type; - out_type.type_source = DataType::ANNOTATED_EXPLICIT; - out_type.kind = DataType::BUILTIN; - out_type.builtin_type = Variant::INT; - return out_type; - } - case SIGNAL: { - DataType out_type; - out_type.type_source = DataType::ANNOTATED_EXPLICIT; - out_type.kind = DataType::BUILTIN; - out_type.builtin_type = Variant::SIGNAL; - // TODO: Add parameter info. - return out_type; - } - case GROUP: { + case ENUM_VALUE: + return enum_value.identifier->get_datatype(); + case SIGNAL: + return signal->get_datatype(); + case GROUP: return DataType(); - } case UNDEFINED: return DataType(); } ERR_FAIL_V_MSG(DataType(), "Reaching unhandled type."); } + Node *get_source_node() const { + switch (type) { + case CLASS: + return m_class; + case CONSTANT: + return constant; + case FUNCTION: + return function; + case VARIABLE: + return variable; + case ENUM: + return m_enum; + case ENUM_VALUE: + return enum_value.identifier; + case SIGNAL: + return signal; + case GROUP: + return annotation; + case UNDEFINED: + return nullptr; + } + ERR_FAIL_V_MSG(nullptr, "Reaching unhandled type."); + } + Member() {} Member(ClassNode *p_class) { @@ -1430,6 +1470,8 @@ public: Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion); ClassNode *get_tree() const { return head; } bool is_tool() const { return _is_tool; } + ClassNode *find_class(const String &p_qualified_name) const; + bool has_class(const GDScriptParser::ClassNode *p_class) const; static Variant::Type get_builtin_type(const StringName &p_type); CompletionContext get_completion_context() const { return completion_context; } diff --git a/modules/gdscript/language_server/godot_lsp.h b/modules/gdscript/language_server/godot_lsp.h index 024da1cab7..8b58d7731e 100644 --- a/modules/gdscript/language_server/godot_lsp.h +++ b/modules/gdscript/language_server/godot_lsp.h @@ -702,7 +702,7 @@ struct DiagnosticRelatedInformation { Dictionary to_json() const { Dictionary dict; - dict["location"] = location.to_json(), + dict["location"] = location.to_json(); dict["message"] = message; return dict; } diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.gd new file mode 100644 index 0000000000..0e1f7256f8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.gd @@ -0,0 +1,4 @@ +signal your_base +signal my_base +func test(): + your_base = my_base diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.out new file mode 100644 index 0000000000..5275183da2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a constant. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out index 87863baf75..b9a1d301ad 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -The member "Vector2" cannot have the same name as a builtin type. +Class "Vector2" hides a built-in type. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd new file mode 100644 index 0000000000..d2f6404cd2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd @@ -0,0 +1,8 @@ +func test(): + print(InnerA.new()) + +class InnerA extends InnerB: + pass + +class InnerB extends InnerA: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out new file mode 100644 index 0000000000..75a94baa17 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cyclic inheritance. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd new file mode 100644 index 0000000000..4292534951 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd @@ -0,0 +1,5 @@ +func test(): + print(c1) + +const c1 = c2 +const c2 = c1 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out new file mode 100644 index 0000000000..e71b3fc56a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Could not resolve member "c1": Cyclic reference. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd new file mode 100644 index 0000000000..1caef3d366 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd @@ -0,0 +1,5 @@ +func test(): + print(E1.V) + +enum E1 {V = E2.V} +enum E2 {V = E1.V} diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out new file mode 100644 index 0000000000..1b6569ba3a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Could not resolve member "E1": Cyclic reference. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd new file mode 100644 index 0000000000..237758f340 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd @@ -0,0 +1,5 @@ +func test(): + print(EV1) + +enum {EV1 = EV2} +enum {EV2 = EV1} diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out new file mode 100644 index 0000000000..233f5fee25 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Could not resolve member "EV1": Cyclic reference. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd new file mode 100644 index 0000000000..52e0d60389 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd @@ -0,0 +1,6 @@ +func test(): + print(v) + +var v = A.v + +const A = preload("cyclic_ref_external_a.notest.gd") diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out new file mode 100644 index 0000000000..64a6bd417d --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Could not resolve member "v". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd new file mode 100644 index 0000000000..9ef1769250 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd @@ -0,0 +1,3 @@ +const B = preload("cyclic_ref_external.gd") + +var v = B.v diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd new file mode 100644 index 0000000000..b610464c44 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd @@ -0,0 +1,9 @@ +func test(): + print(f1()) + print(f2()) + +static func f1(p := f2()) -> int: + return 1 + +static func f2(p := f1()) -> int: + return 2 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out new file mode 100644 index 0000000000..d3ec4b0692 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Could not resolve member "f1": Cyclic reference. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd new file mode 100644 index 0000000000..f750715838 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd @@ -0,0 +1,12 @@ +func test(): + print(v) + +var v := InnerA.new().f() + +class InnerA: + func f(p := InnerB.new().f()) -> int: + return 1 + +class InnerB extends InnerA: + func f(p := 1) -> int: + return super.f() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out new file mode 100644 index 0000000000..6bca25b330 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Could not resolve member "f": Cyclic reference. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd new file mode 100644 index 0000000000..6913888724 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd @@ -0,0 +1,5 @@ +func test(): + print(v1) + +var v1 := v2 +var v2 := v1 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out new file mode 100644 index 0000000000..c337882d9c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Could not resolve member "v1": Cyclic reference. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd new file mode 100644 index 0000000000..65c0d9dabc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd @@ -0,0 +1,12 @@ +class A: + class B: + func test(): + print(A.B.D) + +class C: + class D: + pass + +func test(): + var inst = A.B.new() + inst.test() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.out new file mode 100644 index 0000000000..6baed366f6 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot find member "D" in base "B". diff --git a/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.gd b/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.gd new file mode 100644 index 0000000000..ba1b198cbf --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.gd @@ -0,0 +1,5 @@ +# https://github.com/godotengine/godot/issues/69504#issuecomment-1345725988 + +func test(): + print("cast to Variant == null: ", 1 as Variant == null) + print("cast to Object == null: ", self as Object == null) diff --git a/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.out b/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.out new file mode 100644 index 0000000000..541de99b8e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.out @@ -0,0 +1,3 @@ +GDTEST_OK +cast to Variant == null: false +cast to Object == null: false diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd new file mode 100644 index 0000000000..757744b6f1 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd @@ -0,0 +1,6 @@ +const External = preload("external_enum_as_constant_external.notest.gd") +const MyEnum = External.MyEnum + +func test(): + print(MyEnum.WAITING == 0) + print(MyEnum.GODOT == 1) diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.out b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.out new file mode 100644 index 0000000000..9d111a8322 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.out @@ -0,0 +1,3 @@ +GDTEST_OK +true +true diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd new file mode 100644 index 0000000000..7c090844d0 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd @@ -0,0 +1,4 @@ +enum MyEnum { + WAITING, + GODOT +} diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.gd b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.gd new file mode 100644 index 0000000000..18dca109fb --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.gd @@ -0,0 +1,7 @@ +const External = preload("external_inner_class_as_constant_external.notest.gd") +const ExternalInnerClass = External.InnerClass + +func test(): + var inst_external: ExternalInnerClass = ExternalInnerClass.new() + inst_external.x = 4.0 + print(inst_external.x) diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.out b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.out new file mode 100644 index 0000000000..15666c46ad --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.out @@ -0,0 +1,2 @@ +GDTEST_OK +4 diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant_external.notest.gd new file mode 100644 index 0000000000..788c99d469 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant_external.notest.gd @@ -0,0 +1,2 @@ +class InnerClass: + var x: = 3.0 diff --git a/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.gd b/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.gd new file mode 100644 index 0000000000..39ced354df --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.gd @@ -0,0 +1,9 @@ +# https://github.com/godotengine/godot/issues/61159 + +func get_param(): + return null + +func test(): + var v = get_param() + v = get_param() + print(v) diff --git a/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.out b/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.out new file mode 100644 index 0000000000..f0c83a69b3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.out @@ -0,0 +1,2 @@ +GDTEST_OK +<null> diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd new file mode 100644 index 0000000000..11349cc916 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd @@ -0,0 +1,51 @@ +func test(): + print("v1: ", v1) + print("v1 is String: ", v1 is String) + print("v2: ", v2) + print("v2 is bool: ", v2 is bool) + print("c1: ", c1) + print("c1 is int: ", c1 is int) + print("c2: ", c2) + print("c2 is int: ", c2 is int) + print("E1.V1: ", E1.V1) + print("E1.V2: ", E1.V2) + print("E2.V: ", E2.V) + print("EV1: ", EV1) + print("EV2: ", EV2) + print("EV3: ", EV3) + +var v1 := InnerA.new().fn() + +class InnerA extends InnerAB: + func fn(p2 := E1.V2) -> String: + return "%s, p2=%s" % [super.fn(), p2] + + class InnerAB: + func fn(p1 := c1) -> String: + return "p1=%s" % p1 + +var v2 := f() + +func f() -> bool: + return true + +const c1 := E1.V1 + +enum E1 { + V1 = E2.V + 2, + V2 = V1 - 1 +} + +enum E2 {V = 2} + +const c2 := EV2 + +enum { + EV1 = 42, + UNUSED = EV3, + EV2 +} + +enum { + EV3 = EV1 + 1 +} diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order.out b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.out new file mode 100644 index 0000000000..b1e75d611d --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.out @@ -0,0 +1,15 @@ +GDTEST_OK +v1: p1=4, p2=3 +v1 is String: true +v2: true +v2 is bool: true +c1: 4 +c1 is int: true +c2: 44 +c2 is int: true +E1.V1: 4 +E1.V2: 3 +E2.V: 2 +EV1: 42 +EV2: 44 +EV3: 43 diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd new file mode 100644 index 0000000000..0b162bdff8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd @@ -0,0 +1,39 @@ +const B = preload("out_of_order_external_a.notest.gd") + +func test(): + print("v1: ", v1) + print("v1 is String: ", v1 is String) + print("v2: ", v2) + print("v2 is bool: ", v2 is bool) + print("c1: ", c1) + print("c1 is int: ", c1 is int) + print("c2: ", c2) + print("c2 is int: ", c2 is int) + print("E1.V1: ", E1.V1) + print("E1.V2: ", E1.V2) + print("B.E2.V: ", B.E2.V) + print("EV1: ", EV1) + print("EV2: ", EV2) + print("B.EV3: ", B.EV3) + +var v1 := Inner.new().fn() + +class Inner extends B.Inner: + func fn(p2 := E1.V2) -> String: + return "%s, p2=%s" % [super.fn(), p2] + +var v2 := B.new().f() + +const c1 := E1.V1 + +enum E1 { + V1 = B.E2.V + 2, + V2 = V1 - 1 +} + +const c2 := EV2 + +enum { + EV1 = 42, + EV2 = B.EV3 + 1 +} diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out new file mode 100644 index 0000000000..437f782fe6 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out @@ -0,0 +1,15 @@ +GDTEST_OK +v1: p1=4, p2=3 +v1 is String: true +v2: true +v2 is bool: true +c1: 4 +c1 is int: true +c2: 44 +c2 is int: true +E1.V1: 4 +E1.V2: 3 +B.E2.V: 2 +EV1: 42 +EV2: 44 +B.EV3: 43 diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd new file mode 100644 index 0000000000..d276f72fcf --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd @@ -0,0 +1,12 @@ +const A = preload("out_of_order_external.gd") + +class Inner: + func fn(p1 := A.c1) -> String: + return "p1=%s" % p1 + +func f(p := A.c1) -> bool: + return p is int + +enum E2 {V = 2} + +enum {EV3 = A.EV1 + 1} diff --git a/modules/gltf/editor/editor_scene_importer_fbx.cpp b/modules/gltf/editor/editor_scene_importer_fbx.cpp index fb5fb455b8..27e0052c1a 100644 --- a/modules/gltf/editor/editor_scene_importer_fbx.cpp +++ b/modules/gltf/editor/editor_scene_importer_fbx.cpp @@ -36,6 +36,7 @@ #include "core/config/project_settings.h" #include "editor/editor_settings.h" +#include "main/main.h" uint32_t EditorSceneFormatImporterFBX::get_import_flags() const { return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; @@ -111,4 +112,30 @@ void EditorSceneFormatImporterFBX::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) { } +bool EditorFileSystemImportFormatSupportQueryFBX::is_active() const { + String fbx2gltf_path = EDITOR_GET("filesystem/import/fbx/fbx2gltf_path"); + return !FileAccess::exists(fbx2gltf_path); +} + +Vector<String> EditorFileSystemImportFormatSupportQueryFBX::get_file_extensions() const { + Vector<String> ret; + ret.push_back("fbx"); + return ret; +} + +bool EditorFileSystemImportFormatSupportQueryFBX::query() { + FBXImporterManager::get_singleton()->show_dialog(true); + + while (true) { + OS::get_singleton()->delay_usec(1); + DisplayServer::get_singleton()->process_events(); + Main::iteration(); + if (!FBXImporterManager::get_singleton()->is_visible()) { + break; + } + } + + return false; +} + #endif // TOOLS_ENABLED diff --git a/modules/gltf/editor/editor_scene_importer_fbx.h b/modules/gltf/editor/editor_scene_importer_fbx.h index 6bf9f3e033..82179cc460 100644 --- a/modules/gltf/editor/editor_scene_importer_fbx.h +++ b/modules/gltf/editor/editor_scene_importer_fbx.h @@ -33,6 +33,8 @@ #ifdef TOOLS_ENABLED +#include "editor/editor_file_system.h" +#include "editor/fbx_importer_manager.h" #include "editor/import/resource_importer_scene.h" class Animation; @@ -53,6 +55,15 @@ public: const HashMap<StringName, Variant> &p_options) override; }; +class EditorFileSystemImportFormatSupportQueryFBX : public EditorFileSystemImportFormatSupportQuery { + GDCLASS(EditorFileSystemImportFormatSupportQueryFBX, EditorFileSystemImportFormatSupportQuery); + +public: + virtual bool is_active() const override; + virtual Vector<String> get_file_extensions() const override; + virtual bool query() override; +}; + #endif // TOOLS_ENABLED #endif // EDITOR_SCENE_IMPORTER_FBX_H diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index f4db576b0c..d858f76b28 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -3384,177 +3384,179 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) { if (!material->get_name().is_empty()) { d["name"] = _gen_unique_name(p_state, material->get_name()); } + Ref<BaseMaterial3D> base_material = material; - if (base_material.is_valid()) { - Dictionary mr; - { - Array arr; - const Color c = base_material->get_albedo().srgb_to_linear(); - arr.push_back(c.r); - arr.push_back(c.g); - arr.push_back(c.b); - arr.push_back(c.a); - mr["baseColorFactor"] = arr; + if (base_material.is_null()) { + materials.push_back(d); + continue; + } + + Dictionary mr; + { + Array arr; + const Color c = base_material->get_albedo().srgb_to_linear(); + arr.push_back(c.r); + arr.push_back(c.g); + arr.push_back(c.b); + arr.push_back(c.a); + mr["baseColorFactor"] = arr; + } + { + Dictionary bct; + Ref<Texture2D> albedo_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO); + GLTFTextureIndex gltf_texture_index = -1; + + if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) { + albedo_texture->set_name(material->get_name() + "_albedo"); + gltf_texture_index = _set_texture(p_state, albedo_texture, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT)); } - { - Dictionary bct; - if (base_material.is_valid()) { - Ref<Texture2D> albedo_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO); - GLTFTextureIndex gltf_texture_index = -1; - - if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) { - albedo_texture->set_name(material->get_name() + "_albedo"); - gltf_texture_index = _set_texture(p_state, albedo_texture, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT)); - } - if (gltf_texture_index != -1) { - bct["index"] = gltf_texture_index; - Dictionary extensions = _serialize_texture_transform_uv1(material); - if (!extensions.is_empty()) { - bct["extensions"] = extensions; - p_state->use_khr_texture_transform = true; - } - mr["baseColorTexture"] = bct; - } + if (gltf_texture_index != -1) { + bct["index"] = gltf_texture_index; + Dictionary extensions = _serialize_texture_transform_uv1(material); + if (!extensions.is_empty()) { + bct["extensions"] = extensions; + p_state->use_khr_texture_transform = true; + } + mr["baseColorTexture"] = bct; + } + } + + mr["metallicFactor"] = base_material->get_metallic(); + mr["roughnessFactor"] = base_material->get_roughness(); + bool has_roughness = base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS).is_valid() && base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS)->get_image().is_valid(); + bool has_ao = base_material->get_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION) && base_material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION).is_valid(); + bool has_metalness = base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC).is_valid() && base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC)->get_image().is_valid(); + if (has_ao || has_roughness || has_metalness) { + Dictionary mrt; + Ref<Texture2D> roughness_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS); + BaseMaterial3D::TextureChannel roughness_channel = base_material->get_roughness_texture_channel(); + Ref<Texture2D> metallic_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC); + BaseMaterial3D::TextureChannel metalness_channel = base_material->get_metallic_texture_channel(); + Ref<Texture2D> ao_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION); + BaseMaterial3D::TextureChannel ao_channel = base_material->get_ao_texture_channel(); + Ref<ImageTexture> orm_texture; + orm_texture.instantiate(); + Ref<Image> orm_image; + orm_image.instantiate(); + int32_t height = 0; + int32_t width = 0; + Ref<Image> ao_image; + if (has_ao) { + height = ao_texture->get_height(); + width = ao_texture->get_width(); + ao_image = ao_texture->get_image(); + Ref<ImageTexture> img_tex = ao_image; + if (img_tex.is_valid()) { + ao_image = img_tex->get_image(); + } + if (ao_image->is_compressed()) { + ao_image->decompress(); + } + } + Ref<Image> roughness_image; + if (has_roughness) { + height = roughness_texture->get_height(); + width = roughness_texture->get_width(); + roughness_image = roughness_texture->get_image(); + Ref<ImageTexture> img_tex = roughness_image; + if (img_tex.is_valid()) { + roughness_image = img_tex->get_image(); + } + if (roughness_image->is_compressed()) { + roughness_image->decompress(); } } - if (base_material.is_valid()) { - mr["metallicFactor"] = base_material->get_metallic(); - mr["roughnessFactor"] = base_material->get_roughness(); - bool has_roughness = base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS).is_valid() && base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS)->get_image().is_valid(); - bool has_ao = base_material->get_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION) && base_material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION).is_valid(); - bool has_metalness = base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC).is_valid() && base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC)->get_image().is_valid(); - if (has_ao || has_roughness || has_metalness) { - Dictionary mrt; - Ref<Texture2D> roughness_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS); - BaseMaterial3D::TextureChannel roughness_channel = base_material->get_roughness_texture_channel(); - Ref<Texture2D> metallic_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC); - BaseMaterial3D::TextureChannel metalness_channel = base_material->get_metallic_texture_channel(); - Ref<Texture2D> ao_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION); - BaseMaterial3D::TextureChannel ao_channel = base_material->get_ao_texture_channel(); - Ref<ImageTexture> orm_texture; - orm_texture.instantiate(); - Ref<Image> orm_image; - orm_image.instantiate(); - int32_t height = 0; - int32_t width = 0; - Ref<Image> ao_image; + Ref<Image> metallness_image; + if (has_metalness) { + height = metallic_texture->get_height(); + width = metallic_texture->get_width(); + metallness_image = metallic_texture->get_image(); + Ref<ImageTexture> img_tex = metallness_image; + if (img_tex.is_valid()) { + metallness_image = img_tex->get_image(); + } + if (metallness_image->is_compressed()) { + metallness_image->decompress(); + } + } + Ref<Texture2D> albedo_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO); + if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) { + height = albedo_texture->get_height(); + width = albedo_texture->get_width(); + } + orm_image->initialize_data(width, height, false, Image::FORMAT_RGBA8); + if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) { + ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS); + } + if (roughness_image.is_valid() && roughness_image->get_size() != Vector2(width, height)) { + roughness_image->resize(width, height, Image::INTERPOLATE_LANCZOS); + } + if (metallness_image.is_valid() && metallness_image->get_size() != Vector2(width, height)) { + metallness_image->resize(width, height, Image::INTERPOLATE_LANCZOS); + } + for (int32_t h = 0; h < height; h++) { + for (int32_t w = 0; w < width; w++) { + Color c = Color(1.0f, 1.0f, 1.0f); if (has_ao) { - height = ao_texture->get_height(); - width = ao_texture->get_width(); - ao_image = ao_texture->get_image(); - Ref<ImageTexture> img_tex = ao_image; - if (img_tex.is_valid()) { - ao_image = img_tex->get_image(); - } - if (ao_image->is_compressed()) { - ao_image->decompress(); + if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == ao_channel) { + c.r = ao_image->get_pixel(w, h).r; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == ao_channel) { + c.r = ao_image->get_pixel(w, h).g; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == ao_channel) { + c.r = ao_image->get_pixel(w, h).b; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == ao_channel) { + c.r = ao_image->get_pixel(w, h).a; } } - Ref<Image> roughness_image; if (has_roughness) { - height = roughness_texture->get_height(); - width = roughness_texture->get_width(); - roughness_image = roughness_texture->get_image(); - Ref<ImageTexture> img_tex = roughness_image; - if (img_tex.is_valid()) { - roughness_image = img_tex->get_image(); - } - if (roughness_image->is_compressed()) { - roughness_image->decompress(); + if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == roughness_channel) { + c.g = roughness_image->get_pixel(w, h).r; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == roughness_channel) { + c.g = roughness_image->get_pixel(w, h).g; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == roughness_channel) { + c.g = roughness_image->get_pixel(w, h).b; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == roughness_channel) { + c.g = roughness_image->get_pixel(w, h).a; } } - Ref<Image> metallness_image; if (has_metalness) { - height = metallic_texture->get_height(); - width = metallic_texture->get_width(); - metallness_image = metallic_texture->get_image(); - Ref<ImageTexture> img_tex = metallness_image; - if (img_tex.is_valid()) { - metallness_image = img_tex->get_image(); - } - if (metallness_image->is_compressed()) { - metallness_image->decompress(); - } - } - Ref<Texture2D> albedo_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO); - if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) { - height = albedo_texture->get_height(); - width = albedo_texture->get_width(); - } - orm_image->initialize_data(width, height, false, Image::FORMAT_RGBA8); - if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) { - ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS); - } - if (roughness_image.is_valid() && roughness_image->get_size() != Vector2(width, height)) { - roughness_image->resize(width, height, Image::INTERPOLATE_LANCZOS); - } - if (metallness_image.is_valid() && metallness_image->get_size() != Vector2(width, height)) { - metallness_image->resize(width, height, Image::INTERPOLATE_LANCZOS); - } - for (int32_t h = 0; h < height; h++) { - for (int32_t w = 0; w < width; w++) { - Color c = Color(1.0f, 1.0f, 1.0f); - if (has_ao) { - if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == ao_channel) { - c.r = ao_image->get_pixel(w, h).r; - } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == ao_channel) { - c.r = ao_image->get_pixel(w, h).g; - } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == ao_channel) { - c.r = ao_image->get_pixel(w, h).b; - } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == ao_channel) { - c.r = ao_image->get_pixel(w, h).a; - } - } - if (has_roughness) { - if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == roughness_channel) { - c.g = roughness_image->get_pixel(w, h).r; - } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == roughness_channel) { - c.g = roughness_image->get_pixel(w, h).g; - } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == roughness_channel) { - c.g = roughness_image->get_pixel(w, h).b; - } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == roughness_channel) { - c.g = roughness_image->get_pixel(w, h).a; - } - } - if (has_metalness) { - if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == metalness_channel) { - c.b = metallness_image->get_pixel(w, h).r; - } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == metalness_channel) { - c.b = metallness_image->get_pixel(w, h).g; - } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == metalness_channel) { - c.b = metallness_image->get_pixel(w, h).b; - } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == metalness_channel) { - c.b = metallness_image->get_pixel(w, h).a; - } - } - orm_image->set_pixel(w, h, c); - } - } - orm_image->generate_mipmaps(); - orm_texture->set_image(orm_image); - GLTFTextureIndex orm_texture_index = -1; - if (has_ao || has_roughness || has_metalness) { - orm_texture->set_name(material->get_name() + "_orm"); - orm_texture_index = _set_texture(p_state, orm_texture, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT)); - } - if (has_ao) { - Dictionary occt; - occt["index"] = orm_texture_index; - d["occlusionTexture"] = occt; - } - if (has_roughness || has_metalness) { - mrt["index"] = orm_texture_index; - Dictionary extensions = _serialize_texture_transform_uv1(material); - if (!extensions.is_empty()) { - mrt["extensions"] = extensions; - p_state->use_khr_texture_transform = true; + if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == metalness_channel) { + c.b = metallness_image->get_pixel(w, h).r; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == metalness_channel) { + c.b = metallness_image->get_pixel(w, h).g; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == metalness_channel) { + c.b = metallness_image->get_pixel(w, h).b; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == metalness_channel) { + c.b = metallness_image->get_pixel(w, h).a; } - mr["metallicRoughnessTexture"] = mrt; } + orm_image->set_pixel(w, h, c); } } - d["pbrMetallicRoughness"] = mr; + orm_image->generate_mipmaps(); + orm_texture->set_image(orm_image); + GLTFTextureIndex orm_texture_index = -1; + if (has_ao || has_roughness || has_metalness) { + orm_texture->set_name(material->get_name() + "_orm"); + orm_texture_index = _set_texture(p_state, orm_texture, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT)); + } + if (has_ao) { + Dictionary occt; + occt["index"] = orm_texture_index; + d["occlusionTexture"] = occt; + } + if (has_roughness || has_metalness) { + mrt["index"] = orm_texture_index; + Dictionary extensions = _serialize_texture_transform_uv1(material); + if (!extensions.is_empty()) { + mrt["extensions"] = extensions; + p_state->use_khr_texture_transform = true; + } + mr["metallicRoughnessTexture"] = mrt; + } } + + d["pbrMetallicRoughness"] = mr; if (base_material->get_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING)) { Dictionary nt; Ref<ImageTexture> tex; @@ -3607,6 +3609,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) { arr.push_back(c.b); d["emissiveFactor"] = arr; } + if (base_material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) { Dictionary et; Ref<Texture2D> emission_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_EMISSION); @@ -3621,16 +3624,19 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) { d["emissiveTexture"] = et; } } + const bool ds = base_material->get_cull_mode() == BaseMaterial3D::CULL_DISABLED; if (ds) { d["doubleSided"] = ds; } + if (base_material->get_transparency() == BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR) { d["alphaMode"] = "MASK"; d["alphaCutoff"] = base_material->get_alpha_scissor_threshold(); } else if (base_material->get_transparency() != BaseMaterial3D::TRANSPARENCY_DISABLED) { d["alphaMode"] = "BLEND"; } + materials.push_back(d); } if (!materials.size()) { diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index cd7a23fbb2..2322e13ae2 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -80,16 +80,13 @@ static void _editor_init() { EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "filesystem/import/fbx/fbx2gltf_path", PROPERTY_HINT_GLOBAL_FILE)); if (fbx_enabled) { - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (fbx2gltf_path.is_empty()) { - WARN_PRINT("FBX file import is enabled in the project settings, but no FBX2glTF path is configured in the editor settings. FBX files will not be imported."); - } else if (!da->file_exists(fbx2gltf_path)) { - WARN_PRINT("FBX file import is enabled, but the FBX2glTF path doesn't point to an accessible file. FBX files will not be imported."); - } else { - Ref<EditorSceneFormatImporterFBX> importer; - importer.instantiate(); - ResourceImporterScene::add_importer(importer); - } + Ref<EditorSceneFormatImporterFBX> importer; + importer.instantiate(); + ResourceImporterScene::get_scene_singleton()->add_importer(importer); + + Ref<EditorFileSystemImportFormatSupportQueryFBX> fbx_import_query; + fbx_import_query.instantiate(); + EditorFileSystem::get_singleton()->add_import_format_support_query(fbx_import_query); } } #endif // TOOLS_ENABLED diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index bd5c938364..e0fc4e2697 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -89,13 +89,6 @@ Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in local space. </description> </method> - <method name="get_navigation_layer_value" qualifiers="const"> - <return type="bool" /> - <param index="0" name="layer_number" type="int" /> - <description> - Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32. - </description> - </method> <method name="get_navigation_map" qualifiers="const"> <return type="RID" /> <description> @@ -179,14 +172,6 @@ Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_navigation_layer_value"> - <return type="void" /> - <param index="0" name="layer_number" type="int" /> - <param index="1" name="value" type="bool" /> - <description> - Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32. - </description> - </method> <method name="set_navigation_map"> <return type="void" /> <param index="0" name="navigation_map" type="RID" /> @@ -197,7 +182,7 @@ </methods> <members> <member name="bake_navigation" type="bool" setter="set_bake_navigation" getter="is_baking_navigation" default="false"> - If [code]true[/code], this GridMap bakes a navigation region. + If [code]true[/code], this GridMap creates a navigation region for each cell that uses a [member mesh_library] item with a navigation mesh. The created navigation region will use the navigation layers bitmask assigned to the [MeshLibrary]'s item. </member> <member name="cell_center_x" type="bool" setter="set_center_x" getter="get_center_x" default="true"> If [code]true[/code], grid items are centered on the X axis. @@ -226,12 +211,12 @@ <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1"> The physics layers this GridMap detects collisions in. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. </member> + <member name="collision_priority" type="float" setter="set_collision_priority" getter="get_collision_priority" default="1.0"> + The priority used to solve colliding when occurring penetration. The higher the priority is, the lower the penetration into the object will be. This can for example be used to prevent the player from breaking through the boundaries of a level. + </member> <member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library"> The assigned [MeshLibrary]. </member> - <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> - A bitmask determining all navigation layers the GridMap generated navigation regions belong to. These navigation layers can be checked upon when requesting a path with [method NavigationServer3D.map_get_path]. - </member> <member name="physics_material" type="PhysicsMaterial" setter="set_physics_material" getter="get_physics_material"> Overrides the default friction and bounce physics properties for the whole [GridMap]. </member> diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 06ad806afc..e396d77d86 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -74,7 +74,7 @@ bool GridMap::_set(const StringName &p_name, const Variant &p_value) { bm.mesh = meshes[i]; ERR_CONTINUE(!bm.mesh.is_valid()); bm.instance = RS::get_singleton()->instance_create(); - RS::get_singleton()->get_singleton()->instance_set_base(bm.instance, bm.mesh->get_rid()); + RS::get_singleton()->instance_set_base(bm.instance, bm.mesh->get_rid()); RS::get_singleton()->instance_attach_object_instance_id(bm.instance, get_instance_id()); if (is_inside_tree()) { RS::get_singleton()->instance_set_scenario(bm.instance, get_world_3d()->get_scenario()); @@ -138,7 +138,7 @@ void GridMap::_get_property_list(List<PropertyInfo> *p_list) const { void GridMap::set_collision_layer(uint32_t p_layer) { collision_layer = p_layer; - _reset_physic_bodies_collision_filters(); + _update_physics_bodies_collision_properties(); } uint32_t GridMap::get_collision_layer() const { @@ -147,7 +147,7 @@ uint32_t GridMap::get_collision_layer() const { void GridMap::set_collision_mask(uint32_t p_mask) { collision_mask = p_mask; - _reset_physic_bodies_collision_filters(); + _update_physics_bodies_collision_properties(); } uint32_t GridMap::get_collision_mask() const { @@ -184,6 +184,15 @@ void GridMap::set_collision_mask_value(int p_layer_number, bool p_value) { set_collision_mask(mask); } +void GridMap::set_collision_priority(real_t p_priority) { + collision_priority = p_priority; + _update_physics_bodies_collision_properties(); +} + +real_t GridMap::get_collision_priority() const { + return collision_priority; +} + void GridMap::set_physics_material(Ref<PhysicsMaterial> p_material) { physics_material = p_material; _recreate_octant_data(); @@ -230,7 +239,7 @@ void GridMap::set_navigation_map(RID p_navigation_map) { map_override = p_navigation_map; for (const KeyValue<OctantKey, Octant *> &E : octant_map) { Octant &g = *octant_map[E.key]; - for (KeyValue<IndexKey, Octant::NavMesh> &F : g.navmesh_ids) { + for (KeyValue<IndexKey, Octant::NavigationCell> &F : g.navigation_cell_ids) { if (F.value.region.is_valid()) { NavigationServer3D::get_singleton()->region_set_map(F.value.region, map_override); } @@ -247,33 +256,6 @@ RID GridMap::get_navigation_map() const { return RID(); } -void GridMap::set_navigation_layers(uint32_t p_navigation_layers) { - navigation_layers = p_navigation_layers; - _recreate_octant_data(); -} - -uint32_t GridMap::get_navigation_layers() const { - return navigation_layers; -} - -void GridMap::set_navigation_layer_value(int p_layer_number, bool p_value) { - ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); - ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); - uint32_t _navigation_layers = get_navigation_layers(); - if (p_value) { - _navigation_layers |= 1 << (p_layer_number - 1); - } else { - _navigation_layers &= ~(1 << (p_layer_number - 1)); - } - set_navigation_layers(_navigation_layers); -} - -bool GridMap::get_navigation_layer_value(int p_layer_number) const { - ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); - ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); - return get_navigation_layers() & (1 << (p_layer_number - 1)); -} - void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) { if (!mesh_library.is_null()) { mesh_library->unregister_owner(this); @@ -385,6 +367,7 @@ void GridMap::set_cell_item(const Vector3i &p_position, int p_item, int p_rot) { PhysicsServer3D::get_singleton()->body_attach_object_instance_id(g->static_body, get_instance_id()); PhysicsServer3D::get_singleton()->body_set_collision_layer(g->static_body, collision_layer); PhysicsServer3D::get_singleton()->body_set_collision_mask(g->static_body, collision_mask); + PhysicsServer3D::get_singleton()->body_set_collision_priority(g->static_body, collision_priority); if (physics_material.is_valid()) { PhysicsServer3D::get_singleton()->body_set_param(g->static_body, PhysicsServer3D::BODY_PARAM_FRICTION, physics_material->get_friction()); PhysicsServer3D::get_singleton()->body_set_param(g->static_body, PhysicsServer3D::BODY_PARAM_BOUNCE, physics_material->get_bounce()); @@ -542,13 +525,13 @@ void GridMap::_octant_transform(const OctantKey &p_key) { } // update transform for NavigationServer regions and navigation debugmesh instances - for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) { + for (const KeyValue<IndexKey, Octant::NavigationCell> &E : g.navigation_cell_ids) { if (bake_navigation) { if (E.value.region.is_valid()) { NavigationServer3D::get_singleton()->region_set_transform(E.value.region, get_global_transform() * E.value.xform); } - if (E.value.navmesh_debug_instance.is_valid()) { - RS::get_singleton()->instance_set_transform(E.value.navmesh_debug_instance, get_global_transform() * E.value.xform); + if (E.value.navigation_mesh_debug_instance.is_valid()) { + RS::get_singleton()->instance_set_transform(E.value.navigation_mesh_debug_instance, get_global_transform() * E.value.xform); } } } @@ -574,13 +557,13 @@ bool GridMap::_octant_update(const OctantKey &p_key) { } //erase navigation - for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) { + for (const KeyValue<IndexKey, Octant::NavigationCell> &E : g.navigation_cell_ids) { NavigationServer3D::get_singleton()->free(E.value.region); - if (E.value.navmesh_debug_instance.is_valid()) { - RS::get_singleton()->free(E.value.navmesh_debug_instance); + if (E.value.navigation_mesh_debug_instance.is_valid()) { + RS::get_singleton()->free(E.value.navigation_mesh_debug_instance); } } - g.navmesh_ids.clear(); + g.navigation_cell_ids.clear(); //erase multimeshes @@ -648,17 +631,18 @@ bool GridMap::_octant_update(const OctantKey &p_key) { } } - // add the item's navmesh at given xform to GridMap's Navigation ancestor - Ref<NavigationMesh> navmesh = mesh_library->get_item_navmesh(c.item); - if (navmesh.is_valid()) { - Octant::NavMesh nm; - nm.xform = xform * mesh_library->get_item_navmesh_transform(c.item); + // add the item's navigation_mesh at given xform to GridMap's Navigation ancestor + Ref<NavigationMesh> navigation_mesh = mesh_library->get_item_navigation_mesh(c.item); + if (navigation_mesh.is_valid()) { + Octant::NavigationCell nm; + nm.xform = xform * mesh_library->get_item_navigation_mesh_transform(c.item); + nm.navigation_layers = mesh_library->get_item_navigation_layers(c.item); if (bake_navigation) { RID region = NavigationServer3D::get_singleton()->region_create(); NavigationServer3D::get_singleton()->region_set_owner_id(region, get_instance_id()); - NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers); - NavigationServer3D::get_singleton()->region_set_navmesh(region, navmesh); + NavigationServer3D::get_singleton()->region_set_navigation_layers(region, nm.navigation_layers); + NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, navigation_mesh); NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * nm.xform); if (is_inside_tree()) { if (map_override.is_valid()) { @@ -673,19 +657,19 @@ bool GridMap::_octant_update(const OctantKey &p_key) { // add navigation debugmesh visual instances if debug is enabled SceneTree *st = SceneTree::get_singleton(); if (st && st->is_debugging_navigation_hint()) { - if (!nm.navmesh_debug_instance.is_valid()) { - RID navmesh_debug_rid = navmesh->get_debug_mesh()->get_rid(); - nm.navmesh_debug_instance = RS::get_singleton()->instance_create(); - RS::get_singleton()->instance_set_base(nm.navmesh_debug_instance, navmesh_debug_rid); + if (!nm.navigation_mesh_debug_instance.is_valid()) { + RID navigation_mesh_debug_rid = navigation_mesh->get_debug_mesh()->get_rid(); + nm.navigation_mesh_debug_instance = RS::get_singleton()->instance_create(); + RS::get_singleton()->instance_set_base(nm.navigation_mesh_debug_instance, navigation_mesh_debug_rid); } if (is_inside_tree()) { - RS::get_singleton()->instance_set_scenario(nm.navmesh_debug_instance, get_world_3d()->get_scenario()); - RS::get_singleton()->instance_set_transform(nm.navmesh_debug_instance, get_global_transform() * nm.xform); + RS::get_singleton()->instance_set_scenario(nm.navigation_mesh_debug_instance, get_world_3d()->get_scenario()); + RS::get_singleton()->instance_set_transform(nm.navigation_mesh_debug_instance, get_global_transform() * nm.xform); } } #endif // DEBUG_ENABLED } - g.navmesh_ids[E] = nm; + g.navigation_cell_ids[E] = nm; } } @@ -751,10 +735,11 @@ bool GridMap::_octant_update(const OctantKey &p_key) { return false; } -void GridMap::_reset_physic_bodies_collision_filters() { +void GridMap::_update_physics_bodies_collision_properties() { for (const KeyValue<OctantKey, Octant *> &E : octant_map) { PhysicsServer3D::get_singleton()->body_set_collision_layer(E.value->static_body, collision_layer); PhysicsServer3D::get_singleton()->body_set_collision_mask(E.value->static_body, collision_mask); + PhysicsServer3D::get_singleton()->body_set_collision_priority(E.value->static_body, collision_priority); } } @@ -775,14 +760,14 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) { } if (bake_navigation && mesh_library.is_valid()) { - for (KeyValue<IndexKey, Octant::NavMesh> &F : g.navmesh_ids) { + for (KeyValue<IndexKey, Octant::NavigationCell> &F : g.navigation_cell_ids) { if (cell_map.has(F.key) && F.value.region.is_valid() == false) { - Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[F.key].item); - if (nm.is_valid()) { + Ref<NavigationMesh> navigation_mesh = mesh_library->get_item_navigation_mesh(cell_map[F.key].item); + if (navigation_mesh.is_valid()) { RID region = NavigationServer3D::get_singleton()->region_create(); NavigationServer3D::get_singleton()->region_set_owner_id(region, get_instance_id()); - NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers); - NavigationServer3D::get_singleton()->region_set_navmesh(region, nm); + NavigationServer3D::get_singleton()->region_set_navigation_layers(region, F.value.navigation_layers); + NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, navigation_mesh); NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * F.value.xform); if (map_override.is_valid()) { NavigationServer3D::get_singleton()->region_set_map(region, map_override); @@ -824,14 +809,14 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) { RS::get_singleton()->instance_set_scenario(g.multimesh_instances[i].instance, RID()); } - for (KeyValue<IndexKey, Octant::NavMesh> &F : g.navmesh_ids) { + for (KeyValue<IndexKey, Octant::NavigationCell> &F : g.navigation_cell_ids) { if (F.value.region.is_valid()) { NavigationServer3D::get_singleton()->free(F.value.region); F.value.region = RID(); } - if (F.value.navmesh_debug_instance.is_valid()) { - RS::get_singleton()->free(F.value.navmesh_debug_instance); - F.value.navmesh_debug_instance = RID(); + if (F.value.navigation_mesh_debug_instance.is_valid()) { + RS::get_singleton()->free(F.value.navigation_mesh_debug_instance); + F.value.navigation_mesh_debug_instance = RID(); } } @@ -862,15 +847,15 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) { PhysicsServer3D::get_singleton()->free(g.static_body); // Erase navigation - for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) { + for (const KeyValue<IndexKey, Octant::NavigationCell> &E : g.navigation_cell_ids) { if (E.value.region.is_valid()) { NavigationServer3D::get_singleton()->free(E.value.region); } - if (E.value.navmesh_debug_instance.is_valid()) { - RS::get_singleton()->free(E.value.navmesh_debug_instance); + if (E.value.navigation_mesh_debug_instance.is_valid()) { + RS::get_singleton()->free(E.value.navigation_mesh_debug_instance); } } - g.navmesh_ids.clear(); + g.navigation_cell_ids.clear(); #ifdef DEBUG_ENABLED if (bake_navigation) { @@ -1047,6 +1032,9 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &GridMap::set_collision_layer_value); ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &GridMap::get_collision_layer_value); + ClassDB::bind_method(D_METHOD("set_collision_priority", "priority"), &GridMap::set_collision_priority); + ClassDB::bind_method(D_METHOD("get_collision_priority"), &GridMap::get_collision_priority); + ClassDB::bind_method(D_METHOD("set_physics_material", "material"), &GridMap::set_physics_material); ClassDB::bind_method(D_METHOD("get_physics_material"), &GridMap::get_physics_material); @@ -1056,12 +1044,6 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &GridMap::set_navigation_map); ClassDB::bind_method(D_METHOD("get_navigation_map"), &GridMap::get_navigation_map); - ClassDB::bind_method(D_METHOD("set_navigation_layers", "layers"), &GridMap::set_navigation_layers); - ClassDB::bind_method(D_METHOD("get_navigation_layers"), &GridMap::get_navigation_layers); - - ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &GridMap::set_navigation_layer_value); - ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &GridMap::get_navigation_layer_value); - ClassDB::bind_method(D_METHOD("set_mesh_library", "mesh_library"), &GridMap::set_mesh_library); ClassDB::bind_method(D_METHOD("get_mesh_library"), &GridMap::get_mesh_library); @@ -1118,9 +1100,9 @@ void GridMap::_bind_methods() { ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_priority"), "set_collision_priority", "get_collision_priority"); ADD_GROUP("Navigation", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bake_navigation"), "set_bake_navigation", "is_baking_navigation"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); BIND_CONSTANT(INVALID_CELL_ITEM); @@ -1281,7 +1263,7 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe BakedMesh bm; bm.mesh = mesh; bm.instance = RS::get_singleton()->instance_create(); - RS::get_singleton()->get_singleton()->instance_set_base(bm.instance, bm.mesh->get_rid()); + RS::get_singleton()->instance_set_base(bm.instance, bm.mesh->get_rid()); RS::get_singleton()->instance_attach_object_instance_id(bm.instance, get_instance_id()); if (is_inside_tree()) { RS::get_singleton()->instance_set_scenario(bm.instance, get_world_3d()->get_scenario()); @@ -1390,7 +1372,7 @@ void GridMap::_update_octant_navigation_debug_edge_connections_mesh(const Octant Vector<Vector3> vertex_array; - for (KeyValue<IndexKey, Octant::NavMesh> &F : g.navmesh_ids) { + for (KeyValue<IndexKey, Octant::NavigationCell> &F : g.navigation_cell_ids) { if (cell_map.has(F.key) && F.value.region.is_valid()) { int connections_count = NavigationServer3D::get_singleton()->region_get_connections_count(F.value.region); if (connections_count == 0) { diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 6a53457d25..2a9f34bdda 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -95,10 +95,11 @@ class GridMap : public Node3D { * A GridMap can have multiple Octants. */ struct Octant { - struct NavMesh { + struct NavigationCell { RID region; Transform3D xform; - RID navmesh_debug_instance; + RID navigation_mesh_debug_instance; + uint32_t navigation_layers = 1; }; struct MultimeshInstance { @@ -124,7 +125,7 @@ class GridMap : public Node3D { bool dirty = false; RID static_body; - HashMap<IndexKey, NavMesh> navmesh_ids; + HashMap<IndexKey, NavigationCell> navigation_cell_ids; }; union OctantKey { @@ -150,6 +151,7 @@ class GridMap : public Node3D { uint32_t collision_layer = 1; uint32_t collision_mask = 1; + real_t collision_priority = 1.0; Ref<PhysicsMaterial> physics_material; bool bake_navigation = false; RID map_override; @@ -185,7 +187,7 @@ class GridMap : public Node3D { return Vector3(p_key.x, p_key.y, p_key.z) * cell_size * octant_size; } - void _reset_physic_bodies_collision_filters(); + void _update_physics_bodies_collision_properties(); void _octant_enter_world(const OctantKey &p_key); void _octant_exit_world(const OctantKey &p_key); bool _octant_update(const OctantKey &p_key); @@ -240,6 +242,9 @@ public: void set_collision_mask_value(int p_layer_number, bool p_value); bool get_collision_mask_value(int p_layer_number) const; + void set_collision_priority(real_t p_priority); + real_t get_collision_priority() const; + void set_physics_material(Ref<PhysicsMaterial> p_material); Ref<PhysicsMaterial> get_physics_material() const; @@ -251,12 +256,6 @@ public: void set_navigation_map(RID p_navigation_map); RID get_navigation_map() const; - void set_navigation_layers(uint32_t p_navigation_layers); - uint32_t get_navigation_layers() const; - - void set_navigation_layer_value(int p_layer_number, bool p_value); - bool get_navigation_layer_value(int p_layer_number) const; - void set_mesh_library(const Ref<MeshLibrary> &p_mesh_library); Ref<MeshLibrary> get_mesh_library() const; diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index d0f52488bb..eca53c4831 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -2292,7 +2292,7 @@ bool CSharpScript::can_instantiate() const { // For tool scripts, this will never fire if the class is not found. That's because we // don't know if it's a tool script if we can't find the class to access the attributes. if (extra_cond && !valid) { - ERR_FAIL_V_MSG(false, "Cannot instance script because the associated class could not be found. Script: '" + get_path() + "'."); + ERR_FAIL_V_MSG(false, "Cannot instance script because the associated class could not be found. Script: '" + get_path() + "'. Make sure the script exists and contains a class definition with a name that matches the filename of the script exactly (it's case-sensitive)."); } return valid && extra_cond; diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs index 0c0feb3901..5afaeb736f 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs @@ -91,7 +91,7 @@ namespace Godot.SourceGenerators.Sample } } - // Lamda Property + // Lambda Property private String _lamdaProperty_String = "LamdaProperty_String"; [Export] public String LamdaProperty_String diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs index 88c0e71155..7f1231dbcb 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs @@ -114,22 +114,21 @@ namespace Godot.SourceGenerators RestartIfChanged = 2048, ScriptVariable = 4096, StoreIfNull = 8192, - AnimateAsTrigger = 16384, - UpdateAllIfModified = 32768, - ScriptDefaultValue = 65536, - ClassIsEnum = 131072, - NilIsVariant = 262144, - Internal = 524288, - DoNotShareOnDuplicate = 1048576, - HighEndGfx = 2097152, - NodePathFromSceneRoot = 4194304, - ResourceNotPersistent = 8388608, - KeyingIncrements = 16777216, - DeferredSetResource = 33554432, - EditorInstantiateObject = 67108864, - EditorBasicSetting = 134217728, - ReadOnly = 268435456, - Array = 536870912, + UpdateAllIfModified = 16384, + ScriptDefaultValue = 32768, + ClassIsEnum = 65536, + NilIsVariant = 131072, + Internal = 262144, + DoNotShareOnDuplicate = 524288, + HighEndGfx = 1048576, + NodePathFromSceneRoot = 2097152, + ResourceNotPersistent = 4194304, + KeyingIncrements = 8388608, + DeferredSetResource = 16777216, + EditorInstantiateObject = 33554432, + EditorBasicSetting = 67108864, + ReadOnly = 134217728, + Array = 268435456, Default = 6, DefaultIntl = 38, NoEditor = 2 diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 7ef42247d1..6559cbf75d 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -2489,9 +2489,12 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall, if (!ret_void) { if (return_type->cname != name_cache.type_Variant) { + // Usually the return value takes ownership, but in this case the variant is only used + // for conversion to another return type. As such, the local variable takes ownership. r_output << "using godot_variant " << C_LOCAL_VARARG_RET " = "; } else { - r_output << "using godot_variant " << C_LOCAL_RET " = "; + // Variant's [c_out] takes ownership of the variant value + r_output << "godot_variant " << C_LOCAL_RET " = "; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index f49023555b..4075a878d2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using Godot.NativeInterop; namespace Godot { @@ -187,6 +188,19 @@ namespace Godot } /// <summary> + /// Returns the light intensity of the color, as a value between 0.0 and 1.0 (inclusive). + /// This is useful when determining light or dark color. Colors with a luminance smaller + /// than 0.5 can be generally considered dark. + /// Note: <see cref="Luminance"/> relies on the color being in the linear color space to + /// return an accurate relative luminance value. If the color is in the sRGB color space + /// use <see cref="SrgbToLinear"/> to convert it to the linear color space first. + /// </summary> + public readonly float Luminance + { + get { return 0.2126f * r + 0.7152f * g + 0.0722f * b; } + } + + /// <summary> /// Access color components using their index. /// </summary> /// <value> @@ -363,6 +377,35 @@ namespace Godot } /// <summary> + /// Returns the color converted to the sRGB color space. + /// This method assumes the original color is in the linear color space. + /// See also <see cref="SrgbToLinear"/> which performs the opposite operation. + /// </summary> + /// <returns>The sRGB color.</returns> + public readonly Color LinearToSrgb() + { + return new Color( + r < 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * (float)Mathf.Pow(r, 1.0f / 2.4f) - 0.055f, + g < 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * (float)Mathf.Pow(g, 1.0f / 2.4f) - 0.055f, + b < 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * (float)Mathf.Pow(b, 1.0f / 2.4f) - 0.055f, a); + } + + /// <summary> + /// Returns the color converted to linear color space. + /// This method assumes the original color already is in sRGB color space. + /// See also <see cref="LinearToSrgb"/> which performs the opposite operation. + /// </summary> + /// <returns>The color in linear color space.</returns> + public readonly Color SrgbToLinear() + { + return new Color( + r < 0.04045f ? r * (1.0f / 12.92f) : (float)Mathf.Pow((r + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f), + g < 0.04045f ? g * (1.0f / 12.92f) : (float)Mathf.Pow((g + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f), + b < 0.04045f ? b * (1.0f / 12.92f) : (float)Mathf.Pow((b + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f), + a); + } + + /// <summary> /// Returns the color converted to an unsigned 32-bit integer in ABGR /// format (each byte represents a color channel). /// ABGR is the reversed version of the default format. @@ -565,6 +608,10 @@ namespace Godot /// <see cref="Colors"/> constants. /// </summary> /// <param name="code">The HTML color code or color name to construct from.</param> + /// <exception cref="ArgumentOutOfRangeException"> + /// A color cannot be inferred from the given <paramref name="code"/>. + /// It was invalid HTML and a color with that name was not found. + /// </exception> public Color(string code) { if (HtmlIsValid(code)) @@ -597,7 +644,7 @@ namespace Godot /// <exception name="ArgumentOutOfRangeException"> /// <paramref name="rgba"/> color code is invalid. /// </exception> - private static Color FromHTML(ReadOnlySpan<char> rgba) + public static Color FromHTML(ReadOnlySpan<char> rgba) { Color c; if (rgba.Length == 0) @@ -705,28 +752,59 @@ namespace Godot /// the constants defined in <see cref="Colors"/>. /// </summary> /// <param name="name">The name of the color.</param> + /// <exception cref="ArgumentOutOfRangeException"> + /// A color with the given name is not found. + /// </exception> /// <returns>The constructed color.</returns> private static Color Named(string name) { + if (!FindNamedColor(name, out Color color)) + { + throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}"); + } + + return color; + } + + /// <summary> + /// Returns a color according to the standardized name, with the + /// specified alpha value. Supported color names are the same as + /// the constants defined in <see cref="Colors"/>. + /// If a color with the given name is not found, it returns + /// <paramref name="default"/>. + /// </summary> + /// <param name="name">The name of the color.</param> + /// <param name="default"> + /// The default color to return when a color with the given name + /// is not found. + /// </param> + /// <returns>The constructed color.</returns> + private static Color Named(string name, Color @default) + { + if (!FindNamedColor(name, out Color color)) + { + return @default; + } + + return color; + } + + private static bool FindNamedColor(string name, out Color color) + { name = name.Replace(" ", string.Empty); name = name.Replace("-", string.Empty); name = name.Replace("_", string.Empty); name = name.Replace("'", string.Empty); name = name.Replace(".", string.Empty); - name = name.ToUpper(); - - if (!Colors.namedColors.ContainsKey(name)) - { - throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}"); - } + name = name.ToUpperInvariant(); - return Colors.namedColors[name]; + return Colors.namedColors.TryGetValue(name, out color); } /// <summary> - /// Constructs a color from an HSV profile, with values on the - /// range of 0 to 1. This is equivalent to using each of - /// the <c>h</c>/<c>s</c>/<c>v</c> properties, but much more efficient. + /// Constructs a color from an HSV profile. The <paramref name="hue"/>, + /// <paramref name="saturation"/>, and <paramref name="value"/> are typically + /// between 0.0 and 1.0. /// </summary> /// <param name="hue">The HSV hue, typically on the range of 0 to 1.</param> /// <param name="saturation">The HSV saturation, typically on the range of 0 to 1.</param> @@ -841,13 +919,78 @@ namespace Godot return ParseCol4(str, index) * 16 + ParseCol4(str, index + 1); } + /// <summary> + /// Constructs a color from an OK HSL profile. The <paramref name="hue"/>, + /// <paramref name="saturation"/>, and <paramref name="lightness"/> are typically + /// between 0.0 and 1.0. + /// </summary> + /// <param name="hue">The OK HSL hue, typically on the range of 0 to 1.</param> + /// <param name="saturation">The OK HSL saturation, typically on the range of 0 to 1.</param> + /// <param name="lightness">The OK HSL lightness, typically on the range of 0 to 1.</param> + /// <param name="alpha">The alpha (transparency) value, typically on the range of 0 to 1.</param> + /// <returns>The constructed color.</returns> + public static Color FromOkHsl(float hue, float saturation, float lightness, float alpha = 1.0f) + { + return NativeFuncs.godotsharp_color_from_ok_hsl(hue, saturation, lightness, alpha); + } + + /// <summary> + /// Encodes a <see cref="Color"/> from a RGBE9995 format integer. + /// See <see cref="Image.Format.Rgbe9995"/>. + /// </summary> + /// <param name="rgbe">The RGBE9995 encoded color.</param> + /// <returns>The constructed color.</returns> + public static Color FromRgbe9995(uint rgbe) + { + float r = rgbe & 0x1ff; + float g = (rgbe >> 9) & 0x1ff; + float b = (rgbe >> 18) & 0x1ff; + float e = rgbe >> 27; + float m = (float)Mathf.Pow(2.0f, e - 15.0f - 9.0f); + + float rd = r * m; + float gd = g * m; + float bd = b * m; + + return new Color(rd, gd, bd, 1.0f); + } + + /// <summary> + /// Constructs a color from the given string, which can be either an HTML color + /// code or a named color. Returns <paramref name="default"/> if the color cannot + /// be inferred from the string. Supported color names are the same as the + /// <see cref="Colors"/> constants. + /// </summary> + /// <param name="str">The HTML color code or color name.</param> + /// <param name="default">The fallback color to return if the color cannot be inferred.</param> + /// <returns>The constructed color.</returns> + public static Color FromString(string str, Color @default) + { + if (HtmlIsValid(str)) + { + return FromHTML(str); + } + else + { + return Named(str, @default); + } + } + private static string ToHex32(float val) { byte b = (byte)Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255)); return b.HexEncode(); } - internal static bool HtmlIsValid(ReadOnlySpan<char> color) + /// <summary> + /// Returns <see langword="true"/> if <paramref name="color"/> is a valid HTML hexadecimal + /// color string. The string must be a hexadecimal value (case-insensitive) of either 3, + /// 4, 6 or 8 digits, and may be prefixed by a hash sign (<c>#</c>). This method is + /// identical to <see cref="StringExtensions.IsValidHtmlColor(string)"/>. + /// </summary> + /// <param name="color">The HTML hexadecimal color string.</param> + /// <returns>Whether or not the string was a valid HTML hexadecimal color string.</returns> + public static bool HtmlIsValid(ReadOnlySpan<char> color) { if (color.IsEmpty) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index c7deb6423b..57488bd586 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -153,6 +153,8 @@ namespace Godot.NativeInterop internal static partial void godotsharp_callable_call_deferred(in godot_callable p_callable, godot_variant** p_args, int p_arg_count); + internal static partial Color godotsharp_color_from_ok_hsl(float p_h, float p_s, float p_l, float p_alpha); + // GDNative functions // gdnative.h diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs index 9a3ed85e66..ebfc713ee4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs @@ -92,6 +92,9 @@ public partial class VariantUtils if (typeof(T) == typeof(Transform2D)) return CreateFromTransform2D(UnsafeAs<Transform2D>(from)); + if (typeof(T) == typeof(Projection)) + return CreateFromProjection(UnsafeAs<Projection>(from)); + if (typeof(T) == typeof(Vector3)) return CreateFromVector3(UnsafeAs<Vector3>(from)); @@ -293,6 +296,9 @@ public partial class VariantUtils if (typeof(T) == typeof(Transform3D)) return UnsafeAsT(ConvertToTransform3D(variant)); + if (typeof(T) == typeof(Projection)) + return UnsafeAsT(ConvertToProjection(variant)); + if (typeof(T) == typeof(Vector4)) return UnsafeAsT(ConvertToVector4(variant)); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs index 8b1b73fcc3..fd37f8d9e8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs @@ -652,7 +652,7 @@ namespace Godot /// added to the first and second values of the final column respectively. /// </summary> /// <param name="offset">The offset to apply to the projection.</param> - /// <returns>The offseted projection.</returns> + /// <returns>The offsetted projection.</returns> public readonly Projection JitterOffseted(Vector2 offset) { Projection proj = this; diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index 338e5a0147..a3280a0739 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -514,6 +514,13 @@ void godotsharp_callable_call_deferred(Callable *p_callable, const Variant **p_a p_callable->call_deferredp(p_args, p_arg_count); } +godot_color godotsharp_color_from_ok_hsl(float p_h, float p_s, float p_l, float p_alpha) { + godot_color ret; + Color *dest = (Color *)&ret; + memnew_placement(dest, Color(Color::from_ok_hsl(p_h, p_s, p_l, p_alpha))); + return ret; +} + // GDNative functions // gdnative.h @@ -1345,6 +1352,7 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_callable_get_data_for_marshalling, (void *)godotsharp_callable_call, (void *)godotsharp_callable_call_deferred, + (void *)godotsharp_color_from_ok_hsl, (void *)godotsharp_method_bind_ptrcall, (void *)godotsharp_method_bind_call, (void *)godotsharp_variant_new_string_name, diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index e5949c935b..58a0982425 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -36,6 +36,8 @@ #include "navigation_mesh_generator.h" #endif +using namespace NavigationUtilities; + /// Creates a struct for each function and a function that once called creates /// an instance of that struct with the submitted parameters. /// Then, that struct is stored in an array; the `sync` function consume that array. @@ -228,7 +230,7 @@ Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, const NavMap *map = map_owner.get_or_null(p_map); ERR_FAIL_COND_V(map == nullptr, Vector<Vector3>()); - return map->get_path(p_origin, p_destination, p_optimize, p_navigation_layers); + return map->get_path(p_origin, p_destination, p_optimize, p_navigation_layers, nullptr, nullptr, nullptr); } Vector3 GodotNavigationServer::map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const { @@ -421,20 +423,20 @@ uint32_t GodotNavigationServer::region_get_navigation_layers(RID p_region) const return region->get_navigation_layers(); } -COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh) { +COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navigation_mesh) { NavRegion *region = region_owner.get_or_null(p_region); ERR_FAIL_COND(region == nullptr); - region->set_mesh(p_nav_mesh); + region->set_mesh(p_navigation_mesh); } -void GodotNavigationServer::region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const { - ERR_FAIL_COND(r_mesh.is_null()); - ERR_FAIL_COND(p_node == nullptr); +void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) const { + ERR_FAIL_COND(p_navigation_mesh.is_null()); + ERR_FAIL_COND(p_root_node == nullptr); #ifndef _3D_DISABLED - NavigationMeshGenerator::get_singleton()->clear(r_mesh); - NavigationMeshGenerator::get_singleton()->bake(r_mesh, p_node); + NavigationMeshGenerator::get_singleton()->clear(p_navigation_mesh); + NavigationMeshGenerator::get_singleton()->bake(p_navigation_mesh, p_root_node); #endif } @@ -838,20 +840,34 @@ void GodotNavigationServer::process(real_t p_delta_time) { } } -NavigationUtilities::PathQueryResult GodotNavigationServer::_query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const { - NavigationUtilities::PathQueryResult r_query_result; +PathQueryResult GodotNavigationServer::_query_path(const PathQueryParameters &p_parameters) const { + PathQueryResult r_query_result; const NavMap *map = map_owner.get_or_null(p_parameters.map); ERR_FAIL_COND_V(map == nullptr, r_query_result); // run the pathfinding - if (p_parameters.pathfinding_algorithm == NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR) { + if (p_parameters.pathfinding_algorithm == PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR) { // while postprocessing is still part of map.get_path() need to check and route it here for the correct "optimize" post-processing - if (p_parameters.path_postprocessing == NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL) { - r_query_result.path = map->get_path(p_parameters.start_position, p_parameters.target_position, true, p_parameters.navigation_layers); - } else if (p_parameters.path_postprocessing == NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED) { - r_query_result.path = map->get_path(p_parameters.start_position, p_parameters.target_position, false, p_parameters.navigation_layers); + if (p_parameters.path_postprocessing == PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL) { + r_query_result.path = map->get_path( + p_parameters.start_position, + p_parameters.target_position, + true, + p_parameters.navigation_layers, + p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES) ? &r_query_result.path_types : nullptr, + p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS) ? &r_query_result.path_rids : nullptr, + p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS) ? &r_query_result.path_owner_ids : nullptr); + } else if (p_parameters.path_postprocessing == PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED) { + r_query_result.path = map->get_path( + p_parameters.start_position, + p_parameters.target_position, + false, + p_parameters.navigation_layers, + p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES) ? &r_query_result.path_types : nullptr, + p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS) ? &r_query_result.path_rids : nullptr, + p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS) ? &r_query_result.path_owner_ids : nullptr); } } else { return r_query_result; diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index 18431e39b8..6f39420c67 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -135,8 +135,8 @@ public: COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers); virtual uint32_t region_get_navigation_layers(RID p_region) const override; COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform); - COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh); - virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const override; + COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navigation_mesh); + virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) const override; virtual int region_get_connections_count(RID p_region) const override; virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override; diff --git a/modules/navigation/nav_base.h b/modules/navigation/nav_base.h index f5d2880d36..a3ab77301b 100644 --- a/modules/navigation/nav_base.h +++ b/modules/navigation/nav_base.h @@ -33,6 +33,7 @@ #include "nav_rid.h" #include "nav_utils.h" +#include "servers/navigation/navigation_utilities.h" class NavMap; @@ -42,8 +43,11 @@ protected: float enter_cost = 0.0; float travel_cost = 1.0; ObjectID owner_id; + NavigationUtilities::PathSegmentType type; public: + NavigationUtilities::PathSegmentType get_type() const { return type; } + void set_navigation_layers(uint32_t p_navigation_layers) { navigation_layers = p_navigation_layers; } uint32_t get_navigation_layers() const { return navigation_layers; } diff --git a/modules/navigation/nav_link.h b/modules/navigation/nav_link.h index 8f51a63951..e9c69286f9 100644 --- a/modules/navigation/nav_link.h +++ b/modules/navigation/nav_link.h @@ -43,6 +43,10 @@ class NavLink : public NavBase { bool link_dirty = true; public: + NavLink() { + type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK; + } + void set_map(NavMap *p_map); NavMap *get_map() const { return map; diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 83862e1e34..8d58c9335d 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -38,6 +38,18 @@ #define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a))) +// Helper macro +#define APPEND_METADATA(poly) \ + if (r_path_types) { \ + r_path_types->push_back(poly->owner->get_type()); \ + } \ + if (r_path_rids) { \ + r_path_rids->push_back(poly->owner->get_self()); \ + } \ + if (r_path_owners) { \ + r_path_owners->push_back(poly->owner->get_owner_id()); \ + } + void NavMap::set_up(Vector3 p_up) { up = p_up; regenerate_polygons = true; @@ -71,7 +83,18 @@ gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const { return p; } -Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const { +Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const { + // Clear metadata outputs. + if (r_path_types) { + r_path_types->clear(); + } + if (r_path_rids) { + r_path_rids->clear(); + } + if (r_path_owners) { + r_path_owners->clear(); + } + // Find the start poly and the end poly on this map. const gd::Polygon *begin_poly = nullptr; const gd::Polygon *end_poly = nullptr; @@ -115,6 +138,24 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p return Vector<Vector3>(); } if (begin_poly == end_poly) { + if (r_path_types) { + r_path_types->resize(2); + r_path_types->write[0] = begin_poly->owner->get_type(); + r_path_types->write[1] = end_poly->owner->get_type(); + } + + if (r_path_rids) { + r_path_rids->resize(2); + (*r_path_rids)[0] = begin_poly->owner->get_self(); + (*r_path_rids)[1] = end_poly->owner->get_self(); + } + + if (r_path_owners) { + r_path_owners->resize(2); + r_path_owners->write[0] = begin_poly->owner->get_owner_id(); + r_path_owners->write[1] = end_poly->owner->get_owner_id(); + } + Vector<Vector3> path; path.resize(2); path.write[0] = begin_point; @@ -296,6 +337,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p gd::NavigationPoly *p = apex_poly; path.push_back(end_point); + APPEND_METADATA(end_poly); while (p) { // Set left and right points of the pathway between polygons. @@ -312,7 +354,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p left_poly = p; left_portal = left; } else { - clip_path(navigation_polys, path, apex_poly, right_portal, right_poly); + clip_path(navigation_polys, path, apex_poly, right_portal, right_poly, r_path_types, r_path_rids, r_path_owners); apex_point = right_portal; p = right_poly; @@ -320,7 +362,9 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p apex_poly = p; left_portal = apex_point; right_portal = apex_point; + path.push_back(apex_point); + APPEND_METADATA(apex_poly->poly); skip = true; } } @@ -331,7 +375,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p right_poly = p; right_portal = right; } else { - clip_path(navigation_polys, path, apex_poly, left_portal, left_poly); + clip_path(navigation_polys, path, apex_poly, left_portal, left_poly, r_path_types, r_path_rids, r_path_owners); apex_point = left_portal; p = left_poly; @@ -339,7 +383,9 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p apex_poly = p; right_portal = apex_point; left_portal = apex_point; + path.push_back(apex_point); + APPEND_METADATA(apex_poly->poly); } } @@ -355,12 +401,23 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p // If the last point is not the begin point, add it to the list. if (path[path.size() - 1] != begin_point) { path.push_back(begin_point); + APPEND_METADATA(begin_poly); } path.reverse(); + if (r_path_types) { + r_path_types->reverse(); + } + if (r_path_rids) { + r_path_rids->reverse(); + } + if (r_path_owners) { + r_path_owners->reverse(); + } } else { path.push_back(end_point); + APPEND_METADATA(end_poly); // Add mid points int np_id = least_cost_id; @@ -369,18 +426,37 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p int prev = navigation_polys[np_id].back_navigation_edge; int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size(); Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5; + path.push_back(point); + APPEND_METADATA(navigation_polys[np_id].poly); } else { path.push_back(navigation_polys[np_id].entry); + APPEND_METADATA(navigation_polys[np_id].poly); } np_id = navigation_polys[np_id].back_navigation_poly_id; } path.push_back(begin_point); + APPEND_METADATA(begin_poly); + path.reverse(); + if (r_path_types) { + r_path_types->reverse(); + } + if (r_path_rids) { + r_path_rids->reverse(); + } + if (r_path_owners) { + r_path_owners->reverse(); + } } + // Ensure post conditions (path arrays MUST match in size). + CRASH_COND(r_path_types && path.size() != r_path_types->size()); + CRASH_COND(r_path_rids && path.size() != r_path_rids->size()); + CRASH_COND(r_path_owners && path.size() != r_path_owners->size()); + return path; } @@ -837,7 +913,7 @@ void NavMap::dispatch_callbacks() { } } -void NavMap::clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const { +void NavMap::clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const { Vector3 from = path[path.size() - 1]; if (from.is_equal_approx(p_to_point)) { @@ -863,6 +939,7 @@ void NavMap::clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys if (cut_plane.intersects_segment(pathway_start, pathway_end, &inters)) { if (!inters.is_equal_approx(p_to_point) && !inters.is_equal_approx(path[path.size() - 1])) { path.push_back(inters); + APPEND_METADATA(from_poly->poly); } } } diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h index a3da9fa727..3be13133d2 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -115,7 +115,7 @@ public: gd::PointKey get_point_key(const Vector3 &p_pos) const; - Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const; + Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const; Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const; Vector3 get_closest_point(const Vector3 &p_point) const; Vector3 get_closest_point_normal(const Vector3 &p_point) const; @@ -154,7 +154,7 @@ public: private: void compute_single_step(uint32_t index, RvoAgent **agent); - void clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const; + void clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const; }; #endif // NAV_MAP_H diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h index 8d2b5aa9eb..3ef3ef5748 100644 --- a/modules/navigation/nav_region.h +++ b/modules/navigation/nav_region.h @@ -48,7 +48,9 @@ class NavRegion : public NavBase { LocalVector<gd::Polygon> polygons; public: - NavRegion() {} + NavRegion() { + type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION; + } void scratch_polygons() { polygons_dirty = true; diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index f0d3e329ce..62db6ff4e9 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -470,14 +470,14 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans } } -void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) { +void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_navigation_mesh) { Vector<Vector3> nav_vertices; for (int i = 0; i < p_detail_mesh->nverts; i++) { const float *v = &p_detail_mesh->verts[i * 3]; nav_vertices.push_back(Vector3(v[0], v[1], v[2])); } - p_nav_mesh->set_vertices(nav_vertices); + p_navigation_mesh->set_vertices(nav_vertices); for (int i = 0; i < p_detail_mesh->nmeshes; i++) { const unsigned int *m = &p_detail_mesh->meshes[i * 4]; @@ -492,13 +492,13 @@ void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(con nav_indices.write[0] = ((int)(bverts + tris[j * 4 + 0])); nav_indices.write[1] = ((int)(bverts + tris[j * 4 + 2])); nav_indices.write[2] = ((int)(bverts + tris[j * 4 + 1])); - p_nav_mesh->add_polygon(nav_indices); + p_navigation_mesh->add_polygon(nav_indices); } } } void NavigationMeshGenerator::_build_recast_navigation_mesh( - Ref<NavigationMesh> p_nav_mesh, + Ref<NavigationMesh> p_navigation_mesh, #ifdef TOOLS_ENABLED EditorProgress *ep, #endif @@ -528,42 +528,42 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh( rcConfig cfg; memset(&cfg, 0, sizeof(cfg)); - cfg.cs = p_nav_mesh->get_cell_size(); - cfg.ch = p_nav_mesh->get_cell_height(); - cfg.walkableSlopeAngle = p_nav_mesh->get_agent_max_slope(); - cfg.walkableHeight = (int)Math::ceil(p_nav_mesh->get_agent_height() / cfg.ch); - cfg.walkableClimb = (int)Math::floor(p_nav_mesh->get_agent_max_climb() / cfg.ch); - cfg.walkableRadius = (int)Math::ceil(p_nav_mesh->get_agent_radius() / cfg.cs); - cfg.maxEdgeLen = (int)(p_nav_mesh->get_edge_max_length() / p_nav_mesh->get_cell_size()); - cfg.maxSimplificationError = p_nav_mesh->get_edge_max_error(); - cfg.minRegionArea = (int)(p_nav_mesh->get_region_min_size() * p_nav_mesh->get_region_min_size()); - cfg.mergeRegionArea = (int)(p_nav_mesh->get_region_merge_size() * p_nav_mesh->get_region_merge_size()); - cfg.maxVertsPerPoly = (int)p_nav_mesh->get_verts_per_poly(); - cfg.detailSampleDist = MAX(p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance(), 0.1f); - cfg.detailSampleMaxError = p_nav_mesh->get_cell_height() * p_nav_mesh->get_detail_sample_max_error(); - - if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_nav_mesh->get_agent_height())) { + cfg.cs = p_navigation_mesh->get_cell_size(); + cfg.ch = p_navigation_mesh->get_cell_height(); + cfg.walkableSlopeAngle = p_navigation_mesh->get_agent_max_slope(); + cfg.walkableHeight = (int)Math::ceil(p_navigation_mesh->get_agent_height() / cfg.ch); + cfg.walkableClimb = (int)Math::floor(p_navigation_mesh->get_agent_max_climb() / cfg.ch); + cfg.walkableRadius = (int)Math::ceil(p_navigation_mesh->get_agent_radius() / cfg.cs); + cfg.maxEdgeLen = (int)(p_navigation_mesh->get_edge_max_length() / p_navigation_mesh->get_cell_size()); + cfg.maxSimplificationError = p_navigation_mesh->get_edge_max_error(); + cfg.minRegionArea = (int)(p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size()); + cfg.mergeRegionArea = (int)(p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size()); + cfg.maxVertsPerPoly = (int)p_navigation_mesh->get_vertices_per_polygon(); + cfg.detailSampleDist = MAX(p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance(), 0.1f); + cfg.detailSampleMaxError = p_navigation_mesh->get_cell_height() * p_navigation_mesh->get_detail_sample_max_error(); + + if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_navigation_mesh->get_agent_height())) { WARN_PRINT("Property agent_height is ceiled to cell_height voxel units and loses precision."); } - if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_nav_mesh->get_agent_max_climb())) { + if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_navigation_mesh->get_agent_max_climb())) { WARN_PRINT("Property agent_max_climb is floored to cell_height voxel units and loses precision."); } - if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_nav_mesh->get_agent_radius())) { + if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_navigation_mesh->get_agent_radius())) { WARN_PRINT("Property agent_radius is ceiled to cell_size voxel units and loses precision."); } - if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_nav_mesh->get_edge_max_length())) { + if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_navigation_mesh->get_edge_max_length())) { WARN_PRINT("Property edge_max_length is rounded to cell_size voxel units and loses precision."); } - if (!Math::is_equal_approx((float)cfg.minRegionArea, p_nav_mesh->get_region_min_size() * p_nav_mesh->get_region_min_size())) { + if (!Math::is_equal_approx((float)cfg.minRegionArea, p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size())) { WARN_PRINT("Property region_min_size is converted to int and loses precision."); } - if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_nav_mesh->get_region_merge_size() * p_nav_mesh->get_region_merge_size())) { + if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size())) { WARN_PRINT("Property region_merge_size is converted to int and loses precision."); } - if (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_nav_mesh->get_verts_per_poly())) { - WARN_PRINT("Property verts_per_poly is converted to int and loses precision."); + if (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_navigation_mesh->get_vertices_per_polygon())) { + WARN_PRINT("Property vertices_per_polygon is converted to int and loses precision."); } - if (p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance() < 0.1f) { + if (p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance() < 0.1f) { WARN_PRINT("Property detail_sample_distance is clamped to 0.1 world units as the resulting value from multiplying with cell_size is too low."); } @@ -574,9 +574,9 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh( cfg.bmax[1] = bmax[1]; cfg.bmax[2] = bmax[2]; - AABB baking_aabb = p_nav_mesh->get_filter_baking_aabb(); + AABB baking_aabb = p_navigation_mesh->get_filter_baking_aabb(); if (baking_aabb.has_volume()) { - Vector3 baking_aabb_offset = p_nav_mesh->get_filter_baking_aabb_offset(); + Vector3 baking_aabb_offset = p_navigation_mesh->get_filter_baking_aabb_offset(); cfg.bmin[0] = baking_aabb.position[0] + baking_aabb_offset.x; cfg.bmin[1] = baking_aabb.position[1] + baking_aabb_offset.y; cfg.bmin[2] = baking_aabb.position[2] + baking_aabb_offset.z; @@ -627,13 +627,13 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh( ERR_FAIL_COND(!rcRasterizeTriangles(&ctx, verts, nverts, tris, tri_areas.ptr(), ntris, *hf, cfg.walkableClimb)); } - if (p_nav_mesh->get_filter_low_hanging_obstacles()) { + if (p_navigation_mesh->get_filter_low_hanging_obstacles()) { rcFilterLowHangingWalkableObstacles(&ctx, cfg.walkableClimb, *hf); } - if (p_nav_mesh->get_filter_ledge_spans()) { + if (p_navigation_mesh->get_filter_ledge_spans()) { rcFilterLedgeSpans(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf); } - if (p_nav_mesh->get_filter_walkable_low_height_spans()) { + if (p_navigation_mesh->get_filter_walkable_low_height_spans()) { rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf); } @@ -665,10 +665,10 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh( } #endif - if (p_nav_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) { + if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) { ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf)); ERR_FAIL_COND(!rcBuildRegions(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea)); - } else if (p_nav_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_MONOTONE) { + } else if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_MONOTONE) { ERR_FAIL_COND(!rcBuildRegionsMonotone(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea)); } else { ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, 0, cfg.minRegionArea)); @@ -710,7 +710,7 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh( } #endif - _convert_detail_mesh_to_native_navigation_mesh(detail_mesh, p_nav_mesh); + _convert_detail_mesh_to_native_navigation_mesh(detail_mesh, p_navigation_mesh); rcFreePolyMesh(poly_mesh); poly_mesh = nullptr; @@ -729,8 +729,8 @@ NavigationMeshGenerator::NavigationMeshGenerator() { NavigationMeshGenerator::~NavigationMeshGenerator() { } -void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) { - ERR_FAIL_COND_MSG(!p_nav_mesh.is_valid(), "Invalid navigation mesh."); +void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) { + ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh."); #ifdef TOOLS_ENABLED EditorProgress *ep(nullptr); @@ -755,17 +755,17 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) List<Node *> parse_nodes; - if (p_nav_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_NAVMESH_CHILDREN) { - parse_nodes.push_back(p_node); + if (p_navigation_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) { + parse_nodes.push_back(p_root_node); } else { - p_node->get_tree()->get_nodes_in_group(p_nav_mesh->get_source_group_name(), &parse_nodes); + p_root_node->get_tree()->get_nodes_in_group(p_navigation_mesh->get_source_group_name(), &parse_nodes); } - Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_global_transform().affine_inverse(); + Transform3D navmesh_xform = Object::cast_to<Node3D>(p_root_node)->get_global_transform().affine_inverse(); for (Node *E : parse_nodes) { - NavigationMesh::ParsedGeometryType geometry_type = p_nav_mesh->get_parsed_geometry_type(); - uint32_t collision_mask = p_nav_mesh->get_collision_mask(); - bool recurse_children = p_nav_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT; + NavigationMesh::ParsedGeometryType geometry_type = p_navigation_mesh->get_parsed_geometry_type(); + uint32_t collision_mask = p_navigation_mesh->get_collision_mask(); + bool recurse_children = p_navigation_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT; _parse_geometry(navmesh_xform, E, vertices, indices, geometry_type, collision_mask, recurse_children); } @@ -777,7 +777,7 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) rcPolyMeshDetail *detail_mesh = nullptr; _build_recast_navigation_mesh( - p_nav_mesh, + p_navigation_mesh, #ifdef TOOLS_ENABLED ep, #endif @@ -816,16 +816,16 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) #endif } -void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) { - if (p_nav_mesh.is_valid()) { - p_nav_mesh->clear_polygons(); - p_nav_mesh->set_vertices(Vector<Vector3>()); +void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_navigation_mesh) { + if (p_navigation_mesh.is_valid()) { + p_navigation_mesh->clear_polygons(); + p_navigation_mesh->set_vertices(Vector<Vector3>()); } } void NavigationMeshGenerator::_bind_methods() { - ClassDB::bind_method(D_METHOD("bake", "nav_mesh", "root_node"), &NavigationMeshGenerator::bake); - ClassDB::bind_method(D_METHOD("clear", "nav_mesh"), &NavigationMeshGenerator::clear); + ClassDB::bind_method(D_METHOD("bake", "navigation_mesh", "root_node"), &NavigationMeshGenerator::bake); + ClassDB::bind_method(D_METHOD("clear", "navigation_mesh"), &NavigationMeshGenerator::clear); } #endif diff --git a/modules/navigation/navigation_mesh_generator.h b/modules/navigation/navigation_mesh_generator.h index 8cc1531b53..f6bf39d714 100644 --- a/modules/navigation/navigation_mesh_generator.h +++ b/modules/navigation/navigation_mesh_generator.h @@ -55,9 +55,9 @@ protected: static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices); static void _parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children); - static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh); + static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_navigation_mesh); static void _build_recast_navigation_mesh( - Ref<NavigationMesh> p_nav_mesh, + Ref<NavigationMesh> p_navigation_mesh, #ifdef TOOLS_ENABLED EditorProgress *ep, #endif @@ -75,8 +75,8 @@ public: NavigationMeshGenerator(); ~NavigationMeshGenerator(); - void bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node); - void clear(Ref<NavigationMesh> p_nav_mesh); + void bake(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node); + void clear(Ref<NavigationMesh> p_navigation_mesh); }; #endif diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp index a2c33a91c4..71fcd3ce7f 100644 --- a/modules/openxr/editor/openxr_action_map_editor.cpp +++ b/modules/openxr/editor/openxr_action_map_editor.cpp @@ -57,7 +57,7 @@ void OpenXRActionMapEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { for (int i = 0; i < tabs->get_child_count(); i++) { - Control *tab = static_cast<Control *>(tabs->get_child(i)); + Control *tab = Object::cast_to<Control>(tabs->get_child(i)); if (tab) { tab->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); } @@ -205,11 +205,12 @@ void OpenXRActionMapEditor::_on_remove_action_set(Object *p_action_set_editor) { action_map->set_edited(true); } -void OpenXRActionMapEditor::_on_action_removed() { +void OpenXRActionMapEditor::_on_action_removed(Ref<OpenXRAction> p_action) { for (int i = 0; i < tabs->get_tab_count(); i++) { // First tab won't be an interaction profile editor, but being thorough.. - OpenXRInteractionProfileEditorBase *interaction_profile_editor = static_cast<OpenXRInteractionProfileEditorBase *>(tabs->get_tab_control(i)); + OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i)); if (interaction_profile_editor) { + interaction_profile_editor->remove_all_bindings_for_action(p_action); } } } @@ -309,7 +310,7 @@ void OpenXRActionMapEditor::_on_tabs_tab_changed(int p_tab) { } void OpenXRActionMapEditor::_on_tab_button_pressed(int p_tab) { - OpenXRInteractionProfileEditorBase *interaction_profile_editor = static_cast<OpenXRInteractionProfileEditorBase *>(tabs->get_tab_control(p_tab)); + OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(p_tab)); ERR_FAIL_NULL(interaction_profile_editor); undo_redo->create_action(TTR("Remove interaction profile")); @@ -375,9 +376,9 @@ void OpenXRActionMapEditor::_clear_action_map() { child->queue_free(); } - for (int i = 0; i < tabs->get_tab_count(); i++) { + for (int i = tabs->get_tab_count() - 1; i >= 0; --i) { // First tab won't be an interaction profile editor, but being thorough.. - OpenXRInteractionProfileEditorBase *interaction_profile_editor = static_cast<OpenXRInteractionProfileEditorBase *>(tabs->get_tab_control(i)); + OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i)); if (interaction_profile_editor) { tabs->remove_child(interaction_profile_editor); interaction_profile_editor->queue_free(); diff --git a/modules/openxr/editor/openxr_action_map_editor.h b/modules/openxr/editor/openxr_action_map_editor.h index 8e3210a8e9..2ff6ddf785 100644 --- a/modules/openxr/editor/openxr_action_map_editor.h +++ b/modules/openxr/editor/openxr_action_map_editor.h @@ -75,7 +75,7 @@ private: void _on_add_action_set(); void _set_focus_on_action_set(OpenXRActionSetEditor *p_action_set_editor); void _on_remove_action_set(Object *p_action_set_editor); - void _on_action_removed(); + void _on_action_removed(Ref<OpenXRAction> p_action); void _on_add_interaction_profile(); void _on_interaction_profile_selected(const String p_path); diff --git a/modules/openxr/editor/openxr_action_set_editor.cpp b/modules/openxr/editor/openxr_action_set_editor.cpp index d3b6945635..7f4da8b312 100644 --- a/modules/openxr/editor/openxr_action_set_editor.cpp +++ b/modules/openxr/editor/openxr_action_set_editor.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "openxr_action_set_editor.h" +#include "editor/editor_node.h" #include "openxr_action_editor.h" void OpenXRActionSetEditor::_bind_methods() { @@ -211,6 +212,7 @@ void OpenXRActionSetEditor::set_focus_on_entry() { } OpenXRActionSetEditor::OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set) { + undo_redo = EditorNode::get_undo_redo(); action_map = p_action_map; action_set = p_action_set; diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.cpp b/modules/openxr/editor/openxr_interaction_profile_editor.cpp index 3eca1967f5..9d8ac76187 100644 --- a/modules/openxr/editor/openxr_interaction_profile_editor.cpp +++ b/modules/openxr/editor/openxr_interaction_profile_editor.cpp @@ -124,8 +124,8 @@ void OpenXRInteractionProfileEditorBase::remove_all_bindings_for_action(Ref<Open undo_redo->create_action(TTR("Remove action from interaction profile")); PackedStringArray paths = binding->get_paths(); for (const String &path : paths) { - undo_redo->add_do_method(this, "_remove_binding", p_action, path); - undo_redo->add_undo_method(this, "_add_binding", p_action, path); + undo_redo->add_do_method(this, "_remove_binding", action_name, path); + undo_redo->add_undo_method(this, "_add_binding", action_name, path); } undo_redo->commit_action(false); @@ -308,7 +308,7 @@ void OpenXRInteractionProfileEditor::_update_interaction_profile() { void OpenXRInteractionProfileEditor::_theme_changed() { for (int i = 0; i < main_hb->get_child_count(); i++) { - Control *panel = static_cast<Control *>(main_hb->get_child(i)); + Control *panel = Object::cast_to<Control>(main_hb->get_child(i)); if (panel) { panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer"))); } diff --git a/modules/openxr/editor/openxr_select_action_dialog.cpp b/modules/openxr/editor/openxr_select_action_dialog.cpp index 5f018291d5..c4b2ef95c5 100644 --- a/modules/openxr/editor/openxr_select_action_dialog.cpp +++ b/modules/openxr/editor/openxr_select_action_dialog.cpp @@ -47,7 +47,7 @@ void OpenXRSelectActionDialog::_notification(int p_what) { void OpenXRSelectActionDialog::_on_select_action(const String p_action) { if (selected_action != "") { NodePath button_path = action_buttons[selected_action]; - Button *button = static_cast<Button *>(get_node(button_path)); + Button *button = Object::cast_to<Button>(get_node(button_path)); if (button != nullptr) { button->set_flat(true); } @@ -57,7 +57,7 @@ void OpenXRSelectActionDialog::_on_select_action(const String p_action) { if (selected_action != "") { NodePath button_path = action_buttons[selected_action]; - Button *button = static_cast<Button *>(get_node(button_path)); + Button *button = Object::cast_to<Button>(get_node(button_path)); if (button != nullptr) { button->set_flat(false); } diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp index 6ce26a62db..b871840cb2 100644 --- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp +++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp @@ -46,7 +46,7 @@ void OpenXRSelectInteractionProfileDialog::_notification(int p_what) { void OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile(const String p_interaction_profile) { if (selected_interaction_profile != "") { NodePath button_path = ip_buttons[selected_interaction_profile]; - Button *button = static_cast<Button *>(get_node(button_path)); + Button *button = Object::cast_to<Button>(get_node(button_path)); if (button != nullptr) { button->set_flat(true); } @@ -56,7 +56,7 @@ void OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile(const if (selected_interaction_profile != "") { NodePath button_path = ip_buttons[selected_interaction_profile]; - Button *button = static_cast<Button *>(get_node(button_path)); + Button *button = Object::cast_to<Button>(get_node(button_path)); if (button != nullptr) { button->set_flat(false); } diff --git a/modules/openxr/extensions/openxr_android_extension.cpp b/modules/openxr/extensions/openxr_android_extension.cpp index 753fc5fa89..402a2ee707 100644 --- a/modules/openxr/extensions/openxr_android_extension.cpp +++ b/modules/openxr/extensions/openxr_android_extension.cpp @@ -74,7 +74,7 @@ void OpenXRAndroidExtension::on_before_instance_created() { } // We're keeping the Android create info struct here to avoid including openxr_platform.h in a header, which would break other extensions. -// This is reasonably safe as the struct is only used during intialization and the extension is a singleton. +// This is reasonably safe as the struct is only used during initialization and the extension is a singleton. static XrInstanceCreateInfoAndroidKHR instance_create_info; void *OpenXRAndroidExtension::set_instance_create_info_and_get_next_pointer(void *p_next_pointer) { diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub index 51d75d45b0..37c8a95905 100644 --- a/modules/raycast/SCsub +++ b/modules/raycast/SCsub @@ -68,10 +68,10 @@ if env["builtin_embree"]: if not env.msvc: if env["arch"] in ["x86_64", "x86_32"]: - env_raycast.Append(CPPFLAGS=["-msse2", "-mxsave"]) + env_raycast.Append(CCFLAGS=["-msse2", "-mxsave"]) if env["platform"] == "windows": - env_raycast.Append(CPPFLAGS=["-mstackrealign"]) + env_raycast.Append(CCFLAGS=["-mstackrealign"]) if env["platform"] == "windows": if env.msvc: @@ -92,11 +92,15 @@ if env["builtin_embree"]: env_thirdparty.Append(CPPDEFINES=["__SSE2__", "__SSE__"]) if env["platform"] == "web": - env_thirdparty.Append(CPPFLAGS=["-msimd128"]) + env_thirdparty.Append(CXXFLAGS=["-msimd128"]) if not env.msvc: + # Flags synced with upstream gnu.cmake. + if env["arch"] == "arm64" and env["platform"] == "linuxbsd": + env_thirdparty.Append(CXXFLAGS=["-flax-vector-conversions"]) + env_thirdparty.Append( - CPPFLAGS=[ + CXXFLAGS=[ "-fno-strict-overflow", "-fno-delete-null-pointer-checks", "-fwrapv", diff --git a/modules/regex/config.py b/modules/regex/config.py index 1248a8374d..df9f44cb95 100644 --- a/modules/regex/config.py +++ b/modules/regex/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return not env["arch"].startswith("rv") + return True def configure(env): diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index 2dba4916a0..b194e7cb3f 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -67,19 +67,12 @@ void ImageLoaderSVG::_replace_color_property(const HashMap<Color, Color> &p_colo } } -Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map) { +Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample) { ERR_FAIL_COND_V_MSG(Math::is_zero_approx(p_scale), ERR_INVALID_PARAMETER, "ImageLoaderSVG: Can't load SVG with a scale of 0."); - if (p_color_map.size()) { - _replace_color_property(p_color_map, "stop-color=\"", p_string); - _replace_color_property(p_color_map, "fill=\"", p_string); - _replace_color_property(p_color_map, "stroke=\"", p_string); - } - std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); - PackedByteArray bytes = p_string.to_utf8_buffer(); - tvg::Result result = picture->load((const char *)bytes.ptr(), bytes.size(), "svg", true); + tvg::Result result = picture->load((const char *)p_buffer.ptr(), p_buffer.size(), "svg", true); if (result != tvg::Result::Success) { return ERR_INVALID_DATA; } @@ -149,6 +142,18 @@ Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_stri return OK; } +Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map) { + if (p_color_map.size()) { + _replace_color_property(p_color_map, "stop-color=\"", p_string); + _replace_color_property(p_color_map, "fill=\"", p_string); + _replace_color_property(p_color_map, "stroke=\"", p_string); + } + + PackedByteArray bytes = p_string.to_utf8_buffer(); + + return create_image_from_utf8_buffer(p_image, bytes, p_scale, p_upsample); +} + void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("svg"); } diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h index 84511f1708..d955a04f1e 100644 --- a/modules/svg/image_loader_svg.h +++ b/modules/svg/image_loader_svg.h @@ -41,6 +41,7 @@ class ImageLoaderSVG : public ImageFormatLoader { public: static void set_forced_color_map(const HashMap<Color, Color> &p_color_map); + Error create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample); Error create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map); virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) override; diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 512643867b..f3e7ae9b83 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -3296,7 +3296,7 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca Point2 cpos = p_pos; cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size; Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range); + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); } else { double scale = _font_get_scale(p_font_rid, p_size); Point2 cpos = p_pos; @@ -3388,7 +3388,7 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R Point2 cpos = p_pos; cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size; Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range); + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); } else { Point2 cpos = p_pos; double scale = _font_get_scale(p_font_rid, p_size); @@ -3915,7 +3915,7 @@ bool TextServerAdvanced::_shaped_text_add_string(const RID &p_shaped, const Stri return true; } -bool TextServerAdvanced::_shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length) { +bool TextServerAdvanced::_shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, float p_baseline) { _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -3935,6 +3935,7 @@ bool TextServerAdvanced::_shaped_text_add_object(const RID &p_shaped, const Vari obj.inline_align = p_inline_align; obj.rect.size = p_size; obj.pos = span.start; + obj.baseline = p_baseline; sd->spans.push_back(span); sd->text = sd->text + String::chr(0xfffc).repeat(p_length); @@ -3945,7 +3946,7 @@ bool TextServerAdvanced::_shaped_text_add_object(const RID &p_shaped, const Vari return true; } -bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align) { +bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -3953,6 +3954,7 @@ bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const V ERR_FAIL_COND_V(!sd->objects.has(p_key), false); sd->objects[p_key].rect.size = p_size; sd->objects[p_key].inline_align = p_inline_align; + sd->objects[p_key].baseline = p_baseline; if (sd->valid) { // Recalc string metrics. sd->ascent = 0; @@ -4039,6 +4041,9 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const { case INLINE_ALIGNMENT_CENTER_TO: { E.value.rect.position.y -= E.value.rect.size.y / 2; } break; + case INLINE_ALIGNMENT_BASELINE_TO: { + E.value.rect.position.y -= E.value.baseline; + } break; case INLINE_ALIGNMENT_TOP_TO: { // NOP } break; @@ -4067,6 +4072,9 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const { case INLINE_ALIGNMENT_CENTER_TO: { E.value.rect.position.x -= E.value.rect.size.x / 2; } break; + case INLINE_ALIGNMENT_BASELINE_TO: { + E.value.rect.position.x -= E.value.baseline; + } break; case INLINE_ALIGNMENT_TOP_TO: { // NOP } break; @@ -6238,7 +6246,7 @@ String TextServerAdvanced::_string_to_lower(const String &p_string, const String return String::utf16(lower.ptr(), len); } -PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_string, const String &p_language) const { +PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_string, const String &p_language, int p_chars_per_line) const { const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language; // Convert to UTF-16. Char16String utf16 = p_string.utf16(); @@ -6246,15 +6254,7 @@ PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_str HashSet<int> breaks; UErrorCode err = U_ZERO_ERROR; UBreakIterator *bi = ubrk_open(UBRK_LINE, lang.ascii().get_data(), (const UChar *)utf16.get_data(), utf16.length(), &err); - if (U_FAILURE(err)) { - // No data loaded - use fallback. - for (int i = 0; i < p_string.length(); i++) { - char32_t c = p_string[i]; - if (is_whitespace(c) || is_linebreak(c)) { - breaks.insert(i); - } - } - } else { + if (U_SUCCESS(err)) { while (ubrk_next(bi) != UBRK_DONE) { int pos = _convert_pos(p_string, utf16, ubrk_current(bi)) - 1; if (pos != p_string.length() - 1) { @@ -6265,24 +6265,80 @@ PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_str ubrk_close(bi); PackedInt32Array ret; + + int line_start = 0; + int line_end = 0; // End of last word on current line. + int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word. + int word_length = 0; + for (int i = 0; i < p_string.length(); i++) { - char32_t c = p_string[i]; - if (c == 0xfffc) { - continue; - } - if (u_ispunct(c) && c != 0x005F) { - ret.push_back(i); - continue; - } - if (is_underscore(c)) { - ret.push_back(i); - continue; - } - if (breaks.has(i)) { + const char32_t c = p_string[i]; + + if (is_linebreak(c)) { + // Force newline. + ret.push_back(line_start); ret.push_back(i); + line_start = i + 1; + line_end = line_start; + word_start = line_start; + word_length = 0; + } else if (c == 0xfffc) { continue; + } else if ((u_ispunct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || is_whitespace(c)) { + // A whitespace ends current word. + if (word_length > 0) { + line_end = i - 1; + word_start = -1; + word_length = 0; + } + } else if (breaks.has(i)) { + // End current word, no space. + if (word_length > 0) { + line_end = i; + word_start = i + 1; + word_length = 0; + } + if (p_chars_per_line <= 0) { + ret.push_back(line_start); + ret.push_back(line_end + 1); + line_start = word_start; + line_end = line_start; + } + } else { + if (word_start == -1) { + word_start = i; + if (p_chars_per_line <= 0) { + ret.push_back(line_start); + ret.push_back(line_end + 1); + line_start = word_start; + line_end = line_start; + } + } + word_length += 1; + + if (p_chars_per_line > 0) { + if (word_length > p_chars_per_line) { + // Word too long: wrap before current character. + ret.push_back(line_start); + ret.push_back(i); + line_start = i; + line_end = i; + word_start = i; + word_length = 1; + } else if (i - line_start + 1 > p_chars_per_line) { + // Line too long: wrap after the last word. + ret.push_back(line_start); + ret.push_back(line_end + 1); + line_start = word_start; + line_end = line_start; + } + } } } + if (line_start < p_string.length()) { + ret.push_back(line_start); + ret.push_back(p_string.length()); + } return ret; } diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 5e6d2cc2c0..59f44cf142 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -467,6 +467,7 @@ class TextServerAdvanced : public TextServerExtension { int pos = 0; InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER; Rect2 rect; + float baseline = 0; }; HashMap<Variant, EmbeddedObject, VariantHasher, VariantComparator> objects; @@ -868,8 +869,8 @@ public: MODBIND2RC(int64_t, shaped_text_get_spacing, const RID &, SpacingType); MODBIND7R(bool, shaped_text_add_string, const RID &, const String &, const TypedArray<RID> &, int64_t, const Dictionary &, const String &, const Variant &); - MODBIND5R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t); - MODBIND4R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment); + MODBIND6R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t, float); + MODBIND5R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment, float); MODBIND1RC(int64_t, shaped_get_span_count, const RID &); MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t); @@ -914,7 +915,7 @@ public: MODBIND2RC(String, parse_number, const String &, const String &); MODBIND1RC(String, percent_sign, const String &); - MODBIND2RC(PackedInt32Array, string_get_word_breaks, const String &, const String &); + MODBIND3RC(PackedInt32Array, string_get_word_breaks, const String &, const String &, int); MODBIND2RC(int64_t, is_confusable, const String &, const PackedStringArray &); MODBIND1RC(bool, spoof_check, const String &); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 353d370f14..ea7b7a271b 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -2359,7 +2359,7 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca Point2 cpos = p_pos; cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size; Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range); + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); } else { Point2 cpos = p_pos; double scale = _font_get_scale(p_font_rid, p_size); @@ -2451,7 +2451,7 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R Point2 cpos = p_pos; cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size; Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size; - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range); + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size); } else { Point2 cpos = p_pos; double scale = _font_get_scale(p_font_rid, p_size); @@ -2917,7 +2917,7 @@ bool TextServerFallback::_shaped_text_add_string(const RID &p_shaped, const Stri return true; } -bool TextServerFallback::_shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length) { +bool TextServerFallback::_shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, float p_baseline) { ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -2938,6 +2938,7 @@ bool TextServerFallback::_shaped_text_add_object(const RID &p_shaped, const Vari obj.inline_align = p_inline_align; obj.rect.size = p_size; obj.pos = span.start; + obj.baseline = p_baseline; sd->spans.push_back(span); sd->text = sd->text + String::chr(0xfffc).repeat(p_length); @@ -2948,7 +2949,7 @@ bool TextServerFallback::_shaped_text_add_object(const RID &p_shaped, const Vari return true; } -bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align) { +bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) { ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -2956,6 +2957,7 @@ bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const V ERR_FAIL_COND_V(!sd->objects.has(p_key), false); sd->objects[p_key].rect.size = p_size; sd->objects[p_key].inline_align = p_inline_align; + sd->objects[p_key].baseline = p_baseline; if (sd->valid) { // Recalc string metrics. sd->ascent = 0; @@ -3042,6 +3044,9 @@ void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const { case INLINE_ALIGNMENT_CENTER_TO: { E.value.rect.position.y -= E.value.rect.size.y / 2; } break; + case INLINE_ALIGNMENT_BASELINE_TO: { + E.value.rect.position.y -= E.value.baseline; + } break; case INLINE_ALIGNMENT_TOP_TO: { // NOP } break; @@ -3070,6 +3075,9 @@ void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const { case INLINE_ALIGNMENT_CENTER_TO: { E.value.rect.position.x -= E.value.rect.size.x / 2; } break; + case INLINE_ALIGNMENT_BASELINE_TO: { + E.value.rect.position.x -= E.value.baseline; + } break; case INLINE_ALIGNMENT_TOP_TO: { // NOP } break; @@ -4091,26 +4099,69 @@ String TextServerFallback::_string_to_lower(const String &p_string, const String return lower; } -PackedInt32Array TextServerFallback::_string_get_word_breaks(const String &p_string, const String &p_language) const { +PackedInt32Array TextServerFallback::_string_get_word_breaks(const String &p_string, const String &p_language, int p_chars_per_line) const { PackedInt32Array ret; + + int line_start = 0; + int line_end = 0; // End of last word on current line. + int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word. + int word_length = 0; + for (int i = 0; i < p_string.length(); i++) { - char32_t c = p_string[i]; - if (c == 0xfffc) { - continue; - } - if (is_punct(c) && c != 0x005F) { - ret.push_back(i); - continue; - } - if (is_underscore(c)) { - ret.push_back(i); - continue; - } - if (is_whitespace(c) || is_linebreak(c)) { + const char32_t c = p_string[i]; + + if (is_linebreak(c)) { + // Force newline. + ret.push_back(line_start); ret.push_back(i); + line_start = i + 1; + line_end = line_start; + word_start = line_start; + word_length = 0; + } else if (c == 0xfffc) { continue; + } else if ((is_punct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || is_whitespace(c)) { + // A whitespace ends current word. + if (word_length > 0) { + line_end = i - 1; + word_start = -1; + word_length = 0; + } + } else { + if (word_start == -1) { + word_start = i; + if (p_chars_per_line <= 0) { + ret.push_back(line_start); + ret.push_back(line_end + 1); + line_start = word_start; + line_end = line_start; + } + } + word_length += 1; + + if (p_chars_per_line > 0) { + if (word_length > p_chars_per_line) { + // Word too long: wrap before current character. + ret.push_back(line_start); + ret.push_back(i); + line_start = i; + line_end = i; + word_start = i; + word_length = 1; + } else if (i - line_start + 1 > p_chars_per_line) { + // Line too long: wrap after the last word. + ret.push_back(line_start); + ret.push_back(line_end + 1); + line_start = word_start; + line_end = line_start; + } + } } } + if (line_start < p_string.length()) { + ret.push_back(line_start); + ret.push_back(p_string.length()); + } return ret; } diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index f8a05f9433..49e89214ec 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -417,6 +417,7 @@ class TextServerFallback : public TextServerExtension { int pos = 0; InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER; Rect2 rect; + float baseline = 0; }; HashMap<Variant, EmbeddedObject, VariantHasher, VariantComparator> objects; @@ -743,8 +744,8 @@ public: MODBIND2RC(int64_t, shaped_text_get_spacing, const RID &, SpacingType); MODBIND7R(bool, shaped_text_add_string, const RID &, const String &, const TypedArray<RID> &, int64_t, const Dictionary &, const String &, const Variant &); - MODBIND5R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t); - MODBIND4R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment); + MODBIND6R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t, float); + MODBIND5R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment, float); MODBIND1RC(int64_t, shaped_get_span_count, const RID &); MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t); @@ -785,7 +786,7 @@ public: MODBIND1RC(double, shaped_text_get_underline_position, const RID &); MODBIND1RC(double, shaped_text_get_underline_thickness, const RID &); - MODBIND2RC(PackedInt32Array, string_get_word_breaks, const String &, const String &); + MODBIND3RC(PackedInt32Array, string_get_word_breaks, const String &, const String &, int); MODBIND2RC(String, string_to_upper, const String &, const String &); MODBIND2RC(String, string_to_lower, const String &, const String &); diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml index f3ddaafd0e..80ef10b6a4 100644 --- a/platform/android/java/editor/src/main/AndroidManifest.xml +++ b/platform/android/java/editor/src/main/AndroidManifest.xml @@ -27,6 +27,7 @@ android:icon="@mipmap/icon" android:label="@string/godot_editor_name_string" tools:ignore="GoogleAppIndexingWarning" + android:theme="@style/GodotEditorTheme" android:requestLegacyExternalStorage="true"> <activity @@ -35,7 +36,6 @@ android:launchMode="singleTask" android:screenOrientation="userLandscape" android:exported="true" - android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" android:process=":GodotProjectManager"> <layout android:defaultHeight="@dimen/editor_default_window_height" @@ -53,8 +53,7 @@ android:process=":GodotEditor" android:launchMode="singleTask" android:screenOrientation="userLandscape" - android:exported="false" - android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"> + android:exported="false"> <layout android:defaultHeight="@dimen/editor_default_window_height" android:defaultWidth="@dimen/editor_default_window_width" /> </activity> @@ -66,8 +65,7 @@ android:process=":GodotGame" android:launchMode="singleTask" android:exported="false" - android:screenOrientation="userLandscape" - android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"> + android:screenOrientation="userLandscape"> <layout android:defaultHeight="@dimen/editor_default_window_height" android:defaultWidth="@dimen/editor_default_window_width" /> </activity> diff --git a/platform/android/java/editor/src/main/res/values/themes.xml b/platform/android/java/editor/src/main/res/values/themes.xml new file mode 100644 index 0000000000..fda04d6dc7 --- /dev/null +++ b/platform/android/java/editor/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <style name="GodotEditorTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen"> + </style> +</resources> diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 2f26497cc8..0ba86e4316 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -422,7 +422,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } private static boolean isMouseEvent(int eventSource) { - boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS); + boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & (InputDevice.SOURCE_TOUCHSCREEN | InputDevice.SOURCE_STYLUS)) == InputDevice.SOURCE_STYLUS); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE); } diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index 23b70fbc28..3f4a406116 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -227,27 +227,23 @@ void DisplayServerIOS::_window_callback(const Callable &p_callable, const Varian // MARK: Touches void DisplayServerIOS::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click) { - if (!GLOBAL_GET("debug/disable_touch")) { - Ref<InputEventScreenTouch> ev; - ev.instantiate(); - - ev->set_index(p_idx); - ev->set_pressed(p_pressed); - ev->set_position(Vector2(p_x, p_y)); - ev->set_double_tap(p_double_click); - perform_event(ev); - } + Ref<InputEventScreenTouch> ev; + ev.instantiate(); + + ev->set_index(p_idx); + ev->set_pressed(p_pressed); + ev->set_position(Vector2(p_x, p_y)); + ev->set_double_tap(p_double_click); + perform_event(ev); } void DisplayServerIOS::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) { - if (!GLOBAL_GET("debug/disable_touch")) { - Ref<InputEventScreenDrag> ev; - ev.instantiate(); - ev->set_index(p_idx); - ev->set_position(Vector2(p_x, p_y)); - ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y)); - perform_event(ev); - } + Ref<InputEventScreenDrag> ev; + ev.instantiate(); + ev->set_index(p_idx); + ev->set_position(Vector2(p_x, p_y)); + ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y)); + perform_event(ev); } void DisplayServerIOS::perform_event(const Ref<InputEvent> &p_event) { diff --git a/platform/ios/os_ios.h b/platform/ios/os_ios.h index 186efd14a8..3589dd17c9 100644 --- a/platform/ios/os_ios.h +++ b/platform/ios/os_ios.h @@ -77,6 +77,8 @@ private: CGFloat _stretch_to_ct(int p_stretch) const; String _get_default_fontname(const String &p_font_name) const; + static _FORCE_INLINE_ String get_framework_executable(const String &p_path); + void deinitialize_modules(); public: diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm index 9617627b6f..d0c367cc45 100644 --- a/platform/ios/os_ios.mm +++ b/platform/ios/os_ios.mm @@ -198,8 +198,31 @@ void OS_IOS::finalize() { // MARK: Dynamic Libraries +_FORCE_INLINE_ String OS_IOS::get_framework_executable(const String &p_path) { + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + // Read framework bundle to get executable name. + NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())]; + NSBundle *bundle = [NSBundle bundleWithURL:url]; + if (bundle) { + String exe_path = String::utf8([[bundle executablePath] UTF8String]); + if (da->file_exists(exe_path)) { + return exe_path; + } + } + + // Try default executable name (invalid framework). + if (da->dir_exists(p_path) && da->file_exists(p_path.path_join(p_path.get_file().get_basename()))) { + return p_path.path_join(p_path.get_file().get_basename()); + } + + // Not a framework, try loading as .dylib. + return p_path; +} + Error OS_IOS::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) { if (p_path.length() == 0) { + // Static xcframework. p_library_handle = RTLD_SELF; if (r_resolved_path != nullptr) { @@ -208,7 +231,27 @@ Error OS_IOS::open_dynamic_library(const String p_path, void *&p_library_handle, return OK; } - return OS_Unix::open_dynamic_library(p_path, p_library_handle, p_also_set_library_path, r_resolved_path); + + String path = get_framework_executable(p_path); + + if (!FileAccess::exists(path)) { + // Load .dylib or framework from within the executable path. + path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file())); + } + + if (!FileAccess::exists(path)) { + // Load .dylib or framework from a standard iOS location. + path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file())); + } + + p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); + ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + "."); + + if (r_resolved_path != nullptr) { + *r_resolved_path = path; + } + + return OK; } Error OS_IOS::close_dynamic_library(void *p_library_handle) { diff --git a/platform/linuxbsd/tts_linux.cpp b/platform/linuxbsd/tts_linux.cpp index aea1183d3d..8fa708aad6 100644 --- a/platform/linuxbsd/tts_linux.cpp +++ b/platform/linuxbsd/tts_linux.cpp @@ -117,13 +117,12 @@ void TTS_Linux::speech_event_callback(size_t p_msg_id, size_t p_client_id, SPDNo free_spd_voices(voices); } PackedInt32Array breaks = TS->string_get_word_breaks(message.text, language); - int prev = 0; - for (int i = 0; i < breaks.size(); i++) { - text += message.text.substr(prev, breaks[i] - prev); - text += "<mark name=\"" + String::num_int64(breaks[i], 10) + "\"/>"; - prev = breaks[i]; + for (int i = 0; i < breaks.size(); i += 2) { + const int start = breaks[i]; + const int end = breaks[i + 1]; + text += message.text.substr(start, end - start + 1); + text += "<mark name=\"" + String::num_int64(end, 10) + "\"/>"; } - text += message.text.substr(prev, -1); spd_set_synthesis_voice(tts->synth, message.voice.utf8().get_data()); spd_set_volume(tts->synth, message.volume * 2 - 100); diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 1c5c8b2d19..57f125a754 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -3985,29 +3985,41 @@ void DisplayServerX11::process_events() { } else { DEBUG_LOG_X11("[%u] ButtonRelease window=%lu (%u), button_index=%u \n", frame, event.xbutton.window, window_id, mb->get_button_index()); - if (!wd.focused) { + WindowID window_id_other = INVALID_WINDOW_ID; + Window wd_other_x11_window; + if (wd.focused) { + // Handle cases where an unfocused popup is open that needs to receive button-up events. + WindowID popup_id = _get_focused_window_or_popup(); + if (popup_id != INVALID_WINDOW_ID && popup_id != window_id) { + window_id_other = popup_id; + wd_other_x11_window = windows[popup_id].x11_window; + } + } else { // Propagate the event to the focused window, // because it's received only on the topmost window. // Note: This is needed for drag & drop to work between windows, // because the engine expects events to keep being processed // on the same window dragging started. for (const KeyValue<WindowID, WindowData> &E : windows) { - const WindowData &wd_other = E.value; - WindowID window_id_other = E.key; - if (wd_other.focused) { - if (window_id_other != window_id) { - int x, y; - Window child; - XTranslateCoordinates(x11_display, wd.x11_window, wd_other.x11_window, event.xbutton.x, event.xbutton.y, &x, &y, &child); - - mb->set_window_id(window_id_other); - mb->set_position(Vector2(x, y)); - mb->set_global_position(mb->get_position()); + if (E.value.focused) { + if (E.key != window_id) { + window_id_other = E.key; + wd_other_x11_window = E.value.x11_window; } break; } } } + + if (window_id_other != INVALID_WINDOW_ID) { + int x, y; + Window child; + XTranslateCoordinates(x11_display, wd.x11_window, wd_other_x11_window, event.xbutton.x, event.xbutton.y, &x, &y, &child); + + mb->set_window_id(window_id_other); + mb->set_position(Vector2(x, y)); + mb->set_global_position(mb->get_position()); + } } Input::get_singleton()->parse_input_event(mb); diff --git a/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c index d689ff1aa8..7042a60d47 100644 --- a/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c @@ -5,7 +5,7 @@ // // NOTE: Generated from Xcursor 1.2.0. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11, but absent in libXcursor.so.1, were removed. #include <stdint.h> diff --git a/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h index 43bbcf62c5..d00fccffda 100644 --- a/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h @@ -7,7 +7,7 @@ // // NOTE: Generated from Xcursor 1.2.0. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11, but absent in libXcursor.so.1, were removed. #include <stdint.h> diff --git a/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c index 711dd3fa5e..c8e87a6b85 100644 --- a/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c @@ -5,10 +5,10 @@ // // NOTE: Generated from Xext 1.3.5. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11, but absent in libXext.so.6, were removed and an include needed for // proper parsing was added (this had also to be temporarily added to the -// original header, as dynload-wrapper would complain otherwsise) +// original header, as dynload-wrapper would complain otherwise) #include <stdint.h> // HANDPATCH: Needed for a successful compilation. diff --git a/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h index 991d07b405..aee92b593e 100644 --- a/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h @@ -7,10 +7,10 @@ // // NOTE: Generated from Xext 1.3.5. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11, but absent in libXext.so.6, were removed and an include needed for // proper parsing was added (this had also to be temporarily added to the -// original header, as dynload-wrapper would complain otherwsise) +// original header, as dynload-wrapper would complain otherwise) #include <stdint.h> // HANDPATCH: Needed for a successful compilation. diff --git a/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c index 42af983345..85ac80e3f2 100644 --- a/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c @@ -5,7 +5,7 @@ // // NOTE: Generated from Xinerama 1.1.4. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11, but absent in libXinerama.so.1, were removed. #include <stdint.h> diff --git a/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h index 891d9f21fd..9139421cd6 100644 --- a/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h @@ -7,7 +7,7 @@ // // NOTE: Generated from Xinerama 1.1.4. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11, but absent in libXinerama.so.1, were removed. #include <stdint.h> diff --git a/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c index 5e1f0999fc..5f16bc6111 100644 --- a/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c @@ -5,7 +5,7 @@ // // NOTE: Generated from Xi 1.7.10. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11, liXext and libXfixes, but absent in libXi.so.6, were removed. #include <stdint.h> diff --git a/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h index 95740cee58..ecb7aa5048 100644 --- a/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h @@ -7,7 +7,7 @@ // // NOTE: Generated from Xi 1.7.10. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11, liXext and libXfixes, but absent in libXi.so.6, were removed. #include <stdint.h> diff --git a/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c index eb0f9abf15..f37f3a9db0 100644 --- a/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c @@ -5,7 +5,7 @@ // // NOTE: Generated from Xrandr 1.5.2. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11 and libXrender, but absent in libXrandr.so.2, were removed. #include <stdint.h> diff --git a/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h index f1ca9f94a5..046d4c7de3 100644 --- a/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h @@ -7,7 +7,7 @@ // // NOTE: Generated from Xrandr 1.5.2. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11 and libXrender, but absent in libXrandr.so.2, were removed. #include <stdint.h> diff --git a/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c index b63a1eca8d..2d3847e584 100644 --- a/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c @@ -5,7 +5,7 @@ // // NOTE: Generated from Xrender 0.9.10. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11, but absent in libXrender.so.1, were removed. #include <stdint.h> diff --git a/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h index d3862ed459..e873448ec5 100644 --- a/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h @@ -7,7 +7,7 @@ // // NOTE: Generated from Xrender 0.9.10. // This has been handpatched to workaround some issues with the generator that -// will be eventually fixed. In this case, non-existant symbols inherited from +// will be eventually fixed. In this case, non-existent symbols inherited from // libX11, but absent in libXrender.so.1, were removed. #include <stdint.h> diff --git a/platform/macos/export/plist.cpp b/platform/macos/export/plist.cpp index 82ecd41d9c..c88b6e1077 100644 --- a/platform/macos/export/plist.cpp +++ b/platform/macos/export/plist.cpp @@ -30,6 +30,119 @@ #include "plist.h" +PList::PLNodeType PListNode::get_type() const { + return data_type; +} + +Variant PListNode::get_value() const { + switch (data_type) { + case PList::PL_NODE_TYPE_NIL: { + return Variant(); + } break; + case PList::PL_NODE_TYPE_STRING: { + return String::utf8(data_string.get_data()); + } break; + case PList::PL_NODE_TYPE_ARRAY: { + Array arr; + for (const Ref<PListNode> &E : data_array) { + arr.push_back(E); + } + return arr; + } break; + case PList::PL_NODE_TYPE_DICT: { + Dictionary dict; + for (const KeyValue<String, Ref<PListNode>> &E : data_dict) { + dict[E.key] = E.value; + } + return dict; + } break; + case PList::PL_NODE_TYPE_BOOLEAN: { + return data_bool; + } break; + case PList::PL_NODE_TYPE_INTEGER: { + return data_int; + } break; + case PList::PL_NODE_TYPE_REAL: { + return data_real; + } break; + case PList::PL_NODE_TYPE_DATA: { + int strlen = data_string.length(); + + size_t arr_len = 0; + Vector<uint8_t> buf; + { + buf.resize(strlen / 4 * 3 + 1); + uint8_t *w = buf.ptrw(); + + ERR_FAIL_COND_V(CryptoCore::b64_decode(&w[0], buf.size(), &arr_len, (unsigned char *)data_string.get_data(), strlen) != OK, Vector<uint8_t>()); + } + buf.resize(arr_len); + return buf; + } break; + case PList::PL_NODE_TYPE_DATE: { + return String(data_string.get_data()); + } break; + } + return Variant(); +} + +Ref<PListNode> PListNode::new_node(const Variant &p_value) { + Ref<PListNode> node; + node.instantiate(); + + switch (p_value.get_type()) { + case Variant::NIL: { + node->data_type = PList::PL_NODE_TYPE_NIL; + } break; + case Variant::BOOL: { + node->data_type = PList::PL_NODE_TYPE_BOOLEAN; + node->data_bool = p_value; + } break; + case Variant::INT: { + node->data_type = PList::PL_NODE_TYPE_INTEGER; + node->data_int = p_value; + } break; + case Variant::FLOAT: { + node->data_type = PList::PL_NODE_TYPE_REAL; + node->data_real = p_value; + } break; + case Variant::STRING_NAME: + case Variant::STRING: { + node->data_type = PList::PL_NODE_TYPE_STRING; + node->data_string = p_value.operator String().utf8(); + } break; + case Variant::DICTIONARY: { + node->data_type = PList::PL_NODE_TYPE_DICT; + Dictionary dict = p_value; + const Variant *next = dict.next(nullptr); + while (next) { + Ref<PListNode> sub_node = dict[*next]; + ERR_FAIL_COND_V_MSG(sub_node.is_null(), Ref<PListNode>(), "Invalid dictionary element, should be PListNode."); + node->data_dict[*next] = sub_node; + next = dict.next(next); + } + } break; + case Variant::ARRAY: { + node->data_type = PList::PL_NODE_TYPE_ARRAY; + Array ar = p_value; + for (int i = 0; i < ar.size(); i++) { + Ref<PListNode> sub_node = ar[i]; + ERR_FAIL_COND_V_MSG(sub_node.is_null(), Ref<PListNode>(), "Invalid array element, should be PListNode."); + node->data_array.push_back(sub_node); + } + } break; + case Variant::PACKED_BYTE_ARRAY: { + node->data_type = PList::PL_NODE_TYPE_DATA; + PackedByteArray buf = p_value; + node->data_string = CryptoCore::b64_encode_str(buf.ptr(), buf.size()).utf8(); + } break; + default: { + ERR_FAIL_V_MSG(Ref<PListNode>(), "Unsupported data type."); + } break; + } + return node; +} + Ref<PListNode> PListNode::new_array() { Ref<PListNode> node = memnew(PListNode()); ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); @@ -65,6 +178,7 @@ Ref<PListNode> PListNode::new_date(const String &p_string) { ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATE; node->data_string = p_string.utf8(); + node->data_real = (double)Time::get_singleton()->get_unix_time_from_datetime_string(p_string) - 978307200.0; return node; } @@ -76,7 +190,7 @@ Ref<PListNode> PListNode::new_bool(bool p_bool) { return node; } -Ref<PListNode> PListNode::new_int(int32_t p_int) { +Ref<PListNode> PListNode::new_int(int64_t p_int) { Ref<PListNode> node = memnew(PListNode()); ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); node->data_type = PList::PLNodeType::PL_NODE_TYPE_INTEGER; @@ -84,7 +198,7 @@ Ref<PListNode> PListNode::new_int(int32_t p_int) { return node; } -Ref<PListNode> PListNode::new_real(float p_real) { +Ref<PListNode> PListNode::new_real(double p_real) { Ref<PListNode> node = memnew(PListNode()); ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); node->data_type = PList::PLNodeType::PL_NODE_TYPE_REAL; @@ -337,6 +451,168 @@ PList::PList(const String &p_string) { load_string(p_string); } +uint64_t PList::read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size) { + uint64_t pos = p_file->get_position(); + uint64_t ret = 0; + switch (p_size) { + case 1: { + ret = p_file->get_8(); + } break; + case 2: { + ret = BSWAP16(p_file->get_16()); + } break; + case 3: { + ret = BSWAP32(p_file->get_32() & 0x00FFFFFF); + } break; + case 4: { + ret = BSWAP32(p_file->get_32()); + } break; + case 5: { + ret = BSWAP64(p_file->get_64() & 0x000000FFFFFFFFFF); + } break; + case 6: { + ret = BSWAP64(p_file->get_64() & 0x0000FFFFFFFFFFFF); + } break; + case 7: { + ret = BSWAP64(p_file->get_64() & 0x00FFFFFFFFFFFFFF); + } break; + case 8: { + ret = BSWAP64(p_file->get_64()); + } break; + default: { + ret = 0; + } + } + p_file->seek(pos + p_size); + + return ret; +} + +Ref<PListNode> PList::read_bplist_obj(Ref<FileAccess> p_file, uint64_t p_offset_idx) { + Ref<PListNode> node; + node.instantiate(); + + uint64_t ot_off = trailer.offset_table_start + p_offset_idx * trailer.offset_size; + p_file->seek(ot_off); + uint64_t marker_off = read_bplist_var_size_int(p_file, trailer.offset_size); + ERR_FAIL_COND_V_MSG(marker_off == 0, Ref<PListNode>(), "Invalid marker size."); + + p_file->seek(marker_off); + uint8_t marker = p_file->get_8(); + uint8_t marker_type = marker & 0xF0; + uint64_t marker_size = marker & 0x0F; + + switch (marker_type) { + case 0x00: { + if (marker_size == 0x00) { + node->data_type = PL_NODE_TYPE_NIL; + } else if (marker_size == 0x08) { + node->data_type = PL_NODE_TYPE_BOOLEAN; + node->data_bool = false; + } else if (marker_size == 0x09) { + node->data_type = PL_NODE_TYPE_BOOLEAN; + node->data_bool = true; + } else { + ERR_FAIL_V_MSG(Ref<PListNode>(), "Invalid nil/bool marker value."); + } + } break; + case 0x10: { + node->data_type = PL_NODE_TYPE_INTEGER; + node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, pow(2, marker_size))); + } break; + case 0x20: { + node->data_type = PL_NODE_TYPE_REAL; + node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, pow(2, marker_size))); + } break; + case 0x30: { + node->data_type = PL_NODE_TYPE_DATE; + node->data_int = BSWAP64(p_file->get_64()); + node->data_string = Time::get_singleton()->get_datetime_string_from_unix_time(node->data_real + 978307200.0).utf8(); + } break; + case 0x40: { + if (marker_size == 0x0F) { + uint8_t ext = p_file->get_8() & 0xF; + marker_size = read_bplist_var_size_int(p_file, pow(2, ext)); + } + node->data_type = PL_NODE_TYPE_DATA; + PackedByteArray buf; + buf.resize(marker_size + 1); + p_file->get_buffer(reinterpret_cast<uint8_t *>(buf.ptrw()), marker_size); + node->data_string = CryptoCore::b64_encode_str(buf.ptr(), buf.size()).utf8(); + } break; + case 0x50: { + if (marker_size == 0x0F) { + uint8_t ext = p_file->get_8() & 0xF; + marker_size = read_bplist_var_size_int(p_file, pow(2, ext)); + } + node->data_type = PL_NODE_TYPE_STRING; + node->data_string.resize(marker_size + 1); + p_file->get_buffer(reinterpret_cast<uint8_t *>(node->data_string.ptrw()), marker_size); + } break; + case 0x60: { + if (marker_size == 0x0F) { + uint8_t ext = p_file->get_8() & 0xF; + marker_size = read_bplist_var_size_int(p_file, pow(2, ext)); + } + Char16String cs16; + cs16.resize(marker_size + 1); + for (uint64_t i = 0; i < marker_size; i++) { + cs16[i] = BSWAP16(p_file->get_16()); + } + node->data_type = PL_NODE_TYPE_STRING; + node->data_string = String::utf16(cs16.ptr(), cs16.length()).utf8(); + } break; + case 0x80: { + node->data_type = PL_NODE_TYPE_INTEGER; + node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, marker_size + 1)); + } break; + case 0xA0: + case 0xC0: { + if (marker_size == 0x0F) { + uint8_t ext = p_file->get_8() & 0xF; + marker_size = read_bplist_var_size_int(p_file, pow(2, ext)); + } + uint64_t pos = p_file->get_position(); + + node->data_type = PL_NODE_TYPE_ARRAY; + for (uint64_t i = 0; i < marker_size; i++) { + p_file->seek(pos + trailer.ref_size * i); + uint64_t ref = read_bplist_var_size_int(p_file, trailer.ref_size); + + Ref<PListNode> element = read_bplist_obj(p_file, ref); + ERR_FAIL_COND_V(element.is_null(), Ref<PListNode>()); + node->data_array.push_back(element); + } + } break; + case 0xD0: { + if (marker_size == 0x0F) { + uint8_t ext = p_file->get_8() & 0xF; + marker_size = read_bplist_var_size_int(p_file, pow(2, ext)); + } + uint64_t pos = p_file->get_position(); + + node->data_type = PL_NODE_TYPE_DICT; + for (uint64_t i = 0; i < marker_size; i++) { + p_file->seek(pos + trailer.ref_size * i); + uint64_t key_ref = read_bplist_var_size_int(p_file, trailer.ref_size); + + p_file->seek(pos + trailer.ref_size * (i + marker_size)); + uint64_t obj_ref = read_bplist_var_size_int(p_file, trailer.ref_size); + + Ref<PListNode> element_key = read_bplist_obj(p_file, key_ref); + ERR_FAIL_COND_V(element_key.is_null() || element_key->data_type != PL_NODE_TYPE_STRING, Ref<PListNode>()); + Ref<PListNode> element = read_bplist_obj(p_file, obj_ref); + ERR_FAIL_COND_V(element.is_null(), Ref<PListNode>()); + node->data_dict[String::utf8(element_key->data_string.ptr(), element_key->data_string.length())] = element; + } + } break; + default: { + ERR_FAIL_V_MSG(Ref<PListNode>(), "Invalid marker type."); + } + } + return node; +} + bool PList::load_file(const String &p_filename) { root = Ref<PListNode>(); @@ -349,7 +625,15 @@ bool PList::load_file(const String &p_filename) { fb->get_buffer(magic, 8); if (String((const char *)magic, 8) == "bplist00") { - ERR_FAIL_V_MSG(false, "PList: Binary property lists are not supported."); + fb->seek_end(-26); + trailer.offset_size = fb->get_8(); + trailer.ref_size = fb->get_8(); + trailer.object_num = BSWAP64(fb->get_64()); + trailer.root_offset_idx = BSWAP64(fb->get_64()); + trailer.offset_table_start = BSWAP64(fb->get_64()); + root = read_bplist_obj(fb, trailer.root_offset_idx); + + return root.is_valid(); } else { // Load text plist. Error err; diff --git a/platform/macos/export/plist.h b/platform/macos/export/plist.h index 97331a3629..d53e88832f 100644 --- a/platform/macos/export/plist.h +++ b/platform/macos/export/plist.h @@ -35,6 +35,7 @@ #include "core/crypto/crypto_core.h" #include "core/io/file_access.h" +#include "core/os/time.h" class PListNode; @@ -55,8 +56,20 @@ public: }; private: + struct PListTrailer { + uint8_t offset_size; + uint8_t ref_size; + uint64_t object_num; + uint64_t root_offset_idx; + uint64_t offset_table_start; + }; + + PListTrailer trailer; Ref<PListNode> root; + uint64_t read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size); + Ref<PListNode> read_bplist_obj(Ref<FileAccess> p_file, uint64_t p_offset_idx); + public: PList(); PList(const String &p_string); @@ -82,19 +95,23 @@ public: Vector<Ref<PListNode>> data_array; HashMap<String, Ref<PListNode>> data_dict; union { - int32_t data_int; + int64_t data_int; bool data_bool; - float data_real; + double data_real; }; + PList::PLNodeType get_type() const; + Variant get_value() const; + + static Ref<PListNode> new_node(const Variant &p_value); static Ref<PListNode> new_array(); static Ref<PListNode> new_dict(); static Ref<PListNode> new_string(const String &p_string); static Ref<PListNode> new_data(const String &p_string); static Ref<PListNode> new_date(const String &p_string); static Ref<PListNode> new_bool(bool p_bool); - static Ref<PListNode> new_int(int32_t p_int); - static Ref<PListNode> new_real(float p_real); + static Ref<PListNode> new_int(int64_t p_int); + static Ref<PListNode> new_real(double p_real); bool push_subnode(const Ref<PListNode> &p_node, const String &p_key = ""); diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index 8f2f9f6cf5..fd8ba38808 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -45,16 +45,6 @@ #include <os/log.h> #include <sys/sysctl.h> -_FORCE_INLINE_ String OS_MacOS::get_framework_executable(const String &p_path) { - // Append framework executable name, or return as is if p_path is not a framework. - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (da->dir_exists(p_path) && da->file_exists(p_path.path_join(p_path.get_file().get_basename()))) { - return p_path.path_join(p_path.get_file().get_basename()); - } else { - return p_path; - } -} - void OS_MacOS::pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context) { // Prevent main loop from sleeping and redraw window during modal popup display. // Do not redraw when rendering is done from the separate thread, it will conflict with the OpenGL context updates. @@ -162,6 +152,28 @@ void OS_MacOS::alert(const String &p_alert, const String &p_title) { } } +_FORCE_INLINE_ String OS_MacOS::get_framework_executable(const String &p_path) { + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + // Read framework bundle to get executable name. + NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())]; + NSBundle *bundle = [NSBundle bundleWithURL:url]; + if (bundle) { + String exe_path = String::utf8([[bundle executablePath] UTF8String]); + if (da->file_exists(exe_path)) { + return exe_path; + } + } + + // Try default executable name (invalid framework). + if (da->dir_exists(p_path) && da->file_exists(p_path.path_join(p_path.get_file().get_basename()))) { + return p_path.path_join(p_path.get_file().get_basename()); + } + + // Not a framework, try loading as .dylib. + return p_path; +} + Error OS_MacOS::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) { String path = get_framework_executable(p_path); diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index f04177d79a..89f56ffd93 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -226,7 +226,7 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/modify_resources"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/console_wrapper_icon", PROPERTY_HINT_FILE, "*.ico.*.png,*.webp,*.svg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/console_wrapper_icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "")); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index a083a98c72..f8633d29ac 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1164,8 +1164,11 @@ bool OS_Windows::set_environment(const String &p_var, const String &p_value) con String OS_Windows::get_stdin_string(bool p_block) { if (p_block) { - char buff[1024]; - return fgets(buff, 1024, stdin); + WCHAR buff[1024]; + DWORD count = 0; + if (ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), buff, 1024, &count, nullptr)) { + return String::utf16((const char16_t *)buff, count); + } } return String(); diff --git a/platform/windows/platform_windows_builders.py b/platform/windows/platform_windows_builders.py index 33ca2e8ffa..b522a75a9c 100644 --- a/platform/windows/platform_windows_builders.py +++ b/platform/windows/platform_windows_builders.py @@ -4,18 +4,15 @@ All such functions are invoked in a subprocess on Windows to prevent build flaki """ import os +from detect import get_mingw_bin_prefix from platform_methods import subprocess_main def make_debug_mingw(target, source, env): - mingw_prefix = "" - if env["arch"] == "x86_32": - mingw_prefix = env["mingw_prefix_32"] - else: - mingw_prefix = env["mingw_prefix_64"] - os.system(mingw_prefix + "objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0])) - os.system(mingw_prefix + "strip --strip-debug --strip-unneeded {0}".format(target[0])) - os.system(mingw_prefix + "objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0])) + mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"]) + os.system(mingw_bin_prefix + "objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0])) + os.system(mingw_bin_prefix + "strip --strip-debug --strip-unneeded {0}".format(target[0])) + os.system(mingw_bin_prefix + "objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0])) if __name__ == "__main__": diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp index 8de4c281f4..3ec744ff8e 100644 --- a/scene/2d/joint_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -112,7 +112,7 @@ void Joint2D::_update_joint(bool p_only_free) { ERR_FAIL_COND_MSG(!joint.is_valid(), "Failed to configure the joint."); - PhysicsServer2D::get_singleton()->get_singleton()->joint_set_param(joint, PhysicsServer2D::JOINT_PARAM_BIAS, bias); + PhysicsServer2D::get_singleton()->joint_set_param(joint, PhysicsServer2D::JOINT_PARAM_BIAS, bias); ba = body_a->get_rid(); bb = body_b->get_rid(); @@ -188,7 +188,7 @@ void Joint2D::_notification(int p_what) { void Joint2D::set_bias(real_t p_bias) { bias = p_bias; if (joint.is_valid()) { - PhysicsServer2D::get_singleton()->get_singleton()->joint_set_param(joint, PhysicsServer2D::JOINT_PARAM_BIAS, bias); + PhysicsServer2D::get_singleton()->joint_set_param(joint, PhysicsServer2D::JOINT_PARAM_BIAS, bias); } } diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp index 68d529fd32..fa72fc5b8b 100644 --- a/scene/2d/multimesh_instance_2d.cpp +++ b/scene/2d/multimesh_instance_2d.cpp @@ -30,6 +30,7 @@ #include "multimesh_instance_2d.h" +#include "core/core_string_names.h" #include "scene/scene_string_names.h" void MultiMeshInstance2D::_notification(int p_what) { @@ -60,7 +61,16 @@ void MultiMeshInstance2D::_bind_methods() { } void MultiMeshInstance2D::set_multimesh(const Ref<MultiMesh> &p_multimesh) { + // Cleanup previous connection if any. + if (multimesh.is_valid()) { + multimesh->disconnect(CoreStringNames::get_singleton()->changed, callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + } multimesh = p_multimesh; + + // Connect to the multimesh so the AABB can update when instance transforms are changed. + if (multimesh.is_valid()) { + multimesh->connect(CoreStringNames::get_singleton()->changed, callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); + } queue_redraw(); } diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 904b6564bd..62afe0d89b 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -70,6 +70,9 @@ void NavigationAgent2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationAgent2D::set_navigation_layer_value); ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationAgent2D::get_navigation_layer_value); + ClassDB::bind_method(D_METHOD("set_path_metadata_flags", "flags"), &NavigationAgent2D::set_path_metadata_flags); + ClassDB::bind_method(D_METHOD("get_path_metadata_flags"), &NavigationAgent2D::get_path_metadata_flags); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent2D::set_navigation_map); ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent2D::get_navigation_map); @@ -79,8 +82,9 @@ void NavigationAgent2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent2D::get_next_location); ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target); ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent2D::set_velocity); - ClassDB::bind_method(D_METHOD("get_nav_path"), &NavigationAgent2D::get_nav_path); - ClassDB::bind_method(D_METHOD("get_nav_path_index"), &NavigationAgent2D::get_nav_path_index); + ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent2D::get_current_navigation_result); + ClassDB::bind_method(D_METHOD("get_current_navigation_path"), &NavigationAgent2D::get_current_navigation_path); + ClassDB::bind_method(D_METHOD("get_current_navigation_path_index"), &NavigationAgent2D::get_current_navigation_path_index); ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent2D::is_target_reached); ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent2D::is_target_reachable); ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent2D::is_navigation_finished); @@ -94,6 +98,7 @@ void NavigationAgent2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:px"), "set_target_desired_distance", "get_target_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1,suffix:px"), "set_path_max_distance", "get_path_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags"); ADD_GROUP("Avoidance", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); @@ -105,6 +110,8 @@ void NavigationAgent2D::_bind_methods() { ADD_SIGNAL(MethodInfo("path_changed")); ADD_SIGNAL(MethodInfo("target_reached")); + ADD_SIGNAL(MethodInfo("waypoint_reached", PropertyInfo(Variant::DICTIONARY, "details"))); + ADD_SIGNAL(MethodInfo("link_reached", PropertyInfo(Variant::DICTIONARY, "details"))); ADD_SIGNAL(MethodInfo("navigation_finished")); ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR2, "safe_velocity"))); } @@ -161,7 +168,7 @@ void NavigationAgent2D::_notification(int p_what) { } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - if (agent_parent) { + if (agent_parent && target_position_submitted) { if (avoidance_enabled) { // agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding // no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used @@ -256,6 +263,14 @@ bool NavigationAgent2D::get_navigation_layer_value(int p_layer_number) const { return get_navigation_layers() & (1 << (p_layer_number - 1)); } +void NavigationAgent2D::set_path_metadata_flags(BitField<NavigationPathQueryParameters2D::PathMetadataFlags> p_path_metadata_flags) { + if (path_metadata_flags == p_path_metadata_flags) { + return; + } + + path_metadata_flags = p_path_metadata_flags; +} + void NavigationAgent2D::set_navigation_map(RID p_navigation_map) { map_override = p_navigation_map; NavigationServer2D::get_singleton()->agent_set_map(agent, map_override); @@ -314,6 +329,7 @@ real_t NavigationAgent2D::get_path_max_distance() { void NavigationAgent2D::set_target_location(Vector2 p_location) { target_location = p_location; + target_position_submitted = true; _request_repath(); } @@ -329,14 +345,10 @@ Vector2 NavigationAgent2D::get_next_location() { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector2(), "The agent has no parent."); return agent_parent->get_global_position(); } else { - return navigation_path[nav_path_index]; + return navigation_path[navigation_path_index]; } } -const Vector<Vector2> &NavigationAgent2D::get_nav_path() const { - return navigation_result->get_path(); -} - real_t NavigationAgent2D::distance_to_target() const { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent."); return agent_parent->get_global_position().distance_to(target_location); @@ -402,6 +414,9 @@ void NavigationAgent2D::update_navigation() { if (!agent_parent->is_inside_tree()) { return; } + if (!target_position_submitted) { + return; + } if (update_frame_id == Engine::get_singleton()->get_physics_frames()) { return; } @@ -418,12 +433,12 @@ void NavigationAgent2D::update_navigation() { reload_path = true; } else { // Check if too far from the navigation path - if (nav_path_index > 0) { + if (navigation_path_index > 0) { const Vector<Vector2> &navigation_path = navigation_result->get_path(); Vector2 segment[2]; - segment[0] = navigation_path[nav_path_index - 1]; - segment[1] = navigation_path[nav_path_index]; + segment[0] = navigation_path[navigation_path_index - 1]; + segment[1] = navigation_path[navigation_path_index]; Vector2 p = Geometry2D::get_closest_point_to_segment(origin, segment); if (origin.distance_to(p) >= path_max_distance) { // To faraway, reload path @@ -436,6 +451,7 @@ void NavigationAgent2D::update_navigation() { navigation_query->set_start_position(origin); navigation_query->set_target_position(target_location); navigation_query->set_navigation_layers(navigation_layers); + navigation_query->set_metadata_flags(path_metadata_flags); if (map_override.is_valid()) { navigation_query->set_map(map_override); @@ -445,7 +461,7 @@ void NavigationAgent2D::update_navigation() { NavigationServer2D::get_singleton()->query_path(navigation_query, navigation_result); navigation_finished = false; - nav_path_index = 0; + navigation_path_index = 0; emit_signal(SNAME("path_changed")); } @@ -457,12 +473,57 @@ void NavigationAgent2D::update_navigation() { if (navigation_finished == false) { // Advances to the next far away location. const Vector<Vector2> &navigation_path = navigation_result->get_path(); - while (origin.distance_to(navigation_path[nav_path_index]) < path_desired_distance) { - nav_path_index += 1; - if (nav_path_index == navigation_path.size()) { + const Vector<int32_t> &navigation_path_types = navigation_result->get_path_types(); + const TypedArray<RID> &navigation_path_rids = navigation_result->get_path_rids(); + const Vector<int64_t> &navigation_path_owners = navigation_result->get_path_owner_ids(); + + while (origin.distance_to(navigation_path[navigation_path_index]) < path_desired_distance) { + Dictionary details; + + const Vector2 waypoint = navigation_path[navigation_path_index]; + details[SNAME("location")] = waypoint; + + int waypoint_type = -1; + if (path_metadata_flags.has_flag(NavigationPathQueryParameters2D::PathMetadataFlags::PATH_METADATA_INCLUDE_TYPES)) { + const NavigationPathQueryResult2D::PathSegmentType type = NavigationPathQueryResult2D::PathSegmentType(navigation_path_types[navigation_path_index]); + + details[SNAME("type")] = type; + waypoint_type = type; + } + + if (path_metadata_flags.has_flag(NavigationPathQueryParameters2D::PathMetadataFlags::PATH_METADATA_INCLUDE_RIDS)) { + details[SNAME("rid")] = navigation_path_rids[navigation_path_index]; + } + + if (path_metadata_flags.has_flag(NavigationPathQueryParameters2D::PathMetadataFlags::PATH_METADATA_INCLUDE_OWNERS)) { + const ObjectID waypoint_owner_id = ObjectID(navigation_path_owners[navigation_path_index]); + + // Get a reference to the owning object. + Object *owner = nullptr; + if (waypoint_owner_id.is_valid()) { + owner = ObjectDB::get_instance(waypoint_owner_id); + } + + details[SNAME("owner")] = owner; + } + + // Emit a signal for the waypoint + emit_signal(SNAME("waypoint_reached"), details); + + // Emit a signal if we've reached a navigation link + if (waypoint_type == NavigationPathQueryResult2D::PATH_SEGMENT_TYPE_LINK) { + emit_signal(SNAME("link_reached"), details); + } + + // Move to the next waypoint on the list + navigation_path_index += 1; + + // Check to see if we've finished our route + if (navigation_path_index == navigation_path.size()) { _check_distance_to_target(); - nav_path_index -= 1; + navigation_path_index -= 1; navigation_finished = true; + target_position_submitted = false; emit_signal(SNAME("navigation_finished")); break; } diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 5abd3c0317..439192cce7 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -32,10 +32,10 @@ #define NAVIGATION_AGENT_2D_H #include "scene/main/node.h" +#include "servers/navigation/navigation_path_query_parameters_2d.h" +#include "servers/navigation/navigation_path_query_result_2d.h" class Node2D; -class NavigationPathQueryParameters2D; -class NavigationPathQueryResult2D; class NavigationAgent2D : public Node { GDCLASS(NavigationAgent2D, Node); @@ -48,6 +48,7 @@ class NavigationAgent2D : public Node { bool avoidance_enabled = false; uint32_t navigation_layers = 1; + BitField<NavigationPathQueryParameters2D::PathMetadataFlags> path_metadata_flags = NavigationPathQueryParameters2D::PathMetadataFlags::PATH_METADATA_INCLUDE_ALL; real_t path_desired_distance = 1.0; real_t target_desired_distance = 1.0; @@ -60,9 +61,10 @@ class NavigationAgent2D : public Node { real_t path_max_distance = 3.0; Vector2 target_location; + bool target_position_submitted = false; Ref<NavigationPathQueryParameters2D> navigation_query; Ref<NavigationPathQueryResult2D> navigation_result; - int nav_path_index = 0; + int navigation_path_index = 0; bool velocity_submitted = false; Vector2 prev_safe_velocity; /// The submitted target velocity @@ -95,6 +97,11 @@ public: void set_navigation_layer_value(int p_layer_number, bool p_value); bool get_navigation_layer_value(int p_layer_number) const; + void set_path_metadata_flags(BitField<NavigationPathQueryParameters2D::PathMetadataFlags> p_flags); + BitField<NavigationPathQueryParameters2D::PathMetadataFlags> get_path_metadata_flags() const { + return path_metadata_flags; + } + void set_navigation_map(RID p_navigation_map); RID get_navigation_map() const; @@ -141,10 +148,14 @@ public: Vector2 get_next_location(); - const Vector<Vector2> &get_nav_path() const; - - int get_nav_path_index() const { - return nav_path_index; + Ref<NavigationPathQueryResult2D> get_current_navigation_result() const { + return navigation_result; + } + const Vector<Vector2> &get_current_navigation_path() const { + return navigation_result->get_path(); + } + int get_current_navigation_path_index() const { + return navigation_path_index; } real_t distance_to_target() const; diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 7bf3eec79b..675ef7c780 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -31,333 +31,10 @@ #include "navigation_region_2d.h" #include "core/core_string_names.h" -#include "core/math/geometry_2d.h" -#include "core/os/mutex.h" #include "scene/resources/world_2d.h" #include "servers/navigation_server_2d.h" #include "servers/navigation_server_3d.h" -#include "thirdparty/misc/polypartition.h" - -#ifdef TOOLS_ENABLED -Rect2 NavigationPolygon::_edit_get_rect() const { - if (rect_cache_dirty) { - item_rect = Rect2(); - bool first = true; - - for (int i = 0; i < outlines.size(); i++) { - const Vector<Vector2> &outline = outlines[i]; - const int outline_size = outline.size(); - if (outline_size < 3) { - continue; - } - const Vector2 *p = outline.ptr(); - for (int j = 0; j < outline_size; j++) { - if (first) { - item_rect = Rect2(p[j], Vector2(0, 0)); - first = false; - } else { - item_rect.expand_to(p[j]); - } - } - } - - rect_cache_dirty = false; - } - return item_rect; -} - -bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - for (int i = 0; i < outlines.size(); i++) { - const Vector<Vector2> &outline = outlines[i]; - const int outline_size = outline.size(); - if (outline_size < 3) { - continue; - } - if (Geometry2D::is_point_in_polygon(p_point, Variant(outline))) { - return true; - } - } - return false; -} -#endif - -void NavigationPolygon::set_vertices(const Vector<Vector2> &p_vertices) { - { - MutexLock lock(navmesh_generation); - navmesh.unref(); - } - vertices = p_vertices; - rect_cache_dirty = true; -} - -Vector<Vector2> NavigationPolygon::get_vertices() const { - return vertices; -} - -void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array) { - { - MutexLock lock(navmesh_generation); - navmesh.unref(); - } - polygons.resize(p_array.size()); - for (int i = 0; i < p_array.size(); i++) { - polygons.write[i].indices = p_array[i]; - } -} - -TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const { - TypedArray<Vector<int32_t>> ret; - ret.resize(polygons.size()); - for (int i = 0; i < ret.size(); i++) { - ret[i] = polygons[i].indices; - } - - return ret; -} - -void NavigationPolygon::_set_outlines(const TypedArray<Vector<Vector2>> &p_array) { - outlines.resize(p_array.size()); - for (int i = 0; i < p_array.size(); i++) { - outlines.write[i] = p_array[i]; - } - rect_cache_dirty = true; -} - -TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const { - TypedArray<Vector<Vector2>> ret; - ret.resize(outlines.size()); - for (int i = 0; i < ret.size(); i++) { - ret[i] = outlines[i]; - } - - return ret; -} - -void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) { - Polygon polygon; - polygon.indices = p_polygon; - polygons.push_back(polygon); - { - MutexLock lock(navmesh_generation); - navmesh.unref(); - } -} - -void NavigationPolygon::add_outline_at_index(const Vector<Vector2> &p_outline, int p_index) { - outlines.insert(p_index, p_outline); - rect_cache_dirty = true; -} - -int NavigationPolygon::get_polygon_count() const { - return polygons.size(); -} - -Vector<int> NavigationPolygon::get_polygon(int p_idx) { - ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>()); - return polygons[p_idx].indices; -} - -void NavigationPolygon::clear_polygons() { - polygons.clear(); - { - MutexLock lock(navmesh_generation); - navmesh.unref(); - } -} - -Ref<NavigationMesh> NavigationPolygon::get_mesh() { - MutexLock lock(navmesh_generation); - - if (navmesh.is_null()) { - navmesh.instantiate(); - Vector<Vector3> verts; - { - verts.resize(get_vertices().size()); - Vector3 *w = verts.ptrw(); - - const Vector2 *r = get_vertices().ptr(); - - for (int i(0); i < get_vertices().size(); i++) { - w[i] = Vector3(r[i].x, 0.0, r[i].y); - } - } - navmesh->set_vertices(verts); - - for (int i(0); i < get_polygon_count(); i++) { - navmesh->add_polygon(get_polygon(i)); - } - } - - return navmesh; -} - -void NavigationPolygon::add_outline(const Vector<Vector2> &p_outline) { - outlines.push_back(p_outline); - rect_cache_dirty = true; -} - -int NavigationPolygon::get_outline_count() const { - return outlines.size(); -} - -void NavigationPolygon::set_outline(int p_idx, const Vector<Vector2> &p_outline) { - ERR_FAIL_INDEX(p_idx, outlines.size()); - outlines.write[p_idx] = p_outline; - rect_cache_dirty = true; -} - -void NavigationPolygon::remove_outline(int p_idx) { - ERR_FAIL_INDEX(p_idx, outlines.size()); - outlines.remove_at(p_idx); - rect_cache_dirty = true; -} - -Vector<Vector2> NavigationPolygon::get_outline(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, outlines.size(), Vector<Vector2>()); - return outlines[p_idx]; -} - -void NavigationPolygon::clear_outlines() { - outlines.clear(); - rect_cache_dirty = true; -} - -void NavigationPolygon::make_polygons_from_outlines() { - { - MutexLock lock(navmesh_generation); - navmesh.unref(); - } - List<TPPLPoly> in_poly, out_poly; - - Vector2 outside_point(-1e10, -1e10); - - for (int i = 0; i < outlines.size(); i++) { - Vector<Vector2> ol = outlines[i]; - int olsize = ol.size(); - if (olsize < 3) { - continue; - } - const Vector2 *r = ol.ptr(); - for (int j = 0; j < olsize; j++) { - outside_point.x = MAX(r[j].x, outside_point.x); - outside_point.y = MAX(r[j].y, outside_point.y); - } - } - - outside_point += Vector2(0.7239784, 0.819238); //avoid precision issues - - for (int i = 0; i < outlines.size(); i++) { - Vector<Vector2> ol = outlines[i]; - int olsize = ol.size(); - if (olsize < 3) { - continue; - } - const Vector2 *r = ol.ptr(); - - int interscount = 0; - //test if this is an outer outline - for (int k = 0; k < outlines.size(); k++) { - if (i == k) { - continue; //no self intersect - } - - Vector<Vector2> ol2 = outlines[k]; - int olsize2 = ol2.size(); - if (olsize2 < 3) { - continue; - } - const Vector2 *r2 = ol2.ptr(); - - for (int l = 0; l < olsize2; l++) { - if (Geometry2D::segment_intersects_segment(r[0], outside_point, r2[l], r2[(l + 1) % olsize2], nullptr)) { - interscount++; - } - } - } - - bool outer = (interscount % 2) == 0; - - TPPLPoly tp; - tp.Init(olsize); - for (int j = 0; j < olsize; j++) { - tp[j] = r[j]; - } - - if (outer) { - tp.SetOrientation(TPPL_ORIENTATION_CCW); - } else { - tp.SetOrientation(TPPL_ORIENTATION_CW); - tp.SetHole(true); - } - - in_poly.push_back(tp); - } - - TPPLPartition tpart; - if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed! - ERR_PRINT("NavigationPolygon: Convex partition failed! Failed to convert outlines to a valid NavigationMesh." - "\nNavigationPolygon outlines can not overlap vertices or edges inside same outline or with other outlines or have any intersections." - "\nAdd the outmost and largest outline first. To add holes inside this outline add the smaller outlines with opposite winding order."); - return; - } - - polygons.clear(); - vertices.clear(); - - HashMap<Vector2, int> points; - for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { - TPPLPoly &tp = I->get(); - - struct Polygon p; - - for (int64_t i = 0; i < tp.GetNumPoints(); i++) { - HashMap<Vector2, int>::Iterator E = points.find(tp[i]); - if (!E) { - E = points.insert(tp[i], vertices.size()); - vertices.push_back(tp[i]); - } - p.indices.push_back(E->value); - } - - polygons.push_back(p); - } - - emit_signal(CoreStringNames::get_singleton()->changed); -} - -void NavigationPolygon::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationPolygon::set_vertices); - ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationPolygon::get_vertices); - - ClassDB::bind_method(D_METHOD("add_polygon", "polygon"), &NavigationPolygon::add_polygon); - ClassDB::bind_method(D_METHOD("get_polygon_count"), &NavigationPolygon::get_polygon_count); - ClassDB::bind_method(D_METHOD("get_polygon", "idx"), &NavigationPolygon::get_polygon); - ClassDB::bind_method(D_METHOD("clear_polygons"), &NavigationPolygon::clear_polygons); - ClassDB::bind_method(D_METHOD("get_mesh"), &NavigationPolygon::get_mesh); - - ClassDB::bind_method(D_METHOD("add_outline", "outline"), &NavigationPolygon::add_outline); - ClassDB::bind_method(D_METHOD("add_outline_at_index", "outline", "index"), &NavigationPolygon::add_outline_at_index); - ClassDB::bind_method(D_METHOD("get_outline_count"), &NavigationPolygon::get_outline_count); - ClassDB::bind_method(D_METHOD("set_outline", "idx", "outline"), &NavigationPolygon::set_outline); - ClassDB::bind_method(D_METHOD("get_outline", "idx"), &NavigationPolygon::get_outline); - ClassDB::bind_method(D_METHOD("remove_outline", "idx"), &NavigationPolygon::remove_outline); - ClassDB::bind_method(D_METHOD("clear_outlines"), &NavigationPolygon::clear_outlines); - ClassDB::bind_method(D_METHOD("make_polygons_from_outlines"), &NavigationPolygon::make_polygons_from_outlines); - - ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationPolygon::_set_polygons); - ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationPolygon::_get_polygons); - - ClassDB::bind_method(D_METHOD("_set_outlines", "outlines"), &NavigationPolygon::_set_outlines); - ClassDB::bind_method(D_METHOD("_get_outlines"), &NavigationPolygon::_get_outlines); - - ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); -} - -///////////////////////////// - void NavigationRegion2D::set_enabled(bool p_enabled) { if (enabled == p_enabled) { return; @@ -460,11 +137,11 @@ RID NavigationRegion2D::get_region_rid() const { #ifdef TOOLS_ENABLED Rect2 NavigationRegion2D::_edit_get_rect() const { - return navpoly.is_valid() ? navpoly->_edit_get_rect() : Rect2(); + return navigation_polygon.is_valid() ? navigation_polygon->_edit_get_rect() : Rect2(); } bool NavigationRegion2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - return navpoly.is_valid() ? navpoly->_edit_is_selected_on_click(p_point, p_tolerance) : false; + return navigation_polygon.is_valid() ? navigation_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false; } #endif @@ -490,8 +167,8 @@ void NavigationRegion2D::_notification(int p_what) { case NOTIFICATION_DRAW: { #ifdef DEBUG_ENABLED - if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer3D::get_singleton()->get_debug_enabled()) && navpoly.is_valid()) { - Vector<Vector2> verts = navpoly->get_vertices(); + if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer3D::get_singleton()->get_debug_enabled()) && navigation_polygon.is_valid()) { + Vector<Vector2> verts = navigation_polygon->get_vertices(); if (verts.size() < 3) { return; } @@ -506,9 +183,9 @@ void NavigationRegion2D::_notification(int p_what) { RandomPCG rand; - for (int i = 0; i < navpoly->get_polygon_count(); i++) { + for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) { // An array of vertices for this polygon. - Vector<int> polygon = navpoly->get_polygon(i); + Vector<int> polygon = navigation_polygon->get_polygon(i); Vector<Vector2> vertices; vertices.resize(polygon.size()); for (int j = 0; j < polygon.size(); j++) { @@ -549,36 +226,36 @@ void NavigationRegion2D::_notification(int p_what) { } } -void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_navpoly) { - if (p_navpoly == navpoly) { +void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_navigation_polygon) { + if (p_navigation_polygon == navigation_polygon) { return; } - if (navpoly.is_valid()) { - navpoly->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navpoly_changed)); + if (navigation_polygon.is_valid()) { + navigation_polygon->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed)); } - navpoly = p_navpoly; - NavigationServer2D::get_singleton()->region_set_navpoly(region, p_navpoly); + navigation_polygon = p_navigation_polygon; + NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, p_navigation_polygon); - if (navpoly.is_valid()) { - navpoly->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navpoly_changed)); + if (navigation_polygon.is_valid()) { + navigation_polygon->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed)); } - _navpoly_changed(); + _navigation_polygon_changed(); update_configuration_warnings(); } Ref<NavigationPolygon> NavigationRegion2D::get_navigation_polygon() const { - return navpoly; + return navigation_polygon; } -void NavigationRegion2D::_navpoly_changed() { +void NavigationRegion2D::_navigation_polygon_changed() { if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) { queue_redraw(); } - if (navpoly.is_valid()) { - NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly); + if (navigation_polygon.is_valid()) { + NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, navigation_polygon); } } @@ -594,7 +271,7 @@ PackedStringArray NavigationRegion2D::get_configuration_warnings() const { PackedStringArray warnings = Node2D::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { - if (!navpoly.is_valid()) { + if (!navigation_polygon.is_valid()) { warnings.push_back(RTR("A NavigationMesh resource must be set or created for this node to work. Please set a property or draw a polygon.")); } } @@ -603,7 +280,7 @@ PackedStringArray NavigationRegion2D::get_configuration_warnings() const { } void NavigationRegion2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_navigation_polygon", "navpoly"), &NavigationRegion2D::set_navigation_polygon); + ClassDB::bind_method(D_METHOD("set_navigation_polygon", "navigation_polygon"), &NavigationRegion2D::set_navigation_polygon); ClassDB::bind_method(D_METHOD("get_navigation_polygon"), &NavigationRegion2D::get_navigation_polygon); ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion2D::set_enabled); @@ -623,15 +300,34 @@ void NavigationRegion2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationRegion2D::set_travel_cost); ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationRegion2D::get_travel_cost); - ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationRegion2D::_navpoly_changed); + ClassDB::bind_method(D_METHOD("_navigation_polygon_changed"), &NavigationRegion2D::_navigation_polygon_changed); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navpoly", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_polygon", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); } +#ifndef DISABLE_DEPRECATED +// Compatibility with earlier 4.0 betas. +bool NavigationRegion2D::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "navpoly") { + set_navigation_polygon(p_value); + return true; + } + return false; +} + +bool NavigationRegion2D::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "navpoly") { + r_ret = get_navigation_polygon(); + return true; + } + return false; +} +#endif // DISABLE_DEPRECATED + NavigationRegion2D::NavigationRegion2D() { set_notify_transform(true); diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h index c630e20780..45029fd95f 100644 --- a/scene/2d/navigation_region_2d.h +++ b/scene/2d/navigation_region_2d.h @@ -31,65 +31,7 @@ #ifndef NAVIGATION_REGION_2D_H #define NAVIGATION_REGION_2D_H -#include "scene/2d/node_2d.h" -#include "scene/resources/navigation_mesh.h" - -class NavigationPolygon : public Resource { - GDCLASS(NavigationPolygon, Resource); - - Vector<Vector2> vertices; - struct Polygon { - Vector<int> indices; - }; - Vector<Polygon> polygons; - Vector<Vector<Vector2>> outlines; - - mutable Rect2 item_rect; - mutable bool rect_cache_dirty = true; - - Mutex navmesh_generation; - // Navigation mesh - Ref<NavigationMesh> navmesh; - -protected: - static void _bind_methods(); - - void _set_polygons(const TypedArray<Vector<int32_t>> &p_array); - TypedArray<Vector<int32_t>> _get_polygons() const; - - void _set_outlines(const TypedArray<Vector<Vector2>> &p_array); - TypedArray<Vector<Vector2>> _get_outlines() const; - -public: -#ifdef TOOLS_ENABLED - Rect2 _edit_get_rect() const; - bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; -#endif - - void set_vertices(const Vector<Vector2> &p_vertices); - Vector<Vector2> get_vertices() const; - - void add_polygon(const Vector<int> &p_polygon); - int get_polygon_count() const; - - void add_outline(const Vector<Vector2> &p_outline); - void add_outline_at_index(const Vector<Vector2> &p_outline, int p_index); - void set_outline(int p_idx, const Vector<Vector2> &p_outline); - Vector<Vector2> get_outline(int p_idx) const; - void remove_outline(int p_idx); - int get_outline_count() const; - - void clear_outlines(); - void make_polygons_from_outlines(); - - Vector<int> get_polygon(int p_idx); - void clear_polygons(); - - Ref<NavigationMesh> get_mesh(); - - NavigationPolygon() {} - ~NavigationPolygon() {} -}; +#include "scene/resources/navigation_polygon.h" class NavigationRegion2D : public Node2D { GDCLASS(NavigationRegion2D, Node2D); @@ -99,15 +41,20 @@ class NavigationRegion2D : public Node2D { uint32_t navigation_layers = 1; real_t enter_cost = 0.0; real_t travel_cost = 1.0; - Ref<NavigationPolygon> navpoly; + Ref<NavigationPolygon> navigation_polygon; - void _navpoly_changed(); + void _navigation_polygon_changed(); void _map_changed(RID p_RID); protected: void _notification(int p_what); static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; +#endif // DISABLE_DEPRECATED + public: #ifdef TOOLS_ENABLED virtual Rect2 _edit_get_rect() const override; @@ -131,7 +78,7 @@ public: void set_travel_cost(real_t p_travel_cost); real_t get_travel_cost() const; - void set_navigation_polygon(const Ref<NavigationPolygon> &p_navpoly); + void set_navigation_polygon(const Ref<NavigationPolygon> &p_navigation_polygon); Ref<NavigationPolygon> get_navigation_polygon() const; PackedStringArray get_configuration_warnings() const override; diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 4788a1b813..166bc26848 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -149,6 +149,10 @@ void Node2D::set_rotation(real_t p_radians) { _update_transform(); } +void Node2D::set_rotation_degrees(real_t p_degrees) { + set_rotation(Math::deg_to_rad(p_degrees)); +} + void Node2D::set_skew(real_t p_radians) { if (_xform_dirty) { const_cast<Node2D *>(this)->_update_xform_values(); @@ -187,6 +191,10 @@ real_t Node2D::get_rotation() const { return rotation; } +real_t Node2D::get_rotation_degrees() const { + return Math::rad_to_deg(get_rotation()); +} + real_t Node2D::get_skew() const { if (_xform_dirty) { const_cast<Node2D *>(this)->_update_xform_values(); @@ -259,6 +267,10 @@ real_t Node2D::get_global_rotation() const { return get_global_transform().get_rotation(); } +real_t Node2D::get_global_rotation_degrees() const { + return Math::rad_to_deg(get_global_rotation()); +} + real_t Node2D::get_global_skew() const { return get_global_transform().get_skew(); } @@ -276,6 +288,10 @@ void Node2D::set_global_rotation(const real_t p_radians) { } } +void Node2D::set_global_rotation_degrees(const real_t p_degrees) { + set_global_rotation(Math::deg_to_rad(p_degrees)); +} + void Node2D::set_global_skew(const real_t p_radians) { CanvasItem *parent = get_parent_item(); if (parent) { @@ -372,11 +388,13 @@ void Node2D::_notification(int p_notification) { void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_position", "position"), &Node2D::set_position); ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Node2D::set_rotation); + ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Node2D::set_rotation_degrees); ClassDB::bind_method(D_METHOD("set_skew", "radians"), &Node2D::set_skew); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Node2D::set_scale); ClassDB::bind_method(D_METHOD("get_position"), &Node2D::get_position); ClassDB::bind_method(D_METHOD("get_rotation"), &Node2D::get_rotation); + ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Node2D::get_rotation_degrees); ClassDB::bind_method(D_METHOD("get_skew"), &Node2D::get_skew); ClassDB::bind_method(D_METHOD("get_scale"), &Node2D::get_scale); @@ -390,7 +408,9 @@ void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Node2D::set_global_position); ClassDB::bind_method(D_METHOD("get_global_position"), &Node2D::get_global_position); ClassDB::bind_method(D_METHOD("set_global_rotation", "radians"), &Node2D::set_global_rotation); + ClassDB::bind_method(D_METHOD("set_global_rotation_degrees", "degrees"), &Node2D::set_global_rotation_degrees); ClassDB::bind_method(D_METHOD("get_global_rotation"), &Node2D::get_global_rotation); + ClassDB::bind_method(D_METHOD("get_global_rotation_degrees"), &Node2D::get_global_rotation_degrees); ClassDB::bind_method(D_METHOD("set_global_skew", "radians"), &Node2D::set_global_skew); ClassDB::bind_method(D_METHOD("get_global_skew"), &Node2D::get_global_skew); ClassDB::bind_method(D_METHOD("set_global_scale", "scale"), &Node2D::set_global_scale); @@ -410,12 +430,14 @@ void Node2D::_bind_methods() { ADD_GROUP("Transform", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_less,or_greater,hide_slider,suffix:px"), "set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians"), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale", PROPERTY_HINT_LINK), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "skew", PROPERTY_HINT_RANGE, "-89.9,89.9,0.1,radians"), "set_skew", "get_skew"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "set_transform", "get_transform"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "set_global_position", "get_global_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "global_rotation", PROPERTY_HINT_NONE, "radians", PROPERTY_USAGE_NONE), "set_global_rotation", "get_global_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "global_rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_rotation_degrees", "get_global_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_scale", "get_global_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "global_skew", PROPERTY_HINT_NONE, "radians", PROPERTY_USAGE_NONE), "set_global_skew", "get_global_skew"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_transform", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform"); diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index 76707dc422..b066332dcb 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -73,6 +73,7 @@ public: void set_position(const Point2 &p_pos); void set_rotation(real_t p_radians); + void set_rotation_degrees(real_t p_degrees); void set_skew(real_t p_radians); void set_scale(const Size2 &p_scale); @@ -85,11 +86,13 @@ public: Point2 get_position() const; real_t get_rotation() const; + real_t get_rotation_degrees() const; real_t get_skew() const; Size2 get_scale() const; Point2 get_global_position() const; real_t get_global_rotation() const; + real_t get_global_rotation_degrees() const; real_t get_global_skew() const; Size2 get_global_scale() const; @@ -97,6 +100,7 @@ public: void set_global_transform(const Transform2D &p_transform); void set_global_position(const Point2 &p_pos); void set_global_rotation(const real_t p_radians); + void set_global_rotation_degrees(const real_t p_degrees); void set_global_skew(const real_t p_radians); void set_global_scale(const Size2 &p_scale); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 3ae7a0b34d..3136752a2e 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1092,7 +1092,7 @@ void TileMap::_rendering_update_layer(int p_layer) { /*Transform2D xform; xform.set_origin(Vector2(0, p_layer)); rs->canvas_item_set_transform(ci, xform);*/ - rs->canvas_item_set_draw_index(ci, p_layer); + rs->canvas_item_set_draw_index(ci, p_layer - (int64_t)0x80000000); layers[p_layer].canvas_item = ci; } @@ -1728,10 +1728,10 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List q.navigation_regions[E_cell].resize(tile_set->get_navigation_layers_count()); for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { - Ref<NavigationPolygon> navpoly; - navpoly = tile_data->get_navigation_polygon(layer_index); + Ref<NavigationPolygon> navigation_polygon; + navigation_polygon = tile_data->get_navigation_polygon(layer_index); - if (navpoly.is_valid()) { + if (navigation_polygon.is_valid()) { Transform2D tile_transform; tile_transform.set_origin(map_to_local(E_cell)); @@ -1739,7 +1739,8 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List NavigationServer2D::get_singleton()->region_set_owner_id(region, get_instance_id()); NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); - NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly); + NavigationServer2D::get_singleton()->region_set_navigation_layers(region, tile_set->get_navigation_layer_layers(layer_index)); + NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, navigation_polygon); q.navigation_regions[E_cell].write[layer_index] = region; } } @@ -1996,7 +1997,6 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c if ((source_id == TileSet::INVALID_SOURCE || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) && (source_id != TileSet::INVALID_SOURCE || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) { - WARN_PRINT("Setting a cell as empty requires both source_id, atlas_coord and alternative_tile to be set to their respective \"invalid\" values. Values were thus changes accordingly."); source_id = TileSet::INVALID_SOURCE; atlas_coords = TileSetSource::INVALID_ATLAS_COORDS; alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; @@ -3905,35 +3905,26 @@ void TileMap::draw_cells_outline(Control *p_control, const RBSet<Vector2i> &p_ce DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, 2, 3); DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_LEFT_SIDE, 3, 0); DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_SIDE, 0, 1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 2, 3); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 1, 2); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 3, 0); } else { if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 3, 4); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 2, 3); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 5, 0); - } else { - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 3, 4); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 2, 3); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_LEFT_SIDE, 1, 2); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 5, 0); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, 4, 5); - } + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 3, 4); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 2, 3); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_LEFT_SIDE, 1, 2); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 5, 0); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, 4, 5); } else { - if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 3, 4); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 5, 0); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 2, 3); - } else { - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 3, 4); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, 4, 5); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 5, 0); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_SIDE, 1, 2); - DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 2, 3); - } + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, 3, 4); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, 4, 5); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, 5, 0); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, 0, 1); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_SIDE, 1, 2); + DRAW_SIDE_IF_NEEDED(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, 2, 3); } } } diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index ef381641ab..2729dfc369 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -34,6 +34,7 @@ #include "physics_body_3d.h" #include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" +#include "scene/resources/world_boundary_shape_3d.h" void CollisionShape3D::make_convex_from_siblings() { Node *p = get_parent(); @@ -125,10 +126,12 @@ PackedStringArray CollisionShape3D::get_configuration_warnings() const { warnings.push_back(RTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it.")); } - if (shape.is_valid() && - Object::cast_to<RigidBody3D>(get_parent()) && - Object::cast_to<ConcavePolygonShape3D>(*shape)) { - warnings.push_back(RTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static.")); + if (shape.is_valid() && Object::cast_to<RigidBody3D>(get_parent())) { + if (Object::cast_to<ConcavePolygonShape3D>(*shape)) { + warnings.push_back(RTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static.")); + } else if (Object::cast_to<WorldBoundaryShape3D>(*shape)) { + warnings.push_back(RTR("WorldBoundaryShape3D doesn't support RigidBody3D in another mode than static.")); + } } return warnings; diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 10f3ab5c79..c4ec54fa8f 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -53,21 +53,11 @@ bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) { if (p_name.operator String().begins_with("surface_material_override/")) { int idx = p_name.operator String().get_slicec('/', 1).to_int(); - // This is a bit of a hack to ensure compatibility with older material - // overrides that start indexing at 0. - // We assume that idx 0 is always read first, if its not, this won't work. - if (idx == 0) { - old_surface_index = true; - } - if (old_surface_index) { - idx++; - } - - if (idx > surface_override_materials.size() || idx < 0) { + if (idx >= surface_override_materials.size() || idx < 0) { return false; } - set_surface_override_material(idx - 1, p_value); + set_surface_override_material(idx, p_value); return true; } @@ -86,7 +76,7 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const { } if (p_name.operator String().begins_with("surface_material_override/")) { - int idx = p_name.operator String().get_slicec('/', 1).to_int() - 1; + int idx = p_name.operator String().get_slicec('/', 1).to_int(); if (idx >= surface_override_materials.size() || idx < 0) { return false; } @@ -109,7 +99,7 @@ void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { } if (mesh.is_valid()) { - for (int i = 1; i <= mesh->get_surface_count(); i++) { + for (int i = 0; i < mesh->get_surface_count(); i++) { p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("%s/%d", PNAME("surface_material_override"), i), PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); } } diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h index 9e6dbaf160..e1cc5272ea 100644 --- a/scene/3d/mesh_instance_3d.h +++ b/scene/3d/mesh_instance_3d.h @@ -57,7 +57,7 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - bool old_surface_index = false; + bool surface_index_0 = false; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index e907b9f66f..741f7397ad 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -74,6 +74,9 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationAgent3D::set_navigation_layer_value); ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationAgent3D::get_navigation_layer_value); + ClassDB::bind_method(D_METHOD("set_path_metadata_flags", "flags"), &NavigationAgent3D::set_path_metadata_flags); + ClassDB::bind_method(D_METHOD("get_path_metadata_flags"), &NavigationAgent3D::get_path_metadata_flags); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent3D::set_navigation_map); ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent3D::get_navigation_map); @@ -83,8 +86,9 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent3D::get_next_location); ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent3D::distance_to_target); ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent3D::set_velocity); - ClassDB::bind_method(D_METHOD("get_nav_path"), &NavigationAgent3D::get_nav_path); - ClassDB::bind_method(D_METHOD("get_nav_path_index"), &NavigationAgent3D::get_nav_path_index); + ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent3D::get_current_navigation_result); + ClassDB::bind_method(D_METHOD("get_current_navigation_path"), &NavigationAgent3D::get_current_navigation_path); + ClassDB::bind_method(D_METHOD("get_current_navigation_path_index"), &NavigationAgent3D::get_current_navigation_path_index); ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent3D::is_target_reached); ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent3D::is_target_reachable); ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent3D::is_navigation_finished); @@ -99,6 +103,7 @@ void NavigationAgent3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01,suffix:m"), "set_agent_height_offset", "get_agent_height_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1,suffix:m"), "set_path_max_distance", "get_path_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags"); ADD_GROUP("Avoidance", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); @@ -111,6 +116,8 @@ void NavigationAgent3D::_bind_methods() { ADD_SIGNAL(MethodInfo("path_changed")); ADD_SIGNAL(MethodInfo("target_reached")); + ADD_SIGNAL(MethodInfo("waypoint_reached", PropertyInfo(Variant::DICTIONARY, "details"))); + ADD_SIGNAL(MethodInfo("link_reached", PropertyInfo(Variant::DICTIONARY, "details"))); ADD_SIGNAL(MethodInfo("navigation_finished")); ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR3, "safe_velocity"))); } @@ -167,7 +174,7 @@ void NavigationAgent3D::_notification(int p_what) { } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - if (agent_parent) { + if (agent_parent && target_position_submitted) { if (avoidance_enabled) { // agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding // no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used @@ -263,6 +270,14 @@ bool NavigationAgent3D::get_navigation_layer_value(int p_layer_number) const { return get_navigation_layers() & (1 << (p_layer_number - 1)); } +void NavigationAgent3D::set_path_metadata_flags(BitField<NavigationPathQueryParameters3D::PathMetadataFlags> p_path_metadata_flags) { + if (path_metadata_flags == p_path_metadata_flags) { + return; + } + + path_metadata_flags = p_path_metadata_flags; +} + void NavigationAgent3D::set_navigation_map(RID p_navigation_map) { map_override = p_navigation_map; NavigationServer3D::get_singleton()->agent_set_map(agent, map_override); @@ -330,6 +345,7 @@ real_t NavigationAgent3D::get_path_max_distance() { void NavigationAgent3D::set_target_location(Vector3 p_location) { target_location = p_location; + target_position_submitted = true; _request_repath(); } @@ -345,14 +361,10 @@ Vector3 NavigationAgent3D::get_next_location() { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector3(), "The agent has no parent."); return agent_parent->get_global_transform().origin; } else { - return navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0); + return navigation_path[navigation_path_index] - Vector3(0, navigation_height_offset, 0); } } -const Vector<Vector3> &NavigationAgent3D::get_nav_path() const { - return navigation_result->get_path(); -} - real_t NavigationAgent3D::distance_to_target() const { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent."); return agent_parent->get_global_transform().origin.distance_to(target_location); @@ -417,6 +429,9 @@ void NavigationAgent3D::update_navigation() { if (!agent_parent->is_inside_tree()) { return; } + if (!target_position_submitted) { + return; + } if (update_frame_id == Engine::get_singleton()->get_physics_frames()) { return; } @@ -433,12 +448,12 @@ void NavigationAgent3D::update_navigation() { reload_path = true; } else { // Check if too far from the navigation path - if (nav_path_index > 0) { + if (navigation_path_index > 0) { const Vector<Vector3> &navigation_path = navigation_result->get_path(); Vector3 segment[2]; - segment[0] = navigation_path[nav_path_index - 1]; - segment[1] = navigation_path[nav_path_index]; + segment[0] = navigation_path[navigation_path_index - 1]; + segment[1] = navigation_path[navigation_path_index]; segment[0].y -= navigation_height_offset; segment[1].y -= navigation_height_offset; Vector3 p = Geometry3D::get_closest_point_to_segment(origin, segment); @@ -453,6 +468,7 @@ void NavigationAgent3D::update_navigation() { navigation_query->set_start_position(origin); navigation_query->set_target_position(target_location); navigation_query->set_navigation_layers(navigation_layers); + navigation_query->set_metadata_flags(path_metadata_flags); if (map_override.is_valid()) { navigation_query->set_map(map_override); @@ -462,7 +478,7 @@ void NavigationAgent3D::update_navigation() { NavigationServer3D::get_singleton()->query_path(navigation_query, navigation_result); navigation_finished = false; - nav_path_index = 0; + navigation_path_index = 0; emit_signal(SNAME("path_changed")); } @@ -474,12 +490,57 @@ void NavigationAgent3D::update_navigation() { if (navigation_finished == false) { // Advances to the next far away location. const Vector<Vector3> &navigation_path = navigation_result->get_path(); - while (origin.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < path_desired_distance) { - nav_path_index += 1; - if (nav_path_index == navigation_path.size()) { + const Vector<int32_t> &navigation_path_types = navigation_result->get_path_types(); + const TypedArray<RID> &navigation_path_rids = navigation_result->get_path_rids(); + const Vector<int64_t> &navigation_path_owners = navigation_result->get_path_owner_ids(); + + while (origin.distance_to(navigation_path[navigation_path_index] - Vector3(0, navigation_height_offset, 0)) < path_desired_distance) { + Dictionary details; + + const Vector3 waypoint = navigation_path[navigation_path_index]; + details[SNAME("location")] = waypoint; + + int waypoint_type = -1; + if (path_metadata_flags.has_flag(NavigationPathQueryParameters3D::PathMetadataFlags::PATH_METADATA_INCLUDE_TYPES)) { + const NavigationPathQueryResult3D::PathSegmentType type = NavigationPathQueryResult3D::PathSegmentType(navigation_path_types[navigation_path_index]); + + details[SNAME("type")] = type; + waypoint_type = type; + } + + if (path_metadata_flags.has_flag(NavigationPathQueryParameters3D::PathMetadataFlags::PATH_METADATA_INCLUDE_RIDS)) { + details[SNAME("rid")] = navigation_path_rids[navigation_path_index]; + } + + if (path_metadata_flags.has_flag(NavigationPathQueryParameters3D::PathMetadataFlags::PATH_METADATA_INCLUDE_OWNERS)) { + const ObjectID waypoint_owner_id = ObjectID(navigation_path_owners[navigation_path_index]); + + // Get a reference to the owning object. + Object *owner = nullptr; + if (waypoint_owner_id.is_valid()) { + owner = ObjectDB::get_instance(waypoint_owner_id); + } + + details[SNAME("owner")] = owner; + } + + // Emit a signal for the waypoint + emit_signal(SNAME("waypoint_reached"), details); + + // Emit a signal if we've reached a navigation link + if (waypoint_type == NavigationPathQueryResult3D::PATH_SEGMENT_TYPE_LINK) { + emit_signal(SNAME("link_reached"), details); + } + + // Move to the next waypoint on the list + navigation_path_index += 1; + + // Check to see if we've finished our route + if (navigation_path_index == navigation_path.size()) { _check_distance_to_target(); - nav_path_index -= 1; + navigation_path_index -= 1; navigation_finished = true; + target_position_submitted = false; emit_signal(SNAME("navigation_finished")); break; } diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index 90ceab0242..cda218b538 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -32,10 +32,10 @@ #define NAVIGATION_AGENT_3D_H #include "scene/main/node.h" +#include "servers/navigation/navigation_path_query_parameters_3d.h" +#include "servers/navigation/navigation_path_query_result_3d.h" class Node3D; -class NavigationPathQueryParameters3D; -class NavigationPathQueryResult3D; class NavigationAgent3D : public Node { GDCLASS(NavigationAgent3D, Node); @@ -48,6 +48,7 @@ class NavigationAgent3D : public Node { bool avoidance_enabled = false; uint32_t navigation_layers = 1; + BitField<NavigationPathQueryParameters3D::PathMetadataFlags> path_metadata_flags = NavigationPathQueryParameters3D::PathMetadataFlags::PATH_METADATA_INCLUDE_ALL; real_t path_desired_distance = 1.0; real_t target_desired_distance = 1.0; @@ -62,9 +63,10 @@ class NavigationAgent3D : public Node { real_t path_max_distance = 3.0; Vector3 target_location; + bool target_position_submitted = false; Ref<NavigationPathQueryParameters3D> navigation_query; Ref<NavigationPathQueryResult3D> navigation_result; - int nav_path_index = 0; + int navigation_path_index = 0; bool velocity_submitted = false; Vector3 prev_safe_velocity; /// The submitted target velocity @@ -97,6 +99,11 @@ public: void set_navigation_layer_value(int p_layer_number, bool p_value); bool get_navigation_layer_value(int p_layer_number) const; + void set_path_metadata_flags(BitField<NavigationPathQueryParameters3D::PathMetadataFlags> p_flags); + BitField<NavigationPathQueryParameters3D::PathMetadataFlags> get_path_metadata_flags() const { + return path_metadata_flags; + } + void set_navigation_map(RID p_navigation_map); RID get_navigation_map() const; @@ -153,10 +160,14 @@ public: Vector3 get_next_location(); - const Vector<Vector3> &get_nav_path() const; - - int get_nav_path_index() const { - return nav_path_index; + Ref<NavigationPathQueryResult3D> get_current_navigation_result() const { + return navigation_result; + } + const Vector<Vector3> &get_current_navigation_path() const { + return navigation_result->get_path(); + } + int get_current_navigation_path_index() const { + return navigation_path_index; } real_t distance_to_target() const; diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index bd96c55512..86b78f847e 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -192,26 +192,26 @@ void NavigationRegion3D::_notification(int p_what) { } } -void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh) { - if (p_navmesh == navmesh) { +void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navigation_mesh) { + if (p_navigation_mesh == navigation_mesh) { return; } - if (navmesh.is_valid()) { - navmesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); + if (navigation_mesh.is_valid()) { + navigation_mesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); } - navmesh = p_navmesh; + navigation_mesh = p_navigation_mesh; - if (navmesh.is_valid()) { - navmesh->connect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); + if (navigation_mesh.is_valid()) { + navigation_mesh->connect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); } - NavigationServer3D::get_singleton()->region_set_navmesh(region, p_navmesh); + NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, p_navigation_mesh); #ifdef DEBUG_ENABLED if (is_inside_tree() && NavigationServer3D::get_singleton()->get_debug_enabled()) { - if (navmesh.is_valid()) { + if (navigation_mesh.is_valid()) { _update_debug_mesh(); _update_debug_edge_connections_mesh(); } else { @@ -232,7 +232,7 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navmes } Ref<NavigationMesh> NavigationRegion3D::get_navigation_mesh() const { - return navmesh; + return navigation_mesh; } struct BakeThreadsArgs { @@ -245,7 +245,7 @@ void _bake_navigation_mesh(void *p_user_data) { if (args->nav_region->get_navigation_mesh().is_valid()) { Ref<NavigationMesh> nav_mesh = args->nav_region->get_navigation_mesh()->duplicate(); - NavigationServer3D::get_singleton()->region_bake_navmesh(nav_mesh, args->nav_region); + NavigationServer3D::get_singleton()->region_bake_navigation_mesh(nav_mesh, args->nav_region); args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh); memdelete(args); } else { @@ -278,7 +278,7 @@ PackedStringArray NavigationRegion3D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { - if (!navmesh.is_valid()) { + if (!navigation_mesh.is_valid()) { warnings.push_back(RTR("A NavigationMesh resource must be set or created for this node to work.")); } } @@ -287,7 +287,7 @@ PackedStringArray NavigationRegion3D::get_configuration_warnings() const { } void NavigationRegion3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_navigation_mesh", "navmesh"), &NavigationRegion3D::set_navigation_mesh); + ClassDB::bind_method(D_METHOD("set_navigation_mesh", "navigation_mesh"), &NavigationRegion3D::set_navigation_mesh); ClassDB::bind_method(D_METHOD("get_navigation_mesh"), &NavigationRegion3D::get_navigation_mesh); ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion3D::set_enabled); @@ -308,9 +308,9 @@ void NavigationRegion3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationRegion3D::get_travel_cost); ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationRegion3D::bake_navigation_mesh, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationRegion3D::_bake_finished); + ClassDB::bind_method(D_METHOD("_bake_finished", "navigation_mesh"), &NavigationRegion3D::_bake_finished); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_mesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); @@ -320,6 +320,25 @@ void NavigationRegion3D::_bind_methods() { ADD_SIGNAL(MethodInfo("bake_finished")); } +#ifndef DISABLE_DEPRECATED +// Compatibility with earlier 4.0 betas. +bool NavigationRegion3D::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "navmesh") { + set_navigation_mesh(p_value); + return true; + } + return false; +} + +bool NavigationRegion3D::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "navmesh") { + r_ret = get_navigation_mesh(); + return true; + } + return false; +} +#endif // DISABLE_DEPRECATED + void NavigationRegion3D::_navigation_changed() { update_gizmos(); update_configuration_warnings(); @@ -353,8 +372,8 @@ NavigationRegion3D::NavigationRegion3D() { } NavigationRegion3D::~NavigationRegion3D() { - if (navmesh.is_valid()) { - navmesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); + if (navigation_mesh.is_valid()) { + navigation_mesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); } NavigationServer3D::get_singleton()->free(region); @@ -392,7 +411,7 @@ void NavigationRegion3D::_update_debug_mesh() { return; } - if (!navmesh.is_valid()) { + if (!navigation_mesh.is_valid()) { if (debug_instance.is_valid()) { RS::get_singleton()->instance_set_visible(debug_instance, false); } @@ -412,12 +431,12 @@ void NavigationRegion3D::_update_debug_mesh() { bool enabled_geometry_face_random_color = NavigationServer3D::get_singleton()->get_debug_navigation_enable_geometry_face_random_color(); bool enabled_edge_lines = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_lines(); - Vector<Vector3> vertices = navmesh->get_vertices(); + Vector<Vector3> vertices = navigation_mesh->get_vertices(); if (vertices.size() == 0) { return; } - int polygon_count = navmesh->get_polygon_count(); + int polygon_count = navigation_mesh->get_polygon_count(); if (polygon_count == 0) { return; } @@ -450,7 +469,7 @@ void NavigationRegion3D::_update_debug_mesh() { polygon_color.a = debug_navigation_geometry_face_color.a; } - Vector<int> polygon = navmesh->get_polygon(i); + Vector<int> polygon = navigation_mesh->get_polygon(i); face_vertex_array.push_back(vertices[polygon[0]]); face_vertex_array.push_back(vertices[polygon[1]]); @@ -528,7 +547,7 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { return; } - if (!navmesh.is_valid()) { + if (!navigation_mesh.is_valid()) { if (debug_edge_connections_instance.is_valid()) { RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false); } diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index 660538d314..811c0885a1 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -42,7 +42,7 @@ class NavigationRegion3D : public Node3D { uint32_t navigation_layers = 1; real_t enter_cost = 0.0; real_t travel_cost = 1.0; - Ref<NavigationMesh> navmesh; + Ref<NavigationMesh> navigation_mesh; Thread bake_thread; @@ -64,6 +64,11 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; +#endif // DISABLE_DEPRECATED + public: void set_enabled(bool p_enabled); bool is_enabled() const; @@ -82,13 +87,13 @@ public: void set_travel_cost(real_t p_travel_cost); real_t get_travel_cost() const; - void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh); + void set_navigation_mesh(const Ref<NavigationMesh> &p_navigation_mesh); Ref<NavigationMesh> get_navigation_mesh() const; /// Bakes the navigation mesh; once done, automatically /// sets the new navigation mesh and emits a signal void bake_navigation_mesh(bool p_on_thread); - void _bake_finished(Ref<NavigationMesh> p_nav_mesh); + void _bake_finished(Ref<NavigationMesh> p_navigation_mesh); PackedStringArray get_configuration_warnings() const override; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index a60ccd2169..32bbea8ff3 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -251,12 +251,22 @@ Vector3 Node3D::get_global_rotation() const { return get_global_transform().get_basis().get_euler(); } +Vector3 Node3D::get_global_rotation_degrees() const { + Vector3 radians = get_global_rotation(); + return Vector3(Math::rad_to_deg(radians.x), Math::rad_to_deg(radians.y), Math::rad_to_deg(radians.z)); +} + void Node3D::set_global_rotation(const Vector3 &p_euler_rad) { Transform3D transform = get_global_transform(); transform.basis = Basis::from_euler(p_euler_rad); set_global_transform(transform); } +void Node3D::set_global_rotation_degrees(const Vector3 &p_euler_degrees) { + Vector3 radians(Math::deg_to_rad(p_euler_degrees.x), Math::deg_to_rad(p_euler_degrees.y), Math::deg_to_rad(p_euler_degrees.z)); + set_global_rotation(radians); +} + void Node3D::set_transform(const Transform3D &p_transform) { data.local_transform = p_transform; data.dirty = DIRTY_EULER_ROTATION_AND_SCALE; // Make rot/scale dirty. @@ -440,6 +450,11 @@ void Node3D::set_rotation(const Vector3 &p_euler_rad) { } } +void Node3D::set_rotation_degrees(const Vector3 &p_euler_degrees) { + Vector3 radians(Math::deg_to_rad(p_euler_degrees.x), Math::deg_to_rad(p_euler_degrees.y), Math::deg_to_rad(p_euler_degrees.z)); + set_rotation(radians); +} + void Node3D::set_scale(const Vector3 &p_scale) { if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { // Update rotation only if rotation and scale are dirty, as scale will be overridden. @@ -467,6 +482,11 @@ Vector3 Node3D::get_rotation() const { return data.euler_rotation; } +Vector3 Node3D::get_rotation_degrees() const { + Vector3 radians = get_rotation(); + return Vector3(Math::rad_to_deg(radians.x), Math::rad_to_deg(radians.y), Math::rad_to_deg(radians.z)); +} + Vector3 Node3D::get_scale() const { if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { _update_rotation_and_scale(); @@ -958,8 +978,10 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_transform"), &Node3D::get_transform); ClassDB::bind_method(D_METHOD("set_position", "position"), &Node3D::set_position); ClassDB::bind_method(D_METHOD("get_position"), &Node3D::get_position); - ClassDB::bind_method(D_METHOD("set_rotation", "euler"), &Node3D::set_rotation); + ClassDB::bind_method(D_METHOD("set_rotation", "euler_radians"), &Node3D::set_rotation); ClassDB::bind_method(D_METHOD("get_rotation"), &Node3D::get_rotation); + ClassDB::bind_method(D_METHOD("set_rotation_degrees", "euler_degrees"), &Node3D::set_rotation_degrees); + ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Node3D::get_rotation_degrees); ClassDB::bind_method(D_METHOD("set_rotation_order", "order"), &Node3D::set_rotation_order); ClassDB::bind_method(D_METHOD("get_rotation_order"), &Node3D::get_rotation_order); ClassDB::bind_method(D_METHOD("set_rotation_edit_mode", "edit_mode"), &Node3D::set_rotation_edit_mode); @@ -975,8 +997,10 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform); ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Node3D::set_global_position); ClassDB::bind_method(D_METHOD("get_global_position"), &Node3D::get_global_position); - ClassDB::bind_method(D_METHOD("set_global_rotation", "radians"), &Node3D::set_global_rotation); + ClassDB::bind_method(D_METHOD("set_global_rotation", "euler_radians"), &Node3D::set_global_rotation); ClassDB::bind_method(D_METHOD("get_global_rotation"), &Node3D::get_global_rotation); + ClassDB::bind_method(D_METHOD("set_global_rotation_degrees", "euler_degrees"), &Node3D::set_global_rotation_degrees); + ClassDB::bind_method(D_METHOD("get_global_rotation_degrees"), &Node3D::get_global_rotation_degrees); ClassDB::bind_method(D_METHOD("get_parent_node_3d"), &Node3D::get_parent_node_3d); ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Node3D::set_ignore_transform_notification); @@ -1045,6 +1069,7 @@ void Node3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "suffix:m", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_greater,or_less,hide_slider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "quaternion", PROPERTY_HINT_HIDE_QUATERNION_EDIT, "", PROPERTY_USAGE_EDITOR), "set_quaternion", "get_quaternion"); ADD_PROPERTY(PropertyInfo(Variant::BASIS, "basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_basis", "get_basis"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_LINK, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); @@ -1054,6 +1079,7 @@ void Node3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_position", "get_global_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "global_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_rotation", "get_global_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "global_rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_rotation_degrees", "get_global_rotation_degrees"); ADD_GROUP("Visibility", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent"); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 76c1e3e22c..9558a978fd 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -172,19 +172,23 @@ public: void set_rotation_order(EulerOrder p_order); void set_rotation(const Vector3 &p_euler_rad); + void set_rotation_degrees(const Vector3 &p_euler_degrees); void set_scale(const Vector3 &p_scale); void set_global_position(const Vector3 &p_position); void set_global_rotation(const Vector3 &p_euler_rad); + void set_global_rotation_degrees(const Vector3 &p_euler_degrees); Vector3 get_position() const; EulerOrder get_rotation_order() const; Vector3 get_rotation() const; + Vector3 get_rotation_degrees() const; Vector3 get_scale() const; Vector3 get_global_position() const; Vector3 get_global_rotation() const; + Vector3 get_global_rotation_degrees() const; void set_transform(const Transform3D &p_transform); void set_basis(const Basis &p_basis); diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index d69953fee5..e488ff3059 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -191,9 +191,7 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, uint32_t v_normal; { - Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); - - Vector2 res = n.octahedron_encode(); + Vector2 res = normal.octahedron_encode(); uint32_t value = 0; value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535); value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 73af068c4a..3b21f5c03a 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -30,6 +30,7 @@ #include "visual_instance_3d.h" +#include "core/core_string_names.h" #include "scene/scene_string_names.h" AABB VisualInstance3D::get_aabb() const { @@ -160,7 +161,13 @@ VisualInstance3D::~VisualInstance3D() { } void GeometryInstance3D::set_material_override(const Ref<Material> &p_material) { + if (material_override.is_valid()) { + material_override->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed)); + } material_override = p_material; + if (material_override.is_valid()) { + material_override->connect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed)); + } RS::get_singleton()->instance_geometry_set_material_override(get_instance(), p_material.is_valid() ? p_material->get_rid() : RID()); } @@ -357,8 +364,16 @@ Variant GeometryInstance3D::get_instance_shader_parameter(const StringName &p_na return RS::get_singleton()->instance_geometry_get_shader_parameter(get_instance(), p_name); } -void GeometryInstance3D::set_custom_aabb(AABB aabb) { - RS::get_singleton()->instance_set_custom_aabb(get_instance(), aabb); +void GeometryInstance3D::set_custom_aabb(AABB p_aabb) { + if (p_aabb == custom_aabb) { + return; + } + custom_aabb = p_aabb; + RS::get_singleton()->instance_set_custom_aabb(get_instance(), custom_aabb); +} + +AABB GeometryInstance3D::get_custom_aabb() const { + return custom_aabb; } void GeometryInstance3D::set_lightmap_scale(LightmapScale p_scale) { @@ -468,6 +483,7 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_ignoring_occlusion_culling"), &GeometryInstance3D::is_ignoring_occlusion_culling); ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &GeometryInstance3D::set_custom_aabb); + ClassDB::bind_method(D_METHOD("get_custom_aabb"), &GeometryInstance3D::get_custom_aabb); ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance3D::get_aabb); @@ -477,6 +493,7 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "transparency", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_transparency", "get_transparency"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01,suffix:m"), "set_extra_cull_margin", "get_extra_cull_margin"); + ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling"); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 8241aef96d..2d107f61d2 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -129,6 +129,7 @@ private: mutable HashMap<StringName, StringName> instance_shader_parameter_property_remap; float extra_cull_margin = 0.0; + AABB custom_aabb; LightmapScale lightmap_scale = LIGHTMAP_SCALE_1X; GIMode gi_mode = GI_MODE_STATIC; bool ignore_occlusion_culling = false; @@ -185,7 +186,8 @@ public: void set_instance_shader_parameter(const StringName &p_name, const Variant &p_value); Variant get_instance_shader_parameter(const StringName &p_name) const; - void set_custom_aabb(AABB aabb); + void set_custom_aabb(AABB p_aabb); + AABB get_custom_aabb() const; void set_ignore_occlusion_culling(bool p_enabled); bool is_ignoring_occlusion_culling(); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 6200062f60..26261d6da5 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -144,6 +144,19 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_is_ext } } } + + // Emit start & finish signal. Internally, the detections are the same for backward. + // We should use call_deferred since the track keys are still being prosessed. + if (state->tree) { + // AnimationTree uses seek to 0 "internally" to process the first key of the animation, which is used as the start detection. + if (p_seek && !p_is_external_seeking && cur_time == 0) { + state->tree->call_deferred(SNAME("emit_signal"), "animation_started", animation); + } + // Finished. + if (prev_time < anim_size && cur_time >= anim_size) { + state->tree->call_deferred(SNAME("emit_signal"), "animation_finished", animation); + } + } } if (play_mode == PLAY_MODE_FORWARD) { @@ -351,7 +364,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_exter } else { main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - blend, FILTER_BLEND, sync); // Unlike below, processing this edge is a corner case. } - double os_rem = blend_input(1, os_seek ? cur_time : p_time, os_seek, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_PASS, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + double os_rem = blend_input(1, os_seek ? cur_time : p_time, os_seek, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_PASS, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. if (do_start) { cur_remaining = os_rem; @@ -769,17 +782,18 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex } // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + real_t blend_inv = 1.0 - blend; if (from_start && !p_seek && switched) { //just switched, seek to start of current - rem = blend_input(cur_current, 0, true, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - blend), FILTER_IGNORE, true); + rem = blend_input(cur_current, 0, true, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); } else { - rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - blend), FILTER_IGNORE, true); + rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); } if (p_seek) { - blend_input(cur_prev, p_time, true, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_IGNORE, true); + blend_input(cur_prev, p_time, true, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true); cur_time = p_time; } else { - blend_input(cur_prev, p_time, false, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_IGNORE, true); + blend_input(cur_prev, p_time, false, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true); cur_time += p_time; cur_prev_xfading -= p_time; if (cur_prev_xfading < 0) { diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index d3746c1144..d54740e9b0 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -41,12 +41,12 @@ AnimationNodeStateMachineTransition::SwitchMode AnimationNodeStateMachineTransit return switch_mode; } -void AnimationNodeStateMachineTransition::set_auto_advance(bool p_enable) { - auto_advance = p_enable; +void AnimationNodeStateMachineTransition::set_advance_mode(AdvanceMode p_mode) { + advance_mode = p_mode; } -bool AnimationNodeStateMachineTransition::has_auto_advance() const { - return auto_advance; +AnimationNodeStateMachineTransition::AdvanceMode AnimationNodeStateMachineTransition::get_advance_mode() const { + return advance_mode; } void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) { @@ -107,15 +107,6 @@ Ref<Curve> AnimationNodeStateMachineTransition::get_xfade_curve() const { return xfade_curve; } -void AnimationNodeStateMachineTransition::set_disabled(bool p_disabled) { - disabled = p_disabled; - emit_changed(); -} - -bool AnimationNodeStateMachineTransition::is_disabled() const { - return disabled; -} - void AnimationNodeStateMachineTransition::set_priority(int p_priority) { priority = p_priority; emit_changed(); @@ -129,8 +120,8 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_switch_mode", "mode"), &AnimationNodeStateMachineTransition::set_switch_mode); ClassDB::bind_method(D_METHOD("get_switch_mode"), &AnimationNodeStateMachineTransition::get_switch_mode); - ClassDB::bind_method(D_METHOD("set_auto_advance", "auto_advance"), &AnimationNodeStateMachineTransition::set_auto_advance); - ClassDB::bind_method(D_METHOD("has_auto_advance"), &AnimationNodeStateMachineTransition::has_auto_advance); + ClassDB::bind_method(D_METHOD("set_advance_mode", "mode"), &AnimationNodeStateMachineTransition::set_advance_mode); + ClassDB::bind_method(D_METHOD("get_advance_mode"), &AnimationNodeStateMachineTransition::get_advance_mode); ClassDB::bind_method(D_METHOD("set_advance_condition", "name"), &AnimationNodeStateMachineTransition::set_advance_condition); ClassDB::bind_method(D_METHOD("get_advance_condition"), &AnimationNodeStateMachineTransition::get_advance_condition); @@ -141,9 +132,6 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeStateMachineTransition::set_xfade_curve); ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeStateMachineTransition::get_xfade_curve); - ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &AnimationNodeStateMachineTransition::set_disabled); - ClassDB::bind_method(D_METHOD("is_disabled"), &AnimationNodeStateMachineTransition::is_disabled); - ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority); ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority); @@ -155,17 +143,19 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_GROUP("Switch", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance"); ADD_GROUP("Advance", "advance_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "advance_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Auto"), "set_advance_mode", "get_advance_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "advance_expression", PROPERTY_HINT_EXPRESSION, ""), "set_advance_expression", "get_advance_expression"); - ADD_GROUP("Disabling", ""); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE); BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC); BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END); + BIND_ENUM_CONSTANT(ADVANCE_MODE_DISABLED); + BIND_ENUM_CONSTANT(ADVANCE_MODE_ENABLED); + BIND_ENUM_CONSTANT(ADVANCE_MODE_AUTO); + ADD_SIGNAL(MethodInfo("advance_condition_changed")); } @@ -234,7 +224,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta //build open list for (int i = 0; i < p_state_machine->transitions.size(); i++) { - if (p_state_machine->transitions[i].transition->is_disabled()) { + if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) { continue; } @@ -279,7 +269,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta StringName transition = p_state_machine->transitions[least_cost_transition->get()].local_to; for (int i = 0; i < p_state_machine->transitions.size(); i++) { - if (p_state_machine->transitions[i].transition->is_disabled()) { + if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) { continue; } @@ -379,6 +369,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s // can't travel, then teleport path.clear(); current = start_request; + play_start = true; } start_request = StringName(); //clear start request } @@ -424,7 +415,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s fading_pos += p_time; } fade_blend = MIN(1.0, fading_pos / fading_time); - if (fade_blend >= 1.0) { + if (fade_blend > 1.0) { fading_from = StringName(); } } @@ -433,10 +424,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s if (current_curve.is_valid()) { fade_blend = current_curve->sample(fade_blend); } - float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, fade_blend), AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + double rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + double fade_blend_inv = 1.0 - fade_blend; if (fading_from != StringName()) { - p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - fade_blend), AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend_inv) ? CMP_EPSILON : fade_blend_inv, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. } //guess playback position @@ -445,19 +437,19 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } { //advance and loop check - float next_pos = len_current - rem; + double next_pos = len_current - rem; end_loop = next_pos < pos_current; pos_current = next_pos; //looped } //find next StringName next; - float next_xfade = 0.0; + double next_xfade = 0.0; AnimationNodeStateMachineTransition::SwitchMode switch_mode = AnimationNodeStateMachineTransition::SWITCH_MODE_IMMEDIATE; if (path.size()) { for (int i = 0; i < p_state_machine->transitions.size(); i++) { - if (p_state_machine->transitions[i].transition->is_disabled()) { + if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) { continue; } @@ -473,7 +465,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s int auto_advance_to = -1; for (int i = 0; i < p_state_machine->transitions.size(); i++) { - if (p_state_machine->transitions[i].transition->is_disabled()) { + if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) { continue; } @@ -541,7 +533,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s int auto_advance_to = -1; for (int i = 0; i < prev_state_machine->transitions.size(); i++) { - if (prev_state_machine->transitions[i].transition->is_disabled()) { + if (prev_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) { continue; } @@ -628,14 +620,14 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<AnimationNodeStateMachine> state_machine, const Ref<AnimationNodeStateMachineTransition> transition) const { - if (transition->has_auto_advance()) { - return true; + if (transition->get_advance_mode() != AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO) { + return false; } StringName advance_condition_name = transition->get_advance_condition_name(); - if (advance_condition_name != StringName() && bool(state_machine->get_parameter(advance_condition_name))) { - return true; + if (advance_condition_name != StringName() && !bool(state_machine->get_parameter(advance_condition_name))) { + return false; } if (transition->expression.is_valid()) { @@ -645,20 +637,18 @@ bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<Anima NodePath advance_expression_base_node_path = tree_base->get_advance_expression_base_node(); Node *expression_base = tree_base->get_node_or_null(advance_expression_base_node_path); - WARN_PRINT_ONCE("Animation transition has a valid expression, but no expression base node was set on its AnimationTree."); - if (expression_base) { Ref<Expression> exp = transition->expression; bool ret = exp->execute(Array(), expression_base, false, Engine::get_singleton()->is_editor_hint()); // Avoids allowing the user to crash the system with an expression by only allowing const calls. - if (!exp->has_execute_failed()) { - if (ret) { - return true; - } + if (exp->has_execute_failed() || !ret) { + return false; } + } else { + WARN_PRINT_ONCE("Animation transition has a valid expression, but no expression base node was set on its AnimationTree."); } } - return false; + return true; } void AnimationNodeStateMachinePlayback::_bind_methods() { @@ -834,7 +824,8 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri _rename_transitions(p_name, p_new_name); - emit_signal("tree_changed"); + emit_changed(); + emit_signal(SNAME("tree_changed")); } void AnimationNodeStateMachine::_rename_transitions(const StringName &p_name, const StringName &p_new_name) { @@ -877,9 +868,8 @@ void AnimationNodeStateMachine::_rename_transitions(const StringName &p_name, co transitions.write[i].to = p_new_name; } - - updating_transitions = false; } + updating_transitions = false; } void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { @@ -1291,6 +1281,7 @@ Vector2 AnimationNodeStateMachine::get_node_position(const StringName &p_name) c } void AnimationNodeStateMachine::_tree_changed() { + emit_changed(); emit_signal(SNAME("tree_changed")); } diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index 0dfe5a3a43..83b5e66491 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -44,14 +44,19 @@ public: SWITCH_MODE_AT_END, }; + enum AdvanceMode { + ADVANCE_MODE_DISABLED, + ADVANCE_MODE_ENABLED, + ADVANCE_MODE_AUTO, + }; + private: SwitchMode switch_mode = SWITCH_MODE_IMMEDIATE; - bool auto_advance = false; + AdvanceMode advance_mode = ADVANCE_MODE_ENABLED; StringName advance_condition; StringName advance_condition_name; float xfade_time = 0.0; Ref<Curve> xfade_curve; - bool disabled = false; int priority = 1; String advance_expression; @@ -65,8 +70,8 @@ public: void set_switch_mode(SwitchMode p_mode); SwitchMode get_switch_mode() const; - void set_auto_advance(bool p_enable); - bool has_auto_advance() const; + void set_advance_mode(AdvanceMode p_mode); + AdvanceMode get_advance_mode() const; void set_advance_condition(const StringName &p_condition); StringName get_advance_condition() const; @@ -82,9 +87,6 @@ public: void set_xfade_curve(const Ref<Curve> &p_curve); Ref<Curve> get_xfade_curve() const; - void set_disabled(bool p_disabled); - bool is_disabled() const; - void set_priority(int p_priority); int get_priority() const; @@ -92,6 +94,7 @@ public: }; VARIANT_ENUM_CAST(AnimationNodeStateMachineTransition::SwitchMode) +VARIANT_ENUM_CAST(AnimationNodeStateMachineTransition::AdvanceMode) class AnimationNodeStateMachine; @@ -111,8 +114,8 @@ class AnimationNodeStateMachinePlayback : public Resource { StringName next; }; - float len_current = 0.0; - float pos_current = 0.0; + double len_current = 0.0; + double pos_current = 0.0; bool end_loop = false; StringName current; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index cc6fadd9b2..ff20724f89 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -691,7 +691,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } } else { if (p_started) { - int first_key = a->track_find_key(i, p_prev_time, true); + int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT); if (first_key >= 0) { indices.push_back(first_key); } @@ -761,7 +761,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } } else { if (p_started) { - int first_key = a->track_find_key(i, p_prev_time, true); + int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT); if (first_key >= 0) { indices.push_back(first_key); } @@ -855,7 +855,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double //find stuff to play List<int> to_play; if (p_started) { - int first_key = a->track_find_key(i, p_prev_time, true); + int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT); if (first_key >= 0) { to_play.push_back(first_key); } @@ -968,7 +968,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double //find stuff to play List<int> to_play; if (p_started) { - int first_key = a->track_find_key(i, p_prev_time, true); + int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT); if (first_key >= 0) { to_play.push_back(first_key); } @@ -1072,25 +1072,34 @@ void AnimationPlayer::_animation_process2(double p_delta, bool p_started) { accum_pass++; bool seeked = c.seeked; // The animation may be changed during process, so it is safer that the state is changed before process. + if (p_delta != 0) { c.seeked = false; } - _animation_process_data(c.current, p_delta, 1.0f, seeked, p_started); + float blend = 1.0; // First animation we play at 100% blend - List<Blend>::Element *prev = nullptr; - for (List<Blend>::Element *E = c.blend.back(); E; E = prev) { + List<Blend>::Element *next = NULL; + for (List<Blend>::Element *E = c.blend.front(); E; E = next) { Blend &b = E->get(); - float blend = b.blend_left / b.blend_time; + // Note: There may be issues if an animation event triggers an animation change while this blend is active, + // so it is best to use "deferred" calls instead of "immediate" for animation events that can trigger new animations. + _animation_process_data(b.data, p_delta, blend, false, false); + blend = 1.0 - b.blend_left / b.blend_time; // This is how much to blend the NEXT animation b.blend_left -= Math::absf(speed_scale * p_delta); - prev = E->prev(); + next = E->next(); if (b.blend_left < 0) { - c.blend.erase(E); + // If the blend of this has finished, we need to remove ALL the previous blends + List<Blend>::Element *prev; + while (E) { + prev = E->prev(); + c.blend.erase(E); + E = prev; + } } - // The effect of animation changes during blending is unknown... - // In that case, we recommends to use method call mode "deferred", not "immediate". - _animation_process_data(b.data, p_delta, blend, false, false); } + + _animation_process_data(c.current, p_delta, blend, seeked, p_started); } void AnimationPlayer::_animation_update_transforms() { @@ -1643,6 +1652,8 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa b.data = c.current; b.blend_time = b.blend_left = blend_time; c.blend.push_back(b); + } else { + c.blend.clear(); } } diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index b06a21dea9..b3408c1509 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -432,7 +432,6 @@ void AnimationNode::_bind_methods() { GDVIRTUAL_BIND(_has_filter); ADD_SIGNAL(MethodInfo("removed_from_graph")); - ADD_SIGNAL(MethodInfo("tree_changed")); BIND_ENUM_CONSTANT(FILTER_IGNORE); @@ -1383,7 +1382,7 @@ void AnimationTree::_process_graph(double p_delta) { } } else { if (seeked) { - int idx = a->track_find_key(i, time, !is_external_seeking); + int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT); if (idx < 0) { continue; } @@ -1406,7 +1405,7 @@ void AnimationTree::_process_graph(double p_delta) { TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track); if (seeked) { - int idx = a->track_find_key(i, time, !is_external_seeking); + int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT); if (idx < 0) { continue; } @@ -1440,7 +1439,7 @@ void AnimationTree::_process_graph(double p_delta) { if (seeked) { //find whatever should be playing - int idx = a->track_find_key(i, time, !is_external_seeking); + int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT); if (idx < 0) { continue; } @@ -1553,7 +1552,7 @@ void AnimationTree::_process_graph(double p_delta) { if (seeked) { //seek - int idx = a->track_find_key(i, time, !is_external_seeking); + int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT); if (idx < 0) { continue; } @@ -2037,6 +2036,10 @@ void AnimationTree::_bind_methods() { BIND_ENUM_CONSTANT(ANIMATION_PROCESS_MANUAL); ADD_SIGNAL(MethodInfo("animation_player_changed")); + + // Signals from AnimationNodes. + ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING_NAME, "anim_name"))); + ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING_NAME, "anim_name"))); } AnimationTree::AnimationTree() { diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index ac9034c6fd..9da1fbda1b 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -300,6 +300,7 @@ void BaseButton::set_toggle_mode(bool p_on) { } toggle_mode = p_on; + update_configuration_warnings(); } bool BaseButton::is_toggle_mode() const { @@ -381,6 +382,7 @@ void BaseButton::set_button_group(const Ref<ButtonGroup> &p_group) { } queue_redraw(); //checkbox changes to radio if set a buttongroup + update_configuration_warnings(); } Ref<ButtonGroup> BaseButton::get_button_group() const { @@ -399,6 +401,16 @@ bool BaseButton::is_shortcut_feedback() const { return shortcut_feedback; } +PackedStringArray BaseButton::get_configuration_warnings() const { + PackedStringArray warnings = Control::get_configuration_warnings(); + + if (get_button_group().is_valid() && !is_toggle_mode()) { + warnings.push_back(RTR("ButtonGroup is intended to be used only with buttons that have toggle_mode set to true.")); + } + + return warnings; +} + void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &BaseButton::set_pressed); ClassDB::bind_method(D_METHOD("is_pressed"), &BaseButton::is_pressed); diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index 3acf535f54..5018aea120 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -136,6 +136,8 @@ public: void set_shortcut_feedback(bool p_feedback); bool is_shortcut_feedback() const; + PackedStringArray get_configuration_warnings() const override; + BaseButton(); ~BaseButton(); }; diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index f46daef127..c84438ed5e 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -115,7 +115,9 @@ void CodeEdit::_notification(int p_what) { const Point2 caret_pos = get_caret_draw_pos(); const int total_height = csb->get_minimum_size().y + code_completion_rect.size.height; - if (caret_pos.y + row_height + total_height > get_size().height) { + const bool can_fit_completion_above = (caret_pos.y - row_height > total_height); + const bool can_fit_completion_below = (caret_pos.y + row_height + total_height <= get_size().height); + if (!can_fit_completion_below && can_fit_completion_above) { code_completion_rect.position.y = (caret_pos.y - total_height - row_height) + line_spacing; } else { code_completion_rect.position.y = caret_pos.y + (line_spacing / 2.0f); @@ -2079,20 +2081,22 @@ void CodeEdit::confirm_code_completion(bool p_replace) { } char32_t last_completion_char_display = display_text[display_text.length() - 1]; + bool last_char_matches = (last_completion_char == next_char || last_completion_char_display == next_char); int pre_brace_pair = get_caret_column(i) > 0 ? _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column(i)) : -1; int post_brace_pair = get_caret_column(i) < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column(i)) : -1; - if (post_brace_pair != -1 && (last_completion_char == next_char || last_completion_char_display == next_char)) { + // Strings do not nest like brackets, so ensure we don't add an additional closing pair. + if (has_string_delimiter(String::chr(last_completion_char)) && post_brace_pair != -1 && last_char_matches) { remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); - } - - if (pre_brace_pair != -1 && pre_brace_pair != post_brace_pair && (last_completion_char == next_char || last_completion_char_display == next_char)) { - remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); - adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); - } else if (auto_brace_completion_enabled && pre_brace_pair != -1 && post_brace_pair == -1) { - insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key, i); - set_caret_column(get_caret_column(i) - auto_brace_completion_pairs[pre_brace_pair].close_key.length(), i == 0, i); + } else { + if (pre_brace_pair != -1 && pre_brace_pair != post_brace_pair && last_char_matches) { + remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); + adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); + } else if (auto_brace_completion_enabled && pre_brace_pair != -1) { + insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key, i); + set_caret_column(get_caret_column(i) - auto_brace_completion_pairs[pre_brace_pair].close_key.length(), i == 0, i); + } } if (pre_brace_pair == -1 && post_brace_pair == -1 && get_caret_column(i) > 0 && get_caret_column(i) < get_line(caret_line).length()) { diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index eb9f9039b7..fa4b57d7a7 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -1577,8 +1577,7 @@ void ColorPicker::_bind_methods() { BIND_ENUM_CONSTANT(SHAPE_NONE); } -ColorPicker::ColorPicker() : - BoxContainer(true) { +ColorPicker::ColorPicker() { HBoxContainer *hb_edit = memnew(HBoxContainer); add_child(hb_edit, false, INTERNAL_MODE_FRONT); hb_edit->set_v_size_flags(SIZE_SHRINK_BEGIN); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 3208676539..799ca2d202 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -68,8 +68,8 @@ public: ~ColorPresetButton(); }; -class ColorPicker : public BoxContainer { - GDCLASS(ColorPicker, BoxContainer); +class ColorPicker : public VBoxContainer { + GDCLASS(ColorPicker, VBoxContainer); public: enum ColorModeType { diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 68af7af5d4..21c7c4ddb4 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1475,10 +1475,18 @@ void Control::set_rotation(real_t p_radians) { _notify_transform(); } +void Control::set_rotation_degrees(real_t p_degrees) { + set_rotation(Math::deg_to_rad(p_degrees)); +} + real_t Control::get_rotation() const { return data.rotation; } +real_t Control::get_rotation_degrees() const { + return Math::rad_to_deg(get_rotation()); +} + void Control::set_pivot_offset(const Vector2 &p_pivot) { if (data.pivot_offset == p_pivot) { return; @@ -3042,6 +3050,7 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_global_position", "position", "keep_offsets"), &Control::set_global_position, DEFVAL(false)); ClassDB::bind_method(D_METHOD("_set_global_position", "position"), &Control::_set_global_position); ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation); + ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Control::set_rotation_degrees); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale); ClassDB::bind_method(D_METHOD("set_pivot_offset", "pivot_offset"), &Control::set_pivot_offset); ClassDB::bind_method(D_METHOD("get_begin"), &Control::get_begin); @@ -3049,6 +3058,7 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("get_position"), &Control::get_position); ClassDB::bind_method(D_METHOD("get_size"), &Control::get_size); ClassDB::bind_method(D_METHOD("get_rotation"), &Control::get_rotation); + ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Control::get_rotation_degrees); ClassDB::bind_method(D_METHOD("get_scale"), &Control::get_scale); ClassDB::bind_method(D_METHOD("get_pivot_offset"), &Control::get_pivot_offset); ClassDB::bind_method(D_METHOD("get_custom_minimum_size"), &Control::get_custom_minimum_size); @@ -3217,6 +3227,7 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_EDITOR), "_set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "_set_global_position", "get_global_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians"), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pivot_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_pivot_offset", "get_pivot_offset"); diff --git a/scene/gui/control.h b/scene/gui/control.h index 12710f3a93..f82fba3b1b 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -451,7 +451,9 @@ public: void set_scale(const Vector2 &p_scale); Vector2 get_scale() const; void set_rotation(real_t p_radians); + void set_rotation_degrees(real_t p_degrees); real_t get_rotation() const; + real_t get_rotation_degrees() const; void set_pivot_offset(const Vector2 &p_pivot); Vector2 get_pivot_offset() const; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index bd39ee3bb3..b8e2c76413 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -822,7 +822,7 @@ void LineEdit::_notification(int p_what) { case HORIZONTAL_ALIGNMENT_FILL: case HORIZONTAL_ALIGNMENT_LEFT: { if (rtl) { - x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - style->get_margin(SIDE_RIGHT) - (text_width))); + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - Math::ceil(style->get_margin(SIDE_RIGHT) + (text_width)))); } else { x_ofs = style->get_offset().x; } @@ -838,7 +838,7 @@ void LineEdit::_notification(int p_what) { if (rtl) { x_ofs = style->get_offset().x; } else { - x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - style->get_margin(SIDE_RIGHT) - (text_width))); + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - Math::ceil(style->get_margin(SIDE_RIGHT) + (text_width)))); } } break; } diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 7219e86f52..2c43ca7143 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -108,6 +108,14 @@ String LinkButton::get_language() const { return language; } +void LinkButton::set_uri(const String &p_uri) { + uri = p_uri; +} + +String LinkButton::get_uri() const { + return uri; +} + void LinkButton::set_underline_mode(UnderlineMode p_underline_mode) { if (underline_mode == p_underline_mode) { return; @@ -121,6 +129,14 @@ LinkButton::UnderlineMode LinkButton::get_underline_mode() const { return underline_mode; } +void LinkButton::pressed() { + if (uri.is_empty()) { + return; + } + + OS::get_singleton()->shell_open(uri); +} + Size2 LinkButton::get_minimum_size() const { return text_buf->get_size(); } @@ -245,6 +261,8 @@ void LinkButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text_direction"), &LinkButton::get_text_direction); ClassDB::bind_method(D_METHOD("set_language", "language"), &LinkButton::set_language); ClassDB::bind_method(D_METHOD("get_language"), &LinkButton::get_language); + ClassDB::bind_method(D_METHOD("set_uri", "uri"), &LinkButton::set_uri); + ClassDB::bind_method(D_METHOD("get_uri"), &LinkButton::get_uri); ClassDB::bind_method(D_METHOD("set_underline_mode", "underline_mode"), &LinkButton::set_underline_mode); ClassDB::bind_method(D_METHOD("get_underline_mode"), &LinkButton::get_underline_mode); ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &LinkButton::set_structured_text_bidi_override); @@ -258,6 +276,7 @@ void LinkButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "uri"), "set_uri", "get_uri"); ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index accd848163..c71872fc86 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -49,6 +49,7 @@ private: String xl_text; Ref<TextLine> text_buf; UnderlineMode underline_mode = UNDERLINE_MODE_ALWAYS; + String uri; String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; @@ -76,6 +77,7 @@ private: void _shape(); protected: + virtual void pressed() override; virtual Size2 get_minimum_size() const override; virtual void _update_theme_item_cache() override; void _notification(int p_what); @@ -84,6 +86,8 @@ protected: public: void set_text(const String &p_text); String get_text() const; + void set_uri(const String &p_uri); + String get_uri() const; void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser); TextServer::StructuredTextParser get_structured_text_bidi_override() const; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index f26e05518e..d5c4fd3f07 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -150,7 +150,12 @@ RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item return it; } } break; - case ITEM_IMAGE: + case ITEM_IMAGE: { + offset += 1; + if (offset > p_position) { + return it; + } + } break; case ITEM_TABLE: { offset += 1; } break; @@ -325,7 +330,11 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) { table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } - table->total_width += table->columns[i].width + theme_cache.table_h_separation; + if (i != col_count - 1) { + table->total_width += table->columns[i].width + theme_cache.table_h_separation; + } else { + table->total_width += table->columns[i].width; + } } // Resize to max_width if needed and distribute the remaining space. @@ -366,6 +375,7 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font idx = 0; table->total_height = 0; table->rows.clear(); + table->rows_baseline.clear(); Vector2 offset; float row_height = 0.0; @@ -379,13 +389,13 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font offset.x += frame->padding.position.x; float yofs = frame->padding.position.y; float prev_h = 0; + float row_baseline = 0.0; for (int i = 0; i < (int)frame->lines.size(); i++) { MutexLock sub_lock(frame->lines[i].text_buf->get_mutex()); frame->lines[i].text_buf->set_width(table->columns[column].width); table->columns[column].width = MAX(table->columns[column].width, ceil(frame->lines[i].text_buf->get_size().x)); frame->lines[i].offset.y = prev_h; - frame->lines[i].offset += offset; float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * theme_cache.line_separation; if (i > 0) { @@ -399,6 +409,9 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font } yofs += h; prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * theme_cache.line_separation; + + frame->lines[i].offset += offset; + row_baseline = MAX(row_baseline, frame->lines[i].text_buf->get_line_ascent(frame->lines[i].text_buf->get_line_count() - 1)); } yofs += frame->padding.size.y; offset.x += table->columns[column].width + theme_cache.table_h_separation + frame->padding.size.x; @@ -410,11 +423,17 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font table->total_height += row_height; offset.y += row_height; table->rows.push_back(row_height); + table->rows_baseline.push_back(table->total_height - row_height + row_baseline); row_height = 0; } idx++; } - l.text_buf->resize_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align); + int row_idx = (table->align_to_row < 0) ? table->rows_baseline.size() - 1 : table->align_to_row; + if (table->rows_baseline.size() != 0 && row_idx < (int)table->rows_baseline.size() - 1) { + l.text_buf->resize_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align, Math::round(table->rows_baseline[row_idx])); + } else { + l.text_buf->resize_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align); + } } break; default: break; @@ -600,7 +619,11 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) { table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } - table->total_width += table->columns[i].width + theme_cache.table_h_separation; + if (i != col_count - 1) { + table->total_width += table->columns[i].width + theme_cache.table_h_separation; + } else { + table->total_width += table->columns[i].width; + } } // Resize to max_width if needed and distribute the remaining space. @@ -641,6 +664,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> idx = 0; table->total_height = 0; table->rows.clear(); + table->rows_baseline.clear(); Vector2 offset; float row_height = 0.0; @@ -654,6 +678,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> offset.x += frame->padding.position.x; float yofs = frame->padding.position.y; float prev_h = 0; + float row_baseline = 0.0; for (int i = 0; i < (int)frame->lines.size(); i++) { MutexLock sub_lock(frame->lines[i].text_buf->get_mutex()); @@ -661,7 +686,6 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> table->columns[column].width = MAX(table->columns[column].width, ceil(frame->lines[i].text_buf->get_size().x)); frame->lines[i].offset.y = prev_h; - frame->lines[i].offset += offset; float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * theme_cache.line_separation; if (i > 0) { @@ -675,6 +699,9 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } yofs += h; prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * theme_cache.line_separation; + + frame->lines[i].offset += offset; + row_baseline = MAX(row_baseline, frame->lines[i].text_buf->get_line_ascent(frame->lines[i].text_buf->get_line_count() - 1)); } yofs += frame->padding.size.y; offset.x += table->columns[column].width + theme_cache.table_h_separation + frame->padding.size.x; @@ -687,12 +714,17 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> table->total_height += row_height; offset.y += row_height; table->rows.push_back(row_height); + table->rows_baseline.push_back(table->total_height - row_height + row_baseline); row_height = 0; } idx++; } - - l.text_buf->add_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align, t_char_count); + int row_idx = (table->align_to_row < 0) ? table->rows_baseline.size() - 1 : table->align_to_row; + if (table->rows_baseline.size() != 0 && row_idx < (int)table->rows_baseline.size() - 1) { + l.text_buf->add_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align, t_char_count, Math::round(table->rows_baseline[row_idx])); + } else { + l.text_buf->add_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align, t_char_count); + } txt += String::chr(0xfffc).repeat(t_char_count); } break; default: @@ -3039,18 +3071,18 @@ void RichTextLabel::add_newline() { queue_redraw(); } -bool RichTextLabel::remove_line(const int p_line) { +bool RichTextLabel::remove_paragraph(const int p_paragraph) { _stop_thread(); MutexLock data_lock(data_mutex); - if (p_line >= (int)current_frame->lines.size() || p_line < 0) { + if (p_paragraph >= (int)current_frame->lines.size() || p_paragraph < 0) { return false; } // Remove all subitems with the same line as that provided. Vector<int> subitem_indices_to_remove; for (int i = 0; i < current->subitems.size(); i++) { - if (current->subitems[i]->line == p_line) { + if (current->subitems[i]->line == p_paragraph) { subitem_indices_to_remove.push_back(i); } } @@ -3060,17 +3092,17 @@ bool RichTextLabel::remove_line(const int p_line) { for (int i = subitem_indices_to_remove.size() - 1; i >= 0; i--) { int subitem_idx = subitem_indices_to_remove[i]; had_newline = had_newline || current->subitems[subitem_idx]->type == ITEM_NEWLINE; - _remove_item(current->subitems[subitem_idx], current->subitems[subitem_idx]->line, p_line); + _remove_item(current->subitems[subitem_idx], current->subitems[subitem_idx]->line, p_paragraph); } if (!had_newline) { - current_frame->lines.remove_at(p_line); + current_frame->lines.remove_at(p_paragraph); if (current_frame->lines.size() == 0) { current_frame->lines.resize(1); } } - if (p_line == 0 && current->subitems.size() > 0) { + if (p_paragraph == 0 && current->subitems.size() > 0) { main->lines[0].from = main; } @@ -3150,7 +3182,8 @@ void RichTextLabel::push_normal() { void RichTextLabel::push_bold() { ERR_FAIL_COND(theme_cache.bold_font.is_null()); - _push_def_font(BOLD_FONT); + ItemFont *item_font = _find_font(current); + _push_def_font((item_font && item_font->def_font == ITALICS_FONT) ? BOLD_ITALICS_FONT : BOLD_FONT); } void RichTextLabel::push_bold_italics() { @@ -3162,7 +3195,8 @@ void RichTextLabel::push_bold_italics() { void RichTextLabel::push_italics() { ERR_FAIL_COND(theme_cache.italics_font.is_null()); - _push_def_font(ITALICS_FONT); + ItemFont *item_font = _find_font(current); + _push_def_font((item_font && item_font->def_font == BOLD_FONT) ? BOLD_ITALICS_FONT : ITALICS_FONT); } void RichTextLabel::push_mono() { @@ -3298,7 +3332,7 @@ void RichTextLabel::push_hint(const String &p_string) { _add_item(item, true); } -void RichTextLabel::push_table(int p_columns, InlineAlignment p_alignment) { +void RichTextLabel::push_table(int p_columns, InlineAlignment p_alignment, int p_align_to_row) { _stop_thread(); MutexLock data_lock(data_mutex); @@ -3308,6 +3342,7 @@ void RichTextLabel::push_table(int p_columns, InlineAlignment p_alignment) { item->columns.resize(p_columns); item->total_width = 0; item->inline_align = p_alignment; + item->align_to_row = p_align_to_row; for (int i = 0; i < (int)item->columns.size(); i++) { item->columns[i].expand = false; item->columns[i].expand_ratio = 1; @@ -3788,6 +3823,8 @@ void RichTextLabel::append_text(const String &p_bbcode) { alignment = INLINE_ALIGNMENT_TOP_TO; } else if (subtag[1] == "center" || subtag[1] == "c") { alignment = INLINE_ALIGNMENT_CENTER_TO; + } else if (subtag[1] == "baseline" || subtag[1] == "l") { + alignment = INLINE_ALIGNMENT_BASELINE_TO; } else if (subtag[1] == "bottom" || subtag[1] == "b") { alignment = INLINE_ALIGNMENT_BOTTOM_TO; } @@ -3809,8 +3846,12 @@ void RichTextLabel::append_text(const String &p_bbcode) { alignment = INLINE_ALIGNMENT_BOTTOM; } } + int row = -1; + if (subtag.size() > 3) { + row = subtag[3].to_int(); + } - push_table(columns, (InlineAlignment)alignment); + push_table(columns, (InlineAlignment)alignment, row); pos = brk_end + 1; tag_stack.push_front("table"); } else if (tag == "cell") { @@ -3862,6 +3903,11 @@ void RichTextLabel::append_text(const String &p_bbcode) { Color color1 = Color::from_string(subtag_a[1], fallback_color); set_cell_row_background_color(color1, color1); } + } else if (subtag_a[0] == "padding") { + Vector<String> subtag_b = subtag_a[1].split(","); + if (subtag_b.size() == 4) { + set_cell_padding(Rect2(subtag_b[0].to_float(), subtag_b[1].to_float(), subtag_b[2].to_float(), subtag_b[3].to_float())); + } } } } @@ -5269,7 +5315,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2(0, 0, 0, 0))); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); - ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line); + ClassDB::bind_method(D_METHOD("remove_paragraph", "paragraph"), &RichTextLabel::remove_paragraph); ClassDB::bind_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::push_font); ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size); ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal); @@ -5287,7 +5333,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_hint", "description"), &RichTextLabel::push_hint); ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline); ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough); - ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGNMENT_TOP)); + ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align", "align_to_row"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGNMENT_TOP), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0))); ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand); ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index b00cc3d055..a4bc1c8e03 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -268,7 +268,9 @@ private: LocalVector<Column> columns; LocalVector<float> rows; + LocalVector<float> rows_baseline; + int align_to_row = -1; int total_width = 0; int total_height = 0; InlineAlignment inline_align = INLINE_ALIGNMENT_TOP; @@ -571,7 +573,7 @@ public: void add_text(const String &p_text); void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER, const Rect2 &p_region = Rect2(0, 0, 0, 0)); void add_newline(); - bool remove_line(const int p_line); + bool remove_paragraph(const int p_paragraph); void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0)); void _push_def_font(DefaultFont p_def_font); void _push_def_font_var(DefaultFont p_def_font, const Ref<Font> &p_font, int p_size = -1); @@ -592,7 +594,7 @@ public: void push_list(int p_level, ListType p_list, bool p_capitalize); void push_meta(const Variant &p_meta); void push_hint(const String &p_string); - void push_table(int p_columns, InlineAlignment p_alignment = INLINE_ALIGNMENT_TOP); + void push_table(int p_columns, InlineAlignment p_alignment = INLINE_ALIGNMENT_TOP, int p_align_to_row = -1); void push_fade(int p_start_index, int p_length); void push_shake(int p_strength, float p_rate, bool p_connected); void push_wave(float p_frequency, float p_amplitude, bool p_connected); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 35cc29d080..79bad44e15 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3785,7 +3785,9 @@ bool Tree::edit_selected() { } else if (c.mode == TreeItem::CELL_MODE_STRING || c.mode == TreeItem::CELL_MODE_RANGE) { Rect2 popup_rect; - Vector2 ofs(0, Math::floor((text_editor->get_size().height - rect.size.height) / 2)); // "floor()" centers vertically. + int value_editor_height = c.mode == TreeItem::CELL_MODE_RANGE ? value_editor->get_minimum_size().height : 0; + // "floor()" centers vertically. + Vector2 ofs(0, Math::floor((MAX(text_editor->get_minimum_size().height, rect.size.height - value_editor_height) - rect.size.height) / 2)); Point2i textedpos = get_screen_position() + rect.position - ofs; cache.text_editor_position = textedpos; @@ -3801,7 +3803,7 @@ bool Tree::edit_selected() { text_editor->select_all(); if (c.mode == TreeItem::CELL_MODE_RANGE) { - popup_rect.size.y += value_editor->get_minimum_size().height; + popup_rect.size.y += value_editor_height; value_editor->show(); updating_value_editor = true; @@ -5281,7 +5283,6 @@ Tree::Tree() { add_child(popup_menu, false, INTERNAL_MODE_FRONT); popup_editor = memnew(Popup); - popup_editor->set_wrap_controls(true); add_child(popup_editor, false, INTERNAL_MODE_FRONT); popup_editor_vb = memnew(VBoxContainer); popup_editor->add_child(popup_editor_vb); @@ -5290,11 +5291,9 @@ Tree::Tree() { text_editor = memnew(LineEdit); popup_editor_vb->add_child(text_editor); text_editor->set_v_size_flags(SIZE_EXPAND_FILL); - text_editor->set_h_size_flags(SIZE_EXPAND_FILL); value_editor = memnew(HSlider); - value_editor->set_v_size_flags(SIZE_EXPAND_FILL); - value_editor->set_h_size_flags(SIZE_EXPAND_FILL); popup_editor_vb->add_child(value_editor); + value_editor->set_v_size_flags(SIZE_EXPAND_FILL); value_editor->hide(); h_scroll = memnew(HScrollBar); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 2563fa5914..f3812eb497 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -144,7 +144,7 @@ void CanvasItem::_redraw_callback() { Transform2D CanvasItem::get_global_transform_with_canvas() const { if (canvas_layer) { - return canvas_layer->get_transform() * get_global_transform(); + return canvas_layer->get_final_transform() * get_global_transform(); } else if (is_inside_tree()) { return get_viewport()->get_canvas_transform() * get_global_transform(); } else { @@ -621,10 +621,10 @@ void CanvasItem::draw_texture_rect_region(const Ref<Texture2D> &p_texture, const p_texture->draw_rect_region(canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_clip_uv); } -void CanvasItem::draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, double p_outline, double p_pixel_range) { +void CanvasItem::draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, double p_outline, double p_pixel_range, double p_scale) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_texture.is_null()); - RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(canvas_item, p_rect, p_texture->get_rid(), p_src_rect, p_modulate, p_outline, p_pixel_range); + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(canvas_item, p_rect, p_texture->get_rid(), p_src_rect, p_modulate, p_outline, p_pixel_range, p_scale); } void CanvasItem::draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate) { @@ -950,14 +950,14 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_self_modulate", "self_modulate"), &CanvasItem::set_self_modulate); ClassDB::bind_method(D_METHOD("get_self_modulate"), &CanvasItem::get_self_modulate); - ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &Node2D::set_z_index); - ClassDB::bind_method(D_METHOD("get_z_index"), &Node2D::get_z_index); + ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &CanvasItem::set_z_index); + ClassDB::bind_method(D_METHOD("get_z_index"), &CanvasItem::get_z_index); - ClassDB::bind_method(D_METHOD("set_z_as_relative", "enable"), &Node2D::set_z_as_relative); - ClassDB::bind_method(D_METHOD("is_z_relative"), &Node2D::is_z_relative); + ClassDB::bind_method(D_METHOD("set_z_as_relative", "enable"), &CanvasItem::set_z_as_relative); + ClassDB::bind_method(D_METHOD("is_z_relative"), &CanvasItem::is_z_relative); - ClassDB::bind_method(D_METHOD("set_y_sort_enabled", "enabled"), &Node2D::set_y_sort_enabled); - ClassDB::bind_method(D_METHOD("is_y_sort_enabled"), &Node2D::is_y_sort_enabled); + ClassDB::bind_method(D_METHOD("set_y_sort_enabled", "enabled"), &CanvasItem::set_y_sort_enabled); + ClassDB::bind_method(D_METHOD("is_y_sort_enabled"), &CanvasItem::is_y_sort_enabled); ClassDB::bind_method(D_METHOD("set_draw_behind_parent", "enable"), &CanvasItem::set_draw_behind_parent); ClassDB::bind_method(D_METHOD("is_draw_behind_parent_enabled"), &CanvasItem::is_draw_behind_parent_enabled); @@ -974,7 +974,7 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture", "rect", "src_rect", "modulate", "transpose", "clip_uv"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("draw_msdf_texture_rect_region", "texture", "rect", "src_rect", "modulate", "outline", "pixel_range"), &CanvasItem::draw_msdf_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(0.0), DEFVAL(4.0)); + ClassDB::bind_method(D_METHOD("draw_msdf_texture_rect_region", "texture", "rect", "src_rect", "modulate", "outline", "pixel_range", "scale"), &CanvasItem::draw_msdf_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(0.0), DEFVAL(4.0), DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("draw_lcd_texture_rect_region", "texture", "rect", "src_rect", "modulate"), &CanvasItem::draw_lcd_texture_rect_region, DEFVAL(Color(1, 1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_style_box", "style_box", "rect"), &CanvasItem::draw_style_box); ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0)); @@ -1099,7 +1099,7 @@ Transform2D CanvasItem::get_canvas_transform() const { ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); if (canvas_layer) { - return canvas_layer->get_transform(); + return canvas_layer->get_final_transform(); } else if (Object::cast_to<CanvasItem>(get_parent())) { return Object::cast_to<CanvasItem>(get_parent())->get_canvas_transform(); } else { @@ -1111,7 +1111,7 @@ Transform2D CanvasItem::get_viewport_transform() const { ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); if (canvas_layer) { - return get_viewport()->get_final_transform() * canvas_layer->get_transform(); + return get_viewport()->get_final_transform() * canvas_layer->get_final_transform(); } else { return get_viewport()->get_final_transform() * get_viewport()->get_canvas_transform(); } diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 4ace982825..bae03d706a 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -259,7 +259,7 @@ public: void draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1)); void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false); void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false); - void draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), double p_outline = 0.0, double p_pixel_range = 4.0); + void draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), double p_outline = 0.0, double p_pixel_range = 4.0, double p_scale = 1.0); void draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1)); void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect); void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>(), real_t p_width = 1); diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 5fde18721a..97b784e9d0 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -88,6 +88,18 @@ Transform2D CanvasLayer::get_transform() const { return transform; } +Transform2D CanvasLayer::get_final_transform() const { + if (is_following_viewport()) { + Transform2D follow; + follow.scale(Vector2(get_follow_viewport_scale(), get_follow_viewport_scale())); + if (vp) { + follow = vp->get_canvas_transform() * follow; + } + return follow * transform; + } + return transform; +} + void CanvasLayer::_update_xform() { transform.set_rotation_and_scale(rot, scale); transform.set_origin(ofs); @@ -304,6 +316,7 @@ void CanvasLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CanvasLayer::set_transform); ClassDB::bind_method(D_METHOD("get_transform"), &CanvasLayer::get_transform); + ClassDB::bind_method(D_METHOD("get_final_transform"), &CanvasLayer::get_final_transform); ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CanvasLayer::set_offset); ClassDB::bind_method(D_METHOD("get_offset"), &CanvasLayer::get_offset); diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index 74b5ebd453..2afc842a50 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -77,6 +77,7 @@ public: void set_transform(const Transform2D &p_xform); Transform2D get_transform() const; + Transform2D get_final_transform() const; void set_offset(const Vector2 &p_offset); Vector2 get_offset() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index ceb5b76ff2..e61cca0a0f 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -594,6 +594,12 @@ void SceneTree::finalize() { timer->release_connections(); } timers.clear(); + + // Cleanup tweens. + for (Ref<Tween> &tween : tweens) { + tween->clear(); + } + tweens.clear(); } void SceneTree::quit(int p_exit_code) { @@ -942,6 +948,9 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal } for (const ObjectID &id : no_context_node_ids) { + if (p_viewport->is_input_handled()) { + break; + } Node *n = Object::cast_to<Node>(ObjectDB::get_instance(id)); if (n) { n->_call_shortcut_input(p_input); @@ -1413,7 +1422,7 @@ SceneTree::SceneTree() { ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/quality/msaa_3d", PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/msaa_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)"))); root->set_msaa_3d(Viewport::MSAA(msaa_mode_3d)); - const bool transparent_background = GLOBAL_DEF("rendering/transparent_background", false); + const bool transparent_background = GLOBAL_DEF("rendering/viewport/transparent_background", false); root->set_transparent_background(transparent_background); const int ssaa_mode = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/screen_space_aa", 0); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index fdbcb20d30..afc31ae480 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -642,7 +642,7 @@ void Viewport::_process_picking() { ObjectID canvas_layer_id; if (E) { // A descendant CanvasLayer. - canvas_layer_transform = E->get_transform(); + canvas_layer_transform = E->get_final_transform(); canvas_layer_id = E->get_instance_id(); } else { // This Viewport's builtin canvas. diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 043895b591..4f300cb5d6 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -56,6 +56,7 @@ #include "scene/2d/navigation_agent_2d.h" #include "scene/2d/navigation_link_2d.h" #include "scene/2d/navigation_obstacle_2d.h" +#include "scene/2d/navigation_region_2d.h" #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" #include "scene/2d/path_2d.h" @@ -163,6 +164,7 @@ #include "scene/resources/mesh_data_tool.h" #include "scene/resources/multimesh.h" #include "scene/resources/navigation_mesh.h" +#include "scene/resources/navigation_polygon.h" #include "scene/resources/packed_scene.h" #include "scene/resources/particle_process_material.h" #include "scene/resources/physics_material.h" diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 077a53464e..37407edc33 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1319,7 +1319,7 @@ Error Animation::blend_shape_track_interpolate(int p_track, double p_time, float } void Animation::track_remove_key_at_time(int p_track, double p_time) { - int idx = track_find_key(p_track, p_time, true); + int idx = track_find_key(p_track, p_time, FIND_MODE_APPROX); ERR_FAIL_COND(idx < 0); track_remove_key(p_track, idx); } @@ -1400,7 +1400,7 @@ void Animation::track_remove_key(int p_track, int p_idx) { emit_changed(); } -int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { +int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; @@ -1416,7 +1416,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { uint32_t key_index; bool fetch_compressed_success = _fetch_compressed<3>(tt->compressed_track, p_time, key, time, key_next, time_next, &key_index); ERR_FAIL_COND_V(!fetch_compressed_success, -1); - if (p_exact && time != p_time) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) { return -1; } return key_index; @@ -1426,7 +1426,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= tt->positions.size()) { return -1; } - if (tt->positions[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(tt->positions[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && tt->positions[k].time != p_time)) { return -1; } return k; @@ -1443,7 +1443,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { uint32_t key_index; bool fetch_compressed_success = _fetch_compressed<3>(rt->compressed_track, p_time, key, time, key_next, time_next, &key_index); ERR_FAIL_COND_V(!fetch_compressed_success, -1); - if (p_exact && time != p_time) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) { return -1; } return key_index; @@ -1453,7 +1453,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= rt->rotations.size()) { return -1; } - if (rt->rotations[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(rt->rotations[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && rt->rotations[k].time != p_time)) { return -1; } return k; @@ -1470,7 +1470,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { uint32_t key_index; bool fetch_compressed_success = _fetch_compressed<3>(st->compressed_track, p_time, key, time, key_next, time_next, &key_index); ERR_FAIL_COND_V(!fetch_compressed_success, -1); - if (p_exact && time != p_time) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) { return -1; } return key_index; @@ -1480,7 +1480,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= st->scales.size()) { return -1; } - if (st->scales[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(st->scales[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && st->scales[k].time != p_time)) { return -1; } return k; @@ -1497,7 +1497,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { uint32_t key_index; bool fetch_compressed_success = _fetch_compressed<1>(bst->compressed_track, p_time, key, time, key_next, time_next, &key_index); ERR_FAIL_COND_V(!fetch_compressed_success, -1); - if (p_exact && time != p_time) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) { return -1; } return key_index; @@ -1507,7 +1507,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= bst->blend_shapes.size()) { return -1; } - if (bst->blend_shapes[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(bst->blend_shapes[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && bst->blend_shapes[k].time != p_time)) { return -1; } return k; @@ -1519,7 +1519,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= vt->values.size()) { return -1; } - if (vt->values[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(vt->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && vt->values[k].time != p_time)) { return -1; } return k; @@ -1531,7 +1531,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= mt->methods.size()) { return -1; } - if (mt->methods[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(mt->methods[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && mt->methods[k].time != p_time)) { return -1; } return k; @@ -1543,7 +1543,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= bt->values.size()) { return -1; } - if (bt->values[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(bt->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && bt->values[k].time != p_time)) { return -1; } return k; @@ -1555,7 +1555,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= at->values.size()) { return -1; } - if (at->values[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(at->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && at->values[k].time != p_time)) { return -1; } return k; @@ -1567,7 +1567,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= at->values.size()) { return -1; } - if (at->values[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(at->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && at->values[k].time != p_time)) { return -1; } return k; @@ -2944,12 +2944,12 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl // Not from_time > to_time but most recent of looping... if (p_looped_flag != Animation::LOOPED_FLAG_NONE) { if (!is_backward && Math::is_equal_approx(from_time, 0)) { - int edge = track_find_key(p_track, 0, true); + int edge = track_find_key(p_track, 0, FIND_MODE_EXACT); if (edge >= 0) { p_indices->push_back(edge); } } else if (is_backward && Math::is_equal_approx(to_time, length)) { - int edge = track_find_key(p_track, length, true); + int edge = track_find_key(p_track, length, FIND_MODE_EXACT); if (edge >= 0) { p_indices->push_back(edge); } @@ -2971,7 +2971,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl const PositionTrack *tt = static_cast<const PositionTrack *>(t); if (tt->compressed_track >= 0) { _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices); - _get_compressed_key_indices_in_range<3>(tt->compressed_track, CMP_EPSILON, to_time, p_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices); } else { _track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices, true); _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices, false); @@ -2981,7 +2981,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl const RotationTrack *rt = static_cast<const RotationTrack *>(t); if (rt->compressed_track >= 0) { _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices); - _get_compressed_key_indices_in_range<3>(rt->compressed_track, CMP_EPSILON, to_time, p_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices); } else { _track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices, true); _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices, false); @@ -3072,7 +3072,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); if (bst->compressed_track >= 0) { _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length - CMP_EPSILON, p_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length, p_indices); } else { _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices, false); _track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices, true); @@ -3109,9 +3109,9 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl // The edge will be pingponged in the next frame and processed there, so let's ignore it now... if (!is_backward && Math::is_equal_approx(to_time, length)) { - to_time = length - CMP_EPSILON; + to_time -= CMP_EPSILON; } else if (is_backward && Math::is_equal_approx(from_time, 0)) { - from_time = CMP_EPSILON; + from_time += CMP_EPSILON; } } break; } @@ -3818,7 +3818,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_get_key_count", "track_idx"), &Animation::track_get_key_count); ClassDB::bind_method(D_METHOD("track_get_key_value", "track_idx", "key_idx"), &Animation::track_get_key_value); ClassDB::bind_method(D_METHOD("track_get_key_time", "track_idx", "key_idx"), &Animation::track_get_key_time); - ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "exact"), &Animation::track_find_key, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST)); ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "track_idx", "interpolation"), &Animation::track_set_interpolation_type); ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type); @@ -3905,6 +3905,10 @@ void Animation::_bind_methods() { BIND_ENUM_CONSTANT(LOOPED_FLAG_NONE); BIND_ENUM_CONSTANT(LOOPED_FLAG_END); BIND_ENUM_CONSTANT(LOOPED_FLAG_START); + + BIND_ENUM_CONSTANT(FIND_MODE_NEAREST); + BIND_ENUM_CONSTANT(FIND_MODE_APPROX); + BIND_ENUM_CONSTANT(FIND_MODE_EXACT); } void Animation::clear() { diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 0ac1279063..b7d5a683db 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -79,6 +79,12 @@ public: LOOPED_FLAG_START, }; + enum FindMode { + FIND_MODE_NEAREST, + FIND_MODE_APPROX, + FIND_MODE_EXACT, + }; + #ifdef TOOLS_ENABLED enum HandleMode { HANDLE_MODE_FREE, @@ -392,7 +398,7 @@ public: void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition); void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value); void track_set_key_time(int p_track, int p_key_idx, double p_time); - int track_find_key(int p_track, double p_time, bool p_exact = false) const; + int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const; void track_remove_key(int p_track, int p_idx); void track_remove_key_at_time(int p_track, double p_time); int track_get_key_count(int p_track) const; @@ -489,6 +495,7 @@ VARIANT_ENUM_CAST(Animation::InterpolationType); VARIANT_ENUM_CAST(Animation::UpdateMode); VARIANT_ENUM_CAST(Animation::LoopMode); VARIANT_ENUM_CAST(Animation::LoopedFlag); +VARIANT_ENUM_CAST(Animation::FindMode); #ifdef TOOLS_ENABLED VARIANT_ENUM_CAST(Animation::HandleMode); VARIANT_ENUM_CAST(Animation::HandleSetMode); diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 0df61871d8..86b806bc4f 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -317,7 +317,7 @@ Vector<Vector<Vector2>> BitMap::_march_square(const Rect2i &p_rect, const Point2 if (sv == 6 || sv == 9) { const Point2i cur_pos(curx, cury); - // Find if this point has occured before. + // Find if this point has occurred before. if (HashMap<Point2i, int>::Iterator found = cross_map.find(cur_pos)) { // Add points after the previous crossing to the result. ret.push_back(_points.slice(found->value + 1, points_size)); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index bc2149a8c6..be9c0dc725 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -790,6 +790,19 @@ void Curve2D::_bake_segment2d_even_length(RBMap<real_t, Vector2> &r_bake, real_t } } +Vector2 Curve2D::_calculate_tangent(const Vector2 &p_begin, const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) { + // Handle corner cases. + if (Math::is_zero_approx(p_t - 0.0f) && p_control_1.is_equal_approx(p_begin)) { + return (p_end - p_begin).normalized(); + } + + if (Math::is_zero_approx(p_t - 1.0f) && p_control_2.is_equal_approx(p_end)) { + return (p_end - p_begin).normalized(); + } + + return p_begin.bezier_derivative(p_control_1, p_control_2, p_end, p_t).normalized(); +} + void Curve2D::_bake() const { if (!baked_cache_dirty) { return; @@ -816,7 +829,7 @@ void Curve2D::_bake() const { return; } - // Tesselate curve to (almost) even length segments + // Tessellate curve to (almost) even length segments { Vector<RBMap<real_t, Vector2>> midpoints = _tessellate_even_length(10, bake_interval); @@ -835,19 +848,19 @@ void Curve2D::_bake() const { // Collect positions and sample tilts and tangents for each baked points. bpw[0] = points[0].position; - bfw[0] = points[0].position.bezier_derivative(points[0].position + points[0].out, points[1].position + points[1].in, points[1].position, 0.0).normalized(); + bfw[0] = _calculate_tangent(points[0].position, points[0].position + points[0].out, points[1].position + points[1].in, points[1].position, 0.0); int pidx = 0; for (int i = 0; i < points.size() - 1; i++) { for (const KeyValue<real_t, Vector2> &E : midpoints[i]) { pidx++; bpw[pidx] = E.value; - bfw[pidx] = points[i].position.bezier_derivative(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key).normalized(); + bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key); } pidx++; bpw[pidx] = points[i + 1].position; - bfw[pidx] = points[i].position.bezier_derivative(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0).normalized(); + bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0); } // Recalculate the baked distances. @@ -1031,10 +1044,11 @@ Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const { real_t nearest_dist = -1.0f; for (int i = 0; i < pc - 1; i++) { + const real_t interval = baked_dist_cache[i + 1] - baked_dist_cache[i]; Vector2 origin = r[i]; - Vector2 direction = (r[i + 1] - origin) / bake_interval; + Vector2 direction = (r[i + 1] - origin) / interval; - real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval); + real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, interval); Vector2 proj = origin + direction * d; real_t dist = proj.distance_squared_to(p_to_point); @@ -1070,10 +1084,13 @@ real_t Curve2D::get_closest_offset(const Vector2 &p_to_point) const { real_t offset = 0.0f; for (int i = 0; i < pc - 1; i++) { + offset = baked_dist_cache[i]; + + const real_t interval = baked_dist_cache[i + 1] - baked_dist_cache[i]; Vector2 origin = r[i]; - Vector2 direction = (r[i + 1] - origin) / bake_interval; + Vector2 direction = (r[i + 1] - origin) / interval; - real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval); + real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, interval); Vector2 proj = origin + direction * d; real_t dist = proj.distance_squared_to(p_to_point); @@ -1082,8 +1099,6 @@ real_t Curve2D::get_closest_offset(const Vector2 &p_to_point) const { nearest = offset + d; nearest_dist = dist; } - - offset += bake_interval; } return nearest; @@ -1480,6 +1495,19 @@ void Curve3D::_bake_segment3d_even_length(RBMap<real_t, Vector3> &r_bake, real_t } } +Vector3 Curve3D::_calculate_tangent(const Vector3 &p_begin, const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) { + // Handle corner cases. + if (Math::is_zero_approx(p_t - 0.0f) && p_control_1.is_equal_approx(p_begin)) { + return (p_end - p_begin).normalized(); + } + + if (Math::is_zero_approx(p_t - 1.0f) && p_control_2.is_equal_approx(p_end)) { + return (p_end - p_begin).normalized(); + } + + return p_begin.bezier_derivative(p_control_1, p_control_2, p_end, p_t).normalized(); +} + void Curve3D::_bake() const { if (!baked_cache_dirty) { return; @@ -1518,7 +1546,7 @@ void Curve3D::_bake() const { return; } - // Step 1: Tesselate curve to (almost) even length segments + // Step 1: Tessellate curve to (almost) even length segments { Vector<RBMap<real_t, Vector3>> midpoints = _tessellate_even_length(10, bake_interval); @@ -1539,7 +1567,7 @@ void Curve3D::_bake() const { // Collect positions and sample tilts and tangents for each baked points. bpw[0] = points[0].position; - bfw[0] = points[0].position.bezier_derivative(points[0].position + points[0].out, points[1].position + points[1].in, points[1].position, 0.0).normalized(); + bfw[0] = _calculate_tangent(points[0].position, points[0].position + points[0].out, points[1].position + points[1].in, points[1].position, 0.0); btw[0] = points[0].tilt; int pidx = 0; @@ -1547,13 +1575,13 @@ void Curve3D::_bake() const { for (const KeyValue<real_t, Vector3> &E : midpoints[i]) { pidx++; bpw[pidx] = E.value; - bfw[pidx] = points[i].position.bezier_derivative(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key).normalized(); + bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key); btw[pidx] = Math::lerp(points[i].tilt, points[i + 1].tilt, E.key); } pidx++; bpw[pidx] = points[i + 1].position; - bfw[pidx] = points[i].position.bezier_derivative(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0).normalized(); + bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0); btw[pidx] = points[i + 1].tilt; } @@ -1621,7 +1649,7 @@ void Curve3D::_bake() const { } real_t dot = forward_ptr[0].dot(forward_ptr[point_count - 1]); - if (dot < 1.0 - UNIT_EPSILON) { // Alignment should not be too tight, or it dosen't work for coarse bake interval. + if (dot < 1.0 - UNIT_EPSILON) { // Alignment should not be too tight, or it doesn't work for coarse bake interval. is_loop = false; } } diff --git a/scene/resources/curve.h b/scene/resources/curve.h index ea3ceabb14..26608c47cd 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -178,6 +178,7 @@ class Curve2D : public Resource { void mark_dirty(); + static Vector2 _calculate_tangent(const Vector2 &p_begin, const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t); void _bake() const; real_t bake_interval = 5.0; @@ -261,6 +262,7 @@ class Curve3D : public Resource { void mark_dirty(); + static Vector3 _calculate_tangent(const Vector3 &p_begin, const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t); void _bake() const; struct Interval { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index f179b4b818..571ddee763 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -100,6 +100,11 @@ static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margi void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &bold_font, const Ref<Font> &bold_italics_font, const Ref<Font> &italics_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale) { scale = p_scale; + // Default theme properties. + theme->set_default_font(default_font); + theme->set_default_font_size(default_font_size * scale); + theme->set_default_base_scale(scale); + // Font colors const Color control_font_color = Color(0.875, 0.875, 0.875); const Color control_font_low_color = Color(0.7, 0.7, 0.7); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index a5e7602c8b..af770ddede 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -1118,7 +1118,19 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { if (sl == -1) { return false; } - int idx = sname.substr(8, sl - 8).to_int() - 1; + int idx = sname.substr(8, sl - 8).to_int(); + + // This is a bit of a hack to ensure compatibility with older material + // overrides that start indexing at 1. + // We assume that idx 0 is always read first, if its not, this won't work. + if (idx == 0) { + surface_index_0 = true; + } + if (!surface_index_0) { + // This means the file was created when the indexing started at 1, so decrease by one. + idx--; + } + String what = sname.get_slicec('/', 1); if (what == "material") { surface_set_material(idx, p_value); @@ -1491,7 +1503,7 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const { if (sl == -1) { return false; } - int idx = sname.substr(8, sl - 8).to_int() - 1; + int idx = sname.substr(8, sl - 8).to_int(); String what = sname.get_slicec('/', 1); if (what == "material") { r_ret = surface_get_material(idx); @@ -1519,11 +1531,11 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const { } for (int i = 0; i < surfaces.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i + 1) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); if (surfaces[i].is_2d) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial", PROPERTY_USAGE_EDITOR)); } else { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_EDITOR)); } } } diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 6f995280e8..fabc09a42c 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -262,6 +262,7 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + bool surface_index_0 = false; virtual void reset_state() override; diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index 7c78b757c7..858146da96 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -57,10 +57,18 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) { _set_item_shapes(idx, p_value); } else if (what == "preview") { set_item_preview(idx, p_value); - } else if (what == "navmesh") { - set_item_navmesh(idx, p_value); - } else if (what == "navmesh_transform") { - set_item_navmesh_transform(idx, p_value); + } else if (what == "navigation_mesh") { + set_item_navigation_mesh(idx, p_value); + } else if (what == "navigation_mesh_transform") { + set_item_navigation_mesh_transform(idx, p_value); +#ifndef DISABLE_DEPRECATED + } else if (what == "navmesh") { // Renamed in 4.0 beta 9. + set_item_navigation_mesh(idx, p_value); + } else if (what == "navmesh_transform") { // Renamed in 4.0 beta 9. + set_item_navigation_mesh_transform(idx, p_value); +#endif // DISABLE_DEPRECATED + } else if (what == "navigation_layers") { + set_item_navigation_layers(idx, p_value); } else { return false; } @@ -85,10 +93,18 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_item_mesh_transform(idx); } else if (what == "shapes") { r_ret = _get_item_shapes(idx); - } else if (what == "navmesh") { - r_ret = get_item_navmesh(idx); - } else if (what == "navmesh_transform") { - r_ret = get_item_navmesh_transform(idx); + } else if (what == "navigation_mesh") { + r_ret = get_item_navigation_mesh(idx); + } else if (what == "navigation_mesh_transform") { + r_ret = get_item_navigation_mesh_transform(idx); +#ifndef DISABLE_DEPRECATED + } else if (what == "navmesh") { // Renamed in 4.0 beta 9. + r_ret = get_item_navigation_mesh(idx); + } else if (what == "navmesh_transform") { // Renamed in 4.0 beta 9. + r_ret = get_item_navigation_mesh_transform(idx); +#endif // DISABLE_DEPRECATED + } else if (what == "navigation_layers") { + r_ret = get_item_navigation_layers(idx); } else if (what == "preview") { r_ret = get_item_preview(idx); } else { @@ -105,8 +121,9 @@ void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("mesh"), PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("mesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); p_list->push_back(PropertyInfo(Variant::ARRAY, prop_name + PNAME("shapes"))); - p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("navmesh"), PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("navmesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); + p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("navigation_mesh"), PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("navigation_mesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); + p_list->push_back(PropertyInfo(Variant::INT, prop_name + PNAME("navigation_layers"), PROPERTY_HINT_LAYERS_3D_NAVIGATION)); p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("preview"), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT)); } } @@ -150,18 +167,27 @@ void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) notify_property_list_changed(); } -void MeshLibrary::set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh) { +void MeshLibrary::set_item_navigation_mesh(int p_item, const Ref<NavigationMesh> &p_navigation_mesh) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); - item_map[p_item].navmesh = p_navmesh; + item_map[p_item].navigation_mesh = p_navigation_mesh; notify_property_list_changed(); notify_change_to_owners(); emit_changed(); notify_property_list_changed(); } -void MeshLibrary::set_item_navmesh_transform(int p_item, const Transform3D &p_transform) { +void MeshLibrary::set_item_navigation_mesh_transform(int p_item, const Transform3D &p_transform) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); - item_map[p_item].navmesh_transform = p_transform; + item_map[p_item].navigation_mesh_transform = p_transform; + notify_change_to_owners(); + emit_changed(); + notify_property_list_changed(); +} + +void MeshLibrary::set_item_navigation_layers(int p_item, uint32_t p_navigation_layers) { + ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); + item_map[p_item].navigation_layers = p_navigation_layers; + notify_property_list_changed(); notify_change_to_owners(); emit_changed(); notify_property_list_changed(); @@ -194,14 +220,19 @@ Vector<MeshLibrary::ShapeData> MeshLibrary::get_item_shapes(int p_item) const { return item_map[p_item].shapes; } -Ref<NavigationMesh> MeshLibrary::get_item_navmesh(int p_item) const { +Ref<NavigationMesh> MeshLibrary::get_item_navigation_mesh(int p_item) const { ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Ref<NavigationMesh>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); - return item_map[p_item].navmesh; + return item_map[p_item].navigation_mesh; } -Transform3D MeshLibrary::get_item_navmesh_transform(int p_item) const { +Transform3D MeshLibrary::get_item_navigation_mesh_transform(int p_item) const { ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform3D(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); - return item_map[p_item].navmesh_transform; + return item_map[p_item].navigation_mesh_transform; +} + +uint32_t MeshLibrary::get_item_navigation_layers(int p_item) const { + ERR_FAIL_COND_V_MSG(!item_map.has(p_item), 0, "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); + return item_map[p_item].navigation_layers; } Ref<Texture2D> MeshLibrary::get_item_preview(int p_item) const { @@ -314,15 +345,17 @@ void MeshLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name); ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh); ClassDB::bind_method(D_METHOD("set_item_mesh_transform", "id", "mesh_transform"), &MeshLibrary::set_item_mesh_transform); - ClassDB::bind_method(D_METHOD("set_item_navmesh", "id", "navmesh"), &MeshLibrary::set_item_navmesh); - ClassDB::bind_method(D_METHOD("set_item_navmesh_transform", "id", "navmesh"), &MeshLibrary::set_item_navmesh_transform); + ClassDB::bind_method(D_METHOD("set_item_navigation_mesh", "id", "navigation_mesh"), &MeshLibrary::set_item_navigation_mesh); + ClassDB::bind_method(D_METHOD("set_item_navigation_mesh_transform", "id", "navigation_mesh"), &MeshLibrary::set_item_navigation_mesh_transform); + ClassDB::bind_method(D_METHOD("set_item_navigation_layers", "id", "navigation_layers"), &MeshLibrary::set_item_navigation_layers); ClassDB::bind_method(D_METHOD("set_item_shapes", "id", "shapes"), &MeshLibrary::_set_item_shapes); ClassDB::bind_method(D_METHOD("set_item_preview", "id", "texture"), &MeshLibrary::set_item_preview); ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name); ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh); ClassDB::bind_method(D_METHOD("get_item_mesh_transform", "id"), &MeshLibrary::get_item_mesh_transform); - ClassDB::bind_method(D_METHOD("get_item_navmesh", "id"), &MeshLibrary::get_item_navmesh); - ClassDB::bind_method(D_METHOD("get_item_navmesh_transform", "id"), &MeshLibrary::get_item_navmesh_transform); + ClassDB::bind_method(D_METHOD("get_item_navigation_mesh", "id"), &MeshLibrary::get_item_navigation_mesh); + ClassDB::bind_method(D_METHOD("get_item_navigation_mesh_transform", "id"), &MeshLibrary::get_item_navigation_mesh_transform); + ClassDB::bind_method(D_METHOD("get_item_navigation_layers", "id"), &MeshLibrary::get_item_navigation_layers); ClassDB::bind_method(D_METHOD("get_item_shapes", "id"), &MeshLibrary::_get_item_shapes); ClassDB::bind_method(D_METHOD("get_item_preview", "id"), &MeshLibrary::get_item_preview); ClassDB::bind_method(D_METHOD("remove_item", "id"), &MeshLibrary::remove_item); diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index 79acb41c4e..1d5af9e176 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -49,11 +49,12 @@ public: struct Item { String name; Ref<Mesh> mesh; + Transform3D mesh_transform; Vector<ShapeData> shapes; Ref<Texture2D> preview; - Transform3D navmesh_transform; - Transform3D mesh_transform; - Ref<NavigationMesh> navmesh; + Ref<NavigationMesh> navigation_mesh; + Transform3D navigation_mesh_transform; + uint32_t navigation_layers = 1; }; RBMap<int, Item> item_map; @@ -74,15 +75,17 @@ public: void set_item_name(int p_item, const String &p_name); void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh); void set_item_mesh_transform(int p_item, const Transform3D &p_transform); - void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh); - void set_item_navmesh_transform(int p_item, const Transform3D &p_transform); + void set_item_navigation_mesh(int p_item, const Ref<NavigationMesh> &p_navigation_mesh); + void set_item_navigation_mesh_transform(int p_item, const Transform3D &p_transform); + void set_item_navigation_layers(int p_item, uint32_t p_navigation_layers); void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes); void set_item_preview(int p_item, const Ref<Texture2D> &p_preview); String get_item_name(int p_item) const; Ref<Mesh> get_item_mesh(int p_item) const; Transform3D get_item_mesh_transform(int p_item) const; - Ref<NavigationMesh> get_item_navmesh(int p_item) const; - Transform3D get_item_navmesh_transform(int p_item) const; + Ref<NavigationMesh> get_item_navigation_mesh(int p_item) const; + Transform3D get_item_navigation_mesh_transform(int p_item) const; + uint32_t get_item_navigation_layers(int p_item) const; Vector<ShapeData> get_item_shapes(int p_item) const; Ref<Texture2D> get_item_preview(int p_item) const; diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index ff4a7a4560..8afb0563b2 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -242,6 +242,7 @@ void MultiMesh::set_instance_transform(int p_instance, const Transform3D &p_tran void MultiMesh::set_instance_transform_2d(int p_instance, const Transform2D &p_transform) { RenderingServer::get_singleton()->multimesh_instance_set_transform_2d(multimesh, p_instance, p_transform); + emit_changed(); } Transform3D MultiMesh::get_instance_transform(int p_instance) const { diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index de97498674..bf4291869f 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -225,13 +225,13 @@ float NavigationMesh::get_edge_max_error() const { return edge_max_error; } -void NavigationMesh::set_verts_per_poly(float p_value) { +void NavigationMesh::set_vertices_per_polygon(float p_value) { ERR_FAIL_COND(p_value < 3); - verts_per_poly = p_value; + vertices_per_polygon = p_value; } -float NavigationMesh::get_verts_per_poly() const { - return verts_per_poly; +float NavigationMesh::get_vertices_per_polygon() const { + return vertices_per_polygon; } void NavigationMesh::set_detail_sample_distance(float p_value) { @@ -483,8 +483,8 @@ void NavigationMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_edge_max_error", "edge_max_error"), &NavigationMesh::set_edge_max_error); ClassDB::bind_method(D_METHOD("get_edge_max_error"), &NavigationMesh::get_edge_max_error); - ClassDB::bind_method(D_METHOD("set_verts_per_poly", "verts_per_poly"), &NavigationMesh::set_verts_per_poly); - ClassDB::bind_method(D_METHOD("get_verts_per_poly"), &NavigationMesh::get_verts_per_poly); + ClassDB::bind_method(D_METHOD("set_vertices_per_polygon", "vertices_per_polygon"), &NavigationMesh::set_vertices_per_polygon); + ClassDB::bind_method(D_METHOD("get_vertices_per_polygon"), &NavigationMesh::get_vertices_per_polygon); ClassDB::bind_method(D_METHOD("set_detail_sample_distance", "detail_sample_dist"), &NavigationMesh::set_detail_sample_distance); ClassDB::bind_method(D_METHOD("get_detail_sample_distance"), &NavigationMesh::get_detail_sample_distance); @@ -527,9 +527,9 @@ void NavigationMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_PROPERTY_DEFAULT("geometry_collision_mask", 0xFFFFFFFF); - ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_source_geometry_mode", PROPERTY_HINT_ENUM, "NavMesh Children,Group With Children,Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_source_geometry_mode", PROPERTY_HINT_ENUM, "Root Node Children,Group With Children,Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "geometry_source_group_name"), "set_source_group_name", "get_source_group_name"); - ADD_PROPERTY_DEFAULT("geometry_source_group_name", StringName("navmesh")); + ADD_PROPERTY_DEFAULT("geometry_source_group_name", StringName("navigation_mesh_source_group")); ADD_GROUP("Cells", "cell_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.01,500.0,0.01,or_greater,suffix:m"), "set_cell_size", "get_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_height", PROPERTY_HINT_RANGE, "0.01,500.0,0.01,or_greater,suffix:m"), "set_cell_height", "get_cell_height"); @@ -544,8 +544,8 @@ void NavigationMesh::_bind_methods() { ADD_GROUP("Edges", "edge_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_max_length", PROPERTY_HINT_RANGE, "0.0,50.0,0.01,or_greater,suffix:m"), "set_edge_max_length", "get_edge_max_length"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_max_error", PROPERTY_HINT_RANGE, "0.1,3.0,0.01,or_greater,suffix:m"), "set_edge_max_error", "get_edge_max_error"); - ADD_GROUP("Polygons", "polygon_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "polygon_verts_per_poly", PROPERTY_HINT_RANGE, "3.0,12.0,1.0,or_greater"), "set_verts_per_poly", "get_verts_per_poly"); + ADD_GROUP("Polygons", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vertices_per_polygon", PROPERTY_HINT_RANGE, "3.0,12.0,1.0,or_greater"), "set_vertices_per_polygon", "get_vertices_per_polygon"); ADD_GROUP("Details", "detail_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail_sample_distance", PROPERTY_HINT_RANGE, "0.1,16.0,0.01,or_greater,suffix:m"), "set_detail_sample_distance", "get_detail_sample_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail_sample_max_error", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater,suffix:m"), "set_detail_sample_max_error", "get_detail_sample_max_error"); @@ -566,7 +566,7 @@ void NavigationMesh::_bind_methods() { BIND_ENUM_CONSTANT(PARSED_GEOMETRY_BOTH); BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MAX); - BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_NAVMESH_CHILDREN); + BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_ROOT_NODE_CHILDREN); BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN); BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_EXPLICIT); BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_MAX); @@ -581,7 +581,7 @@ void NavigationMesh::_validate_property(PropertyInfo &p_property) const { } if (p_property.name == "geometry_source_group_name") { - if (source_geometry_mode == SOURCE_GEOMETRY_NAVMESH_CHILDREN) { + if (source_geometry_mode == SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) { p_property.usage = PROPERTY_USAGE_NONE; return; } @@ -590,35 +590,16 @@ void NavigationMesh::_validate_property(PropertyInfo &p_property) const { #ifndef DISABLE_DEPRECATED bool NavigationMesh::_set(const StringName &p_name, const Variant &p_value) { - String prop_name = p_name; - if (prop_name.find("/") != -1) { - // Compatibility with pre-3.5 "category/path" property names. - prop_name = prop_name.replace("/", "_"); - if (prop_name == "sample_partition_type_sample_partition_type") { - set("sample_partition_type", p_value); - } else if (prop_name == "filter_filter_walkable_low_height_spans") { - set("filter_walkable_low_height_spans", p_value); - } else { - set(prop_name, p_value); - } - + if (p_name == "polygon_verts_per_poly") { // Renamed in 4.0 beta 9. + set_vertices_per_polygon(p_value); return true; } return false; } bool NavigationMesh::_get(const StringName &p_name, Variant &r_ret) const { - String prop_name = p_name; - if (prop_name.find("/") != -1) { - // Compatibility with pre-3.5 "category/path" property names. - prop_name = prop_name.replace("/", "_"); - if (prop_name == "sample_partition_type_sample_partition_type") { - r_ret = get("sample_partition_type"); - } else if (prop_name == "filter_filter_walkable_low_height_spans") { - r_ret = get("filter_walkable_low_height_spans"); - } else { - r_ret = get(prop_name); - } + if (p_name == "polygon_verts_per_poly") { // Renamed in 4.0 beta 9. + r_ret = get_vertices_per_polygon(); return true; } return false; diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index 5ddbd75dcb..3d072423db 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -84,7 +84,7 @@ public: }; enum SourceGeometryMode { - SOURCE_GEOMETRY_NAVMESH_CHILDREN = 0, + SOURCE_GEOMETRY_ROOT_NODE_CHILDREN = 0, SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN, SOURCE_GEOMETRY_GROUPS_EXPLICIT, SOURCE_GEOMETRY_MAX @@ -101,7 +101,7 @@ protected: float region_merge_size = 20.0f; float edge_max_length = 12.0f; float edge_max_error = 1.3f; - float verts_per_poly = 6.0f; + float vertices_per_polygon = 6.0f; float detail_sample_distance = 6.0f; float detail_sample_max_error = 1.0f; @@ -109,8 +109,8 @@ protected: ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES; uint32_t collision_mask = 0xFFFFFFFF; - SourceGeometryMode source_geometry_mode = SOURCE_GEOMETRY_NAVMESH_CHILDREN; - StringName source_group_name = "navmesh"; + SourceGeometryMode source_geometry_mode = SOURCE_GEOMETRY_ROOT_NODE_CHILDREN; + StringName source_group_name = "navigation_mesh_source_group"; bool filter_low_hanging_obstacles = false; bool filter_ledge_spans = false; @@ -168,8 +168,8 @@ public: void set_edge_max_error(float p_value); float get_edge_max_error() const; - void set_verts_per_poly(float p_value); - float get_verts_per_poly() const; + void set_vertices_per_polygon(float p_value); + float get_vertices_per_polygon() const; void set_detail_sample_distance(float p_value); float get_detail_sample_distance() const; diff --git a/scene/resources/navigation_polygon.cpp b/scene/resources/navigation_polygon.cpp new file mode 100644 index 0000000000..04077e95a7 --- /dev/null +++ b/scene/resources/navigation_polygon.cpp @@ -0,0 +1,354 @@ +/*************************************************************************/ +/* navigation_polygon.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "navigation_polygon.h" + +#include "core/core_string_names.h" +#include "core/math/geometry_2d.h" +#include "core/os/mutex.h" + +#include "thirdparty/misc/polypartition.h" + +#ifdef TOOLS_ENABLED +Rect2 NavigationPolygon::_edit_get_rect() const { + if (rect_cache_dirty) { + item_rect = Rect2(); + bool first = true; + + for (int i = 0; i < outlines.size(); i++) { + const Vector<Vector2> &outline = outlines[i]; + const int outline_size = outline.size(); + if (outline_size < 3) { + continue; + } + const Vector2 *p = outline.ptr(); + for (int j = 0; j < outline_size; j++) { + if (first) { + item_rect = Rect2(p[j], Vector2(0, 0)); + first = false; + } else { + item_rect.expand_to(p[j]); + } + } + } + + rect_cache_dirty = false; + } + return item_rect; +} + +bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + for (int i = 0; i < outlines.size(); i++) { + const Vector<Vector2> &outline = outlines[i]; + const int outline_size = outline.size(); + if (outline_size < 3) { + continue; + } + if (Geometry2D::is_point_in_polygon(p_point, Variant(outline))) { + return true; + } + } + return false; +} +#endif + +void NavigationPolygon::set_vertices(const Vector<Vector2> &p_vertices) { + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } + vertices = p_vertices; + rect_cache_dirty = true; +} + +Vector<Vector2> NavigationPolygon::get_vertices() const { + return vertices; +} + +void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array) { + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } + polygons.resize(p_array.size()); + for (int i = 0; i < p_array.size(); i++) { + polygons.write[i].indices = p_array[i]; + } +} + +TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const { + TypedArray<Vector<int32_t>> ret; + ret.resize(polygons.size()); + for (int i = 0; i < ret.size(); i++) { + ret[i] = polygons[i].indices; + } + + return ret; +} + +void NavigationPolygon::_set_outlines(const TypedArray<Vector<Vector2>> &p_array) { + outlines.resize(p_array.size()); + for (int i = 0; i < p_array.size(); i++) { + outlines.write[i] = p_array[i]; + } + rect_cache_dirty = true; +} + +TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const { + TypedArray<Vector<Vector2>> ret; + ret.resize(outlines.size()); + for (int i = 0; i < ret.size(); i++) { + ret[i] = outlines[i]; + } + + return ret; +} + +void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) { + Polygon polygon; + polygon.indices = p_polygon; + polygons.push_back(polygon); + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } +} + +void NavigationPolygon::add_outline_at_index(const Vector<Vector2> &p_outline, int p_index) { + outlines.insert(p_index, p_outline); + rect_cache_dirty = true; +} + +int NavigationPolygon::get_polygon_count() const { + return polygons.size(); +} + +Vector<int> NavigationPolygon::get_polygon(int p_idx) { + ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>()); + return polygons[p_idx].indices; +} + +void NavigationPolygon::clear_polygons() { + polygons.clear(); + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } +} + +Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() { + MutexLock lock(navigation_mesh_generation); + + if (navigation_mesh.is_null()) { + navigation_mesh.instantiate(); + Vector<Vector3> verts; + { + verts.resize(get_vertices().size()); + Vector3 *w = verts.ptrw(); + + const Vector2 *r = get_vertices().ptr(); + + for (int i(0); i < get_vertices().size(); i++) { + w[i] = Vector3(r[i].x, 0.0, r[i].y); + } + } + navigation_mesh->set_vertices(verts); + + for (int i(0); i < get_polygon_count(); i++) { + navigation_mesh->add_polygon(get_polygon(i)); + } + } + + return navigation_mesh; +} + +void NavigationPolygon::add_outline(const Vector<Vector2> &p_outline) { + outlines.push_back(p_outline); + rect_cache_dirty = true; +} + +int NavigationPolygon::get_outline_count() const { + return outlines.size(); +} + +void NavigationPolygon::set_outline(int p_idx, const Vector<Vector2> &p_outline) { + ERR_FAIL_INDEX(p_idx, outlines.size()); + outlines.write[p_idx] = p_outline; + rect_cache_dirty = true; +} + +void NavigationPolygon::remove_outline(int p_idx) { + ERR_FAIL_INDEX(p_idx, outlines.size()); + outlines.remove_at(p_idx); + rect_cache_dirty = true; +} + +Vector<Vector2> NavigationPolygon::get_outline(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, outlines.size(), Vector<Vector2>()); + return outlines[p_idx]; +} + +void NavigationPolygon::clear_outlines() { + outlines.clear(); + rect_cache_dirty = true; +} + +void NavigationPolygon::make_polygons_from_outlines() { + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } + List<TPPLPoly> in_poly, out_poly; + + Vector2 outside_point(-1e10, -1e10); + + for (int i = 0; i < outlines.size(); i++) { + Vector<Vector2> ol = outlines[i]; + int olsize = ol.size(); + if (olsize < 3) { + continue; + } + const Vector2 *r = ol.ptr(); + for (int j = 0; j < olsize; j++) { + outside_point.x = MAX(r[j].x, outside_point.x); + outside_point.y = MAX(r[j].y, outside_point.y); + } + } + + outside_point += Vector2(0.7239784, 0.819238); //avoid precision issues + + for (int i = 0; i < outlines.size(); i++) { + Vector<Vector2> ol = outlines[i]; + int olsize = ol.size(); + if (olsize < 3) { + continue; + } + const Vector2 *r = ol.ptr(); + + int interscount = 0; + //test if this is an outer outline + for (int k = 0; k < outlines.size(); k++) { + if (i == k) { + continue; //no self intersect + } + + Vector<Vector2> ol2 = outlines[k]; + int olsize2 = ol2.size(); + if (olsize2 < 3) { + continue; + } + const Vector2 *r2 = ol2.ptr(); + + for (int l = 0; l < olsize2; l++) { + if (Geometry2D::segment_intersects_segment(r[0], outside_point, r2[l], r2[(l + 1) % olsize2], nullptr)) { + interscount++; + } + } + } + + bool outer = (interscount % 2) == 0; + + TPPLPoly tp; + tp.Init(olsize); + for (int j = 0; j < olsize; j++) { + tp[j] = r[j]; + } + + if (outer) { + tp.SetOrientation(TPPL_ORIENTATION_CCW); + } else { + tp.SetOrientation(TPPL_ORIENTATION_CW); + tp.SetHole(true); + } + + in_poly.push_back(tp); + } + + TPPLPartition tpart; + if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed! + ERR_PRINT("NavigationPolygon: Convex partition failed! Failed to convert outlines to a valid NavigationMesh." + "\nNavigationPolygon outlines can not overlap vertices or edges inside same outline or with other outlines or have any intersections." + "\nAdd the outmost and largest outline first. To add holes inside this outline add the smaller outlines with opposite winding order."); + return; + } + + polygons.clear(); + vertices.clear(); + + HashMap<Vector2, int> points; + for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { + TPPLPoly &tp = I->get(); + + struct Polygon p; + + for (int64_t i = 0; i < tp.GetNumPoints(); i++) { + HashMap<Vector2, int>::Iterator E = points.find(tp[i]); + if (!E) { + E = points.insert(tp[i], vertices.size()); + vertices.push_back(tp[i]); + } + p.indices.push_back(E->value); + } + + polygons.push_back(p); + } + + emit_signal(CoreStringNames::get_singleton()->changed); +} + +void NavigationPolygon::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationPolygon::set_vertices); + ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationPolygon::get_vertices); + + ClassDB::bind_method(D_METHOD("add_polygon", "polygon"), &NavigationPolygon::add_polygon); + ClassDB::bind_method(D_METHOD("get_polygon_count"), &NavigationPolygon::get_polygon_count); + ClassDB::bind_method(D_METHOD("get_polygon", "idx"), &NavigationPolygon::get_polygon); + ClassDB::bind_method(D_METHOD("clear_polygons"), &NavigationPolygon::clear_polygons); + ClassDB::bind_method(D_METHOD("get_navigation_mesh"), &NavigationPolygon::get_navigation_mesh); + + ClassDB::bind_method(D_METHOD("add_outline", "outline"), &NavigationPolygon::add_outline); + ClassDB::bind_method(D_METHOD("add_outline_at_index", "outline", "index"), &NavigationPolygon::add_outline_at_index); + ClassDB::bind_method(D_METHOD("get_outline_count"), &NavigationPolygon::get_outline_count); + ClassDB::bind_method(D_METHOD("set_outline", "idx", "outline"), &NavigationPolygon::set_outline); + ClassDB::bind_method(D_METHOD("get_outline", "idx"), &NavigationPolygon::get_outline); + ClassDB::bind_method(D_METHOD("remove_outline", "idx"), &NavigationPolygon::remove_outline); + ClassDB::bind_method(D_METHOD("clear_outlines"), &NavigationPolygon::clear_outlines); + ClassDB::bind_method(D_METHOD("make_polygons_from_outlines"), &NavigationPolygon::make_polygons_from_outlines); + + ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationPolygon::_set_polygons); + ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationPolygon::_get_polygons); + + ClassDB::bind_method(D_METHOD("_set_outlines", "outlines"), &NavigationPolygon::_set_outlines); + ClassDB::bind_method(D_METHOD("_get_outlines"), &NavigationPolygon::_get_outlines); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); +} diff --git a/scene/resources/navigation_polygon.h b/scene/resources/navigation_polygon.h new file mode 100644 index 0000000000..e943cff04e --- /dev/null +++ b/scene/resources/navigation_polygon.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* navigation_polygon.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NAVIGATION_POLYGON_H +#define NAVIGATION_POLYGON_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/navigation_mesh.h" + +class NavigationPolygon : public Resource { + GDCLASS(NavigationPolygon, Resource); + + Vector<Vector2> vertices; + struct Polygon { + Vector<int> indices; + }; + Vector<Polygon> polygons; + Vector<Vector<Vector2>> outlines; + + mutable Rect2 item_rect; + mutable bool rect_cache_dirty = true; + + Mutex navigation_mesh_generation; + // Navigation mesh + Ref<NavigationMesh> navigation_mesh; + +protected: + static void _bind_methods(); + + void _set_polygons(const TypedArray<Vector<int32_t>> &p_array); + TypedArray<Vector<int32_t>> _get_polygons() const; + + void _set_outlines(const TypedArray<Vector<Vector2>> &p_array); + TypedArray<Vector<Vector2>> _get_outlines() const; + +public: +#ifdef TOOLS_ENABLED + Rect2 _edit_get_rect() const; + bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; +#endif + + void set_vertices(const Vector<Vector2> &p_vertices); + Vector<Vector2> get_vertices() const; + + void add_polygon(const Vector<int> &p_polygon); + int get_polygon_count() const; + + void add_outline(const Vector<Vector2> &p_outline); + void add_outline_at_index(const Vector<Vector2> &p_outline, int p_index); + void set_outline(int p_idx, const Vector<Vector2> &p_outline); + Vector<Vector2> get_outline(int p_idx) const; + void remove_outline(int p_idx); + int get_outline_count() const; + + void clear_outlines(); + void make_polygons_from_outlines(); + + Vector<int> get_polygon(int p_idx); + void clear_polygons(); + + Ref<NavigationMesh> get_navigation_mesh(); + + NavigationPolygon() {} + ~NavigationPolygon() {} +}; + +#endif // NAVIGATION_POLYGON_H diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 2e8fcb3717..54d3676c15 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -2171,6 +2171,24 @@ int TubeTrailMesh::get_section_rings() const { return section_rings; } +void TubeTrailMesh::set_cap_top(bool p_cap_top) { + cap_top = p_cap_top; + _request_update(); +} + +bool TubeTrailMesh::is_cap_top() const { + return cap_top; +} + +void TubeTrailMesh::set_cap_bottom(bool p_cap_bottom) { + cap_bottom = p_cap_bottom; + _request_update(); +} + +bool TubeTrailMesh::is_cap_bottom() const { + return cap_bottom; +} + void TubeTrailMesh::set_curve(const Ref<Curve> &p_curve) { if (curve == p_curve) { return; @@ -2284,49 +2302,21 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { thisrow = point; } - // add top - float scale_pos = 1.0; - if (curve.is_valid() && curve->get_point_count() > 0) { - scale_pos = curve->sample_baked(0); - } - - if (scale_pos > CMP_EPSILON) { - float y = depth * 0.5; - - thisrow = point; - points.push_back(Vector3(0.0, y, 0)); - normals.push_back(Vector3(0.0, 1.0, 0.0)); - ADD_TANGENT(1.0, 0.0, 0.0, 1.0) - uvs.push_back(Vector2(0.25, 0.75)); - point++; - - bone_indices.push_back(0); - bone_indices.push_back(0); - bone_indices.push_back(0); - bone_indices.push_back(0); - - bone_weights.push_back(1.0); - bone_weights.push_back(0); - bone_weights.push_back(0); - bone_weights.push_back(0); - - float rm = radius * scale_pos; - - for (int i = 0; i <= radial_steps; i++) { - float r = i; - r /= radial_steps; - - float x = sin(r * Math_TAU); - float z = cos(r * Math_TAU); + if (cap_top) { + // add top + float scale_pos = 1.0; + if (curve.is_valid() && curve->get_point_count() > 0) { + scale_pos = curve->sample_baked(0); + } - float u = ((x + 1.0) * 0.25); - float v = 0.5 + ((z + 1.0) * 0.25); + if (scale_pos > CMP_EPSILON) { + float y = depth * 0.5; - Vector3 p = Vector3(x * rm, y, z * rm); - points.push_back(p); + thisrow = point; + points.push_back(Vector3(0.0, y, 0)); normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) - uvs.push_back(Vector2(u, v)); + uvs.push_back(Vector2(0.25, 0.75)); point++; bone_indices.push_back(0); @@ -2339,57 +2329,59 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { bone_weights.push_back(0); bone_weights.push_back(0); - if (i > 0) { - indices.push_back(thisrow); - indices.push_back(point - 1); - indices.push_back(point - 2); - } - } - } + float rm = radius * scale_pos; - float scale_neg = 1.0; - if (curve.is_valid() && curve->get_point_count() > 0) { - scale_neg = curve->sample_baked(1.0); - } + for (int i = 0; i <= radial_steps; i++) { + float r = i; + r /= radial_steps; - // add bottom - if (scale_neg > CMP_EPSILON) { - float y = depth * -0.5; + float x = sin(r * Math_TAU); + float z = cos(r * Math_TAU); - thisrow = point; - points.push_back(Vector3(0.0, y, 0.0)); - normals.push_back(Vector3(0.0, -1.0, 0.0)); - ADD_TANGENT(1.0, 0.0, 0.0, 1.0) - uvs.push_back(Vector2(0.75, 0.75)); - point++; + float u = ((x + 1.0) * 0.25); + float v = 0.5 + ((z + 1.0) * 0.25); - bone_indices.push_back(sections); - bone_indices.push_back(0); - bone_indices.push_back(0); - bone_indices.push_back(0); + Vector3 p = Vector3(x * rm, y, z * rm); + points.push_back(p); + normals.push_back(Vector3(0.0, 1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(u, v)); + point++; - bone_weights.push_back(1.0); - bone_weights.push_back(0); - bone_weights.push_back(0); - bone_weights.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); - float rm = radius * scale_neg; + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); - for (int i = 0; i <= radial_steps; i++) { - float r = i; - r /= radial_steps; + if (i > 0) { + indices.push_back(thisrow); + indices.push_back(point - 1); + indices.push_back(point - 2); + } + } + } + } - float x = sin(r * Math_TAU); - float z = cos(r * Math_TAU); + if (cap_bottom) { + float scale_neg = 1.0; + if (curve.is_valid() && curve->get_point_count() > 0) { + scale_neg = curve->sample_baked(1.0); + } - float u = 0.5 + ((x + 1.0) * 0.25); - float v = 1.0 - ((z + 1.0) * 0.25); + if (scale_neg > CMP_EPSILON) { + // add bottom + float y = depth * -0.5; - Vector3 p = Vector3(x * rm, y, z * rm); - points.push_back(p); + thisrow = point; + points.push_back(Vector3(0.0, y, 0.0)); normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) - uvs.push_back(Vector2(u, v)); + uvs.push_back(Vector2(0.75, 0.75)); point++; bone_indices.push_back(sections); @@ -2402,10 +2394,40 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { bone_weights.push_back(0); bone_weights.push_back(0); - if (i > 0) { - indices.push_back(thisrow); - indices.push_back(point - 2); - indices.push_back(point - 1); + float rm = radius * scale_neg; + + for (int i = 0; i <= radial_steps; i++) { + float r = i; + r /= radial_steps; + + float x = sin(r * Math_TAU); + float z = cos(r * Math_TAU); + + float u = 0.5 + ((x + 1.0) * 0.25); + float v = 1.0 - ((z + 1.0) * 0.25); + + Vector3 p = Vector3(x * rm, y, z * rm); + points.push_back(p); + normals.push_back(Vector3(0.0, -1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(u, v)); + point++; + + bone_indices.push_back(sections); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); + + if (i > 0) { + indices.push_back(thisrow); + indices.push_back(point - 2); + indices.push_back(point - 1); + } } } } @@ -2435,6 +2457,12 @@ void TubeTrailMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_section_rings", "section_rings"), &TubeTrailMesh::set_section_rings); ClassDB::bind_method(D_METHOD("get_section_rings"), &TubeTrailMesh::get_section_rings); + ClassDB::bind_method(D_METHOD("set_cap_top", "cap_top"), &TubeTrailMesh::set_cap_top); + ClassDB::bind_method(D_METHOD("is_cap_top"), &TubeTrailMesh::is_cap_top); + + ClassDB::bind_method(D_METHOD("set_cap_bottom", "cap_bottom"), &TubeTrailMesh::set_cap_bottom); + ClassDB::bind_method(D_METHOD("is_cap_bottom"), &TubeTrailMesh::is_cap_bottom); + ClassDB::bind_method(D_METHOD("set_curve", "curve"), &TubeTrailMesh::set_curve); ClassDB::bind_method(D_METHOD("get_curve"), &TubeTrailMesh::get_curve); @@ -2447,13 +2475,16 @@ void TubeTrailMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "section_rings", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_rings", "get_section_rings"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); } TubeTrailMesh::TubeTrailMesh() { } -// TUBE TRAIL +// RIBBON TRAIL void RibbonTrailMesh::set_shape(Shape p_shape) { shape = p_shape; diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 06f9781b84..5cef042a18 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -431,6 +431,8 @@ private: int sections = 5; float section_length = 0.2; int section_rings = 3; + bool cap_top = true; + bool cap_bottom = true; Ref<Curve> curve; @@ -456,6 +458,12 @@ public: void set_section_rings(const int p_section_rings); int get_section_rings() const; + void set_cap_top(bool p_cap_top); + bool is_cap_top() const; + + void set_cap_bottom(bool p_cap_bottom); + bool is_cap_bottom() const; + void set_curve(const Ref<Curve> &p_curve); Ref<Curve> get_curve() const; diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index afab44834d..d094563e6a 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -56,8 +56,8 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override); ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextLine::add_string, DEFVAL(""), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); + ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length", "baseline"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align", "baseline"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("set_width", "width"), &TextLine::set_width); ClassDB::bind_method(D_METHOD("get_width"), &TextLine::get_width); @@ -202,15 +202,15 @@ bool TextLine::add_string(const String &p_text, const Ref<Font> &p_font, int p_f return res; } -bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) { - bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); +bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length, float p_baseline) { + bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length, p_baseline); dirty = true; return res; } -bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) { +bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) { const_cast<TextLine *>(this)->_shape(); - return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); + return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align, p_baseline); } Array TextLine::get_objects() const { diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index e70e82cf2b..801a8a8c11 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -76,8 +76,8 @@ public: bool get_preserve_control() const; bool add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", const Variant &p_meta = Variant()); - bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1); - bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER); + bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1, float p_baseline = 0.0); + bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, float p_baseline = 0.0); void set_horizontal_alignment(HorizontalAlignment p_alignment); HorizontalAlignment get_horizontal_alignment() const; diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 59bb24c8b8..d2e85d28e6 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -64,8 +64,8 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap); ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextParagraph::add_string, DEFVAL(""), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); + ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length", "baseline"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align", "baseline"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &TextParagraph::set_alignment); ClassDB::bind_method(D_METHOD("get_alignment"), &TextParagraph::get_alignment); @@ -385,18 +385,18 @@ void TextParagraph::set_bidi_override(const Array &p_override) { lines_dirty = true; } -bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) { +bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length, float p_baseline) { _THREAD_SAFE_METHOD_ - bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); + bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length, p_baseline); lines_dirty = true; return res; } -bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) { +bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) { _THREAD_SAFE_METHOD_ - bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); + bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align, p_baseline); lines_dirty = true; return res; } diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index 0fe82b4364..dced278db4 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -95,8 +95,8 @@ public: void clear_dropcap(); bool add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", const Variant &p_meta = Variant()); - bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1); - bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER); + bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1, float p_baseline = 0.0); + bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, float p_baseline = 0.0); void set_alignment(HorizontalAlignment p_alignment); HorizontalAlignment get_alignment() const; diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 2106619a6b..18915e294e 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -1591,35 +1591,28 @@ bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, return false; } - Rect2 rc = region; - Rect2 src = p_src_rect; if (src.size == Size2()) { - src.size = rc.size; + src.size = region.size; } Vector2 scale = p_rect.size / src.size; - src.position += (rc.position - margin.position); - Rect2 src_c = rc.intersection(src); - if (src_c.size == Size2()) { + src.position += (region.position - margin.position); + Rect2 src_clipped = region.intersection(src); + if (src_clipped.size == Size2()) { return false; } - Vector2 ofs = (src_c.position - src.position); + Vector2 ofs = (src_clipped.position - src.position); if (scale.x < 0) { - float mx = (margin.size.width - margin.position.x); - mx -= margin.position.x; - ofs.x = -(ofs.x + mx); + ofs.x += (src_clipped.size.x - src.size.x); } if (scale.y < 0) { - float my = margin.size.height - margin.position.y; - my -= margin.position.y; - ofs.y = -(ofs.y + my); + ofs.y += (src_clipped.size.y - src.size.y); } - Rect2 dr(p_rect.position + ofs * scale, src_c.size * scale); - r_rect = dr; - r_src_rect = src_c; + r_rect = Rect2(p_rect.position + ofs * scale, src_clipped.size * scale); + r_src_rect = src_clipped; return true; } diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 9d2537bb4d..0b0461432b 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -35,7 +35,6 @@ #include "core/math/geometry_2d.h" #include "core/templates/local_vector.h" #include "core/templates/rb_set.h" -#include "scene/2d/navigation_region_2d.h" #include "scene/gui/control.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "servers/navigation_server_2d.h" @@ -477,7 +476,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source int new_source_id = p_atlas_source_id_override >= 0 ? p_atlas_source_id_override : next_source_id; sources[new_source_id] = p_tile_set_source; - source_ids.append(new_source_id); + source_ids.push_back(new_source_id); source_ids.sort(); p_tile_set_source->set_tile_set(this); _compute_next_source_id(); @@ -517,7 +516,7 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) { sources.erase(p_source_id); source_ids.erase(p_source_id); - source_ids.append(p_new_source_id); + source_ids.push_back(p_new_source_id); source_ids.sort(); _compute_next_source_id(); @@ -1297,7 +1296,7 @@ void TileSet::cleanup_invalid_tile_proxies() { Vector<int> source_to_remove; for (const KeyValue<int, int> &E : source_level_proxies) { if (has_source(E.key)) { - source_to_remove.append(E.key); + source_to_remove.push_back(E.key); } } for (int i = 0; i < source_to_remove.size(); i++) { @@ -1309,7 +1308,7 @@ void TileSet::cleanup_invalid_tile_proxies() { for (const KeyValue<Array, Array> &E : coords_level_proxies) { Array a = E.key; if (has_source(a[0]) && get_source(a[0])->has_tile(a[1])) { - coords_to_remove.append(a); + coords_to_remove.push_back(a); } } for (int i = 0; i < coords_to_remove.size(); i++) { @@ -1322,7 +1321,7 @@ void TileSet::cleanup_invalid_tile_proxies() { for (const KeyValue<Array, Array> &E : alternative_level_proxies) { Array a = E.key; if (has_source(a[0]) && get_source(a[0])->has_tile(a[1]) && get_source(a[0])->has_alternative_tile(a[1], a[2])) { - alternative_to_remove.append(a); + alternative_to_remove.push_back(a); } } for (int i = 0; i < alternative_to_remove.size(); i++) { @@ -1439,16 +1438,18 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti Vector<Vector2> TileSet::get_tile_shape_polygon() { Vector<Vector2> points; if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { - points.append(Vector2(-0.5, -0.5)); - points.append(Vector2(0.5, -0.5)); - points.append(Vector2(0.5, 0.5)); - points.append(Vector2(-0.5, 0.5)); + points.push_back(Vector2(-0.5, -0.5)); + points.push_back(Vector2(0.5, -0.5)); + points.push_back(Vector2(0.5, 0.5)); + points.push_back(Vector2(-0.5, 0.5)); + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + points.push_back(Vector2(0.0, -0.5)); + points.push_back(Vector2(-0.5, 0.0)); + points.push_back(Vector2(0.0, 0.5)); + points.push_back(Vector2(0.5, 0.0)); } else { float overlap = 0.0; switch (tile_shape) { - case TileSet::TILE_SHAPE_ISOMETRIC: - overlap = 0.5; - break; case TileSet::TILE_SHAPE_HEXAGON: overlap = 0.25; break; @@ -1459,12 +1460,13 @@ Vector<Vector2> TileSet::get_tile_shape_polygon() { break; } - points.append(Vector2(0.0, -0.5)); - points.append(Vector2(-0.5, overlap - 0.5)); - points.append(Vector2(-0.5, 0.5 - overlap)); - points.append(Vector2(0.0, 0.5)); - points.append(Vector2(0.5, 0.5 - overlap)); - points.append(Vector2(0.5, overlap - 0.5)); + points.push_back(Vector2(0.0, -0.5)); + points.push_back(Vector2(-0.5, overlap - 0.5)); + points.push_back(Vector2(-0.5, 0.5 - overlap)); + points.push_back(Vector2(0.0, 0.5)); + points.push_back(Vector2(0.5, 0.5 - overlap)); + points.push_back(Vector2(0.5, overlap - 0.5)); + if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { for (int i = 0; i < points.size(); i++) { points.write[i] = Vector2(points[i].y, points[i].x); @@ -3854,7 +3856,7 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) tiles[coords].alternatives[alternative_id] = memnew(TileData); tiles[coords].alternatives[alternative_id]->set_tile_set(tile_set); tiles[coords].alternatives[alternative_id]->set_allow_transform(alternative_id > 0); - tiles[coords].alternatives_ids.append(alternative_id); + tiles[coords].alternatives_ids.push_back(alternative_id); } if (components.size() >= 3) { bool valid; @@ -4028,11 +4030,11 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector tad.alternatives[0]->set_allow_transform(false); tad.alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); tad.alternatives[0]->notify_property_list_changed(); - tad.alternatives_ids.append(0); + tad.alternatives_ids.push_back(0); // Create and resize the tile. tiles.insert(p_atlas_coords, tad); - tiles_ids.append(p_atlas_coords); + tiles_ids.push_back(p_atlas_coords); tiles_ids.sort(); _create_coords_mapping_cache(p_atlas_coords); @@ -4343,7 +4345,7 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_ tiles.erase(p_atlas_coords); tiles_ids.erase(p_atlas_coords); - tiles_ids.append(new_atlas_coords); + tiles_ids.push_back(new_atlas_coords); tiles_ids.sort(); } tiles[new_atlas_coords].size_in_atlas = new_size; @@ -4365,7 +4367,7 @@ int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, i tiles[p_atlas_coords].alternatives[new_alternative_id]->set_allow_transform(true); tiles[p_atlas_coords].alternatives[new_alternative_id]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); tiles[p_atlas_coords].alternatives[new_alternative_id]->notify_property_list_changed(); - tiles[p_atlas_coords].alternatives_ids.append(new_alternative_id); + tiles[p_atlas_coords].alternatives_ids.push_back(new_alternative_id); tiles[p_atlas_coords].alternatives_ids.sort(); _compute_next_alternative_id(p_atlas_coords); @@ -4395,7 +4397,7 @@ void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, ERR_FAIL_COND_MSG(tiles[p_atlas_coords].alternatives.has(p_new_id), vformat("TileSetAtlasSource has already an alternative with id %d at %s.", p_new_id, String(p_atlas_coords))); tiles[p_atlas_coords].alternatives[p_new_id] = tiles[p_atlas_coords].alternatives[p_alternative_tile]; - tiles[p_atlas_coords].alternatives_ids.append(p_new_id); + tiles[p_atlas_coords].alternatives_ids.push_back(p_new_id); tiles[p_atlas_coords].alternatives.erase(p_alternative_tile); tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile); @@ -4683,7 +4685,7 @@ int TileSetScenesCollectionSource::create_scene_tile(Ref<PackedScene> p_packed_s int new_scene_id = p_id_override >= 0 ? p_id_override : next_scene_id; scenes[new_scene_id] = SceneData(); - scenes_ids.append(new_scene_id); + scenes_ids.push_back(new_scene_id); scenes_ids.sort(); set_scene_tile_scene(new_scene_id, p_packed_scene); _compute_next_alternative_id(); @@ -4700,7 +4702,7 @@ void TileSetScenesCollectionSource::set_scene_tile_id(int p_id, int p_new_id) { scenes[p_new_id] = SceneData(); scenes[p_new_id] = scenes[p_id]; - scenes_ids.append(p_new_id); + scenes_ids.push_back(p_new_id); scenes_ids.sort(); _compute_next_alternative_id(); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 9f465a17e9..88bbbd157a 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -36,10 +36,10 @@ #include "core/templates/local_vector.h" #include "core/templates/rb_set.h" #include "scene/2d/light_occluder_2d.h" -#include "scene/2d/navigation_region_2d.h" #include "scene/main/canvas_item.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/navigation_polygon.h" #include "scene/resources/packed_scene.h" #include "scene/resources/physics_material.h" #include "scene/resources/shape_2d.h" diff --git a/scene/theme/theme_db.cpp b/scene/theme/theme_db.cpp index 0268a685fe..ae64313741 100644 --- a/scene/theme/theme_db.cpp +++ b/scene/theme/theme_db.cpp @@ -70,7 +70,9 @@ void ThemeDB::initialize_theme() { Ref<Font> font; if (!font_path.is_empty()) { font = ResourceLoader::load(font_path); - if (!font.is_valid()) { + if (font.is_valid()) { + set_fallback_font(font); + } else { ERR_PRINT("Error loading custom font '" + font_path + "'"); } } @@ -84,9 +86,6 @@ void ThemeDB::initialize_theme() { Ref<Theme> theme = ResourceLoader::load(theme_path); if (theme.is_valid()) { set_project_theme(theme); - if (font.is_valid()) { - set_fallback_font(font); - } } else { ERR_PRINT("Error loading custom theme '" + theme_path + "'"); } diff --git a/scene/theme/theme_owner.cpp b/scene/theme/theme_owner.cpp index e89aa1b28d..44e0c5d0c8 100644 --- a/scene/theme/theme_owner.cpp +++ b/scene/theme/theme_owner.cpp @@ -205,16 +205,7 @@ Variant ThemeOwner::get_theme_item_in_types(Theme::DataType p_data_type, const S while (owner_node) { // For each theme resource check the theme types provided and see if p_name exists with any of them. for (const StringName &E : p_theme_types) { - Ref<Theme> owner_theme; - - Control *owner_c = Object::cast_to<Control>(owner_node); - if (owner_c) { - owner_theme = owner_c->get_theme(); - } - Window *owner_w = Object::cast_to<Window>(owner_node); - if (owner_w) { - owner_theme = owner_w->get_theme(); - } + Ref<Theme> owner_theme = _get_owner_node_theme(owner_node); if (owner_theme.is_valid() && owner_theme->has_theme_item(p_data_type, p_name, E)) { return owner_theme->get_theme_item(p_data_type, p_name, E); @@ -254,16 +245,7 @@ bool ThemeOwner::has_theme_item_in_types(Theme::DataType p_data_type, const Stri while (owner_node) { // For each theme resource check the theme types provided and see if p_name exists with any of them. for (const StringName &E : p_theme_types) { - Ref<Theme> owner_theme; - - Control *owner_c = Object::cast_to<Control>(owner_node); - if (owner_c) { - owner_theme = owner_c->get_theme(); - } - Window *owner_w = Object::cast_to<Window>(owner_node); - if (owner_w) { - owner_theme = owner_w->get_theme(); - } + Ref<Theme> owner_theme = _get_owner_node_theme(owner_node); if (owner_theme.is_valid() && owner_theme->has_theme_item(p_data_type, p_name, E)) { return true; @@ -299,16 +281,7 @@ float ThemeOwner::get_theme_default_base_scale() { Node *owner_node = get_owner_node(); while (owner_node) { - Ref<Theme> owner_theme; - - Control *owner_c = Object::cast_to<Control>(owner_node); - if (owner_c) { - owner_theme = owner_c->get_theme(); - } - Window *owner_w = Object::cast_to<Window>(owner_node); - if (owner_w) { - owner_theme = owner_w->get_theme(); - } + Ref<Theme> owner_theme = _get_owner_node_theme(owner_node); if (owner_theme.is_valid() && owner_theme->has_default_base_scale()) { return owner_theme->get_default_base_scale(); @@ -338,16 +311,7 @@ Ref<Font> ThemeOwner::get_theme_default_font() { Node *owner_node = get_owner_node(); while (owner_node) { - Ref<Theme> owner_theme; - - Control *owner_c = Object::cast_to<Control>(owner_node); - if (owner_c) { - owner_theme = owner_c->get_theme(); - } - Window *owner_w = Object::cast_to<Window>(owner_node); - if (owner_w) { - owner_theme = owner_w->get_theme(); - } + Ref<Theme> owner_theme = _get_owner_node_theme(owner_node); if (owner_theme.is_valid() && owner_theme->has_default_font()) { return owner_theme->get_default_font(); @@ -377,16 +341,7 @@ int ThemeOwner::get_theme_default_font_size() { Node *owner_node = get_owner_node(); while (owner_node) { - Ref<Theme> owner_theme; - - Control *owner_c = Object::cast_to<Control>(owner_node); - if (owner_c) { - owner_theme = owner_c->get_theme(); - } - Window *owner_w = Object::cast_to<Window>(owner_node); - if (owner_w) { - owner_theme = owner_w->get_theme(); - } + Ref<Theme> owner_theme = _get_owner_node_theme(owner_node); if (owner_theme.is_valid() && owner_theme->has_default_font_size()) { return owner_theme->get_default_font_size(); @@ -408,3 +363,17 @@ int ThemeOwner::get_theme_default_font_size() { } return ThemeDB::get_singleton()->get_fallback_font_size(); } + +Ref<Theme> ThemeOwner::_get_owner_node_theme(Node *p_owner_node) const { + const Control *owner_c = Object::cast_to<Control>(p_owner_node); + if (owner_c) { + return owner_c->get_theme(); + } + + const Window *owner_w = Object::cast_to<Window>(p_owner_node); + if (owner_w) { + return owner_w->get_theme(); + } + + return Ref<Theme>(); +} diff --git a/scene/theme/theme_owner.h b/scene/theme/theme_owner.h index 59b72c1627..60d60f525c 100644 --- a/scene/theme/theme_owner.h +++ b/scene/theme/theme_owner.h @@ -43,6 +43,7 @@ class ThemeOwner : public Object { Window *owner_window = nullptr; Node *_get_next_owner_node(Node *p_from_node) const; + Ref<Theme> _get_owner_node_theme(Node *p_owner_node) const; public: // Theme owner node. diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 113e728106..84c21f7cd6 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -410,12 +410,12 @@ AudioStreamPlaybackMicrophone::AudioStreamPlaybackMicrophone() { //////////////////////////////// -void AudioStreamRandomizer::add_stream(int p_index) { +void AudioStreamRandomizer::add_stream(int p_index, Ref<AudioStream> p_stream, float p_weight) { if (p_index < 0) { p_index = audio_stream_pool.size(); } ERR_FAIL_COND(p_index > audio_stream_pool.size()); - PoolEntry entry{ nullptr, 1.0f }; + PoolEntry entry{ p_stream, p_weight }; audio_stream_pool.insert(p_index, entry); emit_signal(SNAME("changed")); notify_property_list_changed(); @@ -709,7 +709,7 @@ void AudioStreamRandomizer::_get_property_list(List<PropertyInfo> *p_list) const } void AudioStreamRandomizer::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_stream", "index"), &AudioStreamRandomizer::add_stream); + ClassDB::bind_method(D_METHOD("add_stream", "index", "stream", "weight"), &AudioStreamRandomizer::add_stream, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("move_stream", "index_from", "index_to"), &AudioStreamRandomizer::move_stream); ClassDB::bind_method(D_METHOD("remove_stream", "index"), &AudioStreamRandomizer::remove_stream); diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index c6ae6817e7..0d3f7bca04 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -241,7 +241,7 @@ protected: void _get_property_list(List<PropertyInfo> *p_list) const; public: - void add_stream(int p_index); + void add_stream(int p_index, Ref<AudioStream> p_stream, float p_weight = 1.0); void move_stream(int p_index_from, int p_index_to); void remove_stream(int p_index); diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index e657570f9b..c7d626c159 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -449,12 +449,8 @@ void AudioServer::_mix_step() { case AudioStreamPlaybackListNode::AWAITING_DELETION: case AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION: playback_list.erase(playback, [](AudioStreamPlaybackListNode *p) { - if (p->prev_bus_details) { - delete p->prev_bus_details; - } - if (p->bus_details) { - delete p->bus_details; - } + delete p->prev_bus_details; + delete p->bus_details; p->stream_playback.unref(); delete p; }); diff --git a/servers/navigation/navigation_path_query_parameters_2d.cpp b/servers/navigation/navigation_path_query_parameters_2d.cpp index 574af90be1..717ada3834 100644 --- a/servers/navigation/navigation_path_query_parameters_2d.cpp +++ b/servers/navigation/navigation_path_query_parameters_2d.cpp @@ -105,11 +105,19 @@ Vector2 NavigationPathQueryParameters2D::get_target_position() const { void NavigationPathQueryParameters2D::set_navigation_layers(uint32_t p_navigation_layers) { parameters.navigation_layers = p_navigation_layers; -}; +} uint32_t NavigationPathQueryParameters2D::get_navigation_layers() const { return parameters.navigation_layers; -}; +} + +void NavigationPathQueryParameters2D::set_metadata_flags(BitField<NavigationPathQueryParameters2D::PathMetadataFlags> p_flags) { + parameters.metadata_flags = (int64_t)p_flags; +} + +BitField<NavigationPathQueryParameters2D::PathMetadataFlags> NavigationPathQueryParameters2D::get_metadata_flags() const { + return (int64_t)parameters.metadata_flags; +} void NavigationPathQueryParameters2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pathfinding_algorithm", "pathfinding_algorithm"), &NavigationPathQueryParameters2D::set_pathfinding_algorithm); @@ -130,15 +138,25 @@ void NavigationPathQueryParameters2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationPathQueryParameters2D::set_navigation_layers); ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationPathQueryParameters2D::get_navigation_layers); + ClassDB::bind_method(D_METHOD("set_metadata_flags", "flags"), &NavigationPathQueryParameters2D::set_metadata_flags); + ClassDB::bind_method(D_METHOD("get_metadata_flags"), &NavigationPathQueryParameters2D::get_metadata_flags); + ADD_PROPERTY(PropertyInfo(Variant::RID, "map"), "set_map", "get_map"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "start_position"), "set_start_position", "get_start_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position"), "set_target_position", "get_target_position"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_metadata_flags", "get_metadata_flags"); BIND_ENUM_CONSTANT(PATHFINDING_ALGORITHM_ASTAR); BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_CORRIDORFUNNEL); BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_EDGECENTERED); + + BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_NONE); + BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_TYPES); + BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_RIDS); + BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_OWNERS); + BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_ALL); } diff --git a/servers/navigation/navigation_path_query_parameters_2d.h b/servers/navigation/navigation_path_query_parameters_2d.h index c0ef337a27..aeabe1f690 100644 --- a/servers/navigation/navigation_path_query_parameters_2d.h +++ b/servers/navigation/navigation_path_query_parameters_2d.h @@ -52,6 +52,14 @@ public: PATH_POSTPROCESSING_EDGECENTERED, }; + enum PathMetadataFlags { + PATH_METADATA_INCLUDE_NONE = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_NONE, + PATH_METADATA_INCLUDE_TYPES = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_TYPES, + PATH_METADATA_INCLUDE_RIDS = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_RIDS, + PATH_METADATA_INCLUDE_OWNERS = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_OWNERS, + PATH_METADATA_INCLUDE_ALL = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_ALL + }; + const NavigationUtilities::PathQueryParameters &get_parameters() const { return parameters; } void set_pathfinding_algorithm(const PathfindingAlgorithm p_pathfinding_algorithm); @@ -71,9 +79,13 @@ public: void set_navigation_layers(uint32_t p_navigation_layers); uint32_t get_navigation_layers() const; + + void set_metadata_flags(BitField<NavigationPathQueryParameters2D::PathMetadataFlags> p_flags); + BitField<NavigationPathQueryParameters2D::PathMetadataFlags> get_metadata_flags() const; }; VARIANT_ENUM_CAST(NavigationPathQueryParameters2D::PathfindingAlgorithm); VARIANT_ENUM_CAST(NavigationPathQueryParameters2D::PathPostProcessing); +VARIANT_BITFIELD_CAST(NavigationPathQueryParameters2D::PathMetadataFlags); #endif // NAVIGATION_PATH_QUERY_PARAMETERS_2D_H diff --git a/servers/navigation/navigation_path_query_parameters_3d.cpp b/servers/navigation/navigation_path_query_parameters_3d.cpp index b09448e82e..180af57af5 100644 --- a/servers/navigation/navigation_path_query_parameters_3d.cpp +++ b/servers/navigation/navigation_path_query_parameters_3d.cpp @@ -111,6 +111,14 @@ uint32_t NavigationPathQueryParameters3D::get_navigation_layers() const { return parameters.navigation_layers; } +void NavigationPathQueryParameters3D::set_metadata_flags(BitField<NavigationPathQueryParameters3D::PathMetadataFlags> p_flags) { + parameters.metadata_flags = (int64_t)p_flags; +} + +BitField<NavigationPathQueryParameters3D::PathMetadataFlags> NavigationPathQueryParameters3D::get_metadata_flags() const { + return (int64_t)parameters.metadata_flags; +} + void NavigationPathQueryParameters3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pathfinding_algorithm", "pathfinding_algorithm"), &NavigationPathQueryParameters3D::set_pathfinding_algorithm); ClassDB::bind_method(D_METHOD("get_pathfinding_algorithm"), &NavigationPathQueryParameters3D::get_pathfinding_algorithm); @@ -130,15 +138,25 @@ void NavigationPathQueryParameters3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationPathQueryParameters3D::set_navigation_layers); ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationPathQueryParameters3D::get_navigation_layers); + ClassDB::bind_method(D_METHOD("set_metadata_flags", "flags"), &NavigationPathQueryParameters3D::set_metadata_flags); + ClassDB::bind_method(D_METHOD("get_metadata_flags"), &NavigationPathQueryParameters3D::get_metadata_flags); + ADD_PROPERTY(PropertyInfo(Variant::RID, "map"), "set_map", "get_map"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "start_position"), "set_start_position", "get_start_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position"), "set_target_position", "get_target_position"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_metadata_flags", "get_metadata_flags"); BIND_ENUM_CONSTANT(PATHFINDING_ALGORITHM_ASTAR); BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_CORRIDORFUNNEL); BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_EDGECENTERED); + + BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_NONE); + BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_TYPES); + BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_RIDS); + BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_OWNERS); + BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_ALL); } diff --git a/servers/navigation/navigation_path_query_parameters_3d.h b/servers/navigation/navigation_path_query_parameters_3d.h index b4cf02fc79..386c83dece 100644 --- a/servers/navigation/navigation_path_query_parameters_3d.h +++ b/servers/navigation/navigation_path_query_parameters_3d.h @@ -52,6 +52,14 @@ public: PATH_POSTPROCESSING_EDGECENTERED, }; + enum PathMetadataFlags { + PATH_METADATA_INCLUDE_NONE = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_NONE, + PATH_METADATA_INCLUDE_TYPES = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_TYPES, + PATH_METADATA_INCLUDE_RIDS = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_RIDS, + PATH_METADATA_INCLUDE_OWNERS = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_OWNERS, + PATH_METADATA_INCLUDE_ALL = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_ALL + }; + const NavigationUtilities::PathQueryParameters &get_parameters() const { return parameters; } void set_pathfinding_algorithm(const PathfindingAlgorithm p_pathfinding_algorithm); @@ -71,9 +79,13 @@ public: void set_navigation_layers(uint32_t p_navigation_layers); uint32_t get_navigation_layers() const; + + void set_metadata_flags(BitField<NavigationPathQueryParameters3D::PathMetadataFlags> p_flags); + BitField<NavigationPathQueryParameters3D::PathMetadataFlags> get_metadata_flags() const; }; VARIANT_ENUM_CAST(NavigationPathQueryParameters3D::PathfindingAlgorithm); VARIANT_ENUM_CAST(NavigationPathQueryParameters3D::PathPostProcessing); +VARIANT_BITFIELD_CAST(NavigationPathQueryParameters3D::PathMetadataFlags); #endif // NAVIGATION_PATH_QUERY_PARAMETERS_3D_H diff --git a/servers/navigation/navigation_path_query_result_2d.cpp b/servers/navigation/navigation_path_query_result_2d.cpp index 8a55451e40..e2764daa9a 100644 --- a/servers/navigation/navigation_path_query_result_2d.cpp +++ b/servers/navigation/navigation_path_query_result_2d.cpp @@ -38,15 +38,57 @@ const Vector<Vector2> &NavigationPathQueryResult2D::get_path() const { return path; } +void NavigationPathQueryResult2D::set_path_types(const Vector<int32_t> &p_path_types) { + path_types = p_path_types; +} + +const Vector<int32_t> &NavigationPathQueryResult2D::get_path_types() const { + return path_types; +} + +void NavigationPathQueryResult2D::set_path_rids(const TypedArray<RID> &p_path_rids) { + path_rids = p_path_rids; +} + +TypedArray<RID> NavigationPathQueryResult2D::get_path_rids() const { + return path_rids; +} + +void NavigationPathQueryResult2D::set_path_owner_ids(const Vector<int64_t> &p_path_owner_ids) { + path_owner_ids = p_path_owner_ids; +} + +const Vector<int64_t> &NavigationPathQueryResult2D::get_path_owner_ids() const { + return path_owner_ids; +} + void NavigationPathQueryResult2D::reset() { path.clear(); + path_types.clear(); + path_rids.clear(); + path_owner_ids.clear(); } void NavigationPathQueryResult2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path", "path"), &NavigationPathQueryResult2D::set_path); ClassDB::bind_method(D_METHOD("get_path"), &NavigationPathQueryResult2D::get_path); + ClassDB::bind_method(D_METHOD("set_path_types", "path_types"), &NavigationPathQueryResult2D::set_path_types); + ClassDB::bind_method(D_METHOD("get_path_types"), &NavigationPathQueryResult2D::get_path_types); + + ClassDB::bind_method(D_METHOD("set_path_rids", "path_rids"), &NavigationPathQueryResult2D::set_path_rids); + ClassDB::bind_method(D_METHOD("get_path_rids"), &NavigationPathQueryResult2D::get_path_rids); + + ClassDB::bind_method(D_METHOD("set_path_owner_ids", "path_owner_ids"), &NavigationPathQueryResult2D::set_path_owner_ids); + ClassDB::bind_method(D_METHOD("get_path_owner_ids"), &NavigationPathQueryResult2D::get_path_owner_ids); + ClassDB::bind_method(D_METHOD("reset"), &NavigationPathQueryResult2D::reset); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "path"), "set_path", "get_path"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "path_types"), "set_path_types", "get_path_types"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "path_rids", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_path_rids", "get_path_rids"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT64_ARRAY, "path_owner_ids"), "set_path_owner_ids", "get_path_owner_ids"); + + BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_REGION); + BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_LINK); } diff --git a/servers/navigation/navigation_path_query_result_2d.h b/servers/navigation/navigation_path_query_result_2d.h index da251ef32d..852a9780ef 100644 --- a/servers/navigation/navigation_path_query_result_2d.h +++ b/servers/navigation/navigation_path_query_result_2d.h @@ -38,15 +38,34 @@ class NavigationPathQueryResult2D : public RefCounted { GDCLASS(NavigationPathQueryResult2D, RefCounted); Vector<Vector2> path; + Vector<int32_t> path_types; + TypedArray<RID> path_rids; + Vector<int64_t> path_owner_ids; protected: static void _bind_methods(); public: + enum PathSegmentType { + PATH_SEGMENT_TYPE_REGION = 0, + PATH_SEGMENT_TYPE_LINK = 1, + }; + void set_path(const Vector<Vector2> &p_path); const Vector<Vector2> &get_path() const; + void set_path_types(const Vector<int32_t> &p_path_types); + const Vector<int32_t> &get_path_types() const; + + void set_path_rids(const TypedArray<RID> &p_path_rids); + TypedArray<RID> get_path_rids() const; + + void set_path_owner_ids(const Vector<int64_t> &p_path_owner_ids); + const Vector<int64_t> &get_path_owner_ids() const; + void reset(); }; +VARIANT_ENUM_CAST(NavigationPathQueryResult2D::PathSegmentType); + #endif // NAVIGATION_PATH_QUERY_RESULT_2D_H diff --git a/servers/navigation/navigation_path_query_result_3d.cpp b/servers/navigation/navigation_path_query_result_3d.cpp index 1e28352995..8218adec18 100644 --- a/servers/navigation/navigation_path_query_result_3d.cpp +++ b/servers/navigation/navigation_path_query_result_3d.cpp @@ -38,15 +38,57 @@ const Vector<Vector3> &NavigationPathQueryResult3D::get_path() const { return path; } +void NavigationPathQueryResult3D::set_path_types(const Vector<int32_t> &p_path_types) { + path_types = p_path_types; +} + +const Vector<int32_t> &NavigationPathQueryResult3D::get_path_types() const { + return path_types; +} + +void NavigationPathQueryResult3D::set_path_rids(const TypedArray<RID> &p_path_rids) { + path_rids = p_path_rids; +} + +TypedArray<RID> NavigationPathQueryResult3D::get_path_rids() const { + return path_rids; +} + +void NavigationPathQueryResult3D::set_path_owner_ids(const Vector<int64_t> &p_path_owner_ids) { + path_owner_ids = p_path_owner_ids; +} + +const Vector<int64_t> &NavigationPathQueryResult3D::get_path_owner_ids() const { + return path_owner_ids; +} + void NavigationPathQueryResult3D::reset() { path.clear(); + path_types.clear(); + path_rids.clear(); + path_owner_ids.clear(); } void NavigationPathQueryResult3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path", "path"), &NavigationPathQueryResult3D::set_path); ClassDB::bind_method(D_METHOD("get_path"), &NavigationPathQueryResult3D::get_path); + ClassDB::bind_method(D_METHOD("set_path_types", "path_types"), &NavigationPathQueryResult3D::set_path_types); + ClassDB::bind_method(D_METHOD("get_path_types"), &NavigationPathQueryResult3D::get_path_types); + + ClassDB::bind_method(D_METHOD("set_path_rids", "path_rids"), &NavigationPathQueryResult3D::set_path_rids); + ClassDB::bind_method(D_METHOD("get_path_rids"), &NavigationPathQueryResult3D::get_path_rids); + + ClassDB::bind_method(D_METHOD("set_path_owner_ids", "path_owner_ids"), &NavigationPathQueryResult3D::set_path_owner_ids); + ClassDB::bind_method(D_METHOD("get_path_owner_ids"), &NavigationPathQueryResult3D::get_path_owner_ids); + ClassDB::bind_method(D_METHOD("reset"), &NavigationPathQueryResult3D::reset); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "path"), "set_path", "get_path"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "path_types"), "set_path_types", "get_path_types"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "path_rids", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_path_rids", "get_path_rids"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT64_ARRAY, "path_owner_ids"), "set_path_owner_ids", "get_path_owner_ids"); + + BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_REGION); + BIND_ENUM_CONSTANT(PATH_SEGMENT_TYPE_LINK); } diff --git a/servers/navigation/navigation_path_query_result_3d.h b/servers/navigation/navigation_path_query_result_3d.h index fc143ac389..ce34e18dc1 100644 --- a/servers/navigation/navigation_path_query_result_3d.h +++ b/servers/navigation/navigation_path_query_result_3d.h @@ -32,21 +32,41 @@ #define NAVIGATION_PATH_QUERY_RESULT_3D_H #include "core/object/ref_counted.h" +#include "core/variant/typed_array.h" #include "servers/navigation/navigation_utilities.h" class NavigationPathQueryResult3D : public RefCounted { GDCLASS(NavigationPathQueryResult3D, RefCounted); Vector<Vector3> path; + Vector<int32_t> path_types; + TypedArray<RID> path_rids; + Vector<int64_t> path_owner_ids; protected: static void _bind_methods(); public: + enum PathSegmentType { + PATH_SEGMENT_TYPE_REGION = 0, + PATH_SEGMENT_TYPE_LINK = 1, + }; + void set_path(const Vector<Vector3> &p_path); const Vector<Vector3> &get_path() const; + void set_path_types(const Vector<int32_t> &p_path_types); + const Vector<int32_t> &get_path_types() const; + + void set_path_rids(const TypedArray<RID> &p_path_rids); + TypedArray<RID> get_path_rids() const; + + void set_path_owner_ids(const Vector<int64_t> &p_path_owner_ids); + const Vector<int64_t> &get_path_owner_ids() const; + void reset(); }; +VARIANT_ENUM_CAST(NavigationPathQueryResult3D::PathSegmentType); + #endif // NAVIGATION_PATH_QUERY_RESULT_3D_H diff --git a/servers/navigation/navigation_utilities.h b/servers/navigation/navigation_utilities.h index e99c139528..c22a2ed952 100644 --- a/servers/navigation/navigation_utilities.h +++ b/servers/navigation/navigation_utilities.h @@ -32,6 +32,7 @@ #define NAVIGATION_UTILITIES_H #include "core/math/vector3.h" +#include "core/variant/typed_array.h" namespace NavigationUtilities { @@ -44,6 +45,19 @@ enum PathPostProcessing { PATH_POSTPROCESSING_EDGECENTERED, }; +enum PathSegmentType { + PATH_SEGMENT_TYPE_REGION = 0, + PATH_SEGMENT_TYPE_LINK +}; + +enum PathMetadataFlags { + PATH_INCLUDE_NONE = 0, + PATH_INCLUDE_TYPES = 1, + PATH_INCLUDE_RIDS = 2, + PATH_INCLUDE_OWNERS = 4, + PATH_INCLUDE_ALL = PATH_INCLUDE_TYPES | PATH_INCLUDE_RIDS | PATH_INCLUDE_OWNERS +}; + struct PathQueryParameters { PathfindingAlgorithm pathfinding_algorithm = PATHFINDING_ALGORITHM_ASTAR; PathPostProcessing path_postprocessing = PATH_POSTPROCESSING_CORRIDORFUNNEL; @@ -51,10 +65,14 @@ struct PathQueryParameters { Vector3 start_position; Vector3 target_position; uint32_t navigation_layers = 1; + BitField<PathMetadataFlags> metadata_flags = PATH_INCLUDE_ALL; }; struct PathQueryResult { Vector<Vector3> path; + Vector<int32_t> path_types; + TypedArray<RID> path_rids; + Vector<int64_t> path_owner_ids; }; } //namespace NavigationUtilities diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index 4ed998a77e..6fd5f222cf 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -154,7 +154,7 @@ static ObjectID id_to_id(const ObjectID &id) { static Ref<NavigationMesh> poly_to_mesh(Ref<NavigationPolygon> d) { if (d.is_valid()) { - return d->get_mesh(); + return d->get_navigation_mesh(); } else { return Ref<NavigationMesh>(); } @@ -258,7 +258,7 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer2D::region_set_navigation_layers); ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer2D::region_get_navigation_layers); ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer2D::region_set_transform); - ClassDB::bind_method(D_METHOD("region_set_navpoly", "region", "nav_poly"), &NavigationServer2D::region_set_navpoly); + ClassDB::bind_method(D_METHOD("region_set_navigation_polygon", "region", "navigation_polygon"), &NavigationServer2D::region_set_navigation_polygon); ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer2D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end); @@ -361,8 +361,8 @@ void FORWARD_2_C(region_set_navigation_layers, RID, p_region, uint32_t, p_naviga uint32_t FORWARD_1_C(region_get_navigation_layers, RID, p_region, rid_to_rid); void FORWARD_2_C(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3); -void NavigationServer2D::region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const { - NavigationServer3D::get_singleton()->region_set_navmesh(p_region, poly_to_mesh(p_nav_mesh)); +void NavigationServer2D::region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) const { + NavigationServer3D::get_singleton()->region_set_navigation_mesh(p_region, poly_to_mesh(p_navigation_polygon)); } int FORWARD_1_C(region_get_connections_count, RID, p_region, rid_to_rid); @@ -427,4 +427,7 @@ void NavigationServer2D::query_path(const Ref<NavigationPathQueryParameters2D> & const NavigationUtilities::PathQueryResult _query_result = NavigationServer3D::get_singleton()->_query_path(p_query_parameters->get_parameters()); p_query_result->set_path(vector_v3_to_v2(_query_result.path)); + p_query_result->set_path_types(_query_result.path_types); + p_query_result->set_path_rids(_query_result.path_rids); + p_query_result->set_path_owner_ids(_query_result.path_owner_ids); } diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index cf6011a0f9..9b921969c0 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -34,7 +34,7 @@ #include "core/object/class_db.h" #include "core/templates/rid.h" -#include "scene/2d/navigation_region_2d.h" +#include "scene/resources/navigation_polygon.h" #include "servers/navigation/navigation_path_query_parameters_2d.h" #include "servers/navigation/navigation_path_query_result_2d.h" @@ -126,7 +126,7 @@ public: virtual void region_set_transform(RID p_region, Transform2D p_transform) const; /// Set the navigation poly of this region. - virtual void region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const; + virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) const; /// Get a list of a region's connection to other regions. virtual int region_get_connections_count(RID p_region) const; diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 940dd61f67..6ad07d3053 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -77,8 +77,8 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer3D::region_set_navigation_layers); ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer3D::region_get_navigation_layers); ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer3D::region_set_transform); - ClassDB::bind_method(D_METHOD("region_set_navmesh", "region", "nav_mesh"), &NavigationServer3D::region_set_navmesh); - ClassDB::bind_method(D_METHOD("region_bake_navmesh", "mesh", "node"), &NavigationServer3D::region_bake_navmesh); + ClassDB::bind_method(D_METHOD("region_set_navigation_mesh", "region", "navigation_mesh"), &NavigationServer3D::region_set_navigation_mesh); + ClassDB::bind_method(D_METHOD("region_bake_navigation_mesh", "navigation_mesh", "root_node"), &NavigationServer3D::region_bake_navigation_mesh); ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer3D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_end); @@ -155,7 +155,7 @@ NavigationServer3D::NavigationServer3D() { debug_navigation_enable_link_connections_xray = GLOBAL_DEF("debug/shapes/navigation/enable_link_connections_xray", true); if (Engine::get_singleton()->is_editor_hint()) { - // enable NavigationServer3D when in Editor or else navmesh edge connections are invisible + // enable NavigationServer3D when in Editor or else navigation mesh edge connections are invisible // on runtime tests SceneTree has "Visible Navigation" set and main iteration takes care of this set_debug_enabled(true); } @@ -491,4 +491,7 @@ void NavigationServer3D::query_path(const Ref<NavigationPathQueryParameters3D> & const NavigationUtilities::PathQueryResult _query_result = _query_path(p_query_parameters->get_parameters()); p_query_result->set_path(_query_result.path); + p_query_result->set_path_types(_query_result.path_types); + p_query_result->set_path_rids(_query_result.path_rids); + p_query_result->set_path_owner_ids(_query_result.path_owner_ids); } diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index bf4c22be8a..65d6d19073 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -138,10 +138,10 @@ public: virtual void region_set_transform(RID p_region, Transform3D p_transform) const = 0; /// Set the navigation mesh of this region. - virtual void region_set_navmesh(RID p_region, Ref<NavigationMesh> p_nav_mesh) const = 0; + virtual void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) const = 0; /// Bake the navigation mesh. - virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const = 0; + virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) const = 0; /// Get a list of a region's connection to other regions. virtual int region_get_connections_count(RID p_region) const = 0; diff --git a/servers/physics_2d/godot_body_pair_2d.cpp b/servers/physics_2d/godot_body_pair_2d.cpp index 7b7c67bbc2..1684835e76 100644 --- a/servers/physics_2d/godot_body_pair_2d.cpp +++ b/servers/physics_2d/godot_body_pair_2d.cpp @@ -161,6 +161,11 @@ void GodotBodyPair2D::_validate_contacts() { } } +// _test_ccd prevents tunneling by slowing down a high velocity body that is about to collide so that next frame it will be at an appropriate location to collide (i.e. slight overlap) +// Warning: the way velocity is adjusted down to cause a collision means the momentum will be weaker than it should for a bounce! +// Process: only proceed if body A's motion is high relative to its size. +// cast forward along motion vector to see if A is going to enter/pass B's collider next frame, only proceed if it does. +// adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it. bool GodotBodyPair2D::_test_ccd(real_t p_step, GodotBody2D *p_A, int p_shape_A, const Transform2D &p_xform_A, GodotBody2D *p_B, int p_shape_B, const Transform2D &p_xform_B) { Vector2 motion = p_A->get_linear_velocity() * p_step; real_t mlen = motion.length(); @@ -180,24 +185,32 @@ bool GodotBodyPair2D::_test_ccd(real_t p_step, GodotBody2D *p_A, int p_shape_A, return false; } - // Going too fast in that direction. + // A is moving fast enough that tunneling might occur. See if it's really about to collide. // Cast a segment from support in motion normal, in the same direction of motion by motion length. - // Support is the worst case collision point, so real collision happened before. + // Support point will the farthest forward collision point along the movement vector. + // i.e. the point that should hit B first if any collision does occur. + + // convert mnormal into body A's local xform because get_support requires (and returns) local coordinates. int a; Vector2 s[2]; - p_A->get_shape(p_shape_A)->get_supports(p_xform_A.basis_xform(mnormal).normalized(), s, a); + p_A->get_shape(p_shape_A)->get_supports(p_xform_A.basis_xform_inv(mnormal).normalized(), s, a); Vector2 from = p_xform_A.xform(s[0]); + // Back up 10% of the per-frame motion behind the support point and use that as the beginning of our cast. + // This should ensure the calculated new velocity will really cause a bit of overlap instead of just getting us very close. Vector2 to = from + motion; Transform2D from_inv = p_xform_B.affine_inverse(); - // Start from a little inside the bounding box. - Vector2 local_from = from_inv.xform(from - mnormal * mlen * 0.1); + // Back up 10% of the per-frame motion behind the support point and use that as the beginning of our cast. + // At high speeds, this may mean we're actually casting from well behind the body instead of inside it, which is odd. But it still works out. + Vector2 local_from = from_inv.xform(from - motion * 0.1); Vector2 local_to = from_inv.xform(to); Vector2 rpos, rnorm; if (!p_B->get_shape(p_shape_B)->intersect_segment(local_from, local_to, rpos, rnorm)) { + // there was no hit. Since the segment is the length of per-frame motion, this means the bodies will not + // actually collide yet on next frame. We'll probably check again next frame once they're closer. return false; } @@ -215,7 +228,7 @@ bool GodotBodyPair2D::_test_ccd(real_t p_step, GodotBody2D *p_A, int p_shape_A, // next frame will hit softly or soft enough. Vector2 hitpos = p_xform_B.xform(rpos); - real_t newlen = hitpos.distance_to(from) - (max - min) * 0.01; + real_t newlen = hitpos.distance_to(from) + (max - min) * 0.01; // adding 1% of body length to the distance between collision and support point should cause body A's support point to arrive just within B's collider next frame. p_A->set_linear_velocity(mnormal * (newlen / p_step)); return true; diff --git a/servers/physics_3d/godot_body_pair_3d.cpp b/servers/physics_3d/godot_body_pair_3d.cpp index 7e6cc6f834..00b7941292 100644 --- a/servers/physics_3d/godot_body_pair_3d.cpp +++ b/servers/physics_3d/godot_body_pair_3d.cpp @@ -161,6 +161,11 @@ void GodotBodyPair3D::validate_contacts() { } } +// _test_ccd prevents tunneling by slowing down a high velocity body that is about to collide so that next frame it will be at an appropriate location to collide (i.e. slight overlap) +// Warning: the way velocity is adjusted down to cause a collision means the momentum will be weaker than it should for a bounce! +// Process: only proceed if body A's motion is high relative to its size. +// cast forward along motion vector to see if A is going to enter/pass B's collider next frame, only proceed if it does. +// adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it. bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A, const Transform3D &p_xform_A, GodotBody3D *p_B, int p_shape_B, const Transform3D &p_xform_B) { Vector3 motion = p_A->get_linear_velocity() * p_step; real_t mlen = motion.length(); @@ -177,33 +182,39 @@ bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A, // Let's say it should move more than 1/3 the size of the object in that axis. bool fast_object = mlen > (max - min) * 0.3; if (!fast_object) { - return false; + return false; // moving slow enough that there's no chance of tunneling. } - // Going too fast in that direction. + // A is moving fast enough that tunneling might occur. See if it's really about to collide. // Cast a segment from support in motion normal, in the same direction of motion by motion length. - // Support is the worst case collision point, so real collision happened before. - Vector3 s = p_A->get_shape(p_shape_A)->get_support(p_xform_A.basis.xform(mnormal).normalized()); + // Support point will the farthest forward collision point along the movement vector. + // i.e. the point that should hit B first if any collision does occur. + + // convert mnormal into body A's local xform because get_support requires (and returns) local coordinates. + Vector3 s = p_A->get_shape(p_shape_A)->get_support(p_xform_A.basis.xform_inv(mnormal).normalized()); Vector3 from = p_xform_A.xform(s); Vector3 to = from + motion; Transform3D from_inv = p_xform_B.affine_inverse(); - // Start from a little inside the bounding box. - Vector3 local_from = from_inv.xform(from - mnormal * mlen * 0.1); + // Back up 10% of the per-frame motion behind the support point and use that as the beginning of our cast. + // At high speeds, this may mean we're actually casting from well behind the body instead of inside it, which is odd. But it still works out. + Vector3 local_from = from_inv.xform(from - motion * 0.1); Vector3 local_to = from_inv.xform(to); Vector3 rpos, rnorm; if (!p_B->get_shape(p_shape_B)->intersect_segment(local_from, local_to, rpos, rnorm, true)) { + // there was no hit. Since the segment is the length of per-frame motion, this means the bodies will not + // actually collide yet on next frame. We'll probably check again next frame once they're closer. return false; } - // Shorten the linear velocity so it does not hit, but gets close enough, - // next frame will hit softly or soft enough. + // Shorten the linear velocity so it will collide next frame. Vector3 hitpos = p_xform_B.xform(rpos); - real_t newlen = hitpos.distance_to(from) - (max - min) * 0.01; + real_t newlen = hitpos.distance_to(from) + (max - min) * 0.01; // adding 1% of body length to the distance between collision and support point should cause body A's support point to arrive just within B's collider next frame. + p_A->set_linear_velocity((mnormal * newlen) / p_step); return true; diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index 33f1f38bf5..6be0419d98 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -1185,7 +1185,7 @@ void RendererCanvasCull::canvas_item_add_texture_rect(RID p_item, const Rect2 &p rect->texture = p_texture; } -void RendererCanvasCull::canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate, int p_outline_size, float p_px_range) { +void RendererCanvasCull::canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate, int p_outline_size, float p_px_range, float p_scale) { Item *canvas_item = canvas_item_owner.get_or_null(p_item); ERR_FAIL_COND(!canvas_item); @@ -1215,7 +1215,7 @@ void RendererCanvasCull::canvas_item_add_msdf_texture_rect_region(RID p_item, co rect->flags ^= RendererCanvasRender::CANVAS_RECT_FLIP_V; rect->source.size.y = -rect->source.size.y; } - rect->outline = p_outline_size; + rect->outline = (float)p_outline_size / p_scale / 4.0; rect->px_range = p_px_range; } diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index 198e1020f0..808c0acc35 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -228,7 +228,7 @@ public: void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color); void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false); void canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false); - void canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, float p_px_range = 1.0); + void canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, float p_px_range = 1.0, float p_scale = 1.0); void canvas_item_add_lcd_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1)); void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, RS::NinePatchAxisMode p_x_axis_mode = RS::NINE_PATCH_STRETCH, RS::NinePatchAxisMode p_y_axis_mode = RS::NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1)); void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0); diff --git a/servers/rendering/renderer_rd/SCsub b/servers/rendering/renderer_rd/SCsub index 10b83dca11..a27439e931 100644 --- a/servers/rendering/renderer_rd/SCsub +++ b/servers/rendering/renderer_rd/SCsub @@ -9,4 +9,5 @@ SConscript("environment/SCsub") SConscript("forward_clustered/SCsub") SConscript("forward_mobile/SCsub") SConscript("shaders/SCsub") +SConscript("spirv-reflect/SCsub") SConscript("storage_rd/SCsub") diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp index eece195946..38066b6613 100644 --- a/servers/rendering/renderer_rd/environment/fog.cpp +++ b/servers/rendering/renderer_rd/environment/fog.cpp @@ -319,10 +319,6 @@ void Fog::free_fog_shader() { material_storage->material_free(volumetric_fog.default_material); } -void Fog::FogShaderData::set_path_hint(const String &p_path) { - path = p_path; -} - void Fog::FogShaderData::set_code(const String &p_code) { //compile @@ -366,83 +362,6 @@ void Fog::FogShaderData::set_code(const String &p_code) { valid = true; } -void Fog::FogShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void Fog::FogShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - RBMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void Fog::FogShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool Fog::FogShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool Fog::FogShaderData::is_animated() const { return false; } @@ -451,15 +370,6 @@ bool Fog::FogShaderData::casts_shadows() const { return false; } -Variant Fog::FogShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode Fog::FogShaderData::get_native_source_code() const { Fog *fog_singleton = Fog::get_singleton(); @@ -789,6 +699,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::get_singleton()->compute_list_bind_uniform_set(compute_list, volumetric_fog.base_uniform_set, VolumetricFogShader::FogSet::FOG_SET_BASE); if (material->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material->uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->compute_list_bind_uniform_set(compute_list, material->uniform_set, VolumetricFogShader::FogSet::FOG_SET_MATERIAL); + material->set_as_used(); } RD::get_singleton()->compute_list_dispatch_threads(compute_list, kernel_size.x, kernel_size.y, kernel_size.z); diff --git a/servers/rendering/renderer_rd/environment/fog.h b/servers/rendering/renderer_rd/environment/fog.h index 0ade995758..a5f06a129d 100644 --- a/servers/rendering/renderer_rd/environment/fog.h +++ b/servers/rendering/renderer_rd/environment/fog.h @@ -188,27 +188,18 @@ private: RID version; RID pipeline; - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size = 0; - String path; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; bool uses_time = false; - virtual void set_path_hint(const String &p_hint); virtual void set_code(const String &p_Code); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; FogShaderData() {} diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 550fe27e4c..61b7dd4504 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -3496,6 +3496,10 @@ void GI::init(SkyRD *p_sky) { { //calculate tables String defines = "\n#define SDFGI_OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + if (RendererSceneRenderRD::get_singleton()->is_vrs_supported()) { + defines += "\n#define USE_VRS\n"; + } + Vector<String> gi_modes; gi_modes.push_back("\n#define USE_VOXEL_GI_INSTANCES\n"); // MODE_VOXEL_GI @@ -4011,7 +4015,7 @@ void GI::process_gi(Ref<RenderSceneBuffersRD> p_render_buffers, const RID *p_nor u.append_id(rbgi->scene_data_ubo); uniforms.push_back(u); } - { + if (RendererSceneRenderRD::get_singleton()->is_vrs_supported()) { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 19; diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 36d26ad783..40f68eb118 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -45,10 +45,6 @@ using namespace RendererRD; //////////////////////////////////////////////////////////////////////////////// // SKY SHADER -void SkyRD::SkyShaderData::set_path_hint(const String &p_path) { - path = p_path; -} - void SkyRD::SkyShaderData::set_code(const String &p_code) { //compile @@ -152,82 +148,6 @@ void SkyRD::SkyShaderData::set_code(const String &p_code) { valid = true; } -void SkyRD::SkyShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void SkyRD::SkyShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - HashMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void SkyRD::SkyShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool SkyRD::SkyShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool SkyRD::SkyShaderData::is_animated() const { return false; } @@ -236,15 +156,6 @@ bool SkyRD::SkyShaderData::casts_shadows() const { return false; } -Variant SkyRD::SkyShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode SkyRD::SkyShaderData::get_native_source_code() const { RendererSceneRenderRD *scene_singleton = static_cast<RendererSceneRenderRD *>(RendererSceneRenderRD::singleton); @@ -1142,6 +1053,8 @@ void SkyRD::setup(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, const P ERR_FAIL_COND(!shader_data); + material->set_as_used(); + // Invalidate supbass buffers if screen size changes if (sky->screen_size != p_screen_size) { sky->screen_size = p_screen_size; @@ -1542,6 +1455,8 @@ void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth ERR_FAIL_COND(!shader_data); + material->set_as_used(); + Basis sky_transform = RendererSceneRenderRD::get_singleton()->environment_get_sky_orientation(p_env); sky_transform.invert(); @@ -1639,6 +1554,8 @@ void SkyRD::update_res_buffers(RID p_env, uint32_t p_view_count, const Projectio ERR_FAIL_COND(!shader_data); + material->set_as_used(); + Basis sky_transform = RendererSceneRenderRD::get_singleton()->environment_get_sky_orientation(p_env); sky_transform.invert(); @@ -1732,6 +1649,8 @@ void SkyRD::draw(RD::DrawListID p_draw_list, RID p_env, RID p_fb, uint32_t p_vie ERR_FAIL_COND(!shader_data); + material->set_as_used(); + Basis sky_transform = RendererSceneRenderRD::get_singleton()->environment_get_sky_orientation(p_env); sky_transform.invert(); diff --git a/servers/rendering/renderer_rd/environment/sky.h b/servers/rendering/renderer_rd/environment/sky.h index 45c4f9bda7..607339b24e 100644 --- a/servers/rendering/renderer_rd/environment/sky.h +++ b/servers/rendering/renderer_rd/environment/sky.h @@ -112,15 +112,12 @@ private: RID version; PipelineCacheRD pipelines[SKY_VERSION_MAX]; - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size = 0; - String path; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; bool uses_time = false; bool uses_position = false; @@ -129,14 +126,8 @@ private: bool uses_light = false; virtual void set_code(const String &p_Code); - virtual void set_path_hint(const String &p_hint); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; SkyShaderData() {} diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 65ceaca8b2..608f7ff958 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -348,6 +348,7 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p #endif material_uniform_set = surf->material_uniform_set; shader = surf->shader; + surf->material->set_as_used(); #ifdef DEBUG_ENABLED } #endif @@ -1632,7 +1633,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool using_ssr = false; bool using_sdfgi = false; bool using_voxelgi = false; - bool reverse_cull = false; + bool reverse_cull = p_render_data->scene_data->cam_transform.basis.determinant() < 0; bool using_ssil = p_render_data->environment.is_valid() && environment_get_ssil_enabled(p_render_data->environment); if (rb.is_valid()) { @@ -3388,6 +3389,7 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet sdcache->flags = flags; sdcache->shader = p_material->shader_data; + sdcache->material = p_material; sdcache->material_uniform_set = p_material->uniform_set; sdcache->surface = mesh_storage->mesh_get_surface(p_mesh, p_surface); sdcache->primitive = mesh_storage->mesh_surface_get_primitive(sdcache->surface); @@ -3606,6 +3608,10 @@ void RenderForwardClustered::_geometry_instance_update(RenderGeometryInstance *p } ginstance->transforms_uniform_set = particles_storage->particles_get_instance_buffer_uniform_set(ginstance->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); + if (particles_storage->particles_get_frame_counter(ginstance->data->base) == 0) { + // Particles haven't been cleared or updated, update once now to ensure they are ready to render. + particles_storage->update_particles(); + } } else if (ginstance->data->base_type == RS::INSTANCE_MESH) { if (mesh_storage->skeleton_is_valid(ginstance->data->skeleton)) { ginstance->transforms_uniform_set = mesh_storage->skeleton_get_3d_uniform_set(ginstance->data->skeleton, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index d9145b5818..a5e8bb76d9 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -441,6 +441,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { void *surface = nullptr; RID material_uniform_set; SceneShaderForwardClustered::ShaderData *shader = nullptr; + SceneShaderForwardClustered::MaterialData *material = nullptr; void *surface_shadow = nullptr; RID material_uniform_set_shadow; diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index b5d4098e65..6abf41a76f 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -37,10 +37,6 @@ using namespace RendererSceneRenderImplementation; -void SceneShaderForwardClustered::ShaderData::set_path_hint(const String &p_path) { - path = p_path; -} - void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { //compile @@ -380,87 +376,6 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { valid = true; } -void SceneShaderForwardClustered::ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void SceneShaderForwardClustered::ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - HashMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { - // Don't expose any of these. - continue; - } - - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void SceneShaderForwardClustered::ShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool SceneShaderForwardClustered::ShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool SceneShaderForwardClustered::ShaderData::is_animated() const { return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex); } @@ -473,15 +388,6 @@ bool SceneShaderForwardClustered::ShaderData::casts_shadows() const { return !has_alpha || (uses_depth_pre_pass && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED)); } -Variant SceneShaderForwardClustered::ShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode SceneShaderForwardClustered::ShaderData::get_native_source_code() const { SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton; diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index 194edf2dcb..51e4dd44d5 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -139,16 +139,12 @@ public: PipelineCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][PIPELINE_VERSION_MAX]; PipelineCacheRD color_pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][PIPELINE_COLOR_PASS_FLAG_COUNT]; - String path; - - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size = 0; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; DepthDraw depth_draw = DEPTH_DRAW_OPAQUE; DepthTest depth_test = DEPTH_TEST_ENABLED; @@ -183,15 +179,9 @@ public: uint32_t index = 0; virtual void set_code(const String &p_Code); - virtual void set_path_hint(const String &p_path); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; SelfList<ShaderData> shader_list_element; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 482f0a2988..e47ca8dd73 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -70,17 +70,17 @@ void RenderForwardMobile::ForwardIDStorageMobile::map_forward_id(RendererRD::For void RenderForwardMobile::ForwardIDStorageMobile::fill_push_constant_instance_indices(GeometryInstanceForwardMobile::PushConstant *p_push_constant, uint32_t &spec_constants, const GeometryInstanceForwardMobile *p_instance) { // first zero out our indices - p_push_constant->omni_lights[0] = 0xFFFF; - p_push_constant->omni_lights[1] = 0xFFFF; + p_push_constant->omni_lights[0] = 0xFFFFFFFF; + p_push_constant->omni_lights[1] = 0xFFFFFFFF; - p_push_constant->spot_lights[0] = 0xFFFF; - p_push_constant->spot_lights[1] = 0xFFFF; + p_push_constant->spot_lights[0] = 0xFFFFFFFF; + p_push_constant->spot_lights[1] = 0xFFFFFFFF; - p_push_constant->decals[0] = 0xFFFF; - p_push_constant->decals[1] = 0xFFFF; + p_push_constant->decals[0] = 0xFFFFFFFF; + p_push_constant->decals[1] = 0xFFFFFFFF; - p_push_constant->reflection_probes[0] = 0xFFFF; - p_push_constant->reflection_probes[1] = 0xFFFF; + p_push_constant->reflection_probes[0] = 0xFFFFFFFF; + p_push_constant->reflection_probes[1] = 0xFFFFFFFF; if (p_instance->omni_light_count == 0) { spec_constants |= 1 << SPEC_CONSTANT_DISABLE_OMNI_LIGHTS; @@ -154,8 +154,8 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RenderSceneBu render_buffers->create_texture(RB_SCOPE_MOBILE, RB_TEX_COLOR_MSAA, format, usage_bits, texture_samples); - format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, usage_bits) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; render_buffers->create_texture(RB_SCOPE_MOBILE, RB_TEX_DEPTH_MSAA, format, usage_bits, texture_samples); } @@ -673,7 +673,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color Size2i screen_size; RID framebuffer; - bool reverse_cull = false; + bool reverse_cull = p_render_data->scene_data->cam_transform.basis.determinant() < 0; bool using_subpass_transparent = true; bool using_subpass_post_process = true; @@ -2102,6 +2102,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr #endif material_uniform_set = surf->material_uniform_set; shader = surf->shader; + surf->material->set_as_used(); #ifdef DEBUG_ENABLED } #endif @@ -2417,6 +2418,7 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI sdcache->flags = flags; sdcache->shader = p_material->shader_data; + sdcache->material = p_material; sdcache->material_uniform_set = p_material->uniform_set; sdcache->surface = mesh_storage->mesh_get_surface(p_mesh, p_surface); sdcache->primitive = mesh_storage->mesh_surface_get_primitive(sdcache->surface); @@ -2634,6 +2636,10 @@ void RenderForwardMobile::_geometry_instance_update(RenderGeometryInstance *p_ge } ginstance->transforms_uniform_set = particles_storage->particles_get_instance_buffer_uniform_set(ginstance->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); + if (particles_storage->particles_get_frame_counter(ginstance->data->base) == 0) { + // Particles haven't been cleared or updated, update once now to ensure they are ready to render. + particles_storage->update_particles(); + } } else if (ginstance->data->base_type == RS::INSTANCE_MESH) { if (mesh_storage->skeleton_is_valid(ginstance->data->skeleton)) { ginstance->transforms_uniform_set = mesh_storage->skeleton_get_3d_uniform_set(ginstance->data->skeleton, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 6c85e1cffc..0f5a229d61 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -440,6 +440,7 @@ protected: void *surface = nullptr; RID material_uniform_set; SceneShaderForwardMobile::ShaderData *shader = nullptr; + SceneShaderForwardMobile::MaterialData *material = nullptr; void *surface_shadow = nullptr; RID material_uniform_set_shadow; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index 2d80cffdad..224bb34189 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -39,10 +39,6 @@ using namespace RendererSceneRenderImplementation; /* ShaderData */ -void SceneShaderForwardMobile::ShaderData::set_path_hint(const String &p_path) { - path = p_path; -} - void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { //compile @@ -336,86 +332,6 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { valid = true; } -void SceneShaderForwardMobile::ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void SceneShaderForwardMobile::ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - HashMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { - continue; - } - - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void SceneShaderForwardMobile::ShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool SceneShaderForwardMobile::ShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool SceneShaderForwardMobile::ShaderData::is_animated() const { return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex); } @@ -428,15 +344,6 @@ bool SceneShaderForwardMobile::ShaderData::casts_shadows() const { return !has_alpha || (uses_depth_pre_pass && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED)); } -Variant SceneShaderForwardMobile::ShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode SceneShaderForwardMobile::ShaderData::get_native_source_code() const { SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h index 5b51cfc8c3..18a897a9e9 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h @@ -100,16 +100,12 @@ public: uint32_t vertex_input_mask = 0; PipelineCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][SHADER_VERSION_MAX]; - String path; - - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size = 0; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; DepthDraw depth_draw; DepthTest depth_test; @@ -141,16 +137,8 @@ public: uint32_t index = 0; virtual void set_code(const String &p_Code); - virtual void set_path_hint(const String &p_path); - - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; SelfList<ShaderData> shader_list_element; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index d41daa18b4..96690ceac1 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -790,7 +790,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend ERR_BREAK(particles_storage->particles_get_mode(pt->particles) != RS::PARTICLES_MODE_2D); particles_storage->particles_request_process(pt->particles); - if (particles_storage->particles_is_inactive(pt->particles)) { + if (particles_storage->particles_is_inactive(pt->particles) || particles_storage->particles_get_frame_counter(pt->particles) == 0) { break; } @@ -1136,6 +1136,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co // Update uniform set. if (material_data->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_data->uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_data->uniform_set, MATERIAL_UNIFORM_SET); + material_data->set_as_used(); } } else { pipeline_variants = &shader.pipeline_variants; @@ -2001,10 +2002,6 @@ void RendererCanvasRenderRD::occluder_polygon_set_cull_mode(RID p_occluder, RS:: oc->cull_mode = p_mode; } -void RendererCanvasRenderRD::CanvasShaderData::set_path_hint(const String &p_path) { - path = p_path; -} - void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) { //compile @@ -2216,86 +2213,6 @@ void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) { valid = true; } -void RendererCanvasRenderRD::CanvasShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void RendererCanvasRenderRD::CanvasShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - HashMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || - E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { - // Don't expose any of these. - continue; - } - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void RendererCanvasRenderRD::CanvasShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool RendererCanvasRenderRD::CanvasShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool RendererCanvasRenderRD::CanvasShaderData::is_animated() const { return false; } @@ -2304,15 +2221,6 @@ bool RendererCanvasRenderRD::CanvasShaderData::casts_shadows() const { return false; } -Variant RendererCanvasRenderRD::CanvasShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode RendererCanvasRenderRD::CanvasShaderData::get_native_source_code() const { RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton); return canvas_singleton->shader.canvas_shader.version_get_native_source_code(version); diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 6e876b1297..f809c7f046 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -160,16 +160,13 @@ class RendererCanvasRenderRD : public RendererCanvasRender { bool valid = false; RID version; PipelineVariants pipeline_variants; - String path; - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size = 0; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; bool uses_screen_texture = false; bool uses_screen_texture_mipmaps = false; @@ -177,15 +174,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender { bool uses_time = false; virtual void set_code(const String &p_Code); - virtual void set_path_hint(const String &p_path); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; CanvasShaderData() {} diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index 3289bfb0ea..f488fd63fe 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -174,13 +174,19 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color texture_storage->texture_2d_initialize(texture, p_image); RID rd_texture = texture_storage->texture_get_rd_texture(texture); + RD::SamplerState sampler_state; + sampler_state.min_filter = p_use_filter ? RD::SAMPLER_FILTER_LINEAR : RD::SAMPLER_FILTER_NEAREST; + sampler_state.mag_filter = p_use_filter ? RD::SAMPLER_FILTER_LINEAR : RD::SAMPLER_FILTER_NEAREST; + sampler_state.max_lod = 0; + RID sampler = RD::get_singleton()->sampler_create(sampler_state); + RID uset; { Vector<RD::Uniform> uniforms; RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; u.binding = 0; - u.append_id(blit.sampler); + u.append_id(sampler); u.append_id(rd_texture); uniforms.push_back(u); uset = RD::get_singleton()->uniform_set_create(uniforms, blit.shader.version_get_shader(blit.shader_version, BLIT_MODE_NORMAL), 0); @@ -241,6 +247,7 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color RD::get_singleton()->swap_buffers(); texture_storage->texture_free(texture); + RD::get_singleton()->free(sampler); } RendererCompositorRD *RendererCompositorRD::singleton = nullptr; diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl index a8b2d77718..631d1968b0 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl @@ -66,6 +66,19 @@ void main() { vec4 normal_roughness = imageLoad(source_normal_roughness, ssC); vec3 normal = normal_roughness.xyz * 2.0 - 1.0; + float roughness = normal_roughness.w; + + // The roughness cutoff of 0.6 is chosen to match the roughness fadeout from GH-69828. + if (roughness > 0.6) { + // Do not compute SSR for rough materials to improve performance at the cost of + // subtle artifacting. +#ifdef MODE_ROUGH + imageStore(blur_radius_image, ssC, vec4(0.0)); +#endif + imageStore(ssr_image, ssC, vec4(0.0)); + return; + } + normal = normalize(normal); normal.y = -normal.y; //because this code reads flipped @@ -81,8 +94,6 @@ void main() { imageStore(ssr_image, ssC, vec4(0.0)); return; } - //ray_dir = normalize(view_dir - normal * dot(normal,view_dir) * 2.0); - //ray_dir = normalize(vec3(1.0, 1.0, -1.0)); //////////////// @@ -223,7 +234,6 @@ void main() { // if roughness is enabled, do screen space cone tracing float blur_radius = 0.0; - float roughness = normal_roughness.w; if (roughness > 0.001) { float cone_angle = min(roughness, 0.999) * M_PI * 0.5; diff --git a/servers/rendering/renderer_rd/shaders/environment/gi.glsl b/servers/rendering/renderer_rd/shaders/environment/gi.glsl index ab927df678..459c4dcb1d 100644 --- a/servers/rendering/renderer_rd/shaders/environment/gi.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/gi.glsl @@ -108,7 +108,9 @@ layout(set = 0, binding = 18, std140) uniform SceneData { } scene_data; +#ifdef USE_VRS layout(r8ui, set = 0, binding = 19) uniform restrict readonly uimage2D vrs_buffer; +#endif layout(push_constant, std430) uniform Params { uint max_voxel_gi_instances; @@ -661,6 +663,7 @@ void main() { ivec2 pos = ivec2(gl_GlobalInvocationID.xy); uint vrs_x, vrs_y; +#ifdef USE_VRS if (sc_use_vrs) { ivec2 vrs_pos; @@ -684,6 +687,7 @@ void main() { return; } } +#endif if (sc_half_res) { pos <<= 1; @@ -708,6 +712,7 @@ void main() { imageStore(ambient_buffer, pos, ambient_light); imageStore(reflection_buffer, pos, reflection_light); +#ifdef USE_VRS if (sc_use_vrs) { if (vrs_x > 1) { imageStore(ambient_buffer, pos + ivec2(1, 0), ambient_light); @@ -766,4 +771,5 @@ void main() { imageStore(reflection_buffer, pos + ivec2(3, 3), reflection_light); } } +#endif } diff --git a/servers/rendering/renderer_rd/shaders/particles.glsl b/servers/rendering/renderer_rd/shaders/particles.glsl index 9f6aa7adc0..a609076e2c 100644 --- a/servers/rendering/renderer_rd/shaders/particles.glsl +++ b/servers/rendering/renderer_rd/shaders/particles.glsl @@ -243,8 +243,14 @@ void main() { if (params.trail_size > 1) { if (params.trail_pass) { + if (particle >= params.total_particles * (params.trail_size - 1)) { + return; + } particle += (particle / (params.trail_size - 1)) + 1; } else { + if (particle >= params.total_particles) { + return; + } particle *= params.trail_size; } } @@ -298,12 +304,17 @@ void main() { PARTICLE.flags = PARTICLE_FLAG_TRAILED | ((frame_history.data[0].frame & PARTICLE_FRAME_MASK) << PARTICLE_FRAME_SHIFT); //mark it as trailed, save in which frame it will start PARTICLE.xform = particles.data[src_idx].xform; } - + if (!bool(particles.data[src_idx].flags & PARTICLE_FLAG_ACTIVE)) { + // Disable the entire trail if the parent is no longer active. + PARTICLE.flags = 0; + return; + } if (bool(PARTICLE.flags & PARTICLE_FLAG_TRAILED) && ((PARTICLE.flags >> PARTICLE_FRAME_SHIFT) == (FRAME.frame & PARTICLE_FRAME_MASK))) { //check this is trailed and see if it should start now // we just assume that this is the first frame of the particle, the rest is deterministic PARTICLE.flags = PARTICLE_FLAG_ACTIVE | (particles.data[src_idx].flags & (PARTICLE_FRAME_MASK << PARTICLE_FRAME_SHIFT)); return; //- this appears like it should be correct, but it seems not to be.. wonder why. } + } else { PARTICLE.flags &= ~PARTICLE_FLAG_STARTED; } diff --git a/drivers/spirv-reflect/SCsub b/servers/rendering/renderer_rd/spirv-reflect/SCsub index 1e7b3de0e6..4c27e5bef7 100644 --- a/drivers/spirv-reflect/SCsub +++ b/servers/rendering/renderer_rd/spirv-reflect/SCsub @@ -14,4 +14,4 @@ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env_thirdparty = env.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources) +env_thirdparty.add_source_files(env.servers_sources, thirdparty_sources) diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 1dd95969e6..ff882c1992 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -1726,13 +1726,13 @@ void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_use //erase lightmap users if (lm->light_texture.is_valid()) { - TextureStorage::Texture *t = texture_storage->get_singleton()->get_texture(lm->light_texture); + TextureStorage::Texture *t = texture_storage->get_texture(lm->light_texture); if (t) { t->lightmap_users.erase(p_lightmap); } } - TextureStorage::Texture *t = texture_storage->get_singleton()->get_texture(p_light); + TextureStorage::Texture *t = texture_storage->get_texture(p_light); lm->light_texture = p_light; lm->uses_spherical_harmonics = p_uses_spherical_haromics; diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index 6703d8e7d0..709e4b449f 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -928,6 +928,104 @@ _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, } /////////////////////////////////////////////////////////////////////////// +// MaterialStorage::ShaderData + +void MaterialStorage::ShaderData::set_path_hint(const String &p_hint) { + path = p_hint; +} + +void MaterialStorage::ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { + if (!p_texture.is_valid()) { + if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { + default_texture_params[p_name].erase(p_index); + + if (default_texture_params[p_name].is_empty()) { + default_texture_params.erase(p_name); + } + } + } else { + if (!default_texture_params.has(p_name)) { + default_texture_params[p_name] = HashMap<int, RID>(); + } + default_texture_params[p_name][p_index] = p_texture; + } +} + +Variant MaterialStorage::ShaderData::get_default_parameter(const StringName &p_parameter) const { + if (uniforms.has(p_parameter)) { + ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); + } + return Variant(); +} + +void MaterialStorage::ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { + SortArray<Pair<StringName, int>, ShaderLanguage::UniformOrderComparator> sorter; + LocalVector<Pair<StringName, int>> filtered_uniforms; + + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { + continue; + } + if (E.value.texture_order >= 0) { + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000)); + } else { + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order)); + } + } + int uniform_count = filtered_uniforms.size(); + sorter.sort(filtered_uniforms.ptr(), uniform_count); + + String last_group; + for (int i = 0; i < uniform_count; i++) { + const StringName &uniform_name = filtered_uniforms[i].first; + const ShaderLanguage::ShaderNode::Uniform &uniform = uniforms[uniform_name]; + + String group = uniform.group; + if (!uniform.subgroup.is_empty()) { + group += "::" + uniform.subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniform); + pi.name = uniform_name; + p_param_list->push_back(pi); + } +} + +void MaterialStorage::ShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + RendererMaterialStorage::InstanceShaderParam p; + p.info = ShaderLanguage::uniform_to_property_info(E.value); + p.info.name = E.key; //supply name + p.index = E.value.instance_index; + p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); + p_param_list->push_back(p); + } +} + +bool MaterialStorage::ShaderData::is_parameter_texture(const StringName &p_param) const { + if (!uniforms.has(p_param)) { + return false; + } + + return uniforms[p_param].texture_order >= 0; +} + +/////////////////////////////////////////////////////////////////////////// // MaterialStorage::MaterialData void MaterialStorage::MaterialData::update_uniform_buffer(const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const HashMap<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color) { @@ -1221,6 +1319,10 @@ void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Va roughness_detect_texture = tex; roughness_channel = RS::TextureDetectRoughnessChannel(p_texture_uniforms[i].hint - ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_R); } + if (tex->render_target) { + tex->render_target->was_used = true; + render_target_cache.push_back(tex->render_target); + } #endif } if (rd_texture.is_null()) { @@ -1307,6 +1409,7 @@ bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap< if ((uint32_t)texture_cache.size() != tex_uniform_count || p_textures_dirty) { texture_cache.resize(tex_uniform_count); + render_target_cache.clear(); p_textures_dirty = true; //clear previous uniform set @@ -1367,6 +1470,12 @@ bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap< return true; } +void MaterialStorage::MaterialData::set_as_used() { + for (int i = 0; i < render_target_cache.size(); i++) { + render_target_cache[i]->was_used = true; + } +} + /////////////////////////////////////////////////////////////////////////// // MaterialStorage diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h index a96fc847e1..989a0f7b45 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h @@ -31,6 +31,8 @@ #ifndef MATERIAL_STORAGE_RD_H #define MATERIAL_STORAGE_RD_H +#include "texture_storage.h" + #include "core/math/projection.h" #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" @@ -54,24 +56,30 @@ public: }; struct ShaderData { - virtual void set_code(const String &p_Code) = 0; - virtual void set_path_hint(const String &p_hint) = 0; - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) = 0; - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const = 0; + String path; + HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + HashMap<StringName, HashMap<int, RID>> default_texture_params; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const = 0; - virtual bool is_parameter_texture(const StringName &p_param) const = 0; + virtual void set_path_hint(const String &p_hint); + virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); + virtual Variant get_default_parameter(const StringName &p_parameter) const; + virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; + virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; + virtual bool is_parameter_texture(const StringName &p_param) const; + + virtual void set_code(const String &p_Code) = 0; virtual bool is_animated() const = 0; virtual bool casts_shadows() const = 0; - virtual Variant get_default_parameter(const StringName &p_parameter) const = 0; virtual RS::ShaderNativeSourceCode get_native_source_code() const { return RS::ShaderNativeSourceCode(); } virtual ~ShaderData() {} }; struct MaterialData { + Vector<RendererRD::TextureStorage::RenderTarget *> render_target_cache; void update_uniform_buffer(const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const HashMap<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color); void update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color); + void set_as_used(); virtual void set_render_priority(int p_priority) = 0; virtual void set_next_pass(RID p_pass) = 0; diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 503a25184e..35fa3931f8 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -434,27 +434,8 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) } if (mesh->surface_count == 0) { - mesh->bone_aabbs = p_surface.bone_aabbs; mesh->aabb = p_surface.aabb; } else { - if (mesh->bone_aabbs.size() < p_surface.bone_aabbs.size()) { - // ArrayMesh::_surface_set_data only allocates bone_aabbs up to max_bone - // Each surface may affect different numbers of bones. - mesh->bone_aabbs.resize(p_surface.bone_aabbs.size()); - } - for (int i = 0; i < p_surface.bone_aabbs.size(); i++) { - const AABB &bone = p_surface.bone_aabbs[i]; - if (bone.has_volume()) { - AABB &mesh_bone = mesh->bone_aabbs.write[i]; - if (mesh_bone != AABB()) { - // Already initialized, merge AABBs. - mesh_bone.merge_with(bone); - } else { - // Not yet initialized, copy the bone AABB. - mesh_bone = bone; - } - } - } mesh->aabb.merge_with(p_surface.aabb); } @@ -715,6 +696,8 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { } } + mesh->aabb = aabb; + mesh->skeleton_aabb_version = skeleton->version; return aabb; } diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index 27c82213e4..a8feddc3b9 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -138,8 +138,6 @@ private: Surface **surfaces = nullptr; uint32_t surface_count = 0; - Vector<AABB> bone_aabbs; - bool has_bone_weights = false; AABB aabb; diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 51aa81745b..708455706b 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -810,6 +810,15 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta //2D collision Transform2D xform = p_particles->sdf_collision_transform; //will use dotproduct manually so invert beforehand + + if (!p_particles->use_local_coords) { + Transform2D emission; + emission.columns[0] = Vector2(p_particles->emission_transform.basis.get_column(0).x, p_particles->emission_transform.basis.get_column(0).y); + emission.columns[1] = Vector2(p_particles->emission_transform.basis.get_column(1).x, p_particles->emission_transform.basis.get_column(1).y); + emission.set_origin(Vector2(p_particles->emission_transform.origin.x, p_particles->emission_transform.origin.y)); + xform = xform * emission.affine_inverse(); + } + Transform2D revert = xform.affine_inverse(); frame_params.collider_count = 1; frame_params.colliders[0].transform[0] = xform.columns[0][0]; @@ -1107,6 +1116,7 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta if (m->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(m->uniform_set)) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, m->uniform_set, 3); + m->set_as_used(); } RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ParticlesShader::PushConstant)); @@ -1405,6 +1415,7 @@ void ParticlesStorage::update_particles() { } bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0; + bool updated = false; if (particles->clear && particles->pre_process_time > 0.0) { double frame_time; @@ -1419,6 +1430,7 @@ void ParticlesStorage::update_particles() { while (todo >= 0) { _particles_process(particles, frame_time); todo -= frame_time; + updated = true; } } @@ -1440,9 +1452,10 @@ void ParticlesStorage::update_particles() { } double todo = particles->frame_remainder + delta; - while (todo >= frame_time) { + while (todo >= frame_time || (particles->clear && !updated)) { _particles_process(particles, frame_time); todo -= decr; + updated = true; } particles->frame_remainder = todo; @@ -1450,14 +1463,16 @@ void ParticlesStorage::update_particles() { } else { if (zero_time_scale) { _particles_process(particles, 0.0); + updated = true; } else { _particles_process(particles, RendererCompositorRD::singleton->get_frame_delta_time()); + updated = true; } } //copy particles to instance buffer - if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { + if (updated && particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { //does not need view dependent operation, do copy here ParticlesShader::CopyPushConstant copy_push_constant; @@ -1534,9 +1549,6 @@ bool ParticlesStorage::particles_is_inactive(RID p_particles) const { /* Particles SHADER */ -void ParticlesStorage::ParticlesShaderData::set_path_hint(const String &p_path) { - path = p_path; -} void ParticlesStorage::ParticlesShaderData::set_code(const String &p_code) { ParticlesStorage *particles_storage = ParticlesStorage::get_singleton(); //compile @@ -1602,83 +1614,6 @@ void ParticlesStorage::ParticlesShaderData::set_code(const String &p_code) { valid = true; } -void ParticlesStorage::ParticlesShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { - if (!p_texture.is_valid()) { - if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { - default_texture_params[p_name].erase(p_index); - - if (default_texture_params[p_name].is_empty()) { - default_texture_params.erase(p_name); - } - } - } else { - if (!default_texture_params.has(p_name)) { - default_texture_params[p_name] = HashMap<int, RID>(); - } - default_texture_params[p_name][p_index] = p_texture; - } -} - -void ParticlesStorage::ParticlesShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { - HashMap<int, StringName> order; - - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - if (E.value.texture_order >= 0) { - order[E.value.texture_order + 100000] = E.key; - } else { - order[E.value.order] = E.key; - } - } - - String last_group; - for (const KeyValue<int, StringName> &E : order) { - String group = uniforms[E.value].group; - if (!uniforms[E.value].subgroup.is_empty()) { - group += "::" + uniforms[E.value].subgroup; - } - - if (group != last_group) { - PropertyInfo pi; - pi.usage = PROPERTY_USAGE_GROUP; - pi.name = group; - p_param_list->push_back(pi); - - last_group = group; - } - - PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); - pi.name = E.value; - p_param_list->push_back(pi); - } -} - -void ParticlesStorage::ParticlesShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { - for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { - continue; - } - - RendererMaterialStorage::InstanceShaderParam p; - p.info = ShaderLanguage::uniform_to_property_info(E.value); - p.info.name = E.key; //supply name - p.index = E.value.instance_index; - p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); - p_param_list->push_back(p); - } -} - -bool ParticlesStorage::ParticlesShaderData::is_parameter_texture(const StringName &p_param) const { - if (!uniforms.has(p_param)) { - return false; - } - - return uniforms[p_param].texture_order >= 0; -} - bool ParticlesStorage::ParticlesShaderData::is_animated() const { return false; } @@ -1687,15 +1622,6 @@ bool ParticlesStorage::ParticlesShaderData::casts_shadows() const { return false; } -Variant ParticlesStorage::ParticlesShaderData::get_default_parameter(const StringName &p_parameter) const { - if (uniforms.has(p_parameter)) { - ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); - } - return Variant(); -} - RS::ShaderNativeSourceCode ParticlesStorage::ParticlesShaderData::get_native_source_code() const { return ParticlesStorage::get_singleton()->particles_shader.shader.version_get_native_source_code(version); } diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h index 49a8444e2f..0701dc95dd 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h @@ -313,15 +313,12 @@ private: RID version; bool uses_collision = false; - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; Vector<uint32_t> ubo_offsets; uint32_t ubo_size = 0; - String path; String code; - HashMap<StringName, HashMap<int, RID>> default_texture_params; RID pipeline; @@ -331,14 +328,8 @@ private: uint32_t userdata_count = 0; virtual void set_code(const String &p_Code); - virtual void set_path_hint(const String &p_hint); - virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); - virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; virtual RS::ShaderNativeSourceCode get_native_source_code() const; ParticlesShaderData() {} @@ -464,6 +455,12 @@ public: return particles->mode; } + _FORCE_INLINE_ uint32_t particles_get_frame_counter(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_COND_V(!particles, false); + return particles->frame_counter; + } + _FORCE_INLINE_ uint32_t particles_get_amount(RID p_particles, uint32_t &r_trail_divisor) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND_V(!particles, 0); @@ -494,9 +491,8 @@ public: _FORCE_INLINE_ RID particles_get_instance_buffer_uniform_set(RID p_particles, RID p_shader, uint32_t p_set) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND_V(!particles, RID()); - if (particles->particles_transforms_buffer_uniform_set.is_null()) { + if (particles->particles_transforms_buffer_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(particles->particles_transforms_buffer_uniform_set)) { _particles_update_buffers(particles); - Vector<RD::Uniform> uniforms; { diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 543eb0cb72..6a4359faf3 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -419,11 +419,12 @@ TextureStorage::TextureStorage() { tformat.format = RD::DATA_FORMAT_R8_UINT; tformat.width = 4; tformat.height = 4; - tformat.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; - if (RD::get_singleton()->has_feature(RD::SUPPORTS_ATTACHMENT_VRS)) { - tformat.usage_bits |= RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT; - } + tformat.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT; tformat.texture_type = RD::TEXTURE_TYPE_2D; + if (!RD::get_singleton()->has_feature(RD::SUPPORTS_ATTACHMENT_VRS)) { + tformat.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; + tformat.format = RD::DATA_FORMAT_R8_UNORM; + } Vector<uint8_t> pv; pv.resize(4 * 4); @@ -581,6 +582,9 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte } ct = t->canvas_texture; + if (t->render_target) { + t->render_target->was_used = true; + } } else { ct = canvas_texture_owner.get_or_null(p_texture); } @@ -611,6 +615,9 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte } else { u.append_id(t->rd_texture); ct->size_cache = Size2i(t->width_2d, t->height_2d); + if (t->render_target) { + t->render_target->was_used = true; + } } uniforms.push_back(u); } @@ -626,6 +633,9 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte } else { u.append_id(t->rd_texture); ct->use_normal_cache = true; + if (t->render_target) { + t->render_target->was_used = true; + } } uniforms.push_back(u); } @@ -641,6 +651,9 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte } else { u.append_id(t->rd_texture); ct->use_specular_cache = true; + if (t->render_target) { + t->render_target->was_used = true; + } } uniforms.push_back(u); } @@ -1764,6 +1777,46 @@ Ref<Image> TextureStorage::_validate_texture_format(const Ref<Image> &p_image, T r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; + case Image::FORMAT_ASTC_4x4: + case Image::FORMAT_ASTC_4x4_HDR: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK; + if (p_image->get_format() == Image::FORMAT_ASTC_4x4) { + r_format.format_srgb = RD::DATA_FORMAT_ASTC_4x4_SRGB_BLOCK; + } + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + + } break; // astc 4x4 + case Image::FORMAT_ASTC_8x8: + case Image::FORMAT_ASTC_8x8_HDR: { + if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ASTC_8x8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { + r_format.format = RD::DATA_FORMAT_ASTC_8x8_UNORM_BLOCK; + if (p_image->get_format() == Image::FORMAT_ASTC_8x8) { + r_format.format_srgb = RD::DATA_FORMAT_ASTC_8x8_SRGB_BLOCK; + } + } else { + //not supported, reconvert + r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + image->decompress(); + image->convert(Image::FORMAT_RGBA8); + } + r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; + r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; + r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; + r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; + + } break; // astc 8x8 default: { } @@ -2398,6 +2451,10 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { rt->color = RID(); rt->color_multisample = RID(); + if (rt->texture.is_valid()) { + Texture *tex = get_texture(rt->texture); + tex->render_target = nullptr; + } } void TextureStorage::_update_render_target(RenderTarget *rt) { @@ -2478,6 +2535,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { tex->rd_texture = RID(); tex->rd_texture_srgb = RID(); + tex->render_target = rt; //create shared textures to the color buffer, //so transparent can be supported diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index f4737eb63d..0c70d3f938 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -108,6 +108,8 @@ private: /* Texture API */ + struct RenderTarget; + class Texture { public: TextureType type; @@ -141,6 +143,7 @@ private: Vector<BufferSlice3D> buffer_slices_3d; uint32_t buffer_size_3d = 0; + RenderTarget *render_target = nullptr; bool is_render_target; bool is_proxy; diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index da1470cec3..106871edfd 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -480,6 +480,7 @@ public: Instance *instance = (Instance *)tracker->userdata; switch (p_notification) { case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: + case Dependency::DEPENDENCY_CHANGED_SKELETON_BONES: case Dependency::DEPENDENCY_CHANGED_AABB: { singleton->_instance_queue_update(instance, true, false); @@ -495,8 +496,7 @@ public: case Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE: { singleton->_instance_queue_update(instance, true, true); } break; - case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: - case Dependency::DEPENDENCY_CHANGED_SKELETON_BONES: { + case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { //ignored } break; case Dependency::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR: { diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index ec19094537..28408ddb8e 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -32,8 +32,18 @@ #include "rendering_device_binds.h" +#include "thirdparty/spirv-reflect/spirv_reflect.h" + RenderingDevice *RenderingDevice::singleton = nullptr; +const char *RenderingDevice::shader_stage_names[RenderingDevice::SHADER_STAGE_MAX] = { + "Vertex", + "Fragment", + "TesselationControl", + "TesselationEvaluation", + "Compute", +}; + RenderingDevice *RenderingDevice::get_singleton() { return singleton; } @@ -368,6 +378,323 @@ void RenderingDevice::_compute_list_set_push_constant(ComputeListID p_list, cons compute_list_set_push_constant(p_list, p_data.ptr(), p_data_size); } +Error RenderingDevice::_reflect_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, SpirvReflectionData &r_reflection_data) { + r_reflection_data = {}; + + for (int i = 0; i < p_spirv.size(); i++) { + ShaderStage stage = p_spirv[i].shader_stage; + ShaderStage stage_flag = (ShaderStage)(1 << p_spirv[i].shader_stage); + + if (p_spirv[i].shader_stage == SHADER_STAGE_COMPUTE) { + r_reflection_data.is_compute = true; + ERR_FAIL_COND_V_MSG(p_spirv.size() != 1, FAILED, + "Compute shaders can only receive one stage, dedicated to compute."); + } + ERR_FAIL_COND_V_MSG(r_reflection_data.stages_mask.has_flag(stage_flag), FAILED, + "Stage " + String(shader_stage_names[p_spirv[i].shader_stage]) + " submitted more than once."); + + { + SpvReflectShaderModule module; + const uint8_t *spirv = p_spirv[i].spir_v.ptr(); + SpvReflectResult result = spvReflectCreateShaderModule(p_spirv[i].spir_v.size(), spirv, &module); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed parsing shader."); + + if (r_reflection_data.is_compute) { + r_reflection_data.compute_local_size[0] = module.entry_points->local_size.x; + r_reflection_data.compute_local_size[1] = module.entry_points->local_size.y; + r_reflection_data.compute_local_size[2] = module.entry_points->local_size.z; + } + uint32_t binding_count = 0; + result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating descriptor bindings."); + + if (binding_count > 0) { + // Parse bindings. + + Vector<SpvReflectDescriptorBinding *> bindings; + bindings.resize(binding_count); + result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, bindings.ptrw()); + + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed getting descriptor bindings."); + + for (uint32_t j = 0; j < binding_count; j++) { + const SpvReflectDescriptorBinding &binding = *bindings[j]; + + SpirvReflectionData::Uniform info{}; + + bool need_array_dimensions = false; + bool need_block_size = false; + bool may_be_writable = false; + + switch (binding.descriptor_type) { + case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: { + info.type = UNIFORM_TYPE_SAMPLER; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { + info.type = UNIFORM_TYPE_TEXTURE; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: { + info.type = UNIFORM_TYPE_IMAGE; + need_array_dimensions = true; + may_be_writable = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { + info.type = UNIFORM_TYPE_TEXTURE_BUFFER; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { + info.type = UNIFORM_TYPE_IMAGE_BUFFER; + need_array_dimensions = true; + may_be_writable = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { + info.type = UNIFORM_TYPE_UNIFORM_BUFFER; + need_block_size = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: { + info.type = UNIFORM_TYPE_STORAGE_BUFFER; + need_block_size = true; + may_be_writable = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: { + ERR_PRINT("Dynamic uniform buffer not supported."); + continue; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: { + ERR_PRINT("Dynamic storage buffer not supported."); + continue; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { + info.type = UNIFORM_TYPE_INPUT_ATTACHMENT; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: { + ERR_PRINT("Acceleration structure not supported."); + continue; + } break; + } + + if (need_array_dimensions) { + if (binding.array.dims_count == 0) { + info.length = 1; + } else { + for (uint32_t k = 0; k < binding.array.dims_count; k++) { + if (k == 0) { + info.length = binding.array.dims[0]; + } else { + info.length *= binding.array.dims[k]; + } + } + } + + } else if (need_block_size) { + info.length = binding.block.size; + } else { + info.length = 0; + } + + if (may_be_writable) { + info.writable = !(binding.type_description->decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) && !(binding.block.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE); + } else { + info.writable = false; + } + + info.binding = binding.binding; + uint32_t set = binding.set; + + ERR_FAIL_COND_V_MSG(set >= MAX_UNIFORM_SETS, FAILED, + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."); + + if (set < (uint32_t)r_reflection_data.uniforms.size()) { + // Check if this already exists. + bool exists = false; + for (int k = 0; k < r_reflection_data.uniforms[set].size(); k++) { + if (r_reflection_data.uniforms[set][k].binding == (uint32_t)info.binding) { + // Already exists, verify that it's the same type. + ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].type != info.type, FAILED, + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type."); + + // Also, verify that it's the same size. + ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].length != info.length, FAILED, + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size."); + + // Also, verify that it has the same writability. + ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].writable != info.writable, FAILED, + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different writability."); + + // Just append stage mask and return. + r_reflection_data.uniforms.write[set].write[k].stages_mask.set_flag(stage_flag); + exists = true; + break; + } + } + + if (exists) { + continue; // Merged. + } + } + + info.stages_mask.set_flag(stage_flag); + + if (set >= (uint32_t)r_reflection_data.uniforms.size()) { + r_reflection_data.uniforms.resize(set + 1); + } + + r_reflection_data.uniforms.write[set].push_back(info); + } + } + + { + // Specialization constants. + + uint32_t sc_count = 0; + result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating specialization constants."); + + if (sc_count) { + Vector<SpvReflectSpecializationConstant *> spec_constants; + spec_constants.resize(sc_count); + + result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, spec_constants.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining specialization constants."); + + for (uint32_t j = 0; j < sc_count; j++) { + int32_t existing = -1; + SpirvReflectionData::SpecializationConstant sconst{}; + SpvReflectSpecializationConstant *spc = spec_constants[j]; + + sconst.constant_id = spc->constant_id; + sconst.int_value = 0; // Clear previous value JIC. + switch (spc->constant_type) { + case SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL: { + sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; + sconst.bool_value = spc->default_value.int_bool_value != 0; + } break; + case SPV_REFLECT_SPECIALIZATION_CONSTANT_INT: { + sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; + sconst.int_value = spc->default_value.int_bool_value; + } break; + case SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT: { + sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; + sconst.float_value = spc->default_value.float_value; + } break; + } + sconst.stages_mask.set_flag(stage_flag); + + for (int k = 0; k < r_reflection_data.specialization_constants.size(); k++) { + if (r_reflection_data.specialization_constants[k].constant_id == sconst.constant_id) { + ERR_FAIL_COND_V_MSG(r_reflection_data.specialization_constants[k].type != sconst.type, FAILED, "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their types differ."); + ERR_FAIL_COND_V_MSG(r_reflection_data.specialization_constants[k].int_value != sconst.int_value, FAILED, "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their default values differ."); + existing = k; + break; + } + } + + if (existing > 0) { + r_reflection_data.specialization_constants.write[existing].stages_mask.set_flag(stage_flag); + } else { + r_reflection_data.specialization_constants.push_back(sconst); + } + } + } + } + + if (stage == SHADER_STAGE_VERTEX) { + uint32_t iv_count = 0; + result = spvReflectEnumerateInputVariables(&module, &iv_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating input variables."); + + if (iv_count) { + Vector<SpvReflectInterfaceVariable *> input_vars; + input_vars.resize(iv_count); + + result = spvReflectEnumerateInputVariables(&module, &iv_count, input_vars.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining input variables."); + + for (uint32_t j = 0; j < iv_count; j++) { + if (input_vars[j] && input_vars[j]->decoration_flags == 0) { // Regular input. + r_reflection_data.vertex_input_mask |= (1 << uint32_t(input_vars[j]->location)); + } + } + } + } + + if (stage == SHADER_STAGE_FRAGMENT) { + uint32_t ov_count = 0; + result = spvReflectEnumerateOutputVariables(&module, &ov_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating output variables."); + + if (ov_count) { + Vector<SpvReflectInterfaceVariable *> output_vars; + output_vars.resize(ov_count); + + result = spvReflectEnumerateOutputVariables(&module, &ov_count, output_vars.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining output variables."); + + for (uint32_t j = 0; j < ov_count; j++) { + const SpvReflectInterfaceVariable *refvar = output_vars[j]; + if (refvar != nullptr && refvar->built_in != SpvBuiltInFragDepth) { + r_reflection_data.fragment_output_mask |= 1 << refvar->location; + } + } + } + } + + uint32_t pc_count = 0; + result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating push constants."); + + if (pc_count) { + ERR_FAIL_COND_V_MSG(pc_count > 1, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "': Only one push constant is supported, which should be the same across shader stages."); + + Vector<SpvReflectBlockVariable *> pconstants; + pconstants.resize(pc_count); + result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, pconstants.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining push constants."); +#if 0 + if (pconstants[0] == nullptr) { + Ref<FileAccess> f = FileAccess::open("res://popo.spv", FileAccess::WRITE); + f->store_buffer((const uint8_t *)&SpirV[0], SpirV.size() * sizeof(uint32_t)); + } +#endif + + ERR_FAIL_COND_V_MSG(r_reflection_data.push_constant_size && r_reflection_data.push_constant_size != pconstants[0]->size, FAILED, + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "': Push constant block must be the same across shader stages."); + + r_reflection_data.push_constant_size = pconstants[0]->size; + r_reflection_data.push_constant_stages_mask.set_flag(stage_flag); + + //print_line("Stage: " + String(shader_stage_names[stage]) + " push constant of size=" + itos(push_constant.push_constant_size)); + } + + // Destroy the reflection data when no longer required. + spvReflectDestroyShaderModule(&module); + } + + r_reflection_data.stages_mask.set_flag(stage_flag); + } + + return OK; +} + void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("texture_create", "format", "view", "data"), &RenderingDevice::_texture_create, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("texture_create_shared", "view", "with_texture"), &RenderingDevice::_texture_create_shared); @@ -806,7 +1133,7 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(INDEX_BUFFER_FORMAT_UINT16); BIND_ENUM_CONSTANT(INDEX_BUFFER_FORMAT_UINT32); - BIND_ENUM_CONSTANT(STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); + BIND_BITFIELD_FLAG(STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); BIND_ENUM_CONSTANT(UNIFORM_TYPE_SAMPLER); //for sampling only (sampler GLSL type) BIND_ENUM_CONSTANT(UNIFORM_TYPE_SAMPLER_WITH_TEXTURE); // for sampling only); but includes a texture); (samplerXX GLSL type)); first a sampler then a texture diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 27c3f77c5b..0adc78894a 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -749,11 +749,11 @@ public: }; enum StorageBufferUsage { - STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT = 1 + STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT = 1, }; virtual RID uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0; - virtual RID storage_buffer_create(uint32_t p_size, const Vector<uint8_t> &p_data = Vector<uint8_t>(), uint32_t p_usage = 0) = 0; + virtual RID storage_buffer_create(uint32_t p_size, const Vector<uint8_t> &p_data = Vector<uint8_t>(), BitField<StorageBufferUsage> p_usage = 0) = 0; virtual RID texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0; struct Uniform { @@ -1301,6 +1301,10 @@ public: RenderingDevice(); protected: + static const char *shader_stage_names[RenderingDevice::SHADER_STAGE_MAX]; + + static const uint32_t MAX_UNIFORM_SETS = 16; + //binders to script API RID _texture_create(const Ref<RDTextureFormat> &p_format, const Ref<RDTextureView> &p_view, const TypedArray<PackedByteArray> &p_data = Array()); RID _texture_create_shared(const Ref<RDTextureView> &p_view, RID p_with_texture); @@ -1329,6 +1333,39 @@ protected: void _draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size); void _compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size); Vector<int64_t> _draw_list_switch_to_next_pass_split(uint32_t p_splits); + + struct SpirvReflectionData { + BitField<ShaderStage> stages_mask; + uint32_t vertex_input_mask; + uint32_t fragment_output_mask; + bool is_compute; + uint32_t compute_local_size[3]; + uint32_t push_constant_size; + BitField<ShaderStage> push_constant_stages_mask; + + struct Uniform { + UniformType type; + uint32_t binding; + BitField<ShaderStage> stages_mask; + uint32_t length; // Size of arrays (in total elements), or ubos (in bytes * total elements). + bool writable; + }; + Vector<Vector<Uniform>> uniforms; + + struct SpecializationConstant { + PipelineSpecializationConstantType type; + uint32_t constant_id; + union { + uint32_t int_value; + float float_value; + bool bool_value; + }; + BitField<ShaderStage> stages_mask; + }; + Vector<SpecializationConstant> specialization_constants; + }; + + Error _reflect_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, SpirvReflectionData &r_reflection_data); }; VARIANT_ENUM_CAST(RenderingDevice::DeviceType) @@ -1348,7 +1385,7 @@ VARIANT_ENUM_CAST(RenderingDevice::SamplerRepeatMode) VARIANT_ENUM_CAST(RenderingDevice::SamplerBorderColor) VARIANT_ENUM_CAST(RenderingDevice::VertexFrequency) VARIANT_ENUM_CAST(RenderingDevice::IndexBufferFormat) -VARIANT_ENUM_CAST(RenderingDevice::StorageBufferUsage) +VARIANT_BITFIELD_CAST(RenderingDevice::StorageBufferUsage) VARIANT_ENUM_CAST(RenderingDevice::UniformType) VARIANT_ENUM_CAST(RenderingDevice::RenderPrimitive) VARIANT_ENUM_CAST(RenderingDevice::PolygonCullMode) diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 2180364695..3f5b135d2a 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -851,7 +851,7 @@ public: FUNC4(canvas_item_add_circle, RID, const Point2 &, float, const Color &) FUNC6(canvas_item_add_texture_rect, RID, const Rect2 &, RID, bool, const Color &, bool) FUNC7(canvas_item_add_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &, bool, bool) - FUNC7(canvas_item_add_msdf_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &, int, float) + FUNC8(canvas_item_add_msdf_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &, int, float, float) FUNC5(canvas_item_add_lcd_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &) FUNC10(canvas_item_add_nine_patch, RID, const Rect2 &, const Rect2 &, RID, const Vector2 &, const Vector2 &, NinePatchAxisMode, NinePatchAxisMode, bool, const Color &) FUNC6(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float) diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 3604756fd8..00b6a8c44c 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -134,6 +134,8 @@ static String _interpstr(SL::DataInterpolation p_interp) { return "flat "; case SL::INTERPOLATION_SMOOTH: return ""; + case SL::INTERPOLATION_DEFAULT: + return ""; } return ""; } @@ -667,6 +669,9 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene fragment_varyings.insert(varying_name); continue; } + if (varying.type < SL::TYPE_INT) { + continue; // Ignore boolean types to prevent crashing (if varying is just declared). + } String vcode; String interp_mode = _interpstr(varying.interpolation); diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index e451fb35c2..8c732b3d5a 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -989,6 +989,18 @@ String ShaderLanguage::get_precision_name(DataPrecision p_type) { return ""; } +String ShaderLanguage::get_interpolation_name(DataInterpolation p_interpolation) { + switch (p_interpolation) { + case INTERPOLATION_FLAT: + return "flat"; + case INTERPOLATION_SMOOTH: + return "smooth"; + default: + break; + } + return ""; +} + String ShaderLanguage::get_datatype_name(DataType p_type) { switch (p_type) { case TYPE_VOID: @@ -4417,13 +4429,17 @@ bool ShaderLanguage::_is_operator_assign(Operator p_op) const { } bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, String *r_message) { - if (current_function != String("vertex") && current_function != String("fragment")) { + if (current_function != "vertex" && current_function != "fragment") { *r_message = vformat(RTR("Varying may not be assigned in the '%s' function."), current_function); return false; } switch (p_varying.stage) { case ShaderNode::Varying::STAGE_UNKNOWN: // first assign if (current_function == varying_function_names.vertex) { + if (p_varying.type < TYPE_INT) { + *r_message = vformat(RTR("Varying with '%s' data type may only be assigned in the 'fragment' function."), get_datatype_name(p_varying.type)); + return false; + } p_varying.stage = ShaderNode::Varying::STAGE_VERTEX; } else if (current_function == varying_function_names.fragment) { p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT; @@ -5223,7 +5239,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (shader->varyings.has(varname)) { switch (shader->varyings[varname].stage) { case ShaderNode::Varying::STAGE_UNKNOWN: { - _set_error(vformat(RTR("Varying '%s' must be assigned in the vertex or fragment function first."), varname)); + _set_error(vformat(RTR("Varying '%s' must be assigned in the 'vertex' or 'fragment' function first."), varname)); return nullptr; } case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT: @@ -5407,6 +5423,16 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } else { switch (var.stage) { + case ShaderNode::Varying::STAGE_UNKNOWN: { + if (var.type < TYPE_INT) { + if (current_function == varying_function_names.vertex) { + _set_error(vformat(RTR("Varying with '%s' data type may only be used in the 'fragment' function."), get_datatype_name(var.type))); + } else { + _set_error(vformat(RTR("Varying '%s' must be assigned in the 'fragment' function first."), identifier)); + } + return nullptr; + } + } break; case ShaderNode::Varying::STAGE_VERTEX: if (current_function == varying_function_names.fragment || current_function == varying_function_names.light) { var.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT; @@ -8225,7 +8251,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f } } DataPrecision precision = PRECISION_DEFAULT; - DataInterpolation interpolation = INTERPOLATION_SMOOTH; + DataInterpolation interpolation = INTERPOLATION_DEFAULT; DataType type; StringName name; int array_size = 0; @@ -8334,6 +8360,11 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f return ERR_PARSE_ERROR; } + if (!is_uniform && interpolation != INTERPOLATION_DEFAULT && type < TYPE_INT) { + _set_error(vformat(RTR("Interpolation modifier '%s' cannot be used with boolean types."), get_interpolation_name(interpolation))); + return ERR_PARSE_ERROR; + } + if (!is_uniform && type > TYPE_MAT4) { _set_error(RTR("Invalid data type for varying.")); return ERR_PARSE_ERROR; diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 2534d1f252..d34114589f 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -247,6 +247,7 @@ public: enum DataInterpolation { INTERPOLATION_FLAT, INTERPOLATION_SMOOTH, + INTERPOLATION_DEFAULT, }; enum Operator { @@ -720,6 +721,12 @@ public: Node(TYPE_SHADER) {} }; + struct UniformOrderComparator { + _FORCE_INLINE_ bool operator()(const Pair<StringName, int> &A, const Pair<StringName, int> &B) const { + return A.second < B.second; + } + }; + struct Expression { bool is_op; union { @@ -768,6 +775,7 @@ public: static bool is_token_arg_qual(TokenType p_type); static DataPrecision get_token_precision(TokenType p_type); static String get_precision_name(DataPrecision p_type); + static String get_interpolation_name(DataInterpolation p_interpolation); static String get_datatype_name(DataType p_type); static String get_uniform_hint_name(ShaderNode::Uniform::Hint p_hint); static String get_texture_filter_name(TextureFilter p_filter); diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp index 9b1842f1d9..78ce4eb4d0 100644 --- a/servers/rendering/storage/environment_storage.cpp +++ b/servers/rendering/storage/environment_storage.cpp @@ -283,7 +283,7 @@ void RendererEnvironmentStorage::environment_set_volumetric_fog(RID p_env, bool env->volumetric_fog_scattering = p_albedo; env->volumetric_fog_emission = p_emission; env->volumetric_fog_emission_energy = p_emission_energy; - env->volumetric_fog_anisotropy = p_anisotropy, + env->volumetric_fog_anisotropy = p_anisotropy; env->volumetric_fog_length = p_length; env->volumetric_fog_detail_spread = p_detail_spread; env->volumetric_fog_gi_inject = p_gi_inject; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 285e03fb06..de3bd8d3e0 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2597,7 +2597,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("canvas_item_add_rect", "item", "rect", "color"), &RenderingServer::canvas_item_add_rect); ClassDB::bind_method(D_METHOD("canvas_item_add_circle", "item", "pos", "radius", "color"), &RenderingServer::canvas_item_add_circle); ClassDB::bind_method(D_METHOD("canvas_item_add_texture_rect", "item", "rect", "texture", "tile", "modulate", "transpose"), &RenderingServer::canvas_item_add_texture_rect, DEFVAL(false), DEFVAL(Color(1, 1, 1)), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("canvas_item_add_msdf_texture_rect_region", "item", "rect", "texture", "src_rect", "modulate", "outline_size", "px_range"), &RenderingServer::canvas_item_add_msdf_texture_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("canvas_item_add_msdf_texture_rect_region", "item", "rect", "texture", "src_rect", "modulate", "outline_size", "px_range", "scale"), &RenderingServer::canvas_item_add_msdf_texture_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(1.0), DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("canvas_item_add_lcd_texture_rect_region", "item", "rect", "texture", "src_rect", "modulate"), &RenderingServer::canvas_item_add_lcd_texture_rect_region); ClassDB::bind_method(D_METHOD("canvas_item_add_texture_rect_region", "item", "rect", "texture", "src_rect", "modulate", "transpose", "clip_uv"), &RenderingServer::canvas_item_add_texture_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(true)); ClassDB::bind_method(D_METHOD("canvas_item_add_nine_patch", "item", "rect", "source", "texture", "topleft", "bottomright", "x_axis_mode", "y_axis_mode", "draw_center", "modulate"), &RenderingServer::canvas_item_add_nine_patch, DEFVAL(NINE_PATCH_STRETCH), DEFVAL(NINE_PATCH_STRETCH), DEFVAL(true), DEFVAL(Color(1, 1, 1))); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 7c9b750503..f3d95e07cb 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1340,7 +1340,7 @@ public: virtual void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color) = 0; virtual void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) = 0; virtual void canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false) = 0; - virtual void canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, float p_px_range = 1.0) = 0; + virtual void canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, float p_px_range = 1.0, float p_scale = 1.0) = 0; virtual void canvas_item_add_lcd_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1)) = 0; virtual void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, NinePatchAxisMode p_x_axis_mode = NINE_PATCH_STRETCH, NinePatchAxisMode p_y_axis_mode = NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1)) = 0; virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0) = 0; diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index 39b87fc483..4baa1db9bf 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -240,8 +240,8 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_shaped_text_get_spacing, "shaped", "spacing"); GDVIRTUAL_BIND(_shaped_text_add_string, "shaped", "text", "fonts", "size", "opentype_features", "language", "meta"); - GDVIRTUAL_BIND(_shaped_text_add_object, "shaped", "key", "size", "inline_align", "length"); - GDVIRTUAL_BIND(_shaped_text_resize_object, "shaped", "key", "size", "inline_align"); + GDVIRTUAL_BIND(_shaped_text_add_object, "shaped", "key", "size", "inline_align", "length", "baseline"); + GDVIRTUAL_BIND(_shaped_text_resize_object, "shaped", "key", "size", "inline_align", "baseline"); GDVIRTUAL_BIND(_shaped_get_span_count, "shaped"); GDVIRTUAL_BIND(_shaped_get_span_meta, "shaped", "index"); @@ -308,7 +308,7 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_strip_diacritics, "string"); GDVIRTUAL_BIND(_is_valid_identifier, "string"); - GDVIRTUAL_BIND(_string_get_word_breaks, "string", "language"); + GDVIRTUAL_BIND(_string_get_word_breaks, "string", "language", "chars_per_line"); GDVIRTUAL_BIND(_is_confusable, "string", "dict"); GDVIRTUAL_BIND(_spoof_check, "string"); @@ -1041,15 +1041,15 @@ bool TextServerExtension::shaped_text_add_string(const RID &p_shaped, const Stri return ret; } -bool TextServerExtension::shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length) { +bool TextServerExtension::shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, float p_baseline) { bool ret = false; - GDVIRTUAL_CALL(_shaped_text_add_object, p_shaped, p_key, p_size, p_inline_align, p_length, ret); + GDVIRTUAL_CALL(_shaped_text_add_object, p_shaped, p_key, p_size, p_inline_align, p_length, p_baseline, ret); return ret; } -bool TextServerExtension::shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align) { +bool TextServerExtension::shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) { bool ret = false; - GDVIRTUAL_CALL(_shaped_text_resize_object, p_shaped, p_key, p_size, p_inline_align, ret); + GDVIRTUAL_CALL(_shaped_text_resize_object, p_shaped, p_key, p_size, p_inline_align, p_baseline, ret); return ret; } @@ -1379,9 +1379,9 @@ TypedArray<Vector2i> TextServerExtension::parse_structured_text(StructuredTextPa return ret; } -PackedInt32Array TextServerExtension::string_get_word_breaks(const String &p_string, const String &p_language) const { +PackedInt32Array TextServerExtension::string_get_word_breaks(const String &p_string, const String &p_language, int p_chars_per_line) const { PackedInt32Array ret; - GDVIRTUAL_CALL(_string_get_word_breaks, p_string, p_language, ret); + GDVIRTUAL_CALL(_string_get_word_breaks, p_string, p_language, p_chars_per_line, ret); return ret; } diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index 69f9a479ed..551e4e9087 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -395,11 +395,11 @@ public: GDVIRTUAL2RC(int64_t, _shaped_text_get_spacing, RID, SpacingType); virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override; - virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1) override; - virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override; + virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1, float p_baseline = 0.0) override; + virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, float p_baseline = 0.0) override; GDVIRTUAL7R(bool, _shaped_text_add_string, RID, const String &, const TypedArray<RID> &, int64_t, const Dictionary &, const String &, const Variant &); - GDVIRTUAL5R(bool, _shaped_text_add_object, RID, const Variant &, const Size2 &, InlineAlignment, int64_t); - GDVIRTUAL4R(bool, _shaped_text_resize_object, RID, const Variant &, const Size2 &, InlineAlignment); + GDVIRTUAL6R(bool, _shaped_text_add_object, RID, const Variant &, const Size2 &, InlineAlignment, int64_t, float); + GDVIRTUAL5R(bool, _shaped_text_resize_object, RID, const Variant &, const Size2 &, InlineAlignment, float); virtual int64_t shaped_get_span_count(const RID &p_shaped) const override; virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const override; @@ -510,8 +510,8 @@ public: virtual String strip_diacritics(const String &p_string) const override; GDVIRTUAL1RC(String, _strip_diacritics, const String &); - virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "") const override; - GDVIRTUAL2RC(PackedInt32Array, _string_get_word_breaks, const String &, const String &); + virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "", int p_chars_per_line = 0) const override; + GDVIRTUAL3RC(PackedInt32Array, _string_get_word_breaks, const String &, const String &, int); virtual bool is_valid_identifier(const String &p_string) const override; GDVIRTUAL1RC(bool, _is_valid_identifier, const String &); diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 1b9cd28cfb..c0f235fe50 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -394,8 +394,8 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_get_spacing", "shaped", "spacing"), &TextServer::shaped_text_get_spacing); ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); + ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length", "baseline"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align", "baseline"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("shaped_get_span_count", "shaped"), &TextServer::shaped_get_span_count); ClassDB::bind_method(D_METHOD("shaped_get_span_meta", "shaped", "index"), &TextServer::shaped_get_span_meta); @@ -454,7 +454,7 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("parse_number", "number", "language"), &TextServer::parse_number, DEFVAL("")); ClassDB::bind_method(D_METHOD("percent_sign", "language"), &TextServer::percent_sign, DEFVAL("")); - ClassDB::bind_method(D_METHOD("string_get_word_breaks", "string", "language"), &TextServer::string_get_word_breaks, DEFVAL("")); + ClassDB::bind_method(D_METHOD("string_get_word_breaks", "string", "language", "chars_per_line"), &TextServer::string_get_word_breaks, DEFVAL(""), DEFVAL(0)); ClassDB::bind_method(D_METHOD("is_confusable", "string", "dict"), &TextServer::is_confusable); ClassDB::bind_method(D_METHOD("spoof_check", "string"), &TextServer::spoof_check); diff --git a/servers/text_server.h b/servers/text_server.h index 5da38a627a..0d94f45b79 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -420,8 +420,8 @@ public: virtual int64_t shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const = 0; virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) = 0; - virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1) = 0; - virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) = 0; + virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1, float p_baseline = 0.0) = 0; + virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, float p_baseline = 0.0) = 0; virtual int64_t shaped_get_span_count(const RID &p_shaped) const = 0; virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const = 0; @@ -493,7 +493,7 @@ public: virtual String percent_sign(const String &p_language = "") const = 0; // String functions. - virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "") const = 0; + virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "", int p_chars_per_line = 0) const = 0; virtual int64_t is_confusable(const String &p_string, const PackedStringArray &p_dict) const { return -1; }; virtual bool spoof_check(const String &p_string) const { return false; }; diff --git a/tests/core/config/test_project_settings.h b/tests/core/config/test_project_settings.h new file mode 100644 index 0000000000..c99ba76a24 --- /dev/null +++ b/tests/core/config/test_project_settings.h @@ -0,0 +1,102 @@ +/*************************************************************************/ +/* test_project_settings.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_PROJECT_SETTINGS_H +#define TEST_PROJECT_SETTINGS_H + +#include "core/config/project_settings.h" +#include "core/variant/variant.h" +#include "tests/test_macros.h" + +namespace TestProjectSettings { + +TEST_CASE("[ProjectSettings] Get existing setting") { + CHECK(ProjectSettings::get_singleton()->has_setting("application/config/name")); + + Variant variant = ProjectSettings::get_singleton()->get_setting("application/config/name"); + CHECK_EQ(variant.get_type(), Variant::STRING); + + String name = variant; + CHECK_EQ(name, "GDScript Integration Test Suite"); +} + +TEST_CASE("[ProjectSettings] Default value is ignored if setting exists") { + CHECK(ProjectSettings::get_singleton()->has_setting("application/config/name")); + + Variant variant = ProjectSettings::get_singleton()->get_setting("application/config/name", "SomeDefaultValue"); + CHECK_EQ(variant.get_type(), Variant::STRING); + + String name = variant; + CHECK_EQ(name, "GDScript Integration Test Suite"); +} + +TEST_CASE("[ProjectSettings] Non existing setting is null") { + CHECK_FALSE(ProjectSettings::get_singleton()->has_setting("not_existing_setting")); + + Variant variant = ProjectSettings::get_singleton()->get_setting("not_existing_setting"); + CHECK_EQ(variant.get_type(), Variant::NIL); +} + +TEST_CASE("[ProjectSettings] Non existing setting should return default value") { + CHECK_FALSE(ProjectSettings::get_singleton()->has_setting("not_existing_setting")); + + Variant variant = ProjectSettings::get_singleton()->get_setting("not_existing_setting"); + CHECK_EQ(variant.get_type(), Variant::NIL); + + variant = ProjectSettings::get_singleton()->get_setting("not_existing_setting", "my_nice_default_value"); + CHECK_EQ(variant.get_type(), Variant::STRING); + + String name = variant; + CHECK_EQ(name, "my_nice_default_value"); + + CHECK_FALSE(ProjectSettings::get_singleton()->has_setting("not_existing_setting")); +} + +TEST_CASE("[ProjectSettings] Set value should be returned when retrieved") { + CHECK_FALSE(ProjectSettings::get_singleton()->has_setting("my_custom_setting")); + + Variant variant = ProjectSettings::get_singleton()->get_setting("my_custom_setting"); + CHECK_EQ(variant.get_type(), Variant::NIL); + + ProjectSettings::get_singleton()->set_setting("my_custom_setting", true); + CHECK(ProjectSettings::get_singleton()->has_setting("my_custom_setting")); + + variant = ProjectSettings::get_singleton()->get_setting("my_custom_setting"); + CHECK_EQ(variant.get_type(), Variant::BOOL); + + bool value = variant; + CHECK_EQ(true, value); + + CHECK(ProjectSettings::get_singleton()->has_setting("my_custom_setting")); +} + +} // namespace TestProjectSettings + +#endif // TEST_PROJECT_SETTINGS_H diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h index 3940bdb37a..fa31a3a461 100644 --- a/tests/scene/test_code_edit.h +++ b/tests/scene/test_code_edit.h @@ -2871,6 +2871,89 @@ TEST_CASE("[SceneTree][CodeEdit] completion") { CHECK(code_edit->get_text() == "\'\'\'"); } + SUBCASE("[CodeEdit] autocomplete with brace completion") { + code_edit->set_auto_brace_completion_enabled(true); + CHECK(code_edit->is_auto_brace_completion_enabled()); + + code_edit->insert_text_at_caret("(te)"); + code_edit->set_caret_column(3); + + // Full completion. + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_FUNCTION, "test()", "test()"); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "(test())"); + CHECK(code_edit->get_caret_column() == 7); + code_edit->undo(); + + // With "arg". + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_FUNCTION, "test(", "test("); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "(test())"); + CHECK(code_edit->get_caret_column() == 6); + code_edit->undo(); + + // brace completion disbaled + code_edit->set_auto_brace_completion_enabled(false); + + // Full completion. + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_FUNCTION, "test()", "test()"); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "(test())"); + CHECK(code_edit->get_caret_column() == 7); + code_edit->undo(); + + // With "arg". + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_FUNCTION, "test(", "test("); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "(test()"); + CHECK(code_edit->get_caret_column() == 6); + + // String + code_edit->set_auto_brace_completion_enabled(true); + code_edit->clear(); + code_edit->insert_text_at_caret("\"\""); + code_edit->set_caret_column(1); + + // Full completion. + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_NODE_PATH, "\"test\"", "\"test\""); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "\"test\""); + CHECK(code_edit->get_caret_column() == 6); + code_edit->undo(); + + // With "arg". + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_NODE_PATH, "\"test", "\"test"); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "\"\"test\"\""); + CHECK(code_edit->get_caret_column() == 7); + code_edit->undo(); + + // brace completion disbaled + code_edit->set_auto_brace_completion_enabled(false); + + // Full completion. + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_NODE_PATH, "\"test\"", "\"test\""); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "\"test\""); + CHECK(code_edit->get_caret_column() == 6); + code_edit->undo(); + + // With "arg". + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_NODE_PATH, "\"test", "\"test"); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "\"\"test\""); + CHECK(code_edit->get_caret_column() == 7); + code_edit->undo(); + } + SUBCASE("[CodeEdit] autocomplete") { code_edit->set_code_completion_enabled(true); CHECK(code_edit->is_code_completion_enabled()); @@ -3027,7 +3110,7 @@ TEST_CASE("[SceneTree][CodeEdit] completion") { CHECK(code_edit->get_code_completion_selected_index() == 0); Point2 caret_pos = code_edit->get_caret_draw_pos(); - caret_pos.y -= code_edit->get_line_height(); + caret_pos.y += code_edit->get_line_height(); SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::WHEEL_DOWN, MouseButton::NONE, Key::NONE); CHECK(code_edit->get_code_completion_selected_index() == 1); @@ -3035,6 +3118,7 @@ TEST_CASE("[SceneTree][CodeEdit] completion") { CHECK(code_edit->get_code_completion_selected_index() == 0); /* Single click selects. */ + caret_pos.y += code_edit->get_line_height() * 2; SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); CHECK(code_edit->get_code_completion_selected_index() == 2); diff --git a/tests/servers/test_text_server.h b/tests/servers/test_text_server.h index 297f7d2068..b3c120e0ba 100644 --- a/tests/servers/test_text_server.h +++ b/tests/servers/test_text_server.h @@ -593,12 +593,18 @@ TEST_SUITE("[TextServer]") { String text1 = U"linguistically similar and effectively form"; // 14^ 22^ 26^ 38^ PackedInt32Array breaks = ts->string_get_word_breaks(text1, "en"); - CHECK(breaks.size() == 4); - if (breaks.size() == 4) { - CHECK(breaks[0] == 14); - CHECK(breaks[1] == 22); - CHECK(breaks[2] == 26); - CHECK(breaks[3] == 38); + CHECK(breaks.size() == 10); + if (breaks.size() == 10) { + CHECK(breaks[0] == 0); + CHECK(breaks[1] == 14); + CHECK(breaks[2] == 15); + CHECK(breaks[3] == 22); + CHECK(breaks[4] == 23); + CHECK(breaks[5] == 26); + CHECK(breaks[6] == 27); + CHECK(breaks[7] == 38); + CHECK(breaks[8] == 39); + CHECK(breaks[9] == 43); } } @@ -608,16 +614,26 @@ TEST_SUITE("[TextServer]") { // 3^ 7^ 13^ 16^ 20^ 25^ 29^ 32^ PackedInt32Array breaks = ts->string_get_word_breaks(text2, "th"); - CHECK(breaks.size() == 8); - if (breaks.size() == 8) { - CHECK(breaks[0] == 3); - CHECK(breaks[1] == 7); - CHECK(breaks[2] == 13); - CHECK(breaks[3] == 16); - CHECK(breaks[4] == 20); - CHECK(breaks[5] == 25); - CHECK(breaks[6] == 29); - CHECK(breaks[7] == 32); + CHECK(breaks.size() == 18); + if (breaks.size() == 18) { + CHECK(breaks[0] == 0); + CHECK(breaks[1] == 4); + CHECK(breaks[2] == 4); + CHECK(breaks[3] == 8); + CHECK(breaks[4] == 8); + CHECK(breaks[5] == 14); + CHECK(breaks[6] == 14); + CHECK(breaks[7] == 17); + CHECK(breaks[8] == 17); + CHECK(breaks[9] == 21); + CHECK(breaks[10] == 21); + CHECK(breaks[11] == 26); + CHECK(breaks[12] == 26); + CHECK(breaks[13] == 30); + CHECK(breaks[14] == 30); + CHECK(breaks[15] == 33); + CHECK(breaks[16] == 33); + CHECK(breaks[17] == 42); } } } diff --git a/tests/test_main.cpp b/tests/test_main.cpp index d58c19ac32..4c4d47a7ae 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -30,6 +30,7 @@ #include "test_main.h" +#include "tests/core/config/test_project_settings.h" #include "tests/core/input/test_input_event_key.h" #include "tests/core/input/test_shortcut.h" #include "tests/core/io/test_config_file.h" diff --git a/thirdparty/README.md b/thirdparty/README.md index 717cc835b0..d50d2cc0ba 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -230,7 +230,7 @@ Files extracted from upstream source: ## harfbuzz - Upstream: https://github.com/harfbuzz/harfbuzz -- Version: 5.3.1 (970321db7bddbe8c579b73751fc655a924ea3ce6, 2022) +- Version: 6.0.0 (afcae83a064843d71d47624bc162e121cc56c08b, 2022) - License: MIT Files extracted from upstream source: @@ -345,7 +345,7 @@ Files extracted from upstream source: ## mbedtls - Upstream: https://github.com/Mbed-TLS/mbedtls -- Version: 2.18.1 (dd79db10014d85b26d11fe57218431f2e5ede6f2, 2022) +- Version: 2.18.2 (89f040a5c938985c5f30728baed21e49d0846a53, 2022) - License: Apache 2.0 File extracted from upstream release tarball: @@ -355,12 +355,10 @@ File extracted from upstream release tarball: - The `LICENSE` file. - Applied the patch in `patches/1453.diff` (upstream PR: https://github.com/ARMmbed/mbedtls/pull/1453). + Applied the patch in `patches/windows-arm64-hardclock.diff` - Added 2 files `godot_core_mbedtls_platform.c` and `godot_core_mbedtls_config.h` providing configuration for light bundling with core. -Some changes have been made in order to fix Windows on ARM build errors. -They are marked with `// -- GODOT start --` and `// -- GODOT end --` - ## meshoptimizer @@ -572,7 +570,7 @@ Exclude: ## pcre2 - Upstream: http://www.pcre.org -- Version: 10.40 (3103b8f20a3b9944b177e812fde29fbfb8b90558, 2022) +- Version: 10.42 (52c08847921a324c804cabf2814549f50bce1265, 2022) - License: BSD-3-Clause Files extracted from upstream source: diff --git a/thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh b/thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh index eef89a2879..fbd7c642ab 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh @@ -49,7 +49,7 @@ struct Coverage HBUINT16 format; /* Format identifier */ CoverageFormat1_3<SmallTypes> format1; CoverageFormat2_4<SmallTypes> format2; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K CoverageFormat1_3<MediumTypes>format3; CoverageFormat2_4<MediumTypes>format4; #endif @@ -65,7 +65,7 @@ struct Coverage { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return_trace (u.format3.sanitize (c)); case 4: return_trace (u.format4.sanitize (c)); #endif @@ -74,10 +74,8 @@ struct Coverage } /* Has interface. */ - static constexpr unsigned SENTINEL = NOT_COVERED; - typedef unsigned int value_t; - value_t operator [] (hb_codepoint_t k) const { return get (k); } - bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + unsigned operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != NOT_COVERED; } /* Predicate. */ bool operator () (hb_codepoint_t k) const { return has (k); } @@ -87,7 +85,7 @@ struct Coverage switch (u.format) { case 1: return u.format1.get_coverage (glyph_id); case 2: return u.format2.get_coverage (glyph_id); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return u.format3.get_coverage (glyph_id); case 4: return u.format4.get_coverage (glyph_id); #endif @@ -100,7 +98,7 @@ struct Coverage switch (u.format) { case 1: return u.format1.get_population (); case 2: return u.format2.get_population (); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return u.format3.get_population (); case 4: return u.format4.get_population (); #endif @@ -127,7 +125,7 @@ struct Coverage } u.format = count <= num_ranges * 3 ? 1 : 2; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K if (count && last > 0xFFFFu) u.format += 2; #endif @@ -136,7 +134,7 @@ struct Coverage { case 1: return_trace (u.format1.serialize (c, glyphs)); case 2: return_trace (u.format2.serialize (c, glyphs)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return_trace (u.format3.serialize (c, glyphs)); case 4: return_trace (u.format4.serialize (c, glyphs)); #endif @@ -166,7 +164,7 @@ struct Coverage { case 1: return u.format1.intersects (glyphs); case 2: return u.format2.intersects (glyphs); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return u.format3.intersects (glyphs); case 4: return u.format4.intersects (glyphs); #endif @@ -179,7 +177,7 @@ struct Coverage { case 1: return u.format1.intersects_coverage (glyphs, index); case 2: return u.format2.intersects_coverage (glyphs, index); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return u.format3.intersects_coverage (glyphs, index); case 4: return u.format4.intersects_coverage (glyphs, index); #endif @@ -196,7 +194,7 @@ struct Coverage { case 1: return u.format1.collect_coverage (glyphs); case 2: return u.format2.collect_coverage (glyphs); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return u.format3.collect_coverage (glyphs); case 4: return u.format4.collect_coverage (glyphs); #endif @@ -212,7 +210,7 @@ struct Coverage { case 1: return u.format1.intersect_set (glyphs, intersect_glyphs); case 2: return u.format2.intersect_set (glyphs, intersect_glyphs); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return u.format3.intersect_set (glyphs, intersect_glyphs); case 4: return u.format4.intersect_set (glyphs, intersect_glyphs); #endif @@ -225,13 +223,13 @@ struct Coverage static constexpr bool is_sorted_iterator = true; iter_t (const Coverage &c_ = Null (Coverage)) { - memset (this, 0, sizeof (*this)); + hb_memset (this, 0, sizeof (*this)); format = c_.u.format; switch (format) { case 1: u.format1.init (c_.u.format1); return; case 2: u.format2.init (c_.u.format2); return; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: u.format3.init (c_.u.format3); return; case 4: u.format4.init (c_.u.format4); return; #endif @@ -244,7 +242,7 @@ struct Coverage { case 1: return u.format1.__more__ (); case 2: return u.format2.__more__ (); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return u.format3.__more__ (); case 4: return u.format4.__more__ (); #endif @@ -257,7 +255,7 @@ struct Coverage { case 1: u.format1.__next__ (); break; case 2: u.format2.__next__ (); break; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: u.format3.__next__ (); break; case 4: u.format4.__next__ (); break; #endif @@ -273,7 +271,7 @@ struct Coverage { case 1: return u.format1.get_glyph (); case 2: return u.format2.get_glyph (); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return u.format3.get_glyph (); case 4: return u.format4.get_glyph (); #endif @@ -287,7 +285,7 @@ struct Coverage { case 1: return u.format1 != o.u.format1; case 2: return u.format2 != o.u.format2; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return u.format3 != o.u.format3; case 4: return u.format4 != o.u.format4; #endif @@ -302,7 +300,7 @@ struct Coverage { case 1: it.u.format1 = u.format1.__end__ (); break; case 2: it.u.format2 = u.format2.__end__ (); break; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: it.u.format3 = u.format3.__end__ (); break; case 4: it.u.format4 = u.format4.__end__ (); break; #endif @@ -314,7 +312,7 @@ struct Coverage private: unsigned int format; union { -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K CoverageFormat2_4<MediumTypes>::iter_t format4; /* Put this one first since it's larger; helps shut up compiler. */ CoverageFormat1_3<MediumTypes>::iter_t format3; #endif diff --git a/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh index 82fd48dc50..5d68e3d15e 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh @@ -77,7 +77,14 @@ struct CoverageFormat1_3 bool intersects (const hb_set_t *glyphs) const { - /* TODO Speed up, using hb_set_next() and bsearch()? */ + if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len) / 2) + { + for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) + if (get_coverage (g) != NOT_COVERED) + return true; + return false; + } + for (const auto& g : glyphArray.as_array ()) if (glyphs->has (g)) return true; diff --git a/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh index 974d094633..d7fcc35202 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh @@ -80,8 +80,6 @@ struct CoverageFormat2_4 TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (this))) return_trace (false); - /* TODO(iter) Write more efficiently? */ - unsigned num_ranges = 0; hb_codepoint_t last = (hb_codepoint_t) -2; for (auto g: glyphs) @@ -115,26 +113,22 @@ struct CoverageFormat2_4 bool intersects (const hb_set_t *glyphs) const { + if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2) + { + for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) + if (get_coverage (g) != NOT_COVERED) + return true; + return false; + } + return hb_any (+ hb_iter (rangeRecord) | hb_map ([glyphs] (const RangeRecord<Types> &range) { return range.intersects (*glyphs); })); } bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { - auto cmp = [] (const void *pk, const void *pr) -> int - { - unsigned index = * (const unsigned *) pk; - const RangeRecord<Types> &range = * (const RangeRecord<Types> *) pr; - if (index < range.value) return -1; - if (index > (unsigned int) range.value + (range.last - range.first)) return +1; - return 0; - }; - - auto arr = rangeRecord.as_array (); - unsigned idx; - if (hb_bsearch_impl (&idx, index, - arr.arrayZ, arr.length, sizeof (arr[0]), - (int (*)(const void *_key, const void *_item)) cmp)) - return arr.arrayZ[idx].intersects (*glyphs); + auto *range = rangeRecord.as_array ().bsearch (index); + if (range) + return range->intersects (*glyphs); return false; } @@ -142,9 +136,14 @@ struct CoverageFormat2_4 hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))> void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const { + /* Break out of loop for overlapping, broken, tables, + * to avoid fuzzer timouts. */ + hb_codepoint_t last = 0; for (const auto& range : rangeRecord) { - hb_codepoint_t last = range.last; + if (unlikely (range.first < last)) + break; + last = range.last; for (hb_codepoint_t g = range.first - 1; glyphs.next (&g) && g <= last;) intersect_glyphs << g; diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh index c99b6b2e4b..edf7099c07 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh @@ -13,7 +13,7 @@ struct MarkBasePos union { HBUINT16 format; /* Format identifier */ MarkBasePosFormat1_2<SmallTypes> format1; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K MarkBasePosFormat1_2<MediumTypes> format2; #endif } u; @@ -26,7 +26,7 @@ struct MarkBasePos if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); #endif default:return_trace (c->default_return_value ()); diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh index 8a4de9ffaa..09152fd876 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh @@ -13,7 +13,7 @@ struct MarkLigPos union { HBUINT16 format; /* Format identifier */ MarkLigPosFormat1_2<SmallTypes> format1; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K MarkLigPosFormat1_2<MediumTypes> format2; #endif } u; @@ -26,7 +26,7 @@ struct MarkLigPos if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); #endif default:return_trace (c->default_return_value ()); diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh index 74b5105c42..4118fc304f 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh @@ -13,7 +13,7 @@ struct MarkMarkPos union { HBUINT16 format; /* Format identifier */ MarkMarkPosFormat1_2<SmallTypes> format1; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K MarkMarkPosFormat1_2<MediumTypes> format2; #endif } u; @@ -26,7 +26,7 @@ struct MarkMarkPos if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); #endif default:return_trace (c->default_return_value ()); diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPos.hh index 72bfc43dc4..9823768cbc 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPos.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPos.hh @@ -15,7 +15,7 @@ struct PairPos HBUINT16 format; /* Format identifier */ PairPosFormat1_3<SmallTypes> format1; PairPosFormat2_4<SmallTypes> format2; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K PairPosFormat1_3<MediumTypes> format3; PairPosFormat2_4<MediumTypes> format4; #endif @@ -30,7 +30,7 @@ struct PairPos switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...)); #endif diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh index ddf7313f94..9c9b268889 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh @@ -51,8 +51,21 @@ struct PairPosFormat1_3 bool intersects (const hb_set_t *glyphs) const { + auto &cov = this+coverage; + + if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4) + { + for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) + { + unsigned i = cov.get_coverage (g); + if ((this+pairSet[i]).intersects (glyphs, valueFormat)) + return true; + } + return false; + } + return - + hb_zip (this+coverage, pairSet) + + hb_zip (cov, pairSet) | hb_filter (*glyphs, hb_first) | hb_map (hb_second) | hb_map ([glyphs, this] (const typename Types::template OffsetTo<PairSet> &_) @@ -171,12 +184,16 @@ struct PairPosFormat1_3 unsigned format1 = 0; unsigned format2 = 0; for (const auto & _ : - + hb_zip (this+coverage, pairSet) | hb_filter (glyphset, hb_first) | hb_map (hb_second)) + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + ) { const PairSet& set = (this + _); const PairValueRecord *record = &set.firstPairValueRecord; - for (unsigned i = 0; i < set.len; i++) + unsigned count = set.len; + for (unsigned i = 0; i < count; i++) { if (record->intersects (glyphset)) { @@ -185,6 +202,9 @@ struct PairPosFormat1_3 } record = &StructAtOffset<const PairValueRecord> (record, record_size); } + + if (format1 == valueFormat[0] && format2 == valueFormat[1]) + break; } return hb_pair (format1, format2); diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh index 83b093b988..9c87ac2b03 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh @@ -220,17 +220,25 @@ struct PairPosFormat2_4 if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) { c->buffer->message (c->font, - "kerning glyphs at %d,%d", + "try kerning glyphs at %d,%d", c->buffer->idx, skippy_iter.idx); } applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos()); applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]); + if (applied_first || applied_second) + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerned glyphs at %d,%d", + c->buffer->idx, skippy_iter.idx); + } + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) { c->buffer->message (c->font, - "kerned glyphs at %d,%d", + "tried kerning glyphs at %d,%d", c->buffer->idx, skippy_iter.idx); } @@ -241,10 +249,15 @@ struct PairPosFormat2_4 boring: buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + if (len2) + { + skippy_iter.idx++; + // https://github.com/harfbuzz/harfbuzz/issues/3824 + // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116 + buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1); + } buffer->idx = skippy_iter.idx; - if (len2) - buffer->idx++; return_trace (true); } @@ -309,6 +322,7 @@ struct PairPosFormat2_4 { unsigned len1 = valueFormat1.get_len (); unsigned len2 = valueFormat2.get_len (); + unsigned record_size = len1 + len2; unsigned format1 = 0; unsigned format2 = 0; @@ -317,10 +331,13 @@ struct PairPosFormat2_4 { for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map)) { - unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * record_size; format1 = format1 | valueFormat1.get_effective_format (&values[idx]); format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]); } + + if (format1 == valueFormat1 && format2 == valueFormat2) + break; } return hb_pair (format1, format2); diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh index aa48d933c3..a318f39913 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh @@ -112,24 +112,38 @@ struct PairSet if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) { c->buffer->message (c->font, - "kerning glyphs at %d,%d", + "try kerning glyphs at %d,%d", c->buffer->idx, pos); } bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()); bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]); + if (applied_first || applied_second) + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerned glyphs at %d,%d", + c->buffer->idx, pos); + } + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) { c->buffer->message (c->font, - "kerned glyphs at %d,%d", + "tried kerning glyphs at %d,%d", c->buffer->idx, pos); } if (applied_first || applied_second) buffer->unsafe_to_break (buffer->idx, pos + 1); + if (len2) - pos++; + { + pos++; + // https://github.com/harfbuzz/harfbuzz/issues/3824 + // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116 + buffer->unsafe_to_break (buffer->idx, pos + 1); + } buffer->idx = pos; return_trace (true); diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh index 4a9e9672eb..6c50c9717a 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh @@ -84,7 +84,7 @@ struct AlternateSet { if (alternates.len && alternate_count) { - + alternates.sub_array (start_offset, alternate_count) + + alternates.as_array ().sub_array (start_offset, alternate_count) | hb_sink (hb_array (alternate_glyphs, *alternate_count)) ; } diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh index 37406179a2..9d7cd6fddf 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh @@ -14,7 +14,7 @@ struct AlternateSubst union { HBUINT16 format; /* Format identifier */ AlternateSubstFormat1_2<SmallTypes> format1; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K AlternateSubstFormat1_2<MediumTypes> format2; #endif } u; @@ -27,7 +27,7 @@ struct AlternateSubst if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); #endif default:return_trace (c->default_return_value ()); diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/Ligature.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/Ligature.hh index 6caa80e056..cdb35f525b 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/Ligature.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/Ligature.hh @@ -118,7 +118,7 @@ struct Ligature match_positions[i] += delta; if (i) *p++ = ','; - snprintf (p, sizeof(buf), "%u", match_positions[i]); + snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]); p += strlen(p); } diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh index 63707972a8..7ba19e844c 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh @@ -14,7 +14,7 @@ struct LigatureSubst union { HBUINT16 format; /* Format identifier */ LigatureSubstFormat1_2<SmallTypes> format1; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K LigatureSubstFormat1_2<MediumTypes> format2; #endif } u; @@ -27,7 +27,7 @@ struct LigatureSubst if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); #endif default:return_trace (c->default_return_value ()); diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh index 852ca3eac5..95710ed2be 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh @@ -14,7 +14,7 @@ struct MultipleSubst union { HBUINT16 format; /* Format identifier */ MultipleSubstFormat1_2<SmallTypes> format1; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K MultipleSubstFormat1_2<MediumTypes> format2; #endif } u; @@ -28,7 +28,7 @@ struct MultipleSubst if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); #endif default:return_trace (c->default_return_value ()); diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh index abf1c643ff..e2190078b8 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh @@ -117,7 +117,7 @@ struct Sequence { if (buf < p) *p++ = ','; - snprintf (p, sizeof(buf), "%u", i); + snprintf (p, sizeof(buf) - (p - buf), "%u", i); p += strlen(p); } diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh index 6942e6997f..7da8103168 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh @@ -16,7 +16,7 @@ struct SingleSubst HBUINT16 format; /* Format identifier */ SingleSubstFormat1_3<SmallTypes> format1; SingleSubstFormat2_4<SmallTypes> format2; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K SingleSubstFormat1_3<MediumTypes> format3; SingleSubstFormat2_4<MediumTypes> format4; #endif @@ -32,7 +32,7 @@ struct SingleSubst switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...)); #endif @@ -55,7 +55,7 @@ struct SingleSubst format = 1; hb_codepoint_t mask = 0xFFFFu; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K if (+ glyphs | hb_map_retains_sorting (hb_first) | hb_filter ([] (hb_codepoint_t gid) { return gid > 0xFFFFu; })) @@ -78,7 +78,7 @@ struct SingleSubst | hb_map_retains_sorting (hb_first), delta)); case 2: return_trace (u.format2.serialize (c, glyphs)); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: return_trace (u.format3.serialize (c, + glyphs | hb_map_retains_sorting (hb_first), diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh index 13665d7ba1..1be21b98bc 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh @@ -57,7 +57,7 @@ struct SingleSubstFormat1_3 hb_codepoint_t max_before = intersection.get_max (); hb_codepoint_t min_after = (min_before + d) & mask; hb_codepoint_t max_after = (max_before + d) & mask; - if (pop >= max_before - min_before && + if (intersection.get_population () == max_before - min_before + 1 && ((min_before <= min_after && min_after <= max_before) || (min_before <= max_after && max_after <= max_before))) return; diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh index 5416299754..01df714525 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh @@ -36,8 +36,24 @@ struct SingleSubstFormat2_4 void closure (hb_closure_context_t *c) const { - + hb_zip (this+coverage, substitute) - | hb_filter (c->parent_active_glyphs (), hb_first) + auto &cov = this+coverage; + auto &glyph_set = c->parent_active_glyphs (); + + if (substitute.len > glyph_set.get_population () * 4) + { + for (auto g : glyph_set) + { + unsigned i = cov.get_coverage (g); + if (i == NOT_COVERED || i >= substitute.len) + continue; + c->output->add (substitute.arrayZ[i]); + } + + return; + } + + + hb_zip (cov, substitute) + | hb_filter (glyph_set, hb_first) | hb_map (hb_second) | hb_sink (c->output) ; diff --git a/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh index fc8e309bc9..edf8cd8797 100644 --- a/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh +++ b/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh @@ -3,6 +3,7 @@ #include "../../hb-open-type.hh" +#include "composite-iter.hh" namespace OT { @@ -121,7 +122,7 @@ struct CompositeGlyphRecord if (flags & ARG_1_AND_2_ARE_WORDS) { // no overflow, copy and update value with deltas - memcpy (out, this, len); + hb_memcpy (out, this, len); const HBINT16 *px = reinterpret_cast<const HBINT16 *> (p); HBINT16 *o = reinterpret_cast<HBINT16 *> (out + len_before_val); @@ -135,7 +136,7 @@ struct CompositeGlyphRecord if (new_x <= 127 && new_x >= -128 && new_y <= 127 && new_y >= -128) { - memcpy (out, this, len); + hb_memcpy (out, this, len); HBINT8 *o = reinterpret_cast<HBINT8 *> (out + len_before_val); o[0] = new_x; o[1] = new_y; @@ -143,7 +144,7 @@ struct CompositeGlyphRecord else { // int8 overflows after deltas applied - memcpy (out, this, len_before_val); + hb_memcpy (out, this, len_before_val); //update flags CompositeGlyphRecord *o = reinterpret_cast<CompositeGlyphRecord *> (out); @@ -152,14 +153,14 @@ struct CompositeGlyphRecord HBINT16 new_value; new_value = new_x; - memcpy (out, &new_value, HBINT16::static_size); + hb_memcpy (out, &new_value, HBINT16::static_size); out += HBINT16::static_size; new_value = new_y; - memcpy (out, &new_value, HBINT16::static_size); + hb_memcpy (out, &new_value, HBINT16::static_size); out += HBINT16::static_size; - memcpy (out, p+2, len - len_before_val - 2); + hb_memcpy (out, p+2, len - len_before_val - 2); len += 2; } } @@ -252,55 +253,7 @@ struct CompositeGlyphRecord DEFINE_SIZE_MIN (4); }; -struct composite_iter_t : hb_iter_with_fallback_t<composite_iter_t, const CompositeGlyphRecord &> -{ - typedef const CompositeGlyphRecord *__item_t__; - composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : - glyph (glyph_), current (nullptr), current_size (0) - { - set_current (current_); - } - - composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} - - item_t __item__ () const { return *current; } - bool __more__ () const { return current; } - void __next__ () - { - if (!current->has_more ()) { current = nullptr; return; } - - set_current (&StructAtOffset<CompositeGlyphRecord> (current, current_size)); - } - composite_iter_t __end__ () const { return composite_iter_t (); } - bool operator != (const composite_iter_t& o) const - { return current != o.current; } - - - void set_current (__item_t__ current_) - { - if (!glyph.check_range (current_, CompositeGlyphRecord::min_size)) - { - current = nullptr; - current_size = 0; - return; - } - unsigned size = current_->get_size (); - if (!glyph.check_range (current_, size)) - { - current = nullptr; - current_size = 0; - return; - } - - current = current_; - current_size = size; - } - - private: - hb_bytes_t glyph; - __item_t__ current; - unsigned current_size; -}; +using composite_iter_t = composite_iter_tmpl<CompositeGlyphRecord>; struct CompositeGlyph { @@ -382,7 +335,7 @@ struct CompositeGlyph unsigned comp_len = component.get_size (); if (component.is_anchored ()) { - memcpy (p, &component, comp_len); + hb_memcpy (p, &component, comp_len); p += comp_len; } else @@ -398,7 +351,7 @@ struct CompositeGlyph if (source_len > source_comp_len) { unsigned instr_len = source_len - source_comp_len; - memcpy (p, (const char *)c + source_comp_len, instr_len); + hb_memcpy (p, (const char *)c + source_comp_len, instr_len); p += instr_len; } diff --git a/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh b/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh index 0b25659acc..b7215b0170 100644 --- a/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh +++ b/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh @@ -7,6 +7,8 @@ #include "GlyphHeader.hh" #include "SimpleGlyph.hh" #include "CompositeGlyph.hh" +#include "VarCompositeGlyph.hh" +#include "coord-setter.hh" namespace OT { @@ -16,6 +18,11 @@ struct glyf_accelerator_t; namespace glyf_impl { +#ifndef HB_GLYF_MAX_POINTS +#define HB_GLYF_MAX_POINTS 10000 +#endif + + enum phantom_point_index_t { PHANTOM_LEFT = 0, @@ -27,7 +34,7 @@ enum phantom_point_index_t struct Glyph { - enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; + enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE, VAR_COMPOSITE }; public: composite_iter_t get_composite_iterator () const @@ -35,6 +42,11 @@ struct Glyph if (type != COMPOSITE) return composite_iter_t (); return CompositeGlyph (*header, bytes).iter (); } + var_composite_iter_t get_var_composite_iterator () const + { + if (type != VAR_COMPOSITE) return var_composite_iter_t (); + return VarCompositeGlyph (*header, bytes).iter (); + } const hb_bytes_t trim_padding () const { @@ -108,25 +120,25 @@ struct Glyph if (unlikely (!glyph_header)) return false; } - int xMin = 0, xMax = 0; - int yMin = 0, yMax = 0; + float xMin = 0, xMax = 0; + float yMin = 0, yMax = 0; if (all_points.length > 4) { - xMin = xMax = roundf (all_points[0].x); - yMin = yMax = roundf (all_points[0].y); + xMin = xMax = all_points[0].x; + yMin = yMax = all_points[0].y; } for (unsigned i = 1; i < all_points.length - 4; i++) { - float rounded_x = roundf (all_points[i].x); - float rounded_y = roundf (all_points[i].y); - xMin = hb_min (xMin, rounded_x); - xMax = hb_max (xMax, rounded_x); - yMin = hb_min (yMin, rounded_y); - yMax = hb_max (yMax, rounded_y); + float x = all_points[i].x; + float y = all_points[i].y; + xMin = hb_min (xMin, x); + xMax = hb_max (xMax, x); + yMin = hb_min (yMin, y); + yMax = hb_max (yMax, y); } - update_mtx (plan, xMin, yMax, all_points); + update_mtx (plan, roundf (xMin), roundf (yMax), all_points); /*for empty glyphs: all_points only include phantom points. *just update metrics and then return */ @@ -134,10 +146,10 @@ struct Glyph return true; glyph_header->numberOfContours = header->numberOfContours; - glyph_header->xMin = xMin; - glyph_header->yMin = yMin; - glyph_header->xMax = xMax; - glyph_header->yMax = yMax; + glyph_header->xMin = roundf (xMin); + glyph_header->yMin = roundf (yMin); + glyph_header->xMax = roundf (xMax); + glyph_header->yMax = roundf (yMax); dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size); return true; @@ -199,15 +211,24 @@ struct Glyph bool shift_points_hori = true, bool use_my_metrics = true, bool phantom_only = false, + hb_array_t<int> coords = hb_array_t<int> (), unsigned int depth = 0) const { if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; + + if (!coords) + coords = hb_array (font->coords, font->num_coords); + contour_point_vector_t stack_points; bool inplace = type == SIMPLE && all_points.length == 0; /* Load into all_points if it's empty, as an optimization. */ contour_point_vector_t &points = inplace ? all_points : stack_points; switch (type) { + case SIMPLE: + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) + return false; + break; case COMPOSITE: { /* pseudo component points for each component in composite glyph */ @@ -215,20 +236,25 @@ struct Glyph if (unlikely (!points.resize (num_points))) return false; break; } - case SIMPLE: - if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) - return false; +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + { + for (auto &item : get_var_composite_iterator ()) + if (unlikely (!item.get_points (points))) return false; + } +#endif + default: break; } /* Init phantom points */ if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; - hb_array_t<contour_point_t> phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); + hb_array_t<contour_point_t> phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); { int lsb = 0; int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ? (int) header->xMin - lsb : 0; - int tsb = 0; + HB_UNUSED int tsb = 0; int v_orig = (int) header->yMax + #ifndef HB_NO_VERTICAL ((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb) @@ -257,7 +283,9 @@ struct Glyph } #ifndef HB_NO_VAR - glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ()); + glyf_accelerator.gvar->apply_deltas_to_points (gid, + coords, + points.as_array ()); #endif // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it @@ -283,11 +311,24 @@ struct Glyph for (auto &item : get_composite_iterator ()) { comp_points.reset (); + if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ()) - .get_points (font, glyf_accelerator, comp_points, - deltas, shift_points_hori, use_my_metrics, phantom_only, depth + 1))) + .get_points (font, + glyf_accelerator, + comp_points, + deltas, + shift_points_hori, + use_my_metrics, + phantom_only, + coords, + depth + 1))) return false; + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (use_my_metrics && item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + /* Apply component transformation & translation */ item.transform_points (comp_points); @@ -308,20 +349,63 @@ struct Glyph } } + all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT)); + + if (all_points.length > HB_GLYF_MAX_POINTS) + return false; + + comp_index++; + } + + all_points.extend (phantoms); + } break; +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + { + contour_point_vector_t comp_points; + hb_array_t<contour_point_t> points_left = points.as_array (); + for (auto &item : get_var_composite_iterator ()) + { + hb_array_t<contour_point_t> record_points = points_left.sub_array (0, item.get_num_points ()); + + comp_points.reset (); + + coord_setter_t coord_setter (coords); + item.set_variations (coord_setter, record_points); + + if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ()) + .get_points (font, + glyf_accelerator, + comp_points, + deltas, + shift_points_hori, + use_my_metrics, + phantom_only, + coord_setter.get_coords (), + depth + 1))) + return false; + + /* Apply component transformation */ + item.transform_points (record_points, comp_points); + /* Copy phantom points from component if USE_MY_METRICS flag set */ if (use_my_metrics && item.is_use_my_metrics ()) for (unsigned int i = 0; i < PHANTOM_COUNT; i++) phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; - all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); + all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT)); - comp_index++; - } + if (all_points.length > HB_GLYF_MAX_POINTS) + return false; + points_left += item.get_num_points (); + } all_points.extend (phantoms); } break; +#endif default: all_points.extend (phantoms); + break; } if (depth == 0 && shift_points_hori) /* Apply at top level */ @@ -360,6 +444,7 @@ struct Glyph int num_contours = header->numberOfContours; if (unlikely (num_contours == 0)) type = EMPTY; else if (num_contours > 0) type = SIMPLE; + else if (num_contours == -2) type = VAR_COMPOSITE; else type = COMPOSITE; /* negative numbers */ } diff --git a/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh index d45f4eb350..2b4aa99d25 100644 --- a/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh +++ b/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh @@ -132,8 +132,8 @@ struct SimpleGlyph if (unlikely (p + 1 > end)) return false; unsigned int repeat_count = *p++; unsigned stop = hb_min (i + repeat_count, count); - for (; i < stop;) - points_.arrayZ[i++].flag = flag; + for (; i < stop; i++) + points_.arrayZ[i].flag = flag; } } return true; @@ -223,33 +223,34 @@ struct SimpleGlyph if (value > 0) flag |= same_flag; else value = -value; - coords.push ((uint8_t)value); + coords.arrayZ[coords.length++] = (uint8_t) value; } else { int16_t val = value; - coords.push (val >> 8); - coords.push (val & 0xff); + coords.arrayZ[coords.length++] = val >> 8; + coords.arrayZ[coords.length++] = val & 0xff; } } static void encode_flag (uint8_t &flag, uint8_t &repeat, - uint8_t &lastflag, + uint8_t lastflag, hb_vector_t<uint8_t> &flags /* OUT */) { if (flag == lastflag && repeat != 255) { - repeat = repeat + 1; + repeat++; if (repeat == 1) { - flags.push(flag); + /* We know there's room. */ + flags.arrayZ[flags.length++] = flag; } else { unsigned len = flags.length; - flags[len-2] = flag | FLAG_REPEAT; - flags[len-1] = repeat; + flags.arrayZ[len-2] = flag | FLAG_REPEAT; + flags.arrayZ[len-1] = repeat; } } else @@ -257,7 +258,6 @@ struct SimpleGlyph repeat = 0; flags.push (flag); } - lastflag = flag; } bool compile_bytes_with_deltas (const contour_point_vector_t &all_points, @@ -269,7 +269,6 @@ struct SimpleGlyph dest_bytes = hb_bytes_t (); return true; } - //convert absolute values to relative values unsigned num_points = all_points.length - 4; hb_vector_t<uint8_t> flags, x_coords, y_coords; @@ -277,23 +276,23 @@ struct SimpleGlyph if (unlikely (!x_coords.alloc (2*num_points))) return false; if (unlikely (!y_coords.alloc (2*num_points))) return false; - uint8_t lastflag = 0, repeat = 0; - int prev_x = 0.f, prev_y = 0.f; - + uint8_t lastflag = 255, repeat = 0; + int prev_x = 0, prev_y = 0; + for (unsigned i = 0; i < num_points; i++) { - uint8_t flag = all_points[i].flag; + uint8_t flag = all_points.arrayZ[i].flag; flag &= FLAG_ON_CURVE + FLAG_OVERLAP_SIMPLE; - float cur_x = roundf (all_points[i].x); - float cur_y = roundf (all_points[i].y); + int cur_x = roundf (all_points.arrayZ[i].x); + int cur_y = roundf (all_points.arrayZ[i].y); encode_coord (cur_x - prev_x, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords); encode_coord (cur_y - prev_y, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords); - if (i == 0) lastflag = flag + 1; //make lastflag != flag for the first point encode_flag (flag, repeat, lastflag, flags); prev_x = cur_x; prev_y = cur_y; + lastflag = flag; } unsigned len_before_instrs = 2 * header.numberOfContours + 2; @@ -303,29 +302,29 @@ struct SimpleGlyph if (!no_hinting) total_len += len_instrs; - char *p = (char *) hb_calloc (total_len, sizeof (char)); + char *p = (char *) hb_malloc (total_len); if (unlikely (!p)) return false; const char *src = bytes.arrayZ + GlyphHeader::static_size; char *cur = p; - memcpy (p, src, len_before_instrs); + hb_memcpy (p, src, len_before_instrs); cur += len_before_instrs; src += len_before_instrs; if (!no_hinting) { - memcpy (cur, src, len_instrs); + hb_memcpy (cur, src, len_instrs); cur += len_instrs; } - memcpy (cur, flags.arrayZ, flags.length); + hb_memcpy (cur, flags.arrayZ, flags.length); cur += flags.length; - memcpy (cur, x_coords.arrayZ, x_coords.length); + hb_memcpy (cur, x_coords.arrayZ, x_coords.length); cur += x_coords.length; - memcpy (cur, y_coords.arrayZ, y_coords.length); + hb_memcpy (cur, y_coords.arrayZ, y_coords.length); dest_bytes = hb_bytes_t (p, total_len); return true; diff --git a/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh index 88fc93c435..1a0370c757 100644 --- a/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh +++ b/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh @@ -21,10 +21,18 @@ struct SubsetGlyph bool serialize (hb_serialize_context_t *c, bool use_short_loca, - const hb_subset_plan_t *plan) const + const hb_subset_plan_t *plan, + hb_font_t *font) { TRACE_SERIALIZE (this); + if (font) + { + const OT::glyf_accelerator_t &glyf = *font->face->table.glyf; + if (!this->compile_bytes_with_deltas (plan, font, glyf)) + return_trace (false); + } + hb_bytes_t dest_glyph = dest_start.copy (c); dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); unsigned int pad_length = use_short_loca ? padding () : 0; diff --git a/thirdparty/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh new file mode 100644 index 0000000000..0f4c71c83d --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh @@ -0,0 +1,353 @@ +#ifndef OT_GLYF_VARCOMPOSITEGLYPH_HH +#define OT_GLYF_VARCOMPOSITEGLYPH_HH + + +#include "../../hb-open-type.hh" +#include "coord-setter.hh" + + +namespace OT { +namespace glyf_impl { + + +struct VarCompositeGlyphRecord +{ + protected: + enum var_composite_glyph_flag_t + { + USE_MY_METRICS = 0x0001, + AXIS_INDICES_ARE_SHORT = 0x0002, + UNIFORM_SCALE = 0x0004, + HAVE_TRANSLATE_X = 0x0008, + HAVE_TRANSLATE_Y = 0x0010, + HAVE_ROTATION = 0x0020, + HAVE_SCALE_X = 0x0040, + HAVE_SCALE_Y = 0x0080, + HAVE_SKEW_X = 0x0100, + HAVE_SKEW_Y = 0x0200, + HAVE_TCENTER_X = 0x0400, + HAVE_TCENTER_Y = 0x0800, + GID_IS_24 = 0x1000, + AXES_HAVE_VARIATION = 0x2000, + }; + + public: + + unsigned int get_size () const + { + unsigned int size = min_size; + + unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 4 : 3; + size += numAxes * axis_width; + + // gid + size += 2; + if (flags & GID_IS_24) size += 1; + + if (flags & HAVE_TRANSLATE_X) size += 2; + if (flags & HAVE_TRANSLATE_Y) size += 2; + if (flags & HAVE_ROTATION) size += 2; + if (flags & HAVE_SCALE_X) size += 2; + if (flags & HAVE_SCALE_Y) size += 2; + if (flags & HAVE_SKEW_X) size += 2; + if (flags & HAVE_SKEW_Y) size += 2; + if (flags & HAVE_TCENTER_X) size += 2; + if (flags & HAVE_TCENTER_Y) size += 2; + + return size; + } + + bool has_more () const { return true; } + + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + + hb_codepoint_t get_gid () const + { + if (flags & GID_IS_24) + return StructAfter<const HBGlyphID24> (numAxes); + else + return StructAfter<const HBGlyphID16> (numAxes); + } + + unsigned get_numAxes () const + { + return numAxes; + } + + unsigned get_num_points () const + { + unsigned num = 0; + if (flags & AXES_HAVE_VARIATION) num += numAxes; + if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) num++; + if (flags & HAVE_ROTATION) num++; + if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y)) num++; + if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y)) num++; + if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) num++; + return num; + } + + void transform_points (hb_array_t<contour_point_t> record_points, + contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + + get_transformation_from_points (record_points, matrix, trans); + + points.transform (matrix); + points.translate (trans); + } + + static inline void transform (float (&matrix)[4], contour_point_t &trans, + float (other)[6]) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L268 + float xx1 = other[0]; + float xy1 = other[1]; + float yx1 = other[2]; + float yy1 = other[3]; + float dx1 = other[4]; + float dy1 = other[5]; + float xx2 = matrix[0]; + float xy2 = matrix[1]; + float yx2 = matrix[2]; + float yy2 = matrix[3]; + float dx2 = trans.x; + float dy2 = trans.y; + + matrix[0] = xx1*xx2 + xy1*yx2; + matrix[1] = xx1*xy2 + xy1*yy2; + matrix[2] = yx1*xx2 + yy1*yx2; + matrix[3] = yx1*xy2 + yy1*yy2; + trans.x = xx2*dx1 + yx2*dy1 + dx2; + trans.y = xy2*dx1 + yy2*dy1 + dy2; + } + + static void translate (float (&matrix)[4], contour_point_t &trans, + float translateX, float translateY) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L213 + float other[6] = {1.f, 0.f, 0.f, 1.f, translateX, translateY}; + transform (matrix, trans, other); + } + + static void scale (float (&matrix)[4], contour_point_t &trans, + float scaleX, float scaleY) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L224 + float other[6] = {scaleX, 0.f, 0.f, scaleY, 0.f, 0.f}; + transform (matrix, trans, other); + } + + static void rotate (float (&matrix)[4], contour_point_t &trans, + float rotation) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 + rotation = rotation * float (M_PI); + float c = cosf (rotation); + float s = sinf (rotation); + float other[6] = {c, s, -s, c, 0.f, 0.f}; + transform (matrix, trans, other); + } + + static void skew (float (&matrix)[4], contour_point_t &trans, + float skewX, float skewY) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255 + skewX = skewX * float (M_PI); + skewY = skewY * float (M_PI); + float other[6] = {1.f, tanf (skewY), tanf (skewX), 1.f, 0.f, 0.f}; + transform (matrix, trans, other); + } + + bool get_points (contour_point_vector_t &points) const + { + float translateX = 0.f; + float translateY = 0.f; + float rotation = 0.f; + float scaleX = 1.f * (1 << 12); + float scaleY = 1.f * (1 << 12); + float skewX = 0.f; + float skewY = 0.f; + float tCenterX = 0.f; + float tCenterY = 0.f; + + if (unlikely (!points.resize (points.length + get_num_points ()))) return false; + + unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1; + unsigned axes_size = numAxes * axis_width; + + const F2DOT14 *q = (const F2DOT14 *) (axes_size + + (flags & GID_IS_24 ? 3 : 2) + + &StructAfter<const HBUINT8> (numAxes)); + + hb_array_t<contour_point_t> rec_points = points.as_array ().sub_array (points.length - get_num_points ()); + + unsigned count = numAxes; + if (flags & AXES_HAVE_VARIATION) + { + for (unsigned i = 0; i < count; i++) + rec_points[i].x = *q++; + rec_points += count; + } + else + q += count; + + const HBUINT16 *p = (const HBUINT16 *) q; + + if (flags & HAVE_TRANSLATE_X) translateX = * (const FWORD *) p++; + if (flags & HAVE_TRANSLATE_Y) translateY = * (const FWORD *) p++; + if (flags & HAVE_ROTATION) rotation = * (const F2DOT14 *) p++; + if (flags & HAVE_SCALE_X) scaleX = * (const F4DOT12 *) p++; + if (flags & HAVE_SCALE_Y) scaleY = * (const F4DOT12 *) p++; + if (flags & HAVE_SKEW_X) skewX = * (const F2DOT14 *) p++; + if (flags & HAVE_SKEW_Y) skewY = * (const F2DOT14 *) p++; + if (flags & HAVE_TCENTER_X) tCenterX = * (const FWORD *) p++; + if (flags & HAVE_TCENTER_Y) tCenterY = * (const FWORD *) p++; + + if ((flags & UNIFORM_SCALE) && !(flags & HAVE_SCALE_Y)) + scaleY = scaleX; + + if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) + { + rec_points[0].x = translateX; + rec_points[0].y = translateY; + rec_points++; + } + if (flags & HAVE_ROTATION) + { + rec_points[0].x = rotation; + rec_points++; + } + if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y)) + { + rec_points[0].x = scaleX; + rec_points[0].y = scaleY; + rec_points++; + } + if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y)) + { + rec_points[0].x = skewX; + rec_points[0].y = skewY; + rec_points++; + } + if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) + { + rec_points[0].x = tCenterX; + rec_points[0].y = tCenterY; + rec_points++; + } + assert (!rec_points); + + return true; + } + + void get_transformation_from_points (hb_array_t<contour_point_t> rec_points, + float (&matrix)[4], contour_point_t &trans) const + { + if (flags & AXES_HAVE_VARIATION) + rec_points += numAxes; + + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + trans.init (0.f, 0.f); + + float translateX = 0.f; + float translateY = 0.f; + float rotation = 0.f; + float scaleX = 1.f; + float scaleY = 1.f; + float skewX = 0.f; + float skewY = 0.f; + float tCenterX = 0.f; + float tCenterY = 0.f; + + if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) + { + translateX = rec_points[0].x; + translateY = rec_points[0].y; + rec_points++; + } + if (flags & HAVE_ROTATION) + { + rotation = rec_points[0].x / (1 << 14); + rec_points++; + } + if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y)) + { + scaleX = rec_points[0].x / (1 << 12); + scaleY = rec_points[0].y / (1 << 12); + rec_points++; + } + if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y)) + { + skewX = rec_points[0].x / (1 << 14); + skewY = rec_points[0].y / (1 << 14); + rec_points++; + } + if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) + { + tCenterX = rec_points[0].x; + tCenterY = rec_points[0].y; + rec_points++; + } + assert (!rec_points); + + translate (matrix, trans, translateX + tCenterX, translateY + tCenterY); + rotate (matrix, trans, rotation); + scale (matrix, trans, scaleX, scaleY); + skew (matrix, trans, -skewX, skewY); + translate (matrix, trans, -tCenterX, -tCenterY); + } + + void set_variations (coord_setter_t &setter, + hb_array_t<contour_point_t> rec_points) const + { + bool have_variations = flags & AXES_HAVE_VARIATION; + unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1; + + const HBUINT8 *p = (const HBUINT8 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24 ? 3 : 2)); + const HBUINT16 *q = (const HBUINT16 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24 ? 3 : 2)); + + const F2DOT14 *a = (const F2DOT14 *) ((HBUINT8 *) (axis_width == 1 ? (p + numAxes) : (HBUINT8 *) (q + numAxes))); + + unsigned count = numAxes; + for (unsigned i = 0; i < count; i++) + { + unsigned axis_index = axis_width == 1 ? (unsigned) *p++ : (unsigned) *q++; + + signed v = have_variations ? rec_points[i].x : *a++; + + v += setter[axis_index]; + v = hb_clamp (v, -(1<<14), (1<<14)); + setter[axis_index] = v; + } + } + + protected: + HBUINT16 flags; + HBUINT8 numAxes; + public: + DEFINE_SIZE_MIN (3); +}; + +using var_composite_iter_t = composite_iter_tmpl<VarCompositeGlyphRecord>; + +struct VarCompositeGlyph +{ + const GlyphHeader &header; + hb_bytes_t bytes; + VarCompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + var_composite_iter_t iter () const + { return var_composite_iter_t (bytes, &StructAfter<VarCompositeGlyphRecord, GlyphHeader> (header)); } + +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_VARCOMPOSITEGLYPH_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/composite-iter.hh b/thirdparty/harfbuzz/src/OT/glyf/composite-iter.hh new file mode 100644 index 0000000000..d05701f3d1 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/composite-iter.hh @@ -0,0 +1,68 @@ +#ifndef OT_GLYF_COMPOSITE_ITER_HH +#define OT_GLYF_COMPOSITE_ITER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +template <typename CompositeGlyphRecord> +struct composite_iter_tmpl : hb_iter_with_fallback_t<composite_iter_tmpl<CompositeGlyphRecord>, + const CompositeGlyphRecord &> +{ + typedef const CompositeGlyphRecord *__item_t__; + composite_iter_tmpl (hb_bytes_t glyph_, __item_t__ current_) : + glyph (glyph_), current (nullptr), current_size (0) + { + set_current (current_); + } + + composite_iter_tmpl () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} + + const CompositeGlyphRecord & __item__ () const { return *current; } + bool __more__ () const { return current; } + void __next__ () + { + if (!current->has_more ()) { current = nullptr; return; } + + set_current (&StructAtOffset<CompositeGlyphRecord> (current, current_size)); + } + composite_iter_tmpl __end__ () const { return composite_iter_tmpl (); } + bool operator != (const composite_iter_tmpl& o) const + { return current != o.current; } + + + void set_current (__item_t__ current_) + { + if (!glyph.check_range (current_, CompositeGlyphRecord::min_size)) + { + current = nullptr; + current_size = 0; + return; + } + unsigned size = current_->get_size (); + if (!glyph.check_range (current_, size)) + { + current = nullptr; + current_size = 0; + return; + } + + current = current_; + current_size = size; + } + + private: + hb_bytes_t glyph; + __item_t__ current; + unsigned current_size; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + +#endif /* OT_GLYF_COMPOSITE_ITER_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/coord-setter.hh b/thirdparty/harfbuzz/src/OT/glyf/coord-setter.hh new file mode 100644 index 0000000000..df64ed5af7 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/coord-setter.hh @@ -0,0 +1,34 @@ +#ifndef OT_GLYF_COORD_SETTER_HH +#define OT_GLYF_COORD_SETTER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +struct coord_setter_t +{ + coord_setter_t (hb_array_t<int> coords) : + coords (coords) {} + + int& operator [] (unsigned idx) + { + if (coords.length < idx + 1) + coords.resize (idx + 1); + return coords[idx]; + } + + hb_array_t<int> get_coords () + { return coords.as_array (); } + + hb_vector_t<int> coords; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + +#endif /* OT_GLYF_COORD_SETTER_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/glyf.hh b/thirdparty/harfbuzz/src/OT/glyf/glyf.hh index 5fb32f67f3..e6e985c38c 100644 --- a/thirdparty/harfbuzz/src/OT/glyf/glyf.hh +++ b/thirdparty/harfbuzz/src/OT/glyf/glyf.hh @@ -42,11 +42,15 @@ struct glyf bool serialize (hb_serialize_context_t *c, Iterator it, bool use_short_loca, - const hb_subset_plan_t *plan) + const hb_subset_plan_t *plan, + hb_font_t *font) { TRACE_SERIALIZE (this); + unsigned init_len = c->length (); - for (const auto &_ : it) _.serialize (c, use_short_loca, plan); + for (auto &_ : it) + if (unlikely (!_.serialize (c, use_short_loca, plan, font))) + return false; /* As a special case when all glyph in the font are empty, add a zero byte * to the table, so that OTS doesn’t reject it, and to make the table work @@ -74,10 +78,11 @@ struct glyf hb_vector_t<glyf_impl::SubsetGlyph> glyphs; _populate_subset_glyphs (c->plan, glyphs); + hb_font_t *font = nullptr; if (!c->plan->pinned_at_default) { - if (!_compile_subset_glyphs_with_deltas (c->plan, &glyphs)) - return_trace (false); + font = _create_font_for_instancing (c->plan); + if (unlikely (!font)) return false; } auto padded_offsets = @@ -85,11 +90,14 @@ struct glyf | hb_map (&glyf_impl::SubsetGlyph::padded_size) ; - unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0); - bool use_short_loca = max_offset < 0x1FFFF; - + bool use_short_loca = false; + if (likely (!c->plan->force_long_loca)) + { + unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0); + use_short_loca = max_offset < 0x1FFFF; + } - glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan); + glyf_prime->serialize (c->serializer, glyphs.writer (), use_short_loca, c->plan, font); if (!use_short_loca) { padded_offsets = + hb_iter (glyphs) @@ -97,9 +105,12 @@ struct glyf ; } - - if (!c->plan->pinned_at_default) + if (font) + { _free_compiled_subset_glyphs (&glyphs); + hb_font_destroy (font); + } + if (unlikely (c->serializer->in_error ())) return_trace (false); return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan, padded_offsets, @@ -110,9 +121,8 @@ struct glyf _populate_subset_glyphs (const hb_subset_plan_t *plan, hb_vector_t<glyf_impl::SubsetGlyph> &glyphs /* OUT */) const; - bool - _compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan, - hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const; + hb_font_t * + _create_font_for_instancing (const hb_subset_plan_t *plan) const; void _free_compiled_subset_glyphs (hb_vector_t<glyf_impl::SubsetGlyph> *glyphs) const { @@ -394,7 +404,11 @@ glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan, plan->pinned_at_default) subset_glyph.source_glyph = glyf_impl::Glyph (); else - subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); + { + /* If plan has an accelerator, the preprocessing step already trimmed glyphs. + * Don't trim them again! */ + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, !plan->accelerator); + } if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) subset_glyph.drop_hints_bytes (); @@ -403,17 +417,15 @@ glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan, } } -inline bool -glyf::_compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan, - hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const +inline hb_font_t * +glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const { - OT::glyf_accelerator_t glyf (plan->source); hb_font_t *font = hb_font_create (plan->source); - if (unlikely (!font)) return false; + if (unlikely (font == hb_font_get_empty ())) return nullptr; hb_vector_t<hb_variation_t> vars; if (unlikely (!vars.alloc (plan->user_axes_location->get_population ()))) - return false; + return nullptr; for (auto _ : *plan->user_axes_location) { @@ -423,18 +435,10 @@ glyf::_compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan, vars.push (var); } +#ifndef HB_NO_VAR hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location->get_population ()); - for (auto& subset_glyph : *glyphs) - { - if (!const_cast<glyf_impl::SubsetGlyph &> (subset_glyph).compile_bytes_with_deltas (plan, font, glyf)) - { - hb_font_destroy (font); - return false; - } - } - - hb_font_destroy (font); - return true; +#endif + return font; } diff --git a/thirdparty/harfbuzz/src/graph/classdef-graph.hh b/thirdparty/harfbuzz/src/graph/classdef-graph.hh index 0bda76ac2f..c2e24a7067 100644 --- a/thirdparty/harfbuzz/src/graph/classdef-graph.hh +++ b/thirdparty/harfbuzz/src/graph/classdef-graph.hh @@ -112,7 +112,7 @@ struct ClassDef : public OT::ClassDef { case 1: return ((ClassDefFormat1*)this)->sanitize (vertex); case 2: return ((ClassDefFormat2*)this)->sanitize (vertex); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K // Not currently supported case 3: case 4: diff --git a/thirdparty/harfbuzz/src/graph/coverage-graph.hh b/thirdparty/harfbuzz/src/graph/coverage-graph.hh index 3c1022f090..49d0936315 100644 --- a/thirdparty/harfbuzz/src/graph/coverage-graph.hh +++ b/thirdparty/harfbuzz/src/graph/coverage-graph.hh @@ -136,7 +136,7 @@ struct Coverage : public OT::Layout::Common::Coverage { case 1: return ((CoverageFormat1*)this)->sanitize (vertex); case 2: return ((CoverageFormat2*)this)->sanitize (vertex); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K // Not currently supported case 3: case 4: diff --git a/thirdparty/harfbuzz/src/graph/graph.hh b/thirdparty/harfbuzz/src/graph/graph.hh index 79c7e690a1..dc5b6a36fe 100644 --- a/thirdparty/harfbuzz/src/graph/graph.hh +++ b/thirdparty/harfbuzz/src/graph/graph.hh @@ -49,6 +49,50 @@ struct graph_t unsigned end = 0; unsigned priority = 0; + + bool link_positions_valid (unsigned num_objects, bool removed_nil) + { + hb_set_t assigned_bytes; + for (const auto& l : obj.real_links) + { + if (l.objidx >= num_objects + || (removed_nil && !l.objidx)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Invalid object index."); + return false; + } + + unsigned start = l.position; + unsigned end = start + l.width - 1; + + if (unlikely (l.width < 2 || l.width > 4)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Invalid link width."); + return false; + } + + if (unlikely (end >= table_size ())) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Link position is out of bounds."); + return false; + } + + if (unlikely (assigned_bytes.intersects (start, end))) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Found offsets whose positions overlap."); + return false; + } + + assigned_bytes.add_range (start, end); + } + + return !assigned_bytes.in_error (); + } + void normalize () { obj.real_links.qsort (); @@ -132,7 +176,7 @@ struct graph_t for (unsigned i = 0; i < parents.length; i++) { if (parents[i] != parent_index) continue; - parents.remove (i); + parents.remove_unordered (i); break; } } @@ -148,7 +192,7 @@ struct graph_t if ((obj.head + link.position) != offset) continue; - obj.real_links.remove (i); + obj.real_links.remove_unordered (i); return; } } @@ -286,8 +330,6 @@ struct graph_t vertices_scratch_.alloc (objects.length); for (unsigned i = 0; i < objects.length; i++) { - // TODO(grieger): check all links point to valid objects. - // If this graph came from a serialization buffer object 0 is the // nil object. We don't need it for our purposes here so drop it. if (i == 0 && !objects[i]) @@ -299,6 +341,9 @@ struct graph_t vertex_t* v = vertices_.push (); if (check_success (!vertices_.in_error ())) v->obj = *objects[i]; + + check_success (v->link_positions_valid (objects.length, removed_nil)); + if (!removed_nil) continue; // Fix indices to account for removed nil object. for (auto& l : v->obj.all_links_writer ()) { @@ -418,6 +463,13 @@ struct graph_t hb_swap (sorted_graph[new_id], vertices_[next_id]); const vertex_t& next = sorted_graph[new_id]; + if (unlikely (!check_success(new_id >= 0))) { + // We are out of ids. Which means we've visited a node more than once. + // This graph contains a cycle which is not allowed. + DEBUG_MSG (SUBSET_REPACK, nullptr, "Invalid graph. Contains cycle."); + return; + } + id_map[next_id] = new_id--; for (const auto& link : next.obj.all_links ()) { @@ -580,7 +632,7 @@ struct graph_t while (roots) { - unsigned next = HB_SET_VALUE_INVALID; + uint32_t next = HB_SET_VALUE_INVALID; if (unlikely (!check_success (!roots.in_error ()))) break; if (!roots.next (&next)) break; @@ -661,8 +713,8 @@ struct graph_t auto new_subgraph = + subgraph.keys () - | hb_map([&] (unsigned node_idx) { - const unsigned *v; + | hb_map([&] (uint32_t node_idx) { + const uint32_t *v; if (index_map.has (node_idx, &v)) return *v; return node_idx; }) @@ -672,10 +724,10 @@ struct graph_t remap_obj_indices (index_map, parents.iter (), true); // Update roots set with new indices as needed. - unsigned next = HB_SET_VALUE_INVALID; + uint32_t next = HB_SET_VALUE_INVALID; while (roots.next (&next)) { - const unsigned *v; + const uint32_t *v; if (index_map.has (next, &v)) { roots.del (next); @@ -690,7 +742,7 @@ struct graph_t { for (const auto& link : vertices_[node_idx].obj.all_links ()) { - const unsigned *v; + const uint32_t *v; if (subgraph.has (link.objidx, &v)) { subgraph.set (link.objidx, *v + 1); @@ -941,6 +993,72 @@ struct graph_t return made_change; } + bool is_fully_connected () + { + update_parents(); + + if (root().parents) + // Root cannot have parents. + return false; + + for (unsigned i = 0; i < root_idx (); i++) + { + if (!vertices_[i].parents) + return false; + } + return true; + } + +#if 0 + /* + * Saves the current graph to a packed binary format which the repacker fuzzer takes + * as a seed. + */ + void save_fuzzer_seed (hb_tag_t tag) const + { + FILE* f = fopen ("./repacker_fuzzer_seed", "w"); + fwrite ((void*) &tag, sizeof (tag), 1, f); + + uint16_t num_objects = vertices_.length; + fwrite ((void*) &num_objects, sizeof (num_objects), 1, f); + + for (const auto& v : vertices_) + { + uint16_t blob_size = v.table_size (); + fwrite ((void*) &blob_size, sizeof (blob_size), 1, f); + fwrite ((const void*) v.obj.head, blob_size, 1, f); + } + + uint16_t link_count = 0; + for (const auto& v : vertices_) + link_count += v.obj.real_links.length; + + fwrite ((void*) &link_count, sizeof (link_count), 1, f); + + typedef struct + { + uint16_t parent; + uint16_t child; + uint16_t position; + uint8_t width; + } link_t; + + for (unsigned i = 0; i < vertices_.length; i++) + { + for (const auto& l : vertices_[i].obj.real_links) + { + link_t link { + (uint16_t) i, (uint16_t) l.objidx, + (uint16_t) l.position, (uint8_t) l.width + }; + fwrite ((void*) &link, sizeof (link), 1, f); + } + } + + fclose (f); + } +#endif + void print_orphaned_nodes () { if (!DEBUG_ENABLED(SUBSET_REPACK)) return; @@ -949,6 +1067,10 @@ struct graph_t parents_invalid = true; update_parents(); + if (root().parents) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Root node has incoming edges."); + } + for (unsigned i = 0; i < root_idx (); i++) { const auto& v = vertices_[i]; @@ -1065,6 +1187,11 @@ struct graph_t } } + for (unsigned i = 0; i < vertices_.length; i++) + // parents arrays must be accurate or downstream operations like cycle detection + // and sorting won't work correctly. + check_success (!vertices_[i].parents.in_error ()); + parents_invalid = false; } @@ -1183,7 +1310,7 @@ struct graph_t { for (auto& link : vertices_[i].obj.all_links_writer ()) { - const unsigned *v; + const uint32_t *v; if (!id_map.has (link.objidx, &v)) continue; if (only_wide && !(link.width == 4 && !link.is_signed)) continue; diff --git a/thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh b/thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh index e8d5bef9a8..c170638409 100644 --- a/thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh +++ b/thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh @@ -201,7 +201,7 @@ struct Lookup : public OT::Lookup + new_subtable_count * OT::Offset16::static_size; char* buffer = (char*) hb_calloc (1, new_size); c.add_buffer (buffer); - memcpy (buffer, v.obj.head, v.table_size()); + hb_memcpy (buffer, v.obj.head, v.table_size()); v.obj.head = buffer; v.obj.tail = buffer + new_size; @@ -355,7 +355,7 @@ struct GSTAR : public OT::GSUBGPOS { switch (u.version.major) { case 1: return u.version1.get_lookup_list_offset (); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 2: return u.version2.get_lookup_list_offset (); #endif default: return 0; @@ -374,7 +374,7 @@ struct GSTAR : public OT::GSUBGPOS { switch (u.version.major) { case 1: find_lookups<SmallTypes> (graph, lookups); break; -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 2: find_lookups<MediumTypes> (graph, lookups); break; #endif } diff --git a/thirdparty/harfbuzz/src/graph/markbasepos-graph.hh b/thirdparty/harfbuzz/src/graph/markbasepos-graph.hh index e42a6042cc..84ef5f71b9 100644 --- a/thirdparty/harfbuzz/src/graph/markbasepos-graph.hh +++ b/thirdparty/harfbuzz/src/graph/markbasepos-graph.hh @@ -112,7 +112,7 @@ struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix auto& child = c.graph.vertices_[child_idx]; child.remove_parent (this_index); - o.real_links.remove (i); + o.real_links.remove_unordered (i); num_links--; i--; } @@ -372,7 +372,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S if (!mark_coverage) return false; hb_set_t marks = sc.marks_for (0, count); auto new_coverage = - + hb_zip (hb_range (), mark_coverage.table->iter ()) + + hb_enumerate (mark_coverage.table->iter ()) | hb_filter (marks, hb_first) | hb_map_retains_sorting (hb_second) ; @@ -431,7 +431,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S if (!mark_coverage) return false; hb_set_t marks = sc.marks_for (start, end); auto new_coverage = - + hb_zip (hb_range (), mark_coverage.table->iter ()) + + hb_enumerate (mark_coverage.table->iter ()) | hb_filter (marks, hb_first) | hb_map_retains_sorting (hb_second) ; @@ -477,7 +477,7 @@ struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos switch (u.format) { case 1: return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 2: HB_FALLTHROUGH; // Don't split 24bit PairPos's. #endif @@ -494,7 +494,7 @@ struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos switch (u.format) { case 1: return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 2: HB_FALLTHROUGH; #endif default: diff --git a/thirdparty/harfbuzz/src/graph/pairpos-graph.hh b/thirdparty/harfbuzz/src/graph/pairpos-graph.hh index 8040778ea3..1c13eb24f9 100644 --- a/thirdparty/harfbuzz/src/graph/pairpos-graph.hh +++ b/thirdparty/harfbuzz/src/graph/pairpos-graph.hh @@ -434,7 +434,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType char* start_addr = ((char*)&values[0]) + start * split_context.class1_record_size; unsigned num_records = end - start; - memcpy (&pair_pos_prime->values[0], + hb_memcpy (&pair_pos_prime->values[0], start_addr, num_records * split_context.class1_record_size); @@ -611,7 +611,7 @@ struct PairPos : public OT::Layout::GPOS_impl::PairPos return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); case 2: return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: HB_FALLTHROUGH; case 4: HB_FALLTHROUGH; // Don't split 24bit PairPos's. @@ -631,7 +631,7 @@ struct PairPos : public OT::Layout::GPOS_impl::PairPos return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); case 2: return ((PairPosFormat2*)(&u.format2))->sanitize (vertex); -#ifndef HB_NO_BORING_EXPANSION +#ifndef HB_NO_BEYOND_64K case 3: HB_FALLTHROUGH; case 4: HB_FALLTHROUGH; #endif diff --git a/thirdparty/harfbuzz/src/graph/serialize.hh b/thirdparty/harfbuzz/src/graph/serialize.hh index ecc6cc5aea..d03a61bd19 100644 --- a/thirdparty/harfbuzz/src/graph/serialize.hh +++ b/thirdparty/harfbuzz/src/graph/serialize.hh @@ -33,6 +33,23 @@ struct overflow_record_t { unsigned parent; unsigned child; + + bool operator != (const overflow_record_t o) const + { return !(*this == o); } + + inline bool operator == (const overflow_record_t& o) const + { + return parent == o.parent && + child == o.child; + } + + inline uint32_t hash () const + { + uint32_t current = 0; + current = current * 31 + hb_hash (parent); + current = current * 31 + hb_hash (child); + return current; + } }; inline @@ -94,6 +111,7 @@ will_overflow (graph_t& graph, if (overflows) overflows->resize (0); graph.update_positions (); + hb_hashmap_t<overflow_record_t*, bool> record_set; const auto& vertices = graph.vertices_; for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--) { @@ -109,7 +127,10 @@ will_overflow (graph_t& graph, overflow_record_t r; r.parent = parent_idx; r.child = link.objidx; + if (record_set.has(&r)) continue; // don't keep duplicate overflows. + overflows->push (r); + record_set.set(&r, true); } } @@ -223,7 +244,7 @@ inline hb_blob_t* serialize (const graph_t& graph) return nullptr; } - memcpy (start, vertices[i].obj.head, size); + hb_memcpy (start, vertices[i].obj.head, size); // Only real links needs to be serialized. for (const auto& link : vertices[i].obj.real_links) diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh index aa4ad4cf3c..8b9190d0be 100644 --- a/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh +++ b/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh @@ -131,14 +131,14 @@ struct RearrangementSubtable hb_glyph_info_t *info = buffer->info; hb_glyph_info_t buf[4]; - memcpy (buf, info + start, l * sizeof (buf[0])); - memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); + hb_memcpy (buf, info + start, l * sizeof (buf[0])); + hb_memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); if (l != r) memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); - memcpy (info + start, buf + 2, r * sizeof (buf[0])); - memcpy (info + end - l, buf, l * sizeof (buf[0])); + hb_memcpy (info + start, buf + 2, r * sizeof (buf[0])); + hb_memcpy (info + end - l, buf, l * sizeof (buf[0])); if (reverse_l) { buf[0] = info[end - 1]; diff --git a/thirdparty/harfbuzz/src/hb-aat-layout.cc b/thirdparty/harfbuzz/src/hb-aat-layout.cc index d60126fe19..78427b0d5a 100644 --- a/thirdparty/harfbuzz/src/hb-aat-layout.cc +++ b/thirdparty/harfbuzz/src/hb-aat-layout.cc @@ -289,7 +289,7 @@ is_deleted_glyph (const hb_glyph_info_t *info) void hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer) { - hb_ot_layout_delete_glyphs_inplace (buffer, is_deleted_glyph); + buffer->delete_glyphs_inplace (is_deleted_glyph); } /** diff --git a/thirdparty/harfbuzz/src/hb-aat-map.hh b/thirdparty/harfbuzz/src/hb-aat-map.hh index c914f58d70..d0ee7d672c 100644 --- a/thirdparty/harfbuzz/src/hb-aat-map.hh +++ b/thirdparty/harfbuzz/src/hb-aat-map.hh @@ -38,7 +38,7 @@ struct hb_aat_map_t void init () { - memset (this, 0, sizeof (*this)); + hb_memset (this, 0, sizeof (*this)); chain_flags.init (); } void fini () { chain_flags.fini (); } diff --git a/thirdparty/harfbuzz/src/hb-algs.hh b/thirdparty/harfbuzz/src/hb-algs.hh index cc37c073da..d85a4afe10 100644 --- a/thirdparty/harfbuzz/src/hb-algs.hh +++ b/thirdparty/harfbuzz/src/hb-algs.hh @@ -236,17 +236,6 @@ struct template <typename T> constexpr auto impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ()) - template <typename T> constexpr uint32_t - impl (const hb::shared_ptr<T>& v, hb_priority<1>) const - { - return v.get () ? v.get ()->hash () : 0; - } - template <typename T> constexpr uint32_t - impl (const hb::unique_ptr<T>& v, hb_priority<1>) const - { - return v.get () ? v.get ()->hash () : 0; - } - template <typename T> constexpr auto impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v))>>{} (hb_deref (v))) @@ -495,6 +484,17 @@ struct } HB_FUNCOBJ (hb_equal); +struct +{ + template <typename T> void + operator () (T& a, T& b) const + { + using std::swap; // allow ADL + swap (a, b); + } +} +HB_FUNCOBJ (hb_swap); + template <typename T1, typename T2> struct hb_pair_t @@ -507,7 +507,7 @@ struct hb_pair_t hb_enable_if (std::is_default_constructible<U1>::value && std::is_default_constructible<U2>::value)> hb_pair_t () : first (), second () {} - hb_pair_t (T1 a, T2 b) : first (a), second (b) {} + hb_pair_t (T1 a, T2 b) : first (std::forward<T1> (a)), second (std::forward<T2> (b)) {} template <typename Q1, typename Q2, hb_enable_if (hb_is_convertible (T1, Q1) && @@ -524,6 +524,25 @@ struct hb_pair_t bool operator > (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); } bool operator <= (const pair_t& o) const { return !(*this > o); } + static int cmp (const void *pa, const void *pb) + { + pair_t *a = (pair_t *) pa; + pair_t *b = (pair_t *) pb; + + if (a->first < b->first) return -1; + if (a->first > b->first) return +1; + if (a->second < b->second) return -1; + if (a->second > b->second) return +1; + return 0; + } + + friend void swap (hb_pair_t& a, hb_pair_t& b) + { + hb_swap (a.first, b.first); + hb_swap (a.second, b.second); + } + + T1 first; T2 second; }; @@ -570,17 +589,6 @@ struct } HB_FUNCOBJ (hb_clamp); -struct -{ - template <typename T> void - operator () (T& a, T& b) const - { - using std::swap; // allow ADL - swap (a, b); - } -} -HB_FUNCOBJ (hb_swap); - /* * Bithacks. */ @@ -849,19 +857,14 @@ hb_in_range (T u, T lo, T hi) return (T)(u - lo) <= (T)(hi - lo); } template <typename T> static inline bool -hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2) +hb_in_ranges (T u, T lo1, T hi1) { - return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2); + return hb_in_range (u, lo1, hi1); } -template <typename T> static inline bool -hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3) +template <typename T, typename ...Ts> static inline bool +hb_in_ranges (T u, T lo1, T hi1, Ts... ds) { - return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3); -} -template <typename T> static inline bool -hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3, T lo4, T hi4) -{ - return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3) || hb_in_range (u, lo4, hi4); + return hb_in_range<T> (u, lo1, hi1) || hb_in_ranges<T> (u, ds...); } @@ -869,10 +872,18 @@ hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3, T lo4, T hi4) * Overflow checking. */ -/* Consider __builtin_mul_overflow use here also */ static inline bool -hb_unsigned_mul_overflows (unsigned int count, unsigned int size) +hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *result = nullptr) { +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) + unsigned stack_result; + if (!result) + result = &stack_result; + return __builtin_mul_overflow (count, size, result); +#endif + + if (result) + *result = count * size; return (size > 0) && (count >= ((unsigned int) -1) / size); } @@ -1164,9 +1175,12 @@ hb_qsort (void *base, size_t nel, size_t width, } -template <typename T, typename T2, typename T3> static inline void -hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2) +template <typename T, typename T2, typename T3 = int> static inline void +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2 = nullptr) { + static_assert (hb_is_trivially_copy_assignable (T), ""); + static_assert (hb_is_trivially_copy_assignable (T3), ""); + for (unsigned int i = 1; i < len; i++) { unsigned int j = i; @@ -1189,12 +1203,6 @@ hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *) } } -template <typename T> static inline void -hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) -{ - hb_stable_sort (array, len, compar, (int *) nullptr); -} - static inline hb_bool_t hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out) { @@ -1322,47 +1330,4 @@ struct HB_FUNCOBJ (hb_dec); -/* Compiler-assisted vectorization. */ - -/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))), - * basically a fixed-size bitset. */ -template <typename elt_t, unsigned int byte_size> -struct hb_vector_size_t -{ - elt_t& operator [] (unsigned int i) { return v[i]; } - const elt_t& operator [] (unsigned int i) const { return v[i]; } - - void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); } - - template <typename Op> - hb_vector_size_t process (const Op& op) const - { - hb_vector_size_t r; - for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) - r.v[i] = op (v[i]); - return r; - } - template <typename Op> - hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const - { - hb_vector_size_t r; - for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) - r.v[i] = op (v[i], o.v[i]); - return r; - } - hb_vector_size_t operator | (const hb_vector_size_t &o) const - { return process (hb_bitwise_or, o); } - hb_vector_size_t operator & (const hb_vector_size_t &o) const - { return process (hb_bitwise_and, o); } - hb_vector_size_t operator ^ (const hb_vector_size_t &o) const - { return process (hb_bitwise_xor, o); } - hb_vector_size_t operator ~ () const - { return process (hb_bitwise_neg); } - - private: - static_assert (0 == byte_size % sizeof (elt_t), ""); - elt_t v[byte_size / sizeof (elt_t)]; -}; - - #endif /* HB_ALGS_HH */ diff --git a/thirdparty/harfbuzz/src/hb-array.hh b/thirdparty/harfbuzz/src/hb-array.hh index 5884007c19..17562bc336 100644 --- a/thirdparty/harfbuzz/src/hb-array.hh +++ b/thirdparty/harfbuzz/src/hb-array.hh @@ -100,10 +100,18 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&> /* Ouch. The operator== compares the contents of the array. For range-based for loops, * it's best if we can just compare arrayZ, though comparing contents is still fast, * but also would require that Type has operator==. As such, we optimize this operator - * for range-based for loop and just compare arrayZ and length. */ + * for range-based for loop and just compare arrayZ and length. + * + * The above comment is outdated now because we implemented separate begin/end to + * objects that were using hb_array_t for range-based loop before. */ bool operator != (const hb_array_t& o) const { return this->arrayZ != o.arrayZ || this->length != o.length; } + /* Faster range-based for loop without bounds-check. */ + Type *begin () const { return arrayZ; } + Type *end () const { return arrayZ + length; } + + /* Extra operators. */ Type * operator & () const { return arrayZ; } @@ -112,11 +120,11 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&> HB_INTERNAL bool operator == (const hb_array_t &o) const; - uint32_t hash () const { + uint32_t hash () const + { uint32_t current = 0; - for (unsigned int i = 0; i < this->length; i++) { - current = current * 31 + hb_hash (this->arrayZ[i]); - } + for (auto &v : *this) + current = current * 31 + hb_hash (v); return current; } @@ -184,23 +192,18 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&> hb_sorted_array_t<Type> qsort (int (*cmp_)(const void*, const void*)) { + //static_assert (hb_enable_if (hb_is_trivially_copy_assignable(Type)), ""); if (likely (length)) hb_qsort (arrayZ, length, this->get_item_size (), cmp_); return hb_sorted_array_t<Type> (*this); } hb_sorted_array_t<Type> qsort () { + //static_assert (hb_enable_if (hb_is_trivially_copy_assignable(Type)), ""); if (likely (length)) hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp); return hb_sorted_array_t<Type> (*this); } - void qsort (unsigned int start, unsigned int end) - { - end = hb_min (end, length); - assert (start <= end); - if (likely (start < end)) - hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp); - } /* * Other methods. @@ -262,17 +265,31 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&> void fini () { hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; } - template <typename hb_serialize_context_t> + template <typename hb_serialize_context_t, + typename U = Type, + hb_enable_if (!(sizeof (U) < sizeof (long long) && hb_is_trivially_copy_assignable(hb_decay<Type>)))> hb_array_t copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); auto* out = c->start_embed (arrayZ); - if (unlikely (!c->extend_size (out, get_size ()))) return_trace (hb_array_t ()); + if (unlikely (!c->extend_size (out, get_size (), false))) return_trace (hb_array_t ()); for (unsigned i = 0; i < length; i++) out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */ return_trace (hb_array_t (out, length)); } + template <typename hb_serialize_context_t, + typename U = Type, + hb_enable_if (sizeof (U) < sizeof (long long) && hb_is_trivially_copy_assignable(hb_decay<Type>))> + hb_array_t copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto* out = c->start_embed (arrayZ); + if (unlikely (!c->extend_size (out, get_size (), false))) return_trace (hb_array_t ()); + hb_memcpy (out, arrayZ, get_size ()); + return_trace (hb_array_t (out, length)); + } + template <typename hb_sanitize_context_t> bool sanitize (hb_sanitize_context_t *c) const { return c->check_array (arrayZ, length); } @@ -295,8 +312,8 @@ hb_array (T (&array_)[length_]) template <typename Type> struct hb_sorted_array_t : - hb_iter_t<hb_sorted_array_t<Type>, Type&>, - hb_array_t<Type> + hb_array_t<Type>, + hb_iter_t<hb_sorted_array_t<Type>, Type&> { typedef hb_iter_t<hb_sorted_array_t, Type&> iter_base_t; HB_ITER_USING (iter_base_t); @@ -316,8 +333,8 @@ struct hb_sorted_array_t : template <typename U, hb_enable_if (hb_is_cr_convertible(U, Type))> constexpr hb_sorted_array_t (const hb_array_t<U> &o) : - hb_iter_t<hb_sorted_array_t, Type&> (), - hb_array_t<Type> (o) {} + hb_array_t<Type> (o), + hb_iter_t<hb_sorted_array_t, Type&> () {} template <typename U, hb_enable_if (hb_is_cr_convertible(U, Type))> hb_sorted_array_t& operator = (const hb_array_t<U> &o) @@ -329,6 +346,11 @@ struct hb_sorted_array_t : bool operator != (const hb_sorted_array_t& o) const { return this->arrayZ != o.arrayZ || this->length != o.length; } + /* Faster range-based for loop without bounds-check. */ + Type *begin () const { return this->arrayZ; } + Type *end () const { return this->arrayZ + this->length; } + + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const { return hb_sorted_array_t (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); } hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const @@ -421,18 +443,42 @@ inline bool hb_array_t<const unsigned char>::operator == (const hb_array_t<const return 0 == hb_memcmp (arrayZ, o.arrayZ, length); } + +/* Specialize hash() for byte arrays. */ + template <> -inline uint32_t hb_array_t<const char>::hash () const { +inline uint32_t hb_array_t<const char>::hash () const +{ uint32_t current = 0; - for (unsigned int i = 0; i < this->length; i++) - current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u); + unsigned i = 0; + +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) + struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + for (; i + 4 <= this->length; i += 4) + current = current * 31 + hb_hash ((uint32_t) ((packed_uint32_t *) &this->arrayZ[i])->v); +#endif + + for (; i < this->length; i++) + current = current * 31 + hb_hash (this->arrayZ[i]); return current; } + template <> -inline uint32_t hb_array_t<const unsigned char>::hash () const { +inline uint32_t hb_array_t<const unsigned char>::hash () const +{ uint32_t current = 0; - for (unsigned int i = 0; i < this->length; i++) - current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u); + unsigned i = 0; + +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) + struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + for (; i + 4 <= this->length; i += 4) + current = current * 31 + hb_hash ((uint32_t) ((packed_uint32_t *) &this->arrayZ[i])->v); +#endif + + for (; i < this->length; i++) + current = current * 31 + hb_hash (this->arrayZ[i]); return current; } diff --git a/thirdparty/harfbuzz/src/hb-bit-page.hh b/thirdparty/harfbuzz/src/hb-bit-page.hh index 95ae1b7bf9..11987054f8 100644 --- a/thirdparty/harfbuzz/src/hb-bit-page.hh +++ b/thirdparty/harfbuzz/src/hb-bit-page.hh @@ -30,6 +30,53 @@ #include "hb.hh" + +/* Compiler-assisted vectorization. */ + +/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))), + * basically a fixed-size bitset. */ +template <typename elt_t, unsigned int byte_size> +struct hb_vector_size_t +{ + elt_t& operator [] (unsigned int i) { return v[i]; } + const elt_t& operator [] (unsigned int i) const { return v[i]; } + + void clear (unsigned char v = 0) { hb_memset (this, v, sizeof (*this)); } + + template <typename Op> + hb_vector_size_t process (const Op& op) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i]); + return r; + } + template <typename Op> + hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i], o.v[i]); + return r; + } + hb_vector_size_t operator | (const hb_vector_size_t &o) const + { return process (hb_bitwise_or, o); } + hb_vector_size_t operator & (const hb_vector_size_t &o) const + { return process (hb_bitwise_and, o); } + hb_vector_size_t operator ^ (const hb_vector_size_t &o) const + { return process (hb_bitwise_xor, o); } + hb_vector_size_t operator ~ () const + { return process (hb_bitwise_neg); } + + hb_array_t<const elt_t> iter () const + { return hb_array (v); } + + private: + static_assert (0 == byte_size % sizeof (elt_t), ""); + elt_t v[byte_size / sizeof (elt_t)]; +}; + + struct hb_bit_page_t { void init0 () { v.clear (); } @@ -40,17 +87,17 @@ struct hb_bit_page_t bool is_empty () const { - for (unsigned i = 0; i < len (); i++) - if (v[i]) - return false; - return true; + return + + hb_iter (v) + | hb_none + ; } uint32_t hash () const { - uint32_t h = 0; - for (unsigned i = 0; i < len (); i++) - h = h * 31 + hb_hash (v[i]); - return h; + return + + hb_iter (v) + | hb_reduce ([] (uint32_t h, const elt_t &_) { return h * 31 + hb_hash (_); }, (uint32_t) 0u) + ; } void add (hb_codepoint_t g) { elt (g) |= mask (g); } @@ -69,7 +116,7 @@ struct hb_bit_page_t *la |= ~(mask (a) - 1); la++; - memset (la, 0xff, (char *) lb - (char *) la); + hb_memset (la, 0xff, (char *) lb - (char *) la); *lb |= ((mask (b) << 1) - 1); } @@ -85,7 +132,7 @@ struct hb_bit_page_t *la &= mask (a) - 1; la++; - memset (la, 0, (char *) lb - (char *) la); + hb_memset (la, 0, (char *) lb - (char *) la); *lb &= ~((mask (b) << 1) - 1); } @@ -101,13 +148,13 @@ struct hb_bit_page_t hb_codepoint_t *p, unsigned int size) const { - unsigned int start_v = start_value >> ELT_BITS_LOG_2; + unsigned int start_v = start_value / ELT_BITS; unsigned int start_bit = start_value & ELT_MASK; unsigned int count = 0; for (unsigned i = start_v; i < len () && count < size; i++) { elt_t bits = v[i]; - uint32_t v_base = base | (i << ELT_BITS_LOG_2); + uint32_t v_base = base | (i * ELT_BITS); for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++) { if ((elt_t(1) << j) & bits) { @@ -132,13 +179,13 @@ struct hb_bit_page_t unsigned int size, hb_codepoint_t *next_value) const { - unsigned int start_v = start_value >> ELT_BITS_LOG_2; + unsigned int start_v = start_value / ELT_BITS; unsigned int start_bit = start_value & ELT_MASK; unsigned int count = 0; for (unsigned i = start_v; i < len () && count < size; i++) { elt_t bits = v[i]; - uint32_t v_offset = i << ELT_BITS_LOG_2; + uint32_t v_offset = i * ELT_BITS; for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++) { if ((elt_t(1) << j) & bits) @@ -161,7 +208,10 @@ struct hb_bit_page_t bool is_equal (const hb_bit_page_t &other) const { - return 0 == hb_memcmp (&v, &other.v, sizeof (v)); + for (unsigned i = 0; i < len (); i++) + if (v[i] != other.v[i]) + return false; + return true; } bool is_subset (const hb_bit_page_t &larger_page) const { @@ -173,10 +223,10 @@ struct hb_bit_page_t unsigned int get_population () const { - unsigned int pop = 0; - for (unsigned int i = 0; i < len (); i++) - pop += hb_popcount (v[i]); - return pop; + return + + hb_iter (v) + | hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u) + ; } bool next (hb_codepoint_t *codepoint) const @@ -262,8 +312,6 @@ struct hb_bit_page_t typedef hb_vector_size_t<elt_t, PAGE_BITS / 8> vector_t; static constexpr unsigned ELT_BITS = sizeof (elt_t) * 8; - static constexpr unsigned ELT_BITS_LOG_2 = 6; - static_assert (1 << ELT_BITS_LOG_2 == ELT_BITS, ""); static constexpr unsigned ELT_MASK = ELT_BITS - 1; static constexpr unsigned BITS = sizeof (vector_t) * 8; diff --git a/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh b/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh index 27fb0732ea..ff8aecc60c 100644 --- a/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh +++ b/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh @@ -123,10 +123,8 @@ struct hb_bit_set_invertible_t bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; } /* Has interface. */ - static constexpr bool SENTINEL = false; - typedef bool value_t; - value_t operator [] (hb_codepoint_t k) const { return get (k); } - bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + bool operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } /* Predicate. */ bool operator () (hb_codepoint_t k) const { return has (k); } diff --git a/thirdparty/harfbuzz/src/hb-bit-set.hh b/thirdparty/harfbuzz/src/hb-bit-set.hh index a63887efda..8de6e037fb 100644 --- a/thirdparty/harfbuzz/src/hb-bit-set.hh +++ b/thirdparty/harfbuzz/src/hb-bit-set.hh @@ -85,10 +85,10 @@ struct hb_bit_set_t void err () { if (successful) successful = false; } /* TODO Remove */ bool in_error () const { return !successful; } - bool resize (unsigned int count) + bool resize (unsigned int count, bool clear = true) { if (unlikely (!successful)) return false; - if (unlikely (!pages.resize (count) || !page_map.resize (count))) + if (unlikely (!pages.resize (count, clear) || !page_map.resize (count, clear))) { pages.resize (page_map.length); successful = false; @@ -330,10 +330,8 @@ struct hb_bit_set_t } /* Has interface. */ - static constexpr bool SENTINEL = false; - typedef bool value_t; - value_t operator [] (hb_codepoint_t k) const { return get (k); } - bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + bool operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } /* Predicate. */ bool operator () (hb_codepoint_t k) const { return has (k); } @@ -352,7 +350,7 @@ struct hb_bit_set_t { if (unlikely (!successful)) return; unsigned int count = other.pages.length; - if (unlikely (!resize (count))) + if (unlikely (!resize (count, false))) return; population = other.population; @@ -391,7 +389,7 @@ struct hb_bit_set_t bool is_subset (const hb_bit_set_t &larger_set) const { if (has_population () && larger_set.has_population () && - population != larger_set.population) + population > larger_set.population) return false; uint32_t spi = 0; @@ -540,21 +538,21 @@ struct hb_bit_set_t b = nb; for (; a && b; ) { - if (page_map[a - 1].major == other.page_map[b - 1].major) + if (page_map.arrayZ[a - 1].major == other.page_map.arrayZ[b - 1].major) { a--; b--; count--; - page_map[count] = page_map[a]; + page_map.arrayZ[count] = page_map.arrayZ[a]; page_at (count).v = op (page_at (a).v, other.page_at (b).v); } - else if (page_map[a - 1].major > other.page_map[b - 1].major) + else if (page_map.arrayZ[a - 1].major > other.page_map.arrayZ[b - 1].major) { a--; if (passthru_left) { count--; - page_map[count] = page_map[a]; + page_map.arrayZ[count] = page_map.arrayZ[a]; } } else @@ -563,8 +561,8 @@ struct hb_bit_set_t if (passthru_right) { count--; - page_map[count].major = other.page_map[b].major; - page_map[count].index = next_page++; + page_map.arrayZ[count].major = other.page_map.arrayZ[b].major; + page_map.arrayZ[count].index = next_page++; page_at (count).v = other.page_at (b).v; } } @@ -574,15 +572,15 @@ struct hb_bit_set_t { a--; count--; - page_map[count] = page_map [a]; + page_map.arrayZ[count] = page_map.arrayZ[a]; } if (passthru_right) while (b) { b--; count--; - page_map[count].major = other.page_map[b].major; - page_map[count].index = next_page++; + page_map.arrayZ[count].major = other.page_map.arrayZ[b].major; + page_map.arrayZ[count].index = next_page++; page_at (count).v = other.page_at (b).v; } assert (!count); @@ -605,8 +603,6 @@ struct hb_bit_set_t bool next (hb_codepoint_t *codepoint) const { - // TODO: this should be merged with prev() as both implementations - // are very similar. if (unlikely (*codepoint == INVALID)) { *codepoint = get_min (); return *codepoint != INVALID; @@ -640,7 +636,7 @@ struct hb_bit_set_t for (; i < page_map.length; i++) { - const page_map_t ¤t = page_map.arrayZ[i]; + const page_map_t ¤t = page_map_array[i]; hb_codepoint_t m = pages_array[current.index].get_min (); if (m != INVALID) { @@ -663,21 +659,21 @@ struct hb_bit_set_t page_map_t map = {get_major (*codepoint), 0}; unsigned int i; page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST); - if (i < page_map.length && page_map[i].major == map.major) + if (i < page_map.length && page_map.arrayZ[i].major == map.major) { - if (pages[page_map[i].index].previous (codepoint)) + if (pages[page_map.arrayZ[i].index].previous (codepoint)) { - *codepoint += page_map[i].major * page_t::PAGE_BITS; + *codepoint += page_map.arrayZ[i].major * page_t::PAGE_BITS; return true; } } i--; for (; (int) i >= 0; i--) { - hb_codepoint_t m = pages[page_map[i].index].get_max (); + hb_codepoint_t m = pages.arrayZ[page_map.arrayZ[i].index].get_max (); if (m != INVALID) { - *codepoint = page_map[i].major * page_t::PAGE_BITS + m; + *codepoint = page_map.arrayZ[i].major * page_t::PAGE_BITS + m; return true; } } @@ -905,7 +901,7 @@ struct hb_bit_set_t { auto &cached_page = page_map.arrayZ[i]; if (cached_page.major == major) - return &pages[cached_page.index]; + return &pages.arrayZ[cached_page.index]; } page_map_t map = {major, pages.length}; @@ -917,15 +913,15 @@ struct hb_bit_set_t if (unlikely (!resize (pages.length + 1))) return nullptr; - pages[map.index].init0 (); - memmove (page_map + i + 1, - page_map + i, + pages.arrayZ[map.index].init0 (); + memmove (page_map.arrayZ + i + 1, + page_map.arrayZ + i, (page_map.length - 1 - i) * page_map.item_size); page_map[i] = map; } last_page_lookup = i; - return &pages[page_map[i].index]; + return &pages.arrayZ[page_map.arrayZ[i].index]; } const page_t *page_for (hb_codepoint_t g) const { @@ -939,7 +935,7 @@ struct hb_bit_set_t { auto &cached_page = page_map.arrayZ[i]; if (cached_page.major == major) - return &pages[cached_page.index]; + return &pages.arrayZ[cached_page.index]; } page_map_t key = {major}; @@ -947,10 +943,18 @@ struct hb_bit_set_t return nullptr; last_page_lookup = i; - return &pages[page_map[i].index]; + return &pages.arrayZ[page_map[i].index]; + } + page_t &page_at (unsigned int i) + { + assert (i < page_map.length); + return pages.arrayZ[page_map.arrayZ[i].index]; + } + const page_t &page_at (unsigned int i) const + { + assert (i < page_map.length); + return pages.arrayZ[page_map.arrayZ[i].index]; } - page_t &page_at (unsigned int i) { return pages[page_map[i].index]; } - const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; } unsigned int get_major (hb_codepoint_t g) const { return g >> page_t::PAGE_BITS_LOG_2; } unsigned int page_remainder (hb_codepoint_t g) const { return g & page_t::PAGE_BITMASK; } hb_codepoint_t major_start (unsigned int major) const { return major << page_t::PAGE_BITS_LOG_2; } diff --git a/thirdparty/harfbuzz/src/hb-blob.cc b/thirdparty/harfbuzz/src/hb-blob.cc index 9bc12ea3fa..f0fda1fa4d 100644 --- a/thirdparty/harfbuzz/src/hb-blob.cc +++ b/thirdparty/harfbuzz/src/hb-blob.cc @@ -495,7 +495,7 @@ hb_blob_t::try_make_writable () DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data); - memcpy (new_data, this->data, this->length); + hb_memcpy (new_data, this->data, this->length); this->destroy_user_data (); this->mode = HB_MEMORY_MODE_WRITABLE; this->data = new_data; diff --git a/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh b/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh index 87095855d6..993bb1f698 100644 --- a/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh +++ b/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh @@ -32,7 +32,7 @@ #include "hb.hh" -#line 36 "hb-buffer-deserialize-json.hh" +#line 33 "hb-buffer-deserialize-json.hh" static const unsigned char _deserialize_json_trans_keys[] = { 0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, @@ -557,12 +557,12 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer, hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 561 "hb-buffer-deserialize-json.hh" +#line 554 "hb-buffer-deserialize-json.hh" { cs = deserialize_json_start; } -#line 566 "hb-buffer-deserialize-json.hh" +#line 557 "hb-buffer-deserialize-json.hh" { int _slen; int _trans; @@ -590,8 +590,8 @@ _resume: case 1: #line 38 "hb-buffer-deserialize-json.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } break; case 5: @@ -774,7 +774,7 @@ _resume: *end_ptr = p; } break; -#line 778 "hb-buffer-deserialize-json.hh" +#line 735 "hb-buffer-deserialize-json.hh" } _again: diff --git a/thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh b/thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh index 9062610de2..6b9b4282fc 100644 --- a/thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh +++ b/thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh @@ -32,32 +32,30 @@ #include "hb.hh" -#line 36 "hb-buffer-deserialize-text.hh" +#line 33 "hb-buffer-deserialize-text.hh" static const unsigned char _deserialize_text_trans_keys[] = { - 0u, 0u, 9u, 91u, 85u, 85u, 43u, 43u, 48u, 102u, 9u, 85u, 48u, 57u, 45u, 57u, - 48u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, - 44u, 57u, 43u, 124u, 45u, 57u, 48u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, 9u, 85u, + 0u, 0u, 9u, 91u, 85u, 85u, 43u, 43u, 48u, 102u, 9u, 85u, 48u, 57u, 48u, 57u, + 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 44u, 44u, + 45u, 57u, 48u, 57u, 44u, 57u, 43u, 124u, 9u, 124u, 9u, 124u, 0u, 0u, 9u, 85u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, - 9u, 124u, 9u, 124u, 9u, 124u, 0 + 0 }; static const char _deserialize_text_key_spans[] = { - 0, 83, 1, 1, 55, 77, 10, 13, - 10, 10, 10, 13, 10, 1, 13, 10, - 14, 82, 13, 10, 116, 116, 0, 77, - 116, 116, 116, 116, 116, 116, 116, 116, + 0, 83, 1, 1, 55, 77, 10, 10, + 13, 10, 13, 10, 10, 13, 10, 1, + 13, 10, 14, 82, 116, 116, 0, 77, 116, 116, 116, 116, 116, 116, 116, 116, - 116, 116, 116 + 116, 116, 116, 116, 116, 116, 116, 116 }; static const short _deserialize_text_index_offsets[] = { 0, 0, 84, 86, 88, 144, 222, 233, - 247, 258, 269, 280, 294, 305, 307, 321, - 332, 347, 430, 444, 455, 572, 689, 690, + 244, 258, 269, 283, 294, 305, 319, 330, + 332, 346, 357, 372, 455, 572, 689, 690, 768, 885, 1002, 1119, 1236, 1353, 1470, 1587, - 1704, 1821, 1938, 2055, 2172, 2289, 2406, 2523, - 2640, 2757, 2874 + 1704, 1821, 1938, 2055, 2172, 2289, 2406, 2523 }; static const char _deserialize_text_indicies[] = { @@ -90,34 +88,34 @@ static const char _deserialize_text_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 8, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 1, 10, 1, 1, 11, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 1, + 9, 1, 10, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 1, 12, 1, 1, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 1, 15, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 1, 17, 18, - 18, 18, 18, 18, 18, 18, 18, 18, - 1, 19, 1, 1, 20, 21, 21, 21, + 16, 16, 16, 16, 16, 1, 17, 1, + 1, 18, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 1, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 1, 22, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 1, 24, 1, 25, 1, 1, 26, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 1, 28, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 1, 24, 1, 1, - 1, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 1, 30, 30, 1, 1, + 23, 1, 24, 1, 1, 25, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 1, + 27, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 1, 29, 1, 30, 1, 1, + 31, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 1, 33, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 1, 29, 1, + 1, 1, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 1, 35, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 30, 1, - 1, 30, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 35, + 1, 1, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 30, 30, 1, + 1, 1, 1, 1, 1, 1, 35, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 30, 1, 31, - 1, 1, 32, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 1, 34, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 1, + 1, 1, 1, 1, 1, 1, 35, 1, 36, 36, 36, 36, 36, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 36, @@ -201,282 +199,235 @@ static const char _deserialize_text_indicies[] = { 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 56, 48, - 57, 57, 57, 57, 57, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 57, - 30, 30, 58, 30, 30, 30, 30, 30, - 30, 30, 59, 1, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 60, 30, 30, 61, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 62, 63, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 64, 30, 57, 57, 57, - 57, 57, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 57, 30, 30, 58, - 30, 30, 30, 30, 30, 30, 30, 59, - 1, 30, 30, 30, 65, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 30, 30, - 30, 60, 30, 30, 61, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 62, 63, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 64, 30, 67, 67, 67, 67, 67, 1, + 57, 57, 57, 57, 57, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 57, + 35, 35, 58, 35, 35, 35, 35, 35, + 35, 35, 59, 1, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 60, 35, 35, 61, + 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 62, 63, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 64, 35, 65, 65, 65, + 65, 65, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 67, 1, 1, 68, 1, 1, 1, - 1, 1, 1, 1, 1, 69, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 70, 1, + 1, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 71, 1, 72, - 72, 72, 72, 72, 1, 1, 1, 1, + 67, 1, 68, 68, 68, 68, 68, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 72, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 68, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 42, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 42, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 73, 1, 74, 74, 74, 74, - 74, 48, 48, 48, 48, 48, 48, 48, + 1, 1, 1, 1, 1, 69, 1, 70, + 70, 70, 70, 70, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 74, 48, 48, 50, 48, - 48, 48, 48, 48, 48, 48, 51, 1, + 48, 48, 48, 48, 48, 48, 70, 48, + 48, 50, 48, 48, 48, 48, 48, 48, + 48, 51, 1, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 52, 48, 48, 53, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 52, 48, 48, 53, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 54, - 55, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 54, 55, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 56, - 48, 75, 75, 75, 75, 75, 1, 1, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 56, 48, 71, 71, 71, 71, + 71, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 71, 1, 1, 72, 1, + 1, 1, 1, 1, 1, 1, 1, 73, 1, 1, 1, 1, 1, 1, 1, 1, - 75, 1, 1, 76, 1, 1, 1, 1, - 1, 1, 1, 77, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 78, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 45, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 79, 1, 80, 80, - 80, 80, 80, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 75, + 1, 76, 76, 76, 76, 76, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 76, 1, 1, 77, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 81, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 78, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 82, 1, 80, 80, 80, 80, 80, + 1, 1, 1, 1, 79, 1, 76, 76, + 76, 76, 76, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 76, 1, 1, + 77, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 78, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 81, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 79, 1, 71, 71, 71, 71, 71, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 82, 1, - 84, 84, 84, 84, 84, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 84, - 1, 1, 85, 1, 1, 1, 1, 1, - 1, 1, 86, 1, 1, 1, 1, 1, + 1, 1, 71, 1, 1, 72, 1, 1, + 1, 1, 1, 1, 1, 1, 73, 1, + 1, 1, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 87, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 75, 1, + 80, 80, 80, 80, 80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 88, 1, 84, 84, 84, - 84, 84, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 80, + 1, 1, 81, 1, 1, 1, 1, 1, + 1, 1, 82, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 84, 1, 1, 85, - 1, 1, 1, 1, 1, 1, 1, 86, - 1, 1, 1, 1, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 83, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 45, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 84, 1, 85, 85, 85, + 85, 85, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 88, 1, 75, 75, 75, 75, 75, 1, + 1, 1, 1, 1, 85, 1, 1, 86, + 1, 1, 1, 1, 1, 1, 1, 87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 75, 1, 1, 76, 1, 1, 1, - 1, 1, 1, 1, 77, 1, 1, 1, - 1, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 1, 1, 1, 1, 1, - 1, 78, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 45, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 88, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 79, 1, 90, - 90, 90, 90, 90, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 90, 1, - 1, 91, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 89, 1, 85, 85, 85, 85, 85, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 85, 1, 1, 86, 1, 1, 1, + 1, 1, 1, 1, 87, 1, 1, 1, + 1, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 92, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 88, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 93, 1, 90, 90, 90, 90, - 90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 90, 1, 1, 91, 1, + 1, 1, 1, 1, 1, 89, 1, 80, + 80, 80, 80, 80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 80, 1, + 1, 81, 1, 1, 1, 1, 1, 1, + 1, 82, 1, 1, 1, 1, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, + 1, 1, 1, 1, 1, 1, 83, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 45, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 92, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 93, - 1, 67, 67, 67, 67, 67, 1, 1, + 1, 1, 84, 1, 65, 65, 65, 65, + 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 67, 1, 1, 68, 1, 1, 1, 1, - 1, 1, 1, 1, 69, 1, 1, 1, - 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 71, 1, 94, 94, - 94, 94, 94, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 94, 30, 30, - 58, 30, 30, 30, 30, 30, 30, 30, - 59, 1, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 60, 30, 30, 61, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 62, 95, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 96, 30, 94, 94, 94, 94, 94, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 94, 30, 30, 58, 30, 30, - 30, 30, 30, 30, 30, 59, 1, 30, - 30, 30, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 30, 30, 30, 60, - 30, 30, 61, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 62, 95, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 96, 30, - 0 + 1, 1, 1, 1, 1, 1, 1, 67, + 1, 0 }; static const char _deserialize_text_trans_targs[] = { 1, 0, 2, 26, 3, 4, 20, 5, - 24, 25, 8, 29, 40, 29, 40, 32, - 37, 33, 34, 12, 13, 16, 13, 16, - 14, 15, 35, 36, 35, 36, 27, 19, - 38, 39, 38, 39, 21, 20, 6, 22, + 24, 25, 28, 39, 9, 31, 34, 31, + 34, 11, 32, 33, 32, 33, 35, 38, + 14, 15, 18, 15, 18, 16, 17, 36, + 37, 36, 37, 27, 21, 20, 6, 22, 23, 21, 22, 23, 21, 22, 23, 25, - 27, 27, 28, 7, 9, 11, 17, 22, - 31, 27, 28, 7, 9, 11, 17, 22, - 31, 41, 42, 30, 10, 18, 22, 31, - 30, 31, 31, 30, 10, 7, 11, 31, - 30, 22, 31, 34, 30, 10, 7, 22, - 31, 37, 30, 10, 22, 31, 27, 22, - 31, 42 + 27, 27, 7, 8, 12, 13, 19, 22, + 30, 27, 7, 8, 12, 13, 19, 22, + 30, 29, 22, 30, 29, 30, 30, 29, + 7, 10, 22, 30, 29, 7, 22, 30, + 29, 7, 8, 13, 30, 29, 7, 8, + 22, 30, 38, 39 }; static const char _deserialize_text_trans_actions[] = { 0, 0, 0, 0, 1, 0, 2, 0, - 2, 2, 3, 4, 4, 5, 5, 4, - 4, 4, 4, 3, 3, 3, 0, 0, - 6, 3, 4, 4, 5, 5, 5, 3, - 4, 4, 5, 5, 7, 8, 9, 7, + 2, 2, 3, 3, 4, 3, 3, 5, + 5, 4, 3, 3, 5, 5, 3, 3, + 4, 4, 4, 0, 0, 6, 4, 3, + 3, 5, 5, 5, 7, 8, 9, 7, 7, 0, 0, 0, 10, 10, 10, 8, - 12, 13, 14, 15, 15, 15, 16, 11, - 11, 18, 19, 20, 20, 20, 0, 17, - 17, 4, 4, 21, 22, 22, 21, 21, - 0, 0, 13, 10, 23, 23, 23, 10, - 24, 24, 24, 5, 25, 26, 26, 25, - 25, 5, 27, 28, 27, 27, 30, 29, - 29, 5 + 12, 13, 14, 14, 14, 14, 15, 11, + 11, 17, 18, 18, 18, 18, 0, 16, + 16, 19, 19, 19, 0, 0, 13, 20, + 21, 21, 20, 20, 22, 23, 22, 22, + 10, 24, 24, 24, 10, 25, 26, 26, + 25, 25, 5, 5 }; static const char _deserialize_text_eof_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, - 10, 10, 11, 17, 17, 21, 0, 11, - 10, 24, 24, 25, 25, 10, 27, 27, - 21, 29, 29 + 10, 10, 11, 16, 19, 0, 11, 20, + 22, 22, 20, 10, 25, 25, 10, 19 }; static const int deserialize_text_start = 1; @@ -509,12 +460,12 @@ _hb_buffer_deserialize_text (hb_buffer_t *buffer, hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 513 "hb-buffer-deserialize-text.hh" +#line 457 "hb-buffer-deserialize-text.hh" { cs = deserialize_text_start; } -#line 518 "hb-buffer-deserialize-text.hh" +#line 460 "hb-buffer-deserialize-text.hh" { int _slen; int _trans; @@ -542,11 +493,11 @@ _resume: case 1: #line 38 "hb-buffer-deserialize-text.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } break; - case 3: + case 4: #line 51 "hb-buffer-deserialize-text.rl" { tok = p; @@ -560,7 +511,7 @@ _resume: #line 56 "hb-buffer-deserialize-text.rl" { if (unlikely (!buffer->ensure_unicode ())) return false; } break; - case 20: + case 18: #line 58 "hb-buffer-deserialize-text.rl" { /* TODO Unescape delimiters. */ @@ -574,7 +525,7 @@ _resume: #line 66 "hb-buffer-deserialize-text.rl" {if (!parse_hex (tok, p, &info.codepoint )) return false; } break; - case 23: + case 24: #line 68 "hb-buffer-deserialize-text.rl" { if (!parse_uint (tok, p, &info.cluster )) return false; } break; @@ -586,26 +537,26 @@ _resume: #line 70 "hb-buffer-deserialize-text.rl" { if (!parse_int (tok, p, &pos.y_offset )) return false; } break; - case 22: + case 21: #line 71 "hb-buffer-deserialize-text.rl" { if (!parse_int (tok, p, &pos.x_advance)) return false; } break; - case 28: + case 23: #line 72 "hb-buffer-deserialize-text.rl" { if (!parse_int (tok, p, &pos.y_advance)) return false; } break; - case 16: + case 15: #line 38 "hb-buffer-deserialize-text.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } #line 51 "hb-buffer-deserialize-text.rl" { tok = p; } break; - case 4: + case 3: #line 51 "hb-buffer-deserialize-text.rl" { tok = p; @@ -621,7 +572,7 @@ _resume: #line 56 "hb-buffer-deserialize-text.rl" { if (unlikely (!buffer->ensure_unicode ())) return false; } break; - case 17: + case 16: #line 58 "hb-buffer-deserialize-text.rl" { /* TODO Unescape delimiters. */ @@ -639,18 +590,6 @@ _resume: *end_ptr = p; } break; - case 19: -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 55 "hb-buffer-deserialize-text.rl" - { if (unlikely (!buffer->ensure_glyphs ())) return false; } - break; case 7: #line 66 "hb-buffer-deserialize-text.rl" {if (!parse_hex (tok, p, &info.codepoint )) return false; } @@ -687,7 +626,7 @@ _resume: *end_ptr = p; } break; - case 21: + case 20: #line 71 "hb-buffer-deserialize-text.rl" { if (!parse_int (tok, p, &pos.x_advance)) return false; } #line 43 "hb-buffer-deserialize-text.rl" @@ -699,7 +638,7 @@ _resume: *end_ptr = p; } break; - case 27: + case 22: #line 72 "hb-buffer-deserialize-text.rl" { if (!parse_int (tok, p, &pos.y_advance)) return false; } #line 43 "hb-buffer-deserialize-text.rl" @@ -711,7 +650,7 @@ _resume: *end_ptr = p; } break; - case 24: + case 19: #line 73 "hb-buffer-deserialize-text.rl" { if (!parse_uint (tok, p, &info.mask )) return false; } #line 43 "hb-buffer-deserialize-text.rl" @@ -726,8 +665,8 @@ _resume: case 12: #line 38 "hb-buffer-deserialize-text.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } #line 51 "hb-buffer-deserialize-text.rl" { @@ -736,11 +675,11 @@ _resume: #line 55 "hb-buffer-deserialize-text.rl" { if (unlikely (!buffer->ensure_glyphs ())) return false; } break; - case 15: + case 14: #line 38 "hb-buffer-deserialize-text.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } #line 51 "hb-buffer-deserialize-text.rl" { @@ -755,7 +694,7 @@ _resume: return false; } break; - case 18: + case 17: #line 58 "hb-buffer-deserialize-text.rl" { /* TODO Unescape delimiters. */ @@ -775,31 +714,11 @@ _resume: *end_ptr = p; } break; - case 29: -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 73 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.mask )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; case 11: #line 38 "hb-buffer-deserialize-text.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } #line 51 "hb-buffer-deserialize-text.rl" { @@ -822,54 +741,11 @@ _resume: *end_ptr = p; } break; - case 14: -#line 38 "hb-buffer-deserialize-text.rl" - { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 55 "hb-buffer-deserialize-text.rl" - { if (unlikely (!buffer->ensure_glyphs ())) return false; } - break; - case 30: -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 73 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.mask )) return false; } -#line 55 "hb-buffer-deserialize-text.rl" - { if (unlikely (!buffer->ensure_glyphs ())) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; case 13: #line 38 "hb-buffer-deserialize-text.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } #line 51 "hb-buffer-deserialize-text.rl" { @@ -894,7 +770,7 @@ _resume: *end_ptr = p; } break; -#line 898 "hb-buffer-deserialize-text.hh" +#line 715 "hb-buffer-deserialize-text.hh" } _again: @@ -906,7 +782,7 @@ _again: if ( p == eof ) { switch ( _deserialize_text_eof_actions[cs] ) { - case 17: + case 16: #line 58 "hb-buffer-deserialize-text.rl" { /* TODO Unescape delimiters. */ @@ -960,7 +836,7 @@ _again: *end_ptr = p; } break; - case 21: + case 20: #line 71 "hb-buffer-deserialize-text.rl" { if (!parse_int (tok, p, &pos.x_advance)) return false; } #line 43 "hb-buffer-deserialize-text.rl" @@ -972,7 +848,7 @@ _again: *end_ptr = p; } break; - case 27: + case 22: #line 72 "hb-buffer-deserialize-text.rl" { if (!parse_int (tok, p, &pos.y_advance)) return false; } #line 43 "hb-buffer-deserialize-text.rl" @@ -984,27 +860,7 @@ _again: *end_ptr = p; } break; - case 24: -#line 73 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.mask )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 29: -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} + case 19: #line 73 "hb-buffer-deserialize-text.rl" { if (!parse_uint (tok, p, &info.mask )) return false; } #line 43 "hb-buffer-deserialize-text.rl" @@ -1019,8 +875,8 @@ _again: case 11: #line 38 "hb-buffer-deserialize-text.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } #line 51 "hb-buffer-deserialize-text.rl" { @@ -1043,7 +899,7 @@ _again: *end_ptr = p; } break; -#line 1047 "hb-buffer-deserialize-text.hh" +#line 825 "hb-buffer-deserialize-text.hh" } } diff --git a/thirdparty/harfbuzz/src/hb-buffer-serialize.cc b/thirdparty/harfbuzz/src/hb-buffer-serialize.cc index d1e1775430..a458f2318f 100644 --- a/thirdparty/harfbuzz/src/hb-buffer-serialize.cc +++ b/thirdparty/harfbuzz/src/hb-buffer-serialize.cc @@ -183,7 +183,7 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; @@ -241,7 +241,7 @@ _hb_buffer_serialize_unicode_json (hb_buffer_t *buffer, unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; @@ -329,7 +329,7 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; @@ -381,7 +381,7 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer, unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; diff --git a/thirdparty/harfbuzz/src/hb-buffer-verify.cc b/thirdparty/harfbuzz/src/hb-buffer-verify.cc index 5453e1ca94..1cd52b39b1 100644 --- a/thirdparty/harfbuzz/src/hb-buffer-verify.cc +++ b/thirdparty/harfbuzz/src/hb-buffer-verify.cc @@ -186,7 +186,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer, bool ret = true; hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); - if (diff) + if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH) { buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break test failed."); ret = false; @@ -313,7 +313,6 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, bool ret = true; hb_buffer_diff_flags_t diff; - /* * Shape the two fragment streams. */ @@ -382,7 +381,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, * Diff results. */ diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); - if (diff) + if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH) { buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-concat test failed."); ret = false; diff --git a/thirdparty/harfbuzz/src/hb-buffer.cc b/thirdparty/harfbuzz/src/hb-buffer.cc index 57a5ae03ed..4b6c2d9eaa 100644 --- a/thirdparty/harfbuzz/src/hb-buffer.cc +++ b/thirdparty/harfbuzz/src/hb-buffer.cc @@ -172,12 +172,13 @@ hb_buffer_t::enlarge (unsigned int size) while (size >= new_allocated) new_allocated += (new_allocated >> 1) + 32; - static_assert ((sizeof (info[0]) == sizeof (pos[0])), ""); - if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0])))) + unsigned new_bytes; + if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]), &new_bytes))) goto done; - new_pos = (hb_glyph_position_t *) hb_realloc (pos, new_allocated * sizeof (pos[0])); - new_info = (hb_glyph_info_t *) hb_realloc (info, new_allocated * sizeof (info[0])); + static_assert (sizeof (info[0]) == sizeof (pos[0]), ""); + new_pos = (hb_glyph_position_t *) hb_realloc (pos, new_bytes); + new_info = (hb_glyph_info_t *) hb_realloc (info, new_bytes); done: if (unlikely (!new_pos || !new_info)) @@ -208,7 +209,7 @@ hb_buffer_t::make_room_for (unsigned int num_in, assert (have_output); out_info = (hb_glyph_info_t *) pos; - memcpy (out_info, info, out_len * sizeof (out_info[0])); + hb_memcpy (out_info, info, out_len * sizeof (out_info[0])); } return true; @@ -229,7 +230,7 @@ hb_buffer_t::shift_forward (unsigned int count) * Ideally, we should at least set Default_Ignorable bits on * these, as well as consistent cluster values. But the former * is layering violation... */ - memset (info + len, 0, (idx + count - len) * sizeof (info[0])); + hb_memset (info + len, 0, (idx + count - len) * sizeof (info[0])); } len += count; idx += count; @@ -298,8 +299,8 @@ hb_buffer_t::clear () out_len = 0; out_info = info; - memset (context, 0, sizeof context); - memset (context_len, 0, sizeof context_len); + hb_memset (context, 0, sizeof context); + hb_memset (context_len, 0, sizeof context_len); deallocate_var_all (); serial = 0; @@ -313,15 +314,14 @@ hb_buffer_t::enter () serial = 0; shaping_failed = false; scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; - if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR))) + unsigned mul; + if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR, &mul))) { - max_len = hb_max (len * HB_BUFFER_MAX_LEN_FACTOR, - (unsigned) HB_BUFFER_MAX_LEN_MIN); + max_len = hb_max (mul, (unsigned) HB_BUFFER_MAX_LEN_MIN); } - if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_OPS_FACTOR))) + if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_OPS_FACTOR, &mul))) { - max_ops = hb_max (len * HB_BUFFER_MAX_OPS_FACTOR, - (unsigned) HB_BUFFER_MAX_OPS_MIN); + max_ops = hb_max (mul, (unsigned) HB_BUFFER_MAX_OPS_MIN); } } void @@ -345,7 +345,7 @@ hb_buffer_t::add (hb_codepoint_t codepoint, glyph = &info[len]; - memset (glyph, 0, sizeof (*glyph)); + hb_memset (glyph, 0, sizeof (*glyph)); glyph->codepoint = codepoint; glyph->mask = 0; glyph->cluster = cluster; @@ -606,6 +606,53 @@ done: } void +hb_buffer_t::delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info)) +{ + /* Merge clusters and delete filtered glyphs. + * NOTE! We can't use out-buffer as we have positioning data. */ + unsigned int j = 0; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + if (filter (&info[i])) + { + /* Merge clusters. + * Same logic as delete_glyph(), but for in-place removal. */ + + unsigned int cluster = info[i].cluster; + if (i + 1 < count && cluster == info[i + 1].cluster) + continue; /* Cluster survives; do nothing. */ + + if (j) + { + /* Merge cluster backward. */ + if (cluster < info[j - 1].cluster) + { + unsigned int mask = info[i].mask; + unsigned int old_cluster = info[j - 1].cluster; + for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--) + set_cluster (info[k - 1], cluster, mask); + } + continue; + } + + if (i + 1 < count) + merge_clusters (i, i + 2); /* Merge cluster forward. */ + + continue; + } + + if (j != i) + { + info[j] = info[i]; + pos[j] = pos[i]; + } + j++; + } + len = j; +} + +void hb_buffer_t::guess_segment_properties () { assert_unicode (); @@ -933,7 +980,6 @@ hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer) void hb_buffer_set_direction (hb_buffer_t *buffer, hb_direction_t direction) - { if (unlikely (hb_object_is_immutable (buffer))) return; @@ -1385,9 +1431,9 @@ hb_buffer_set_length (hb_buffer_t *buffer, /* Wipe the new space */ if (length > buffer->len) { - memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); + hb_memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); if (buffer->have_positions) - memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); + hb_memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); } buffer->len = length; @@ -1795,7 +1841,9 @@ hb_buffer_add_latin1 (hb_buffer_t *buffer, * marks at stat of run. * * This function does not check the validity of @text, it is up to the caller - * to ensure it contains a valid Unicode code points. + * to ensure it contains a valid Unicode scalar values. In contrast, + * hb_buffer_add_utf32() can be used that takes similar input but performs + * sanity-check on the input. * * Since: 0.9.31 **/ @@ -1858,9 +1906,9 @@ hb_buffer_append (hb_buffer_t *buffer, hb_segment_properties_overlay (&buffer->props, &source->props); - memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0])); + hb_memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0])); if (buffer->have_positions) - memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0])); + hb_memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0])); if (source->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) { @@ -2048,7 +2096,7 @@ hb_buffer_diff (hb_buffer_t *buffer, result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH; if (buf_info->cluster != ref_info->cluster) result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH; - if ((buf_info->mask & ~ref_info->mask & HB_GLYPH_FLAG_DEFINED)) + if ((buf_info->mask ^ ref_info->mask) & HB_GLYPH_FLAG_DEFINED) result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH; if (contains && ref_info->codepoint == dottedcircle_glyph) result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; @@ -2103,6 +2151,13 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, hb_buffer_message_func_t func, void *user_data, hb_destroy_func_t destroy) { + if (unlikely (hb_object_is_immutable (buffer))) + { + if (destroy) + destroy (user_data); + return; + } + if (buffer->message_destroy) buffer->message_destroy (buffer->message_data); diff --git a/thirdparty/harfbuzz/src/hb-buffer.hh b/thirdparty/harfbuzz/src/hb-buffer.hh index 26c3f0fac8..bb1efe9dd3 100644 --- a/thirdparty/harfbuzz/src/hb-buffer.hh +++ b/thirdparty/harfbuzz/src/hb-buffer.hh @@ -32,6 +32,7 @@ #include "hb.hh" #include "hb-unicode.hh" +#include "hb-set-digest.hh" #ifndef HB_BUFFER_MAX_LEN_FACTOR @@ -207,6 +208,14 @@ struct hb_buffer_t hb_glyph_info_t &prev () { return out_info[out_len ? out_len - 1 : 0]; } hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; } + hb_set_digest_t digest () const + { + hb_set_digest_t d; + d.init (); + d.add_array (&info[0].codepoint, len, sizeof (info[0])); + return d; + } + HB_INTERNAL void similar (const hb_buffer_t &src); HB_INTERNAL void reset (); HB_INTERNAL void clear (); @@ -402,6 +411,8 @@ struct hb_buffer_t HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end); /* Merge clusters for deleting current glyph, and skip it. */ HB_INTERNAL void delete_glyph (); + HB_INTERNAL void delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info)); + /* Adds glyph flags in mask to infos with clusters between start and end. diff --git a/thirdparty/harfbuzz/src/hb-cache.hh b/thirdparty/harfbuzz/src/hb-cache.hh index 897f313fbd..f8c8108f1f 100644 --- a/thirdparty/harfbuzz/src/hb-cache.hh +++ b/thirdparty/harfbuzz/src/hb-cache.hh @@ -46,7 +46,7 @@ struct hb_cache_t >::type; static_assert ((key_bits >= cache_bits), ""); - static_assert ((key_bits + value_bits - cache_bits <= 8 * sizeof (item_t)), ""); + static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), ""); void init () { clear (); } void fini () {} diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh index 5c2cb060a4..49805a89c5 100644 --- a/thirdparty/harfbuzz/src/hb-cff-interp-common.hh +++ b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh @@ -284,65 +284,56 @@ struct UnsizedByteStr : UnsizedArrayOf <HBUINT8> /* A byte string associated with the current offset and an error condition */ struct byte_str_ref_t { - byte_str_ref_t () { init (); } - - void init () - { - str = hb_ubytes_t (); - offset = 0; - error = false; - } - - void fini () {} + byte_str_ref_t () + : str () {} byte_str_ref_t (const hb_ubytes_t &str_, unsigned int offset_ = 0) - : str (str_), offset (offset_), error (false) {} + : str (str_) { set_offset (offset_); } void reset (const hb_ubytes_t &str_, unsigned int offset_ = 0) { str = str_; - offset = offset_; - error = false; + set_offset (offset_); } const unsigned char& operator [] (int i) { - if (unlikely ((unsigned int) (offset + i) >= str.length)) + if (unlikely ((unsigned int) (get_offset () + i) >= str.length)) { set_error (); return Null (unsigned char); } - return str[offset + i]; + return str.arrayZ[get_offset () + i]; } + unsigned char head_unchecked () const { return str.arrayZ[get_offset ()]; } + /* Conversion to hb_ubytes_t */ - operator hb_ubytes_t () const { return str.sub_array (offset, str.length - offset); } + operator hb_ubytes_t () const { return str.sub_array (get_offset ()); } hb_ubytes_t sub_array (unsigned int offset_, unsigned int len_) const { return str.sub_array (offset_, len_); } bool avail (unsigned int count=1) const - { return (!in_error () && offset + count <= str.length); } + { return get_offset () + count <= str.length; } void inc (unsigned int count=1) { - if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length))) - { - offset += count; - } - else - { - offset = str.length; - set_error (); - } + /* Automatically puts us in error if count is out-of-range. */ + set_offset (get_offset () + count); } - void set_error () { error = true; } - bool in_error () const { return error; } + /* We (ab)use ubytes backwards_length as a cursor (called offset), + * as well as to store error condition. */ - hb_ubytes_t str; - unsigned int offset; /* beginning of the sub-string within str */ + unsigned get_offset () const { return str.backwards_length; } + void set_offset (unsigned offset) { str.backwards_length = offset; } + + void set_error () { str.backwards_length = str.length + 1; } + bool in_error () const { return str.backwards_length > str.length; } + + unsigned total_size () const { return str.length; } protected: - bool error; + hb_ubytes_t str; }; using byte_str_array_t = hb_vector_t<hb_ubytes_t>; @@ -491,8 +482,15 @@ struct arg_stack_t : cff_stack_t<ARG, 513> /* an operator prefixed by its operands in a byte string */ struct op_str_t { - hb_ubytes_t str; + /* This used to have a hb_ubytes_t. Using a pointer and length + * in a particular order, saves 8 bytes in this struct and more + * in our parsed_cs_op_t subclass. */ + + const unsigned char *ptr = nullptr; + op_code_t op; + + uint8_t length = 0; }; /* base of OP_SERIALIZER */ @@ -503,9 +501,11 @@ struct op_serializer_t { TRACE_SERIALIZE (this); - HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.length); + unsigned char *d = c->allocate_size<unsigned char> (opstr.length); if (unlikely (!d)) return_trace (false); - memcpy (d, &opstr.str[0], opstr.str.length); + /* Faster than hb_memcpy for small strings. */ + for (unsigned i = 0; i < opstr.length; i++) + d[i] = opstr.ptr[i]; return_trace (true); } }; @@ -529,16 +529,20 @@ struct parsed_values_t { VAL *val = values.push (); val->op = op; - val->str = str_ref.str.sub_array (opStart, str_ref.offset - opStart); - opStart = str_ref.offset; + auto arr = str_ref.sub_array (opStart, str_ref.get_offset () - opStart); + val->ptr = arr.arrayZ; + val->length = arr.length; + opStart = str_ref.get_offset (); } void add_op (op_code_t op, const byte_str_ref_t& str_ref, const VAL &v) { VAL *val = values.push (v); val->op = op; - val->str = str_ref.sub_array ( opStart, str_ref.offset - opStart); - opStart = str_ref.offset; + auto arr = str_ref.sub_array (opStart, str_ref.get_offset () - opStart); + val->ptr = arr.arrayZ; + val->length = arr.length; + opStart = str_ref.get_offset (); } bool has_op (op_code_t op) const @@ -549,8 +553,7 @@ struct parsed_values_t } unsigned get_count () const { return values.length; } - const VAL &get_value (unsigned int i) const { return values[i]; } - const VAL &operator [] (unsigned int i) const { return get_value (i); } + const VAL &operator [] (unsigned int i) const { return values[i]; } unsigned int opStart; hb_vector_t<VAL> values; @@ -565,23 +568,23 @@ struct interp_env_t str_ref.reset (str_); } bool in_error () const - { return error || str_ref.in_error () || argStack.in_error (); } + { return str_ref.in_error () || argStack.in_error (); } - void set_error () { error = true; } + void set_error () { str_ref.set_error (); } op_code_t fetch_op () { op_code_t op = OpCode_Invalid; if (unlikely (!str_ref.avail ())) return OpCode_Invalid; - op = (op_code_t)(unsigned char)str_ref[0]; + op = (op_code_t) str_ref.head_unchecked (); + str_ref.inc (); if (op == OpCode_escape) { if (unlikely (!str_ref.avail ())) return OpCode_Invalid; - op = Make_OpCode_ESC(str_ref[1]); + op = Make_OpCode_ESC (str_ref.head_unchecked ()); str_ref.inc (); } - str_ref.inc (); return op; } @@ -596,8 +599,6 @@ struct interp_env_t str_ref; arg_stack_t<ARG> argStack; - protected: - bool error = false; }; using num_interp_env_t = interp_env_t<>; diff --git a/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh index d0b9e7b086..00c25800e6 100644 --- a/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh +++ b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh @@ -40,13 +40,15 @@ struct blend_arg_t : number_t void set_real (double v) { reset_blends (); number_t::set_real (v); } void set_blends (unsigned int numValues_, unsigned int valueIndex_, - unsigned int numBlends, hb_array_t<const blend_arg_t> blends_) + hb_array_t<const blend_arg_t> blends_) { numValues = numValues_; valueIndex = valueIndex_; - deltas.resize (numBlends); + unsigned numBlends = blends_.length; + if (unlikely (!deltas.resize (numBlends))) + return; for (unsigned int i = 0; i < numBlends; i++) - deltas[i] = blends_[i]; + deltas.arrayZ[i] = blends_.arrayZ[i]; } bool blending () const { return deltas.length > 0; } @@ -61,7 +63,6 @@ struct blend_arg_t : number_t hb_vector_t<number_t> deltas; }; -typedef interp_env_t<blend_arg_t> BlendInterpEnv; typedef biased_subrs_t<CFF2Subrs> cff2_biased_subrs_t; template <typename ELEM> @@ -154,8 +155,9 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs> { if (likely (scalars.length == deltas.length)) { - for (unsigned int i = 0; i < scalars.length; i++) - v += (double) scalars[i] * deltas[i].to_real (); + unsigned count = scalars.length; + for (unsigned i = 0; i < count; i++) + v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real (); } } return v; @@ -220,7 +222,7 @@ struct cff2_cs_opset_t : cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PAR const hb_array_t<const ELEM> blends, unsigned n, unsigned i) { - arg.set_blends (n, i, blends.length, blends); + arg.set_blends (n, i, blends); } template <typename T = ELEM, hb_enable_if (!hb_is_same (T, blend_arg_t))> diff --git a/thirdparty/harfbuzz/src/hb-common.cc b/thirdparty/harfbuzz/src/hb-common.cc index bbb6cd552b..e9f9cfeb5f 100644 --- a/thirdparty/harfbuzz/src/hb-common.cc +++ b/thirdparty/harfbuzz/src/hb-common.cc @@ -285,7 +285,7 @@ struct hb_language_item_t { lang = (hb_language_t) hb_malloc(len); if (likely (lang)) { - memcpy((unsigned char *) lang, s, len); + hb_memcpy((unsigned char *) lang, s, len); for (unsigned char *p = (unsigned char *) lang; *p; p++) *p = canon_map[*p]; } @@ -379,7 +379,7 @@ hb_language_from_string (const char *str, int len) /* NUL-terminate it. */ char strbuf[64]; len = hb_min (len, (int) sizeof (strbuf) - 1); - memcpy (strbuf, str, len); + hb_memcpy (strbuf, str, len); strbuf[len] = '\0'; item = lang_find_or_insert (strbuf); } @@ -976,7 +976,7 @@ hb_feature_from_string (const char *str, int len, } if (feature) - memset (feature, 0, sizeof (*feature)); + hb_memset (feature, 0, sizeof (*feature)); return false; } @@ -1025,7 +1025,7 @@ hb_feature_to_string (hb_feature_t *feature, } assert (len < ARRAY_LENGTH (s)); len = hb_min (len, size - 1); - memcpy (buf, s, len); + hb_memcpy (buf, s, len); buf[len] = '\0'; } @@ -1088,7 +1088,7 @@ hb_variation_from_string (const char *str, int len, } if (variation) - memset (variation, 0, sizeof (*variation)); + hb_memset (variation, 0, sizeof (*variation)); return false; } @@ -1136,7 +1136,7 @@ get_C_locale () /** * hb_variation_to_string: * @variation: an #hb_variation_t to convert - * @buf: (array length=size) (out): output string + * @buf: (array length=size) (out caller-allocates): output string * @size: the allocated size of @buf * * Converts an #hb_variation_t into a `NULL`-terminated string in the format @@ -1166,7 +1166,7 @@ hb_variation_to_string (hb_variation_t *variation, assert (len < ARRAY_LENGTH (s)); len = hb_min (len, size - 1); - memcpy (buf, s, len); + hb_memcpy (buf, s, len); buf[len] = '\0'; } diff --git a/thirdparty/harfbuzz/src/hb-config.hh b/thirdparty/harfbuzz/src/hb-config.hh index d56617f6a9..98b1e9d0cf 100644 --- a/thirdparty/harfbuzz/src/hb-config.hh +++ b/thirdparty/harfbuzz/src/hb-config.hh @@ -35,8 +35,9 @@ #include "config.h" #endif -#ifndef HB_BORING_EXPANSION -#define HB_NO_BORING_EXPANSION +#ifndef HB_EXPERIMENTAL_API +#define HB_NO_BEYOND_64K +#define HB_NO_VAR_COMPOSITES #endif #ifdef HB_TINY @@ -84,6 +85,7 @@ #define HB_NO_OT_SHAPE_FRACTIONS #define HB_NO_STYLE #define HB_NO_SUBSET_LAYOUT +#define HB_NO_VERTICAL #define HB_NO_VAR #endif @@ -104,7 +106,7 @@ #ifdef HB_NO_BORING_EXPANSION #define HB_NO_BEYOND_64K -#define HB_NO_VARIATIONS2 +#define HB_NO_AVAR2 #endif #ifdef HB_DISABLE_DEPRECATED @@ -113,6 +115,11 @@ #define HB_IF_NOT_DEPRECATED(x) x #endif +#ifdef HB_NO_SHAPER +#define HB_NO_OT_SHAPE +#define HB_NO_AAT_SHAPE +#endif + #ifdef HB_NO_AAT #define HB_NO_OT_NAME_LANGUAGE_AAT #define HB_NO_AAT_SHAPE @@ -159,6 +166,7 @@ #define HB_NO_OT_SHAPER_HEBREW_FALLBACK #define HB_NO_OT_SHAPER_THAI_FALLBACK #define HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS +#define HB_NO_OT_SHAPER_MYANMAR_ZAWGYI #endif #ifdef NDEBUG diff --git a/thirdparty/harfbuzz/src/hb-coretext.cc b/thirdparty/harfbuzz/src/hb-coretext.cc index 99b33c001e..4267e0e13e 100644 --- a/thirdparty/harfbuzz/src/hb-coretext.cc +++ b/thirdparty/harfbuzz/src/hb-coretext.cc @@ -347,10 +347,13 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font) hb_ot_var_axis_info_t info; unsigned int c = 1; hb_ot_var_get_axis_infos (font->face, i, &c, &info); - CFDictionarySetValue (variations, - CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag), - CFNumberCreate (kCFAllocatorDefault, kCFNumberFloatType, &font->design_coords[i]) - ); + float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value); + + CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag); + CFNumberRef value_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberFloatType, &v); + CFDictionarySetValue (variations, tag_number, value_number); + CFRelease (tag_number); + CFRelease (value_number); } CFDictionaryRef attributes = @@ -648,7 +651,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } else { active_feature_t *feature = active_features.lsearch (event->feature); if (feature) - active_features.remove (feature - active_features.arrayZ); + active_features.remove_ordered (feature - active_features.arrayZ); } } } diff --git a/thirdparty/harfbuzz/src/hb-draw.cc b/thirdparty/harfbuzz/src/hb-draw.cc index 46797e64e6..72c203f24f 100644 --- a/thirdparty/harfbuzz/src/hb-draw.cc +++ b/thirdparty/harfbuzz/src/hb-draw.cc @@ -80,6 +80,56 @@ hb_draw_close_path_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UN void *user_data HB_UNUSED) {} +static bool +_hb_draw_funcs_set_preamble (hb_draw_funcs_t *dfuncs, + bool func_is_null, + void **user_data, + hb_destroy_func_t *destroy) +{ + if (hb_object_is_immutable (dfuncs)) + { + if (*destroy) + (*destroy) (*user_data); + return false; + } + + if (func_is_null) + { + if (*destroy) + (*destroy) (*user_data); + *destroy = nullptr; + *user_data = nullptr; + } + + return true; +} + +static bool +_hb_draw_funcs_set_middle (hb_draw_funcs_t *dfuncs, + void *user_data, + hb_destroy_func_t destroy) +{ + if (user_data && !dfuncs->user_data) + { + dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data)); + if (unlikely (!dfuncs->user_data)) + goto fail; + } + if (destroy && !dfuncs->destroy) + { + dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy)); + if (unlikely (!dfuncs->destroy)) + goto fail; + } + + return true; + +fail: + if (destroy) + (destroy) (user_data); + return false; +} + #define HB_DRAW_FUNC_IMPLEMENT(name) \ \ void \ @@ -88,43 +138,24 @@ hb_draw_funcs_set_##name##_func (hb_draw_funcs_t *dfuncs, \ void *user_data, \ hb_destroy_func_t destroy) \ { \ - if (hb_object_is_immutable (dfuncs)) \ - return; \ + if (!_hb_draw_funcs_set_preamble (dfuncs, !func, &user_data, &destroy))\ + return; \ \ if (dfuncs->destroy && dfuncs->destroy->name) \ dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \ \ - if (user_data && !dfuncs->user_data) \ - { \ - dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data)); \ - if (unlikely (!dfuncs->user_data)) \ - goto fail; \ - } \ - if (destroy && !dfuncs->destroy) \ - { \ - dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy)); \ - if (unlikely (!dfuncs->destroy)) \ - goto fail; \ - } \ + if (!_hb_draw_funcs_set_middle (dfuncs, user_data, destroy)) \ + return; \ \ - if (func) { \ + if (func) \ dfuncs->func.name = func; \ - if (dfuncs->user_data) \ - dfuncs->user_data->name = user_data; \ - if (dfuncs->destroy) \ - dfuncs->destroy->name = destroy; \ - } else { \ + else \ dfuncs->func.name = hb_draw_##name##_nil; \ - if (dfuncs->user_data) \ - dfuncs->user_data->name = nullptr; \ - if (dfuncs->destroy) \ - dfuncs->destroy->name = nullptr; \ - } \ - return; \ - \ -fail: \ - if (destroy) \ - destroy (user_data); \ + \ + if (dfuncs->user_data) \ + dfuncs->user_data->name = user_data; \ + if (dfuncs->destroy) \ + dfuncs->destroy->name = destroy; \ } HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS diff --git a/thirdparty/harfbuzz/src/hb-face.cc b/thirdparty/harfbuzz/src/hb-face.cc index e7deb31dd8..8b4b635c7a 100644 --- a/thirdparty/harfbuzz/src/hb-face.cc +++ b/thirdparty/harfbuzz/src/hb-face.cc @@ -288,6 +288,7 @@ hb_face_destroy (hb_face_t *face) { if (!hb_object_destroy (face)) return; +#ifndef HB_NO_SHAPER for (hb_face_t::plan_node_t *node = face->shape_plans; node; ) { hb_face_t::plan_node_t *next = node->next; @@ -295,6 +296,7 @@ hb_face_destroy (hb_face_t *face) hb_free (node); node = next; } +#endif face->data.fini (); face->table.fini (); @@ -636,7 +638,7 @@ hb_face_collect_variation_unicodes (hb_face_t *face, struct face_table_info_t { hb_blob_t* data; - unsigned order; + signed order; }; struct hb_face_builder_data_t @@ -784,16 +786,16 @@ hb_face_builder_create () hb_bool_t hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) { - if (tag == HB_MAP_VALUE_INVALID) + if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) return false; - if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) + if (tag == HB_MAP_VALUE_INVALID) return false; hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; hb_blob_t* previous = data->tables.get (tag).data; - if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), 0})) + if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), -1})) { hb_blob_destroy (blob); return false; @@ -819,13 +821,16 @@ void hb_face_builder_sort_tables (hb_face_t *face, const hb_tag_t *tags) { + if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) + return; + hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; // Sort all unspecified tables after any specified tables. for (auto& info : data->tables.values_ref()) - info.order = -1; + info.order = (unsigned) -1; - unsigned order = 0; + signed order = 0; for (const hb_tag_t* tag = tags; *tag; tag++) diff --git a/thirdparty/harfbuzz/src/hb-face.hh b/thirdparty/harfbuzz/src/hb-face.hh index 12e10d01e0..1bf0606e52 100644 --- a/thirdparty/harfbuzz/src/hb-face.hh +++ b/thirdparty/harfbuzz/src/hb-face.hh @@ -65,7 +65,9 @@ struct hb_face_t hb_shape_plan_t *shape_plan; plan_node_t *next; }; +#ifndef HB_NO_SHAPER hb_atomic_ptr_t<plan_node_t> shape_plans; +#endif hb_blob_t *reference_table (hb_tag_t tag) const { diff --git a/thirdparty/harfbuzz/src/hb-fallback-shape.cc b/thirdparty/harfbuzz/src/hb-fallback-shape.cc index f8524ecc8e..c54ad8764b 100644 --- a/thirdparty/harfbuzz/src/hb-fallback-shape.cc +++ b/thirdparty/harfbuzz/src/hb-fallback-shape.cc @@ -75,16 +75,6 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, const hb_feature_t *features HB_UNUSED, unsigned int num_features HB_UNUSED) { - /* TODO - * - * - Apply fallback kern. - * - Handle Variation Selectors? - * - Apply normalization? - * - * This will make the fallback shaper into a dumb "TrueType" - * shaper which many people unfortunately still request. - */ - hb_codepoint_t space; bool has_space = (bool) font->get_nominal_glyph (' ', &space); diff --git a/thirdparty/harfbuzz/src/hb-font.cc b/thirdparty/harfbuzz/src/hb-font.cc index 856bbdda32..0ce3e2608a 100644 --- a/thirdparty/harfbuzz/src/hb-font.cc +++ b/thirdparty/harfbuzz/src/hb-font.cc @@ -71,7 +71,7 @@ hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED, hb_font_extents_t *extents, void *user_data HB_UNUSED) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return false; } @@ -96,7 +96,7 @@ hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED, hb_font_extents_t *extents, void *user_data HB_UNUSED) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return false; } @@ -409,7 +409,7 @@ hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, hb_glyph_extents_t *extents, void *user_data HB_UNUSED) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return false; } @@ -518,6 +518,7 @@ typedef struct hb_font_get_glyph_shape_default_adaptor_t { void *draw_data; float x_scale; float y_scale; + float slant; } hb_font_get_glyph_shape_default_adaptor_t; static void @@ -530,9 +531,10 @@ hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; + float slant = adaptor->slant; adaptor->draw_funcs->emit_move_to (adaptor->draw_data, *st, - x_scale * to_x, y_scale * to_y); + x_scale * to_x + slant * to_y, y_scale * to_y); } static void @@ -544,12 +546,13 @@ hb_draw_line_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; + float slant = adaptor->slant; - st->current_x *= x_scale; - st->current_y *= y_scale; + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_line_to (adaptor->draw_data, *st, - x_scale * to_x, y_scale * to_y); + x_scale * to_x + slant * to_y, y_scale * to_y); } static void @@ -562,13 +565,14 @@ hb_draw_quadratic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; + float slant = adaptor->slant; - st->current_x *= x_scale; - st->current_y *= y_scale; + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_quadratic_to (adaptor->draw_data, *st, - x_scale * control_x, y_scale * control_y, - x_scale * to_x, y_scale * to_y); + x_scale * control_x + slant * control_y, y_scale * control_y, + x_scale * to_x + slant * to_y, y_scale * to_y); } static void @@ -582,14 +586,15 @@ hb_draw_cubic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; + float slant = adaptor->slant; - st->current_x *= x_scale; - st->current_y *= y_scale; + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_cubic_to (adaptor->draw_data, *st, - x_scale * control1_x, y_scale * control1_y, - x_scale * control2_x, y_scale * control2_y, - x_scale * to_x, y_scale * to_y); + x_scale * control1_x + slant * control1_y, y_scale * control1_y, + x_scale * control2_x + slant * control2_y, y_scale * control2_y, + x_scale * to_x + slant * to_y, y_scale * to_y); } static void @@ -623,8 +628,10 @@ hb_font_get_glyph_shape_default (hb_font_t *font, hb_font_get_glyph_shape_default_adaptor_t adaptor = { draw_funcs, draw_data, - (float) font->x_scale / (float) font->parent->x_scale, - (float) font->y_scale / (float) font->parent->y_scale + font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, + font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, + font->parent->y_scale ? (font->slant - font->parent->slant) * + (float) font->x_scale / (float) font->parent->y_scale : 0.f }; font->parent->get_glyph_shape (glyph, @@ -822,6 +829,56 @@ hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs) } +static bool +_hb_font_funcs_set_preamble (hb_font_funcs_t *ffuncs, + bool func_is_null, + void **user_data, + hb_destroy_func_t *destroy) +{ + if (hb_object_is_immutable (ffuncs)) + { + if (*destroy) + (*destroy) (*user_data); + return false; + } + + if (func_is_null) + { + if (*destroy) + (*destroy) (*user_data); + *destroy = nullptr; + *user_data = nullptr; + } + + return true; +} + +static bool +_hb_font_funcs_set_middle (hb_font_funcs_t *ffuncs, + void *user_data, + hb_destroy_func_t destroy) +{ + if (user_data && !ffuncs->user_data) + { + ffuncs->user_data = (decltype (ffuncs->user_data)) hb_calloc (1, sizeof (*ffuncs->user_data)); + if (unlikely (!ffuncs->user_data)) + goto fail; + } + if (destroy && !ffuncs->destroy) + { + ffuncs->destroy = (decltype (ffuncs->destroy)) hb_calloc (1, sizeof (*ffuncs->destroy)); + if (unlikely (!ffuncs->destroy)) + goto fail; + } + + return true; + +fail: + if (destroy) + (destroy) (user_data); + return false; +} + #define HB_FONT_FUNC_IMPLEMENT(name) \ \ void \ @@ -830,51 +887,24 @@ hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ void *user_data, \ hb_destroy_func_t destroy) \ { \ - if (hb_object_is_immutable (ffuncs)) \ - goto fail; \ - \ - if (!func) \ - { \ - if (destroy) \ - destroy (user_data); \ - destroy = nullptr; \ - user_data = nullptr; \ - } \ + if (!_hb_font_funcs_set_preamble (ffuncs, !func, &user_data, &destroy))\ + return; \ \ if (ffuncs->destroy && ffuncs->destroy->name) \ ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); \ \ - if (user_data && !ffuncs->user_data) \ - { \ - ffuncs->user_data = (decltype (ffuncs->user_data)) hb_calloc (1, sizeof (*ffuncs->user_data)); \ - if (unlikely (!ffuncs->user_data)) \ - goto fail; \ - } \ - if (destroy && !ffuncs->destroy) \ - { \ - ffuncs->destroy = (decltype (ffuncs->destroy)) hb_calloc (1, sizeof (*ffuncs->destroy)); \ - if (unlikely (!ffuncs->destroy)) \ - goto fail; \ - } \ + if (!_hb_font_funcs_set_middle (ffuncs, user_data, destroy)) \ + return; \ \ - if (func) { \ + if (func) \ ffuncs->get.f.name = func; \ - if (ffuncs->user_data) \ - ffuncs->user_data->name = user_data; \ - if (ffuncs->destroy) \ - ffuncs->destroy->name = destroy; \ - } else { \ + else \ ffuncs->get.f.name = hb_font_get_##name##_default; \ - if (ffuncs->user_data) \ - ffuncs->user_data->name = nullptr; \ - if (ffuncs->destroy) \ - ffuncs->destroy->name = nullptr; \ - } \ - return; \ - \ -fail: \ - if (destroy) \ - destroy (user_data); \ + \ + if (ffuncs->user_data) \ + ffuncs->user_data->name = user_data; \ + if (ffuncs->destroy) \ + ffuncs->destroy->name = destroy; \ } HB_FONT_FUNCS_IMPLEMENT_CALLBACKS @@ -1323,7 +1353,7 @@ hb_font_get_glyph_from_name (hb_font_t *font, * @draw_data: User data to pass to draw callbacks * * Fetches the glyph shape that corresponds to a glyph in the specified @font. - * The shape is returned by way of calls to the callsbacks of the @dfuncs + * The shape is returned by way of calls to the callbacks of the @dfuncs * objects, with @draw_data passed to them. * * Since: 4.0.0 @@ -1780,8 +1810,8 @@ hb_font_create_sub_font (hb_font_t *parent) float *design_coords = (float *) hb_calloc (num_coords, sizeof (parent->design_coords[0])); if (likely (coords && design_coords)) { - memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); - memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); + hb_memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); + hb_memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); _hb_font_adopt_var_coords (font, coords, design_coords, num_coords); } else @@ -2443,7 +2473,7 @@ hb_font_set_var_coords_design (hb_font_t *font, } if (coords_length) - memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); + hb_memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); @@ -2519,8 +2549,8 @@ hb_font_set_var_coords_normalized (hb_font_t *font, if (coords_length) { - memcpy (copy, coords, coords_length * sizeof (coords[0])); - memcpy (unmapped, coords, coords_length * sizeof (coords[0])); + hb_memcpy (copy, coords, coords_length * sizeof (coords[0])); + hb_memcpy (unmapped, coords, coords_length * sizeof (coords[0])); } /* Best effort design coords simulation */ diff --git a/thirdparty/harfbuzz/src/hb-font.hh b/thirdparty/harfbuzz/src/hb-font.hh index bb402e23eb..6942d99c70 100644 --- a/thirdparty/harfbuzz/src/hb-font.hh +++ b/thirdparty/harfbuzz/src/hb-font.hh @@ -206,14 +206,14 @@ struct hb_font_t hb_bool_t get_font_h_extents (hb_font_extents_t *extents) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return klass->get.f.font_h_extents (this, user_data, extents, !klass->user_data ? nullptr : klass->user_data->font_h_extents); } hb_bool_t get_font_v_extents (hb_font_extents_t *extents) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return klass->get.f.font_v_extents (this, user_data, extents, !klass->user_data ? nullptr : klass->user_data->font_v_extents); @@ -342,7 +342,7 @@ struct hb_font_t hb_bool_t get_glyph_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return klass->get.f.glyph_extents (this, user_data, glyph, extents, diff --git a/thirdparty/harfbuzz/src/hb-ft.cc b/thirdparty/harfbuzz/src/hb-ft.cc index bcc1dd080f..3892dedc13 100644 --- a/thirdparty/harfbuzz/src/hb-ft.cc +++ b/thirdparty/harfbuzz/src/hb-ft.cc @@ -89,7 +89,7 @@ struct hb_ft_font_t bool unref; /* Whether to destroy ft_face when done. */ bool transform; /* Whether to apply FT_Face's transform. */ - mutable hb_mutex_t lock; + mutable hb_mutex_t lock; /* Protects members below. */ FT_Face ft_face; mutable unsigned cached_serial; mutable hb_ft_advance_cache_t advance_cache; @@ -732,16 +732,18 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, static int _hb_ft_move_to (const FT_Vector *to, - hb_draw_session_t *drawing) + void *arg) { + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; drawing->move_to (to->x, to->y); return FT_Err_Ok; } static int _hb_ft_line_to (const FT_Vector *to, - hb_draw_session_t *drawing) + void *arg) { + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; drawing->line_to (to->x, to->y); return FT_Err_Ok; } @@ -749,8 +751,9 @@ _hb_ft_line_to (const FT_Vector *to, static int _hb_ft_conic_to (const FT_Vector *control, const FT_Vector *to, - hb_draw_session_t *drawing) + void *arg) { + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; drawing->quadratic_to (control->x, control->y, to->x, to->y); return FT_Err_Ok; @@ -760,8 +763,9 @@ static int _hb_ft_cubic_to (const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, - hb_draw_session_t *drawing) + void *arg) { + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; drawing->cubic_to (control1->x, control1->y, control2->x, control2->y, to->x, to->y); @@ -787,10 +791,10 @@ hb_ft_get_glyph_shape (hb_font_t *font HB_UNUSED, return; const FT_Outline_Funcs outline_funcs = { - (FT_Outline_MoveToFunc) _hb_ft_move_to, - (FT_Outline_LineToFunc) _hb_ft_line_to, - (FT_Outline_ConicToFunc) _hb_ft_conic_to, - (FT_Outline_CubicToFunc) _hb_ft_cubic_to, + _hb_ft_move_to, + _hb_ft_line_to, + _hb_ft_conic_to, + _hb_ft_cubic_to, 0, /* shift */ 0, /* delta */ }; @@ -975,8 +979,9 @@ hb_ft_face_create_referenced (FT_Face ft_face) } static void -hb_ft_face_finalize (FT_Face ft_face) +hb_ft_face_finalize (void *arg) { + FT_Face ft_face = (FT_Face) arg; hb_face_destroy ((hb_face_t *) ft_face->generic.data); } @@ -1008,7 +1013,7 @@ hb_ft_face_create_cached (FT_Face ft_face) ft_face->generic.finalizer (ft_face); ft_face->generic.data = hb_ft_face_create (ft_face, nullptr); - ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize; + ft_face->generic.finalizer = hb_ft_face_finalize; } return hb_face_reference ((hb_face_t *) ft_face->generic.data); @@ -1217,8 +1222,9 @@ get_ft_library () } static void -_release_blob (FT_Face ft_face) +_release_blob (void *arg) { + FT_Face ft_face = (FT_Face) arg; hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); } @@ -1271,7 +1277,7 @@ hb_ft_font_set_funcs (hb_font_t *font) ft_face->generic.data = blob; - ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob; + ft_face->generic.finalizer = _release_blob; _hb_ft_font_set_funcs (font, ft_face, true); hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); diff --git a/thirdparty/harfbuzz/src/hb-glib.cc b/thirdparty/harfbuzz/src/hb-glib.cc index 8ddc7ebad8..1da81696e7 100644 --- a/thirdparty/harfbuzz/src/hb-glib.cc +++ b/thirdparty/harfbuzz/src/hb-glib.cc @@ -129,32 +129,9 @@ hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, { #if GLIB_CHECK_VERSION(2,29,12) return g_unichar_compose (a, b, ab); +#else + return false; #endif - - /* We don't ifdef-out the fallback code such that compiler always - * sees it and makes sure it's compilable. */ - - gchar utf8[12]; - gchar *normalized; - int len; - hb_bool_t ret; - - len = g_unichar_to_utf8 (a, utf8); - len += g_unichar_to_utf8 (b, utf8 + len); - normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFC); - len = g_utf8_strlen (normalized, -1); - if (unlikely (!len)) - return false; - - if (len == 1) { - *ab = g_utf8_get_char (normalized); - ret = true; - } else { - ret = false; - } - - g_free (normalized); - return ret; } static hb_bool_t @@ -166,55 +143,9 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, { #if GLIB_CHECK_VERSION(2,29,12) return g_unichar_decompose (ab, a, b); +#else + return false; #endif - - /* We don't ifdef-out the fallback code such that compiler always - * sees it and makes sure it's compilable. */ - - gchar utf8[6]; - gchar *normalized; - int len; - hb_bool_t ret; - - len = g_unichar_to_utf8 (ab, utf8); - normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFD); - len = g_utf8_strlen (normalized, -1); - if (unlikely (!len)) - return false; - - if (len == 1) { - *a = g_utf8_get_char (normalized); - *b = 0; - ret = *a != ab; - } else if (len == 2) { - *a = g_utf8_get_char (normalized); - *b = g_utf8_get_char (g_utf8_next_char (normalized)); - /* Here's the ugly part: if ab decomposes to a single character and - * that character decomposes again, we have to detect that and undo - * the second part :-(. */ - gchar *recomposed = g_utf8_normalize (normalized, -1, G_NORMALIZE_NFC); - hb_codepoint_t c = g_utf8_get_char (recomposed); - if (c != ab && c != *a) { - *a = c; - *b = 0; - } - g_free (recomposed); - ret = true; - } else { - /* If decomposed to more than two characters, take the last one, - * and recompose the rest to get the first component. */ - gchar *end = g_utf8_offset_to_pointer (normalized, len - 1); - gchar *recomposed; - *b = g_utf8_get_char (end); - recomposed = g_utf8_normalize (normalized, end - normalized, G_NORMALIZE_NFC); - /* We expect that recomposed has exactly one character now. */ - *a = g_utf8_get_char (recomposed); - g_free (recomposed); - ret = true; - } - - g_free (normalized); - return ret; } diff --git a/thirdparty/harfbuzz/src/hb-graphite2.cc b/thirdparty/harfbuzz/src/hb-graphite2.cc index 4d0e687c75..9e068f8d84 100644 --- a/thirdparty/harfbuzz/src/hb-graphite2.cc +++ b/thirdparty/harfbuzz/src/hb-graphite2.cc @@ -318,7 +318,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, #undef ALLOCATE_ARRAY - memset (clusters, 0, sizeof (clusters[0]) * buffer->len); + hb_memset (clusters, 0, sizeof (clusters[0]) * buffer->len); hb_codepoint_t *pg = gids; clusters[0].cluster = buffer->info[0].cluster; diff --git a/thirdparty/harfbuzz/src/hb-iter.hh b/thirdparty/harfbuzz/src/hb-iter.hh index 1a3ab43de0..b57f37b132 100644 --- a/thirdparty/harfbuzz/src/hb-iter.hh +++ b/thirdparty/harfbuzz/src/hb-iter.hh @@ -73,8 +73,10 @@ struct hb_iter_t /* Operators. */ iter_t iter () const { return *thiz(); } iter_t operator + () const { return *thiz(); } - iter_t begin () const { return *thiz(); } - iter_t end () const { return thiz()->__end__ (); } + iter_t _begin () const { return *thiz(); } + iter_t begin () const { return _begin (); } + iter_t _end () const { return thiz()->__end__ (); } + iter_t end () const { return _end (); } explicit operator bool () const { return thiz()->__more__ (); } unsigned len () const { return thiz()->__len__ (); } /* The following can only be enabled if item_t is reference type. Otherwise @@ -118,7 +120,9 @@ struct hb_iter_t #define HB_ITER_USING(Name) \ using item_t = typename Name::item_t; \ + using Name::_begin; \ using Name::begin; \ + using Name::_end; \ using Name::end; \ using Name::get_item_size; \ using Name::is_iterator; \ @@ -377,7 +381,7 @@ struct hb_map_iter_t : void __forward__ (unsigned n) { it += n; } void __prev__ () { --it; } void __rewind__ (unsigned n) { it -= n; } - hb_map_iter_t __end__ () const { return hb_map_iter_t (it.end (), f); } + hb_map_iter_t __end__ () const { return hb_map_iter_t (it._end (), f); } bool operator != (const hb_map_iter_t& o) const { return it != o.it; } @@ -440,7 +444,7 @@ struct hb_filter_iter_t : bool __more__ () const { return bool (it); } void __next__ () { do ++it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } void __prev__ () { do --it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } - hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it.end (), p, f); } + hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it._end (), p, f); } bool operator != (const hb_filter_iter_t& o) const { return it != o.it; } @@ -553,7 +557,7 @@ struct hb_zip_iter_t : void __forward__ (unsigned n) { a += n; b += n; } void __prev__ () { --a; --b; } void __rewind__ (unsigned n) { a -= n; b -= n; } - hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a.end (), b.end ()); } + hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a._end (), b._end ()); } /* Note, we should stop if ANY of the iters reaches end. As such two compare * unequal if both items are unequal, NOT if either is unequal. */ bool operator != (const hb_zip_iter_t& o) const @@ -637,7 +641,7 @@ struct hb_concat_iter_t : } } - hb_concat_iter_t __end__ () const { return hb_concat_iter_t (a.end (), b.end ()); } + hb_concat_iter_t __end__ () const { return hb_concat_iter_t (a._end (), b._end ()); } bool operator != (const hb_concat_iter_t& o) const { return a != o.a diff --git a/thirdparty/harfbuzz/src/hb-machinery.hh b/thirdparty/harfbuzz/src/hb-machinery.hh index 2571f22e15..b555739cfb 100644 --- a/thirdparty/harfbuzz/src/hb-machinery.hh +++ b/thirdparty/harfbuzz/src/hb-machinery.hh @@ -136,6 +136,13 @@ static inline Type& StructAfter(TObject &X) /* * Lazy loaders. + * + * The lazy-loaders are thread-safe pointer-like objects that create their + * instead on-demand. They also support access to a "data" object that is + * necessary for creating their instance. The data object, if specified, + * is accessed via pointer math, located at a location before the position + * of the loader itself. This avoids having to store a pointer to data + * for every lazy-loader. Multiple lazy-loaders can access the same data. */ template <typename Data, unsigned int WheresData> @@ -228,7 +235,8 @@ struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData> bool cmpexch (Stored *current, Stored *value) const { - /* This *must* be called when there are no other threads accessing. */ + /* This function can only be safely called directly if no + * other thread is accessing. */ return this->instance.cmpexch (current, value); } @@ -261,7 +269,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData> hb_free (p); } -// private: + private: /* Must only have one pointer. */ hb_atomic_ptr_t<Stored *> instance; }; @@ -283,7 +291,7 @@ struct hb_table_lazy_loader_t : hb_lazy_loader_t<T, { auto c = hb_sanitize_context_t (); if (core) - c.set_num_glyphs (0); // So we don't recurse ad infinitum... + c.set_num_glyphs (0); // So we don't recurse ad infinitum, or doesn't need num_glyphs return c.reference_table<T> (face); } static void destroy (hb_blob_t *p) { hb_blob_destroy (p); } diff --git a/thirdparty/harfbuzz/src/hb-map.hh b/thirdparty/harfbuzz/src/hb-map.hh index 8302e3f8c7..bfb1b3f768 100644 --- a/thirdparty/harfbuzz/src/hb-map.hh +++ b/thirdparty/harfbuzz/src/hb-map.hh @@ -43,9 +43,9 @@ struct hb_hashmap_t hb_hashmap_t () { init (); } ~hb_hashmap_t () { fini (); } - hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (population); hb_copy (o, *this); } + hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (o.population); hb_copy (o, *this); } hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); } - hb_hashmap_t& operator= (const hb_hashmap_t& o) { resize (population); hb_copy (o, *this); return *this; } + hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); resize (o.population); hb_copy (o, *this); return *this; } hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; } hb_hashmap_t (std::initializer_list<hb_pair_t<K, V>> lst) : hb_hashmap_t () @@ -71,6 +71,11 @@ struct hb_hashmap_t uint32_t is_tombstone_ : 1; V value; + item_t () : key (), + hash (0), + is_used_ (false), is_tombstone_ (false), + value () {} + bool is_used () const { return is_used_; } void set_used (bool is_used) { is_used_ = is_used; } bool is_tombstone () const { return is_tombstone_; } @@ -88,17 +93,8 @@ struct hb_hashmap_t return minus_1; }; - void clear () - { - new (std::addressof (key)) K (); - new (std::addressof (value)) V (); - hash = 0; - is_used_ = false; - is_tombstone_ = false; - } - - bool operator == (const K &o) { return hb_deref (key) == hb_deref (o); } - bool operator == (const item_t &o) { return *this == o.key; } + bool operator == (const K &o) const { return hb_deref (key) == hb_deref (o); } + bool operator == (const item_t &o) const { return *this == o.key; } hb_pair_t<K, V> get_pair() const { return hb_pair_t<K, V> (key, value); } hb_pair_t<const K &, const V &> get_pair_ref() const { return hb_pair_t<const K &, const V &> (key, value); } @@ -107,8 +103,8 @@ struct hb_hashmap_t }; hb_object_header_t header; - bool successful; /* Allocations successful */ - unsigned int population; /* Not including tombstones. */ + unsigned int successful : 1; /* Allocations successful */ + unsigned int population : 31; /* Not including tombstones. */ unsigned int occupancy; /* Including tombstones. */ unsigned int mask; unsigned int prime; @@ -118,7 +114,10 @@ struct hb_hashmap_t { if (unlikely (!a.successful || !b.successful)) return; - hb_swap (a.population, b.population); + unsigned tmp = a.population; + a.population = b.population; + b.population = tmp; + //hb_swap (a.population, b.population); hb_swap (a.occupancy, b.occupancy); hb_swap (a.mask, b.mask); hb_swap (a.prime, b.prime); @@ -160,7 +159,9 @@ struct hb_hashmap_t { if (unlikely (!successful)) return false; - unsigned int power = hb_bit_storage (hb_max (population, new_population) * 2 + 8); + if (new_population != 0 && (new_population + new_population / 2) < mask) return true; + + unsigned int power = hb_bit_storage (hb_max ((unsigned) population, new_population) * 2 + 8); unsigned int new_size = 1u << power; item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t)); if (unlikely (!new_items)) @@ -169,9 +170,9 @@ struct hb_hashmap_t return false; } for (auto &_ : hb_iter (new_items, new_size)) - _.clear (); + new (&_) item_t (); - unsigned int old_size = mask + 1; + unsigned int old_size = size (); item_t *old_items = items; /* Switch to new, empty, array. */ @@ -181,47 +182,82 @@ struct hb_hashmap_t items = new_items; /* Insert back old items. */ - if (old_items) - for (unsigned int i = 0; i < old_size; i++) + for (unsigned int i = 0; i < old_size; i++) + { + if (old_items[i].is_real ()) { - if (old_items[i].is_real ()) - { - set_with_hash (old_items[i].key, - old_items[i].hash, - std::move (old_items[i].value)); - } - old_items[i].~item_t (); + set_with_hash (std::move (old_items[i].key), + old_items[i].hash, + std::move (old_items[i].value)); } + old_items[i].~item_t (); + } hb_free (old_items); return true; } + template <typename KK, typename VV> + bool set_with_hash (KK&& key, uint32_t hash, VV&& value, bool is_delete=false) + { + if (unlikely (!successful)) return false; + if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false; + item_t &item = item_for_hash (key, hash); + + if (is_delete && !(item == key)) + return true; /* Trying to delete non-existent key. */ + + if (item.is_used ()) + { + occupancy--; + if (!item.is_tombstone ()) + population--; + } + + item.key = std::forward<KK> (key); + item.value = std::forward<VV> (value); + item.hash = hash; + item.set_used (true); + item.set_tombstone (is_delete); + + occupancy++; + if (!is_delete) + population++; + + return true; + } + + template <typename VV> + bool set (const K &key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward<VV> (value)); } template <typename VV> - bool set (K key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward<VV> (value)); } + bool set (K &&key, VV&& value) { return set_with_hash (std::move (key), hb_hash (key), std::forward<VV> (value)); } - const V& get (K key) const + const V& get_with_hash (const K &key, uint32_t hash) const + { + if (unlikely (!items)) return item_t::default_value (); + auto &item = item_for_hash (key, hash); + return item.is_real () && item == key ? item.value : item_t::default_value (); + } + const V& get (const K &key) const { if (unlikely (!items)) return item_t::default_value (); - unsigned int i = bucket_for (key); - return items[i].is_real () && items[i] == key ? items[i].value : item_t::default_value (); + return get_with_hash (key, hb_hash (key)); } - void del (K key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); } + void del (const K &key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); } /* Has interface. */ - typedef const V& value_t; - value_t operator [] (K k) const { return get (k); } + const V& operator [] (K k) const { return get (k); } template <typename VV=V> bool has (K key, VV **vp = nullptr) const { if (unlikely (!items)) return false; - unsigned int i = bucket_for (key); - if (items[i].is_real () && items[i] == key) + auto &item = item_for_hash (key, hb_hash (key)); + if (item.is_real () && item == key) { - if (vp) *vp = &items[i].value; + if (vp) *vp = std::addressof (item.value); return true; } else @@ -230,13 +266,18 @@ struct hb_hashmap_t /* Projection. */ V operator () (K k) const { return get (k); } + unsigned size () const { return mask ? mask + 1 : 0; } + void clear () { if (unlikely (!successful)) return; - if (items) - for (auto &_ : hb_iter (items, mask + 1)) - _.clear (); + for (auto &_ : hb_iter (items, size ())) + { + /* Reconstruct items. */ + _.~item_t (); + new (&_) item_t (); + } population = occupancy = 0; } @@ -246,11 +287,10 @@ struct hb_hashmap_t uint32_t hash () const { - uint32_t h = 0; - for (const auto &item : + hb_array (items, mask ? mask + 1 : 0) - | hb_filter (&item_t::is_real)) - h ^= item.total_hash (); - return h; + return + + iter_items () + | hb_reduce ([] (uint32_t h, const item_t &_) { return h ^ _.total_hash (); }, (uint32_t) 0u) + ; } bool is_equal (const hb_hashmap_t &other) const @@ -258,7 +298,7 @@ struct hb_hashmap_t if (population != other.population) return false; for (auto pair : iter ()) - if (get (pair.first) != pair.second) + if (other.get (pair.first) != pair.second) return false; return true; @@ -271,87 +311,54 @@ struct hb_hashmap_t /* * Iterator */ - auto iter () const HB_AUTO_RETURN + + auto iter_items () const HB_AUTO_RETURN ( - + hb_array (items, mask ? mask + 1 : 0) + + hb_iter (items, size ()) | hb_filter (&item_t::is_real) - | hb_map (&item_t::get_pair) ) auto iter_ref () const HB_AUTO_RETURN ( - + hb_array (items, mask ? mask + 1 : 0) - | hb_filter (&item_t::is_real) + + iter_items () | hb_map (&item_t::get_pair_ref) ) - auto keys () const HB_AUTO_RETURN + auto iter () const HB_AUTO_RETURN ( - + hb_array (items, mask ? mask + 1 : 0) - | hb_filter (&item_t::is_real) - | hb_map (&item_t::key) - | hb_map (hb_ridentity) + + iter_items () + | hb_map (&item_t::get_pair) ) auto keys_ref () const HB_AUTO_RETURN ( - + hb_array (items, mask ? mask + 1 : 0) - | hb_filter (&item_t::is_real) + + iter_items () | hb_map (&item_t::key) ) - auto values () const HB_AUTO_RETURN + auto keys () const HB_AUTO_RETURN ( - + hb_array (items, mask ? mask + 1 : 0) - | hb_filter (&item_t::is_real) - | hb_map (&item_t::value) + + keys_ref () | hb_map (hb_ridentity) ) auto values_ref () const HB_AUTO_RETURN ( - + hb_array (items, mask ? mask + 1 : 0) - | hb_filter (&item_t::is_real) + + iter_items () | hb_map (&item_t::value) ) + auto values () const HB_AUTO_RETURN + ( + + values_ref () + | hb_map (hb_ridentity) + ) /* Sink interface. */ hb_hashmap_t& operator << (const hb_pair_t<K, V>& v) { set (v.first, v.second); return *this; } - - protected: - - template <typename VV> - bool set_with_hash (K key, uint32_t hash, VV&& value, bool is_delete=false) - { - if (unlikely (!successful)) return false; - if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false; - unsigned int i = bucket_for_hash (key, hash); - - if (is_delete && items[i].key != key) - return true; /* Trying to delete non-existent key. */ - - if (items[i].is_used ()) - { - occupancy--; - if (!items[i].is_tombstone ()) - population--; - } - - items[i].key = key; - items[i].value = std::forward<VV> (value); - items[i].hash = hash; - items[i].set_used (true); - items[i].set_tombstone (is_delete); - - occupancy++; - if (!is_delete) - population++; - - return true; - } - - unsigned int bucket_for (const K &key) const - { - return bucket_for_hash (key, hb_hash (key)); - } - - unsigned int bucket_for_hash (const K &key, uint32_t hash) const + hb_hashmap_t& operator << (const hb_pair_t<K, V&&>& v) + { set (v.first, std::move (v.second)); return *this; } + hb_hashmap_t& operator << (const hb_pair_t<K&&, V>& v) + { set (std::move (v.first), v.second); return *this; } + hb_hashmap_t& operator << (const hb_pair_t<K&&, V&&>& v) + { set (std::move (v.first), std::move (v.second)); return *this; } + + item_t& item_for_hash (const K &key, uint32_t hash) const { hash &= 0x3FFFFFFF; // We only store lower 30bit of hash unsigned int i = hash % prime; @@ -360,12 +367,12 @@ struct hb_hashmap_t while (items[i].is_used ()) { if (items[i].hash == hash && items[i] == key) - return i; + return items[i]; if (tombstone == (unsigned) -1 && items[i].is_tombstone ()) tombstone = i; i = (i + ++step) & mask; } - return tombstone == (unsigned) -1 ? i : tombstone; + return items[tombstone == (unsigned) -1 ? i : tombstone]; } static unsigned int prime_for (unsigned int shift) diff --git a/thirdparty/harfbuzz/src/hb-meta.hh b/thirdparty/harfbuzz/src/hb-meta.hh index 52a6791e31..31aa7fa6f1 100644 --- a/thirdparty/harfbuzz/src/hb-meta.hh +++ b/thirdparty/harfbuzz/src/hb-meta.hh @@ -112,8 +112,7 @@ template <typename T> auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_ident template <typename T> using hb_add_pointer = decltype (_hb_try_add_pointer<T> (hb_prioritize)); -/* TODO Add feature-parity to std::decay. */ -template <typename T> using hb_decay = hb_remove_const<hb_remove_reference<T>>; +template <typename T> using hb_decay = typename std::decay<T>::type; #define hb_is_convertible(From,To) std::is_convertible<From, To>::value diff --git a/thirdparty/harfbuzz/src/hb-ms-feature-ranges.hh b/thirdparty/harfbuzz/src/hb-ms-feature-ranges.hh index d40fdeaa82..46a20c91ea 100644 --- a/thirdparty/harfbuzz/src/hb-ms-feature-ranges.hh +++ b/thirdparty/harfbuzz/src/hb-ms-feature-ranges.hh @@ -166,7 +166,7 @@ hb_ms_setup_features (const hb_feature_t *features, { auto *feature = active_features.lsearch (event->feature); if (feature) - active_features.remove (feature - active_features.arrayZ); + active_features.remove_ordered (feature - active_features.arrayZ); } } diff --git a/thirdparty/harfbuzz/src/hb-multimap.hh b/thirdparty/harfbuzz/src/hb-multimap.hh new file mode 100644 index 0000000000..f0f95917aa --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-multimap.hh @@ -0,0 +1,92 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_MULTIMAP_HH +#define HB_MULTIMAP_HH + +#include "hb.hh" +#include "hb-map.hh" +#include "hb-vector.hh" + + +/* + * hb_multimap_t + */ + +struct hb_multimap_t +{ + void add (hb_codepoint_t k, hb_codepoint_t v) + { + hb_codepoint_t *i; + if (multiples_indices.has (k, &i)) + { + multiples_values[*i].push (v); + return; + } + + hb_codepoint_t *old_v; + if (singulars.has (k, &old_v)) + { + hb_codepoint_t old = *old_v; + singulars.del (k); + + multiples_indices.set (k, multiples_values.length); + auto *vec = multiples_values.push (); + + vec->push (old); + vec->push (v); + + return; + } + + singulars.set (k, v); + } + + hb_array_t<const hb_codepoint_t> get (hb_codepoint_t k) const + { + hb_codepoint_t *v; + if (singulars.has (k, &v)) + return hb_array (v, 1); + + hb_codepoint_t *i; + if (multiples_indices.has (k, &i)) + return multiples_values[*i].as_array (); + + return hb_array_t<hb_codepoint_t> (); + } + + bool in_error () const + { + return singulars.in_error () || multiples_indices.in_error () || multiples_values.in_error (); + } + + protected: + hb_map_t singulars; + hb_map_t multiples_indices; + hb_vector_t<hb_vector_t<hb_codepoint_t>> multiples_values; +}; + + + +#endif /* HB_MULTIMAP_HH */ diff --git a/thirdparty/harfbuzz/src/hb-mutex.hh b/thirdparty/harfbuzz/src/hb-mutex.hh index 6914b22450..053f9ddcc4 100644 --- a/thirdparty/harfbuzz/src/hb-mutex.hh +++ b/thirdparty/harfbuzz/src/hb-mutex.hh @@ -108,10 +108,11 @@ struct hb_mutex_t struct hb_lock_t { - hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); } - ~hb_lock_t () { mutex.unlock (); } + hb_lock_t (hb_mutex_t &mutex_) : mutex (&mutex_) { mutex->lock (); } + hb_lock_t (hb_mutex_t *mutex_) : mutex (mutex_) { if (mutex) mutex->lock (); } + ~hb_lock_t () { if (mutex) mutex->unlock (); } private: - hb_mutex_t &mutex; + hb_mutex_t *mutex; }; diff --git a/thirdparty/harfbuzz/src/hb-number-parser.hh b/thirdparty/harfbuzz/src/hb-number-parser.hh index 1a9dbba6dd..ec68c3a728 100644 --- a/thirdparty/harfbuzz/src/hb-number-parser.hh +++ b/thirdparty/harfbuzz/src/hb-number-parser.hh @@ -31,7 +31,7 @@ #include "hb.hh" -#line 35 "hb-number-parser.hh" +#line 32 "hb-number-parser.hh" static const unsigned char _double_parser_trans_keys[] = { 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, 46u, 101u, 0 @@ -135,12 +135,12 @@ strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) int cs; -#line 139 "hb-number-parser.hh" +#line 132 "hb-number-parser.hh" { cs = double_parser_start; } -#line 144 "hb-number-parser.hh" +#line 135 "hb-number-parser.hh" { int _slen; int _trans; @@ -198,7 +198,7 @@ _resume: exp_overflow = true; } break; -#line 202 "hb-number-parser.hh" +#line 187 "hb-number-parser.hh" } _again: diff --git a/thirdparty/harfbuzz/src/hb-object.hh b/thirdparty/harfbuzz/src/hb-object.hh index 9876c2923c..a23c25f7ca 100644 --- a/thirdparty/harfbuzz/src/hb-object.hh +++ b/thirdparty/harfbuzz/src/hb-object.hh @@ -80,7 +80,7 @@ struct hb_lockable_set_t if (item) { item_t old = *item; - *item = items[items.length - 1]; + *item = std::move (items.tail ()); items.pop (); l.unlock (); old.fini (); @@ -123,7 +123,7 @@ struct hb_lockable_set_t l.lock (); while (items.length) { - item_t old = items[items.length - 1]; + item_t old = items.tail (); items.pop (); l.unlock (); old.fini (); diff --git a/thirdparty/harfbuzz/src/hb-open-file.hh b/thirdparty/harfbuzz/src/hb-open-file.hh index 6eee5827c1..13570a46e0 100644 --- a/thirdparty/harfbuzz/src/hb-open-file.hh +++ b/thirdparty/harfbuzz/src/hb-open-file.hh @@ -90,7 +90,7 @@ typedef struct OpenTypeOffsetTable { if (table_count) { - + tables.sub_array (start_offset, table_count) + + tables.as_array ().sub_array (start_offset, table_count) | hb_map (&TableRecord::tag) | hb_sink (hb_array (table_tags, *table_count)) ; @@ -158,7 +158,7 @@ typedef struct OpenTypeOffsetTable return_trace (false); if (likely (len)) - memcpy (start, blob->data, len); + hb_memcpy (start, blob->data, len); /* 4-byte alignment. */ c->align (4); diff --git a/thirdparty/harfbuzz/src/hb-open-type.hh b/thirdparty/harfbuzz/src/hb-open-type.hh index e66f451820..290799127a 100644 --- a/thirdparty/harfbuzz/src/hb-open-type.hh +++ b/thirdparty/harfbuzz/src/hb-open-type.hh @@ -148,7 +148,7 @@ struct HBFixed : Type static_assert (Type::static_size * 8 > fraction_bits, ""); HBFixed& operator = (typename Type::type i ) { Type::operator= (i); return *this; } - float to_float () const { return ((int32_t) Type::v) / shift; } + float to_float (float offset = 0) const { return ((int32_t) Type::v + offset) / shift; } void set_float (float f) { Type::v = roundf (f * shift); } public: DEFINE_SIZE_STATIC (Type::static_size); @@ -157,6 +157,9 @@ struct HBFixed : Type /* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ using F2DOT14 = HBFixed<HBINT16, 14>; +/* 16-bit signed fixed number with the low 12 bits of fraction (4.12). */ +using F4DOT12 = HBFixed<HBINT16, 12>; + /* 32-bit signed fixed-point number (16.16). */ using F16DOT16 = HBFixed<HBINT32, 16>; @@ -209,6 +212,12 @@ typedef Index NameID; struct VarIdx : HBUINT32 { static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu; + static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, ""); + static uint32_t add (uint32_t i, unsigned short v) + { + if (i == NO_VARIATION) return i; + return i + v; + } VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } }; DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx); @@ -493,10 +502,10 @@ struct UnsizedArrayOf void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1) { as_array (len).qsort (start, end); } - bool serialize (hb_serialize_context_t *c, unsigned int items_len) + bool serialize (hb_serialize_context_t *c, unsigned int items_len, bool clear = true) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend (this, items_len))) return_trace (false); + if (unlikely (!c->extend_size (this, get_size (items_len), clear))) return_trace (false); return_trace (true); } template <typename Iterator, @@ -504,8 +513,8 @@ struct UnsizedArrayOf bool serialize (hb_serialize_context_t *c, Iterator items) { TRACE_SERIALIZE (this); - unsigned count = items.len (); - if (unlikely (!serialize (c, count))) return_trace (false); + unsigned count = hb_len (items); + if (unlikely (!serialize (c, count, false))) return_trace (false); /* TODO Umm. Just exhaust the iterator instead? Being extra * cautious right now.. */ for (unsigned i = 0; i < count; i++, ++items) @@ -646,14 +655,9 @@ struct ArrayOf operator iter_t () const { return iter (); } operator writer_t () { return writer (); } - hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count); } - hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count); } - hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count); } - hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count); } + /* Faster range-based for loop. */ + const Type *begin () const { return arrayZ; } + const Type *end () const { return arrayZ + len; } template <typename T> Type &lsearch (const T &x, Type ¬_found = Crap (Type)) @@ -667,15 +671,15 @@ struct ArrayOf unsigned int to_store = (unsigned int) -1) const { return as_array ().lfind (x, i, not_found, to_store); } - void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1) - { as_array ().qsort (start, end); } + void qsort () + { as_array ().qsort (); } - HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned items_len) + HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned items_len, bool clear = true) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (this))) return_trace (false); c->check_assign (len, items_len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); - if (unlikely (!c->extend (this))) return_trace (false); + if (unlikely (!c->extend_size (this, get_size (), clear))) return_trace (false); return_trace (true); } template <typename Iterator, @@ -683,8 +687,8 @@ struct ArrayOf HB_NODISCARD bool serialize (hb_serialize_context_t *c, Iterator items) { TRACE_SERIALIZE (this); - unsigned count = items.len (); - if (unlikely (!serialize (c, count))) return_trace (false); + unsigned count = hb_len (items); + if (unlikely (!serialize (c, count, false))) return_trace (false); /* TODO Umm. Just exhaust the iterator instead? Being extra * cautious right now.. */ for (unsigned i = 0; i < count; i++, ++items) @@ -828,21 +832,25 @@ struct HeadlessArrayOf operator iter_t () const { return iter (); } operator writer_t () { return writer (); } - bool serialize (hb_serialize_context_t *c, unsigned int items_len) + /* Faster range-based for loop. */ + const Type *begin () const { return arrayZ; } + const Type *end () const { return arrayZ + get_length (); } + + HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned int items_len, bool clear = true) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (this))) return_trace (false); c->check_assign (lenP1, items_len + 1, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); - if (unlikely (!c->extend (this))) return_trace (false); + if (unlikely (!c->extend_size (this, get_size (), clear))) return_trace (false); return_trace (true); } template <typename Iterator, hb_requires (hb_is_source_of (Iterator, Type))> - bool serialize (hb_serialize_context_t *c, Iterator items) + HB_NODISCARD bool serialize (hb_serialize_context_t *c, Iterator items) { TRACE_SERIALIZE (this); - unsigned count = items.len (); - if (unlikely (!serialize (c, count))) return_trace (false); + unsigned count = hb_len (items); + if (unlikely (!serialize (c, count, false))) return_trace (false); /* TODO Umm. Just exhaust the iterator instead? Being extra * cautious right now.. */ for (unsigned i = 0; i < count; i++, ++items) @@ -944,14 +952,9 @@ struct SortedArrayOf : ArrayOf<Type, LenType> operator iter_t () const { return iter (); } operator writer_t () { return writer (); } - hb_sorted_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count); } - hb_sorted_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count); } - hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count); } - hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count); } + /* Faster range-based for loop. */ + const Type *begin () const { return this->arrayZ; } + const Type *end () const { return this->arrayZ + this->len; } bool serialize (hb_serialize_context_t *c, unsigned int items_len) { diff --git a/thirdparty/harfbuzz/src/hb-ot-cff-common.hh b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh index ae3b83a256..f22824fc69 100644 --- a/thirdparty/harfbuzz/src/hb-ot-cff-common.hh +++ b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh @@ -66,95 +66,25 @@ struct CFFIndex { TRACE_SERIALIZE (this); unsigned int size = get_size (); - CFFIndex *out = c->allocate_size<CFFIndex> (size); + CFFIndex *out = c->allocate_size<CFFIndex> (size, false); if (likely (out)) - memcpy (out, this, size); + hb_memcpy (out, this, size); return_trace (out); } + template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> bool serialize (hb_serialize_context_t *c, - unsigned int offSize_, - const byte_str_array_t &byteArray) + const Iterable &iterable) { TRACE_SERIALIZE (this); - - if (byteArray.length == 0) - { - COUNT *dest = c->allocate_min<COUNT> (); - if (unlikely (!dest)) return_trace (false); - *dest = 0; - return_trace (true); - } - - /* serialize CFFIndex header */ - if (unlikely (!c->extend_min (this))) return_trace (false); - this->count = byteArray.length; - this->offSize = offSize_; - if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (byteArray.length + 1)))) - return_trace (false); - - /* serialize indices */ - unsigned int offset = 1; - unsigned int i = 0; - for (; i < byteArray.length; i++) - { - set_offset_at (i, offset); - offset += byteArray[i].get_size (); - } - set_offset_at (i, offset); - - /* serialize data */ - for (unsigned int i = 0; i < byteArray.length; i++) - { - const hb_ubytes_t &bs = byteArray[i]; - unsigned char *dest = c->allocate_size<unsigned char> (bs.length); - if (unlikely (!dest)) return_trace (false); - memcpy (dest, &bs[0], bs.length); - } - - return_trace (true); - } - - bool serialize (hb_serialize_context_t *c, - unsigned int offSize_, - const str_buff_vec_t &buffArray) - { - byte_str_array_t byteArray; - byteArray.init (); - byteArray.resize (buffArray.length); - for (unsigned int i = 0; i < byteArray.length; i++) - byteArray[i] = hb_ubytes_t (buffArray[i].arrayZ, buffArray[i].length); - bool result = this->serialize (c, offSize_, byteArray); - byteArray.fini (); - return result; - } - - template <typename Iterator, - hb_requires (hb_is_iterator (Iterator))> - bool serialize (hb_serialize_context_t *c, - Iterator it) - { - TRACE_SERIALIZE (this); - serialize_header(c, + it | hb_map ([] (const hb_ubytes_t &_) { return _.length; })); + auto it = hb_iter (iterable); + serialize_header(c, + it | hb_map (hb_iter) | hb_map (hb_len)); for (const auto &_ : +it) - _.copy (c); + hb_iter (_).copy (c); return_trace (true); } - bool serialize (hb_serialize_context_t *c, - const byte_str_array_t &byteArray) - { return serialize (c, + hb_iter (byteArray)); } - - bool serialize (hb_serialize_context_t *c, - const str_buff_vec_t &buffArray) - { - auto it = - + hb_iter (buffArray) - | hb_map ([] (const str_buff_t &_) { return hb_ubytes_t (_.arrayZ, _.length); }) - ; - return serialize (c, it); - } - template <typename Iterator, hb_requires (hb_is_iterator (Iterator))> bool serialize_header (hb_serialize_context_t *c, @@ -171,7 +101,7 @@ struct CFFIndex if (!this->count) return_trace (true); if (unlikely (!c->extend (this->offSize))) return_trace (false); this->offSize = off_size; - if (unlikely (!c->allocate_size<HBUINT8> (off_size * (this->count + 1)))) + if (unlikely (!c->allocate_size<HBUINT8> (off_size * (this->count + 1), false))) return_trace (false); /* serialize indices */ @@ -179,14 +109,27 @@ struct CFFIndex unsigned int i = 0; for (unsigned _ : +it) { - CFFIndex<COUNT>::set_offset_at (i++, offset); + set_offset_at (i++, offset); offset += _; } - CFFIndex<COUNT>::set_offset_at (i, offset); + set_offset_at (i, offset); return_trace (true); } + template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> + static unsigned total_size (const Iterable &iterable) + { + auto it = + hb_iter (iterable) | hb_map (hb_iter) | hb_map (hb_len); + if (!it) return 0; + + unsigned total = + it | hb_reduce (hb_add, 0); + unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8; + + return min_size + HBUINT8::static_size + (hb_len (it) + 1) * off_size + total; + } + void set_offset_at (unsigned int index, unsigned int offset) { assert (index <= count); @@ -207,10 +150,14 @@ struct CFFIndex unsigned int size = offSize; const HBUINT8 *p = offsets + size * index; - unsigned int offset = 0; - for (; size; size--) - offset = (offset << 8) + *p++; - return offset; + switch (size) + { + case 1: return * (HBUINT8 *) p; + case 2: return * (HBUINT16 *) p; + case 3: return * (HBUINT24 *) p; + case 4: return * (HBUINT32 *) p; + default: return 0; + } } unsigned int length_at (unsigned int index) const @@ -229,6 +176,7 @@ struct CFFIndex hb_ubytes_t operator [] (unsigned int index) const { if (unlikely (index >= count)) return hb_ubytes_t (); + _hb_compiler_memory_r_barrier (); unsigned length = length_at (index); if (unlikely (!length)) return hb_ubytes_t (); return hb_ubytes_t (data_base () + offset_at (index) - 1, length); @@ -280,7 +228,7 @@ struct CFFIndexOf : CFFIndex<COUNT> if (unlikely (!c->extend_min (this))) return_trace (false); this->count = dataArrayLen; this->offSize = offSize_; - if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1)))) + if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1), false))) return_trace (false); /* serialize indices */ @@ -288,10 +236,10 @@ struct CFFIndexOf : CFFIndex<COUNT> unsigned int i = 0; for (; i < dataArrayLen; i++) { - CFFIndex<COUNT>::set_offset_at (i, offset); + this->set_offset_at (i, offset); offset += dataSizeArray[i]; } - CFFIndex<COUNT>::set_offset_at (i, offset); + this->set_offset_at (i, offset); /* serialize data */ for (unsigned int i = 0; i < dataArrayLen; i++) @@ -324,13 +272,12 @@ struct Dict : UnsizedByteStr template <typename T, typename V> static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp) { - // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation - if (/*unlikely*/ (!serialize_int<T, V> (c, intOp, value))) + if (unlikely ((!serialize_int<T, V> (c, intOp, value)))) return false; TRACE_SERIALIZE (this); /* serialize the opcode */ - HBUINT8 *p = c->allocate_size<HBUINT8> (OpCode_Size (op)); + HBUINT8 *p = c->allocate_size<HBUINT8> (OpCode_Size (op), false); if (unlikely (!p)) return_trace (false); if (Is_OpCode_ESC (op)) { @@ -415,9 +362,8 @@ struct FDSelect0 { TRACE_SANITIZE (this); if (unlikely (!(c->check_struct (this)))) return_trace (false); - for (unsigned int i = 0; i < c->get_num_glyphs (); i++) - if (unlikely (!fds[i].sanitize (c))) - return_trace (false); + if (unlikely (!c->check_array (fds, c->get_num_glyphs ()))) + return_trace (false); return_trace (true); } @@ -471,14 +417,20 @@ struct FDSelect3_4 return_trace (true); } - hb_codepoint_t get_fd (hb_codepoint_t glyph) const + static int _cmp_range (const void *_key, const void *_item) { - unsigned int i; - for (i = 1; i < nRanges (); i++) - if (glyph < ranges[i].first) - break; + hb_codepoint_t glyph = * (hb_codepoint_t *) _key; + FDSelect3_4_Range<GID_TYPE, FD_TYPE> *range = (FDSelect3_4_Range<GID_TYPE, FD_TYPE> *) _item; - return (hb_codepoint_t) ranges[i - 1].fd; + if (glyph < range[0].first) return -1; + if (glyph < range[1].first) return 0; + return +1; + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + auto *range = hb_bsearch (glyph, &ranges[0], nRanges () - 1, sizeof (ranges[0]), _cmp_range); + return range ? range->fd : ranges[nRanges () - 1].fd; } GID_TYPE &nRanges () { return ranges.len; } @@ -501,9 +453,9 @@ struct FDSelect { TRACE_SERIALIZE (this); unsigned int size = src.get_size (num_glyphs); - FDSelect *dest = c->allocate_size<FDSelect> (size); + FDSelect *dest = c->allocate_size<FDSelect> (size, false); if (unlikely (!dest)) return_trace (false); - memcpy (dest, &src, size); + hb_memcpy (dest, &src, size); return_trace (true); } diff --git a/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh index 17b0296616..bb856c9ddb 100644 --- a/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh @@ -175,7 +175,7 @@ struct Encoding unsigned int size = src.get_size (); Encoding *dest = c->allocate_size<Encoding> (size); if (unlikely (!dest)) return_trace (false); - memcpy (dest, &src, size); + hb_memcpy (dest, &src, size); return_trace (true); } @@ -471,7 +471,7 @@ struct Charset unsigned int size = src.get_size (num_glyphs); Charset *dest = c->allocate_size<Charset> (size); if (unlikely (!dest)) return_trace (false); - memcpy (dest, &src, size); + hb_memcpy (dest, &src, size); return_trace (true); } @@ -617,7 +617,6 @@ struct CFF1StringIndex : CFF1Index } byte_str_array_t bytesArray; - bytesArray.init (); if (!bytesArray.resize (sidmap.get_population ())) return_trace (false); for (unsigned int i = 0; i < strings.count; i++) @@ -628,7 +627,6 @@ struct CFF1StringIndex : CFF1Index } bool result = CFF1Index::serialize (c, bytesArray); - bytesArray.fini (); return_trace (result); } }; @@ -813,7 +811,7 @@ struct cff1_top_dict_opset_t : top_dict_opset_t<cff1_top_dict_val_t> break; default: - env.last_offset = env.str_ref.offset; + env.last_offset = env.str_ref.get_offset (); top_dict_opset_t<cff1_top_dict_val_t>::process_op (op, env, dictval); /* Record this operand below if stack is empty, otherwise done */ if (!env.argStack.is_empty ()) return; @@ -1295,10 +1293,10 @@ struct cff1 } protected: - hb_blob_t *blob = nullptr; hb_sanitize_context_t sc; public: + hb_blob_t *blob = nullptr; const Encoding *encoding = nullptr; const Charset *charset = nullptr; const CFF1NameIndex *nameIndex = nullptr; diff --git a/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh index 746160dc8e..9081930bbe 100644 --- a/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh @@ -56,7 +56,7 @@ struct CFF2FDSelect unsigned int size = src.get_size (num_glyphs); CFF2FDSelect *dest = c->allocate_size<CFF2FDSelect> (size); if (unlikely (!dest)) return_trace (false); - memcpy (dest, &src, size); + hb_memcpy (dest, &src, size); return_trace (true); } @@ -124,7 +124,7 @@ struct CFF2VariationStore unsigned int size_ = varStore->get_size (); CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (size_); if (unlikely (!dest)) return_trace (false); - memcpy (dest, varStore, size_); + hb_memcpy (dest, varStore, size_); return_trace (true); } @@ -483,13 +483,18 @@ struct cff2 blob = nullptr; } + hb_map_t *create_glyph_to_sid_map () const + { + return nullptr; + } + bool is_valid () const { return blob; } protected: - hb_blob_t *blob = nullptr; hb_sanitize_context_t sc; public: + hb_blob_t *blob = nullptr; cff2_top_dict_values_t topDict; const CFF2Subrs *globalSubrs = nullptr; const CFF2VariationStore *varStore = nullptr; diff --git a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh index 09c9fe93f3..523196fa79 100644 --- a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh @@ -909,7 +909,7 @@ struct DefaultUVS : SortedArray32Of<UnicodeValueRange> hb_codepoint_t first = arrayZ[i].startUnicodeValue; hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount), (hb_codepoint_t) HB_UNICODE_MAX); - out->add_range (first, hb_min (last, 0x10FFFFu)); + out->add_range (first, last); } } @@ -925,37 +925,75 @@ struct DefaultUVS : SortedArray32Of<UnicodeValueRange> if (unlikely (!c->copy<HBUINT32> (len))) return nullptr; unsigned init_len = c->length (); - hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID; - int count = -1; - - for (const UnicodeValueRange& _ : as_array ()) + if (this->len > unicodes->get_population () * hb_bit_storage ((unsigned) this->len)) { - for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1)) + hb_codepoint_t start = HB_SET_VALUE_INVALID; + hb_codepoint_t end = HB_SET_VALUE_INVALID; + + for (hb_codepoint_t u = HB_SET_VALUE_INVALID; + unicodes->next (&u);) { - unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt; - if (!unicodes->has (curEntry)) continue; - count += 1; - if (lastCode == HB_MAP_VALUE_INVALID) - lastCode = curEntry; - else if (lastCode + count != curEntry) + if (!as_array ().bsearch (u)) + continue; + if (start == HB_SET_VALUE_INVALID) { + start = u; + end = start - 1; + } + if (end + 1 != u || end - start == 255) + { UnicodeValueRange rec; - rec.startUnicodeValue = lastCode; - rec.additionalCount = count - 1; + rec.startUnicodeValue = start; + rec.additionalCount = end - start; c->copy<UnicodeValueRange> (rec); - - lastCode = curEntry; - count = 0; + start = u; } + end = u; + } + if (start != HB_SET_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = start; + rec.additionalCount = end - start; + c->copy<UnicodeValueRange> (rec); } - } - if (lastCode != HB_MAP_VALUE_INVALID) + } + else { - UnicodeValueRange rec; - rec.startUnicodeValue = lastCode; - rec.additionalCount = count; - c->copy<UnicodeValueRange> (rec); + hb_codepoint_t lastCode = HB_SET_VALUE_INVALID; + int count = -1; + + for (const UnicodeValueRange& _ : *this) + { + hb_codepoint_t curEntry = (hb_codepoint_t) (_.startUnicodeValue - 1); + hb_codepoint_t end = curEntry + _.additionalCount + 2; + + for (; unicodes->next (&curEntry) && curEntry < end;) + { + count += 1; + if (lastCode == HB_SET_VALUE_INVALID) + lastCode = curEntry; + else if (lastCode + count != curEntry) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count - 1; + c->copy<UnicodeValueRange> (rec); + + lastCode = curEntry; + count = 0; + } + } + } + + if (lastCode != HB_MAP_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count; + c->copy<UnicodeValueRange> (rec); + } } if (c->length () - init_len == 0) @@ -1474,32 +1512,80 @@ struct EncodingRecord DEFINE_SIZE_STATIC (8); }; +struct cmap; + struct SubtableUnicodesCache { private: - const void* base; - hb_hashmap_t<intptr_t, hb::unique_ptr<hb_set_t>> cached_unicodes; + hb_blob_ptr_t<cmap> base_blob; + const char* base; + hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> cached_unicodes; public: + + static SubtableUnicodesCache* create (hb_blob_ptr_t<cmap> source_table) + { + SubtableUnicodesCache* cache = + (SubtableUnicodesCache*) hb_malloc (sizeof(SubtableUnicodesCache)); + new (cache) SubtableUnicodesCache (source_table); + return cache; + } + + static void destroy (void* value) { + if (!value) return; + + SubtableUnicodesCache* cache = (SubtableUnicodesCache*) value; + cache->~SubtableUnicodesCache (); + hb_free (cache); + } + SubtableUnicodesCache(const void* cmap_base) - : base(cmap_base), cached_unicodes() {} + : base_blob(), + base ((const char*) cmap_base), + cached_unicodes () + {} + + SubtableUnicodesCache(hb_blob_ptr_t<cmap> base_blob_) + : base_blob(base_blob_), + base ((const char *) base_blob.get()), + cached_unicodes () + {} + + ~SubtableUnicodesCache() + { + base_blob.destroy (); + } + + bool same_base(const void* other) const + { + return other == (const void*) base; + } - hb_set_t* set_for (const EncodingRecord* record) + const hb_set_t* set_for (const EncodingRecord* record, + SubtableUnicodesCache& mutable_cache) const { - if (!cached_unicodes.has ((intptr_t) record)) + if (cached_unicodes.has ((unsigned) ((const char *) record - base))) + return cached_unicodes.get ((unsigned) ((const char *) record - base)); + + return mutable_cache.set_for (record); + } + + const hb_set_t* set_for (const EncodingRecord* record) + { + if (!cached_unicodes.has ((unsigned) ((const char *) record - base))) { hb_set_t *s = hb_set_create (); if (unlikely (s->in_error ())) return hb_set_get_empty (); - + (base+record->subtable).collect_unicodes (s); - if (unlikely (!cached_unicodes.set ((intptr_t) record, hb::unique_ptr<hb_set_t> {s}))) + if (unlikely (!cached_unicodes.set ((unsigned) ((const char *) record - base), hb::unique_ptr<hb_set_t> {s}))) return hb_set_get_empty (); return s; } - return cached_unicodes.get ((intptr_t) record); + return cached_unicodes.get ((unsigned) ((const char *) record - base)); } }; @@ -1523,13 +1609,30 @@ struct cmap { static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap; + + static SubtableUnicodesCache* create_filled_cache(hb_blob_ptr_t<cmap> source_table) { + const cmap* cmap = source_table.get(); + auto it = + + hb_iter (cmap->encodingRecord) + | hb_filter ([&](const EncodingRecord& _) { + return cmap::filter_encoding_records_for_subset (cmap, _); + }) + ; + + SubtableUnicodesCache* cache = SubtableUnicodesCache::create(source_table); + for (const EncodingRecord& _ : it) + cache->set_for(&_); // populate the cache for this encoding record. + + return cache; + } + template<typename Iterator, typename EncodingRecIter, hb_requires (hb_is_iterator (EncodingRecIter))> bool serialize (hb_serialize_context_t *c, Iterator it, EncodingRecIter encodingrec_iter, const void *base, - const hb_subset_plan_t *plan, + hb_subset_plan_t *plan, bool drop_format_4 = false) { if (unlikely (!c->extend_min ((*this)))) return false; @@ -1538,7 +1641,14 @@ struct cmap unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; auto snap = c->snapshot (); - SubtableUnicodesCache unicodes_cache (base); + SubtableUnicodesCache local_unicodes_cache (base); + const SubtableUnicodesCache* unicodes_cache = &local_unicodes_cache; + + if (plan->accelerator && + plan->accelerator->cmap_cache && + plan->accelerator->cmap_cache->same_base (base)) + unicodes_cache = plan->accelerator->cmap_cache; + for (const EncodingRecord& _ : encodingrec_iter) { if (c->in_error ()) @@ -1547,7 +1657,7 @@ struct cmap unsigned format = (base+_.subtable).u.format; if (format != 4 && format != 12 && format != 14) continue; - hb_set_t* unicodes_set = unicodes_cache.set_for (&_); + const hb_set_t* unicodes_set = unicodes_cache->set_for (&_, local_unicodes_cache); if (!drop_format_4 && format == 4) { @@ -1566,7 +1676,13 @@ struct cmap else if (format == 12) { - if (_can_drop (_, *unicodes_set, base, unicodes_cache, + it | hb_map (hb_first), encodingrec_iter)) continue; + if (_can_drop (_, + *unicodes_set, + base, + *unicodes_cache, + local_unicodes_cache, + + it | hb_map (hb_first), encodingrec_iter)) + continue; c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 12u, base, plan, &format12objidx); } else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx); @@ -1585,7 +1701,8 @@ struct cmap bool _can_drop (const EncodingRecord& cmap12, const hb_set_t& cmap12_unicodes, const void* base, - SubtableUnicodesCache& unicodes_cache, + const SubtableUnicodesCache& unicodes_cache, + SubtableUnicodesCache& local_unicodes_cache, Iterator subset_unicodes, EncodingRecordIterator encoding_records) { @@ -1616,7 +1733,7 @@ struct cmap || (base+_.subtable).get_language() != target_language) continue; - hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_); + const hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_, local_unicodes_cache); auto cmap12 = + subset_unicodes | hb_filter (cmap12_unicodes); auto sibling = + subset_unicodes | hb_filter (*sibling_unicodes); @@ -1653,17 +1770,9 @@ struct cmap auto encodingrec_iter = + hb_iter (encodingRecord) - | hb_filter ([&] (const EncodingRecord& _) - { - if ((_.platformID == 0 && _.encodingID == 3) || - (_.platformID == 0 && _.encodingID == 4) || - (_.platformID == 3 && _.encodingID == 1) || - (_.platformID == 3 && _.encodingID == 10) || - (this + _.subtable).u.format == 14) - return true; - - return false; - }) + | hb_filter ([&](const EncodingRecord& _) { + return cmap::filter_encoding_records_for_subset (this, _); + }) ; if (unlikely (!encodingrec_iter.len ())) return_trace (false); @@ -1692,7 +1801,11 @@ struct cmap { return (_.second != HB_MAP_VALUE_INVALID); }) ; - return_trace (cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan)); + return_trace (cmap_prime->serialize (c->serializer, + it, + encodingrec_iter, + this, + c->plan)); } const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const @@ -1928,6 +2041,19 @@ struct cmap encodingRecord.sanitize (c, this)); } + private: + + static bool filter_encoding_records_for_subset(const cmap* cmap, + const EncodingRecord& _) + { + return + (_.platformID == 0 && _.encodingID == 3) || + (_.platformID == 0 && _.encodingID == 4) || + (_.platformID == 3 && _.encodingID == 1) || + (_.platformID == 3 && _.encodingID == 10) || + (cmap + _.subtable).u.format == 14; + } + protected: HBUINT16 version; /* Table version number (0). */ SortedArray16Of<EncodingRecord> diff --git a/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh index 23fa56c4f6..3246894d39 100644 --- a/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh @@ -67,7 +67,7 @@ _copy_data_to_cbdt (hb_vector_t<char> *cbdt_prime, { unsigned int new_len = cbdt_prime->length + length; if (unlikely (!cbdt_prime->alloc (new_len))) return false; - memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length); + hb_memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length); cbdt_prime->length = new_len; return true; } @@ -468,13 +468,13 @@ struct IndexSubtableRecord if (unlikely (!c->serializer->check_success (records->resize (records->length + 1)))) return_trace (false); - (*records)[records->length - 1].firstGlyphIndex = 1; - (*records)[records->length - 1].lastGlyphIndex = 0; + records->tail ().firstGlyphIndex = 1; + records->tail ().lastGlyphIndex = 0; bitmap_size_context->size += IndexSubtableRecord::min_size; c->serializer->push (); - if (unlikely (!add_new_subtable (c, bitmap_size_context, &((*records)[records->length - 1]), lookup, base, start))) + if (unlikely (!add_new_subtable (c, bitmap_size_context, &(records->tail ()), lookup, base, start))) { c->serializer->pop_discard (); c->serializer->revert (snap); diff --git a/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh index 908bf550f0..1af9d30937 100644 --- a/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh @@ -39,11 +39,7 @@ #define HB_OT_TAG_COLR HB_TAG('C','O','L','R') #ifndef HB_COLRV1_MAX_NESTING_LEVEL -#define HB_COLRV1_MAX_NESTING_LEVEL 100 -#endif - -#ifndef COLRV1_ENABLE_SUBSETTING -#define COLRV1_ENABLE_SUBSETTING 1 +#define HB_COLRV1_MAX_NESTING_LEVEL 16 #endif namespace OT { @@ -188,6 +184,7 @@ struct Variable protected: T value; + public: VarIdx varIdxBase; public: DEFINE_SIZE_STATIC (4 + T::static_size); @@ -196,6 +193,8 @@ struct Variable template <typename T> struct NoVariable { + static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION; + NoVariable<T>* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); @@ -888,6 +887,11 @@ struct PaintComposite DEFINE_SIZE_STATIC (8); }; +struct ClipBoxData +{ + int xMin, yMin, xMax, yMax; +}; + struct ClipBoxFormat1 { bool sanitize (hb_sanitize_context_t *c) const @@ -896,6 +900,14 @@ struct ClipBoxFormat1 return_trace (c->check_struct (this)); } + void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer HB_UNUSED) const + { + clip_box.xMin = xMin; + clip_box.yMin = yMin; + clip_box.xMax = xMax; + clip_box.yMax = yMax; + } + public: HBUINT8 format; /* format = 1(noVar) or 2(Var)*/ FWORD xMin; @@ -906,7 +918,20 @@ struct ClipBoxFormat1 DEFINE_SIZE_STATIC (1 + 4 * FWORD::static_size); }; -struct ClipBoxFormat2 : Variable<ClipBoxFormat1> {}; +struct ClipBoxFormat2 : Variable<ClipBoxFormat1> +{ + void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const + { + value.get_clip_box(clip_box, instancer); + if (instancer) + { + clip_box.xMin += _hb_roundf (instancer (varIdxBase, 0)); + clip_box.yMin += _hb_roundf (instancer (varIdxBase, 1)); + clip_box.xMax += _hb_roundf (instancer (varIdxBase, 2)); + clip_box.yMax += _hb_roundf (instancer (varIdxBase, 3)); + } + } +}; struct ClipBox { @@ -932,6 +957,28 @@ struct ClipBox } } + bool get_extents (hb_glyph_extents_t *extents, + const VarStoreInstancer &instancer) const + { + ClipBoxData clip_box; + switch (u.format) { + case 1: + u.format1.get_clip_box (clip_box, instancer); + break; + case 2: + u.format2.get_clip_box (clip_box, instancer); + break; + default: + return false; + } + + extents->x_bearing = clip_box.xMin; + extents->y_bearing = clip_box.yMax; + extents->width = clip_box.xMax - clip_box.xMin; + extents->height = clip_box.yMin - clip_box.yMax; + return true; + } + protected: union { HBUINT8 format; /* Format identifier */ @@ -942,6 +989,9 @@ struct ClipBox struct ClipRecord { + int cmp (hb_codepoint_t g) const + { return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; } + ClipRecord* copy (hb_serialize_context_t *c, const void *base) const { TRACE_SERIALIZE (this); @@ -957,6 +1007,13 @@ struct ClipRecord return_trace (c->check_struct (this) && clipBox.sanitize (c, base)); } + bool get_extents (hb_glyph_extents_t *extents, + const void *base, + const VarStoreInstancer &instancer) const + { + return (base+clipBox).get_extents (extents, instancer); + } + public: HBUINT16 startGlyphID; // first gid clip applies to HBUINT16 endGlyphID; // last gid clip applies to, inclusive @@ -964,6 +1021,7 @@ struct ClipRecord public: DEFINE_SIZE_STATIC (7); }; +DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord); struct ClipList { @@ -1052,11 +1110,26 @@ struct ClipList bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); + // TODO Make a formatted struct! return_trace (c->check_struct (this) && clips.sanitize (c, this)); } + bool + get_extents (hb_codepoint_t gid, + hb_glyph_extents_t *extents, + const VarStoreInstancer &instancer) const + { + auto *rec = clips.as_array ().bsearch (gid); + if (rec) + { + rec->get_extents (extents, this, instancer); + return true; + } + return false; + } + HBUINT8 format; // Set to 1. - Array32Of<ClipRecord> clips; // Clip records, sorted by startGlyphID + SortedArray32Of<ClipRecord> clips; // Clip records, sorted by startGlyphID public: DEFINE_SIZE_ARRAY_SIZED (5, clips); }; @@ -1359,7 +1432,7 @@ struct COLR (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) && (this+layersZ).sanitize (c, numLayers) && (version == 0 || - (COLRV1_ENABLE_SUBSETTING && version == 1 && + (version == 1 && baseGlyphList.sanitize (c, this) && layerList.sanitize (c, this) && clipList.sanitize (c, this) && @@ -1516,6 +1589,30 @@ struct COLR return_trace (true); } + bool + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + { + if (version != 1) + return false; + + VarStoreInstancer instancer (this+varStore, + this+varIdxMap, + hb_array (font->coords, font->num_coords)); + + if ((this+clipList).get_extents (glyph, + extents, + instancer)) + { + extents->x_bearing = font->em_scale_x (extents->x_bearing); + extents->y_bearing = font->em_scale_x (extents->y_bearing); + extents->width = font->em_scale_x (extents->width); + extents->height = font->em_scale_x (extents->height); + return true; + } + + return false; + } + protected: HBUINT16 version; /* Table version number (starts at 0). */ HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */ diff --git a/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh b/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh index c05034b3bb..c9da36c1bb 100644 --- a/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh +++ b/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh @@ -56,9 +56,9 @@ HB_OT_CORE_TABLE (OT, maxp) #if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT) HB_OT_ACCELERATOR (OT, cmap) #endif -HB_OT_TABLE (OT, hhea) +HB_OT_CORE_TABLE (OT, hhea) HB_OT_ACCELERATOR (OT, hmtx) -HB_OT_TABLE (OT, OS2) +HB_OT_CORE_TABLE (OT, OS2) #if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS) || !defined(HB_NO_STYLE) HB_OT_ACCELERATOR (OT, post) #endif @@ -66,7 +66,7 @@ HB_OT_ACCELERATOR (OT, post) HB_OT_ACCELERATOR (OT, name) #endif #ifndef HB_NO_STYLE -HB_OT_TABLE (OT, STAT) +HB_OT_CORE_TABLE (OT, STAT) #endif #ifndef HB_NO_META HB_OT_ACCELERATOR (OT, meta) @@ -74,9 +74,9 @@ HB_OT_ACCELERATOR (OT, meta) /* Vertical layout. */ #ifndef HB_NO_VERTICAL -HB_OT_TABLE (OT, vhea) +HB_OT_CORE_TABLE (OT, vhea) HB_OT_ACCELERATOR (OT, vmtx) -HB_OT_TABLE (OT, VORG) +HB_OT_CORE_TABLE (OT, VORG) #endif /* TrueType outlines. */ @@ -91,15 +91,15 @@ HB_OT_ACCELERATOR (OT, cff2) /* OpenType variations. */ #ifndef HB_NO_VAR -HB_OT_TABLE (OT, fvar) -HB_OT_TABLE (OT, avar) +HB_OT_CORE_TABLE (OT, fvar) +HB_OT_CORE_TABLE (OT, avar) HB_OT_ACCELERATOR (OT, gvar) -HB_OT_TABLE (OT, MVAR) +HB_OT_CORE_TABLE (OT, MVAR) #endif /* Legacy kern. */ #ifndef HB_NO_OT_KERN -HB_OT_TABLE (OT, kern) +HB_OT_CORE_TABLE (OT, kern) #endif /* OpenType shaping. */ @@ -107,12 +107,12 @@ HB_OT_TABLE (OT, kern) HB_OT_ACCELERATOR (OT, GDEF) HB_OT_ACCELERATOR (OT, GSUB) HB_OT_ACCELERATOR (OT, GPOS) -//HB_OT_TABLE (OT, JSTF) +//HB_OT_CORE_TABLE (OT, JSTF) #endif /* OpenType baseline. */ #ifndef HB_NO_BASE -HB_OT_TABLE (OT, BASE) +HB_OT_CORE_TABLE (OT, BASE) #endif /* AAT shaping. */ @@ -129,8 +129,8 @@ HB_OT_TABLE (AAT, feat) /* OpenType color fonts. */ #ifndef HB_NO_COLOR -HB_OT_TABLE (OT, COLR) -HB_OT_TABLE (OT, CPAL) +HB_OT_CORE_TABLE (OT, COLR) +HB_OT_CORE_TABLE (OT, CPAL) HB_OT_ACCELERATOR (OT, CBDT) HB_OT_ACCELERATOR (OT, sbix) HB_OT_ACCELERATOR (OT, SVG) @@ -138,7 +138,7 @@ HB_OT_ACCELERATOR (OT, SVG) /* OpenType math. */ #ifndef HB_NO_MATH -HB_OT_TABLE (OT, MATH) +HB_OT_CORE_TABLE (OT, MATH) #endif diff --git a/thirdparty/harfbuzz/src/hb-ot-font.cc b/thirdparty/harfbuzz/src/hb-ot-font.cc index 825b30853c..8405103423 100644 --- a/thirdparty/harfbuzz/src/hb-ot-font.cc +++ b/thirdparty/harfbuzz/src/hb-ot-font.cc @@ -45,6 +45,7 @@ #include "hb-ot-vorg-table.hh" #include "hb-ot-color-cbdt-table.hh" #include "hb-ot-color-sbix-table.hh" +#include "hb-ot-color-colr-table.hh" /** @@ -350,13 +351,15 @@ hb_ot_get_glyph_extents (hb_font_t *font, if (ot_face->sbix->get_extents (font, glyph, extents)) return true; if (ot_face->CBDT->get_extents (font, glyph, extents)) return true; #endif +#if !defined(HB_NO_COLOR) + if (ot_face->COLR->get_extents (font, glyph, extents)) return true; +#endif if (ot_face->glyf->get_extents (font, glyph, extents)) return true; #ifndef HB_NO_OT_FONT_CFF if (ot_face->cff1->get_extents (font, glyph, extents)) return true; if (ot_face->cff2->get_extents (font, glyph, extents)) return true; #endif - // TODO Hook up side-bearings variations. return false; } diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh index 579abf011a..0f424f5aa6 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh +++ b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh @@ -478,7 +478,7 @@ struct IndexArray : Array16Of<Index> { if (_count) { - + this->sub_array (start_offset, _count) + + this->as_array ().sub_array (start_offset, _count) | hb_sink (hb_array (_indexes, *_count)) ; } @@ -658,7 +658,7 @@ struct FeatureParamsCharacterVariants { if (char_count) { - + characters.sub_array (start_offset, char_count) + + characters.as_array ().sub_array (start_offset, char_count) | hb_sink (hb_array (chars, *char_count)) ; } @@ -932,7 +932,7 @@ struct RecordArrayOf : SortedArray16Of<Record<Type>> { if (record_count) { - + this->sub_array (start_offset, record_count) + + this->as_array ().sub_array (start_offset, record_count) | hb_map (&Record<Type>::tag) | hb_sink (hb_array (record_tags, *record_count)) ; @@ -980,18 +980,16 @@ struct RecordListOfFeature : RecordListOf<Feature> auto *out = c->serializer->start_embed (*this); if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); - unsigned count = this->len; - - + hb_zip (*this, hb_range (count)) - | hb_filter (l->feature_index_map, hb_second) - | hb_apply ([l, out, this] (const hb_pair_t<const Record<Feature>&, unsigned>& _) + + hb_enumerate (*this) + | hb_filter (l->feature_index_map, hb_first) + | hb_apply ([l, out, this] (const hb_pair_t<unsigned, const Record<Feature>&>& _) { const Feature *f_sub = nullptr; const Feature **f = nullptr; - if (l->feature_substitutes_map->has (_.second, &f)) + if (l->feature_substitutes_map->has (_.first, &f)) f_sub = *f; - subset_record_array (l, out, this, f_sub) (_.first); + subset_record_array (l, out, this, f_sub) (_.second); }) ; @@ -1079,7 +1077,7 @@ struct LangSys auto *out = c->serializer->start_embed (*this); if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); - const unsigned *v; + const uint32_t *v; out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex, &v) ? *v : 0xFFFFu; if (!l->visitFeatureIndex (featureIndex.len)) @@ -1147,7 +1145,6 @@ struct Script return; } - unsigned langsys_count = get_lang_sys_count (); if (has_default_lang_sys ()) { //only collect features from non-redundant langsys @@ -1156,24 +1153,24 @@ struct Script d.collect_features (c); } - for (auto _ : + hb_zip (langSys, hb_range (langsys_count))) + for (auto _ : + hb_enumerate (langSys)) { - const LangSys& l = this+_.first.offset; + const LangSys& l = this+_.second.offset; if (!c->visitLangsys (l.get_feature_count ())) continue; if (l.compare (d, c->duplicate_feature_map)) continue; l.collect_features (c); - c->script_langsys_map->get (script_index)->add (_.second); + c->script_langsys_map->get (script_index)->add (_.first); } } else { - for (auto _ : + hb_zip (langSys, hb_range (langsys_count))) + for (auto _ : + hb_enumerate (langSys)) { - const LangSys& l = this+_.first.offset; + const LangSys& l = this+_.second.offset; if (!c->visitLangsys (l.get_feature_count ())) continue; l.collect_features (c); - c->script_langsys_map->get (script_index)->add (_.second); + c->script_langsys_map->get (script_index)->add (_.first); } } } @@ -1211,10 +1208,9 @@ struct Script const hb_set_t *active_langsys = l->script_langsys_map->get (l->cur_script_index); if (active_langsys) { - unsigned count = langSys.len; - + hb_zip (langSys, hb_range (count)) - | hb_filter (active_langsys, hb_second) - | hb_map (hb_first) + + hb_enumerate (langSys) + | hb_filter (active_langsys, hb_first) + | hb_map (hb_second) | hb_filter ([=] (const Record<LangSys>& record) {return l->visitLangSys (); }) | hb_apply (subset_record_array (l, &(out->langSys), this)) ; @@ -1250,12 +1246,11 @@ struct RecordListOfScript : RecordListOf<Script> auto *out = c->serializer->start_embed (*this); if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); - unsigned count = this->len; - for (auto _ : + hb_zip (*this, hb_range (count))) + for (auto _ : + hb_enumerate (*this)) { auto snap = c->serializer->snapshot (); - l->cur_script_index = _.second; - bool ret = _.first.subset (l, this); + l->cur_script_index = _.first; + bool ret = _.second.subset (l, this); if (!ret) c->serializer->revert (snap); else out->len++; } @@ -1388,7 +1383,13 @@ struct Lookup outMarkFilteringSet = markFilteringSet; } - return_trace (out->subTable.len); + // Always keep the lookup even if it's empty. The rest of layout subsetting depends on lookup + // indices being consistent with those computed during planning. So if an empty lookup is + // discarded during the subset phase it will invalidate all subsequent lookup indices. + // Generally we shouldn't end up with an empty lookup as we pre-prune them during the planning + // phase, but it can happen in rare cases such as when during closure subtable is considered + // degenerate (see: https://github.com/harfbuzz/harfbuzz/issues/3853) + return true; } template <typename TSubTable> @@ -1454,10 +1455,9 @@ struct LookupOffsetList : List16OfOffsetTo<TLookup, OffsetType> auto *out = c->serializer->start_embed (this); if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); - unsigned count = this->len; - + hb_zip (*this, hb_range (count)) - | hb_filter (l->lookup_index_map, hb_second) - | hb_map (hb_first) + + hb_enumerate (*this) + | hb_filter (l->lookup_index_map, hb_first) + | hb_map (hb_second) | hb_apply (subset_offset_array (c, *out, this)) ; return_trace (true); @@ -1491,7 +1491,7 @@ static bool ClassDef_remap_and_serialize (hb_serialize_context_t *c, klass_map->set (0, 0); unsigned idx = klass_map->has (0) ? 1 : 0; - for (const unsigned k: klasses.iter ()) + for (const unsigned k: klasses) { if (klass_map->has (k)) continue; klass_map->set (k, idx); @@ -1524,6 +1524,11 @@ struct ClassDefFormat1_3 return classValue[(unsigned int) (glyph_id - startGlyph)]; } + unsigned get_population () const + { + return classValue.len; + } + template<typename Iterator, hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> bool serialize (hb_serialize_context_t *c, @@ -1548,7 +1553,7 @@ struct ClassDefFormat1_3 startGlyph = glyph_min; if (unlikely (!classValue.serialize (c, glyph_count))) return_trace (false); - for (const hb_pair_t<hb_codepoint_t, unsigned> gid_klass_pair : + it) + for (const hb_pair_t<hb_codepoint_t, uint32_t> gid_klass_pair : + it) { unsigned idx = gid_klass_pair.first - glyph_min; classValue[idx] = gid_klass_pair.second; @@ -1639,11 +1644,10 @@ struct ClassDefFormat1_3 bool intersects (const hb_set_t *glyphs) const { - /* TODO Speed up, using hb_set_next()? */ hb_codepoint_t start = startGlyph; hb_codepoint_t end = startGlyph + classValue.len; for (hb_codepoint_t iter = startGlyph - 1; - hb_set_next (glyphs, &iter) && iter < end;) + glyphs->next (&iter) && iter < end;) if (classValue[iter - start]) return true; return false; } @@ -1654,10 +1658,10 @@ struct ClassDefFormat1_3 { /* Match if there's any glyph that is not listed! */ hb_codepoint_t g = HB_SET_VALUE_INVALID; - if (!hb_set_next (glyphs, &g)) return false; + if (!glyphs->next (&g)) return false; if (g < startGlyph) return true; g = startGlyph + count - 1; - if (hb_set_next (glyphs, &g)) return true; + if (glyphs->next (&g)) return true; /* Fall through. */ } /* TODO Speed up, using set overlap first? */ @@ -1675,12 +1679,12 @@ struct ClassDefFormat1_3 if (klass == 0) { unsigned start_glyph = startGlyph; - for (unsigned g = HB_SET_VALUE_INVALID; - hb_set_next (glyphs, &g) && g < start_glyph;) + for (uint32_t g = HB_SET_VALUE_INVALID; + glyphs->next (&g) && g < start_glyph;) intersect_glyphs->add (g); - for (unsigned g = startGlyph + count - 1; - hb_set_next (glyphs, &g);) + for (uint32_t g = startGlyph + count - 1; + glyphs-> next (&g);) intersect_glyphs->add (g); return; @@ -1696,7 +1700,7 @@ struct ClassDefFormat1_3 unsigned start_glyph = startGlyph; unsigned end_glyph = start_glyph + count; for (unsigned g = startGlyph - 1; - hb_set_next (glyphs, &g) && g < end_glyph;) + glyphs->next (&g) && g < end_glyph;) if (classValue.arrayZ[g - start_glyph] == klass) intersect_glyphs->add (g); #endif @@ -1739,6 +1743,14 @@ struct ClassDefFormat2_4 return rangeRecord.bsearch (glyph_id).value; } + unsigned get_population () const + { + typename Types::large_int ret = 0; + for (const auto &r : rangeRecord) + ret += r.get_population (); + return ret > UINT_MAX ? UINT_MAX : (unsigned) ret; + } + template<typename Iterator, hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> bool serialize (hb_serialize_context_t *c, @@ -1802,28 +1814,45 @@ struct ClassDefFormat2_4 { TRACE_SUBSET (this); const hb_map_t &glyph_map = *c->plan->glyph_map_gsub; + const hb_set_t &glyph_set = *c->plan->glyphset_gsub (); hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass; hb_set_t orig_klasses; - unsigned num_source_glyphs = c->plan->source->get_num_glyphs (); - unsigned count = rangeRecord.len; - for (unsigned i = 0; i < count; i++) + if (glyph_set.get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2 + < get_population ()) { - unsigned klass = rangeRecord[i].value; - if (!klass) continue; - hb_codepoint_t start = rangeRecord[i].first; - hb_codepoint_t end = hb_min (rangeRecord[i].last + 1, num_source_glyphs); - for (hb_codepoint_t g = start; g < end; g++) + for (hb_codepoint_t g : glyph_set) { - hb_codepoint_t new_gid = glyph_map[g]; + unsigned klass = get_class (g); + if (!klass) continue; + hb_codepoint_t new_gid = glyph_map[g]; if (new_gid == HB_MAP_VALUE_INVALID) continue; - if (glyph_filter && !glyph_filter->has (g)) continue; - + if (glyph_filter && !glyph_filter->has (g)) continue; glyph_and_klass.push (hb_pair (new_gid, klass)); orig_klasses.add (klass); } } + else + { + unsigned num_source_glyphs = c->plan->source->get_num_glyphs (); + for (auto &range : rangeRecord) + { + unsigned klass = range.value; + if (!klass) continue; + hb_codepoint_t start = range.first; + hb_codepoint_t end = hb_min (range.last + 1, num_source_glyphs); + for (hb_codepoint_t g = start; g < end; g++) + { + hb_codepoint_t new_gid = glyph_map[g]; + if (new_gid == HB_MAP_VALUE_INVALID) continue; + if (glyph_filter && !glyph_filter->has (g)) continue; + + glyph_and_klass.push (hb_pair (new_gid, klass)); + orig_klasses.add (klass); + } + } + } const hb_set_t& glyphset = *c->plan->glyphset_gsub (); unsigned glyph_count = glyph_filter @@ -1850,10 +1879,9 @@ struct ClassDefFormat2_4 template <typename set_t> bool collect_coverage (set_t *glyphs) const { - unsigned int count = rangeRecord.len; - for (unsigned int i = 0; i < count; i++) - if (rangeRecord[i].value) - if (unlikely (!rangeRecord[i].collect_coverage (glyphs))) + for (auto &range : rangeRecord) + if (range.value) + if (unlikely (!range.collect_coverage (glyphs))) return false; return true; } @@ -1861,11 +1889,10 @@ struct ClassDefFormat2_4 template <typename set_t> bool collect_class (set_t *glyphs, unsigned int klass) const { - unsigned int count = rangeRecord.len; - for (unsigned int i = 0; i < count; i++) + for (auto &range : rangeRecord) { - if (rangeRecord[i].value == klass) - if (unlikely (!rangeRecord[i].collect_coverage (glyphs))) + if (range.value == klass) + if (unlikely (!range.collect_coverage (glyphs))) return false; } return true; @@ -1873,32 +1900,32 @@ struct ClassDefFormat2_4 bool intersects (const hb_set_t *glyphs) const { - /* TODO Speed up, using hb_set_next() and bsearch()? */ - unsigned int count = rangeRecord.len; - for (unsigned int i = 0; i < count; i++) + if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2) { - const auto& range = rangeRecord[i]; - if (range.intersects (*glyphs) && range.value) - return true; + for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) + if (get_class (g)) + return true; + return false; } - return false; + + return hb_any (+ hb_iter (rangeRecord) + | hb_map ([glyphs] (const RangeRecord<Types> &range) { return range.intersects (*glyphs) && range.value; })); } bool intersects_class (const hb_set_t *glyphs, uint16_t klass) const { - unsigned int count = rangeRecord.len; if (klass == 0) { /* Match if there's any glyph that is not listed! */ hb_codepoint_t g = HB_SET_VALUE_INVALID; - for (unsigned int i = 0; i < count; i++) + for (auto &range : rangeRecord) { - if (!hb_set_next (glyphs, &g)) + if (!glyphs->next (&g)) break; - if (g < rangeRecord[i].first) + if (g < range.first) return true; - g = rangeRecord[i].last; + g = range.last; } - if (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g)) + if (g != HB_SET_VALUE_INVALID && glyphs->next (&g)) return true; /* Fall through. */ } @@ -1910,49 +1937,49 @@ struct ClassDefFormat2_4 void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const { - unsigned count = rangeRecord.len; if (klass == 0) { hb_codepoint_t g = HB_SET_VALUE_INVALID; - for (unsigned int i = 0; i < count; i++) + for (auto &range : rangeRecord) { - if (!hb_set_next (glyphs, &g)) + if (!glyphs->next (&g)) goto done; - while (g < rangeRecord[i].first) + while (g < range.first) { intersect_glyphs->add (g); - if (!hb_set_next (glyphs, &g)) + if (!glyphs->next (&g)) goto done; } - g = rangeRecord[i].last; + g = range.last; } - while (hb_set_next (glyphs, &g)) + while (glyphs->next (&g)) intersect_glyphs->add (g); done: return; } -#if 0 - /* The following implementation is faster asymptotically, but slower - * in practice. */ - if ((count >> 3) > glyphs->get_population ()) + unsigned count = rangeRecord.len; + if (count > glyphs->get_population () * hb_bit_storage (count) * 8) { for (hb_codepoint_t g = HB_SET_VALUE_INVALID; - hb_set_next (glyphs, &g);) - if (rangeRecord.as_array ().bfind (g)) + glyphs->next (&g);) + { + unsigned i; + if (rangeRecord.as_array ().bfind (g, &i) && + rangeRecord.arrayZ[i].value == klass) intersect_glyphs->add (g); + } return; } -#endif - for (unsigned int i = 0; i < count; i++) + for (auto &range : rangeRecord) { - if (rangeRecord[i].value != klass) continue; + if (range.value != klass) continue; - unsigned end = rangeRecord[i].last + 1; - for (hb_codepoint_t g = rangeRecord[i].first - 1; - hb_set_next (glyphs, &g) && g < end;) + unsigned end = range.last + 1; + for (hb_codepoint_t g = range.first - 1; + glyphs->next (&g) && g < end;) intersect_glyphs->add (g); } } @@ -1961,25 +1988,24 @@ struct ClassDefFormat2_4 { if (glyphs->is_empty ()) return; - unsigned count = rangeRecord.len; hb_codepoint_t g = HB_SET_VALUE_INVALID; - for (unsigned int i = 0; i < count; i++) + for (auto &range : rangeRecord) { - if (!hb_set_next (glyphs, &g)) + if (!glyphs->next (&g)) break; - if (g < rangeRecord[i].first) + if (g < range.first) { intersect_classes->add (0); break; } - g = rangeRecord[i].last; + g = range.last; } - if (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g)) + if (g != HB_SET_VALUE_INVALID && glyphs->next (&g)) intersect_classes->add (0); - for (const auto& record : rangeRecord.iter ()) - if (record.intersects (*glyphs)) - intersect_classes->add (record.value); + for (const auto& range : rangeRecord) + if (range.intersects (*glyphs)) + intersect_classes->add (range.value); } protected: @@ -1994,10 +2020,8 @@ struct ClassDefFormat2_4 struct ClassDef { /* Has interface. */ - static constexpr unsigned SENTINEL = 0; - typedef unsigned int value_t; - value_t operator [] (hb_codepoint_t k) const { return get (k); } - bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + unsigned operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } /* Projection. */ hb_codepoint_t operator () (hb_codepoint_t k) const { return get (k); } @@ -2015,6 +2039,19 @@ struct ClassDef } } + unsigned get_population () const + { + switch (u.format) { + case 1: return u.format1.get_population (); + case 2: return u.format2.get_population (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_population (); + case 4: return u.format4.get_population (); +#endif + default:return NOT_COVERED; + } + } + template<typename Iterator, hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> bool serialize (hb_serialize_context_t *c, Iterator it_with_class_zero) @@ -2332,7 +2369,7 @@ struct VarRegionList { unsigned int backward = region_map.backward (r); if (backward >= region_count) return_trace (false); - memcpy (&axesZ[axisCount * r], &src->axesZ[axisCount * backward], VarRegionAxis::static_size * axisCount); + hb_memcpy (&axesZ[axisCount * r], &src->axesZ[axisCount * backward], VarRegionAxis::static_size * axisCount); } return_trace (true); @@ -2442,21 +2479,26 @@ struct VarData unsigned ri_count = src->regionIndices.len; enum delta_size_t { kZero=0, kNonWord, kWord }; hb_vector_t<delta_size_t> delta_sz; - hb_vector_t<unsigned int> ri_map; /* maps old index to new index */ + hb_vector_t<unsigned int> ri_map; /* maps new index to old index */ delta_sz.resize (ri_count); ri_map.resize (ri_count); unsigned int new_word_count = 0; unsigned int r; + const HBUINT8 *src_delta_bytes = src->get_delta_bytes (); + unsigned src_row_size = src->get_row_size (); + unsigned src_word_count = src->wordCount (); + bool src_long_words = src->longWords (); + bool has_long = false; - if (src->longWords ()) + if (src_long_words) { - for (r = 0; r < ri_count; r++) + for (r = 0; r < src_word_count; r++) { for (unsigned int i = 0; i < inner_map.get_next_value (); i++) { unsigned int old = inner_map.backward (i); - int32_t delta = src->get_item_delta (old, r); + int32_t delta = src->get_item_delta_fast (old, r, src_delta_bytes, src_row_size); if (delta < -65536 || 65535 < delta) { has_long = true; @@ -2470,11 +2512,13 @@ struct VarData signed max_threshold = has_long ? +65535 : +127; for (r = 0; r < ri_count; r++) { + bool short_circuit = src_long_words == has_long && src_word_count <= r; + delta_sz[r] = kZero; for (unsigned int i = 0; i < inner_map.get_next_value (); i++) { unsigned int old = inner_map.backward (i); - int32_t delta = src->get_item_delta (old, r); + int32_t delta = src->get_item_delta_fast (old, r, src_delta_bytes, src_row_size); if (delta < min_threshold || max_threshold < delta) { delta_sz[r] = kWord; @@ -2482,7 +2526,11 @@ struct VarData break; } else if (delta != 0) + { delta_sz[r] = kNonWord; + if (short_circuit) + break; + } } } @@ -2492,7 +2540,8 @@ struct VarData for (r = 0; r < ri_count; r++) if (delta_sz[r]) { - ri_map[r] = (delta_sz[r] == kWord)? word_index++ : non_word_index++; + unsigned new_r = (delta_sz[r] == kWord)? word_index++ : non_word_index++; + ri_map[new_r] = r; new_ri_count++; } @@ -2502,14 +2551,20 @@ struct VarData if (unlikely (!c->extend (this))) return_trace (false); - for (r = 0; r < ri_count; r++) - if (delta_sz[r]) regionIndices[ri_map[r]] = region_map[src->regionIndices[r]]; + for (r = 0; r < new_ri_count; r++) + regionIndices[r] = region_map[src->regionIndices[ri_map[r]]]; - for (unsigned int i = 0; i < itemCount; i++) + HBUINT8 *delta_bytes = get_delta_bytes (); + unsigned row_size = get_row_size (); + unsigned count = itemCount; + for (unsigned int i = 0; i < count; i++) { - unsigned int old = inner_map.backward (i); - for (unsigned int r = 0; r < ri_count; r++) - if (delta_sz[r]) set_item_delta (i, ri_map[r], src->get_item_delta (old, r)); + unsigned int old = inner_map.backward (i); + for (unsigned int r = 0; r < new_ri_count; r++) + set_item_delta_fast (i, r, + src->get_item_delta_fast (old, ri_map[r], + src_delta_bytes, src_row_size), + delta_bytes, row_size); } return_trace (true); @@ -2517,12 +2572,15 @@ struct VarData void collect_region_refs (hb_set_t ®ion_indices, const hb_inc_bimap_t &inner_map) const { + const HBUINT8 *delta_bytes = get_delta_bytes (); + unsigned row_size = get_row_size (); + for (unsigned int r = 0; r < regionIndices.len; r++) { - unsigned int region = regionIndices[r]; + unsigned int region = regionIndices.arrayZ[r]; if (region_indices.has (region)) continue; for (unsigned int i = 0; i < inner_map.get_next_value (); i++) - if (get_item_delta (inner_map.backward (i), r) != 0) + if (get_item_delta_fast (inner_map.backward (i), r, delta_bytes, row_size) != 0) { region_indices.add (region); break; @@ -2537,10 +2595,12 @@ struct VarData HBUINT8 *get_delta_bytes () { return &StructAfter<HBUINT8> (regionIndices); } - int32_t get_item_delta (unsigned int item, unsigned int region) const + int32_t get_item_delta_fast (unsigned int item, unsigned int region, + const HBUINT8 *delta_bytes, unsigned row_size) const { - if ( item >= itemCount || unlikely (region >= regionIndices.len)) return 0; - const HBINT8 *p = (const HBINT8 *) get_delta_bytes () + item * get_row_size (); + if (unlikely (item >= itemCount || region >= regionIndices.len)) return 0; + + const HBINT8 *p = (const HBINT8 *) delta_bytes + item * row_size; unsigned word_count = wordCount (); bool is_long = longWords (); if (is_long) @@ -2558,10 +2618,17 @@ struct VarData return (p + HBINT16::static_size * word_count)[region - word_count]; } } + int32_t get_item_delta (unsigned int item, unsigned int region) const + { + return get_item_delta_fast (item, region, + get_delta_bytes (), + get_row_size ()); + } - void set_item_delta (unsigned int item, unsigned int region, int32_t delta) + void set_item_delta_fast (unsigned int item, unsigned int region, int32_t delta, + HBUINT8 *delta_bytes, unsigned row_size) { - HBINT8 *p = (HBINT8 *)get_delta_bytes () + item * get_row_size (); + HBINT8 *p = (HBINT8 *) delta_bytes + item * row_size; unsigned word_count = wordCount (); bool is_long = longWords (); if (is_long) @@ -2579,6 +2646,12 @@ struct VarData (p + HBINT16::static_size * word_count)[region - word_count] = delta; } } + void set_item_delta (unsigned int item, unsigned int region, int32_t delta) + { + set_item_delta_fast (item, region, delta, + get_delta_bytes (), + get_row_size ()); + } bool longWords () const { return wordSizeCount & 0x8000u /* LONG_WORDS */; } unsigned wordCount () const { return wordSizeCount & 0x7FFFu /* WORD_DELTA_COUNT_MASK */; } @@ -2642,6 +2715,14 @@ struct VariationStore unsigned int inner = index & 0xFFFF; return get_delta (outer, inner, coords, coord_count, cache); } + float get_delta (unsigned int index, + hb_array_t<int> coords, + VarRegionList::cache_t *cache = nullptr) const + { + return get_delta (index, + coords.arrayZ, coords.length, + cache); + } bool sanitize (hb_sanitize_context_t *c) const { @@ -2948,7 +3029,7 @@ struct ConditionSet // all conditions met if (num_kept_cond == 0) return DROP_COND_WITH_VAR; - + //check if condition_set is unique with variations if (c->conditionset_map->has (p)) //duplicate found, drop the entire record @@ -3420,17 +3501,16 @@ struct VariationDevice { TRACE_SERIALIZE (this); if (!layout_variation_idx_delta_map) return_trace (nullptr); - auto snap = c->snapshot (); + + hb_pair_t<unsigned, int> *v; + if (!layout_variation_idx_delta_map->has (varIdx, &v)) + return_trace (nullptr); + + c->start_zerocopy (this->static_size); auto *out = c->embed (this); if (unlikely (!out)) return_trace (nullptr); - /* TODO Just get() and bail if NO_VARIATION. Needs to setup the map to return that. */ - if (!layout_variation_idx_delta_map->has (varIdx)) - { - c->revert (snap); - return_trace (nullptr); - } - unsigned new_idx = hb_first (layout_variation_idx_delta_map->get (varIdx)); + unsigned new_idx = hb_first (*v); out->varIdx = new_idx; return_trace (out); } diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh index 22925fdfa9..a84edef162 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh @@ -73,7 +73,7 @@ struct AttachList if (point_count) { - + points.sub_array (start_offset, point_count) + + points.as_array ().sub_array (start_offset, point_count) | hb_sink (hb_array (point_array, *point_count)) ; } @@ -322,7 +322,7 @@ struct LigGlyph { if (caret_count) { - + carets.sub_array (start_offset, caret_count) + + carets.as_array ().sub_array (start_offset, caret_count) | hb_map (hb_add (this)) | hb_map ([&] (const CaretValue &value) { return value.get_caret_value (font, direction, glyph_id, var_store); }) | hb_sink (hb_array (caret_array, *caret_count)) diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh index d1d94255f4..04ccefd108 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh +++ b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh @@ -100,8 +100,8 @@ struct hb_closure_context_t : bool is_lookup_done (unsigned int lookup_index) { - if (done_lookups_glyph_count->in_error () || - done_lookups_glyph_set->in_error ()) + if (unlikely (done_lookups_glyph_count->in_error () || + done_lookups_glyph_set->in_error ())) return true; /* Have we visited this lookup with the current set of glyphs? */ @@ -535,7 +535,12 @@ struct hb_ot_apply_context_t : bool next (unsigned *unsafe_to = nullptr) { assert (num_items > 0); - while (idx + num_items < end) + /* The alternate condition below is faster at string boundaries, + * but produces subpar "unsafe-to-concat" values. */ + signed stop = (signed) end - (signed) num_items; + if (c->buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) + stop = (signed) end - 1; + while ((signed) idx < stop) { idx++; hb_glyph_info_t &info = c->buffer->info[idx]; @@ -568,7 +573,12 @@ struct hb_ot_apply_context_t : bool prev (unsigned *unsafe_from = nullptr) { assert (num_items > 0); - while (idx > num_items - 1) + /* The alternate condition below is faster at string boundaries, + * but produces subpar "unsafe-to-concat" values. */ + unsigned stop = num_items - 1; + if (c->buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) + stop = 1 - 1; + while (idx > stop) { idx--; hb_glyph_info_t &info = c->buffer->out_info[idx]; @@ -672,6 +682,7 @@ struct hb_ot_apply_context_t : const GDEF &gdef; const VariationStore &var_store; VariationStore::cache_t *var_store_cache; + hb_set_digest_t digest; hb_direction_t direction; hb_mask_t lookup_mask = 1; @@ -707,6 +718,7 @@ struct hb_ot_apply_context_t : nullptr #endif ), + digest (buffer_->digest ()), direction (buffer_->props.direction), has_glyph_classes (gdef.has_glyph_classes ()) { init_iters (); } @@ -781,8 +793,10 @@ struct hb_ot_apply_context_t : void _set_glyph_class (hb_codepoint_t glyph_index, unsigned int class_guess = 0, bool ligature = false, - bool component = false) const + bool component = false) { + digest.add (glyph_index); + if (new_syllables != (unsigned) -1) buffer->cur().syllable() = new_syllables; @@ -815,24 +829,24 @@ struct hb_ot_apply_context_t : _hb_glyph_info_set_glyph_props (&buffer->cur(), props); } - void replace_glyph (hb_codepoint_t glyph_index) const + void replace_glyph (hb_codepoint_t glyph_index) { _set_glyph_class (glyph_index); (void) buffer->replace_glyph (glyph_index); } - void replace_glyph_inplace (hb_codepoint_t glyph_index) const + void replace_glyph_inplace (hb_codepoint_t glyph_index) { _set_glyph_class (glyph_index); buffer->cur().codepoint = glyph_index; } void replace_glyph_with_ligature (hb_codepoint_t glyph_index, - unsigned int class_guess) const + unsigned int class_guess) { _set_glyph_class (glyph_index, class_guess, true); (void) buffer->replace_glyph (glyph_index); } void output_glyph_for_component (hb_codepoint_t glyph_index, - unsigned int class_guess) const + unsigned int class_guess) { _set_glyph_class (glyph_index, class_guess, false, true); (void) buffer->output_glyph (glyph_index); @@ -844,7 +858,7 @@ struct hb_accelerate_subtables_context_t : hb_dispatch_context_t<hb_accelerate_subtables_context_t> { template <typename Type> - static inline bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c) + static inline bool apply_to (const void *obj, hb_ot_apply_context_t *c) { const Type *typed_obj = (const Type *) obj; return typed_obj->apply (c); @@ -852,30 +866,30 @@ struct hb_accelerate_subtables_context_t : #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE template <typename T> - static inline auto apply_cached_ (const T *obj, OT::hb_ot_apply_context_t *c, hb_priority<1>) HB_RETURN (bool, obj->apply (c, true) ) + static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, hb_priority<1>) HB_RETURN (bool, obj->apply (c, true) ) template <typename T> - static inline auto apply_cached_ (const T *obj, OT::hb_ot_apply_context_t *c, hb_priority<0>) HB_RETURN (bool, obj->apply (c) ) + static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, hb_priority<0>) HB_RETURN (bool, obj->apply (c) ) template <typename Type> - static inline bool apply_cached_to (const void *obj, OT::hb_ot_apply_context_t *c) + static inline bool apply_cached_to (const void *obj, hb_ot_apply_context_t *c) { const Type *typed_obj = (const Type *) obj; return apply_cached_ (typed_obj, c, hb_prioritize); } template <typename T> - static inline auto cache_func_ (const T *obj, OT::hb_ot_apply_context_t *c, bool enter, hb_priority<1>) HB_RETURN (bool, obj->cache_func (c, enter) ) + static inline auto cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<1>) HB_RETURN (bool, obj->cache_func (c, enter) ) template <typename T> - static inline bool cache_func_ (const T *obj, OT::hb_ot_apply_context_t *c, bool enter, hb_priority<0>) { return false; } + static inline bool cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<0>) { return false; } template <typename Type> - static inline bool cache_func_to (const void *obj, OT::hb_ot_apply_context_t *c, bool enter) + static inline bool cache_func_to (const void *obj, hb_ot_apply_context_t *c, bool enter) { const Type *typed_obj = (const Type *) obj; return cache_func_ (typed_obj, c, enter, hb_prioritize); } #endif - typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_ot_apply_context_t *c); - typedef bool (*hb_cache_func_t) (const void *obj, OT::hb_ot_apply_context_t *c, bool enter); + typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c); + typedef bool (*hb_cache_func_t) (const void *obj, hb_ot_apply_context_t *c, bool enter); struct hb_applicable_t { @@ -901,20 +915,20 @@ struct hb_accelerate_subtables_context_t : obj_.get_coverage ().collect_coverage (&digest); } - bool apply (OT::hb_ot_apply_context_t *c) const + bool apply (hb_ot_apply_context_t *c) const { return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c); } #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - bool apply_cached (OT::hb_ot_apply_context_t *c) const + bool apply_cached (hb_ot_apply_context_t *c) const { return digest.may_have (c->buffer->cur().codepoint) && apply_cached_func (obj, c); } - bool cache_enter (OT::hb_ot_apply_context_t *c) const + bool cache_enter (hb_ot_apply_context_t *c) const { return cache_func (obj, c, true); } - void cache_leave (OT::hb_ot_apply_context_t *c) const + void cache_leave (hb_ot_apply_context_t *c) const { cache_func (obj, c, false); } @@ -988,8 +1002,8 @@ struct hb_accelerate_subtables_context_t : }; -typedef bool (*intersects_func_t) (const hb_set_t *glyphs, unsigned value, const void *data); -typedef void (*intersected_glyphs_func_t) (const hb_set_t *glyphs, const void *data, unsigned value, hb_set_t *intersected_glyphs); +typedef bool (*intersects_func_t) (const hb_set_t *glyphs, unsigned value, const void *data, void *cache); +typedef void (*intersected_glyphs_func_t) (const hb_set_t *glyphs, const void *data, unsigned value, hb_set_t *intersected_glyphs, void *cache); typedef void (*collect_glyphs_func_t) (hb_set_t *glyphs, unsigned value, const void *data); typedef bool (*match_func_t) (hb_glyph_info_t &info, unsigned value, const void *data); @@ -1012,16 +1026,25 @@ struct ChainContextApplyFuncs }; -static inline bool intersects_glyph (const hb_set_t *glyphs, unsigned value, const void *data HB_UNUSED) +static inline bool intersects_glyph (const hb_set_t *glyphs, unsigned value, const void *data HB_UNUSED, void *cache HB_UNUSED) { return glyphs->has (value); } -static inline bool intersects_class (const hb_set_t *glyphs, unsigned value, const void *data) +static inline bool intersects_class (const hb_set_t *glyphs, unsigned value, const void *data, void *cache) { const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); - return class_def.intersects_class (glyphs, value); + hb_map_t *map = (hb_map_t *) cache; + + hb_codepoint_t *cached_v; + if (map->has (value, &cached_v)) + return *cached_v; + + bool v = class_def.intersects_class (glyphs, value); + map->set (value, v); + + return v; } -static inline bool intersects_coverage (const hb_set_t *glyphs, unsigned value, const void *data) +static inline bool intersects_coverage (const hb_set_t *glyphs, unsigned value, const void *data, void *cache HB_UNUSED) { Offset16To<Coverage> coverage; coverage = value; @@ -1029,17 +1052,36 @@ static inline bool intersects_coverage (const hb_set_t *glyphs, unsigned value, } -static inline void intersected_glyph (const hb_set_t *glyphs HB_UNUSED, const void *data, unsigned value, hb_set_t *intersected_glyphs) +static inline void intersected_glyph (const hb_set_t *glyphs HB_UNUSED, const void *data, unsigned value, hb_set_t *intersected_glyphs, HB_UNUSED void *cache) { unsigned g = reinterpret_cast<const HBUINT16 *>(data)[value]; intersected_glyphs->add (g); } -static inline void intersected_class_glyphs (const hb_set_t *glyphs, const void *data, unsigned value, hb_set_t *intersected_glyphs) + +using intersected_class_cache_t = hb_hashmap_t<unsigned, hb_set_t>; + +static inline void intersected_class_glyphs (const hb_set_t *glyphs, const void *data, unsigned value, hb_set_t *intersected_glyphs, void *cache) { const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); - class_def.intersected_class_glyphs (glyphs, value, intersected_glyphs); + + intersected_class_cache_t *map = (intersected_class_cache_t *) cache; + + hb_set_t *cached_v; + if (map->has (value, &cached_v)) + { + intersected_glyphs->union_ (*cached_v); + return; + } + + hb_set_t v; + class_def.intersected_class_glyphs (glyphs, value, &v); + + intersected_glyphs->union_ (v); + + map->set (value, std::move (v)); } -static inline void intersected_coverage_glyphs (const hb_set_t *glyphs, const void *data, unsigned value, hb_set_t *intersected_glyphs) + +static inline void intersected_coverage_glyphs (const hb_set_t *glyphs, const void *data, unsigned value, hb_set_t *intersected_glyphs, HB_UNUSED void *cache) { Offset16To<Coverage> coverage; coverage = value; @@ -1052,10 +1094,11 @@ static inline bool array_is_subset_of (const hb_set_t *glyphs, unsigned int count, const HBUINT values[], intersects_func_t intersects_func, - const void *intersects_data) + const void *intersects_data, + void *cache) { for (const auto &_ : + hb_iter (values, count)) - if (!intersects_func (glyphs, _, intersects_data)) return false; + if (!intersects_func (glyphs, _, intersects_data, cache)) return false; return true; } @@ -1492,7 +1535,8 @@ static void context_closure_recurse_lookups (hb_closure_context_t *c, unsigned value, ContextFormat context_format, const void *data, - intersected_glyphs_func_t intersected_glyphs_func) + intersected_glyphs_func_t intersected_glyphs_func, + void *cache) { hb_set_t *covered_seq_indicies = hb_set_create (); for (unsigned int i = 0; i < lookupCount; i++) @@ -1513,7 +1557,7 @@ static void context_closure_recurse_lookups (hb_closure_context_t *c, pos_glyphs.add (value); break; case ContextFormat::ClassBasedContext: - intersected_glyphs_func (&c->parent_active_glyphs (), data, value, &pos_glyphs); + intersected_glyphs_func (&c->parent_active_glyphs (), data, value, &pos_glyphs, cache); break; case ContextFormat::CoverageBasedContext: pos_glyphs.set (c->parent_active_glyphs ()); @@ -1530,7 +1574,7 @@ static void context_closure_recurse_lookups (hb_closure_context_t *c, input_value = input[seqIndex - 1]; } - intersected_glyphs_func (c->glyphs, input_data, input_value, &pos_glyphs); + intersected_glyphs_func (c->glyphs, input_data, input_value, &pos_glyphs, cache); } } @@ -1710,6 +1754,8 @@ struct ContextClosureLookupContext ContextClosureFuncs funcs; ContextFormat context_format; const void *intersects_data; + void *intersects_cache; + void *intersected_glyphs_cache; }; struct ContextCollectGlyphsLookupContext @@ -1732,7 +1778,9 @@ static inline bool context_intersects (const hb_set_t *glyphs, { return array_is_subset_of (glyphs, inputCount ? inputCount - 1 : 0, input, - lookup_context.funcs.intersects, lookup_context.intersects_data); + lookup_context.funcs.intersects, + lookup_context.intersects_data, + lookup_context.intersects_cache); } template <typename HBUINT> @@ -1753,7 +1801,8 @@ static inline void context_closure_lookup (hb_closure_context_t *c, value, lookup_context.context_format, lookup_context.intersects_data, - lookup_context.funcs.intersected_glyphs); + lookup_context.funcs.intersected_glyphs, + lookup_context.intersected_glyphs_cache); } template <typename HBUINT> @@ -1777,7 +1826,7 @@ static inline bool context_would_apply_lookup (hb_would_apply_context_t *c, const HBUINT input[], /* Array of input values--start with second glyph */ unsigned int lookupCount HB_UNUSED, const LookupRecord lookupRecord[] HB_UNUSED, - ContextApplyLookupContext &lookup_context) + const ContextApplyLookupContext &lookup_context) { return would_match_input (c, inputCount, input, @@ -1790,7 +1839,7 @@ static inline bool context_apply_lookup (hb_ot_apply_context_t *c, const HBUINT input[], /* Array of input values--start with second glyph */ unsigned int lookupCount, const LookupRecord lookupRecord[], - ContextApplyLookupContext &lookup_context) + const ContextApplyLookupContext &lookup_context) { unsigned match_end = 0; unsigned match_positions[HB_MAX_CONTEXT_LENGTH]; @@ -1858,7 +1907,7 @@ struct Rule } bool would_apply (hb_would_apply_context_t *c, - ContextApplyLookupContext &lookup_context) const + const ContextApplyLookupContext &lookup_context) const { const auto &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>> (inputZ.as_array (inputCount ? inputCount - 1 : 0)); @@ -1869,7 +1918,7 @@ struct Rule } bool apply (hb_ot_apply_context_t *c, - ContextApplyLookupContext &lookup_context) const + const ContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); const auto &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>> @@ -1989,7 +2038,7 @@ struct RuleSet } bool would_apply (hb_would_apply_context_t *c, - ContextApplyLookupContext &lookup_context) const + const ContextApplyLookupContext &lookup_context) const { return + hb_iter (rule) @@ -2000,7 +2049,7 @@ struct RuleSet } bool apply (hb_ot_apply_context_t *c, - ContextApplyLookupContext &lookup_context) const + const ContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); return_trace ( @@ -2108,7 +2157,7 @@ struct ContextFormat1_4 void closure_lookups (hb_closure_lookups_context_t *c) const { struct ContextClosureLookupContext lookup_context = { - {intersects_glyph, intersected_glyph}, + {intersects_glyph, nullptr}, ContextFormat::SimpleContext, nullptr }; @@ -2220,10 +2269,12 @@ struct ContextFormat2_5 const ClassDef &class_def = this+classDef; + hb_map_t cache; struct ContextClosureLookupContext lookup_context = { - {intersects_class, intersected_class_glyphs}, + {intersects_class, nullptr}, ContextFormat::ClassBasedContext, - &class_def + &class_def, + &cache }; hb_set_t retained_coverage_glyphs; @@ -2259,10 +2310,14 @@ struct ContextFormat2_5 const ClassDef &class_def = this+classDef; + hb_map_t cache; + intersected_class_cache_t intersected_cache; struct ContextClosureLookupContext lookup_context = { {intersects_class, intersected_class_glyphs}, ContextFormat::ClassBasedContext, - &class_def + &class_def, + &cache, + &intersected_cache }; + hb_enumerate (ruleSet) @@ -2286,10 +2341,12 @@ struct ContextFormat2_5 const ClassDef &class_def = this+classDef; + hb_map_t cache; struct ContextClosureLookupContext lookup_context = { - {intersects_class, intersected_class_glyphs}, + {intersects_class, nullptr}, ContextFormat::ClassBasedContext, - &class_def + &class_def, + &cache }; + hb_iter (ruleSet) @@ -2407,6 +2464,7 @@ struct ContextFormat2_5 const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; bool ret = true; int non_zero_index = -1, index = 0; + auto snapshot = c->serializer->snapshot(); for (const auto& _ : + hb_enumerate (ruleSet) | hb_filter (klass_map, hb_first)) { @@ -2418,8 +2476,10 @@ struct ContextFormat2_5 } if (coverage_glyph_classes.has (_.first) && - o->serialize_subset (c, _.second, this, lookup_map, &klass_map)) + o->serialize_subset (c, _.second, this, lookup_map, &klass_map)) { non_zero_index = index; + snapshot = c->serializer->snapshot(); + } index++; } @@ -2433,6 +2493,7 @@ struct ContextFormat2_5 out->ruleSet.pop (); index--; } + c->serializer->revert (snapshot); return_trace (bool (out->ruleSet)); } @@ -2469,7 +2530,7 @@ struct ContextFormat3 return false; struct ContextClosureLookupContext lookup_context = { - {intersects_coverage, intersected_coverage_glyphs}, + {intersects_coverage, nullptr}, ContextFormat::CoverageBasedContext, this }; @@ -2655,6 +2716,8 @@ struct ChainContextClosureLookupContext ContextClosureFuncs funcs; ContextFormat context_format; const void *intersects_data[3]; + void *intersects_cache[3]; + void *intersected_glyphs_cache; }; struct ChainContextCollectGlyphsLookupContext @@ -2681,13 +2744,19 @@ static inline bool chain_context_intersects (const hb_set_t *glyphs, { return array_is_subset_of (glyphs, backtrackCount, backtrack, - lookup_context.funcs.intersects, lookup_context.intersects_data[0]) + lookup_context.funcs.intersects, + lookup_context.intersects_data[0], + lookup_context.intersects_cache[0]) && array_is_subset_of (glyphs, inputCount ? inputCount - 1 : 0, input, - lookup_context.funcs.intersects, lookup_context.intersects_data[1]) + lookup_context.funcs.intersects, + lookup_context.intersects_data[1], + lookup_context.intersects_cache[1]) && array_is_subset_of (glyphs, lookaheadCount, lookahead, - lookup_context.funcs.intersects, lookup_context.intersects_data[2]); + lookup_context.funcs.intersects, + lookup_context.intersects_data[2], + lookup_context.intersects_cache[2]); } template <typename HBUINT> @@ -2714,7 +2783,8 @@ static inline void chain_context_closure_lookup (hb_closure_context_t *c, value, lookup_context.context_format, lookup_context.intersects_data[1], - lookup_context.funcs.intersected_glyphs); + lookup_context.funcs.intersected_glyphs, + lookup_context.intersected_glyphs_cache); } template <typename HBUINT> @@ -2752,7 +2822,7 @@ static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c const HBUINT lookahead[] HB_UNUSED, unsigned int lookupCount HB_UNUSED, const LookupRecord lookupRecord[] HB_UNUSED, - ChainContextApplyLookupContext &lookup_context) + const ChainContextApplyLookupContext &lookup_context) { return (c->zero_context ? !backtrackCount && !lookaheadCount : true) && would_match_input (c, @@ -2770,7 +2840,7 @@ static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c, const HBUINT lookahead[], unsigned int lookupCount, const LookupRecord lookupRecord[], - ChainContextApplyLookupContext &lookup_context) + const ChainContextApplyLookupContext &lookup_context) { unsigned end_index = c->buffer->idx; unsigned match_end = 0; @@ -2864,7 +2934,7 @@ struct ChainRule } bool would_apply (hb_would_apply_context_t *c, - ChainContextApplyLookupContext &lookup_context) const + const ChainContextApplyLookupContext &lookup_context) const { const auto &input = StructAfter<decltype (inputX)> (backtrack); const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); @@ -2876,7 +2946,8 @@ struct ChainRule lookup.arrayZ, lookup_context); } - bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const + bool apply (hb_ot_apply_context_t *c, + const ChainContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); const auto &input = StructAfter<decltype (inputX)> (backtrack); @@ -3042,7 +3113,8 @@ struct ChainRuleSet ; } - bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const + bool would_apply (hb_would_apply_context_t *c, + const ChainContextApplyLookupContext &lookup_context) const { return + hb_iter (rule) @@ -3052,7 +3124,8 @@ struct ChainRuleSet ; } - bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const + bool apply (hb_ot_apply_context_t *c, + const ChainContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); return_trace ( @@ -3166,7 +3239,7 @@ struct ChainContextFormat1_4 void closure_lookups (hb_closure_lookups_context_t *c) const { struct ChainContextClosureLookupContext lookup_context = { - {intersects_glyph, intersected_glyph}, + {intersects_glyph, nullptr}, ContextFormat::SimpleContext, {nullptr, nullptr, nullptr} }; @@ -3278,12 +3351,14 @@ struct ChainContextFormat2_5 const ClassDef &input_class_def = this+inputClassDef; const ClassDef &lookahead_class_def = this+lookaheadClassDef; + hb_map_t caches[3] = {}; struct ChainContextClosureLookupContext lookup_context = { - {intersects_class, intersected_class_glyphs}, + {intersects_class, nullptr}, ContextFormat::ClassBasedContext, {&backtrack_class_def, &input_class_def, - &lookahead_class_def} + &lookahead_class_def}, + {&caches[0], &caches[1], &caches[2]} }; hb_set_t retained_coverage_glyphs; @@ -3321,12 +3396,16 @@ struct ChainContextFormat2_5 const ClassDef &input_class_def = this+inputClassDef; const ClassDef &lookahead_class_def = this+lookaheadClassDef; + hb_map_t caches[3] = {}; + intersected_class_cache_t intersected_cache; struct ChainContextClosureLookupContext lookup_context = { {intersects_class, intersected_class_glyphs}, ContextFormat::ClassBasedContext, {&backtrack_class_def, &input_class_def, - &lookahead_class_def} + &lookahead_class_def}, + {&caches[0], &caches[1], &caches[2]}, + &intersected_cache }; + hb_enumerate (ruleSet) @@ -3352,12 +3431,14 @@ struct ChainContextFormat2_5 const ClassDef &input_class_def = this+inputClassDef; const ClassDef &lookahead_class_def = this+lookaheadClassDef; + hb_map_t caches[3] = {}; struct ChainContextClosureLookupContext lookup_context = { - {intersects_class, intersected_class_glyphs}, + {intersects_class, nullptr}, ContextFormat::ClassBasedContext, {&backtrack_class_def, &input_class_def, - &lookahead_class_def} + &lookahead_class_def}, + {&caches[0], &caches[1], &caches[2]} }; + hb_iter (ruleSet) @@ -3587,7 +3668,7 @@ struct ChainContextFormat3 const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); struct ChainContextClosureLookupContext lookup_context = { - {intersects_coverage, intersected_coverage_glyphs}, + {intersects_coverage, nullptr}, ContextFormat::CoverageBasedContext, {this, this, this} }; @@ -3938,13 +4019,14 @@ struct hb_ot_layout_lookup_accelerator_t template <typename TLookup> void init (const TLookup &lookup) { - digest.init (); - lookup.collect_coverage (&digest); - subtables.init (); - OT::hb_accelerate_subtables_context_t c_accelerate_subtables (subtables); + hb_accelerate_subtables_context_t c_accelerate_subtables (subtables); lookup.dispatch (&c_accelerate_subtables); + digest.init (); + for (auto& subtable : hb_iter (subtables)) + digest.add (subtable.digest); + #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE cache_user_idx = c_accelerate_subtables.cache_user_idx; for (unsigned i = 0; i < subtables.length; i++) @@ -3962,21 +4044,25 @@ struct hb_ot_layout_lookup_accelerator_t #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE if (use_cache) { - for (unsigned int i = 0; i < subtables.length; i++) - if (subtables[i].apply_cached (c)) - return true; + return + + hb_iter (subtables) + | hb_map ([&c] (const hb_accelerate_subtables_context_t::hb_applicable_t &_) { return _.apply_cached (c); }) + | hb_any + ; } else #endif { - for (unsigned int i = 0; i < subtables.length; i++) - if (subtables[i].apply (c)) - return true; + return + + hb_iter (subtables) + | hb_map ([&c] (const hb_accelerate_subtables_context_t::hb_applicable_t &_) { return _.apply (c); }) + | hb_any + ; } return false; } - bool cache_enter (OT::hb_ot_apply_context_t *c) const + bool cache_enter (hb_ot_apply_context_t *c) const { #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE return cache_user_idx != (unsigned) -1 && @@ -3985,7 +4071,7 @@ struct hb_ot_layout_lookup_accelerator_t return false; #endif } - void cache_leave (OT::hb_ot_apply_context_t *c) const + void cache_leave (hb_ot_apply_context_t *c) const { #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE subtables[cache_user_idx].cache_leave (c); @@ -3993,8 +4079,8 @@ struct hb_ot_layout_lookup_accelerator_t } - private: hb_set_digest_t digest; + private: hb_accelerate_subtables_context_t::array_t subtables; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE unsigned cache_user_idx = (unsigned) -1; @@ -4255,11 +4341,11 @@ struct GSUBGPOS hb_set_t *lookup_indexes /* IN/OUT */) const { hb_set_t visited_lookups, inactive_lookups; - OT::hb_closure_lookups_context_t c (face, glyphs, &visited_lookups, &inactive_lookups); + hb_closure_lookups_context_t c (face, glyphs, &visited_lookups, &inactive_lookups); c.set_recurse_func (TLookup::template dispatch_recurse_func<hb_closure_lookups_context_t>); - for (unsigned lookup_index : + hb_iter (lookup_indexes)) + for (unsigned lookup_index : *lookup_indexes) reinterpret_cast<const TLookup &> (get_lookup (lookup_index)).closure_lookups (&c, lookup_index); hb_set_union (lookup_indexes, &visited_lookups); @@ -4301,7 +4387,7 @@ struct GSUBGPOS } #endif - for (unsigned i : feature_indices->iter()) + for (unsigned i : hb_iter (feature_indices)) { hb_tag_t tag = get_feature_tag (i); if (tag == HB_TAG ('p', 'r', 'e', 'f')) diff --git a/thirdparty/harfbuzz/src/hb-ot-layout.cc b/thirdparty/harfbuzz/src/hb-ot-layout.cc index dbb30076a2..e8091ec3e0 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout.cc +++ b/thirdparty/harfbuzz/src/hb-ot-layout.cc @@ -1465,56 +1465,6 @@ hb_ot_layout_substitute_start (hb_font_t *font, _hb_ot_layout_set_glyph_props (font, buffer); } -void -hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer, - bool (*filter) (const hb_glyph_info_t *info)) -{ - /* Merge clusters and delete filtered glyphs. - * NOTE! We can't use out-buffer as we have positioning data. */ - unsigned int j = 0; - unsigned int count = buffer->len; - hb_glyph_info_t *info = buffer->info; - hb_glyph_position_t *pos = buffer->pos; - for (unsigned int i = 0; i < count; i++) - { - if (filter (&info[i])) - { - /* Merge clusters. - * Same logic as buffer->delete_glyph(), but for in-place removal. */ - - unsigned int cluster = info[i].cluster; - if (i + 1 < count && cluster == info[i + 1].cluster) - continue; /* Cluster survives; do nothing. */ - - if (j) - { - /* Merge cluster backward. */ - if (cluster < info[j - 1].cluster) - { - unsigned int mask = info[i].mask; - unsigned int old_cluster = info[j - 1].cluster; - for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--) - buffer->set_cluster (info[k - 1], cluster, mask); - } - continue; - } - - if (i + 1 < count) - buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */ - - continue; - } - - if (j != i) - { - info[j] = info[i]; - pos[j] = pos[i]; - } - j++; - } - buffer->len = j; -} - /** * hb_ot_layout_lookup_substitute_closure: * @face: #hb_face_t to work upon @@ -1867,7 +1817,7 @@ apply_forward (OT::hb_ot_apply_context_t *c, while (buffer->idx < buffer->len && buffer->successful) { bool applied = false; - if (accel.may_have (buffer->cur().codepoint) && + if (accel.digest.may_have (buffer->cur().codepoint) && (buffer->cur().mask & c->lookup_mask) && c->check_glyph_property (&buffer->cur(), c->lookup_props)) { @@ -1894,7 +1844,7 @@ apply_backward (OT::hb_ot_apply_context_t *c, hb_buffer_t *buffer = c->buffer; do { - if (accel.may_have (buffer->cur().codepoint) && + if (accel.digest.may_have (buffer->cur().codepoint) && (buffer->cur().mask & c->lookup_mask) && c->check_glyph_property (&buffer->cur(), c->lookup_props)) ret |= accel.apply (c, false); @@ -1908,15 +1858,16 @@ apply_backward (OT::hb_ot_apply_context_t *c, } template <typename Proxy> -static inline void +static inline bool apply_string (OT::hb_ot_apply_context_t *c, const typename Proxy::Lookup &lookup, const OT::hb_ot_layout_lookup_accelerator_t &accel) { + bool ret = false; hb_buffer_t *buffer = c->buffer; if (unlikely (!buffer->len || !c->lookup_mask)) - return; + return ret; c->set_lookup_props (lookup.get_props ()); @@ -1927,7 +1878,7 @@ apply_string (OT::hb_ot_apply_context_t *c, buffer->clear_output (); buffer->idx = 0; - apply_forward (c, accel); + ret = apply_forward (c, accel); if (!Proxy::always_inplace) buffer->sync (); @@ -1937,8 +1888,10 @@ apply_string (OT::hb_ot_apply_context_t *c, /* in-place backward substitution/positioning */ assert (!buffer->have_output); buffer->idx = buffer->len - 1; - apply_backward (c, accel); + ret = apply_backward (c, accel); } + + return ret; } template <typename Proxy> @@ -1957,23 +1910,42 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, const stage_map_t *stage = &stages[table_index][stage_index]; for (; i < stage->last_lookup; i++) { - unsigned int lookup_index = lookups[table_index][i].index; - if (!buffer->message (font, "start lookup %d", lookup_index)) continue; - c.set_lookup_index (lookup_index); - c.set_lookup_mask (lookups[table_index][i].mask); - c.set_auto_zwj (lookups[table_index][i].auto_zwj); - c.set_auto_zwnj (lookups[table_index][i].auto_zwnj); - c.set_random (lookups[table_index][i].random); - c.set_per_syllable (lookups[table_index][i].per_syllable); - - apply_string<Proxy> (&c, - proxy.table.get_lookup (lookup_index), - proxy.accels[lookup_index]); - (void) buffer->message (font, "end lookup %d", lookup_index); + auto &lookup = lookups[table_index][i]; + + unsigned int lookup_index = lookup.index; + if (!buffer->message (font, "start lookup %d feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue; + + /* c.digest is a digest of all the current glyphs in the buffer + * (plus some past glyphs). + * + * Only try applying the lookup if there is any overlap. */ + if (proxy.accels[lookup_index].digest.may_have (c.digest)) + { + c.set_lookup_index (lookup_index); + c.set_lookup_mask (lookup.mask); + c.set_auto_zwj (lookup.auto_zwj); + c.set_auto_zwnj (lookup.auto_zwnj); + c.set_random (lookup.random); + c.set_per_syllable (lookup.per_syllable); + + apply_string<Proxy> (&c, + proxy.table.get_lookup (lookup_index), + proxy.accels[lookup_index]); + } + else + (void) buffer->message (font, "skipped lookup %d feature '%c%c%c%c' because no glyph matches", lookup_index, HB_UNTAG (lookup.feature_tag)); + + (void) buffer->message (font, "end lookup %d feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag)); } if (stage->pause_func) - stage->pause_func (plan, font, buffer); + { + if (stage->pause_func (plan, font, buffer)) + { + /* Refresh working buffer digest since buffer changed. */ + c.digest = buffer->digest (); + } + } } } diff --git a/thirdparty/harfbuzz/src/hb-ot-layout.hh b/thirdparty/harfbuzz/src/hb-ot-layout.hh index de06610cb5..9505d5f147 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout.hh +++ b/thirdparty/harfbuzz/src/hb-ot-layout.hh @@ -102,10 +102,6 @@ HB_INTERNAL void hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer); -HB_INTERNAL void -hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer, - bool (*filter) (const hb_glyph_info_t *info)); - namespace OT { struct hb_ot_apply_context_t; struct hb_ot_layout_lookup_accelerator_t; @@ -552,7 +548,7 @@ _hb_glyph_info_clear_substituted (hb_glyph_info_t *info) info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED); } -static inline void +static inline bool _hb_clear_substitution_flags (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) @@ -561,6 +557,7 @@ _hb_clear_substitution_flags (const hb_ot_shape_plan_t *plan HB_UNUSED, unsigned int count = buffer->len; for (unsigned int i = 0; i < count; i++) _hb_glyph_info_clear_substituted (&info[i]); + return false; } diff --git a/thirdparty/harfbuzz/src/hb-ot-map.cc b/thirdparty/harfbuzz/src/hb-ot-map.cc index 39215b335f..8882dbaccb 100644 --- a/thirdparty/harfbuzz/src/hb-ot-map.cc +++ b/thirdparty/harfbuzz/src/hb-ot-map.cc @@ -45,7 +45,7 @@ void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_o hb_ot_map_builder_t::hb_ot_map_builder_t (hb_face_t *face_, const hb_segment_properties_t &props_) { - memset (this, 0, sizeof (*this)); + hb_memset (this, 0, sizeof (*this)); feature_infos.init (); for (unsigned int table_index = 0; table_index < 2; table_index++) @@ -133,7 +133,8 @@ hb_ot_map_builder_t::add_lookups (hb_ot_map_t &m, bool auto_zwnj, bool auto_zwj, bool random, - bool per_syllable) + bool per_syllable, + hb_tag_t feature_tag) { unsigned int lookup_indices[32]; unsigned int offset, len; @@ -162,6 +163,7 @@ hb_ot_map_builder_t::add_lookups (hb_ot_map_t &m, lookup->auto_zwj = auto_zwj; lookup->random = random; lookup->per_syllable = per_syllable; + lookup->feature_tag = feature_tag; } offset += len; @@ -212,24 +214,26 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, if (feature_infos.length) { feature_infos.qsort (); + auto *f = feature_infos.arrayZ; unsigned int j = 0; - for (unsigned int i = 1; i < feature_infos.length; i++) - if (feature_infos[i].tag != feature_infos[j].tag) - feature_infos[++j] = feature_infos[i]; + unsigned count = feature_infos.length; + for (unsigned int i = 1; i < count; i++) + if (f[i].tag != f[j].tag) + f[++j] = f[i]; else { - if (feature_infos[i].flags & F_GLOBAL) { - feature_infos[j].flags |= F_GLOBAL; - feature_infos[j].max_value = feature_infos[i].max_value; - feature_infos[j].default_value = feature_infos[i].default_value; + if (f[i].flags & F_GLOBAL) { + f[j].flags |= F_GLOBAL; + f[j].max_value = f[i].max_value; + f[j].default_value = f[i].default_value; } else { - if (feature_infos[j].flags & F_GLOBAL) - feature_infos[j].flags ^= F_GLOBAL; - feature_infos[j].max_value = hb_max (feature_infos[j].max_value, feature_infos[i].max_value); + if (f[j].flags & F_GLOBAL) + f[j].flags ^= F_GLOBAL; + f[j].max_value = hb_max (f[j].max_value, f[i].max_value); /* Inherit default_value from j */ } - feature_infos[j].flags |= (feature_infos[i].flags & F_HAS_FALLBACK); - feature_infos[j].stage[0] = hb_min (feature_infos[j].stage[0], feature_infos[i].stage[0]); - feature_infos[j].stage[1] = hb_min (feature_infos[j].stage[1], feature_infos[i].stage[1]); + f[j].flags |= (f[i].flags & F_HAS_FALLBACK); + f[j].stage[0] = hb_min (f[j].stage[0], f[i].stage[0]); + f[j].stage[1] = hb_min (f[j].stage[1], f[i].stage[1]); } feature_infos.shrink (j + 1); } @@ -239,7 +243,8 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), ""); unsigned int next_bit = hb_popcount (HB_GLYPH_FLAG_DEFINED) + 1; - for (unsigned int i = 0; i < feature_infos.length; i++) + unsigned count = feature_infos.length; + for (unsigned int i = 0; i < count; i++) { const feature_info_t *info = &feature_infos[i]; @@ -308,7 +313,7 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, map->_1_mask = (1u << map->shift) & map->mask; map->needs_fallback = !found; } - feature_infos.shrink (0); /* Done with these */ + //feature_infos.shrink (0); /* Done with these */ add_gsub_pause (nullptr); @@ -317,6 +322,7 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, for (unsigned int table_index = 0; table_index < 2; table_index++) { /* Collect lookup indices for features */ + auto &lookups = m.lookups[table_index]; unsigned int stage_index = 0; unsigned int last_num_lookups = 0; @@ -329,36 +335,39 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, key.variations_index[table_index], global_bit_mask); - for (unsigned i = 0; i < m.features.length; i++) - if (m.features[i].stage[table_index] == stage) + for (auto &feature : m.features) + { + if (feature.stage[table_index] == stage) add_lookups (m, table_index, - m.features[i].index[table_index], + feature.index[table_index], key.variations_index[table_index], - m.features[i].mask, - m.features[i].auto_zwnj, - m.features[i].auto_zwj, - m.features[i].random, - m.features[i].per_syllable); + feature.mask, + feature.auto_zwnj, + feature.auto_zwj, + feature.random, + feature.per_syllable, + feature.tag); + } /* Sort lookups and merge duplicates */ - if (last_num_lookups < m.lookups[table_index].length) + if (last_num_lookups < lookups.length) { - m.lookups[table_index].qsort (last_num_lookups, m.lookups[table_index].length); + lookups.as_array ().sub_array (last_num_lookups, lookups.length - last_num_lookups).qsort (); unsigned int j = last_num_lookups; - for (unsigned int i = j + 1; i < m.lookups[table_index].length; i++) - if (m.lookups[table_index][i].index != m.lookups[table_index][j].index) - m.lookups[table_index][++j] = m.lookups[table_index][i]; + for (unsigned int i = j + 1; i < lookups.length; i++) + if (lookups.arrayZ[i].index != lookups.arrayZ[j].index) + lookups.arrayZ[++j] = lookups.arrayZ[i]; else { - m.lookups[table_index][j].mask |= m.lookups[table_index][i].mask; - m.lookups[table_index][j].auto_zwnj &= m.lookups[table_index][i].auto_zwnj; - m.lookups[table_index][j].auto_zwj &= m.lookups[table_index][i].auto_zwj; + lookups.arrayZ[j].mask |= lookups.arrayZ[i].mask; + lookups.arrayZ[j].auto_zwnj &= lookups.arrayZ[i].auto_zwnj; + lookups.arrayZ[j].auto_zwj &= lookups.arrayZ[i].auto_zwj; } - m.lookups[table_index].shrink (j + 1); + lookups.shrink (j + 1); } - last_num_lookups = m.lookups[table_index].length; + last_num_lookups = lookups.length; if (stage_index < stages[table_index].length && stages[table_index][stage_index].index == stage) { hb_ot_map_t::stage_map_t *stage_map = m.stages[table_index].push (); diff --git a/thirdparty/harfbuzz/src/hb-ot-map.hh b/thirdparty/harfbuzz/src/hb-ot-map.hh index a7b5eec30d..efc8cae96a 100644 --- a/thirdparty/harfbuzz/src/hb-ot-map.hh +++ b/thirdparty/harfbuzz/src/hb-ot-map.hh @@ -69,6 +69,7 @@ struct hb_ot_map_t unsigned short random : 1; unsigned short per_syllable : 1; hb_mask_t mask; + hb_tag_t feature_tag; HB_INTERNAL static int cmp (const void *pa, const void *pb) { @@ -78,7 +79,9 @@ struct hb_ot_map_t } }; - typedef void (*pause_func_t) (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); + /* Pause functions return true if new glyph indices might have been + * added to the buffer. This is used to update buffer digest. */ + typedef bool (*pause_func_t) (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); struct stage_map_t { unsigned int last_lookup; /* Cumulative */ @@ -87,13 +90,13 @@ struct hb_ot_map_t void init () { - memset (this, 0, sizeof (*this)); + hb_memset (this, 0, sizeof (*this)); - features.init (); + features.init0 (); for (unsigned int table_index = 0; table_index < 2; table_index++) { - lookups[table_index].init (); - stages[table_index].init (); + lookups[table_index].init0 (); + stages[table_index].init0 (); } } void fini () @@ -239,7 +242,8 @@ struct hb_ot_map_builder_t bool auto_zwnj = true, bool auto_zwj = true, bool random = false, - bool per_syllable = false); + bool per_syllable = false, + hb_tag_t feature_tag = HB_TAG(' ',' ',' ',' ')); struct feature_info_t { hb_tag_t tag; diff --git a/thirdparty/harfbuzz/src/hb-ot-math-table.hh b/thirdparty/harfbuzz/src/hb-ot-math-table.hh index d834d94371..93953370e2 100644 --- a/thirdparty/harfbuzz/src/hb-ot-math-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-math-table.hh @@ -77,11 +77,11 @@ struct MathConstants HBINT16 *p = c->allocate_size<HBINT16> (HBINT16::static_size * 2); if (unlikely (!p)) return_trace (nullptr); - memcpy (p, percentScaleDown, HBINT16::static_size * 2); + hb_memcpy (p, percentScaleDown, HBINT16::static_size * 2); HBUINT16 *m = c->allocate_size<HBUINT16> (HBUINT16::static_size * 2); if (unlikely (!m)) return_trace (nullptr); - memcpy (m, minHeight, HBUINT16::static_size * 2); + hb_memcpy (m, minHeight, HBUINT16::static_size * 2); unsigned count = ARRAY_LENGTH (mathValueRecords); for (unsigned i = 0; i < count; i++) @@ -786,7 +786,7 @@ struct MathGlyphAssembly if (parts_count) { int64_t mult = font->dir_mult (direction); - for (auto _ : hb_zip (partRecords.sub_array (start_offset, parts_count), + for (auto _ : hb_zip (partRecords.as_array ().sub_array (start_offset, parts_count), hb_array (parts, *parts_count))) _.first.extract (_.second, mult, font); } @@ -855,7 +855,7 @@ struct MathGlyphConstruction if (variants_count) { int64_t mult = font->dir_mult (direction); - for (auto _ : hb_zip (mathGlyphVariantRecord.sub_array (start_offset, variants_count), + for (auto _ : hb_zip (mathGlyphVariantRecord.as_array ().sub_array (start_offset, variants_count), hb_array (variants, *variants_count))) _.second = {_.first.variantGlyph, font->em_mult (_.first.advanceMeasurement, mult)}; } diff --git a/thirdparty/harfbuzz/src/hb-ot-meta-table.hh b/thirdparty/harfbuzz/src/hb-ot-meta-table.hh index 93e64c5327..e1b68bcf91 100644 --- a/thirdparty/harfbuzz/src/hb-ot-meta-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-meta-table.hh @@ -84,7 +84,7 @@ struct meta { if (count) { - + table->dataMaps.sub_array (start_offset, count) + + table->dataMaps.as_array ().sub_array (start_offset, count) | hb_map (&DataMap::get_tag) | hb_map ([](hb_tag_t tag) { return (hb_ot_meta_tag_t) tag; }) | hb_sink (hb_array (entries, *count)) diff --git a/thirdparty/harfbuzz/src/hb-ot-name-table.hh b/thirdparty/harfbuzz/src/hb-ot-name-table.hh index 1f2131ffcc..6f4461cc15 100644 --- a/thirdparty/harfbuzz/src/hb-ot-name-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-name-table.hh @@ -30,10 +30,55 @@ #include "hb-open-type.hh" #include "hb-ot-name-language.hh" #include "hb-aat-layout.hh" +#include "hb-utf.hh" namespace OT { +template <typename in_utf_t, typename out_utf_t> +inline unsigned int +hb_ot_name_convert_utf (hb_bytes_t bytes, + unsigned int *text_size /* IN/OUT */, + typename out_utf_t::codepoint_t *text /* OUT */) +{ + unsigned int src_len = bytes.length / sizeof (typename in_utf_t::codepoint_t); + const typename in_utf_t::codepoint_t *src = (const typename in_utf_t::codepoint_t *) bytes.arrayZ; + const typename in_utf_t::codepoint_t *src_end = src + src_len; + + typename out_utf_t::codepoint_t *dst = text; + + hb_codepoint_t unicode; + const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + + if (text_size && *text_size) + { + (*text_size)--; /* Save room for NUL-termination. */ + const typename out_utf_t::codepoint_t *dst_end = text + *text_size; + + while (src < src_end && dst < dst_end) + { + const typename in_utf_t::codepoint_t *src_next = in_utf_t::next (src, src_end, &unicode, replacement); + typename out_utf_t::codepoint_t *dst_next = out_utf_t::encode (dst, dst_end, unicode); + if (dst_next == dst) + break; /* Out-of-room. */ + + dst = dst_next; + src = src_next; + } + + *text_size = dst - text; + *dst = 0; /* NUL-terminate. */ + } + + /* Accumulate length of rest. */ + unsigned int dst_len = dst - text; + while (src < src_end) + { + src = in_utf_t::next (src, src_end, &unicode, replacement); + dst_len += out_utf_t::encode_len (unicode); + } + return dst_len; +} #define entry_score var.u16[0] #define entry_index var.u16[1] @@ -97,12 +142,68 @@ struct NameRecord return UNSUPPORTED; } - NameRecord* copy (hb_serialize_context_t *c, const void *base) const + NameRecord* copy (hb_serialize_context_t *c, const void *base +#ifdef HB_EXPERIMENTAL_API + , const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides +#endif + ) const { TRACE_SERIALIZE (this); + HB_UNUSED auto snap = c->snapshot (); auto *out = c->embed (this); if (unlikely (!out)) return_trace (nullptr); - out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length); +#ifdef HB_EXPERIMENTAL_API + hb_ot_name_record_ids_t record_ids (platformID, encodingID, languageID, nameID); + hb_bytes_t* name_bytes; + + if (name_table_overrides->has (record_ids, &name_bytes)) { + hb_bytes_t encoded_bytes = *name_bytes; + char *name_str_utf16_be = nullptr; + + if (platformID != 1) + { + unsigned text_size = hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, nullptr, nullptr); + + text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf() + unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size; + name_str_utf16_be = (char *) hb_calloc (byte_len, 1); + if (!name_str_utf16_be) + { + c->revert (snap); + return_trace (nullptr); + } + hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, &text_size, + (hb_utf16_be_t::codepoint_t *) name_str_utf16_be); + + unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size; + if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) { + c->revert (snap); + hb_free (name_str_utf16_be); + return_trace (nullptr); + } + + encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len); + } + else + { + // mac platform, copy the UTF-8 string(all ascii characters) as is + if (!c->check_assign (out->length, encoded_bytes.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) { + c->revert (snap); + return_trace (nullptr); + } + } + + out->offset = 0; + c->push (); + encoded_bytes.copy (c); + c->add_link (out->offset, c->pop_pack (), hb_serialize_context_t::Tail, 0); + hb_free (name_str_utf16_be); + } + else +#endif + { + out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length); + } return_trace (out); } @@ -216,29 +317,61 @@ struct name hb_requires (hb_is_source_of (Iterator, const NameRecord &))> bool serialize (hb_serialize_context_t *c, Iterator it, - const void *src_string_pool) + const void *src_string_pool +#ifdef HB_EXPERIMENTAL_API + , const hb_vector_t<hb_ot_name_record_ids_t>& insert_name_records + , const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides +#endif + ) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min ((*this)))) return_trace (false); + unsigned total_count = it.len () +#ifdef HB_EXPERIMENTAL_API + + insert_name_records.length +#endif + ; this->format = 0; - this->count = it.len (); + if (!c->check_assign (this->count, total_count, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return false; - NameRecord *name_records = (NameRecord *) hb_calloc (it.len (), NameRecord::static_size); + NameRecord *name_records = (NameRecord *) hb_calloc (total_count, NameRecord::static_size); if (unlikely (!name_records)) return_trace (false); - hb_array_t<NameRecord> records (name_records, it.len ()); + hb_array_t<NameRecord> records (name_records, total_count); for (const NameRecord& record : it) { + hb_memcpy (name_records, &record, NameRecord::static_size); + name_records++; + } + +#ifdef HB_EXPERIMENTAL_API + for (unsigned i = 0; i < insert_name_records.length; i++) + { + const hb_ot_name_record_ids_t& ids = insert_name_records[i]; + NameRecord record; + record.platformID = ids.platform_id; + record.encodingID = ids.encoding_id; + record.languageID = ids.language_id; + record.nameID = ids.name_id; + record.length = 0; // handled in NameRecord copy() + record.offset = 0; memcpy (name_records, &record, NameRecord::static_size); name_records++; } +#endif records.qsort (); - c->copy_all (records, src_string_pool); + c->copy_all (records, + src_string_pool +#ifdef HB_EXPERIMENTAL_API + , name_table_overrides +#endif + ); hb_free (records.arrayZ); @@ -256,6 +389,11 @@ struct name name *name_prime = c->serializer->start_embed<name> (); if (unlikely (!name_prime)) return_trace (false); +#ifdef HB_EXPERIMENTAL_API + const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides = + c->plan->name_table_overrides; +#endif + auto it = + nameRecordZ.as_array (count) | hb_filter (c->plan->name_ids, &NameRecord::nameID) @@ -265,10 +403,48 @@ struct name (c->plan->flags & HB_SUBSET_FLAGS_NAME_LEGACY) || namerecord.isUnicode (); }) +#ifdef HB_EXPERIMENTAL_API + | hb_filter ([&] (const NameRecord& namerecord) { + if (name_table_overrides->is_empty ()) + return true; + hb_ot_name_record_ids_t rec_ids (namerecord.platformID, + namerecord.encodingID, + namerecord.languageID, + namerecord.nameID); + + hb_bytes_t *p; + if (name_table_overrides->has (rec_ids, &p) && + (*p).length == 0) + return false; + return true; + }) +#endif ; - name_prime->serialize (c->serializer, it, std::addressof (this + stringOffset)); - return_trace (name_prime->count); +#ifdef HB_EXPERIMENTAL_API + hb_vector_t<hb_ot_name_record_ids_t> insert_name_records; + if (!name_table_overrides->is_empty ()) + { + if (unlikely (!insert_name_records.alloc (name_table_overrides->get_population ()))) + return_trace (false); + for (const auto& record_ids : name_table_overrides->keys ()) + { + if (name_table_overrides->get (record_ids).length == 0) + continue; + if (has_name_record_with_ids (record_ids)) + continue; + insert_name_records.push (record_ids); + } + } +#endif + + return (name_prime->serialize (c->serializer, it, + std::addressof (this + stringOffset) +#ifdef HB_EXPERIMENTAL_API + , insert_name_records + , name_table_overrides +#endif + )); } bool sanitize_records (hb_sanitize_context_t *c) const @@ -378,6 +554,23 @@ struct name hb_vector_t<hb_ot_name_entry_t> names; }; + private: + // sometimes NameRecords are not sorted in the font file, so use linear search + // here + bool has_name_record_with_ids (const hb_ot_name_record_ids_t& record_ids) const + { + for (const auto& record : nameRecordZ.as_array (count)) + { + if (record.platformID == record_ids.platform_id && + record.encodingID == record_ids.encoding_id && + record.languageID == record_ids.language_id && + record.nameID == record_ids.name_id) + return true; + } + return false; + } + + public: /* We only implement format 0 for now. */ HBUINT16 format; /* Format selector (=0/1). */ HBUINT16 count; /* Number of name records. */ diff --git a/thirdparty/harfbuzz/src/hb-ot-name.cc b/thirdparty/harfbuzz/src/hb-ot-name.cc index c35ac5b3dc..6adf1e8fbe 100644 --- a/thirdparty/harfbuzz/src/hb-ot-name.cc +++ b/thirdparty/harfbuzz/src/hb-ot-name.cc @@ -64,52 +64,6 @@ hb_ot_name_list_names (hb_face_t *face, return (const hb_ot_name_entry_t *) name.names; } - -template <typename in_utf_t, typename out_utf_t> -static inline unsigned int -hb_ot_name_convert_utf (hb_bytes_t bytes, - unsigned int *text_size /* IN/OUT */, - typename out_utf_t::codepoint_t *text /* OUT */) -{ - unsigned int src_len = bytes.length / sizeof (typename in_utf_t::codepoint_t); - const typename in_utf_t::codepoint_t *src = (const typename in_utf_t::codepoint_t *) bytes.arrayZ; - const typename in_utf_t::codepoint_t *src_end = src + src_len; - - typename out_utf_t::codepoint_t *dst = text; - - hb_codepoint_t unicode; - const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; - - if (text_size && *text_size) - { - (*text_size)--; /* Same room for NUL-termination. */ - const typename out_utf_t::codepoint_t *dst_end = text + *text_size; - - while (src < src_end && dst < dst_end) - { - const typename in_utf_t::codepoint_t *src_next = in_utf_t::next (src, src_end, &unicode, replacement); - typename out_utf_t::codepoint_t *dst_next = out_utf_t::encode (dst, dst_end, unicode); - if (dst_next == dst) - break; /* Out-of-room. */ - - dst = dst_next; - src = src_next; - } - - *text_size = dst - text; - *dst = 0; /* NUL-terminate. */ - } - - /* Accumulate length of rest. */ - unsigned int dst_len = dst - text; - while (src < src_end) - { - src = in_utf_t::next (src, src_end, &unicode, replacement); - dst_len += out_utf_t::encode_len (unicode); - } - return dst_len; -} - template <typename utf_t> static inline unsigned int hb_ot_name_get_utf (hb_face_t *face, @@ -130,10 +84,10 @@ hb_ot_name_get_utf (hb_face_t *face, hb_bytes_t bytes = name.get_name (idx); if (width == 2) /* UTF16-BE */ - return hb_ot_name_convert_utf<hb_utf16_be_t, utf_t> (bytes, text_size, text); + return OT::hb_ot_name_convert_utf<hb_utf16_be_t, utf_t> (bytes, text_size, text); if (width == 1) /* ASCII */ - return hb_ot_name_convert_utf<hb_ascii_t, utf_t> (bytes, text_size, text); + return OT::hb_ot_name_convert_utf<hb_ascii_t, utf_t> (bytes, text_size, text); } if (text_size) @@ -227,5 +181,4 @@ hb_ot_name_get_utf32 (hb_face_t *face, return hb_ot_name_get_utf<hb_utf32_t> (face, name_id, language, text_size, text); } - #endif diff --git a/thirdparty/harfbuzz/src/hb-ot-os2-table.hh b/thirdparty/harfbuzz/src/hb-ot-os2-table.hh index c6e8fad6fc..5b017d56a6 100644 --- a/thirdparty/harfbuzz/src/hb-ot-os2-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-os2-table.hh @@ -212,17 +212,6 @@ struct OS2 TRACE_SUBSET (this); OS2 *os2_prime = c->serializer->embed (this); if (unlikely (!os2_prime)) return_trace (false); - if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES) - return_trace (true); - - /* when --gids option is not used, no need to do collect_mapping that is - * iterating all codepoints in each subtable, which is not efficient */ - uint16_t min_cp, max_cp; - find_min_and_max_codepoint (c->plan->unicodes, &min_cp, &max_cp); - os2_prime->usFirstCharIndex = min_cp; - os2_prime->usLastCharIndex = max_cp; - - _update_unicode_ranges (c->plan->unicodes, os2_prime->ulUnicodeRange); if (c->plan->user_axes_location->has (HB_TAG ('w','g','h','t')) && !c->plan->pinned_at_default) @@ -244,6 +233,18 @@ struct OS2 return_trace (false); } + if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES) + return_trace (true); + + /* when --gids option is not used, no need to do collect_mapping that is + * iterating all codepoints in each subtable, which is not efficient */ + uint16_t min_cp, max_cp; + find_min_and_max_codepoint (c->plan->unicodes, &min_cp, &max_cp); + os2_prime->usFirstCharIndex = min_cp; + os2_prime->usLastCharIndex = max_cp; + + _update_unicode_ranges (c->plan->unicodes, os2_prime->ulUnicodeRange); + return_trace (true); } diff --git a/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh b/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh index 4d427e5431..951e6395d6 100644 --- a/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh +++ b/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh @@ -78,14 +78,14 @@ HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const post::accelerator_t _post (c->plan->source); - hb_hashmap_t<hb_bytes_t, unsigned, true> glyph_name_to_new_index; + hb_hashmap_t<hb_bytes_t, uint32_t, true> glyph_name_to_new_index; for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++) { hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); unsigned old_index = glyphNameIndex[old_gid]; unsigned new_index; - const unsigned *new_index2; + const uint32_t *new_index2; if (old_index <= 257) new_index = old_index; else if (old_new_index_map.has (old_index, &new_index2)) { diff --git a/thirdparty/harfbuzz/src/hb-ot-post-table.hh b/thirdparty/harfbuzz/src/hb-ot-post-table.hh index 59c1de3784..a04b80357b 100644 --- a/thirdparty/harfbuzz/src/hb-ot-post-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-post-table.hh @@ -84,7 +84,7 @@ struct post post *post_prime = c->allocate_min<post> (); if (unlikely (!post_prime)) return_trace (false); - memcpy (post_prime, this, post::min_size); + hb_memcpy (post_prime, this, post::min_size); if (!glyph_names) return_trace (c->check_assign (post_prime->version.major, 3, HB_SERIALIZE_ERROR_INT_OVERFLOW)); // Version 3 does not have any glyph names. diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc b/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc index 7db0b25b73..897377aa15 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc @@ -341,7 +341,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, { unsigned int end; for (end = buffer->idx + 1; end < count; end++) - if (unlikely (_hb_glyph_info_is_unicode_mark (&buffer->info[end]))) + if (_hb_glyph_info_is_unicode_mark (&buffer->info[end])) break; if (end < count) diff --git a/thirdparty/harfbuzz/src/hb-ot-shape.cc b/thirdparty/harfbuzz/src/hb-ot-shape.cc index 249b5a864c..bbdfc214a1 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shape.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shape.cc @@ -91,9 +91,11 @@ hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *fac script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; script_fallback_mark_positioning = shaper->fallback_position; +#ifndef HB_NO_AAT_SHAPE /* https://github.com/harfbuzz/harfbuzz/issues/1528 */ if (apply_morx && shaper != &_hb_ot_shaper_default) shaper = &_hb_ot_shaper_dumber; +#endif } void @@ -864,7 +866,7 @@ hb_ot_hide_default_ignorables (hb_buffer_t *buffer, } } else - hb_ot_layout_delete_glyphs_inplace (buffer, _hb_glyph_info_is_default_ignorable); + buffer->delete_glyphs_inplace (_hb_glyph_info_is_default_ignorable); } diff --git a/thirdparty/harfbuzz/src/hb-ot-shape.hh b/thirdparty/harfbuzz/src/hb-ot-shape.hh index cd6f15cbe2..ace28602f6 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shape.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shape.hh @@ -51,7 +51,7 @@ struct hb_ot_shape_plan_key_t bool equal (const hb_ot_shape_plan_key_t *other) { - return 0 == memcmp (this, other, sizeof (*this)); + return 0 == hb_memcmp (this, other, sizeof (*this)); } }; diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc index ed2748b828..2332ae3697 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc @@ -161,22 +161,23 @@ static const struct arabic_state_table_entry { }; -static void +static bool arabic_fallback_shape (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void +static bool record_stch (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void +static bool deallocate_buffer_var (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); + return false; } static void @@ -412,19 +413,19 @@ setup_masks_arabic (const hb_ot_shape_plan_t *plan, setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script); } -static void +static bool arabic_fallback_shape (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { #ifdef HB_NO_OT_SHAPER_ARABIC_FALLBACK - return; + return false; #endif const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; if (!arabic_plan->do_fallback) - return; + return false; retry: arabic_fallback_plan_t *fallback_plan = arabic_plan->fallback_plan; @@ -440,6 +441,7 @@ retry: } arabic_fallback_plan_shape (fallback_plan, font, buffer); + return true; } /* @@ -450,14 +452,14 @@ retry: * marks can use it as well. */ -static void +static bool record_stch (const hb_ot_shape_plan_t *plan, hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) { const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; if (!arabic_plan->has_stch) - return; + return false; /* 'stch' feature was just applied. Look for anything that multiplied, * and record it for stch treatment later. Note that rtlm, frac, etc @@ -473,6 +475,7 @@ record_stch (const hb_ot_shape_plan_t *plan, info[i].arabic_shaping_action() = comp % 2 ? STCH_REPEATING : STCH_FIXED; buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH; } + return false; } static void diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-default.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-default.cc index 2f6f499eec..f0404a4d2c 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-default.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-default.cc @@ -49,6 +49,7 @@ const hb_ot_shaper_t _hb_ot_shaper_default = true, /* fallback_position */ }; +#ifndef HB_NO_AAT_SHAPE /* Same as default but no mark advance zeroing / fallback positioning. * Dumbest shaper ever, basically. */ const hb_ot_shaper_t _hb_ot_shaper_dumber = @@ -68,6 +69,7 @@ const hb_ot_shaper_t _hb_ot_shaper_dumber = HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; +#endif #endif diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh index d52b13f616..d6c67b81bd 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh @@ -53,7 +53,7 @@ enum indic_syllable_type_t { }; -#line 57 "hb-ot-shaper-indic-machine.hh" +#line 54 "hb-ot-shaper-indic-machine.hh" #define indic_syllable_machine_ex_A 9u #define indic_syllable_machine_ex_C 1u #define indic_syllable_machine_ex_CM 16u @@ -61,6 +61,7 @@ enum indic_syllable_type_t { #define indic_syllable_machine_ex_DOTTEDCIRCLE 11u #define indic_syllable_machine_ex_H 4u #define indic_syllable_machine_ex_M 7u +#define indic_syllable_machine_ex_MPst 13u #define indic_syllable_machine_ex_N 3u #define indic_syllable_machine_ex_PLACEHOLDER 10u #define indic_syllable_machine_ex_RS 12u @@ -75,268 +76,302 @@ enum indic_syllable_type_t { #define indic_syllable_machine_ex_ZWNJ 5u -#line 79 "hb-ot-shaper-indic-machine.hh" +#line 75 "hb-ot-shaper-indic-machine.hh" static const unsigned char _indic_syllable_machine_trans_keys[] = { - 8u, 8u, 4u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 4u, 12u, 4u, 8u, 8u, 8u, - 5u, 7u, 5u, 8u, 4u, 8u, 4u, 12u, 4u, 12u, 4u, 12u, 8u, 8u, 5u, 7u, - 5u, 8u, 4u, 8u, 4u, 8u, 4u, 12u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, - 4u, 8u, 5u, 8u, 8u, 8u, 1u, 18u, 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, - 5u, 9u, 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, 1u, 15u, 1u, 15u, 3u, 9u, - 4u, 9u, 5u, 9u, 4u, 9u, 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, - 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, - 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, 1u, 15u, 3u, 9u, 4u, 9u, 5u, 9u, - 4u, 9u, 5u, 9u, 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, 4u, 8u, - 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, - 5u, 9u, 1u, 15u, 1u, 15u, 3u, 9u, 4u, 9u, 5u, 9u, 3u, 16u, 4u, 9u, - 5u, 9u, 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 4u, 12u, 4u, 8u, 3u, 16u, - 3u, 16u, 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, - 1u, 15u, 1u, 15u, 3u, 9u, 4u, 9u, 5u, 9u, 3u, 16u, 4u, 9u, 5u, 9u, - 5u, 9u, 3u, 9u, 5u, 9u, 1u, 16u, 3u, 16u, 1u, 16u, 4u, 12u, 5u, 9u, - 9u, 9u, 5u, 9u, 1u, 15u, 3u, 9u, 5u, 9u, 5u, 9u, 9u, 9u, 5u, 9u, - 1u, 15u, 0 + 8u, 8u, 4u, 13u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, + 8u, 8u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, + 8u, 8u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, 8u, 8u, + 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 5u, 13u, 8u, 8u, 1u, 18u, + 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 5u, 9u, 5u, 9u, 9u, 9u, 5u, 9u, + 1u, 15u, 1u, 15u, 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 4u, 13u, + 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, 3u, 16u, 3u, 16u, 4u, 16u, + 1u, 15u, 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, + 1u, 15u, 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 4u, 13u, 5u, 9u, + 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, 4u, 13u, 3u, 16u, 3u, 16u, + 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, + 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 3u, 16u, 4u, 13u, 5u, 9u, + 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 4u, 13u, 4u, 13u, 3u, 16u, 3u, 16u, + 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, + 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 3u, 16u, 4u, 13u, 5u, 9u, + 5u, 9u, 3u, 9u, 5u, 9u, 1u, 16u, 3u, 16u, 1u, 16u, 4u, 13u, 5u, 13u, + 5u, 13u, 9u, 9u, 5u, 9u, 1u, 15u, 3u, 9u, 5u, 9u, 5u, 9u, 9u, 9u, + 5u, 9u, 1u, 15u, 0 }; static const char _indic_syllable_machine_key_spans[] = { - 1, 5, 3, 4, 5, 9, 5, 1, - 3, 4, 5, 9, 9, 9, 1, 3, - 4, 5, 5, 9, 1, 3, 4, 5, - 5, 4, 1, 18, 14, 14, 13, 15, - 5, 5, 1, 5, 15, 15, 15, 7, - 6, 5, 6, 5, 7, 5, 14, 14, - 14, 14, 13, 15, 14, 14, 13, 15, - 5, 1, 5, 15, 15, 7, 6, 5, - 6, 5, 5, 7, 5, 14, 14, 5, - 14, 14, 13, 15, 14, 15, 5, 1, - 5, 15, 15, 7, 6, 5, 14, 6, - 5, 5, 7, 5, 14, 9, 5, 14, - 14, 13, 15, 14, 15, 5, 1, 5, - 15, 15, 7, 6, 5, 14, 6, 5, - 5, 7, 5, 16, 14, 16, 9, 5, - 1, 5, 15, 7, 5, 5, 1, 5, - 15 + 1, 10, 9, 9, 1, 10, 10, 10, + 1, 9, 9, 1, 10, 10, 10, 10, + 1, 9, 9, 1, 10, 10, 10, 1, + 9, 9, 1, 10, 10, 9, 1, 18, + 14, 14, 13, 15, 5, 5, 1, 5, + 15, 15, 15, 11, 10, 9, 9, 10, + 5, 7, 5, 14, 14, 14, 14, 13, + 15, 14, 14, 13, 15, 5, 1, 5, + 15, 15, 11, 10, 9, 9, 10, 5, + 5, 7, 5, 14, 14, 10, 14, 14, + 13, 15, 14, 15, 5, 1, 5, 15, + 15, 11, 10, 9, 9, 14, 10, 5, + 5, 7, 5, 14, 10, 10, 14, 14, + 13, 15, 14, 15, 5, 1, 5, 15, + 15, 11, 10, 9, 9, 14, 10, 5, + 5, 7, 5, 16, 14, 16, 10, 9, + 9, 1, 5, 15, 7, 5, 5, 1, + 5, 15 }; static const short _indic_syllable_machine_index_offsets[] = { - 0, 2, 8, 12, 17, 23, 33, 39, - 41, 45, 50, 56, 66, 76, 86, 88, - 92, 97, 103, 109, 119, 121, 125, 130, - 136, 142, 147, 149, 168, 183, 198, 212, - 228, 234, 240, 242, 248, 264, 280, 296, - 304, 311, 317, 324, 330, 338, 344, 359, - 374, 389, 404, 418, 434, 449, 464, 478, - 494, 500, 502, 508, 524, 540, 548, 555, - 561, 568, 574, 580, 588, 594, 609, 624, - 630, 645, 660, 674, 690, 705, 721, 727, - 729, 735, 751, 767, 775, 782, 788, 803, - 810, 816, 822, 830, 836, 851, 861, 867, - 882, 897, 911, 927, 942, 958, 964, 966, - 972, 988, 1004, 1012, 1019, 1025, 1040, 1047, - 1053, 1059, 1067, 1073, 1090, 1105, 1122, 1132, - 1138, 1140, 1146, 1162, 1170, 1176, 1182, 1184, - 1190 + 0, 2, 13, 23, 33, 35, 46, 57, + 68, 70, 80, 90, 92, 103, 114, 125, + 136, 138, 148, 158, 160, 171, 182, 193, + 195, 205, 215, 217, 228, 239, 249, 251, + 270, 285, 300, 314, 330, 336, 342, 344, + 350, 366, 382, 398, 410, 421, 431, 441, + 452, 458, 466, 472, 487, 502, 517, 532, + 546, 562, 577, 592, 606, 622, 628, 630, + 636, 652, 668, 680, 691, 701, 711, 722, + 728, 734, 742, 748, 763, 778, 789, 804, + 819, 833, 849, 864, 880, 886, 888, 894, + 910, 926, 938, 949, 959, 969, 984, 995, + 1001, 1007, 1015, 1021, 1036, 1047, 1058, 1073, + 1088, 1102, 1118, 1133, 1149, 1155, 1157, 1163, + 1179, 1195, 1207, 1218, 1228, 1238, 1253, 1264, + 1270, 1276, 1284, 1290, 1307, 1322, 1339, 1350, + 1360, 1370, 1372, 1378, 1394, 1402, 1408, 1414, + 1416, 1422 }; static const unsigned char _indic_syllable_machine_indicies[] = { - 1, 0, 2, 3, 3, 4, 1, 0, - 3, 3, 4, 0, 3, 3, 4, 1, - 0, 5, 3, 3, 4, 1, 0, 2, - 3, 3, 4, 1, 0, 0, 0, 6, - 0, 8, 9, 9, 10, 11, 7, 11, - 7, 9, 9, 10, 7, 9, 9, 10, - 11, 7, 12, 9, 9, 10, 11, 7, - 8, 9, 9, 10, 11, 7, 7, 7, - 13, 7, 8, 9, 9, 10, 11, 7, - 7, 7, 14, 7, 16, 17, 17, 18, - 19, 15, 15, 15, 20, 15, 19, 15, - 17, 17, 18, 21, 17, 17, 18, 19, - 15, 16, 17, 17, 18, 19, 15, 22, - 17, 17, 18, 19, 15, 24, 25, 25, - 26, 27, 23, 23, 23, 28, 23, 27, - 23, 25, 25, 26, 23, 25, 25, 26, - 27, 23, 24, 25, 25, 26, 27, 23, - 29, 25, 25, 26, 27, 23, 17, 17, - 18, 1, 0, 31, 30, 33, 34, 35, - 36, 37, 38, 18, 19, 39, 40, 40, - 20, 32, 41, 42, 43, 44, 45, 32, - 47, 48, 49, 50, 4, 1, 51, 46, - 46, 6, 46, 46, 46, 52, 46, 53, - 48, 54, 54, 4, 1, 51, 46, 46, - 46, 46, 46, 46, 52, 46, 48, 54, - 54, 4, 1, 51, 46, 46, 46, 46, - 46, 46, 52, 46, 33, 46, 46, 46, - 55, 56, 46, 1, 51, 46, 46, 46, - 46, 46, 33, 46, 57, 57, 46, 1, - 51, 46, 51, 46, 46, 58, 51, 46, - 51, 46, 51, 46, 46, 46, 51, 46, - 33, 46, 59, 46, 57, 57, 46, 1, - 51, 46, 46, 46, 46, 46, 33, 46, - 33, 46, 46, 46, 57, 57, 46, 1, - 51, 46, 46, 46, 46, 46, 33, 46, - 33, 46, 46, 46, 57, 56, 46, 1, - 51, 46, 46, 46, 46, 46, 33, 46, - 60, 61, 62, 62, 4, 1, 51, 46, - 61, 62, 62, 4, 1, 51, 46, 62, - 62, 4, 1, 51, 46, 63, 64, 64, - 4, 1, 51, 46, 55, 65, 46, 1, - 51, 46, 55, 46, 57, 57, 46, 1, - 51, 46, 57, 65, 46, 1, 51, 46, - 47, 48, 54, 54, 4, 1, 51, 46, - 46, 46, 46, 46, 46, 52, 46, 47, - 48, 49, 54, 4, 1, 51, 46, 46, - 6, 46, 46, 46, 52, 46, 67, 68, - 69, 70, 10, 11, 71, 66, 66, 14, - 66, 66, 66, 72, 66, 73, 68, 74, - 70, 10, 11, 71, 66, 66, 66, 66, - 66, 66, 72, 66, 68, 74, 70, 10, - 11, 71, 66, 66, 66, 66, 66, 66, - 72, 66, 75, 66, 66, 66, 76, 77, - 66, 11, 71, 66, 66, 66, 66, 66, - 75, 66, 78, 68, 79, 80, 10, 11, - 71, 66, 66, 13, 66, 66, 66, 72, - 66, 81, 68, 74, 74, 10, 11, 71, - 66, 66, 66, 66, 66, 66, 72, 66, - 68, 74, 74, 10, 11, 71, 66, 66, - 66, 66, 66, 66, 72, 66, 75, 66, - 66, 66, 82, 77, 66, 11, 71, 66, - 66, 66, 66, 66, 75, 66, 71, 66, - 66, 83, 71, 66, 71, 66, 71, 66, - 66, 66, 71, 66, 75, 66, 84, 66, - 82, 82, 66, 11, 71, 66, 66, 66, - 66, 66, 75, 66, 75, 66, 66, 66, - 82, 82, 66, 11, 71, 66, 66, 66, - 66, 66, 75, 66, 85, 86, 87, 87, - 10, 11, 71, 66, 86, 87, 87, 10, - 11, 71, 66, 87, 87, 10, 11, 71, - 66, 88, 89, 89, 10, 11, 71, 66, - 76, 90, 66, 11, 71, 66, 82, 82, - 66, 11, 71, 66, 76, 66, 82, 82, - 66, 11, 71, 66, 82, 90, 66, 11, - 71, 66, 78, 68, 74, 74, 10, 11, - 71, 66, 66, 66, 66, 66, 66, 72, - 66, 78, 68, 79, 74, 10, 11, 71, - 66, 66, 13, 66, 66, 66, 72, 66, - 8, 9, 9, 10, 11, 66, 67, 68, - 74, 70, 10, 11, 71, 66, 66, 66, - 66, 66, 66, 72, 66, 92, 36, 93, - 93, 18, 19, 39, 91, 91, 91, 91, - 91, 91, 43, 91, 36, 93, 93, 18, - 19, 39, 91, 91, 91, 91, 91, 91, - 43, 91, 94, 91, 91, 91, 95, 96, - 91, 19, 39, 91, 91, 91, 91, 91, - 94, 91, 35, 36, 97, 98, 18, 19, - 39, 91, 91, 20, 91, 91, 91, 43, - 91, 94, 91, 91, 91, 99, 96, 91, - 19, 39, 91, 91, 91, 91, 91, 94, - 91, 39, 91, 91, 100, 39, 91, 39, - 91, 39, 91, 91, 91, 39, 91, 94, - 91, 101, 91, 99, 99, 91, 19, 39, - 91, 91, 91, 91, 91, 94, 91, 94, - 91, 91, 91, 99, 99, 91, 19, 39, - 91, 91, 91, 91, 91, 94, 91, 102, - 103, 104, 104, 18, 19, 39, 91, 103, - 104, 104, 18, 19, 39, 91, 104, 104, - 18, 19, 39, 91, 35, 36, 93, 93, - 18, 19, 39, 91, 91, 91, 91, 91, - 91, 43, 91, 105, 106, 106, 18, 19, - 39, 91, 95, 107, 91, 19, 39, 91, - 99, 99, 91, 19, 39, 91, 95, 91, - 99, 99, 91, 19, 39, 91, 99, 107, - 91, 19, 39, 91, 35, 36, 97, 93, - 18, 19, 39, 91, 91, 20, 91, 91, - 91, 43, 91, 16, 17, 17, 18, 19, - 108, 108, 108, 20, 108, 16, 17, 17, - 18, 19, 108, 110, 111, 112, 113, 26, - 27, 114, 109, 109, 28, 109, 109, 109, - 115, 109, 116, 111, 113, 113, 26, 27, - 114, 109, 109, 109, 109, 109, 109, 115, - 109, 111, 113, 113, 26, 27, 114, 109, - 109, 109, 109, 109, 109, 115, 109, 117, - 109, 109, 109, 118, 119, 109, 27, 114, - 109, 109, 109, 109, 109, 117, 109, 110, - 111, 112, 40, 26, 27, 114, 109, 109, - 28, 109, 109, 109, 115, 109, 117, 109, - 109, 109, 120, 119, 109, 27, 114, 109, - 109, 109, 109, 109, 117, 109, 114, 109, - 109, 121, 114, 109, 114, 109, 114, 109, - 109, 109, 114, 109, 117, 109, 122, 109, - 120, 120, 109, 27, 114, 109, 109, 109, - 109, 109, 117, 109, 117, 109, 109, 109, - 120, 120, 109, 27, 114, 109, 109, 109, - 109, 109, 117, 109, 123, 124, 125, 125, - 26, 27, 114, 109, 124, 125, 125, 26, - 27, 114, 109, 125, 125, 26, 27, 114, - 109, 110, 111, 113, 113, 26, 27, 114, - 109, 109, 109, 109, 109, 109, 115, 109, - 126, 127, 127, 26, 27, 114, 109, 118, - 128, 109, 27, 114, 109, 120, 120, 109, - 27, 114, 109, 118, 109, 120, 120, 109, - 27, 114, 109, 120, 128, 109, 27, 114, - 109, 33, 34, 35, 36, 97, 93, 18, - 19, 39, 40, 40, 20, 91, 91, 33, - 43, 91, 47, 129, 49, 50, 4, 1, - 51, 46, 46, 6, 46, 46, 46, 52, - 46, 33, 34, 35, 36, 130, 131, 18, - 132, 133, 46, 40, 20, 46, 46, 33, - 43, 46, 16, 134, 134, 18, 132, 51, - 46, 46, 20, 46, 133, 46, 46, 135, - 133, 46, 133, 46, 133, 46, 46, 46, - 133, 46, 33, 46, 59, 16, 134, 134, - 18, 132, 51, 46, 46, 46, 46, 46, - 33, 46, 137, 136, 138, 138, 136, 31, - 139, 136, 138, 138, 136, 31, 139, 136, - 139, 136, 136, 140, 139, 136, 139, 136, - 139, 136, 136, 136, 139, 136, 33, 108, - 108, 108, 108, 108, 108, 108, 108, 40, - 108, 108, 108, 108, 33, 108, 0 + 1, 0, 2, 3, 3, 4, 5, 0, + 0, 0, 0, 4, 0, 3, 3, 4, + 6, 0, 0, 0, 0, 4, 0, 3, + 3, 4, 5, 0, 0, 0, 0, 4, + 0, 4, 0, 7, 3, 3, 4, 5, + 0, 0, 0, 0, 4, 0, 2, 3, + 3, 4, 5, 0, 0, 0, 8, 4, + 0, 10, 11, 11, 12, 13, 9, 9, + 9, 9, 12, 9, 14, 9, 11, 11, + 12, 15, 9, 9, 9, 9, 12, 9, + 11, 11, 12, 13, 9, 9, 9, 9, + 12, 9, 12, 9, 16, 11, 11, 12, + 13, 9, 9, 9, 9, 12, 9, 10, + 11, 11, 12, 13, 9, 9, 9, 17, + 12, 9, 10, 11, 11, 12, 13, 9, + 9, 9, 18, 12, 9, 20, 21, 21, + 22, 23, 19, 19, 19, 24, 22, 19, + 25, 19, 21, 21, 22, 27, 26, 26, + 26, 26, 22, 26, 21, 21, 22, 23, + 19, 19, 19, 19, 22, 19, 22, 26, + 20, 21, 21, 22, 23, 19, 19, 19, + 19, 22, 19, 28, 21, 21, 22, 23, + 19, 19, 19, 19, 22, 19, 30, 31, + 31, 32, 33, 29, 29, 29, 34, 32, + 29, 35, 29, 31, 31, 32, 36, 29, + 29, 29, 29, 32, 29, 31, 31, 32, + 33, 29, 29, 29, 29, 32, 29, 32, + 29, 30, 31, 31, 32, 33, 29, 29, + 29, 29, 32, 29, 37, 31, 31, 32, + 33, 29, 29, 29, 29, 32, 29, 21, + 21, 22, 38, 0, 0, 0, 0, 22, + 0, 40, 39, 42, 43, 44, 45, 46, + 47, 22, 23, 48, 49, 49, 24, 22, + 50, 51, 52, 53, 54, 41, 56, 57, + 58, 59, 4, 5, 60, 55, 55, 8, + 4, 55, 55, 61, 55, 62, 57, 63, + 63, 4, 5, 60, 55, 55, 55, 4, + 55, 55, 61, 55, 57, 63, 63, 4, + 5, 60, 55, 55, 55, 4, 55, 55, + 61, 55, 42, 55, 55, 55, 64, 65, + 55, 1, 60, 55, 55, 55, 55, 55, + 42, 55, 66, 66, 55, 1, 60, 55, + 60, 55, 55, 67, 60, 55, 60, 55, + 60, 55, 55, 55, 60, 55, 42, 55, + 68, 55, 66, 66, 55, 1, 60, 55, + 55, 55, 55, 55, 42, 55, 42, 55, + 55, 55, 66, 66, 55, 1, 60, 55, + 55, 55, 55, 55, 42, 55, 42, 55, + 55, 55, 66, 65, 55, 1, 60, 55, + 55, 55, 55, 55, 42, 55, 69, 70, + 71, 71, 4, 5, 60, 55, 55, 55, + 4, 55, 70, 71, 71, 4, 5, 60, + 55, 55, 55, 4, 55, 71, 71, 4, + 5, 60, 55, 55, 55, 4, 55, 60, + 55, 55, 67, 60, 55, 55, 55, 4, + 55, 72, 73, 73, 4, 5, 60, 55, + 55, 55, 4, 55, 64, 74, 55, 1, + 60, 55, 64, 55, 66, 66, 55, 1, + 60, 55, 66, 74, 55, 1, 60, 55, + 56, 57, 63, 63, 4, 5, 60, 55, + 55, 55, 4, 55, 55, 61, 55, 56, + 57, 58, 63, 4, 5, 60, 55, 55, + 8, 4, 55, 55, 61, 55, 76, 77, + 78, 79, 12, 13, 80, 75, 75, 18, + 12, 75, 75, 81, 75, 82, 77, 83, + 79, 12, 13, 80, 75, 75, 75, 12, + 75, 75, 81, 75, 77, 83, 79, 12, + 13, 80, 75, 75, 75, 12, 75, 75, + 81, 75, 84, 75, 75, 75, 85, 86, + 75, 14, 80, 75, 75, 75, 75, 75, + 84, 75, 87, 77, 88, 89, 12, 13, + 80, 75, 75, 17, 12, 75, 75, 81, + 75, 90, 77, 83, 83, 12, 13, 80, + 75, 75, 75, 12, 75, 75, 81, 75, + 77, 83, 83, 12, 13, 80, 75, 75, + 75, 12, 75, 75, 81, 75, 84, 75, + 75, 75, 91, 86, 75, 14, 80, 75, + 75, 75, 75, 75, 84, 75, 80, 75, + 75, 92, 80, 75, 80, 75, 80, 75, + 75, 75, 80, 75, 84, 75, 93, 75, + 91, 91, 75, 14, 80, 75, 75, 75, + 75, 75, 84, 75, 84, 75, 75, 75, + 91, 91, 75, 14, 80, 75, 75, 75, + 75, 75, 84, 75, 94, 95, 96, 96, + 12, 13, 80, 75, 75, 75, 12, 75, + 95, 96, 96, 12, 13, 80, 75, 75, + 75, 12, 75, 96, 96, 12, 13, 80, + 75, 75, 75, 12, 75, 80, 75, 75, + 92, 80, 75, 75, 75, 12, 75, 97, + 98, 98, 12, 13, 80, 75, 75, 75, + 12, 75, 85, 99, 75, 14, 80, 75, + 91, 91, 75, 14, 80, 75, 85, 75, + 91, 91, 75, 14, 80, 75, 91, 99, + 75, 14, 80, 75, 87, 77, 83, 83, + 12, 13, 80, 75, 75, 75, 12, 75, + 75, 81, 75, 87, 77, 88, 83, 12, + 13, 80, 75, 75, 17, 12, 75, 75, + 81, 75, 10, 11, 11, 12, 13, 75, + 75, 75, 75, 12, 75, 76, 77, 83, + 79, 12, 13, 80, 75, 75, 75, 12, + 75, 75, 81, 75, 101, 45, 102, 102, + 22, 23, 48, 100, 100, 100, 22, 100, + 100, 52, 100, 45, 102, 102, 22, 23, + 48, 100, 100, 100, 22, 100, 100, 52, + 100, 103, 100, 100, 100, 104, 105, 100, + 25, 48, 100, 100, 100, 100, 100, 103, + 100, 44, 45, 106, 107, 22, 23, 48, + 100, 100, 24, 22, 100, 100, 52, 100, + 103, 100, 100, 100, 108, 105, 100, 25, + 48, 100, 100, 100, 100, 100, 103, 100, + 48, 100, 100, 109, 48, 100, 48, 100, + 48, 100, 100, 100, 48, 100, 103, 100, + 110, 100, 108, 108, 100, 25, 48, 100, + 100, 100, 100, 100, 103, 100, 103, 100, + 100, 100, 108, 108, 100, 25, 48, 100, + 100, 100, 100, 100, 103, 100, 111, 112, + 113, 113, 22, 23, 48, 100, 100, 100, + 22, 100, 112, 113, 113, 22, 23, 48, + 100, 100, 100, 22, 100, 113, 113, 22, + 23, 48, 100, 100, 100, 22, 100, 48, + 100, 100, 109, 48, 100, 100, 100, 22, + 100, 44, 45, 102, 102, 22, 23, 48, + 100, 100, 100, 22, 100, 100, 52, 100, + 114, 115, 115, 22, 23, 48, 100, 100, + 100, 22, 100, 104, 116, 100, 25, 48, + 100, 108, 108, 100, 25, 48, 100, 104, + 100, 108, 108, 100, 25, 48, 100, 108, + 116, 100, 25, 48, 100, 44, 45, 106, + 102, 22, 23, 48, 100, 100, 24, 22, + 100, 100, 52, 100, 20, 21, 21, 22, + 23, 117, 117, 117, 24, 22, 117, 20, + 21, 21, 22, 23, 117, 117, 117, 117, + 22, 117, 119, 120, 121, 122, 32, 33, + 123, 118, 118, 34, 32, 118, 118, 124, + 118, 125, 120, 122, 122, 32, 33, 123, + 118, 118, 118, 32, 118, 118, 124, 118, + 120, 122, 122, 32, 33, 123, 118, 118, + 118, 32, 118, 118, 124, 118, 126, 118, + 118, 118, 127, 128, 118, 35, 123, 118, + 118, 118, 118, 118, 126, 118, 119, 120, + 121, 49, 32, 33, 123, 118, 118, 34, + 32, 118, 118, 124, 118, 126, 118, 118, + 118, 129, 128, 118, 35, 123, 118, 118, + 118, 118, 118, 126, 118, 123, 118, 118, + 130, 123, 118, 123, 118, 123, 118, 118, + 118, 123, 118, 126, 118, 131, 118, 129, + 129, 118, 35, 123, 118, 118, 118, 118, + 118, 126, 118, 126, 118, 118, 118, 129, + 129, 118, 35, 123, 118, 118, 118, 118, + 118, 126, 118, 132, 133, 134, 134, 32, + 33, 123, 118, 118, 118, 32, 118, 133, + 134, 134, 32, 33, 123, 118, 118, 118, + 32, 118, 134, 134, 32, 33, 123, 118, + 118, 118, 32, 118, 123, 118, 118, 130, + 123, 118, 118, 118, 32, 118, 119, 120, + 122, 122, 32, 33, 123, 118, 118, 118, + 32, 118, 118, 124, 118, 135, 136, 136, + 32, 33, 123, 118, 118, 118, 32, 118, + 127, 137, 118, 35, 123, 118, 129, 129, + 118, 35, 123, 118, 127, 118, 129, 129, + 118, 35, 123, 118, 129, 137, 118, 35, + 123, 118, 42, 43, 44, 45, 106, 102, + 22, 23, 48, 49, 49, 24, 22, 100, + 42, 52, 100, 56, 138, 58, 59, 4, + 5, 60, 55, 55, 8, 4, 55, 55, + 61, 55, 42, 43, 44, 45, 139, 140, + 22, 141, 142, 55, 49, 24, 22, 55, + 42, 52, 55, 20, 143, 143, 22, 141, + 60, 55, 55, 24, 22, 55, 60, 55, + 55, 67, 60, 55, 55, 55, 22, 55, + 142, 55, 55, 144, 142, 55, 55, 55, + 22, 55, 142, 55, 142, 55, 55, 55, + 142, 55, 42, 55, 68, 20, 143, 143, + 22, 141, 60, 55, 55, 55, 22, 55, + 42, 55, 146, 145, 147, 147, 145, 40, + 148, 145, 147, 147, 145, 40, 148, 145, + 148, 145, 145, 149, 148, 145, 148, 145, + 148, 145, 145, 145, 148, 145, 42, 117, + 117, 117, 117, 117, 117, 117, 117, 49, + 117, 117, 117, 117, 42, 117, 0 }; static const unsigned char _indic_syllable_machine_trans_targs[] = { - 27, 33, 38, 2, 39, 45, 46, 27, - 55, 8, 61, 56, 68, 69, 72, 27, - 77, 15, 83, 78, 86, 27, 91, 27, - 100, 21, 106, 101, 109, 114, 27, 125, - 27, 28, 48, 73, 75, 93, 94, 79, - 95, 115, 116, 87, 123, 128, 27, 29, - 31, 5, 47, 34, 42, 30, 1, 32, - 36, 0, 35, 37, 40, 41, 3, 43, - 4, 44, 27, 49, 51, 12, 71, 57, - 64, 50, 6, 52, 66, 59, 53, 11, - 70, 54, 7, 58, 60, 62, 63, 9, - 65, 10, 67, 27, 74, 17, 76, 89, - 81, 13, 92, 14, 80, 82, 84, 85, - 16, 88, 18, 90, 27, 27, 96, 98, - 19, 23, 102, 110, 97, 99, 112, 104, - 20, 103, 105, 107, 108, 22, 111, 24, - 113, 117, 118, 122, 119, 120, 25, 121, - 27, 124, 26, 126, 127 + 31, 37, 42, 2, 43, 46, 4, 50, + 51, 31, 60, 9, 66, 69, 61, 11, + 74, 75, 78, 31, 83, 17, 89, 92, + 93, 84, 31, 19, 98, 31, 107, 24, + 113, 116, 117, 108, 26, 122, 127, 31, + 134, 31, 32, 53, 79, 81, 100, 101, + 85, 102, 123, 124, 94, 132, 137, 31, + 33, 35, 6, 52, 38, 47, 34, 1, + 36, 40, 0, 39, 41, 44, 45, 3, + 48, 5, 49, 31, 54, 56, 14, 77, + 62, 70, 55, 7, 57, 72, 64, 58, + 13, 76, 59, 8, 63, 65, 67, 68, + 10, 71, 12, 73, 31, 80, 20, 82, + 96, 87, 15, 99, 16, 86, 88, 90, + 91, 18, 95, 21, 97, 31, 31, 103, + 105, 22, 27, 109, 118, 104, 106, 120, + 111, 23, 110, 112, 114, 115, 25, 119, + 28, 121, 125, 126, 131, 128, 129, 29, + 130, 31, 133, 30, 135, 136 }; static const char _indic_syllable_machine_trans_actions[] = { - 1, 0, 2, 0, 2, 2, 2, 3, - 2, 0, 2, 0, 2, 2, 2, 4, - 2, 0, 5, 0, 5, 6, 2, 7, - 2, 0, 2, 0, 2, 2, 8, 0, - 11, 2, 2, 5, 0, 12, 12, 0, - 2, 5, 2, 5, 2, 0, 13, 2, - 0, 0, 2, 0, 2, 2, 0, 2, - 2, 0, 0, 2, 2, 2, 0, 0, - 0, 2, 14, 2, 0, 0, 2, 0, - 2, 2, 0, 2, 2, 2, 2, 0, + 1, 0, 2, 0, 2, 0, 0, 2, + 2, 3, 2, 0, 2, 0, 0, 0, + 2, 2, 2, 4, 2, 0, 5, 0, + 5, 0, 6, 0, 2, 7, 2, 0, + 2, 0, 2, 0, 0, 2, 0, 8, + 0, 11, 2, 2, 5, 0, 12, 12, + 0, 2, 5, 2, 5, 2, 0, 13, + 2, 0, 0, 2, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 2, 0, - 0, 0, 2, 15, 5, 0, 5, 2, - 2, 0, 5, 0, 0, 2, 5, 5, - 0, 0, 0, 2, 16, 17, 2, 0, - 0, 0, 0, 2, 2, 2, 2, 2, - 0, 0, 2, 2, 2, 0, 0, 0, - 2, 0, 18, 18, 0, 0, 0, 0, - 19, 2, 0, 0, 0 + 0, 0, 2, 14, 2, 0, 0, 2, + 0, 2, 2, 0, 2, 2, 2, 2, + 0, 2, 2, 0, 0, 2, 2, 2, + 0, 0, 0, 2, 15, 5, 0, 5, + 2, 2, 0, 5, 0, 0, 2, 5, + 5, 0, 0, 0, 2, 16, 17, 2, + 0, 0, 0, 0, 2, 2, 2, 2, + 2, 0, 0, 2, 2, 2, 0, 0, + 0, 2, 0, 18, 18, 0, 0, 0, + 0, 19, 2, 0, 0, 0 }; static const char _indic_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -349,14 +384,16 @@ static const char _indic_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 }; static const char _indic_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 10, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -369,41 +406,42 @@ static const char _indic_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0 + 0, 0 }; static const short _indic_syllable_machine_eof_trans[] = { - 1, 1, 1, 1, 1, 1, 8, 8, - 8, 8, 8, 8, 8, 16, 16, 22, - 16, 16, 16, 24, 24, 24, 24, 24, - 24, 1, 31, 0, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, - 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, - 67, 92, 92, 92, 92, 92, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92, - 92, 92, 92, 92, 92, 109, 109, 110, - 110, 110, 110, 110, 110, 110, 110, 110, - 110, 110, 110, 110, 110, 110, 110, 110, - 110, 110, 110, 92, 47, 47, 47, 47, - 47, 47, 47, 137, 137, 137, 137, 137, - 109 + 1, 1, 1, 1, 1, 1, 1, 10, + 10, 10, 10, 10, 10, 10, 10, 20, + 20, 27, 20, 27, 20, 20, 30, 30, + 30, 30, 30, 30, 30, 1, 40, 0, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 118, 118, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 101, 56, 56, 56, 56, + 56, 56, 56, 56, 146, 146, 146, 146, + 146, 118 }; -static const int indic_syllable_machine_start = 27; -static const int indic_syllable_machine_first_final = 27; +static const int indic_syllable_machine_start = 31; +static const int indic_syllable_machine_first_final = 31; static const int indic_syllable_machine_error = -1; -static const int indic_syllable_machine_en_main = 27; +static const int indic_syllable_machine_en_main = 31; #line 58 "hb-ot-shaper-indic-machine.rl" -#line 117 "hb-ot-shaper-indic-machine.rl" +#line 118 "hb-ot-shaper-indic-machine.rl" #define found_syllable(syllable_type) \ @@ -412,7 +450,7 @@ static const int indic_syllable_machine_en_main = 27; for (unsigned int i = ts; i < te; i++) \ info[i].syllable() = (syllable_serial << 4) | syllable_type; \ syllable_serial++; \ - if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + if (syllable_serial == 16) syllable_serial = 1; \ } HB_STMT_END inline void @@ -422,7 +460,7 @@ find_syllables_indic (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 426 "hb-ot-shaper-indic-machine.hh" +#line 453 "hb-ot-shaper-indic-machine.hh" { cs = indic_syllable_machine_start; ts = 0; @@ -430,7 +468,7 @@ find_syllables_indic (hb_buffer_t *buffer) act = 0; } -#line 137 "hb-ot-shaper-indic-machine.rl" +#line 138 "hb-ot-shaper-indic-machine.rl" p = 0; @@ -438,7 +476,7 @@ find_syllables_indic (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 442 "hb-ot-shaper-indic-machine.hh" +#line 465 "hb-ot-shaper-indic-machine.hh" { int _slen; int _trans; @@ -452,7 +490,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 456 "hb-ot-shaper-indic-machine.hh" +#line 477 "hb-ot-shaper-indic-machine.hh" } _keys = _indic_syllable_machine_trans_keys + (cs<<1); @@ -475,51 +513,51 @@ _eof_trans: {te = p+1;} break; case 11: -#line 113 "hb-ot-shaper-indic-machine.rl" +#line 114 "hb-ot-shaper-indic-machine.rl" {te = p+1;{ found_syllable (indic_non_indic_cluster); }} break; case 13: -#line 108 "hb-ot-shaper-indic-machine.rl" +#line 109 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_consonant_syllable); }} break; case 14: -#line 109 "hb-ot-shaper-indic-machine.rl" +#line 110 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_vowel_syllable); }} break; case 17: -#line 110 "hb-ot-shaper-indic-machine.rl" +#line 111 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_standalone_cluster); }} break; case 19: -#line 111 "hb-ot-shaper-indic-machine.rl" +#line 112 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_symbol_cluster); }} break; case 15: -#line 112 "hb-ot-shaper-indic-machine.rl" +#line 113 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} break; case 16: -#line 113 "hb-ot-shaper-indic-machine.rl" +#line 114 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_non_indic_cluster); }} break; case 1: -#line 108 "hb-ot-shaper-indic-machine.rl" +#line 109 "hb-ot-shaper-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (indic_consonant_syllable); }} break; case 3: -#line 109 "hb-ot-shaper-indic-machine.rl" +#line 110 "hb-ot-shaper-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (indic_vowel_syllable); }} break; case 7: -#line 110 "hb-ot-shaper-indic-machine.rl" +#line 111 "hb-ot-shaper-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (indic_standalone_cluster); }} break; case 8: -#line 111 "hb-ot-shaper-indic-machine.rl" +#line 112 "hb-ot-shaper-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (indic_symbol_cluster); }} break; case 4: -#line 112 "hb-ot-shaper-indic-machine.rl" +#line 113 "hb-ot-shaper-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} break; case 6: @@ -540,19 +578,19 @@ _eof_trans: case 18: #line 1 "NONE" {te = p+1;} -#line 108 "hb-ot-shaper-indic-machine.rl" +#line 109 "hb-ot-shaper-indic-machine.rl" {act = 1;} break; case 5: #line 1 "NONE" {te = p+1;} -#line 112 "hb-ot-shaper-indic-machine.rl" +#line 113 "hb-ot-shaper-indic-machine.rl" {act = 5;} break; case 12: #line 1 "NONE" {te = p+1;} -#line 113 "hb-ot-shaper-indic-machine.rl" +#line 114 "hb-ot-shaper-indic-machine.rl" {act = 6;} break; #line 559 "hb-ot-shaper-indic-machine.hh" @@ -564,7 +602,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 568 "hb-ot-shaper-indic-machine.hh" +#line 566 "hb-ot-shaper-indic-machine.hh" } if ( ++p != pe ) @@ -580,7 +618,7 @@ _again: } -#line 145 "hb-ot-shaper-indic-machine.rl" +#line 146 "hb-ot-shaper-indic-machine.rl" } diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-indic-table.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-indic-table.cc index bf6a2757bb..d9fb0510e4 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-indic-table.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-indic-table.cc @@ -42,6 +42,7 @@ #define OT_PLACEHOLDER I_Cat(PLACEHOLDER) #define OT_DOTTEDCIRCLE I_Cat(DOTTEDCIRCLE) #define OT_RS I_Cat(RS) +#define OT_MPst I_Cat(MPst) #define OT_Repha I_Cat(Repha) #define OT_Ra I_Cat(Ra) #define OT_CM I_Cat(CM) @@ -80,9 +81,10 @@ static_assert (OT_VPst == M_Cat(VPst), ""); #define _OT_CS OT_CS /* 2 chars; CS */ #define _OT_DC OT_DOTTEDCIRCLE /* 1 chars; DOTTEDCIRCLE */ #define _OT_H OT_H /* 11 chars; H */ -#define _OT_M OT_M /* 143 chars; M */ +#define _OT_M OT_M /* 142 chars; M */ #define _OT_MH OT_MH /* 1 chars; MH */ #define _OT_ML OT_ML /* 1 chars; ML */ +#define _OT_MP OT_MPst /* 1 chars; MPst */ #define _OT_MR OT_MR /* 1 chars; MR */ #define _OT_MW OT_MW /* 2 chars; MW */ #define _OT_MY OT_MY /* 3 chars; MY */ @@ -200,7 +202,7 @@ static const uint16_t indic_table[] = { /* 0A28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), /* 0A30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(X,X), /* 0A38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(X,X), _(M,AP), _(M,LM), - /* 0A40 */ _(M,AP), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), + /* 0A40 */_(MP,AP), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), /* 0A48 */ _(M,AP), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X), /* 0A50 */ _(X,X), _(M,B), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), /* 0A58 */ _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X), @@ -451,15 +453,12 @@ static const uint16_t indic_table[] = { /* Grantha */ /* 11300 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X), - /* 11308 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 11310 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 11318 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 11320 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 11328 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 11330 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x11338u 1720 + /* 11338 */ _(X,X), _(X,X), _(X,X), _(N,X), _(N,X), _(X,X), _(X,X), _(X,X), -}; /* Table items: 1776; occupancy: 69% */ +}; /* Table items: 1728; occupancy: 71% */ uint16_t hb_indic_get_categories (hb_codepoint_t u) @@ -497,7 +496,8 @@ hb_indic_get_categories (hb_codepoint_t u) break; case 0x11u: - if (hb_in_range<hb_codepoint_t> (u, 0x11300u, 0x1133Fu)) return indic_table[u - 0x11300u + indic_offset_0x11300u]; + if (hb_in_range<hb_codepoint_t> (u, 0x11300u, 0x11307u)) return indic_table[u - 0x11300u + indic_offset_0x11300u]; + if (hb_in_range<hb_codepoint_t> (u, 0x11338u, 0x1133Fu)) return indic_table[u - 0x11338u + indic_offset_0x11338u]; break; default: @@ -519,6 +519,7 @@ hb_indic_get_categories (hb_codepoint_t u) #undef _OT_M #undef _OT_MH #undef _OT_ML +#undef _OT_MP #undef _OT_MR #undef _OT_MW #undef _OT_MY diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-indic.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-indic.cc index 55509c1101..7652210d9d 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-indic.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-indic.cc @@ -223,15 +223,15 @@ enum { INDIC_BASIC_FEATURES = INDIC_INIT, /* Don't forget to update this! */ }; -static void +static bool setup_syllables_indic (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void +static bool initial_reordering_indic (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void +static bool final_reordering_indic (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); @@ -413,7 +413,7 @@ setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, set_indic_properties (info[i]); } -static void +static bool setup_syllables_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) @@ -422,6 +422,7 @@ setup_syllables_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, find_syllables_indic (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); + return false; } static int @@ -714,6 +715,9 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, } } } else if (info[i].indic_position() != POS_SMVD) { + if (info[i].indic_category() == I_Cat(MPst) && + i > start && info[i - 1].indic_category() == I_Cat(SM)) + info[i - 1].indic_position() = info[i].indic_position(); last_pos = (indic_position_t) info[i].indic_position(); } } @@ -729,7 +733,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, if (info[j].indic_position() < POS_SMVD) info[j].indic_position() = info[i].indic_position(); last = i; - } else if (info[i].indic_category() == I_Cat(M)) + } else if (FLAG_UNSAFE (info[i].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)))) last = i; } @@ -742,14 +746,40 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, /* Sit tight, rock 'n roll! */ hb_stable_sort (info + start, end - start, compare_indic_order); - /* Find base again */ + + /* Find base again; also flip left-matra sequence. */ + unsigned first_left_matra = end; + unsigned last_left_matra = end; base = end; for (unsigned int i = start; i < end; i++) + { if (info[i].indic_position() == POS_BASE_C) { base = i; break; } + else if (info[i].indic_position() == POS_PRE_M) + { + if (first_left_matra == end) + first_left_matra = i; + last_left_matra = i; + } + } + /* https://github.com/harfbuzz/harfbuzz/issues/3863 */ + if (first_left_matra < last_left_matra) + { + /* No need to merge clusters, handled later. */ + buffer->reverse_range (first_left_matra, last_left_matra + 1); + /* Reverse back nuktas, etc. */ + unsigned i = first_left_matra; + for (unsigned j = i; j <= last_left_matra; j++) + if (FLAG_UNSAFE (info[j].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)))) + { + buffer->reverse_range (i, j + 1); + i = j + 1; + } + } + /* Things are out-of-control for post base positions, they may shuffle * around like crazy. In old-spec mode, we move halants around, so in * that case merge all clusters after base. Otherwise, check the sort @@ -955,25 +985,29 @@ initial_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, } } -static void +static bool initial_reordering_indic (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { + bool ret = false; if (!buffer->message (font, "start reordering indic initial")) - return; + return ret; update_consonant_positions_indic (plan, font, buffer); - hb_syllabic_insert_dotted_circles (font, buffer, - indic_broken_cluster, - I_Cat(DOTTEDCIRCLE), - I_Cat(Repha), - POS_END); + if (hb_syllabic_insert_dotted_circles (font, buffer, + indic_broken_cluster, + I_Cat(DOTTEDCIRCLE), + I_Cat(Repha), + POS_END)) + ret = true; foreach_syllable (buffer, start, end) initial_reordering_syllable_indic (plan, font->face, buffer, start, end); (void) buffer->message (font, "end reordering indic initial"); + + return ret; } static void @@ -1116,7 +1150,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, { search: while (new_pos > start && - !(is_one_of (info[new_pos], (FLAG (I_Cat(M)) | FLAG (I_Cat(H)))))) + !(is_one_of (info[new_pos], (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)) | FLAG (I_Cat(H)))))) new_pos--; /* If we found no Halant we are done. @@ -1316,7 +1350,8 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, unlikely (is_halant (info[new_reph_pos]))) { for (unsigned int i = base + 1; i < new_reph_pos; i++) - if (info[i].indic_category() == I_Cat(M)) { + if (FLAG_UNSAFE (info[i].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)))) + { /* Ok, got it. */ new_reph_pos--; } @@ -1376,7 +1411,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL) { while (new_pos > start && - !(is_one_of (info[new_pos - 1], FLAG(I_Cat(M)) | FLAG (I_Cat(H))))) + !(is_one_of (info[new_pos - 1], FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)) | FLAG (I_Cat(H))))) new_pos--; } @@ -1439,13 +1474,13 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, } -static void +static bool final_reordering_indic (const hb_ot_shape_plan_t *plan, hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) { unsigned int count = buffer->len; - if (unlikely (!count)) return; + if (unlikely (!count)) return false; if (buffer->message (font, "start reordering indic final")) { foreach_syllable (buffer, start, end) @@ -1455,6 +1490,8 @@ final_reordering_indic (const hb_ot_shape_plan_t *plan, HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category); HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position); + + return false; } diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-khmer-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-khmer-machine.hh index e18bd75ef1..fd91ee0caf 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-khmer-machine.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-khmer-machine.hh @@ -48,7 +48,7 @@ enum khmer_syllable_type_t { }; -#line 52 "hb-ot-shaper-khmer-machine.hh" +#line 49 "hb-ot-shaper-khmer-machine.hh" #define khmer_syllable_machine_ex_C 1u #define khmer_syllable_machine_ex_DOTTEDCIRCLE 11u #define khmer_syllable_machine_ex_H 4u @@ -66,7 +66,7 @@ enum khmer_syllable_type_t { #define khmer_syllable_machine_ex_ZWNJ 5u -#line 70 "hb-ot-shaper-khmer-machine.hh" +#line 65 "hb-ot-shaper-khmer-machine.hh" static const unsigned char _khmer_syllable_machine_trans_keys[] = { 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, @@ -284,7 +284,7 @@ static const int khmer_syllable_machine_en_main = 21; for (unsigned int i = ts; i < te; i++) \ info[i].syllable() = (syllable_serial << 4) | syllable_type; \ syllable_serial++; \ - if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + if (syllable_serial == 16) syllable_serial = 1; \ } HB_STMT_END inline void @@ -294,7 +294,7 @@ find_syllables_khmer (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 298 "hb-ot-shaper-khmer-machine.hh" +#line 287 "hb-ot-shaper-khmer-machine.hh" { cs = khmer_syllable_machine_start; ts = 0; @@ -310,7 +310,7 @@ find_syllables_khmer (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 314 "hb-ot-shaper-khmer-machine.hh" +#line 299 "hb-ot-shaper-khmer-machine.hh" { int _slen; int _trans; @@ -324,7 +324,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 328 "hb-ot-shaper-khmer-machine.hh" +#line 311 "hb-ot-shaper-khmer-machine.hh" } _keys = _khmer_syllable_machine_trans_keys + (cs<<1); @@ -394,7 +394,7 @@ _eof_trans: #line 98 "hb-ot-shaper-khmer-machine.rl" {act = 3;} break; -#line 398 "hb-ot-shaper-khmer-machine.hh" +#line 368 "hb-ot-shaper-khmer-machine.hh" } _again: @@ -403,7 +403,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 407 "hb-ot-shaper-khmer-machine.hh" +#line 375 "hb-ot-shaper-khmer-machine.hh" } if ( ++p != pe ) diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-khmer.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-khmer.cc index d9795589fa..019a285102 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-khmer.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-khmer.cc @@ -89,11 +89,11 @@ set_khmer_properties (hb_glyph_info_t &info) info.khmer_category() = (khmer_category_t) (type & 0xFFu); } -static void +static bool setup_syllables_khmer (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void +static bool reorder_khmer (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); @@ -192,7 +192,7 @@ setup_masks_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, set_khmer_properties (info[i]); } -static void +static bool setup_syllables_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) @@ -201,6 +201,7 @@ setup_syllables_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, find_syllables_khmer (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); + return false; } @@ -303,23 +304,27 @@ reorder_syllable_khmer (const hb_ot_shape_plan_t *plan, } } -static void +static bool reorder_khmer (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { + bool ret = false; if (buffer->message (font, "start reordering khmer")) { - hb_syllabic_insert_dotted_circles (font, buffer, - khmer_broken_cluster, - K_Cat(DOTTEDCIRCLE), - (unsigned) -1); + if (hb_syllabic_insert_dotted_circles (font, buffer, + khmer_broken_cluster, + K_Cat(DOTTEDCIRCLE), + (unsigned) -1)) + ret = true; foreach_syllable (buffer, start, end) reorder_syllable_khmer (plan, font->face, buffer, start, end); (void) buffer->message (font, "end reordering khmer"); } HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category); + + return ret; } diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh index b109708937..87cded4ed8 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh @@ -50,7 +50,7 @@ enum myanmar_syllable_type_t { }; -#line 54 "hb-ot-shaper-myanmar-machine.hh" +#line 51 "hb-ot-shaper-myanmar-machine.hh" #define myanmar_syllable_machine_ex_A 9u #define myanmar_syllable_machine_ex_As 32u #define myanmar_syllable_machine_ex_C 1u @@ -77,7 +77,7 @@ enum myanmar_syllable_type_t { #define myanmar_syllable_machine_ex_ZWNJ 5u -#line 81 "hb-ot-shaper-myanmar-machine.hh" +#line 76 "hb-ot-shaper-myanmar-machine.hh" static const unsigned char _myanmar_syllable_machine_trans_keys[] = { 1u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, 5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 39u, 3u, 39u, @@ -433,7 +433,7 @@ static const int myanmar_syllable_machine_en_main = 0; for (unsigned int i = ts; i < te; i++) \ info[i].syllable() = (syllable_serial << 4) | syllable_type; \ syllable_serial++; \ - if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + if (syllable_serial == 16) syllable_serial = 1; \ } HB_STMT_END inline void @@ -443,7 +443,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 447 "hb-ot-shaper-myanmar-machine.hh" +#line 436 "hb-ot-shaper-myanmar-machine.hh" { cs = myanmar_syllable_machine_start; ts = 0; @@ -459,7 +459,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 463 "hb-ot-shaper-myanmar-machine.hh" +#line 448 "hb-ot-shaper-myanmar-machine.hh" { int _slen; int _trans; @@ -473,7 +473,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 477 "hb-ot-shaper-myanmar-machine.hh" +#line 460 "hb-ot-shaper-myanmar-machine.hh" } _keys = _myanmar_syllable_machine_trans_keys + (cs<<1); @@ -519,7 +519,7 @@ _eof_trans: #line 113 "hb-ot-shaper-myanmar-machine.rl" {te = p;p--;{ found_syllable (myanmar_non_myanmar_cluster); }} break; -#line 523 "hb-ot-shaper-myanmar-machine.hh" +#line 498 "hb-ot-shaper-myanmar-machine.hh" } _again: @@ -528,7 +528,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 532 "hb-ot-shaper-myanmar-machine.hh" +#line 505 "hb-ot-shaper-myanmar-machine.hh" } if ( ++p != pe ) diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar.cc index 78bd8de524..1b2a085a8d 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar.cc @@ -98,11 +98,11 @@ is_consonant_myanmar (const hb_glyph_info_t &info) } -static void +static bool setup_syllables_myanmar (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void +static bool reorder_myanmar (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); @@ -150,7 +150,7 @@ setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, set_myanmar_properties (info[i]); } -static void +static bool setup_syllables_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) @@ -159,6 +159,7 @@ setup_syllables_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, find_syllables_myanmar (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); + return false; } static int @@ -270,6 +271,33 @@ initial_reordering_consonant_syllable (hb_buffer_t *buffer, /* Sit tight, rock 'n roll! */ buffer->sort (start, end, compare_myanmar_order); + + /* Flip left-matra sequence. */ + unsigned first_left_matra = end; + unsigned last_left_matra = end; + for (unsigned int i = start; i < end; i++) + { + if (info[i].myanmar_position() == POS_PRE_M) + { + if (first_left_matra == end) + first_left_matra = i; + last_left_matra = i; + } + } + /* https://github.com/harfbuzz/harfbuzz/issues/3863 */ + if (first_left_matra < last_left_matra) + { + /* No need to merge clusters, done already? */ + buffer->reverse_range (first_left_matra, last_left_matra + 1); + /* Reverse back VS, etc. */ + unsigned i = first_left_matra; + for (unsigned j = i; j <= last_left_matra; j++) + if (info[j].myanmar_category() == M_Cat(VPre)) + { + buffer->reverse_range (i, j + 1); + i = j + 1; + } + } } static void @@ -291,16 +319,18 @@ reorder_syllable_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, } } -static void +static bool reorder_myanmar (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { + bool ret = false; if (buffer->message (font, "start reordering myanmar")) { - hb_syllabic_insert_dotted_circles (font, buffer, - myanmar_broken_cluster, - M_Cat(DOTTEDCIRCLE)); + if (hb_syllabic_insert_dotted_circles (font, buffer, + myanmar_broken_cluster, + M_Cat(DOTTEDCIRCLE))) + ret = true; foreach_syllable (buffer, start, end) reorder_syllable_myanmar (plan, font->face, buffer, start, end); @@ -309,6 +339,8 @@ reorder_myanmar (const hb_ot_shape_plan_t *plan, HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category); HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position); + + return ret; } @@ -331,6 +363,7 @@ const hb_ot_shaper_t _hb_ot_shaper_myanmar = }; +#ifndef HB_NO_OT_SHAPER_MYANMAR_ZAWGYI /* Ugly Zawgyi encoding. * Disable all auto processing. * https://github.com/harfbuzz/harfbuzz/issues/1162 */ @@ -351,6 +384,7 @@ const hb_ot_shaper_t _hb_ot_shaper_myanmar_zawgyi = HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; +#endif #endif diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.cc index a8e0d8e8c1..89226ae4a1 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.cc @@ -29,7 +29,7 @@ #include "hb-ot-shaper-syllabic.hh" -void +bool hb_syllabic_insert_dotted_circles (hb_font_t *font, hb_buffer_t *buffer, unsigned int broken_syllable_type, @@ -38,13 +38,13 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font, int dottedcircle_position) { if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) - return; + return false; if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE))) - return; + return false; hb_codepoint_t dottedcircle_glyph; if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph)) - return; + return false; hb_glyph_info_t dottedcircle = {0}; dottedcircle.codepoint = 0x25CCu; @@ -84,14 +84,16 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font, (void) buffer->next_glyph (); } buffer->sync (); + return true; } -HB_INTERNAL void +HB_INTERNAL bool hb_syllabic_clear_var (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { HB_BUFFER_DEALLOCATE_VAR (buffer, syllable); + return false; } diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.hh index e8a15bb48a..f240ad1c26 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.hh @@ -30,7 +30,7 @@ #include "hb-ot-shaper.hh" -HB_INTERNAL void +HB_INTERNAL bool hb_syllabic_insert_dotted_circles (hb_font_t *font, hb_buffer_t *buffer, unsigned int broken_syllable_type, @@ -38,7 +38,7 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font, int repha_category = -1, int dottedcircle_position = -1); -HB_INTERNAL void +HB_INTERNAL bool hb_syllabic_clear_var (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-thai.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-thai.cc index 15349b1e64..6cd67cde35 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-thai.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-thai.cc @@ -98,9 +98,9 @@ static hb_codepoint_t thai_pua_shape (hb_codepoint_t u, thai_action_t action, hb_font_t *font) { struct thai_pua_mapping_t { - hb_codepoint_t u; - hb_codepoint_t win_pua; - hb_codepoint_t mac_pua; + uint16_t u; + uint16_t win_pua; + uint16_t mac_pua; } const *pua_mappings = nullptr; static const thai_pua_mapping_t SD_mappings[] = { {0x0E48u, 0xF70Au, 0xF88Bu}, /* MAI EK */ diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-use-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-use-machine.hh index 41e8a34f3b..f2fbdb725b 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-use-machine.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-use-machine.hh @@ -53,7 +53,7 @@ enum use_syllable_type_t { }; -#line 57 "hb-ot-shaper-use-machine.hh" +#line 54 "hb-ot-shaper-use-machine.hh" #define use_syllable_machine_ex_B 1u #define use_syllable_machine_ex_CGJ 6u #define use_syllable_machine_ex_CMAbv 31u @@ -97,7 +97,7 @@ enum use_syllable_type_t { #define use_syllable_machine_ex_ZWNJ 14u -#line 101 "hb-ot-shaper-use-machine.hh" +#line 96 "hb-ot-shaper-use-machine.hh" static const unsigned char _use_syllable_machine_trans_keys[] = { 0u, 53u, 11u, 53u, 11u, 53u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, @@ -839,7 +839,7 @@ static const int use_syllable_machine_en_main = 0; for (unsigned i = (*ts).second.first; i < (*te).second.first; ++i) \ info[i].syllable() = (syllable_serial << 4) | syllable_type; \ syllable_serial++; \ - if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + if (syllable_serial == 16) syllable_serial = 1; \ } HB_STMT_END @@ -929,7 +929,7 @@ find_syllables_use (hb_buffer_t *buffer) unsigned int act HB_UNUSED; int cs; -#line 933 "hb-ot-shaper-use-machine.hh" +#line 922 "hb-ot-shaper-use-machine.hh" { cs = use_syllable_machine_start; ts = 0; @@ -942,7 +942,7 @@ find_syllables_use (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 946 "hb-ot-shaper-use-machine.hh" +#line 931 "hb-ot-shaper-use-machine.hh" { int _slen; int _trans; @@ -956,7 +956,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 960 "hb-ot-shaper-use-machine.hh" +#line 943 "hb-ot-shaper-use-machine.hh" } _keys = _use_syllable_machine_trans_keys + (cs<<1); @@ -1046,7 +1046,7 @@ _eof_trans: #line 178 "hb-ot-shaper-use-machine.rl" {te = p;p--;{ found_syllable (use_non_cluster); }} break; -#line 1050 "hb-ot-shaper-use-machine.hh" +#line 1014 "hb-ot-shaper-use-machine.hh" } _again: @@ -1055,7 +1055,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1059 "hb-ot-shaper-use-machine.hh" +#line 1021 "hb-ot-shaper-use-machine.hh" } if ( ++p != pe ) diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-use-table.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-use-table.hh index 9833fb55de..6b6b552ee5 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-use-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-use-table.hh @@ -91,6 +91,9 @@ #define VMPre USE(VMPre) #pragma GCC diagnostic pop + +#ifndef HB_OPTIMIZE_SIZE + static const uint8_t hb_use_u8[3141] = { @@ -357,6 +360,273 @@ hb_use_get_category (unsigned u) return u<921600u?hb_use_u8[2777+(((hb_use_u8[593+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; } + +#else + +static const uint8_t +hb_use_u8[3413] = +{ + 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 51, 57, 58, 179, 195, 61, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 14, 0, 1, 1, 2, 1, 1, 3, 4, 5, 6, 7, 8, 9, 10, 1, + 11, 12, 1, 1, 1, 1, 1, 1, 13, 14, 15, 16, 17, 18, 19, 1, + 1, 20, 1, 1, 1, 1, 21, 1, 1, 1, 1, 1, 1, 1, 22, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 24, 25, 26, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, + 28, 1, 1, 1, 1, 1, 29, 1, 1, 1, 1, 30, 31, 1, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 1, 46, 47, 48, + 49, 50, 50, 50, 50, 51, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 52, 53, 1, 1, 1, + 54, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 50, 55, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 56, 1, 1, + 1, 1, 57, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 58, 59, 1, 60, 1, 1, 1, 1, 61, 1, 1, 1, 1, 1, + 1, 62, 63, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 8, 0, 0, 0, 0, + 0, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 36, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 0, 55, 56, 57, 58, 59, 0, 0, 0, 60, 61, 62, 63, 55, 64, 65, + 66, 67, 55, 55, 68, 69, 70, 0, 0, 71, 72, 73, 74, 55, 75, 76, + 0, 77, 55, 78, 79, 80, 0, 0, 0, 81, 82, 83, 84, 85, 86, 55, + 87, 55, 88, 89, 0, 0, 0, 90, 91, 0, 0, 0, 0, 0, 0, 0, + 92, 93, 94, 0, 95, 96, 0, 0, 97, 0, 0, 0, 0, 0, 0, 98, + 0, 0, 99, 55, 100, 0, 0, 0, 0, 101, 102, 55, 103, 104, 105, 106, + 107, 55, 108, 109, 0, 110, 111, 112, 113, 55, 114, 115, 116, 55, 117, 118, + 119, 0, 0, 0, 0, 0, 0, 55, 120, 121, 0, 0, 0, 0, 0, 0, + 122, 0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, 124, 125, 126, 0, + 0, 127, 128, 129, 0, 0, 0, 50, 130, 0, 0, 0, 0, 131, 132, 0, + 0, 55, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 134, 0, + 0, 0, 99, 135, 99, 136, 137, 138, 0, 139, 140, 141, 142, 143, 144, 145, + 0, 146, 147, 148, 149, 143, 150, 151, 152, 153, 154, 155, 0, 156, 157, 158, + 159, 160, 161, 162, 163, 0, 0, 0, 0, 55, 164, 165, 166, 167, 168, 169, + 0, 0, 0, 0, 0, 55, 170, 171, 0, 55, 172, 173, 0, 55, 174, 66, + 0, 175, 176, 177, 0, 0, 0, 0, 0, 55, 178, 0, 0, 0, 0, 0, + 0, 179, 180, 181, 0, 0, 182, 183, 184, 185, 186, 187, 55, 188, 0, 0, + 0, 189, 190, 191, 192, 193, 194, 0, 0, 195, 196, 197, 198, 199, 66, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 200, 201, 202, 203, 0, 0, 0, 0, + 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 204, 205, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 66, 0, 55, 206, 0, 0, 0, 0, 0, + 0, 55, 55, 207, 208, 209, 0, 0, 210, 55, 55, 55, 55, 55, 55, 211, + 0, 55, 55, 55, 212, 213, 0, 0, 0, 0, 0, 0, 214, 0, 0, 0, + 0, 55, 215, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 217, 55, + 218, 0, 0, 0, 0, 0, 0, 99, 219, 55, 55, 220, 0, 0, 0, 0, + 0, 221, 221, 221, 221, 221, 221, 221, 221, 222, 222, 222, 222, 222, 222, 222, + 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 2, 2, 2, 2, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, + 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 8, 9, 9, 9, 9, 0, 0, 0, 7, 10, + 0, 2, 2, 2, 2, 11, 12, 0, 0, 9, 13, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 14, 15, 16, 17, 18, 19, 20, 14, 21, 22, + 23, 10, 24, 25, 18, 2, 2, 2, 2, 2, 18, 0, 2, 2, 2, 2, + 2, 0, 2, 2, 2, 2, 2, 2, 2, 26, 27, 28, 2, 2, 2, 7, + 28, 7, 28, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 2, 2, + 2, 7, 7, 0, 2, 2, 0, 15, 16, 17, 18, 29, 30, 31, 30, 32, + 0, 0, 0, 0, 33, 0, 0, 2, 28, 2, 0, 0, 0, 0, 0, 7, + 34, 10, 13, 28, 2, 2, 7, 0, 28, 7, 2, 28, 7, 2, 0, 35, + 16, 17, 29, 0, 25, 36, 25, 37, 0, 38, 0, 0, 0, 28, 2, 7, + 7, 0, 0, 0, 2, 2, 2, 2, 2, 39, 40, 41, 0, 0, 0, 0, + 0, 10, 13, 28, 2, 2, 2, 2, 28, 2, 28, 2, 2, 2, 2, 2, + 2, 7, 2, 28, 2, 2, 0, 15, 16, 17, 18, 19, 25, 20, 33, 22, + 0, 0, 0, 0, 0, 28, 39, 39, 42, 10, 27, 28, 2, 2, 2, 7, + 28, 7, 2, 28, 2, 2, 0, 15, 43, 0, 0, 25, 20, 0, 0, 2, + 28, 28, 0, 0, 0, 0, 0, 0, 0, 0, 44, 28, 2, 2, 7, 0, + 2, 7, 2, 2, 0, 28, 7, 7, 2, 0, 28, 7, 0, 2, 7, 0, + 2, 2, 2, 2, 2, 2, 0, 0, 21, 14, 45, 0, 46, 31, 46, 32, + 0, 0, 0, 0, 33, 0, 0, 0, 0, 13, 27, 47, 2, 2, 2, 7, + 2, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 15, + 20, 14, 21, 45, 20, 36, 20, 37, 0, 0, 0, 25, 29, 2, 7, 0, + 0, 8, 27, 28, 2, 2, 2, 7, 2, 2, 2, 28, 2, 2, 0, 15, + 43, 0, 0, 33, 45, 0, 0, 0, 7, 48, 49, 0, 0, 0, 0, 0, + 0, 9, 27, 2, 2, 2, 2, 7, 2, 2, 2, 2, 2, 2, 50, 51, + 21, 21, 17, 29, 46, 31, 46, 32, 52, 0, 0, 0, 33, 0, 0, 0, + 28, 10, 27, 28, 2, 2, 2, 2, 2, 2, 2, 2, 7, 0, 2, 2, + 2, 2, 28, 2, 2, 2, 2, 28, 0, 2, 2, 2, 7, 0, 53, 0, + 33, 21, 20, 29, 29, 16, 46, 46, 23, 0, 21, 0, 0, 0, 0, 0, + 0, 2, 0, 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, + 0, 2, 2, 54, 54, 55, 0, 0, 16, 2, 2, 2, 2, 28, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 7, 0, 56, 19, 57, 20, 20, 18, 18, + 44, 19, 9, 29, 9, 2, 2, 58, 59, 59, 59, 59, 59, 60, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, + 0, 0, 0, 0, 62, 0, 0, 0, 0, 2, 2, 2, 2, 2, 63, 43, + 57, 64, 20, 20, 65, 66, 67, 68, 69, 2, 2, 2, 2, 2, 1, 0, + 3, 2, 2, 2, 21, 18, 2, 2, 70, 69, 71, 72, 63, 71, 27, 27, + 2, 50, 20, 51, 2, 2, 2, 2, 2, 2, 73, 74, 75, 27, 27, 76, + 77, 2, 2, 2, 2, 2, 27, 43, 0, 2, 57, 78, 0, 0, 0, 0, + 28, 2, 57, 45, 0, 0, 0, 0, 0, 2, 57, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 7, 2, 7, 57, 0, 0, 0, 0, 0, + 0, 2, 2, 79, 43, 20, 57, 18, 46, 46, 46, 46, 13, 80, 81, 82, + 83, 84, 85, 0, 0, 0, 0, 86, 0, 7, 0, 0, 28, 0, 87, 79, + 88, 2, 2, 2, 2, 7, 0, 0, 0, 40, 40, 89, 90, 2, 2, 2, + 2, 2, 2, 2, 2, 11, 7, 0, 0, 91, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 7, 20, 78, 43, 20, 92, 59, 0, + 0, 93, 94, 93, 93, 95, 96, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 2, 7, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, + 0, 2, 2, 2, 2, 27, 0, 0, 0, 2, 2, 2, 2, 2, 7, 0, + 0, 2, 2, 2, 50, 97, 43, 0, 0, 2, 2, 98, 99, 100, 101, 59, + 61, 102, 14, 43, 20, 57, 19, 78, 46, 46, 74, 9, 9, 9, 103, 44, + 38, 9, 104, 72, 2, 2, 2, 2, 2, 2, 2, 105, 20, 18, 18, 20, + 46, 46, 20, 106, 2, 2, 2, 7, 0, 0, 0, 0, 0, 0, 107, 108, + 109, 109, 109, 0, 0, 0, 0, 0, 0, 104, 72, 2, 2, 2, 2, 2, + 2, 58, 59, 57, 23, 20, 110, 59, 2, 2, 2, 2, 105, 20, 21, 43, + 43, 100, 12, 0, 0, 0, 0, 0, 0, 2, 2, 59, 16, 46, 21, 111, + 100, 100, 100, 112, 113, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 28, + 2, 9, 44, 114, 114, 114, 9, 114, 114, 13, 114, 114, 114, 24, 0, 38, + 0, 0, 0, 115, 49, 9, 3, 0, 0, 0, 0, 0, 0, 0, 116, 0, + 0, 0, 0, 0, 0, 0, 4, 117, 118, 40, 40, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 118, 118, 119, 118, 118, 118, 118, 118, 118, 118, + 118, 0, 0, 120, 0, 0, 0, 0, 0, 0, 5, 120, 0, 0, 0, 0, + 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 0, 2, 2, 2, 2, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, + 121, 2, 51, 2, 106, 2, 8, 2, 2, 2, 63, 17, 14, 0, 0, 29, + 0, 2, 2, 0, 0, 0, 0, 0, 0, 27, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 122, 21, 21, 21, 21, 21, 21, 21, 123, 0, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 0, + 50, 2, 2, 2, 20, 20, 124, 114, 0, 2, 2, 2, 125, 18, 57, 18, + 111, 100, 126, 0, 0, 0, 0, 0, 0, 9, 127, 2, 2, 2, 2, 2, + 2, 2, 128, 21, 20, 18, 46, 129, 130, 131, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 50, 28, 2, 2, 2, 2, 2, 2, 2, 2, 8, 20, 57, + 97, 74, 132, 133, 134, 0, 0, 0, 0, 2, 135, 2, 2, 2, 2, 136, + 0, 28, 2, 40, 3, 0, 77, 13, 2, 51, 20, 137, 50, 51, 2, 2, + 103, 8, 7, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 138, 19, + 23, 0, 0, 139, 140, 0, 0, 0, 0, 2, 63, 43, 21, 78, 45, 141, + 0, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, 0, 0, 0, 0, 0, + 4, 118, 118, 118, 118, 119, 0, 0, 0, 2, 2, 2, 2, 2, 7, 2, + 2, 2, 7, 2, 28, 2, 2, 2, 2, 2, 28, 2, 2, 2, 28, 7, + 0, 125, 18, 25, 29, 0, 0, 142, 143, 2, 2, 28, 2, 28, 2, 2, + 2, 2, 2, 2, 0, 12, 35, 0, 144, 2, 2, 11, 35, 0, 28, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 2, 2, + 7, 2, 2, 9, 39, 0, 0, 0, 0, 2, 2, 2, 2, 2, 25, 36, + 0, 2, 2, 2, 114, 114, 114, 114, 114, 145, 2, 7, 0, 0, 0, 0, + 0, 2, 12, 12, 0, 0, 0, 0, 0, 7, 2, 2, 7, 2, 2, 2, + 2, 28, 2, 7, 0, 28, 2, 0, 0, 146, 147, 148, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 20, 20, 18, 18, 18, 20, 20, 131, 0, 0, 0, + 0, 0, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 2, 2, 2, 2, + 2, 51, 50, 51, 0, 0, 0, 0, 150, 9, 72, 2, 2, 2, 2, 2, + 2, 16, 17, 19, 14, 22, 35, 0, 0, 0, 29, 0, 0, 0, 0, 0, + 0, 9, 47, 2, 2, 2, 2, 2, 2, 2, 2, 2, 125, 18, 20, 151, + 20, 19, 152, 153, 2, 2, 2, 2, 2, 0, 0, 63, 154, 0, 0, 0, + 0, 2, 11, 0, 0, 0, 0, 0, 0, 2, 63, 23, 18, 18, 18, 20, + 20, 106, 155, 0, 0, 54, 156, 29, 157, 28, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 21, 17, 20, 20, 158, 42, 0, 0, 0, + 47, 125, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 7, 7, 2, 2, + 28, 2, 2, 2, 2, 2, 2, 2, 28, 2, 2, 2, 2, 2, 2, 2, + 8, 16, 17, 19, 20, 159, 29, 0, 0, 9, 9, 28, 2, 2, 2, 7, + 28, 7, 2, 28, 2, 2, 56, 15, 21, 14, 21, 45, 30, 31, 30, 32, + 0, 0, 0, 0, 33, 0, 0, 0, 2, 2, 21, 0, 9, 9, 9, 44, + 0, 9, 9, 44, 0, 0, 0, 0, 0, 2, 2, 63, 23, 18, 18, 18, + 20, 21, 123, 13, 15, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, + 160, 161, 0, 0, 0, 0, 0, 0, 0, 16, 17, 18, 18, 64, 97, 23, + 157, 9, 162, 7, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 63, 23, 18, 18, 0, 46, 46, 9, 163, 35, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 18, 0, 21, 17, 18, 18, 19, 14, 80, + 163, 36, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 8, 164, + 23, 18, 20, 20, 162, 7, 0, 0, 0, 2, 2, 2, 2, 2, 7, 41, + 133, 21, 20, 18, 74, 19, 20, 0, 0, 2, 2, 2, 7, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 16, 17, 18, 19, 20, 103, 163, 35, 0, + 0, 2, 2, 2, 7, 28, 0, 2, 2, 2, 2, 28, 7, 2, 2, 2, + 2, 21, 21, 16, 30, 31, 10, 165, 166, 167, 168, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 0, 2, 2, 2, 63, 23, 18, 18, 0, 20, 21, + 27, 106, 0, 31, 0, 0, 0, 0, 0, 50, 18, 20, 20, 20, 137, 2, + 2, 2, 169, 170, 9, 13, 171, 70, 172, 0, 0, 1, 144, 0, 0, 0, + 0, 50, 18, 20, 14, 17, 18, 2, 2, 2, 2, 155, 155, 155, 173, 173, + 173, 173, 173, 173, 13, 174, 0, 28, 0, 20, 18, 18, 29, 20, 20, 9, + 163, 0, 59, 59, 59, 59, 59, 59, 59, 64, 19, 80, 44, 0, 0, 0, + 0, 2, 2, 2, 7, 2, 28, 2, 2, 50, 20, 20, 29, 0, 36, 20, + 25, 9, 156, 175, 171, 0, 0, 0, 0, 2, 2, 2, 28, 7, 2, 2, + 2, 2, 2, 2, 2, 2, 21, 21, 45, 20, 33, 80, 66, 0, 0, 0, + 0, 2, 176, 64, 45, 0, 0, 0, 0, 9, 177, 2, 2, 2, 2, 2, + 2, 2, 2, 21, 20, 18, 29, 0, 46, 14, 140, 0, 0, 0, 0, 0, + 0, 178, 178, 178, 106, 179, 178, 0, 0, 145, 2, 2, 180, 114, 114, 114, + 114, 114, 114, 114, 0, 0, 0, 0, 0, 9, 9, 9, 44, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 7, 0, 56, 181, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0, + 38, 114, 24, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, + 0, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 56, + 35, 0, 4, 118, 118, 118, 119, 0, 0, 9, 9, 9, 47, 2, 2, 2, + 0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 44, 2, 2, 2, 2, 2, 2, 9, 9, 2, 2, 2, 2, 2, 2, 20, + 20, 2, 2, 42, 42, 42, 90, 0, 0, O, O, O, GB, B, B, GB, + O, O, WJ,FMPst,FMPst, O, CGJ, B, O, B,VMAbv,VMAbv,VMAbv, O,VMAbv, B, + CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, + VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre, VPst,VMBlw, O, O, VAbv, GB,VMAbv,VMPst, + VMPst, O, B, VBlw, O, O, VPre, VPre, O, VPre, H, O, VPst,FMAbv, O,CMBlw, + O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, MBlw,CMAbv,CMAbv, VPst, + VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv, B, O, CS, CS,VMPst, B, VAbv, VAbv, + B, R, O, HVM, O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, VBlw, B, SUB, SUB, + SUB, O, SUB, SUB, O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv,VMBlw,VMPst, IS, VAbv, + MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv, + VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv,FMAbv, FAbv,CMAbv,FMAbv, + VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB,CMAbv,CMAbv, B, GB, + B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw, + SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O, + SMAbv,SMBlw,SMAbv,SMAbv,SMAbv, VPst, IS, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv, + CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, O, H, MPst, VPst, H, + VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, H, + O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, B, B, VPre, O, + VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, + CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, IS,CMAbv, O, VPst, B, R, + R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,FMAbv, B, CS, CS, H,CMBlw,VMPst, + H,VMPst, VAbv,VMAbv, VPst, IS, R, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, R, + MBlw, MBlw, GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, GB, VAbv, R,VMPst, H, H, B, + H, B,VMBlw, O, VBlw, +}; +static const uint16_t +hb_use_u16[448] = +{ + 0, 0, 1, 2, 3, 4, 0, 5, 6, 0, 7, 0, 8, 9, 10, 11, + 9, 12, 13, 9, 9, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 17, 25, 26, 20, 21, 27, 28, 29, 30, 31, 32, 33, 21, 34, 35, 0, + 17, 36, 37, 20, 21, 38, 23, 39, 17, 40, 41, 42, 43, 44, 45, 46, + 30, 0, 47, 48, 21, 49, 50, 51, 17, 0, 52, 48, 21, 53, 50, 54, + 17, 55, 56, 48, 9, 57, 58, 59, 60, 61, 9, 62, 63, 64, 30, 65, + 66, 67, 9, 68, 69, 9, 70, 71, 72, 73, 74, 75, 76, 0, 9, 9, + 77, 78, 79, 80, 81, 82, 83, 84, 9, 85, 9, 86, 9, 87, 88, 89, + 9, 90, 91, 92, 2, 0, 93, 0, 9, 94, 95, 9, 96, 0, 97, 98, + 99,100, 30, 9,101,102,103, 9,104,105, 9,106, 9,107,108,109, + 2, 2,110, 9, 9,111,112, 2,113,114,115, 9,116, 9,117,118, + 119,120,121, 0, 0,122,123,124, 0,125,126,127,128, 0,129,130, + 131, 0, 0,132,133, 0, 0, 9,134,135,136, 9,137, 0, 9,138, + 139, 9, 9,140,141, 2,142,143,144, 9,145,146,147, 9, 9,148, + 149, 2,150, 98,151,152,153, 2, 9,154, 9,155,156, 0,157,158, + 159, 2,160, 0, 0,161, 0,162, 0,163,163,164, 33,165,166,167, + 9,168, 94, 0,169, 0, 9,170,171, 0,172, 2,173,170,174,175, + 176, 0, 0,177,178, 0,179, 9, 9,180,181,182,183,184,185, 9, + 9,186,187, 0,188, 9,189,190,191, 9, 9,192, 9,193,194,105, + 195,102, 9, 33,196,197,198, 0,199,200, 94, 9, 9,201,202, 2, + 203, 20, 21,204,205,206,207,208, 9,209,210,211,212, 0,195, 9, + 9,213,214, 2,215,216,217,218, 9,219,220, 2,221,222, 9,223, + 224,103,225, 0,226,227,228,229, 9,230,231, 2,232, 9, 9,233, + 234, 0,235, 9, 9,236,237,238,239,240, 21, 9,215,241, 7, 9, + 70, 18, 9,242, 73,243,244, 9, 9,245,246, 2,247, 9,248,249, + 9,250,251, 48, 9,252,253, 2, 9,254,255,256, 9,257,258,259, + 260,260,261,262,263, 0, 9,264,105, 70, 94,265, 0,266, 70,267, + 268, 0,269, 0,270, 2,271, 2,272, 2,129,129,160,160,160,129, +}; + +static inline unsigned +hb_use_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline uint_fast8_t +hb_use_get_category (unsigned u) +{ + return u<921600u?hb_use_u8[3049+(((hb_use_u8[865+(((hb_use_u16[((hb_use_u8[353+(((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>1>>3>>4))<<4)+((u>>1>>3>>1>>3)&15u))])<<3)+((u>>1>>3>>1)&7u))])<<1)+((u>>1>>3)&1u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; +} + +#endif + #undef B #undef CGJ #undef CS diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-use.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-use.cc index c40ec52f9c..342aba1235 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-use.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-use.cc @@ -89,19 +89,19 @@ use_other_features[] = HB_TAG('p','s','t','s'), }; -static void +static bool setup_syllables_use (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void +static bool record_rphf_use (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void +static bool record_pref_use (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void +static bool reorder_use (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); @@ -293,7 +293,7 @@ setup_topographical_masks (const hb_ot_shape_plan_t *plan, } } -static void +static bool setup_syllables_use (const hb_ot_shape_plan_t *plan, hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) @@ -304,9 +304,10 @@ setup_syllables_use (const hb_ot_shape_plan_t *plan, buffer->unsafe_to_break (start, end); setup_rphf_mask (plan, buffer); setup_topographical_masks (plan, buffer); + return false; } -static void +static bool record_rphf_use (const hb_ot_shape_plan_t *plan, hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) @@ -314,7 +315,7 @@ record_rphf_use (const hb_ot_shape_plan_t *plan, const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; hb_mask_t mask = use_plan->rphf_mask; - if (!mask) return; + if (!mask) return false; hb_glyph_info_t *info = buffer->info; foreach_syllable (buffer, start, end) @@ -327,9 +328,10 @@ record_rphf_use (const hb_ot_shape_plan_t *plan, break; } } + return false; } -static void +static bool record_pref_use (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) @@ -346,6 +348,7 @@ record_pref_use (const hb_ot_shape_plan_t *plan HB_UNUSED, break; } } + return false; } static inline bool @@ -438,17 +441,19 @@ reorder_syllable_use (hb_buffer_t *buffer, unsigned int start, unsigned int end) } } -static void +static bool reorder_use (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { + bool ret = false; if (buffer->message (font, "start reordering USE")) { - hb_syllabic_insert_dotted_circles (font, buffer, - use_broken_cluster, - USE(B), - USE(R)); + if (hb_syllabic_insert_dotted_circles (font, buffer, + use_broken_cluster, + USE(B), + USE(R))) + ret = true; foreach_syllable (buffer, start, end) reorder_syllable_use (buffer, start, end); @@ -457,6 +462,8 @@ reorder_use (const hb_ot_shape_plan_t *plan, } HB_BUFFER_DEALLOCATE_VAR (buffer, use_category); + + return ret; } diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper.hh b/thirdparty/harfbuzz/src/hb-ot-shaper.hh index b2d1acb39b..0207b2bbe3 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper.hh @@ -262,11 +262,13 @@ hb_ot_shaper_categorize (const hb_ot_shape_planner_t *planner) return &_hb_ot_shaper_myanmar; +#ifndef HB_NO_OT_SHAPER_MYANMAR_ZAWGYI #define HB_SCRIPT_MYANMAR_ZAWGYI ((hb_script_t) HB_TAG ('Q','a','a','g')) case HB_SCRIPT_MYANMAR_ZAWGYI: /* https://github.com/harfbuzz/harfbuzz/issues/1162 */ return &_hb_ot_shaper_myanmar_zawgyi; +#endif /* Unicode-2.0 additions */ diff --git a/thirdparty/harfbuzz/src/hb-ot-stat-table.hh b/thirdparty/harfbuzz/src/hb-ot-stat-table.hh index af6d550c0e..59bb2daccd 100644 --- a/thirdparty/harfbuzz/src/hb-ot-stat-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-stat-table.hh @@ -320,7 +320,7 @@ struct AxisValueFormat4 unsigned total_size = min_size + axisCount * AxisValueRecord::static_size; auto *out = c->serializer->allocate_size<AxisValueFormat4> (total_size); if (unlikely (!out)) return_trace (false); - memcpy (out, this, total_size); + hb_memcpy (out, this, total_size); return_trace (true); } diff --git a/thirdparty/harfbuzz/src/hb-ot-tag.cc b/thirdparty/harfbuzz/src/hb-ot-tag.cc index 1b18270cca..f6ba3b0d47 100644 --- a/thirdparty/harfbuzz/src/hb-ot-tag.cc +++ b/thirdparty/harfbuzz/src/hb-ot-tag.cc @@ -577,7 +577,7 @@ hb_ot_tags_to_script_and_language (hb_tag_t script_tag, else { int shift; - memcpy (buf, lang_str, len); + hb_memcpy (buf, lang_str, len); if (lang_str[0] != 'x' || lang_str[1] != '-') { buf[len++] = '-'; buf[len++] = 'x'; diff --git a/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh index f60bc4a3ce..cc5c5c0068 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh @@ -143,7 +143,7 @@ struct avar TRACE_SANITIZE (this); if (!(version.sanitize (c) && (version.major == 1 -#ifndef HB_NO_VARIATIONS2 +#ifndef HB_NO_AVAR2 || version.major == 2 #endif ) && @@ -159,7 +159,7 @@ struct avar map = &StructAfter<SegmentMaps> (*map); } -#ifndef HB_NO_VARIATIONS2 +#ifndef HB_NO_AVAR2 if (version.major < 2) return_trace (true); @@ -182,7 +182,7 @@ struct avar map = &StructAfter<SegmentMaps> (*map); } -#ifndef HB_NO_VARIATIONS2 +#ifndef HB_NO_AVAR2 if (version.major < 2) return; diff --git a/thirdparty/harfbuzz/src/hb-ot-var-common.hh b/thirdparty/harfbuzz/src/hb-ot-var-common.hh index 1d29e3e4f9..4997c2e2e8 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-common.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-common.hh @@ -47,7 +47,7 @@ struct DeltaSetIndexMapFormat01 HBUINT8 *p = c->allocate_size<HBUINT8> (total_size); if (unlikely (!p)) return_trace (nullptr); - memcpy (p, this, HBUINT8::static_size * total_size); + hb_memcpy (p, this, HBUINT8::static_size * total_size); return_trace (out); } @@ -219,6 +219,25 @@ struct DeltaSetIndexMap DEFINE_SIZE_UNION (1, format); }; + +struct VarStoreInstancer +{ + VarStoreInstancer (const VariationStore &varStore, + const DeltaSetIndexMap &varIdxMap, + hb_array_t<int> coords) : + varStore (varStore), varIdxMap (varIdxMap), coords (coords) {} + + operator bool () const { return bool (coords); } + + float operator() (uint32_t varIdx, unsigned short offset = 0) const + { return varStore.get_delta (varIdxMap.map (VarIdx::add (varIdx, offset)), coords); } + + const VariationStore &varStore; + const DeltaSetIndexMap &varIdxMap; + hb_array_t<int> coords; +}; + + } /* namespace OT */ diff --git a/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh index af23862870..c1d57a002a 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh @@ -59,7 +59,7 @@ struct InstanceRecord const hb_hashmap_t<hb_tag_t, float> *axes_location = c->plan->user_axes_location; for (unsigned i = 0 ; i < axis_count; i++) { - unsigned *axis_tag; + uint32_t *axis_tag; // only keep instances whose coordinates == pinned axis location if (!c->plan->axes_old_index_tag_map->has (i, &axis_tag)) continue; @@ -337,13 +337,13 @@ struct fvar { const InstanceRecord *instance = get_instance (i); - if (hb_any (+ hb_zip (instance->get_coordinates (axisCount), hb_range ((unsigned)axisCount)) - | hb_filter (pinned_axes, hb_second) - | hb_map ([&] (const hb_pair_t<const F16DOT16&, unsigned>& _) + if (hb_any (+ hb_enumerate (instance->get_coordinates (axisCount)) + | hb_filter (pinned_axes, hb_first) + | hb_map ([&] (const hb_pair_t<unsigned, const F16DOT16&>& _) { - hb_tag_t axis_tag = pinned_axes.get (_.second); + hb_tag_t axis_tag = pinned_axes.get (_.first); float location = user_axes_location->get (axis_tag); - if (fabs ((double)location - (double)_.first.to_float ()) > 0.001) return true; + if (fabs ((double)location - (double)_.second.to_float ()) > 0.001) return true; return false; }) )) diff --git a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh index bf1039d1d6..e02063ca43 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh @@ -56,12 +56,11 @@ struct contour_point_vector_t : hb_vector_t<contour_point_t> void extend (const hb_array_t<contour_point_t> &a) { unsigned int old_len = length; - if (unlikely (!resize (old_len + a.length))) + if (unlikely (!resize (old_len + a.length, false))) return; auto arrayZ = this->arrayZ + old_len; unsigned count = a.length; - for (unsigned int i = 0; i < count; i++) - arrayZ[i] = a.arrayZ[i]; + hb_memcpy (arrayZ, a.arrayZ, count * sizeof (arrayZ[0])); } void transform (const float (&matrix)[4]) @@ -231,8 +230,8 @@ struct GlyphVariationData { return (index < var_data->tupleVarCount.get_count ()) && var_data_bytes.check_range (current_tuple, TupleVariationHeader::min_size) && - var_data_bytes.check_range (current_tuple, hb_max (current_tuple->get_data_size (), current_tuple->get_size (axis_count))) && - current_tuple->get_size (axis_count); + var_data_bytes.check_range (current_tuple, hb_max (current_tuple->get_data_size (), + current_tuple->get_size (axis_count))); } bool move_to_next () @@ -281,42 +280,42 @@ struct GlyphVariationData if (unlikely (p + 1 > end)) return false; - uint16_t count = *p++; + unsigned count = *p++; if (count & POINTS_ARE_WORDS) { if (unlikely (p + 1 > end)) return false; count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++; } - if (unlikely (!points.resize (count))) return false; + if (unlikely (!points.resize (count, false))) return false; - unsigned int n = 0; - uint16_t i = 0; + unsigned n = 0; + unsigned i = 0; while (i < count) { if (unlikely (p + 1 > end)) return false; - uint16_t j; - uint8_t control = *p++; - uint16_t run_count = (control & POINT_RUN_COUNT_MASK) + 1; + unsigned control = *p++; + unsigned run_count = (control & POINT_RUN_COUNT_MASK) + 1; + if (unlikely (i + run_count > count)) return false; + unsigned j; if (control & POINTS_ARE_WORDS) { - for (j = 0; j < run_count && i < count; j++, i++) + if (unlikely (p + run_count * HBUINT16::static_size > end)) return false; + for (j = 0; j < run_count; j++, i++) { - if (unlikely (p + HBUINT16::static_size > end)) return false; n += *(const HBUINT16 *)p; - points[i] = n; + points.arrayZ[i] = n; p += HBUINT16::static_size; } } else { - for (j = 0; j < run_count && i < count; j++, i++) + if (unlikely (p + run_count > end)) return false; + for (j = 0; j < run_count; j++, i++) { - if (unlikely (p + 1 > end)) return false; n += *p++; - points[i] = n; + points.arrayZ[i] = n; } } - if (j < run_count) return false; } return true; } @@ -332,32 +331,37 @@ struct GlyphVariationData DELTA_RUN_COUNT_MASK = 0x3F }; - unsigned int i = 0; - unsigned int count = deltas.length; + unsigned i = 0; + unsigned count = deltas.length; while (i < count) { if (unlikely (p + 1 > end)) return false; - uint8_t control = *p++; - unsigned int run_count = (control & DELTA_RUN_COUNT_MASK) + 1; - unsigned int j; + unsigned control = *p++; + unsigned run_count = (control & DELTA_RUN_COUNT_MASK) + 1; + if (unlikely (i + run_count > count)) return false; + unsigned j; if (control & DELTAS_ARE_ZERO) - for (j = 0; j < run_count && i < count; j++, i++) - deltas[i] = 0; + { + for (j = 0; j < run_count; j++, i++) + deltas.arrayZ[i] = 0; + } else if (control & DELTAS_ARE_WORDS) - for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (p + run_count * HBUINT16::static_size > end)) return false; + for (j = 0; j < run_count; j++, i++) { - if (unlikely (p + HBUINT16::static_size > end)) return false; - deltas[i] = *(const HBINT16 *) p; + deltas.arrayZ[i] = * (const HBINT16 *) p; p += HBUINT16::static_size; } + } else - for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (p + run_count > end)) return false; + for (j = 0; j < run_count; j++, i++) { - if (unlikely (p + 1 > end)) return false; - deltas[i] = *(const HBINT8 *) p++; + deltas.arrayZ[i] = * (const HBINT8 *) p++; } - if (j < run_count) - return false; + } } return true; } @@ -450,7 +454,7 @@ struct gvar F2DOT14 *tuples = c->serializer->allocate_size<F2DOT14> (shared_tuple_size); if (!tuples) return_trace (false); out->sharedTuples = (char *) tuples - (char *) out; - memcpy (tuples, this+sharedTuples, shared_tuple_size); + hb_memcpy (tuples, this+sharedTuples, shared_tuple_size); } char *subset_data = c->serializer->allocate_size<char> (subset_data_size); @@ -473,7 +477,7 @@ struct gvar ((HBUINT16 *) subset_offsets)[gid] = glyph_offset / 2; if (var_data_bytes.length > 0) - memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length); + hb_memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length); subset_data += var_data_bytes.length; glyph_offset += var_data_bytes.length; } @@ -501,6 +505,7 @@ struct gvar unsigned get_offset (unsigned i) const { if (unlikely (i > glyphCount)) return 0; + _hb_compiler_memory_r_barrier (); return is_long_offset () ? get_long_offset_array ()[i] : get_short_offset_array ()[i] * 2; } @@ -521,11 +526,11 @@ struct gvar unsigned int target, unsigned int prev, unsigned int next, float contour_point_t::*m) { - float target_val = points[target].*m; - float prev_val = points[prev].*m; - float next_val = points[next].*m; - float prev_delta = deltas[prev].*m; - float next_delta = deltas[next].*m; + float target_val = points.arrayZ[target].*m; + float prev_val = points.arrayZ[prev].*m; + float next_val = points.arrayZ[next].*m; + float prev_delta = deltas.arrayZ[prev].*m; + float next_delta = deltas.arrayZ[next].*m; if (prev_val == next_val) return (prev_delta == next_delta) ? prev_delta : 0.f; @@ -543,10 +548,11 @@ struct gvar { return (i >= end) ? start : (i + 1); } public: - bool apply_deltas_to_points (hb_codepoint_t glyph, hb_font_t *font, + bool apply_deltas_to_points (hb_codepoint_t glyph, + hb_array_t<int> coords, const hb_array_t<contour_point_t> points) const { - if (!font->num_coords) return true; + if (!coords) return true; if (unlikely (glyph >= table->glyphCount)) return true; @@ -559,20 +565,20 @@ struct gvar return true; /* so isn't applied at all */ /* Save original points for inferred delta calculation */ - contour_point_vector_t orig_points; - if (unlikely (!orig_points.resize (points.length))) return false; - for (unsigned int i = 0; i < orig_points.length; i++) - orig_points.arrayZ[i] = points.arrayZ[i]; + contour_point_vector_t orig_points_vec; + orig_points_vec.extend (points); + if (unlikely (orig_points_vec.in_error ())) return false; + auto orig_points = orig_points_vec.as_array (); - contour_point_vector_t deltas; /* flag is used to indicate referenced point */ - if (unlikely (!deltas.resize (points.length))) return false; + contour_point_vector_t deltas_vec; /* flag is used to indicate referenced point */ + if (unlikely (!deltas_vec.resize (points.length, false))) return false; + auto deltas = deltas_vec.as_array (); hb_vector_t<unsigned> end_points; for (unsigned i = 0; i < points.length; ++i) - if (points[i].is_end_point) + if (points.arrayZ[i].is_end_point) end_points.push (i); - auto coords = hb_array (font->coords, font->num_coords); unsigned num_coords = table->axisCount; hb_array_t<const F2DOT14> shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount); @@ -598,70 +604,89 @@ struct gvar bool apply_to_all = (indices.length == 0); unsigned int num_deltas = apply_to_all ? points.length : indices.length; - if (unlikely (!x_deltas.resize (num_deltas))) return false; + if (unlikely (!x_deltas.resize (num_deltas, false))) return false; if (unlikely (!GlyphVariationData::unpack_deltas (p, x_deltas, end))) return false; - if (unlikely (!y_deltas.resize (num_deltas))) return false; + if (unlikely (!y_deltas.resize (num_deltas, false))) return false; if (unlikely (!GlyphVariationData::unpack_deltas (p, y_deltas, end))) return false; - for (unsigned int i = 0; i < deltas.length; i++) - deltas[i].init (); - for (unsigned int i = 0; i < num_deltas; i++) - { - unsigned int pt_index = apply_to_all ? i : indices[i]; - if (unlikely (pt_index >= deltas.length)) continue; - deltas.arrayZ[pt_index].flag = 1; /* this point is referenced, i.e., explicit deltas specified */ - deltas.arrayZ[pt_index].x += x_deltas.arrayZ[i] * scalar; - deltas.arrayZ[pt_index].y += y_deltas.arrayZ[i] * scalar; - } + hb_memset (deltas.arrayZ, 0, deltas.get_size ()); + + unsigned ref_points = 0; + if (scalar != 1.0f) + for (unsigned int i = 0; i < num_deltas; i++) + { + unsigned int pt_index = apply_to_all ? i : indices[i]; + if (unlikely (pt_index >= deltas.length)) continue; + auto &delta = deltas.arrayZ[pt_index]; + ref_points += !delta.flag; + delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */ + delta.x += x_deltas.arrayZ[i] * scalar; + delta.y += y_deltas.arrayZ[i] * scalar; + } + else + for (unsigned int i = 0; i < num_deltas; i++) + { + unsigned int pt_index = apply_to_all ? i : indices[i]; + if (unlikely (pt_index >= deltas.length)) continue; + auto &delta = deltas.arrayZ[pt_index]; + ref_points += !delta.flag; + delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */ + delta.x += x_deltas.arrayZ[i]; + delta.y += y_deltas.arrayZ[i]; + } /* infer deltas for unreferenced points */ - unsigned start_point = 0; - for (unsigned c = 0; c < end_points.length; c++) + if (ref_points && ref_points < orig_points.length) { - unsigned end_point = end_points[c]; + unsigned start_point = 0; + for (unsigned c = 0; c < end_points.length; c++) + { + unsigned end_point = end_points.arrayZ[c]; - /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */ - unsigned unref_count = 0; - for (unsigned i = start_point; i <= end_point; i++) - if (!deltas[i].flag) unref_count++; + /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */ + unsigned unref_count = 0; + for (unsigned i = start_point; i < end_point + 1; i++) + unref_count += deltas.arrayZ[i].flag; + unref_count = (end_point - start_point + 1) - unref_count; - unsigned j = start_point; - if (unref_count == 0 || unref_count > end_point - start_point) - goto no_more_gaps; + unsigned j = start_point; + if (unref_count == 0 || unref_count > end_point - start_point) + goto no_more_gaps; - for (;;) - { - /* Locate the next gap of unreferenced points between two referenced points prev and next. - * Note that a gap may wrap around at left (start_point) and/or at right (end_point). - */ - unsigned int prev, next, i; - for (;;) - { - i = j; - j = next_index (i, start_point, end_point); - if (deltas[i].flag && !deltas[j].flag) break; - } - prev = j = i; - for (;;) - { - i = j; - j = next_index (i, start_point, end_point); - if (!deltas[i].flag && deltas[j].flag) break; - } - next = j; - /* Infer deltas for all unref points in the gap between prev and next */ - i = prev; for (;;) { - i = next_index (i, start_point, end_point); - if (i == next) break; - deltas[i].x = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next, &contour_point_t::x); - deltas[i].y = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next, &contour_point_t::y); - if (--unref_count == 0) goto no_more_gaps; + /* Locate the next gap of unreferenced points between two referenced points prev and next. + * Note that a gap may wrap around at left (start_point) and/or at right (end_point). + */ + unsigned int prev, next, i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (deltas.arrayZ[i].flag && !deltas.arrayZ[j].flag) break; + } + prev = j = i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (!deltas.arrayZ[i].flag && deltas.arrayZ[j].flag) break; + } + next = j; + /* Infer deltas for all unref points in the gap between prev and next */ + i = prev; + for (;;) + { + i = next_index (i, start_point, end_point); + if (i == next) break; + deltas.arrayZ[i].x = infer_delta (orig_points, deltas, i, prev, next, &contour_point_t::x); + deltas.arrayZ[i].y = infer_delta (orig_points, deltas, i, prev, next, &contour_point_t::y); + if (--unref_count == 0) goto no_more_gaps; + } } + no_more_gaps: + start_point = end_point + 1; } - no_more_gaps: - start_point = end_point + 1; } /* apply specified / inferred deltas to points */ diff --git a/thirdparty/harfbuzz/src/hb-pool.hh b/thirdparty/harfbuzz/src/hb-pool.hh index dcf8f6627d..e4bb64f627 100644 --- a/thirdparty/harfbuzz/src/hb-pool.hh +++ b/thirdparty/harfbuzz/src/hb-pool.hh @@ -35,15 +35,13 @@ template <typename T, unsigned ChunkLen = 16> struct hb_pool_t { hb_pool_t () : next (nullptr) {} - ~hb_pool_t () { fini (); } - - void fini () + ~hb_pool_t () { next = nullptr; - for (chunk_t *_ : chunks) hb_free (_); - - chunks.fini (); + + hb_iter (chunks) + | hb_apply (hb_free) + ; } T* alloc () @@ -60,7 +58,7 @@ struct hb_pool_t T* obj = next; next = * ((T**) next); - memset (obj, 0, sizeof (T)); + hb_memset (obj, 0, sizeof (T)); return obj; } diff --git a/thirdparty/harfbuzz/src/hb-priority-queue.hh b/thirdparty/harfbuzz/src/hb-priority-queue.hh index ffb86e30ae..ac76b7d955 100644 --- a/thirdparty/harfbuzz/src/hb-priority-queue.hh +++ b/thirdparty/harfbuzz/src/hb-priority-queue.hh @@ -63,7 +63,9 @@ struct hb_priority_queue_t heap.arrayZ[0] = heap.arrayZ[heap.length - 1]; heap.shrink (heap.length - 1); - bubble_down (0); + + if (!is_empty ()) + bubble_down (0); return result; } @@ -100,7 +102,7 @@ struct hb_priority_queue_t void bubble_down (unsigned index) { - assert (index <= heap.length); + assert (index < heap.length); unsigned left = left_child (index); unsigned right = right_child (index); @@ -112,7 +114,7 @@ struct hb_priority_queue_t bool has_right = right < heap.length; if (heap.arrayZ[index].first <= heap.arrayZ[left].first - && (!has_right || heap[index].first <= heap.arrayZ[right].first)) + && (!has_right || heap.arrayZ[index].first <= heap.arrayZ[right].first)) return; if (!has_right || heap.arrayZ[left].first < heap.arrayZ[right].first) @@ -128,7 +130,7 @@ struct hb_priority_queue_t void bubble_up (unsigned index) { - assert (index <= heap.length); + assert (index < heap.length); if (index == 0) return; @@ -142,8 +144,8 @@ struct hb_priority_queue_t void swap (unsigned a, unsigned b) { - assert (a <= heap.length); - assert (b <= heap.length); + assert (a < heap.length); + assert (b < heap.length); hb_swap (heap.arrayZ[a], heap.arrayZ[b]); } }; diff --git a/thirdparty/harfbuzz/src/hb-repacker.hh b/thirdparty/harfbuzz/src/hb-repacker.hh index c97ce6cc2c..7a3143cec3 100644 --- a/thirdparty/harfbuzz/src/hb-repacker.hh +++ b/thirdparty/harfbuzz/src/hb-repacker.hh @@ -209,7 +209,7 @@ bool _try_isolating_subgraphs (const hb_vector_t<graph::overflow_record_t>& over // Only move at most half of the roots in a space at a time. unsigned extra = roots_to_isolate.get_population () - maximum_to_move; while (extra--) { - unsigned root = HB_SET_VALUE_INVALID; + uint32_t root = HB_SET_VALUE_INVALID; roots_to_isolate.previous (&root); roots_to_isolate.del (root); } @@ -283,6 +283,11 @@ hb_resolve_graph_overflows (hb_tag_t table_tag, graph_t& sorted_graph /* IN/OUT */) { sorted_graph.sort_shortest_distance (); + if (sorted_graph.in_error ()) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Sorted graph in error state after initial sort."); + return false; + } bool will_overflow = graph::will_overflow (sorted_graph); if (!will_overflow) @@ -376,6 +381,26 @@ hb_resolve_overflows (const T& packed, unsigned max_rounds = 20, bool recalculate_extensions = false) { graph_t sorted_graph (packed); + if (sorted_graph.in_error ()) + { + // Invalid graph definition. + return nullptr; + } + + if (!sorted_graph.is_fully_connected ()) + { + sorted_graph.print_orphaned_nodes (); + return nullptr; + } + + if (sorted_graph.in_error ()) + { + // Allocations failed somewhere + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Graph is in error, likely due to a memory allocation error."); + return nullptr; + } + if (!hb_resolve_graph_overflows (table_tag, max_rounds, recalculate_extensions, sorted_graph)) return nullptr; diff --git a/thirdparty/harfbuzz/src/hb-sanitize.hh b/thirdparty/harfbuzz/src/hb-sanitize.hh index 65c2772201..bd3250e580 100644 --- a/thirdparty/harfbuzz/src/hb-sanitize.hh +++ b/thirdparty/harfbuzz/src/hb-sanitize.hh @@ -198,10 +198,11 @@ struct hb_sanitize_context_t : void start_processing () { reset_object (); - if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR))) + unsigned m; + if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR, &m))) this->max_ops = HB_SANITIZE_MAX_OPS_MAX; else - this->max_ops = hb_clamp ((unsigned) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR, + this->max_ops = hb_clamp (m, (unsigned) HB_SANITIZE_MAX_OPS_MIN, (unsigned) HB_SANITIZE_MAX_OPS_MAX); this->edit_count = 0; @@ -252,8 +253,9 @@ struct hb_sanitize_context_t : unsigned int a, unsigned int b) const { - return !hb_unsigned_mul_overflows (a, b) && - this->check_range (base, a * b); + unsigned m; + return !hb_unsigned_mul_overflows (a, b, &m) && + this->check_range (base, m); } template <typename T> @@ -262,8 +264,9 @@ struct hb_sanitize_context_t : unsigned int b, unsigned int c) const { - return !hb_unsigned_mul_overflows (a, b) && - this->check_range (base, a * b, c); + unsigned m; + return !hb_unsigned_mul_overflows (a, b, &m) && + this->check_range (base, m, c); } template <typename T> diff --git a/thirdparty/harfbuzz/src/hb-serialize.hh b/thirdparty/harfbuzz/src/hb-serialize.hh index f47cde5eb5..d5573281f1 100644 --- a/thirdparty/harfbuzz/src/hb-serialize.hh +++ b/thirdparty/harfbuzz/src/hb-serialize.hh @@ -194,7 +194,6 @@ struct hb_serialize_context_t current = current->next; _->fini (); } - object_pool.fini (); } bool in_error () const { return bool (errors); } @@ -224,6 +223,7 @@ struct hb_serialize_context_t this->errors = HB_SERIALIZE_ERROR_NONE; this->head = this->start; this->tail = this->end; + this->zerocopy = nullptr; this->debug_depth = 0; fini (); @@ -326,7 +326,8 @@ struct hb_serialize_context_t if (unlikely (in_error() && !only_overflow ())) return; current = current->next; - revert (obj->head, obj->tail); + revert (zerocopy ? zerocopy : obj->head, obj->tail); + zerocopy = nullptr; obj->fini (); object_pool.release (obj); } @@ -344,8 +345,11 @@ struct hb_serialize_context_t current = current->next; obj->tail = head; obj->next = nullptr; + assert (obj->head <= obj->tail); unsigned len = obj->tail - obj->head; - head = obj->head; /* Rewind head. */ + head = zerocopy ? zerocopy : obj->head; /* Rewind head. */ + bool was_zerocopy = zerocopy; + zerocopy = nullptr; if (!len) { @@ -355,9 +359,11 @@ struct hb_serialize_context_t } objidx_t objidx; + uint32_t hash = 0; if (share) { - objidx = packed_map.get (obj); + hash = hb_hash (obj); + objidx = packed_map.get_with_hash (obj, hash); if (objidx) { merge_virtual_links (obj, objidx); @@ -367,7 +373,10 @@ struct hb_serialize_context_t } tail -= len; - memmove (tail, obj->head, len); + if (was_zerocopy) + assert (tail == obj->head); + else + memmove (tail, obj->head, len); obj->head = tail; obj->tail = tail + len; @@ -385,7 +394,7 @@ struct hb_serialize_context_t objidx = packed.length - 1; - if (share) packed_map.set (obj, objidx); + if (share) packed_map.set_with_hash (obj, hash, objidx); propagate_error (packed_map); return objidx; @@ -569,8 +578,26 @@ struct hb_serialize_context_t return !bool ((errors = (errors | err_type))); } + bool start_zerocopy (size_t size) + { + if (unlikely (in_error ())) return false; + + if (unlikely (size > INT_MAX || this->tail - this->head < ptrdiff_t (size))) + { + err (HB_SERIALIZE_ERROR_OUT_OF_ROOM); + return false; + } + + assert (!this->zerocopy); + this->zerocopy = this->head; + + assert (this->current->head == this->head); + this->current->head = this->current->tail = this->head = this->tail - size; + return true; + } + template <typename Type> - Type *allocate_size (size_t size) + Type *allocate_size (size_t size, bool clear = true) { if (unlikely (in_error ())) return nullptr; @@ -579,7 +606,8 @@ struct hb_serialize_context_t err (HB_SERIALIZE_ERROR_OUT_OF_ROOM); return nullptr; } - hb_memset (this->head, 0, size); + if (clear) + hb_memset (this->head, 0, size); char *ret = this->head; this->head += size; return reinterpret_cast<Type *> (ret); @@ -593,9 +621,9 @@ struct hb_serialize_context_t Type *embed (const Type *obj) { unsigned int size = obj->get_size (); - Type *ret = this->allocate_size<Type> (size); + Type *ret = this->allocate_size<Type> (size, false); if (unlikely (!ret)) return nullptr; - memcpy (ret, obj, size); + hb_memcpy (ret, obj, size); return ret; } template <typename Type> @@ -616,7 +644,7 @@ struct hb_serialize_context_t } /* Like embed, but active: calls obj.operator=() or obj.copy() to transfer data - * instead of memcpy(). */ + * instead of hb_memcpy(). */ template <typename Type, typename ...Ts> Type *copy (const Type &src, Ts&&... ds) { return _copy (src, hb_prioritize, std::forward<Ts> (ds)...); } @@ -634,7 +662,7 @@ struct hb_serialize_context_t hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; } template <typename Type> - Type *extend_size (Type *obj, size_t size) + Type *extend_size (Type *obj, size_t size, bool clear = true) { if (unlikely (in_error ())) return nullptr; @@ -642,12 +670,12 @@ struct hb_serialize_context_t assert ((char *) obj <= this->head); assert ((size_t) (this->head - (char *) obj) <= size); if (unlikely (((char *) obj + size < (char *) obj) || - !this->allocate_size<Type> (((char *) obj) + size - this->head))) return nullptr; + !this->allocate_size<Type> (((char *) obj) + size - this->head, clear))) return nullptr; return reinterpret_cast<Type *> (obj); } template <typename Type> - Type *extend_size (Type &obj, size_t size) - { return extend_size (std::addressof (obj), size); } + Type *extend_size (Type &obj, size_t size, bool clear = true) + { return extend_size (std::addressof (obj), size, clear); } template <typename Type> Type *extend_min (Type *obj) { return extend_size (obj, obj->min_size); } @@ -676,8 +704,8 @@ struct hb_serialize_context_t char *p = (char *) hb_malloc (len); if (unlikely (!p)) return hb_bytes_t (); - memcpy (p, this->start, this->head - this->start); - memcpy (p + (this->head - this->start), this->tail, this->end - this->tail); + hb_memcpy (p, this->start, this->head - this->start); + hb_memcpy (p + (this->head - this->start), this->tail, this->end - this->tail); return hb_bytes_t (p, len); } template <typename Type> @@ -704,7 +732,7 @@ struct hb_serialize_context_t } public: - char *start, *head, *tail, *end; + char *start, *head, *tail, *end, *zerocopy; unsigned int debug_depth; hb_serialize_error_t errors; diff --git a/thirdparty/harfbuzz/src/hb-set-digest.hh b/thirdparty/harfbuzz/src/hb-set-digest.hh index fab36216e4..e8409111f2 100644 --- a/thirdparty/harfbuzz/src/hb-set-digest.hh +++ b/thirdparty/harfbuzz/src/hb-set-digest.hh @@ -28,6 +28,7 @@ #define HB_SET_DIGEST_HH #include "hb.hh" +#include "hb-machinery.hh" /* * The set-digests here implement various "filters" that support @@ -75,6 +76,8 @@ struct hb_set_digest_bits_pattern_t void init () { mask = 0; } + void add (const hb_set_digest_bits_pattern_t &o) { mask |= o.mask; } + void add (hb_codepoint_t g) { mask |= mask_for (g); } bool add_range (hb_codepoint_t a, hb_codepoint_t b) @@ -95,7 +98,7 @@ struct hb_set_digest_bits_pattern_t for (unsigned int i = 0; i < count; i++) { add (*array); - array = (const T *) (stride + (const char *) array); + array = &StructAtOffsetUnaligned<T> ((const void *) array, stride); } } template <typename T> @@ -103,16 +106,15 @@ struct hb_set_digest_bits_pattern_t template <typename T> bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) { - for (unsigned int i = 0; i < count; i++) - { - add (*array); - array = (const T *) (stride + (const char *) array); - } + add_array (array, count, stride); return true; } template <typename T> bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); } + bool may_have (const hb_set_digest_bits_pattern_t &o) const + { return mask & o.mask; } + bool may_have (hb_codepoint_t g) const { return mask & mask_for (g); } @@ -132,6 +134,12 @@ struct hb_set_digest_combiner_t tail.init (); } + void add (const hb_set_digest_combiner_t &o) + { + head.add (o.head); + tail.add (o.tail); + } + void add (hb_codepoint_t g) { head.add (g); @@ -140,9 +148,8 @@ struct hb_set_digest_combiner_t bool add_range (hb_codepoint_t a, hb_codepoint_t b) { - head.add_range (a, b); - tail.add_range (a, b); - return true; + return head.add_range (a, b) && + tail.add_range (a, b); } template <typename T> void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) @@ -155,13 +162,17 @@ struct hb_set_digest_combiner_t template <typename T> bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) { - head.add_sorted_array (array, count, stride); - tail.add_sorted_array (array, count, stride); - return true; + return head.add_sorted_array (array, count, stride) && + tail.add_sorted_array (array, count, stride); } template <typename T> bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); } + bool may_have (const hb_set_digest_combiner_t &o) const + { + return head.may_have (o.head) && tail.may_have (o.tail); + } + bool may_have (hb_codepoint_t g) const { return head.may_have (g) && tail.may_have (g); diff --git a/thirdparty/harfbuzz/src/hb-set.hh b/thirdparty/harfbuzz/src/hb-set.hh index 5d5576cb9e..f958a081fb 100644 --- a/thirdparty/harfbuzz/src/hb-set.hh +++ b/thirdparty/harfbuzz/src/hb-set.hh @@ -105,10 +105,8 @@ struct hb_sparseset_t bool get (hb_codepoint_t g) const { return s.get (g); } /* Has interface. */ - static constexpr bool SENTINEL = false; - typedef bool value_t; - value_t operator [] (hb_codepoint_t k) const { return get (k); } - bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + bool operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } /* Predicate. */ bool operator () (hb_codepoint_t k) const { return has (k); } diff --git a/thirdparty/harfbuzz/src/hb-shape-plan.cc b/thirdparty/harfbuzz/src/hb-shape-plan.cc index 9e513c0458..cabcbd4e72 100644 --- a/thirdparty/harfbuzz/src/hb-shape-plan.cc +++ b/thirdparty/harfbuzz/src/hb-shape-plan.cc @@ -31,6 +31,8 @@ #include "hb-buffer.hh" +#ifndef HB_NO_SHAPER + /** * SECTION:hb-shape-plan * @title: hb-shape-plan @@ -74,7 +76,7 @@ hb_shape_plan_key_t::init (bool copy, this->user_features = copy ? features : user_features; if (copy && num_user_features) { - memcpy (features, user_features, num_user_features * sizeof (hb_feature_t)); + hb_memcpy (features, user_features, num_user_features * sizeof (hb_feature_t)); /* Make start/end uniform to easier catch bugs. */ for (unsigned int i = 0; i < num_user_features; i++) { @@ -574,3 +576,6 @@ retry: return hb_shape_plan_reference (shape_plan); } + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-shape.cc b/thirdparty/harfbuzz/src/hb-shape.cc index 547d0afc47..7b5bf2c5ef 100644 --- a/thirdparty/harfbuzz/src/hb-shape.cc +++ b/thirdparty/harfbuzz/src/hb-shape.cc @@ -35,6 +35,8 @@ #include "hb-machinery.hh" +#ifndef HB_NO_SHAPER + /** * SECTION:hb-shape * @title: hb-shape @@ -192,3 +194,6 @@ hb_shape (hb_font_t *font, { hb_shape_full (font, buffer, features, num_features, nullptr); } + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-shaper.cc b/thirdparty/harfbuzz/src/hb-shaper.cc index a900ac6991..c4885cda94 100644 --- a/thirdparty/harfbuzz/src/hb-shaper.cc +++ b/thirdparty/harfbuzz/src/hb-shaper.cc @@ -53,7 +53,7 @@ static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<hb_shaper_entry_t, if (unlikely (!shapers)) return nullptr; - memcpy (shapers, _hb_all_shapers, sizeof (_hb_all_shapers)); + hb_memcpy (shapers, _hb_all_shapers, sizeof (_hb_all_shapers)); /* Reorder shaper list to prefer requested shapers. */ unsigned int i = 0; diff --git a/thirdparty/harfbuzz/src/hb-static.cc b/thirdparty/harfbuzz/src/hb-static.cc index 5d4c7cda1b..eac50754c2 100644 --- a/thirdparty/harfbuzz/src/hb-static.cc +++ b/thirdparty/harfbuzz/src/hb-static.cc @@ -33,6 +33,7 @@ #include "hb-aat-layout-feat-table.hh" #include "hb-ot-layout-common.hh" #include "hb-ot-cmap-table.hh" +#include "hb-ot-color-colr-table.hh" #include "hb-ot-glyf-table.hh" #include "hb-ot-head-table.hh" #include "hb-ot-maxp-table.hh" @@ -47,6 +48,7 @@ DEFINE_NULL_NAMESPACE_BYTES (OT, Index) = {0xFF,0xFF}; DEFINE_NULL_NAMESPACE_BYTES (OT, VarIdx) = {0xFF,0xFF,0xFF,0xFF}; DEFINE_NULL_NAMESPACE_BYTES (OT, LangSys) = {0x00,0x00, 0xFF,0xFF, 0x00,0x00}; DEFINE_NULL_NAMESPACE_BYTES (OT, RangeRecord) = {0x01}; +DEFINE_NULL_NAMESPACE_BYTES (OT, ClipRecord) = {0x01}; DEFINE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup) = {0x00,0x00,0x00,0x01, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00}; DEFINE_NULL_NAMESPACE_BYTES (AAT, SettingName) = {0xFF,0xFF, 0xFF,0xFF}; DEFINE_NULL_NAMESPACE_BYTES (AAT, Lookup) = {0xFF,0xFF}; diff --git a/thirdparty/harfbuzz/src/hb-subset-accelerator.hh b/thirdparty/harfbuzz/src/hb-subset-accelerator.hh index 34bd0534fb..63ae6d77e2 100644 --- a/thirdparty/harfbuzz/src/hb-subset-accelerator.hh +++ b/thirdparty/harfbuzz/src/hb-subset-accelerator.hh @@ -31,21 +31,34 @@ #include "hb.hh" #include "hb-map.hh" +#include "hb-multimap.hh" #include "hb-set.hh" +extern HB_INTERNAL hb_user_data_key_t _hb_subset_accelerator_user_data_key; + +namespace CFF { +struct cff_subset_accelerator_t; +} + +namespace OT { +struct SubtableUnicodesCache; +}; + struct hb_subset_accelerator_t { static hb_user_data_key_t* user_data_key() { - static hb_user_data_key_t key; - return &key; + return &_hb_subset_accelerator_user_data_key; } static hb_subset_accelerator_t* create(const hb_map_t& unicode_to_gid_, - const hb_set_t& unicodes_) { + const hb_multimap_t gid_to_unicodes_, + const hb_set_t& unicodes_, + bool has_seac_) { hb_subset_accelerator_t* accel = (hb_subset_accelerator_t*) hb_malloc (sizeof(hb_subset_accelerator_t)); - new (accel) hb_subset_accelerator_t (unicode_to_gid_, unicodes_); + new (accel) hb_subset_accelerator_t (unicode_to_gid_, gid_to_unicodes_, unicodes_); + accel->has_seac = has_seac_; return accel; } @@ -53,22 +66,54 @@ struct hb_subset_accelerator_t if (!value) return; hb_subset_accelerator_t* accel = (hb_subset_accelerator_t*) value; + + if (accel->cff_accelerator && accel->destroy_cff_accelerator) + accel->destroy_cff_accelerator ((void*) accel->cff_accelerator); + + if (accel->cmap_cache && accel->destroy_cmap_cache) + accel->destroy_cmap_cache ((void*) accel->cmap_cache); + accel->~hb_subset_accelerator_t (); hb_free (accel); } - hb_subset_accelerator_t(const hb_map_t& unicode_to_gid_, + hb_subset_accelerator_t (const hb_map_t& unicode_to_gid_, + const hb_multimap_t& gid_to_unicodes_, const hb_set_t& unicodes_) - : unicode_to_gid(unicode_to_gid_), unicodes(unicodes_) {} + : unicode_to_gid(unicode_to_gid_), gid_to_unicodes (gid_to_unicodes_), unicodes(unicodes_), + cmap_cache(nullptr), destroy_cmap_cache(nullptr), + has_seac(false), cff_accelerator(nullptr), destroy_cff_accelerator(nullptr) + { sanitized_table_cache_lock.init (); } + + ~hb_subset_accelerator_t () + { sanitized_table_cache_lock.fini (); } + + // Generic + + mutable hb_mutex_t sanitized_table_cache_lock; + mutable hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_blob_t>> sanitized_table_cache; const hb_map_t unicode_to_gid; + const hb_multimap_t gid_to_unicodes; const hb_set_t unicodes; + + // cmap + const OT::SubtableUnicodesCache* cmap_cache; + hb_destroy_func_t destroy_cmap_cache; + + // CFF + bool has_seac; + const CFF::cff_subset_accelerator_t* cff_accelerator; + hb_destroy_func_t destroy_cff_accelerator; + // TODO(garretrieger): cumulative glyf checksum map - // TODO(garretrieger): sanitized table cache. bool in_error () const { - return unicode_to_gid.in_error() || unicodes.in_error (); + return unicode_to_gid.in_error () || + gid_to_unicodes.in_error () || + unicodes.in_error () || + sanitized_table_cache.in_error (); } }; diff --git a/thirdparty/harfbuzz/src/hb-subset-cff-common.cc b/thirdparty/harfbuzz/src/hb-subset-cff-common.cc index 711b2236d6..6e1b6f713d 100644 --- a/thirdparty/harfbuzz/src/hb-subset-cff-common.cc +++ b/thirdparty/harfbuzz/src/hb-subset-cff-common.cc @@ -66,8 +66,7 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, { /* use hb_set to determine the subset of font dicts */ - hb_set_t *set = hb_set_create (); - if (unlikely (set == &Null (hb_set_t))) return false; + hb_set_t set; hb_codepoint_t prev_fd = CFF_UNDEF_CODE; for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++) { @@ -79,7 +78,7 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, glyph = i; } fd = src.get_fd (glyph); - set->add (fd); + set.add (fd); if (fd != prev_fd) { @@ -90,12 +89,11 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, } } - subset_fd_count = set->get_population (); + subset_fd_count = set.get_population (); if (subset_fd_count == fdCount) { /* all font dicts belong to the subset. no need to subset FDSelect & FDArray */ fdmap.identity (fdCount); - hb_set_destroy (set); } else { @@ -103,9 +101,8 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, fdmap.reset (); hb_codepoint_t fd = CFF_UNDEF_CODE; - while (set->next (&fd)) + while (set.next (&fd)) fdmap.add (fd); - hb_set_destroy (set); if (unlikely (fdmap.get_population () != subset_fd_count)) return false; } diff --git a/thirdparty/harfbuzz/src/hb-subset-cff-common.hh b/thirdparty/harfbuzz/src/hb-subset-cff-common.hh index bb9f27eec1..8bbc017656 100644 --- a/thirdparty/harfbuzz/src/hb-subset-cff-common.hh +++ b/thirdparty/harfbuzz/src/hb-subset-cff-common.hh @@ -38,15 +38,16 @@ namespace CFF { struct str_encoder_t { str_encoder_t (str_buff_t &buff_) - : buff (buff_), error (false) {} + : buff (buff_) {} void reset () { buff.reset (); } void encode_byte (unsigned char b) { - buff.push (b); - if (unlikely (buff.in_error ())) - set_error (); + if (likely ((signed) buff.length < buff.allocated)) + buff.arrayZ[buff.length++] = b; + else + buff.push (b); } void encode_int (int v) @@ -108,27 +109,18 @@ struct str_encoder_t encode_byte (op); } - void copy_str (const hb_ubytes_t &str) + void copy_str (const unsigned char *str, unsigned length) { - unsigned int offset = buff.length; - /* Manually resize buffer since faster. */ - if ((signed) (buff.length + str.length) <= buff.allocated) - buff.length += str.length; - else if (unlikely (!buff.resize (offset + str.length))) - { - set_error (); - return; - } - memcpy (buff.arrayZ + offset, &str[0], str.length); + assert ((signed) (buff.length + length) <= buff.allocated); + hb_memcpy (buff.arrayZ + buff.length, str, length); + buff.length += length; } - bool is_error () const { return error; } + bool in_error () const { return buff.in_error (); } protected: - void set_error () { error = true; } str_buff_t &buff; - bool error; }; struct cff_sub_table_info_t { @@ -187,9 +179,12 @@ struct cff_font_dict_op_serializer_t : op_serializer_t } else { - HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.length); + unsigned char *d = c->allocate_size<unsigned char> (opstr.length); if (unlikely (!d)) return_trace (false); - memcpy (d, &opstr.str[0], opstr.str.length); + /* Faster than hb_memcpy for small strings. */ + for (unsigned i = 0; i < opstr.length; i++) + d[i] = opstr.ptr[i]; + //hb_memcpy (d, opstr.ptr, opstr.length); } return_trace (true); } @@ -239,11 +234,10 @@ struct subr_flattener_t bool flatten (str_buff_vec_t &flat_charstrings) { - if (!flat_charstrings.resize (plan->num_output_glyphs ())) + unsigned count = plan->num_output_glyphs (); + if (!flat_charstrings.resize (count)) return false; - for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) - flat_charstrings[i].init (); - for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) + for (unsigned int i = 0; i < count; i++) { hb_codepoint_t glyph; if (!plan->old_gid_for_new_gid (i, &glyph)) @@ -259,7 +253,7 @@ struct subr_flattener_t ENV env (str, acc, fd); cs_interpreter_t<ENV, OPSET, flatten_param_t> interp (env); flatten_param_t param = { - flat_charstrings[i], + flat_charstrings.arrayZ[i], (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) }; if (unlikely (!interp.interpret (param))) @@ -274,11 +268,9 @@ struct subr_flattener_t struct subr_closures_t { - subr_closures_t (unsigned int fd_count) : valid (false), global_closure (), local_closures () + subr_closures_t (unsigned int fd_count) : global_closure (), local_closures () { - valid = true; - if (!local_closures.resize (fd_count)) - valid = false; + local_closures.resize (fd_count); } void reset () @@ -288,47 +280,38 @@ struct subr_closures_t local_closures[i].clear(); } - bool is_valid () const { return valid; } - bool valid; + bool in_error () const { return local_closures.in_error (); } hb_set_t global_closure; hb_vector_t<hb_set_t> local_closures; }; struct parsed_cs_op_t : op_str_t { - void init (unsigned int subr_num_ = 0) - { - subr_num = subr_num_; - drop_flag = false; - keep_flag = false; - skip_flag = false; - } - - bool for_drop () const { return drop_flag; } - void set_drop () { if (!for_keep ()) drop_flag = true; } + parsed_cs_op_t (unsigned int subr_num_ = 0) : + subr_num (subr_num_) {} - bool for_keep () const { return keep_flag; } - void set_keep () { keep_flag = true; } + bool is_hinting () const { return hinting_flag; } + void set_hinting () { hinting_flag = true; } - bool for_skip () const { return skip_flag; } - void set_skip () { skip_flag = true; } - - unsigned int subr_num; + /* The layout of this struct is designed to fit within the + * padding of op_str_t! */ protected: - bool drop_flag; - bool keep_flag; - bool skip_flag; + bool hinting_flag = false; + + public: + uint16_t subr_num; }; struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t> { - void init () + parsed_cs_str_t () : + parsed (false), + hint_dropped (false), + has_prefix_ (false), + has_calls_ (false) { SUPER::init (); - parsed = false; - hint_dropped = false; - has_prefix_ = false; } void add_op (op_code_t op, const byte_str_ref_t& str_ref) @@ -341,13 +324,12 @@ struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t> { if (!is_parsed ()) { - unsigned int parsed_len = get_count (); - if (likely (parsed_len > 0)) - values[parsed_len-1].set_skip (); + has_calls_ = true; - parsed_cs_op_t val; - val.init (subr_num); - SUPER::add_op (op, str_ref, val); + /* Pop the subroutine number. */ + values.pop (); + + SUPER::add_op (op, str_ref, {subr_num}); } } @@ -377,11 +359,14 @@ struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t> op_code_t prefix_op () const { return prefix_op_; } const number_t &prefix_num () const { return prefix_num_; } + bool has_calls () const { return has_calls_; } + protected: - bool parsed; - bool hint_dropped; - bool vsindex_dropped; - bool has_prefix_; + bool parsed : 1; + bool hint_dropped : 1; + bool vsindex_dropped : 1; + bool has_prefix_ : 1; + bool has_calls_ : 1; op_code_t prefix_op_; number_t prefix_num_; @@ -395,6 +380,59 @@ struct parsed_cs_str_vec_t : hb_vector_t<parsed_cs_str_t> typedef hb_vector_t<parsed_cs_str_t> SUPER; }; +struct cff_subset_accelerator_t +{ + static cff_subset_accelerator_t* create ( + hb_blob_t* original_blob, + const parsed_cs_str_vec_t& parsed_charstrings, + const parsed_cs_str_vec_t& parsed_global_subrs, + const hb_vector_t<parsed_cs_str_vec_t>& parsed_local_subrs) { + cff_subset_accelerator_t* accel = + (cff_subset_accelerator_t*) hb_malloc (sizeof(cff_subset_accelerator_t)); + new (accel) cff_subset_accelerator_t (original_blob, + parsed_charstrings, + parsed_global_subrs, + parsed_local_subrs); + return accel; + } + + static void destroy (void* value) { + if (!value) return; + + cff_subset_accelerator_t* accel = (cff_subset_accelerator_t*) value; + accel->~cff_subset_accelerator_t (); + hb_free (accel); + } + + cff_subset_accelerator_t( + hb_blob_t* original_blob_, + const parsed_cs_str_vec_t& parsed_charstrings_, + const parsed_cs_str_vec_t& parsed_global_subrs_, + const hb_vector_t<parsed_cs_str_vec_t>& parsed_local_subrs_) + { + parsed_charstrings = parsed_charstrings_; + parsed_global_subrs = parsed_global_subrs_; + parsed_local_subrs = parsed_local_subrs_; + + // the parsed charstrings point to memory in the original CFF table so we must hold a reference + // to it to keep the memory valid. + original_blob = hb_blob_reference (original_blob_); + } + + ~cff_subset_accelerator_t() { + hb_blob_destroy (original_blob); + hb_map_destroy (glyph_to_sid_map.get_relaxed ()); + } + + parsed_cs_str_vec_t parsed_charstrings; + parsed_cs_str_vec_t parsed_global_subrs; + hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs; + mutable hb_atomic_ptr_t<hb_map_t> glyph_to_sid_map = nullptr; + + private: + hb_blob_t* original_blob; +}; + struct subr_subset_param_t { subr_subset_param_t (parsed_cs_str_t *parsed_charstring_, @@ -446,7 +484,11 @@ struct subr_subset_param_t if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0))) env.set_error (); else + { + if (!parsed_str->is_parsed ()) + parsed_str->alloc (env.str_ref.total_size () / 2); current_parsed_str = parsed_str; + } } parsed_cs_str_t *current_parsed_str; @@ -506,7 +548,7 @@ struct subr_remaps_t { global_remap.create (&closures.global_closure); for (unsigned int i = 0; i < local_remaps.length; i++) - local_remaps[i].create (&closures.local_closures[i]); + local_remaps.arrayZ[i].create (&closures.local_closures[i]); } subr_remap_t global_remap; @@ -517,7 +559,8 @@ template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typena struct subr_subsetter_t { subr_subsetter_t (ACC &acc_, const hb_subset_plan_t *plan_) - : acc (acc_), plan (plan_), closures(acc_.fdCount), remaps(acc_.fdCount) + : acc (acc_), plan (plan_), closures(acc_.fdCount), + remaps(acc_.fdCount) {} /* Subroutine subsetting with --no-desubroutinize runs in phases: @@ -536,55 +579,96 @@ struct subr_subsetter_t */ bool subset (void) { - parsed_charstrings.resize (plan->num_output_glyphs ()); - parsed_global_subrs.resize (acc.globalSubrs->count); - - if (unlikely (remaps.in_error() - || parsed_charstrings.in_error () - || parsed_global_subrs.in_error ())) { - return false; + unsigned fd_count = acc.fdCount; + const cff_subset_accelerator_t* cff_accelerator = nullptr; + if (plan->accelerator && plan->accelerator->cff_accelerator) { + cff_accelerator = plan->accelerator->cff_accelerator; + fd_count = cff_accelerator->parsed_local_subrs.length; } - if (unlikely (!parsed_local_subrs.resize (acc.fdCount))) return false; + if (cff_accelerator) { + // If we are not dropping hinting then charstrings are not modified so we can + // just use a reference to the cached copies. + cached_charstrings.resize (plan->num_output_glyphs ()); + parsed_global_subrs = &cff_accelerator->parsed_global_subrs; + parsed_local_subrs = &cff_accelerator->parsed_local_subrs; + } else { + parsed_charstrings.resize (plan->num_output_glyphs ()); + parsed_global_subrs_storage.resize (acc.globalSubrs->count); - for (unsigned int i = 0; i < acc.fdCount; i++) - { - parsed_local_subrs[i].resize (acc.privateDicts[i].localSubrs->count); - if (unlikely (parsed_local_subrs[i].in_error ())) return false; + if (unlikely (!parsed_local_subrs_storage.resize (fd_count))) return false; + + for (unsigned int i = 0; i < acc.fdCount; i++) + { + unsigned count = acc.privateDicts[i].localSubrs->count; + parsed_local_subrs_storage[i].resize (count); + if (unlikely (parsed_local_subrs_storage[i].in_error ())) return false; + } + + parsed_global_subrs = &parsed_global_subrs_storage; + parsed_local_subrs = &parsed_local_subrs_storage; } - if (unlikely (!closures.valid)) + + if (unlikely (remaps.in_error() + || cached_charstrings.in_error () + || parsed_charstrings.in_error () + || parsed_global_subrs->in_error () + || closures.in_error ())) { return false; + } /* phase 1 & 2 */ for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { hb_codepoint_t glyph; if (!plan->old_gid_for_new_gid (i, &glyph)) - continue; + continue; + const hb_ubytes_t str = (*acc.charStrings)[glyph]; unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) - return false; + return false; + + if (cff_accelerator) + { + // parsed string already exists in accelerator, copy it and move + // on. + if (cached_charstrings) + cached_charstrings[i] = &cff_accelerator->parsed_charstrings[glyph]; + else + parsed_charstrings[i] = cff_accelerator->parsed_charstrings[glyph]; + + continue; + } ENV env (str, acc, fd); cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp (env); - parsed_charstrings[i].alloc (str.length); + parsed_charstrings[i].alloc (str.length / 2); subr_subset_param_t param (&parsed_charstrings[i], - &parsed_global_subrs, - &parsed_local_subrs[fd], - &closures.global_closure, - &closures.local_closures[fd], - plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + &parsed_global_subrs_storage, + &parsed_local_subrs_storage[fd], + &closures.global_closure, + &closures.local_closures[fd], + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); if (unlikely (!interp.interpret (param))) - return false; + return false; /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[i]); } - if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + // Since parsed strings were loaded from accelerator, we still need + // to compute the subroutine closures which would have normally happened during + // parsing. + if (cff_accelerator && + !closure_subroutines(*parsed_global_subrs, + *parsed_local_subrs)) + return false; + + if ((plan->flags & HB_SUBSET_FLAGS_NO_HINTING && !cff_accelerator) || + plan->inprogress_accelerator) { /* mark hint ops and arguments for drop */ for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) @@ -596,8 +680,8 @@ struct subr_subsetter_t if (unlikely (fd >= acc.fdCount)) return false; subr_subset_param_t param (&parsed_charstrings[i], - &parsed_global_subrs, - &parsed_local_subrs[fd], + &parsed_global_subrs_storage, + &parsed_local_subrs_storage[fd], &closures.global_closure, &closures.local_closures[fd], plan->flags & HB_SUBSET_FLAGS_NO_HINTING); @@ -612,27 +696,14 @@ struct subr_subsetter_t } /* after dropping hints recreate closures of actually used subrs */ - closures.reset (); - for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) - { - hb_codepoint_t glyph; - if (!plan->old_gid_for_new_gid (i, &glyph)) - continue; - unsigned int fd = acc.fdSelect->get_fd (glyph); - if (unlikely (fd >= acc.fdCount)) - return false; - subr_subset_param_t param (&parsed_charstrings[i], - &parsed_global_subrs, - &parsed_local_subrs[fd], - &closures.global_closure, - &closures.local_closures[fd], - plan->flags & HB_SUBSET_FLAGS_NO_HINTING); - collect_subr_refs_in_str (parsed_charstrings[i], param); - } + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING && + !cff_accelerator && + !closure_subroutines(*parsed_global_subrs, *parsed_local_subrs)) return false; } remaps.create (closures); + populate_subset_accelerator (); return true; } @@ -646,13 +717,13 @@ struct subr_subsetter_t if (!plan->old_gid_for_new_gid (i, &glyph)) { /* add an endchar only charstring for a missing glyph if CFF1 */ - if (endchar_op != OpCode_Invalid) buffArray[i].push (endchar_op); + if (endchar_op != OpCode_Invalid) buffArray.arrayZ[i].push (endchar_op); continue; } unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) return false; - if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i]))) + if (unlikely (!encode_str (get_parsed_charstring (i), fd, buffArray.arrayZ[i]))) return false; } return true; @@ -664,26 +735,25 @@ struct subr_subsetter_t if (unlikely (!buffArray.resize (count))) return false; - for (unsigned int old_num = 0; old_num < subrs.length; old_num++) + for (unsigned int new_num = 0; new_num < count; new_num++) { - hb_codepoint_t new_num = remap[old_num]; - if (new_num != CFF_UNDEF_CODE) - { - if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num]))) - return false; - } + hb_codepoint_t old_num = remap.backward (new_num); + assert (old_num != CFF_UNDEF_CODE); + + if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num]))) + return false; } return true; } bool encode_globalsubrs (str_buff_vec_t &buffArray) { - return encode_subrs (parsed_global_subrs, remaps.global_remap, 0, buffArray); + return encode_subrs (*parsed_global_subrs, remaps.global_remap, 0, buffArray); } bool encode_localsubrs (unsigned int fd, str_buff_vec_t &buffArray) const { - return encode_subrs (parsed_local_subrs[fd], remaps.local_remaps[fd], fd, buffArray); + return encode_subrs ((*parsed_local_subrs)[fd], remaps.local_remaps[fd], fd, buffArray); } protected: @@ -712,7 +782,7 @@ struct subr_subsetter_t * then this entire subroutine must be a hint. drop its call. */ if (drop.ends_in_hint) { - str.values[pos].set_drop (); + str.values[pos].set_hinting (); /* if this subr call is at the end of the parent subr, propagate the flag * otherwise reset the flag */ if (!str.at_end (pos)) @@ -720,7 +790,7 @@ struct subr_subsetter_t } else if (drop.all_dropped) { - str.values[pos].set_drop (); + str.values[pos].set_hinting (); } return has_hint; @@ -731,20 +801,22 @@ struct subr_subsetter_t { bool seen_hint = false; - for (unsigned int pos = 0; pos < str.values.length; pos++) + unsigned count = str.values.length; + auto *values = str.values.arrayZ; + for (unsigned int pos = 0; pos < count; pos++) { bool has_hint = false; - switch (str.values[pos].op) + switch (values[pos].op) { case OpCode_callsubr: has_hint = drop_hints_in_subr (str, pos, - *param.parsed_local_subrs, str.values[pos].subr_num, + *param.parsed_local_subrs, values[pos].subr_num, param, drop); break; case OpCode_callgsubr: has_hint = drop_hints_in_subr (str, pos, - *param.parsed_global_subrs, str.values[pos].subr_num, + *param.parsed_global_subrs, values[pos].subr_num, param, drop); break; @@ -758,7 +830,7 @@ struct subr_subsetter_t case OpCode_cntrmask: if (drop.seen_moveto) { - str.values[pos].set_drop (); + values[pos].set_hinting (); break; } HB_FALLTHROUGH; @@ -768,13 +840,13 @@ struct subr_subsetter_t case OpCode_hstem: case OpCode_vstem: has_hint = true; - str.values[pos].set_drop (); + values[pos].set_hinting (); if (str.at_end (pos)) drop.ends_in_hint = true; break; case OpCode_dotsection: - str.values[pos].set_drop (); + values[pos].set_hinting (); break; default: @@ -785,10 +857,10 @@ struct subr_subsetter_t { for (int i = pos - 1; i >= 0; i--) { - parsed_cs_op_t &csop = str.values[(unsigned)i]; - if (csop.for_drop ()) + parsed_cs_op_t &csop = values[(unsigned)i]; + if (csop.is_hinting ()) break; - csop.set_drop (); + csop.set_hinting (); if (csop.op == OpCode_vsindexcs) drop.vsindex_dropped = true; } @@ -801,12 +873,12 @@ struct subr_subsetter_t * only (usually one) hintmask operator, then calls to this subr can be dropped. */ drop.all_dropped = true; - for (unsigned int pos = 0; pos < str.values.length; pos++) + for (unsigned int pos = 0; pos < count; pos++) { - parsed_cs_op_t &csop = str.values[pos]; + parsed_cs_op_t &csop = values[pos]; if (csop.op == OpCode_return) break; - if (!csop.for_drop ()) + if (!csop.is_hinting ()) { drop.all_dropped = false; break; @@ -816,32 +888,62 @@ struct subr_subsetter_t return seen_hint; } - void collect_subr_refs_in_subr (parsed_cs_str_t &str, unsigned int pos, - unsigned int subr_num, parsed_cs_str_vec_t &subrs, + bool closure_subroutines (const parsed_cs_str_vec_t& global_subrs, + const hb_vector_t<parsed_cs_str_vec_t>& local_subrs) + { + closures.reset (); + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) + { + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + continue; + unsigned int fd = acc.fdSelect->get_fd (glyph); + if (unlikely (fd >= acc.fdCount)) + return false; + + // Note: const cast is safe here because the collect_subr_refs_in_str only performs a + // closure and does not modify any of the charstrings. + subr_subset_param_t param (const_cast<parsed_cs_str_t*> (&get_parsed_charstring (i)), + const_cast<parsed_cs_str_vec_t*> (&global_subrs), + const_cast<parsed_cs_str_vec_t*> (&local_subrs[fd]), + &closures.global_closure, + &closures.local_closures[fd], + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + collect_subr_refs_in_str (get_parsed_charstring (i), param); + } + + return true; + } + + void collect_subr_refs_in_subr (unsigned int subr_num, parsed_cs_str_vec_t &subrs, hb_set_t *closure, const subr_subset_param_t ¶m) { + if (closure->has (subr_num)) + return; closure->add (subr_num); collect_subr_refs_in_str (subrs[subr_num], param); } - void collect_subr_refs_in_str (parsed_cs_str_t &str, const subr_subset_param_t ¶m) + void collect_subr_refs_in_str (const parsed_cs_str_t &str, + const subr_subset_param_t ¶m) { - for (unsigned int pos = 0; pos < str.values.length; pos++) + if (!str.has_calls ()) + return; + + for (auto &opstr : str.values) { - if (!str.values[pos].for_drop ()) + if (!param.drop_hints || !opstr.is_hinting ()) { - switch (str.values[pos].op) + switch (opstr.op) { case OpCode_callsubr: - collect_subr_refs_in_subr (str, pos, - str.values[pos].subr_num, *param.parsed_local_subrs, + collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_local_subrs, param.local_closure, param); break; case OpCode_callgsubr: - collect_subr_refs_in_subr (str, pos, - str.values[pos].subr_num, *param.parsed_global_subrs, + collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_global_subrs, param.global_closure, param); break; @@ -853,42 +955,113 @@ struct subr_subsetter_t bool encode_str (const parsed_cs_str_t &str, const unsigned int fd, str_buff_t &buff) const { - unsigned count = str.get_count (); str_encoder_t encoder (buff); encoder.reset (); - buff.alloc (count * 3); + bool hinting = !(plan->flags & HB_SUBSET_FLAGS_NO_HINTING); /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints, * re-insert it at the beginning of charstreing */ - if (str.has_prefix () && str.is_hint_dropped ()) + if (str.has_prefix () && !hinting && str.is_hint_dropped ()) { encoder.encode_num (str.prefix_num ()); if (str.prefix_op () != OpCode_Invalid) encoder.encode_op (str.prefix_op ()); } - for (unsigned int i = 0; i < count; i++) + + unsigned size = 0; + for (auto &opstr : str.values) { - const parsed_cs_op_t &opstr = str.values[i]; - if (!opstr.for_drop () && !opstr.for_skip ()) + size += opstr.length; + if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr) + size += 3; + } + if (!buff.alloc (buff.length + size)) + return false; + + for (auto &opstr : str.values) + { + if (hinting || !opstr.is_hinting ()) { switch (opstr.op) { case OpCode_callsubr: encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num)); - encoder.encode_op (OpCode_callsubr); + encoder.copy_str (opstr.ptr, opstr.length); break; case OpCode_callgsubr: encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num)); - encoder.encode_op (OpCode_callgsubr); + encoder.copy_str (opstr.ptr, opstr.length); break; default: - encoder.copy_str (opstr.str); + encoder.copy_str (opstr.ptr, opstr.length); break; } } } - return !encoder.is_error (); + return !encoder.in_error (); + } + + void compact_parsed_strings () const + { + for (auto &cs : parsed_charstrings) + compact_string (cs); + for (auto &cs : parsed_global_subrs_storage) + compact_string (cs); + for (auto &vec : parsed_local_subrs_storage) + for (auto &cs : vec) + compact_string (cs); + } + + static void compact_string (parsed_cs_str_t &str) + { + unsigned count = str.values.length; + if (unlikely (!count)) return; + auto &opstr = str.values.arrayZ; + unsigned j = 0; + for (unsigned i = 1; i < count; i++) + { + /* See if we can combine op j and op i. */ + bool combine = + (opstr[j].op != OpCode_callsubr && opstr[j].op != OpCode_callgsubr) && + (opstr[i].op != OpCode_callsubr && opstr[i].op != OpCode_callgsubr) && + (opstr[j].is_hinting () == opstr[i].is_hinting ()) && + (opstr[j].ptr + opstr[j].length == opstr[i].ptr) && + (opstr[j].length + opstr[i].length <= 255); + + if (combine) + { + opstr[j].length += opstr[i].length; + opstr[j].op = OpCode_Invalid; + } + else + { + opstr[++j] = opstr[i]; + } + } + str.values.shrink (j + 1); + } + + void populate_subset_accelerator () const + { + if (!plan->inprogress_accelerator) return; + + compact_parsed_strings (); + + plan->inprogress_accelerator->cff_accelerator = + cff_subset_accelerator_t::create(acc.blob, + parsed_charstrings, + parsed_global_subrs_storage, + parsed_local_subrs_storage); + plan->inprogress_accelerator->destroy_cff_accelerator = + cff_subset_accelerator_t::destroy; + + } + + const parsed_cs_str_t& get_parsed_charstring (unsigned i) const + { + if (cached_charstrings) return *(cached_charstrings[i]); + return parsed_charstrings[i]; } protected: @@ -897,13 +1070,17 @@ struct subr_subsetter_t subr_closures_t closures; - parsed_cs_str_vec_t parsed_charstrings; - parsed_cs_str_vec_t parsed_global_subrs; - hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs; + hb_vector_t<const parsed_cs_str_t*> cached_charstrings; + const parsed_cs_str_vec_t* parsed_global_subrs; + const hb_vector_t<parsed_cs_str_vec_t>* parsed_local_subrs; subr_remaps_t remaps; private: + + parsed_cs_str_vec_t parsed_charstrings; + parsed_cs_str_vec_t parsed_global_subrs_storage; + hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs_storage; typedef typename SUBRS::count_type subr_count_type; }; diff --git a/thirdparty/harfbuzz/src/hb-subset-cff1.cc b/thirdparty/harfbuzz/src/hb-subset-cff1.cc index 52bb13d320..538f28f5aa 100644 --- a/thirdparty/harfbuzz/src/hb-subset-cff1.cc +++ b/thirdparty/harfbuzz/src/hb-subset-cff1.cc @@ -167,9 +167,10 @@ struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<cff1_top_dic * for supplement, the original byte string is copied along with the op code */ op_str_t supp_op; supp_op.op = op; - if ( unlikely (!(opstr.str.length >= opstr.last_arg_offset + 3))) + if ( unlikely (!(opstr.length >= opstr.last_arg_offset + 3))) return_trace (false); - supp_op.str = hb_ubytes_t (&opstr.str + opstr.last_arg_offset, opstr.str.length - opstr.last_arg_offset); + supp_op.ptr = opstr.ptr + opstr.last_arg_offset; + supp_op.length = opstr.length - opstr.last_arg_offset; return_trace (UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::registry]) && UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::ordering]) && copy_opstr (c, supp_op)); @@ -442,8 +443,17 @@ struct cff_subset_plan { return; } - bool use_glyph_to_sid_map = plan->num_output_glyphs () > plan->source->get_num_glyphs () / 8.; - hb_map_t *glyph_to_sid_map = use_glyph_to_sid_map ? acc.create_glyph_to_sid_map () : nullptr; + hb_map_t *glyph_to_sid_map = (plan->accelerator && plan->accelerator->cff_accelerator) ? + plan->accelerator->cff_accelerator->glyph_to_sid_map : + nullptr; + bool created_map = false; + if (!glyph_to_sid_map && + ((plan->accelerator && plan->accelerator->cff_accelerator) || + plan->num_output_glyphs () > plan->source->get_num_glyphs () / 8.)) + { + created_map = true; + glyph_to_sid_map = acc.create_glyph_to_sid_map (); + } unsigned int glyph; for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++) @@ -467,8 +477,12 @@ struct cff_subset_plan { last_sid = sid; } - if (glyph_to_sid_map) - hb_map_destroy (glyph_to_sid_map); + if (created_map) + { + if (!(plan->accelerator && plan->accelerator->cff_accelerator) || + !plan->accelerator->cff_accelerator->glyph_to_sid_map.cmpexch (nullptr, glyph_to_sid_map)) + hb_map_destroy (glyph_to_sid_map); + } bool two_byte = subset_charset_ranges.complete (glyph); @@ -728,11 +742,17 @@ static bool _serialize_cff1 (hb_serialize_context_t *c, /* CharStrings */ { + c->push<CFF1CharStrings> (); + + unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings); + if (unlikely (!c->start_zerocopy (total_size))) + return false; + CFF1CharStrings *cs = c->start_embed<CFF1CharStrings> (); if (unlikely (!cs)) return false; - c->push (); + if (likely (cs->serialize (c, plan.subset_charstrings))) - plan.info.char_strings_link = c->pop_pack (); + plan.info.char_strings_link = c->pop_pack (false); else { c->pop_discard (); @@ -815,7 +835,7 @@ static bool _serialize_cff1 (hb_serialize_context_t *c, CFF1Subrs *dest = c->start_embed <CFF1Subrs> (); if (unlikely (!dest)) return false; if (likely (dest->serialize (c, plan.subset_globalsubrs))) - c->pop_pack (); + c->pop_pack (false); else { c->pop_discard (); diff --git a/thirdparty/harfbuzz/src/hb-subset-cff2.cc b/thirdparty/harfbuzz/src/hb-subset-cff2.cc index 08e820efcf..60946b0281 100644 --- a/thirdparty/harfbuzz/src/hb-subset-cff2.cc +++ b/thirdparty/harfbuzz/src/hb-subset-cff2.cc @@ -334,7 +334,7 @@ static bool _serialize_cff2 (hb_serialize_context_t *c, if (unlikely (!dest)) return false; c->push (); if (likely (dest->serialize (c, plan.subset_localsubrs[i]))) - subrs_link = c->pop_pack (); + subrs_link = c->pop_pack (false); else { c->pop_discard (); @@ -361,11 +361,17 @@ static bool _serialize_cff2 (hb_serialize_context_t *c, /* CharStrings */ { + c->push (); + + unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings); + if (unlikely (!c->start_zerocopy (total_size))) + return false; + CFF2CharStrings *cs = c->start_embed<CFF2CharStrings> (); if (unlikely (!cs)) return false; - c->push (); + if (likely (cs->serialize (c, plan.subset_charstrings))) - plan.info.char_strings_link = c->pop_pack (); + plan.info.char_strings_link = c->pop_pack (false); else { c->pop_discard (); @@ -377,9 +383,10 @@ static bool _serialize_cff2 (hb_serialize_context_t *c, if (acc.fdSelect != &Null (CFF2FDSelect)) { c->push (); - if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect, plan.orig_fdcount, - plan.subset_fdselect_format, plan.subset_fdselect_size, - plan.subset_fdselect_ranges))) + if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect, + plan.orig_fdcount, + plan.subset_fdselect_format, plan.subset_fdselect_size, + plan.subset_fdselect_ranges))) plan.info.fd_select.link = c->pop_pack (); else { @@ -401,7 +408,7 @@ static bool _serialize_cff2 (hb_serialize_context_t *c, hb_iter (private_dict_infos)) ; if (unlikely (!fda->serialize (c, it, fontSzr))) return false; - plan.info.fd_array_link = c->pop_pack (); + plan.info.fd_array_link = c->pop_pack (false); } /* variation store */ @@ -410,7 +417,7 @@ static bool _serialize_cff2 (hb_serialize_context_t *c, c->push (); CFF2VariationStore *dest = c->start_embed<CFF2VariationStore> (); if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false; - plan.info.var_store_link = c->pop_pack (); + plan.info.var_store_link = c->pop_pack (false); } OT::cff2 *cff2 = c->allocate_min<OT::cff2> (); diff --git a/thirdparty/harfbuzz/src/hb-subset-input.cc b/thirdparty/harfbuzz/src/hb-subset-input.cc index fd250104bb..42434ae0ef 100644 --- a/thirdparty/harfbuzz/src/hb-subset-input.cc +++ b/thirdparty/harfbuzz/src/hb-subset-input.cc @@ -26,7 +26,7 @@ #include "hb-subset.hh" #include "hb-set.hh" - +#include "hb-utf.hh" /** * hb_subset_input_create_or_fail: * @@ -49,8 +49,15 @@ hb_subset_input_create_or_fail (void) set = hb_set_create (); input->axes_location = hb_hashmap_create<hb_tag_t, float> (); +#ifdef HB_EXPERIMENTAL_API + input->name_table_overrides = hb_hashmap_create<hb_ot_name_record_ids_t, hb_bytes_t> (); +#endif - if (!input->axes_location || input->in_error ()) + if (!input->axes_location || +#ifdef HB_EXPERIMENTAL_API + !input->name_table_overrides || +#endif + input->in_error ()) { hb_subset_input_destroy (input); return nullptr; @@ -248,6 +255,15 @@ hb_subset_input_destroy (hb_subset_input_t *input) hb_hashmap_destroy (input->axes_location); +#ifdef HB_EXPERIMENTAL_API + if (input->name_table_overrides) + { + for (auto _ : *input->name_table_overrides) + _.second.fini (); + } + hb_hashmap_destroy (input->name_table_overrides); +#endif + hb_free (input); } @@ -379,7 +395,6 @@ hb_subset_input_get_user_data (const hb_subset_input_t *input, return hb_object_get_user_data (input, key); } -#ifdef HB_EXPERIMENTAL_API #ifndef HB_NO_VAR /** * hb_subset_input_pin_axis_to_default: (skip) @@ -388,9 +403,12 @@ hb_subset_input_get_user_data (const hb_subset_input_t *input, * * Pin an axis to its default location in the given subset input object. * + * Currently only works for fonts with 'glyf' tables. CFF and CFF2 is not + * yet supported. Additionally all axes in a font must be pinned. + * * Return value: `true` if success, `false` otherwise * - * Since: EXPERIMENTAL + * Since: 6.0.0 **/ HB_EXTERN hb_bool_t hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, @@ -412,9 +430,12 @@ hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, * * Pin an axis to a fixed location in the given subset input object. * + * Currently only works for fonts with 'glyf' tables. CFF and CFF2 is not + * yet supported. Additionally all axes in a font must be pinned. + * * Return value: `true` if success, `false` otherwise * - * Since: EXPERIMENTAL + * Since: 6.0.0 **/ HB_EXTERN hb_bool_t hb_subset_input_pin_axis_location (hb_subset_input_t *input, @@ -430,28 +451,41 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input, return input->axes_location->set (axis_tag, val); } #endif -#endif -#ifdef HB_EXPERIMENTAL_API /** - * hb_subset_preprocess - * @input: a #hb_face_t object. + * hb_subset_preprocess: + * @source: a #hb_face_t object. * * Preprocesses the face and attaches data that will be needed by the * subsetter. Future subsetting operations can then use the precomputed data * to speed up the subsetting operation. * - * Since: EXPERIMENTAL + * See [subset-preprocessing](https://github.com/harfbuzz/harfbuzz/blob/main/docs/subset-preprocessing.md) + * for more information. + * + * Note: the preprocessed face may contain sub-blobs that reference the memory + * backing the source #hb_face_t. Therefore in the case that this memory is not + * owned by the source face you will need to ensure that memory lives + * as long as the returned #hb_face_t. + * + * Returns: a new #hb_face_t. + * + * Since: 6.0.0 **/ HB_EXTERN hb_face_t * hb_subset_preprocess (hb_face_t *source) { hb_subset_input_t* input = hb_subset_input_create_or_fail (); + if (!input) + return source; hb_set_clear (hb_subset_input_set(input, HB_SUBSET_SETS_UNICODE)); hb_set_invert (hb_subset_input_set(input, HB_SUBSET_SETS_UNICODE)); + hb_set_clear (hb_subset_input_set(input, HB_SUBSET_SETS_GLYPH_INDEX)); + hb_set_invert (hb_subset_input_set(input, HB_SUBSET_SETS_GLYPH_INDEX)); + hb_set_clear (hb_subset_input_set(input, HB_SUBSET_SETS_LAYOUT_FEATURE_TAG)); hb_set_invert (hb_subset_input_set(input, @@ -467,15 +501,100 @@ hb_subset_preprocess (hb_face_t *source) hb_set_invert (hb_subset_input_set(input, HB_SUBSET_SETS_NAME_ID)); + hb_set_clear (hb_subset_input_set(input, + HB_SUBSET_SETS_NAME_LANG_ID)); + hb_set_invert (hb_subset_input_set(input, + HB_SUBSET_SETS_NAME_LANG_ID)); + hb_subset_input_set_flags(input, HB_SUBSET_FLAGS_NOTDEF_OUTLINE | HB_SUBSET_FLAGS_GLYPH_NAMES | - HB_SUBSET_FLAGS_RETAIN_GIDS); + HB_SUBSET_FLAGS_RETAIN_GIDS | + HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES); input->attach_accelerator_data = true; + // Always use long loca in the preprocessed version. This allows + // us to store the glyph bytes unpadded which allows the future subset + // operation to run faster by skipping the trim padding step. + input->force_long_loca = true; + hb_face_t* new_source = hb_subset_or_fail (source, input); hb_subset_input_destroy (input); + if (!new_source) { + DEBUG_MSG (SUBSET, nullptr, "Preprocessing failed due to subset failure."); + return source; + } + return new_source; } + +#ifdef HB_EXPERIMENTAL_API +/** + * hb_subset_input_override_name_table: + * @input: a #hb_subset_input_t object. + * @name_id: name_id of a nameRecord + * @platform_id: platform ID of a nameRecord + * @encoding_id: encoding ID of a nameRecord + * @language_id: language ID of a nameRecord + * @name_str: pointer to name string new value or null to indicate should remove + * @str_len: the size of @name_str, or -1 if it is `NULL`-terminated + * + * Override the name string of the NameRecord identified by name_id, + * platform_id, encoding_id and language_id. If a record with that name_id + * doesn't exist, create it and insert to the name table. + * + * Note: for mac platform, we only support name_str with all ascii characters, + * name_str with non-ascii characters will be ignored. + * + * Since: EXPERIMENTAL + **/ +HB_EXTERN hb_bool_t +hb_subset_input_override_name_table (hb_subset_input_t *input, + hb_ot_name_id_t name_id, + unsigned platform_id, + unsigned encoding_id, + unsigned language_id, + const char *name_str, + int str_len /* -1 means nul-terminated */) +{ + if (!name_str) + { + str_len = 0; + } + else if (str_len == -1) + { + str_len = strlen (name_str); + } + + hb_bytes_t name_bytes (nullptr, 0); + if (str_len) + { + if (platform_id == 1) + { + const uint8_t *src = reinterpret_cast<const uint8_t*> (name_str); + const uint8_t *src_end = src + str_len; + + hb_codepoint_t unicode; + const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + while (src < src_end) + { + src = hb_utf8_t::next (src, src_end, &unicode, replacement); + if (unicode >= 0x0080u) + { + printf ("Non-ascii character detected, ignored...This API supports acsii characters only for mac platform\n"); + return false; + } + } + } + char *override_name = (char *) hb_malloc (str_len); + if (unlikely (!override_name)) return false; + + hb_memcpy (override_name, name_str, str_len); + name_bytes = hb_bytes_t (override_name, str_len); + } + input->name_table_overrides->set (hb_ot_name_record_ids_t (platform_id, encoding_id, language_id, name_id), name_bytes); + return true; +} + #endif diff --git a/thirdparty/harfbuzz/src/hb-subset-input.hh b/thirdparty/harfbuzz/src/hb-subset-input.hh index dabb4918fe..ecd47b7abf 100644 --- a/thirdparty/harfbuzz/src/hb-subset-input.hh +++ b/thirdparty/harfbuzz/src/hb-subset-input.hh @@ -36,6 +36,48 @@ #include "hb-font.hh" +struct hb_ot_name_record_ids_t +{ + hb_ot_name_record_ids_t () = default; + hb_ot_name_record_ids_t (unsigned platform_id_, + unsigned encoding_id_, + unsigned language_id_, + unsigned name_id_) + :platform_id (platform_id_), + encoding_id (encoding_id_), + language_id (language_id_), + name_id (name_id_) {} + + bool operator != (const hb_ot_name_record_ids_t o) const + { return !(*this == o); } + + inline bool operator == (const hb_ot_name_record_ids_t& o) const + { + return platform_id == o.platform_id && + encoding_id == o.encoding_id && + language_id == o.language_id && + name_id == o.name_id; + } + + inline uint32_t hash () const + { + uint32_t current = 0; + current = current * 31 + hb_hash (platform_id); + current = current * 31 + hb_hash (encoding_id); + current = current * 31 + hb_hash (language_id); + current = current * 31 + hb_hash (name_id); + return current; + } + + unsigned platform_id; + unsigned encoding_id; + unsigned language_id; + unsigned name_id; +}; + +typedef struct hb_ot_name_record_ids_t hb_ot_name_record_ids_t; + + HB_MARK_AS_FLAG_T (hb_subset_flags_t); struct hb_subset_input_t @@ -60,7 +102,14 @@ struct hb_subset_input_t unsigned flags; bool attach_accelerator_data = false; + + // If set loca format will always be the long version. + bool force_long_loca = false; + hb_hashmap_t<hb_tag_t, float> *axes_location; +#ifdef HB_EXPERIMENTAL_API + hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides; +#endif inline unsigned num_sets () const { @@ -80,7 +129,11 @@ struct hb_subset_input_t return true; } - return axes_location->in_error (); + return axes_location->in_error () +#ifdef HB_EXPERIMENTAL_API + || name_table_overrides->in_error () +#endif + ; } }; diff --git a/thirdparty/harfbuzz/src/hb-subset-plan.cc b/thirdparty/harfbuzz/src/hb-subset-plan.cc index 9cf7c9e431..71bc908ffb 100644 --- a/thirdparty/harfbuzz/src/hb-subset-plan.cc +++ b/thirdparty/harfbuzz/src/hb-subset-plan.cc @@ -27,6 +27,7 @@ #include "hb-subset-plan.hh" #include "hb-subset-accelerator.hh" #include "hb-map.hh" +#include "hb-multimap.hh" #include "hb-set.hh" #include "hb-ot-cmap-table.hh" @@ -47,7 +48,7 @@ using OT::Layout::GPOS; typedef hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> script_langsys_map; #ifndef HB_NO_SUBSET_CFF -static inline void +static inline bool _add_cff_seac_components (const OT::cff1::accelerator_t &cff, hb_codepoint_t gid, hb_set_t *gids_to_retain) @@ -57,7 +58,9 @@ _add_cff_seac_components (const OT::cff1::accelerator_t &cff, { gids_to_retain->add (base_gid); gids_to_retain->add (accent_gid); + return true; } + return false; } #endif @@ -82,10 +85,8 @@ static void _remap_indexes (const hb_set_t *indexes, hb_map_t *mapping /* OUT */) { - unsigned count = indexes->get_population (); - - for (auto _ : + hb_zip (indexes->iter (), hb_range (count))) - mapping->set (_.first, _.second); + for (auto _ : + hb_enumerate (indexes->iter ())) + mapping->set (_.second, _.first); } @@ -344,7 +345,9 @@ _get_hb_font_with_variations (const hb_subset_plan_t *plan) vars.push (var); } +#ifndef HB_NO_VAR hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location->get_population ()); +#endif return font; } @@ -521,14 +524,44 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, cmap_unicodes = &plan->accelerator->unicodes; } - for (hb_codepoint_t cp : *cmap_unicodes) + if (plan->accelerator && + unicodes->get_population () < cmap_unicodes->get_population () && + glyphs->get_population () < cmap_unicodes->get_population ()) + { + auto &gid_to_unicodes = plan->accelerator->gid_to_unicodes; + for (hb_codepoint_t gid : *glyphs) + { + auto unicodes = gid_to_unicodes.get (gid); + + for (hb_codepoint_t cp : unicodes) + { + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } + } + for (hb_codepoint_t cp : *unicodes) + { + /* Don't double-add entry. */ + if (plan->codepoint_to_glyph->has (cp)) + continue; + + hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } + plan->unicode_to_new_gid_list.qsort (); + } + else { - hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; - if (!unicodes->has (cp) && !glyphs->has (gid)) - continue; + for (hb_codepoint_t cp : *cmap_unicodes) + { + hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; + if (!unicodes->has (cp) && !glyphs->has (gid)) + continue; - plan->codepoint_to_glyph->set (cp, gid); - plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } } /* Add gids which where requested, but not mapped in cmap */ @@ -639,9 +672,15 @@ _populate_gids_to_retain (hb_subset_plan_t* plan, else plan->_glyphset->union_ (cur_glyphset); #ifndef HB_NO_SUBSET_CFF - if (cff.is_valid ()) - for (hb_codepoint_t gid : cur_glyphset) - _add_cff_seac_components (cff, gid, plan->_glyphset); + if (!plan->accelerator || plan->accelerator->has_seac) + { + bool has_seac = false; + if (cff.is_valid ()) + for (hb_codepoint_t gid : cur_glyphset) + if (_add_cff_seac_components (cff, gid, plan->_glyphset)) + has_seac = true; + plan->has_seac = has_seac; + } #endif _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ()); @@ -848,9 +887,28 @@ hb_subset_plan_create_or_fail (hb_face_t *face, plan->check_success (plan->vmtx_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ()); plan->check_success (plan->hmtx_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ()); +#ifdef HB_EXPERIMENTAL_API + plan->check_success (plan->name_table_overrides = hb_hashmap_create<hb_ot_name_record_ids_t, hb_bytes_t> ()); + if (plan->name_table_overrides && input->name_table_overrides) + { + for (auto _ : *input->name_table_overrides) + { + hb_bytes_t name_bytes = _.second; + unsigned len = name_bytes.length; + char *name_str = (char *) hb_malloc (len); + if (unlikely (!plan->check_success (name_str))) + break; + + hb_memcpy (name_str, name_bytes.arrayZ, len); + plan->name_table_overrides->set (_.first, hb_bytes_t (name_str, len)); + } + } +#endif + void* accel = hb_face_get_user_data(face, hb_subset_accelerator_t::user_data_key()); plan->attach_accelerator_data = input->attach_accelerator_data; + plan->force_long_loca = input->force_long_loca; if (accel) plan->accelerator = (hb_subset_accelerator_t*) accel; @@ -893,6 +951,28 @@ hb_subset_plan_create_or_fail (hb_face_t *face, hb_subset_plan_destroy (plan); return nullptr; } + + + if (plan->attach_accelerator_data) + { + hb_multimap_t gid_to_unicodes; + + hb_map_t &unicode_to_gid = *plan->codepoint_to_glyph; + + for (auto unicode : *plan->unicodes) + { + auto gid = unicode_to_gid[unicode]; + gid_to_unicodes.add (gid, unicode); + } + + plan->inprogress_accelerator = + hb_subset_accelerator_t::create (*plan->codepoint_to_glyph, + gid_to_unicodes, + *plan->unicodes, + plan->has_seac); + } + + return plan; } diff --git a/thirdparty/harfbuzz/src/hb-subset-plan.hh b/thirdparty/harfbuzz/src/hb-subset-plan.hh index 15fabba9c3..1b2aee7825 100644 --- a/thirdparty/harfbuzz/src/hb-subset-plan.hh +++ b/thirdparty/harfbuzz/src/hb-subset-plan.hh @@ -87,6 +87,18 @@ struct hb_subset_plan_t hb_hashmap_destroy (vmtx_map); hb_hashmap_destroy (layout_variation_idx_delta_map); +#ifdef HB_EXPERIMENTAL_API + if (name_table_overrides) + { + for (auto _ : *name_table_overrides) + _.second.fini (); + } + hb_hashmap_destroy (name_table_overrides); +#endif + + if (inprogress_accelerator) + hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator); + if (user_axes_location) { hb_object_destroy (user_axes_location); @@ -99,10 +111,11 @@ struct hb_subset_plan_t bool successful; unsigned flags; bool attach_accelerator_data = false; + bool force_long_loca = false; // For each cp that we'd like to retain maps to the corresponding gid. hb_set_t *unicodes; - hb_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> unicode_to_new_gid_list; + hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> unicode_to_new_gid_list; // name_ids we would like to retain hb_set_t *name_ids; @@ -185,31 +198,41 @@ struct hb_subset_plan_t hb_map_t *axes_old_index_tag_map; bool all_axes_pinned; bool pinned_at_default; + bool has_seac; //hmtx metrics map: new gid->(advance, lsb) hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *hmtx_map; //vmtx metrics map: new gid->(advance, lsb) hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *vmtx_map; +#ifdef HB_EXPERIMENTAL_API + // name table overrides map: hb_ot_name_record_ids_t-> name string new value or + // None to indicate should remove + hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides; +#endif + const hb_subset_accelerator_t* accelerator; + hb_subset_accelerator_t* inprogress_accelerator; public: template<typename T> hb_blob_ptr_t<T> source_table() { - if (sanitized_table_cache - && !sanitized_table_cache->in_error () - && sanitized_table_cache->has (T::tableTag)) { - return hb_blob_reference (sanitized_table_cache->get (T::tableTag).get ()); + hb_lock_t (accelerator ? &accelerator->sanitized_table_cache_lock : nullptr); + + auto *cache = accelerator ? &accelerator->sanitized_table_cache : sanitized_table_cache; + if (cache + && !cache->in_error () + && cache->has (+T::tableTag)) { + return hb_blob_reference (cache->get (+T::tableTag).get ()); } hb::unique_ptr<hb_blob_t> table_blob {hb_sanitize_context_t ().reference_table<T> (source)}; hb_blob_t* ret = hb_blob_reference (table_blob.get ()); - if (likely (sanitized_table_cache)) - sanitized_table_cache->set (T::tableTag, - std::move (table_blob)); + if (likely (cache)) + cache->set (+T::tableTag, std::move (table_blob)); return ret; } diff --git a/thirdparty/harfbuzz/src/hb-subset.cc b/thirdparty/harfbuzz/src/hb-subset.cc index 6026aa6eff..186b12dbb8 100644 --- a/thirdparty/harfbuzz/src/hb-subset.cc +++ b/thirdparty/harfbuzz/src/hb-subset.cc @@ -82,6 +82,10 @@ using OT::Layout::GPOS; * retain glyph ids option and configure the subset to pass through the layout tables untouched. */ + +hb_user_data_key_t _hb_subset_accelerator_user_data_key = {}; + + /* * The list of tables in the open type spec. Used to check for tables that may need handling * if we are unable to list the tables in a face. @@ -409,20 +413,14 @@ _passthrough (hb_subset_plan_t *plan, hb_tag_t tag) static bool _dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag, - hb_set_t &visited_set, hb_set_t &revisit_set) + const hb_set_t &subsetted_tags, + const hb_set_t &pending_subset_tags) { switch (tag) { case HB_OT_TAG_hmtx: case HB_OT_TAG_vmtx: - if (!plan->pinned_at_default && - !visited_set.has (HB_OT_TAG_glyf)) - { - revisit_set.add (tag); - return false; - } - return true; - + return plan->pinned_at_default || !pending_subset_tags.has (HB_OT_TAG_glyf); default: return true; } @@ -495,12 +493,14 @@ _subset_table (hb_subset_plan_t *plan, } } -static void _attach_accelerator_data (const hb_subset_plan_t* plan, +static void _attach_accelerator_data (hb_subset_plan_t* plan, hb_face_t* face /* IN/OUT */) { - hb_subset_accelerator_t* accel = - hb_subset_accelerator_t::create (*plan->codepoint_to_glyph, - *plan->unicodes); + if (!plan->inprogress_accelerator) return; + + // Transfer the accelerator from the plan to us. + hb_subset_accelerator_t* accel = plan->inprogress_accelerator; + plan->inprogress_accelerator = nullptr; if (accel->in_error ()) { @@ -508,6 +508,11 @@ static void _attach_accelerator_data (const hb_subset_plan_t* plan, return; } + // Populate caches that need access to the final tables. + hb_blob_ptr_t<OT::cmap> cmap_ptr (hb_sanitize_context_t ().reference_table<OT::cmap> (face)); + accel->cmap_cache = OT::cmap::create_filled_cache (cmap_ptr); + accel->destroy_cmap_cache = OT::SubtableUnicodesCache::destroy; + if (!hb_face_set_user_data(face, hb_subset_accelerator_t::user_data_key(), accel, @@ -561,41 +566,63 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan) return nullptr; } - hb_set_t tags_set, revisit_set; - bool success = true; hb_tag_t table_tags[32]; unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags); - hb_vector_t<char> buf; - buf.alloc (4096 - 16); + hb_set_t subsetted_tags, pending_subset_tags; while (((void) _get_table_tags (plan, offset, &num_tables, table_tags), num_tables)) { for (unsigned i = 0; i < num_tables; ++i) { hb_tag_t tag = table_tags[i]; - if (_should_drop_table (plan, tag) && !tags_set.has (tag)) continue; - if (!_dependencies_satisfied (plan, tag, tags_set, revisit_set)) continue; - tags_set.add (tag); - success = _subset_table (plan, buf, tag); - if (unlikely (!success)) goto end; + if (_should_drop_table (plan, tag)) continue; + pending_subset_tags.add (tag); + } + + offset += num_tables; + } + + hb_vector_t<char> buf; + buf.alloc (4096 - 16); + + + bool success = true; + + while (!pending_subset_tags.is_empty ()) + { + if (subsetted_tags.in_error () + || pending_subset_tags.in_error ()) { + success = false; + goto end; } - /*delayed subsetting for some tables since they might have dependency on other tables in some cases: - e.g: during instantiating glyf tables, hmetrics/vmetrics are updated and saved in subset plan, - hmtx/vmtx subsetting need to use these updated metrics values*/ - while (!revisit_set.is_empty ()) + bool made_changes = false; + for (hb_tag_t tag : pending_subset_tags) { - hb_set_t revisit_temp; - for (hb_tag_t tag : revisit_set) + if (!_dependencies_satisfied (plan, tag, + subsetted_tags, + pending_subset_tags)) { - if (!_dependencies_satisfied (plan, tag, tags_set, revisit_temp)) continue; - tags_set.add (tag); - success = _subset_table (plan, buf, tag); - if (unlikely (!success)) goto end; + // delayed subsetting for some tables since they might have dependency on other tables + // in some cases: e.g: during instantiating glyf tables, hmetrics/vmetrics are updated + // and saved in subset plan, hmtx/vmtx subsetting need to use these updated metrics values + continue; } - revisit_set = revisit_temp; + + pending_subset_tags.del (tag); + subsetted_tags.add (tag); + made_changes = true; + + success = _subset_table (plan, buf, tag); + if (unlikely (!success)) goto end; + } + + if (!made_changes) + { + DEBUG_MSG (SUBSET, nullptr, "Table dependencies unable to be satisfied. Subset failed."); + success = false; + goto end; } - offset += num_tables; } if (success && plan->attach_accelerator_data) { diff --git a/thirdparty/harfbuzz/src/hb-subset.h b/thirdparty/harfbuzz/src/hb-subset.h index 6a2c5f6114..b83264ea5e 100644 --- a/thirdparty/harfbuzz/src/hb-subset.h +++ b/thirdparty/harfbuzz/src/hb-subset.h @@ -28,6 +28,7 @@ #define HB_SUBSET_H #include "hb.h" +#include "hb-ot.h" HB_BEGIN_DECLS @@ -164,8 +165,6 @@ HB_EXTERN void hb_subset_input_set_flags (hb_subset_input_t *input, unsigned value); -#ifdef HB_EXPERIMENTAL_API -#ifndef HB_NO_VAR HB_EXTERN hb_bool_t hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, hb_face_t *face, @@ -176,16 +175,22 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input, hb_face_t *face, hb_tag_t axis_tag, float axis_value); -#endif -#endif #ifdef HB_EXPERIMENTAL_API +HB_EXTERN hb_bool_t +hb_subset_input_override_name_table (hb_subset_input_t *input, + hb_ot_name_id_t name_id, + unsigned platform_id, + unsigned encoding_id, + unsigned language_id, + const char *name_str, + int str_len); + +#endif HB_EXTERN hb_face_t * hb_subset_preprocess (hb_face_t *source); -#endif - HB_EXTERN hb_face_t * hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input); diff --git a/thirdparty/harfbuzz/src/hb-ucd-table.hh b/thirdparty/harfbuzz/src/hb-ucd-table.hh index a372a4afce..f7d76eee18 100644 --- a/thirdparty/harfbuzz/src/hb-ucd-table.hh +++ b/thirdparty/harfbuzz/src/hb-ucd-table.hh @@ -1069,7 +1069,7 @@ _hb_ucd_dm2_u64_map[388] = #ifndef HB_OPTIMIZE_SIZE static const uint8_t -_hb_ucd_u8[18260] = +_hb_ucd_u8[17868] = { 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 11, 12, 13, 13, 13, 14, @@ -1535,191 +1535,166 @@ _hb_ucd_u8[18260] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, 0, 0, 40, 41, 42, 0, 43, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 0, - 9, 0, 10, 11, 0, 0, 12, 13, 14, 15, 16, 0, 0, 0, 0, 17, - 18, 19, 20, 0, 21, 0, 22, 23, 0, 24, 25, 0, 0, 24, 26, 27, - 0, 24, 26, 0, 0, 24, 26, 0, 0, 24, 26, 0, 0, 0, 26, 0, - 0, 24, 28, 0, 0, 24, 26, 0, 0, 29, 26, 0, 0, 0, 30, 0, - 0, 31, 32, 0, 0, 33, 34, 0, 35, 36, 0, 37, 38, 0, 39, 0, - 0, 40, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 0, 0, 0, 45, 0, - 0, 0, 0, 0, 0, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, - 48, 0, 0, 49, 0, 50, 51, 0, 0, 52, 53, 54, 0, 55, 0, 56, - 0, 57, 0, 0, 0, 0, 58, 59, 0, 0, 0, 0, 0, 0, 60, 61, - 0, 0, 0, 0, 0, 0, 62, 63, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, - 0, 67, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 69, 70, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, - 72, 73, 0, 0, 0, 0, 53, 74, 0, 75, 76, 0, 0, 77, 78, 0, - 0, 0, 0, 0, 0, 79, 80, 81, 0, 0, 0, 0, 0, 0, 0, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, - 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 0, 0, 0, 0, 0, 0, 0, 85, 0, 0, 0, 86, 0, 0, 0, 0, - 87, 88, 0, 0, 0, 0, 0, 89, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 91, 0, 92, 0, 0, 93, 0, 94, 0, 0, 0, - 0, 0, 72, 95, 0, 96, 0, 0, 97, 98, 0, 77, 0, 0, 99, 0, - 0,100, 0, 0, 0, 0, 0,101, 0,102, 26,103, 0, 0, 0, 0, - 0, 0,104, 0, 0, 0,105, 0, 0, 0, 0, 0, 0, 65,106, 0, - 0, 65, 0, 0, 0,107, 0, 0, 0,108, 0, 0, 0, 0, 0, 0, - 0, 96, 0, 0, 0, 0, 0, 0, 0,109,110, 0, 0, 0, 0, 78, - 0, 44,111, 0,112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 65, 0, 0, 0, 0, 0, 0, 0, 0,113, 0,114, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,115, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,116, 0,117, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118, - 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120,121,122, 0, 0, - 0, 0,123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 124,125, 0, 0,126, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,127, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,129, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,130, 0, 0, 0,131, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, - 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, - 18, 1, 1, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, - 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 31, 0, - 0, 0, 32, 33, 34, 35, 1, 36, 0, 0, 0, 0, 37, 0, 0, 0, - 0, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 0, 0, 0, 0, - 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 43, 36, 44, 45, - 21, 45, 46, 0, 0, 0, 0, 0, 0, 0, 19, 1, 21, 0, 0, 47, - 0, 0, 0, 0, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 52, 1, - 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, - 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, - 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, - 0, 0, 0, 56, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 61, 62, - 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, - 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, - 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, - 0, 0, 0, 0, 0, 69, 70, 0, 0, 0, 0, 0, 71, 72, 73, 74, - 75, 76, 0, 0, 0, 0, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 80, 0, 0, - 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 63, 0, 0, 81, - 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, - 0, 0, 0, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, - 0, 0, 0, 0, 1, 52, 15, 86, 36, 10, 21, 87, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 55, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, - 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, 0, - 0, 89, 0, 0, 88, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, - 0, 0, 0, 0, 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, - 21, 1, 21, 92, 93, 1, 1, 1, 1, 1, 1, 1, 1, 94, 95, 96, - 0, 0, 0, 0, 97, 1, 98, 58, 81, 99,100, 4, 58, 0, 0, 0, - 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,101,102, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 19, 0, 1, 1, 50, - 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 50, 0, 0, 0, - 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, - 1, 1, 1, 1, 50, 0, 0, 0, 0, 0,104, 68, 0, 0, 0, 0, - 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, - 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105,106, 58, 38, - 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, - 0, 0, 0, 0, 0, 0, 0,107, 1, 14, 4, 12, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 47, 84, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,109, 61, 0,110, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 58, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 51, 0,111, 14, 52, 84, 0, 0, 0, - 112, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 61, - 0, 0, 0, 0, 0, 0,113, 0, 87, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 78, 55, 0, 38, 1, 58, 1, 58, 0, 0, - 63, 89, 0, 0, 0, 0, 0, 59,115, 0, 0, 0, 0, 0, 0, 0, - 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,115, 0, 0, - 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, - 78, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 61, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 91, 0, 0, 0, 0, 0, 0, 1, 87, 0, 0, - 0, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, - 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, 0, 0, - 0, 0, 38, 50, 0, 0, 0, 0, 38, 58, 0, 0, 0, 0, 0, 0, - 1, 87, 1, 1, 1, 1, 39, 1, 48,105, 87, 0, 0, 0, 0, 0, - 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 59, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,123, 0, 0, 0, 0, - 0, 0, 0, 0, 4,122, 0, 0, 0, 1,124, 0, 0, 0, 0, 0, - 0, 0, 0, 0,230,230,230,230,230,232,220,220,220,220,232,216, - 220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220, - 1, 1, 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220, - 220,220,230,230,230,220,220, 0,230,230,230,220,220,220,220,230, - 232,220,220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, - 0,220,230,230,230,230,220,230,230,230,222,220,230,230,220,220, - 230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, - 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, - 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230, - 220,230,230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230, - 230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230, - 220,220,230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0, - 230,230, 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220, - 230,220,220,220,230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, - 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, - 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0, - 103,103, 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122, - 220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0, - 132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, - 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, - 9, 9, 0, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220, - 220, 0, 0, 0,230, 0, 0,220,230,220, 0,220,230,230,230, 0, - 0, 0, 9, 9, 0, 0, 7, 0,230, 0, 1, 1, 1, 0, 0, 0, - 230,234,214,220,202,230,230,230,230,230,232,228,228,220,218,230, - 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, - 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, - 0, 0, 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, - 0,220, 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, - 0, 0,230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, - 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, - 0,226,216,216,216,216,216, 0,220,220,220, 0,232,232,220,230, - 230,230, 7, 0, 16, 17, 17, 17, 17, 17, 17, 33, 17, 17, 17, 19, - 17, 17, 17, 17, 20,101, 17,113,129,169, 17, 27, 28, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, + 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, + 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, + 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, + 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, + 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, + 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, + 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, + 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, + 0, 0, 75, 76, 0, 77, 78, 0, 0, 79, 80, 0, 81, 62, 0, 82, + 83, 0, 0, 84, 85, 86, 0, 0, 0, 87, 0, 88, 0, 0, 51, 89, + 51, 0, 90, 0, 91, 0, 0, 0, 80, 0, 0, 0, 92, 93, 0, 94, + 95, 96, 97, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 98, 99, 0, + 0, 0, 0, 0, 0,100, 0, 0, 0, 0, 0,101,102, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,103, 0, 0,104, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,105,106, 0, 0,107, 0, 0, 0, 0, 0, 0, + 108, 0,109, 0,102, 0, 0, 0, 0, 0,110,111, 0, 0, 0, 0, + 0, 0, 0,112, 0, 0, 0, 0, 0, 0, 0,113, 0,114, 0, 0, + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 0, 0, 0, + 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, 14, 15, 0, 16, + 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, 0, 0, 22, 23, + 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, 28, 29, 30, 31, + 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, 33, 0, 0, 0, + 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, 0, 39, + 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 43, 0, 44, + 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 48, + 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, 0, 53, 0, 0, + 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, 0, 0, 57, 58, + 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, 52, 0, 62, 63, + 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, 0, 68, 69, 70, + 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, 0, 0, 0, 79, + 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, 0, 0, 77, 82, + 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, 52, 0, 1, 78, + 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, 57, 0, 0, 0, + 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, 0, 91, 0, 0, + 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, 0, 0, 0, 94, + 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99, 0, + 0, 0, 0,100,101, 93, 0, 0,102, 0, 0, 0, 84, 0, 0,103, + 0, 0, 0,104,105, 0, 0,106,107, 0, 0, 0, 0, 0, 0,108, + 0, 0,109, 0, 0, 0, 0,110, 33, 0,111,112,113, 35, 0, 0, + 114, 0, 0, 0,115, 0, 0, 0, 0, 0, 0,116, 0, 0,117, 0, + 0, 0, 0,118, 88, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52, + 119, 0, 0, 0, 0,120, 0, 0,121, 0, 0, 0, 0,119, 0, 0, + 122, 0, 0, 0, 0, 0, 0,123, 0, 0, 0,124, 0, 0, 0,125, + 0,126, 0, 0, 0, 0,127,128,129, 0,130, 0,131, 0, 0, 0, + 132,133,134, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,135, 0, + 0, 0,136, 0, 0,137, 0, 0,138, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, 4, 8, 9, 10, + 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, 19, 1, 0, 0, + 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, 29, 30, 0, 0, + 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, 37, 0, 0, 0, + 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, 43, 36, 44, 45, + 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, 0, 38, 48, 1, + 1, 49, 49, 50, 0, 0, 51, 0, 0, 0, 52, 1, 0, 0, 38, 14, + 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, 0, 0, 0, 55, + 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, 0, 60, 0, 0, + 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, 0, 0, 65, 0, + 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 69, 70, 0, + 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, 0, 78, 79, 0, + 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, 0, 62, 0, 0, + 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 19, 84, 0, + 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, 36, 10, 21, 87, + 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 88, 0, + 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 87, 9, 12, 4, + 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, 93, 1, 1, 1, + 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, 58, 0, 0, 0, + 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0,101,102, + 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, 0, 0, + 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, 78, 0, 0, 0, + 105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, 1, 14, 4, 12, + 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, 0, 0,109, 61, + 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 19, 58, 0, 0, 0, 51, + 0,111, 14, 52,112, 41, 0, 0, 62, 0, 0, 61, 0, 0,113, 0, + 87, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, 0,113, 0, 0, + 0, 0,114, 0, 0, 0, 78, 55, 0, 38, 1, 58, 1, 58, 0, 0, + 63, 89, 0, 0,115, 0, 0, 0, 55, 0, 0, 0, 0,115, 0, 0, + 0, 0, 61, 0, 0, 0, 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, + 89, 80, 0, 0, 79, 0, 0, 0, 8, 91, 0, 0, 1, 87, 0, 0, + 116, 0, 0, 0, 0, 0, 0,117, 0,118,119,120,121, 0,104, 4, + 122, 49, 23, 0, 0, 0, 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, + 1, 1, 39, 1, 48,105, 87, 0, 0, 0, 0, 1, 0, 0, 0,123, + 4,122, 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230, + 230,232,220,220,220,220,232,216,220,220,220,220,220,202,202,220, + 220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220, + 220,230,230,230,230,240,230,220,220,220,230,230,230,220,220, 0, + 230,230,230,220,220,220,220,230,232,220,220,230,233,234,234,233, + 234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230, + 230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0, + 230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, + 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, + 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, + 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230,220,230, + 220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230, + 230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, + 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230, + 230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107, + 118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, + 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130, + 130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, + 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, + 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220, + 230,220, 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0, + 230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230, + 230,230,232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, + 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228, + 232,222,224,224, 0, 8, 8, 0, 0, 0, 0,220,230, 0,230,230, + 220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, + 0,230,220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, + 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, + 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0, + 220,220,220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 17, + 17, 17, 17, 33, 17, 17, 17, 19, 17, 17, 17, 17, 20,101, 17,113, + 129,169, 17, 27, 28, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,237, 0, 1, 2, 2, 0, 3, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 5, 0, 0, 0, 0, 6, 7, 8, 9, 0, 0, 0, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, - 0, 0, 21, 22, 0, 0, 0, 0, 23, 24, 25, 26, 0, 27, 0, 28, - 29, 30, 31, 32, 0, 0, 0, 0, 0, 0, 0, 33, 34, 35, 36, 0, - 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, - 0, 0, 0, 0, 1, 2, 40, 41, 0, 1, 2, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, - 0, 0, 3, 4, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, - 0, 0, 7, 1, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, - 0, 0, 10, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 11, 12, - 0, 13, 0, 14, 15, 16, 0, 0, 0, 0, 0, 1, 17, 18, 0, 19, - 7, 1, 0, 0, 0, 20, 20, 7, 20, 20, 20, 20, 20, 20, 20, 8, - 21, 0, 22, 0, 7, 23, 24, 0, 20, 20, 25, 0, 0, 0, 26, 27, - 1, 7, 20, 20, 20, 20, 20, 1, 28, 29, 30, 31, 0, 0, 20, 0, - 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 20, 20, - 20, 1, 0, 0, 8, 21, 32, 4, 0, 10, 0, 33, 7, 20, 20, 20, - 0, 0, 0, 0, 8, 34, 34, 35, 36, 34, 37, 0, 38, 1, 20, 20, - 0, 0, 39, 0, 1, 1, 0, 8, 21, 1, 20, 0, 0, 0, 1, 0, - 0, 40, 1, 1, 0, 0, 8, 21, 0, 1, 0, 1, 0, 1, 0, 0, - 0, 0, 26, 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 7, 20, 41, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 0, 42, 43, 44, 0, 45, - 0, 8, 21, 0, 0, 0, 0, 0, 0, 0, 0, 46, 7, 1, 10, 1, - 0, 0, 0, 1, 20, 20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 26, 34, 9, 0, 0, 20, 20, 1, 20, 20, 0, 0, 0, 0, 0, - 0, 0, 26, 21, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, - 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 9, 10, 11, 11, 11, 11, 12, 13, 13, 13, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 13, 22, 13, 13, 13, 13, 23, 24, 24, 25, 26, 13, 13, - 13, 27, 28, 29, 13, 30, 31, 32, 33, 34, 35, 36, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 13, 42, 7, 7, 43, 7, - 44, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,237, 0, 1, 2, 2, + 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 7, 8, + 9, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 21, 22, 0, 0, 0, 0, + 23, 24, 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 0, 0, 0, 0, + 0, 0, 0, 33, 34, 35, 36, 0, 0, 0, 0, 0, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, 1, 2, 40, 41, + 0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 5, 0, + 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, + 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 10, + 0, 0, 0, 0, 0, 0, 11, 12, 0, 13, 0, 14, 15, 16, 0, 0, + 0, 0, 0, 1, 17, 18, 0, 19, 7, 1, 0, 0, 0, 20, 20, 7, + 20, 20, 20, 20, 20, 20, 20, 8, 21, 0, 22, 0, 7, 23, 24, 0, + 20, 20, 25, 0, 0, 0, 26, 27, 1, 7, 20, 20, 20, 20, 20, 1, + 28, 29, 30, 31, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 10, 0, + 0, 0, 0, 0, 0, 0, 20, 20, 20, 1, 0, 0, 8, 21, 32, 4, + 0, 10, 0, 33, 7, 20, 20, 20, 0, 0, 0, 0, 8, 34, 34, 35, + 36, 34, 37, 0, 38, 1, 20, 20, 0, 0, 39, 0, 1, 1, 0, 8, + 21, 1, 20, 0, 0, 0, 1, 0, 0, 40, 1, 1, 0, 0, 8, 21, + 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 26, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 21, 7, 20, 41, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 21, 0, 42, 43, 44, 0, 45, 0, 8, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 46, 7, 1, 10, 1, 0, 0, 0, 1, 20, 20, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 34, 9, 0, 0, 20, 20, + 1, 20, 20, 0, 0, 0, 0, 0, 0, 0, 26, 21, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 47, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, + 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 13, 22, 13, 13, 13, + 13, 23, 24, 24, 25, 26, 13, 13, 13, 27, 28, 29, 13, 30, 31, 32, + 33, 34, 35, 36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, + 7, 41, 13, 42, 7, 7, 43, 7, 44, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, @@ -1740,420 +1715,420 @@ _hb_ucd_u8[18260] = 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 45, 0, 0, 1, - 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 32, 33, 34, 35, 36, 37, 37, 37, 37, 37, 38, 39, 40, 41, 42, - 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 2, 2, 53, 54, 55, 56, - 57, 58, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, 59, 59, 61, 61, - 59, 59, 59, 59, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, - 74, 75, 76, 77, 78, 59, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, + 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, + 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 59, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 79, 70, 70, 70, 70, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 81, 82, 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 79, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 80, 80, 80, 81, + 82, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 95, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 70, 70, 97, 98, 99,100,101,101,102,103,104,105,106,107,108,109, - 110,111, 96,112,113,114,115,116,117,118,119,119,120,121,122,123, - 124,125,126,127,128,129,130,131,132, 96,133,134,135,136,137,138, - 139,140,141,142,143, 96,144,145, 96,146,147,148,149, 96,150,151, - 152,153,154,155,156, 96,157,158,159,160, 96,161,162,163,164,164, - 164,164,164,164,164,165,166,164,167, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,168,169,169, - 169,169,169,169,169,169,170, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96,171,171,171,171,172, 96, 96, 96,173,173, - 173,173,174,175,176,177, 96, 96, 96, 96,178,179,180,181,182,182, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, 97, 98, 99,100,101,101, + 102,103,104,105,106,107,108,109,110,111, 96,112,113,114,115,116, + 117,118,119,119,120,121,122,123,124,125,126,127,128,129,130,131, + 132, 96,133,134,135,136,137,138,139,140,141,142,143, 96,144,145, + 96,146,147,148,149, 96,150,151,152,153,154,155,156, 96,157,158, + 159,160, 96,161,162,163,164,164,164,164,164,164,164,165,166,164, + 167, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96,168,169,169,169,169,169,169,169,169,170, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,171,171, + 171,171,172, 96, 96, 96,173,173,173,173,174,175,176,177, 96, 96, + 96, 96,178,179,180,181,182,182,182,182,182,182,182,182,182,182, 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, - 182,182,182,182,182,182,182,182,182,182,182,182,182,183,182,182, - 182,182,182,182,184,184,184,185,186, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,187,188,189, - 190,191,191,192, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96,193,194, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,195,196, 59,197, - 198,199,200,201,202, 96,203,204,205, 59, 59,206, 59,207,208,208, - 208,208,208,209, 96, 96, 96, 96, 96, 96, 96, 96,210, 96,211,212, - 213, 96, 96,214, 96, 96, 96,215, 96, 96, 96, 96, 96,216,217,218, - 219, 96, 96, 96, 96, 96,220,221,222, 96,223,224, 96, 96,225,226, - 59,227,228, 96, 59, 59, 59, 59, 59, 59, 59,229,230,231,232,233, - 59, 59,234,235, 59,236, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,237, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,238, 70,239, 70, + 182,182,182,182,182,183,182,182,182,182,182,182,184,184,184,185, + 186, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96,187,188,189,190,191,191,192, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,193,194, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96,195,196, 59,197,198,199,200,201,202, 96,203,204, + 205, 59, 59,206, 59,207,208,208,208,208,208,209, 96, 96, 96, 96, + 96, 96, 96, 96,210, 96,211,212,213, 96, 96,214, 96, 96, 96,215, + 96, 96, 96, 96, 96,216,217,218,219, 96, 96, 96, 96, 96,220,221, + 222, 96,223,224, 96, 96,225,226, 59,227,228, 96, 59, 59, 59, 59, + 59, 59, 59,229,230,231,232,233, 59, 59,234,235, 59,236, 96, 96, + 96, 96, 96, 96, 96, 96, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70,237, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70,238, 70,239, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,240, 70, 70, 70, 70, - 70, 70, 70, 70, 70,241, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, - 70, 70,242, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, - 70, 70, 70, 70,243, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70,244, 96, 96, 96, 96, 96, 96, 96, 96,245, 96, - 246,247, 0, 1, 2, 2, 0, 1, 2, 2, 2, 3, 4, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, - 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, - 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, - 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, - 9, 9, 0, 9, 9, 9, 2, 2, 9, 9, 9, 9, 0, 9, 2, 2, - 2, 2, 9, 0, 9, 0, 9, 9, 9, 2, 9, 2, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 2, 4, 4, 4, 2, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, - 2, 2, 2, 2, 2, 2, 14, 14, 14, 2, 2, 2, 2, 14, 14, 14, - 14, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, - 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, - 3, 3, 3, 3, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 2, 37, 37, 37, 37, 2, 2, 37, 37, 37, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, - 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 2, 2, 95, 2, 37, 37, 37, 2, 2, 2, 2, 2, 3, 3, - 3, 3, 3, 3, 3, 2, 3, 3, 2, 2, 2, 2, 2, 2, 3, 3, - 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, - 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, - 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, - 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, - 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, - 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, - 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, - 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, - 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, - 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, - 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, - 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, - 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, - 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, - 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, - 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, - 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, - 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, - 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, - 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, - 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, - 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, - 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, - 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, - 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, - 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, - 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, - 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, - 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, - 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, - 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, - 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 23, 2, 2, 23, 23, - 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, - 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, - 2, 2, 2, 16, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 2, 16, - 16, 16, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 2, 2, - 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 2, 36, - 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36, 36, - 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, 2, - 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, - 2, 2, 2, 2, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 2, 2, 2, 2, 0, 24, 24, 24, 24, 2, 2, 2, 2, 2, 18, - 18, 2, 18, 2, 18, 18, 18, 18, 18, 2, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, - 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, - 18, 18, 18, 18, 18, 2, 18, 18, 2, 2, 18, 18, 18, 18, 25, 25, - 25, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 2, 2, 2, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, - 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 2, 2, 2, 2, 33, 33, - 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 2, 8, 2, 2, 2, 2, 2, 8, 2, 2, 8, 8, - 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, - 30, 30, 30, 30, 30, 2, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, - 30, 30, 30, 2, 2, 2, 30, 30, 2, 2, 2, 2, 2, 2, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 28, 28, - 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 2, 2, 2, 2, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 2, 2, 31, 31, - 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 2, 2, 2, 2, 2, 2, 32, 2, - 2, 2, 2, 2, 2, 2, 32, 32, 32, 2, 2, 2, 2, 2, 28, 28, - 28, 28, 28, 28, 2, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 2, 48, 48, 48, 48, 2, 2, 2, 2, 48, 2, - 2, 2, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 2, 2, 52, 52, 52, 52, 52, 2, 2, 2, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 2, 2, 2, 2, 58, 58, - 2, 2, 2, 2, 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 2, 2, 54, 54, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 2, 91, 91, - 91, 91, 91, 2, 2, 91, 91, 91, 2, 2, 2, 2, 2, 2, 91, 91, - 91, 91, 91, 91, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 2, 2, 2, 62, 62, - 62, 62, 62, 62, 62, 2, 76, 76, 76, 76, 76, 76, 76, 76, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 2, 2, 2, 2, 2, 2, - 2, 2, 93, 93, 93, 93, 70, 70, 70, 70, 70, 70, 70, 70, 2, 2, - 2, 70, 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 73, 73, - 73, 73, 73, 73, 73, 73, 6, 2, 2, 2, 2, 2, 2, 2, 8, 8, - 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 19, 19, - 19, 19, 19, 19, 9, 9, 9, 9, 9, 6, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 9, 9, 9, 9, - 9, 19, 19, 19, 19, 19, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, 9, 9, 2, 2, 2, 9, - 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, - 9, 9, 2, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 2, 2, - 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 19, - 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, - 0, 0, 0, 0, 0, 2, 19, 19, 19, 19, 19, 2, 2, 2, 0, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 9, 0, 0, 0, 19, 19, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 2, 2, 2, 2, 0, 0, - 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 0, 0, - 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 56, 56, - 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, - 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, - 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, - 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 1, 1, 1, 1, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 2, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 0, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 0, 0, 17, 17, 17, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, - 17, 17, 17, 17, 17, 0, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 86, 86, - 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, - 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, - 19, 2, 2, 2, 2, 2, 19, 19, 2, 19, 2, 19, 19, 19, 19, 19, - 2, 2, 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 2, 2, 2, 0, 0, - 2, 2, 2, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, - 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, - 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, - 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 2, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, - 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, - 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 2, 2, 30, 30, 30, 30, 30, 30, 2, 19, 19, - 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, - 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 2, 2, 87, 87, - 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, - 2, 12, 12, 12, 12, 12, 13, 13, 2, 2, 2, 2, 2, 2, 19, 19, - 19, 19, 19, 19, 19, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, - 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, - 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, 3, - 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 0, 0, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, - 2, 2, 2, 2, 2, 3, 1, 1, 1, 1, 1, 1, 6, 6, 0, 0, - 0, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, - 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 2, 2, - 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 2, 2, 2, 2, 0, - 0, 0, 0, 0, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, - 49, 2, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 2, 2, 49, 49, - 49, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, - 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 2, 2, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 2, 2, 2, 2, 2, 2, 2, 1, 0, - 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 42, 42, 42, 41, 41, - 41, 41, 41, 41, 41, 41, 41, 41, 41, 2, 2, 2, 2, 2,118,118, - 118,118,118,118,118,118,118,118,118, 2, 2, 2, 2, 2, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 2, 53, 59, 59, - 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, 59, 59, - 59, 59, 59, 59, 2, 2, 40, 40, 40, 40, 40, 40, 40, 40, 51, 51, - 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 2, 2, 50, 50, 2, 2, 2, 2, 2, 2,135,135, - 135,135,135,135,135,135,135,135,135,135, 2, 2, 2, 2,106,106, - 106,106,106,106,106,106,104,104,104,104,104,104,104,104,104,104, - 104,104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,104,161,161, - 161,161,161,161,161,161,161,161,161, 2,161,161,161,161,161,161, - 161, 2,161,161, 2,161,161,161, 2,161,161,161,161,161,161,161, - 2,161,161, 2, 2, 2,110,110,110,110,110,110,110,110,110,110, - 110,110,110,110,110, 2,110,110,110,110,110,110, 2, 2, 19, 19, - 19, 19, 19, 19, 2, 19, 19, 2, 19, 19, 19, 19, 19, 19, 47, 47, - 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, - 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 2, 81,120,120,120,120,120,120,120,120,116,116, - 116,116,116,116,116,116,116,116,116,116,116,116,116, 2, 2, 2, - 2, 2, 2, 2, 2,116,128,128,128,128,128,128,128,128,128,128, - 128, 2,128,128, 2, 2, 2, 2, 2,128,128,128,128,128, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 2, 2, 2, 2, 2, 72, 98, 98, - 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2, - 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 57, 57, - 57, 57, 2, 57, 57, 2, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, - 57, 57, 2, 57, 57, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57, - 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 2, 2, 2, 2, 88, 88, - 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,112,112, - 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, - 2,112,112,112,112,112, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 82, 82, - 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2,122,122, - 122,122,122,122,122,122,122,122, 2, 2, 2, 2, 2, 2, 2,122, - 122,122,122, 2, 2, 2, 2,122,122,122,122,122,122,122, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 2, 2, 2, 2, 2, 2, 2,130,130, - 130,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2, - 130,130,130,130,130,130,144,144,144,144,144,144,144,144,144,144, - 2, 2, 2, 2, 2, 2,156,156,156,156,156,156,156,156,156,156, - 2,156,156,156, 2, 2,156,156, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 3, 3, 3,147,147,147,147,147,147,147,147,148,148, - 148,148,148,148,148,148,148,148, 2, 2, 2, 2, 2, 2,158,158, - 158,158,158,158,158,158,158,158, 2, 2, 2, 2, 2, 2,153,153, - 153,153,153,153,153,153,153,153,153,153, 2, 2, 2, 2,149,149, - 149,149,149,149,149,149,149,149,149,149,149,149,149, 2, 94, 94, - 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, - 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, 2, 2, 2, 94, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 85, 2, 2,101,101,101,101,101,101,101,101,101, 2, - 2, 2, 2, 2, 2, 2,101,101, 2, 2, 2, 2, 2, 2, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 2, 96, 96,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111, 2,100,100, - 100,100,100,100,100,100, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 2, 2, 2,108,108,108,108,108,108,108,108,108,108, - 2,108,108,108,108,108,108,108, 2, 2, 2, 2, 2, 2,129,129, - 129,129,129,129,129, 2,129, 2,129,129,129,129, 2,129,129,129, - 129,129,129,129,129,129,129,129,129,129,129,129, 2,129,129,129, - 2, 2, 2, 2, 2, 2,109,109,109,109,109,109,109,109,109,109, - 109, 2, 2, 2, 2, 2,109,109, 2, 2, 2, 2, 2, 2,107,107, - 107,107, 2,107,107,107,107,107,107,107,107, 2, 2,107,107, 2, - 2,107,107,107,107,107,107,107,107,107,107,107,107,107,107, 2, - 107,107,107,107,107,107,107, 2,107,107, 2,107,107,107,107,107, - 2, 1,107,107,107,107,107, 2, 2,107,107,107, 2, 2,107, 2, - 2, 2, 2, 2, 2,107, 2, 2, 2, 2, 2,107,107,107,107,107, - 107,107, 2, 2,107,107,107,107,107,107,107, 2, 2, 2,137,137, - 137,137,137,137,137,137,137,137,137,137, 2,137,137,137,137,137, - 2, 2, 2, 2, 2, 2,124,124,124,124,124,124,124,124,124,124, - 2, 2, 2, 2, 2, 2,123,123,123,123,123,123,123,123,123,123, - 123,123,123,123, 2, 2,114,114,114,114,114,114,114,114,114,114, - 114,114,114, 2, 2, 2,114,114, 2, 2, 2, 2, 2, 2, 32, 32, - 32, 32, 32, 2, 2, 2,102,102,102,102,102,102,102,102,102,102, - 2, 2, 2, 2, 2, 2,126,126,126,126,126,126,126,126,126,126, - 126, 2, 2,126,126,126,126,126,126,126, 2, 2, 2, 2,126,126, - 126,126,126,126,126, 2,142,142,142,142,142,142,142,142,142,142, - 142,142, 2, 2, 2, 2,125,125,125,125,125,125,125,125,125,125, - 125, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,125,154,154, - 154,154,154,154,154, 2, 2,154, 2, 2,154,154,154,154,154,154, - 154,154, 2,154,154, 2,154,154,154,154,154,154,154,154,154,154, - 154,154,154,154, 2,154,154, 2, 2,154,154,154,154,154,154,154, - 2, 2, 2, 2, 2, 2,150,150,150,150,150,150,150,150, 2, 2, - 150,150,150,150,150,150,150,150,150,150,150, 2, 2, 2,141,141, - 141,141,141,141,141,141,140,140,140,140,140,140,140,140,140,140, - 140, 2, 2, 2, 2, 2,121,121,121,121,121,121,121,121,121, 2, - 2, 2, 2, 2, 2, 2, 7, 7, 2, 2, 2, 2, 2, 2,133,133, - 133,133,133,133,133,133,133, 2,133,133,133,133,133,133,133,133, - 133,133,133,133,133, 2,133,133,133,133,133,133, 2, 2,133,133, - 133,133,133, 2, 2, 2,134,134,134,134,134,134,134,134, 2, 2, - 134,134,134,134,134,134, 2,134,134,134,134,134,134,134,134,134, - 134,134,134,134,134, 2,138,138,138,138,138,138,138, 2,138,138, - 2,138,138,138,138,138,138,138,138,138,138,138,138,138, 2, 2, - 138, 2,138,138, 2,138,138,138, 2, 2, 2, 2, 2, 2,143,143, - 143,143,143,143, 2,143,143, 2,143,143,143,143,143,143,143,143, - 143,143,143,143,143,143,143,143,143,143,143,143,143, 2,143,143, - 2,143,143,143,143,143,143, 2, 2, 2, 2, 2, 2, 2,143,143, - 2, 2, 2, 2, 2, 2,145,145,145,145,145,145,145,145,145, 2, - 2, 2, 2, 2, 2, 2,163,163,163,163,163,163,163,163,163, 2, - 163,163,163,163,163,163,163,163,163, 2, 2, 2,163,163,163,163, - 2, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 22, 22, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 2, 2, 2, 2, 2, 2, 63, 63, - 63, 63, 63, 63, 63, 2, 63, 63, 63, 63, 63, 2, 2, 2, 63, 63, - 63, 63, 2, 2, 2, 2,157,157,157,157,157,157,157,157,157,157, - 157, 2, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 2, 2,127,127,127,127,127,127,127,127,127,127, - 127,127,127,127,127, 2, 79, 2, 2, 2, 2, 2, 2, 2,115,115, - 115,115,115,115,115,115,115,115,115,115,115,115,115, 2,115,115, - 2, 2, 2, 2,115,115,159,159,159,159,159,159,159,159,159,159, - 159,159,159,159,159, 2,159,159, 2, 2, 2, 2, 2, 2,103,103, - 103,103,103,103,103,103,103,103,103,103,103,103, 2, 2,119,119, - 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,119,119, - 2,119,119,119,119,119, 2, 2, 2, 2, 2,119,119,119,146,146, - 146,146,146,146,146,146,146,146,146, 2, 2, 2, 2, 2, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99, 2, 2, - 2, 2, 2, 2, 2, 99,136,139, 13, 13,155, 2, 2, 2,136,136, - 136,136,136,136,136,136,155,155,155,155,155,155,155,155,155,155, - 155,155,155,155, 2, 2,136, 2, 2, 2, 2, 2, 2, 2, 17, 17, - 17, 17, 2, 17, 17, 17, 17, 17, 17, 17, 2, 17, 17, 2, 17, 15, - 15, 15, 15, 15, 15, 15, 17, 17, 17, 2, 2, 2, 2, 2, 2, 2, - 15, 2, 2, 2, 2, 2, 15, 15, 15, 2, 2, 17, 2, 2, 2, 2, - 2, 2, 17, 17, 17, 17,139,139,139,139,139,139,139,139,139,139, - 139,139, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, - 105, 2, 2, 2, 2, 2,105,105,105,105,105, 2, 2, 2,105, 2, - 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 1, 1, - 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 0, - 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0,131,131, - 131,131,131,131,131,131,131,131,131,131, 2, 2, 2, 2, 2, 2, - 2,131,131,131,131,131, 2,131,131,131,131,131,131,131, 2, 2, - 2, 2, 2, 19, 19, 19, 56, 56, 56, 56, 56, 56, 56, 2, 56, 2, - 2, 56, 56, 56, 56, 56, 56, 56, 2, 56, 56, 2, 56, 56, 56, 56, - 56, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 6,151,151,151,151,151,151,151,151,151,151, - 151,151,151, 2, 2, 2,151,151,151,151,151,151, 2, 2,151,151, - 2, 2, 2, 2,151,151,160,160,160,160,160,160,160,160,160,160, - 160,160,160,160,160, 2,152,152,152,152,152,152,152,152,152,152, - 2, 2, 2, 2, 2,152,164,164,164,164,164,164,164,164,164,164, - 2, 2, 2, 2, 2, 2, 30, 30, 30, 30, 2, 30, 30, 2,113,113, - 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113, - 113,113,113,113,113, 2,132,132,132,132,132,132,132,132,132,132, - 132,132, 2, 2, 2, 2,132,132, 2, 2, 2, 2,132,132, 3, 3, - 3, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, - 2, 3, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 3, 2, 3, - 2, 3, 2, 3, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, - 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, - 2, 2, 2, 2, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 13, 2, - 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, 0, - 2, 2, 2, 2, 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 70, 70, 70,240, 70, 70, 70, 70, 70, 70, 70, 70, 70,241, 96, 96, + 96, 96, 96, 96, 96, 96, 70, 70, 70, 70,242, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 70, 70, 70, 70, 70, 70,243, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,244, 96, 96, + 96, 96, 96, 96, 96, 96,245, 96,246,247, 0, 1, 2, 2, 0, 1, + 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 19, 19, + 19, 19, 19, 19, 19, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 19, + 19, 19, 19, 0, 0, 0, 0, 0, 26, 26, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, + 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, + 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 2, 9, 9, 9, 9, 9, 9, 9, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 1, 1, 6, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, + 4, 2, 2, 4, 4, 4, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2, 2, 2, 2, 14, 14, + 14, 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 2, 2, 2, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 0, 0, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 2, 37, 37, 37, + 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 2, 2, 2, 2, 2, 2, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 2, 2, 90, 90, 90, 90, 90, 90, 90, 2, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 2, 2, 95, 2, 37, 37, + 37, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, + 2, 2, 2, 2, 2, 2, 3, 3, 0, 3, 3, 3, 3, 3, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 7, 7, + 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, + 5, 5, 5, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 2, 2, 2, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 2, + 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, + 2, 2, 5, 5, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 2, 2, 11, 11, 11, 2, 11, 11, 11, 11, 11, + 11, 2, 2, 2, 2, 11, 11, 2, 2, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 2, 11, 11, 2, 11, 11, 2, 2, 11, 2, 11, 11, 11, 2, + 2, 11, 11, 11, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, + 11, 11, 11, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 2, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 2, 10, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, 10, 10, + 2, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 10, 10, + 10, 10, 2, 2, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 10, 2, 21, 21, 21, 2, 21, 21, 21, 21, 21, + 21, 21, 21, 2, 2, 21, 21, 2, 2, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 21, 21, 2, + 2, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 2, 2, + 2, 2, 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, 2, + 22, 22, 2, 22, 22, 22, 22, 22, 22, 2, 2, 2, 22, 22, 22, 2, + 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 22, 2, 22, 22, 2, 2, + 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 2, 2, 2, 2, 22, 22, 22, 2, 2, 2, 2, 2, 2, 22, 2, 2, + 2, 2, 2, 2, 22, 22, 22, 22, 22, 2, 2, 2, 2, 2, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 2, 23, 23, 23, 2, + 23, 23, 23, 23, 23, 23, 23, 23, 2, 2, 23, 23, 23, 23, 23, 2, + 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 2, 23, 23, + 23, 2, 2, 23, 2, 2, 23, 23, 23, 23, 2, 2, 23, 23, 2, 2, + 2, 2, 2, 2, 2, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 2, 16, 16, 16, 2, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 2, 16, 16, 16, 16, 16, 2, 2, 16, 16, 16, 16, 16, 2, + 16, 16, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 16, 2, 16, 16, + 16, 16, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, 2, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, + 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, + 20, 20, 2, 2, 20, 20, 2, 36, 36, 36, 2, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 36, + 36, 36, 36, 2, 36, 2, 2, 2, 2, 2, 2, 2, 36, 36, 2, 2, + 36, 36, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 2, 2, 2, 2, 0, 24, 24, + 24, 24, 2, 2, 2, 2, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, + 18, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 2, 18, 18, + 18, 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 18, 18, + 2, 2, 18, 18, 18, 18, 25, 25, 25, 25, 25, 25, 25, 25, 2, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 2, 2, 2, 25, 25, + 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 0, 0, 0, 0, 25, + 25, 2, 2, 2, 2, 2, 33, 33, 33, 33, 33, 33, 33, 33, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 8, 2, 2, + 2, 2, 2, 8, 2, 2, 8, 8, 8, 0, 8, 8, 8, 8, 12, 12, + 12, 12, 12, 12, 12, 12, 30, 30, 30, 30, 30, 30, 30, 30, 30, 2, + 30, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, + 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 2, 2, 2, 30, 30, + 2, 2, 2, 2, 2, 2, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 2, 2, 28, 28, 28, 28, 28, 28, 28, 28, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 2, 2, 2, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, + 2, 2, 2, 2, 2, 2, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 2, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 2, 2, 2, 2, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 2, 46, 46, 46, 2, + 46, 46, 2, 2, 2, 2, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 2, 2, 31, 31, 2, 2, 2, 2, 2, 2, 32, 32, + 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 2, 2, 2, 2, 2, 2, 32, 2, 2, 2, 2, 2, 2, 2, 32, 32, + 32, 2, 2, 2, 2, 2, 28, 28, 28, 28, 28, 28, 2, 2, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 2, 48, 48, + 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 48, 48, 48, 48, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 2, 2, 52, 52, + 52, 52, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 2, 2, 2, 2, 58, 58, 2, 2, 2, 2, 2, 2, 58, 58, + 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 2, 2, 91, 91, 91, + 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 2, 76, 76, + 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, + 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 2, + 2, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, + 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, + 9, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, 2, 9, 9, 9, + 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, + 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 2, 19, 19, + 19, 19, 19, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 27, 27, + 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, 56, 56, 56, 56, 56, 56, 56, 56, 55, 55, + 55, 55, 2, 2, 2, 2, 2, 55, 55, 55, 55, 55, 55, 55, 61, 61, + 61, 61, 61, 61, 61, 61, 2, 2, 2, 2, 2, 2, 2, 61, 61, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 2, 0, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, + 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, + 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, + 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, + 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 0, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, + 39, 39, 39, 39, 39, 2, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 2, 2, 2, 2, 79, 79, + 79, 79, 79, 79, 79, 79, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, + 0, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 19, 19, + 2, 19, 2, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 2, 2, 2, + 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 65, 65, + 65, 65, 65, 65, 65, 65, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 2, 2, 75, 75, 75, 75, + 2, 2, 2, 2, 2, 2, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 74, 12, 12, + 12, 12, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, + 33, 33, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 2, 68, 68, 68, 68, 68, 68, 2, 2, 68, 68, + 2, 2, 68, 68, 68, 68, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 2, 2, 2, 2, 2, 2, 2, 2, 92, 92, 92, 92, 92, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 2, 2, 30, + 30, 30, 30, 30, 30, 2, 19, 19, 19, 0, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 87, 87, + 87, 87, 87, 87, 2, 2, 87, 87, 2, 2, 2, 2, 2, 2, 12, 12, + 12, 12, 2, 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 13, 13, + 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, + 2, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 2, 14, 14, 14, 14, 14, 2, 14, 2, 14, 14, + 2, 14, 14, 2, 14, 14, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 2, 2, + 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, + 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 3, 3, + 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, + 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, + 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, + 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, + 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, + 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, + 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, + 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, + 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, + 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,110,110, + 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, + 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, + 19, 19, 19, 19, 19, 19, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81,120,120, + 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,128,128, + 128,128,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, + 2,128,128,128,128,128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, + 97, 97, 97, 97, 97, 97, 2, 2, 2, 2, 97, 97, 97, 97, 2, 2, + 97, 97, 97, 97, 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 2, + 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 2, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 57, 2, + 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88,117,117, + 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112, + 112,112,112,112,112, 2, 2, 2, 2,112,112,112,112,112, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, + 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 2, 2, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 2, 2, 2, 2, 2,122,122,122,122,122,122,122,122,122,122, + 2, 2, 2, 2, 2, 2, 2,122,122,122,122, 2, 2, 2, 2,122, + 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, + 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, + 130, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,144,144, + 144,144,144,144,144,144,144,144, 2, 2, 2, 2, 2, 2,156,156, + 156,156,156,156,156,156,156,156, 2,156,156,156, 2, 2,156,156, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,147,147, + 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148, + 2, 2, 2, 2, 2, 2,158,158,158,158,158,158,158,158,158,158, + 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, + 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, 2, + 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 85, 2, 2,101,101, + 101,101,101,101,101,101,101, 2, 2, 2, 2, 2, 2, 2,101,101, + 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 2, 96, 96,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111, 2,100,100,100,100,100,100,100,100, 2, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,108,108, + 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, + 2, 2, 2, 2, 2, 2,129,129,129,129,129,129,129, 2,129, 2, + 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129, 2,129,129,129, 2, 2, 2, 2, 2, 2,109,109, + 109,109,109,109,109,109,109,109,109, 2, 2, 2, 2, 2,109,109, + 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, + 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, + 107,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, 2, + 107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, 2, + 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, + 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, + 107,107,107, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, + 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2,124,124, + 124,124,124,124,124,124,124,124, 2, 2, 2, 2, 2, 2,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123, 2, 2,114,114, + 114,114,114,114,114,114,114,114,114,114,114, 2, 2, 2,114,114, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 2, 2, 2,102,102, + 102,102,102,102,102,102,102,102, 2, 2, 2, 2, 2, 2,126,126, + 126,126,126,126,126,126,126,126,126, 2, 2,126,126,126,126,126, + 126,126, 2, 2, 2, 2,126,126,126,126,126,126,126, 2,142,142, + 142,142,142,142,142,142,142,142,142,142, 2, 2, 2, 2,125,125, + 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, + 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, + 2,154,154,154,154,154,154,154, 2, 2, 2, 2, 2, 2,150,150, + 150,150,150,150,150,150, 2, 2,150,150,150,150,150,150,150,150, + 150,150,150, 2, 2, 2,141,141,141,141,141,141,141,141,140,140, + 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2,121,121, + 121,121,121,121,121,121,121, 2, 2, 2, 2, 2, 2, 2, 7, 7, + 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133, 2, + 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133, + 133,133,133,133, 2, 2,133,133,133,133,133, 2, 2, 2,134,134, + 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134, 2,134, + 134,134,134,134,134,134,134,134,134,134,134,134,134, 2,138,138, + 138,138,138,138,138, 2,138,138, 2,138,138,138,138,138,138,138, + 138,138,138,138,138,138, 2, 2,138, 2,138,138, 2,138,138,138, + 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, 2, + 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, + 143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, + 2, 2, 2, 2, 2, 2,143,143, 2, 2, 2, 2, 2, 2,145,145, + 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, + 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, + 163, 2, 2, 2,163,163,163,163, 2, 2, 2, 2, 2, 2, 86, 2, + 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, + 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, + 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2,127,127, + 127,127,127,127,127,127,127,127,127,127,127,127,127, 2, 79, 2, + 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, + 115,115,115,115,115, 2,115,115, 2, 2, 2, 2,115,115,159,159, + 159,159,159,159,159,159,159,159,159,159,159,159,159, 2,159,159, + 2, 2, 2, 2, 2, 2,103,103,103,103,103,103,103,103,103,103, + 103,103,103,103, 2, 2,119,119,119,119,119,119,119,119,119,119, + 119,119,119,119, 2, 2,119,119, 2,119,119,119,119,119, 2, 2, + 2, 2, 2,119,119,119,146,146,146,146,146,146,146,146,146,146, + 146, 2, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 2, 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 99,136,139, + 13, 13,155, 2, 2, 2,136,136,136,136,136,136,136,136,155,155, + 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2,136, 2, + 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 17, 17, 17, 17, 17, + 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 15, 15, 15, 15, 17, 17, + 17, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 15, 15, + 15, 2, 2, 17, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17,139,139, + 139,139,139,139,139,139,139,139,139,139, 2, 2, 2, 2,105,105, + 105,105,105,105,105,105,105,105,105, 2, 2, 2, 2, 2,105,105, + 105,105,105, 2, 2, 2,105, 2, 2, 2, 2, 2, 2, 2,105,105, + 2, 2,105,105,105,105, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, + 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0,131,131,131,131,131,131,131,131,131,131, + 131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131, + 131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, 56, + 56, 56, 56, 56, 56, 2, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56, + 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2, 6, 6, + 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6,151,151, + 151,151,151,151,151,151,151,151,151,151,151, 2, 2, 2,151,151, + 151,151,151,151, 2, 2,151,151, 2, 2, 2, 2,151,151,160,160, + 160,160,160,160,160,160,160,160,160,160,160,160,160, 2,152,152, + 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164, + 164,164,164,164,164,164,164,164, 2, 2, 2, 2, 2, 2, 30, 30, + 30, 30, 2, 30, 30, 2,113,113,113,113,113,113,113,113,113,113, + 113,113,113, 2, 2,113,113,113,113,113,113,113,113, 2,132,132, + 132,132,132,132,132,132,132,132,132,132, 2, 2, 2, 2,132,132, + 2, 2, 2, 2,132,132, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, + 3, 2, 3, 2, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 2, 2, + 3, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 2, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, + 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 15, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 0, 0, 0, 13, 2, 2, 2, 2, 2, 2, 2, 13, 13, + 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, + 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 16, 17, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 19, - 20, 9, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -2162,57 +2137,57 @@ _hb_ucd_u8[18260] = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 23, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, - 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, - 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, 31, 32, 0, 33, + 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, - 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, - 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, - 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, - 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, - 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, + 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, + 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, - 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, - 99,100,101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0, - 107, 0, 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, - 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124, - 125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 69, + 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, + 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0,108, 0,109, 0, + 110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,128,129,130,131,132,133,134,135,136,137,138,139, - 140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155, - 156,157, 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162,163, 0, - 0, 0, 0, 0, 0, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,165, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,166, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,167, + 118,119,120,121, 0,122,123,124,125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,168, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,129,130,131, + 132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147, + 148,149,150,151,152,153,154,155,156,157, 0, 0, 0,158,159,160, + 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,162,163, 0, 0, 0, 0, 0, 0, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,169,170, 0, 0, 0, 0,171, - 172, 0, 0, 0,173,174,175,176,177,178,179,180,181,182,183,184, - 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, - 201,202,203,204,205,206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 2, 3, 4, + 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,168, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,169,170, 0, 0, 0, 0,171,172, 0, 0, 0,173,174,175,176, + 177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192, + 193,194,195,196,197,198,199,200,201,202,203,204,205,206, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, }; static const uint16_t _hb_ucd_u16[9320] = @@ -2827,7 +2802,7 @@ _hb_ucd_gc (unsigned u) static inline uint_fast8_t _hb_ucd_ccc (unsigned u) { - return u<125259u?_hb_ucd_u8[9184+(((_hb_ucd_u8[8128+(((_hb_ucd_u8[7424+(((_hb_ucd_u8[7178+(u>>2>>3>>4)])<<4)+((u>>2>>3)&15u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u))]:0; + return u<125259u?_hb_ucd_u8[8792+(((_hb_ucd_u8[8236+(((_hb_ucd_u8[7776+(((_hb_ucd_u8[7424+(((_hb_ucd_u8[7178+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; } static inline unsigned _hb_ucd_b4 (const uint8_t* a, unsigned i) @@ -2837,24 +2812,24 @@ _hb_ucd_b4 (const uint8_t* a, unsigned i) static inline int_fast16_t _hb_ucd_bmg (unsigned u) { - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9932+(((_hb_ucd_u8[9812+(((_hb_ucd_b4(9684+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9540+(((_hb_ucd_u8[9420+(((_hb_ucd_b4(9292+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; } static inline uint_fast8_t _hb_ucd_sc (unsigned u) { - return u<918000u?_hb_ucd_u8[11454+(((_hb_ucd_u16[2040+(((_hb_ucd_u8[10718+(((_hb_ucd_u8[10268+(u>>3>>4>>4)])<<4)+((u>>3>>4)&15u))])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:2; + return u<918000u?_hb_ucd_u8[11062+(((_hb_ucd_u16[2040+(((_hb_ucd_u8[10326+(((_hb_ucd_u8[9876+(u>>3>>4>>4)])<<4)+((u>>3>>4)&15u))])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:2; } static inline uint_fast16_t _hb_ucd_dm (unsigned u) { - return u<195102u?_hb_ucd_u16[6008+(((_hb_ucd_u8[17460+(((_hb_ucd_u8[17078+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; + return u<195102u?_hb_ucd_u16[6008+(((_hb_ucd_u8[17068+(((_hb_ucd_u8[16686+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; } #elif !defined(HB_NO_UCD_UNASSIGNED) static const uint8_t -_hb_ucd_u8[17868] = +_hb_ucd_u8[14744] = { 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 11, 12, 13, 13, 13, 14, @@ -3436,51 +3411,54 @@ _hb_ucd_u8[17868] = 0,230,220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0, - 220,220,220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 17, - 17, 17, 17, 33, 17, 17, 17, 19, 17, 17, 17, 17, 20,101, 17,113, - 129,169, 17, 27, 28, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 220,220,220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 33, + 17, 49, 17, 17, 84, 97,135,145, 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,237, 0, 1, 2, 2, - 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 7, 8, - 9, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 21, 22, 0, 0, 0, 0, - 23, 24, 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 0, 0, 0, 0, - 0, 0, 0, 33, 34, 35, 36, 0, 0, 0, 0, 0, 37, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, 1, 2, 40, 41, - 0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 5, 0, - 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, - 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 10, - 0, 0, 0, 0, 0, 0, 11, 12, 0, 13, 0, 14, 15, 16, 0, 0, - 0, 0, 0, 1, 17, 18, 0, 19, 7, 1, 0, 0, 0, 20, 20, 7, - 20, 20, 20, 20, 20, 20, 20, 8, 21, 0, 22, 0, 7, 23, 24, 0, - 20, 20, 25, 0, 0, 0, 26, 27, 1, 7, 20, 20, 20, 20, 20, 1, - 28, 29, 30, 31, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 10, 0, - 0, 0, 0, 0, 0, 0, 20, 20, 20, 1, 0, 0, 8, 21, 32, 4, - 0, 10, 0, 33, 7, 20, 20, 20, 0, 0, 0, 0, 8, 34, 34, 35, - 36, 34, 37, 0, 38, 1, 20, 20, 0, 0, 39, 0, 1, 1, 0, 8, - 21, 1, 20, 0, 0, 0, 1, 0, 0, 40, 1, 1, 0, 0, 8, 21, - 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 26, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 21, 7, 20, 41, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 21, 0, 42, 43, 44, 0, 45, 0, 8, 21, 0, 0, 0, 0, 0, - 0, 0, 0, 46, 7, 1, 10, 1, 0, 0, 0, 1, 20, 20, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 34, 9, 0, 0, 20, 20, - 1, 20, 20, 0, 0, 0, 0, 0, 0, 0, 26, 21, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 47, 48, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, - 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 13, 22, 13, 13, 13, - 13, 23, 24, 24, 25, 26, 13, 13, 13, 27, 28, 29, 13, 30, 31, 32, - 33, 34, 35, 36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, - 7, 41, 13, 42, 7, 7, 43, 7, 44, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,177, 0, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 6, 7, 8, 3, + 3, 3, 3, 3, 9, 10, 11, 12, 13, 3, 3, 3, 3, 3, 3, 3, + 3, 14, 3, 15, 3, 3, 3, 3, 3, 3, 16, 17, 18, 19, 20, 21, + 3, 3, 3, 22, 23, 24, 3, 3, 3, 3, 3, 3, 25, 3, 3, 3, + 3, 3, 3, 3, 3, 26, 3, 3, 27, 28, 0, 1, 0, 0, 0, 0, + 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, + 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0, 0, + 0, 0, 0, 0, 0, 10, 11, 12, 13, 0, 0, 14, 15, 16, 6, 0, + 17, 18, 19, 19, 19, 20, 21, 22, 23, 24, 19, 25, 0, 26, 27, 19, + 19, 28, 29, 30, 0, 31, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, + 0, 19, 28, 0, 32, 33, 9, 34, 35, 19, 0, 0, 36, 37, 38, 39, + 40, 19, 0, 41, 42, 43, 44, 31, 0, 1, 45, 42, 0, 0, 0, 0, + 0, 32, 14, 14, 0, 0, 0, 0, 14, 0, 0, 46, 47, 47, 47, 47, + 48, 49, 47, 47, 47, 47, 50, 51, 52, 53, 43, 21, 0, 0, 0, 0, + 0, 0, 0, 54, 6, 55, 0, 14, 19, 1, 0, 0, 0, 0, 56, 57, + 0, 0, 0, 0, 0, 19, 58, 31, 0, 0, 0, 0, 0, 0, 0, 59, + 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 60, + 61, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 3, + 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 1, 1, 0, + 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, + 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, 1, 0, + 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, 0, 0, 22, 0, 0, 0, + 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, 0, 0, 8, 21, 27, 0, + 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, 29, 30, 0, 31, 32, 20, + 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, 5, 0, 0, 0, 33, 9, + 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, 34, 1, 35, 21, 21, 21, + 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, 40, 0, 8, 21, 21, 21, + 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, 1, 8, 21, 21, 21, 9, + 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, 1, 44, 0, 0, 0, 45, + 8, 9, 1, 0, 0, 0, 8, 21, 21, 21, 9, 0, 1, 0, 1, 1, + 8, 21, 21, 9, 0, 4, 5, 8, 9, 1, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 9, 10, 11, 11, 11, 11, 12, 13, 13, 13, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 13, 22, 13, 13, 13, 13, 23, 24, 24, 25, 26, 13, 13, + 13, 27, 28, 29, 13, 30, 31, 32, 33, 34, 35, 36, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 13, 42, 7, 7, 43, 7, + 44, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, @@ -3501,419 +3479,221 @@ _hb_ucd_u8[17868] = 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, - 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, - 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 63, 64, 65, - 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 59, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 45, 0, 0, 1, + 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 34, 35, 36, 37, 37, 37, 37, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 2, 2, 53, 54, 55, 56, + 57, 58, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, 59, 59, 61, 61, + 59, 59, 59, 59, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 59, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 79, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 80, 80, 80, 81, - 82, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 79, 70, 70, 70, 70, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 81, 82, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, 97, 98, 99,100,101,101, - 102,103,104,105,106,107,108,109,110,111, 96,112,113,114,115,116, - 117,118,119,119,120,121,122,123,124,125,126,127,128,129,130,131, - 132, 96,133,134,135,136,137,138,139,140,141,142,143, 96,144,145, - 96,146,147,148,149, 96,150,151,152,153,154,155,156, 96,157,158, - 159,160, 96,161,162,163,164,164,164,164,164,164,164,165,166,164, - 167, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96,168,169,169,169,169,169,169,169,169,170, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,171,171, - 171,171,172, 96, 96, 96,173,173,173,173,174,175,176,177, 96, 96, - 96, 96,178,179,180,181,182,182,182,182,182,182,182,182,182,182, - 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, - 182,182,182,182,182,183,182,182,182,182,182,182,184,184,184,185, - 186, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96,187,188,189,190,191,191,192, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,193,194, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96,195,196, 59,197,198,199,200,201,202, 96,203,204, - 205, 59, 59,206, 59,207,208,208,208,208,208,209, 96, 96, 96, 96, - 96, 96, 96, 96,210, 96,211,212,213, 96, 96,214, 96, 96, 96,215, - 96, 96, 96, 96, 96,216,217,218,219, 96, 96, 96, 96, 96,220,221, - 222, 96,223,224, 96, 96,225,226, 59,227,228, 96, 59, 59, 59, 59, - 59, 59, 59,229,230,231,232,233, 59, 59,234,235, 59,236, 96, 96, - 96, 96, 96, 96, 96, 96, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70,237, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70,238, 70,239, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 97, 98, 99,100,101,101,102,103,104,105,106,107,108,109, + 110,111, 96,112,113,114,115,116,117,118,119,119,120,121,122,123, + 124,125,126,127,128,129,130,131,132, 96,133,134,135,136,137,138, + 139,140,141,142,143, 96,144,145, 96,146,147,148,149, 96,150,151, + 152,153,154,155,156, 96,157,158,159,160, 96,161,162,163,164,164, + 164,164,164,164,164,165,166,164,167, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,168,169,169, + 169,169,169,169,169,169,170, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96,171,171,171,171,172, 96, 96, 96,173,173, + 173,173,174,175,176,177, 96, 96, 96, 96,178,179,180,181,182,182, + 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, + 182,182,182,182,182,182,182,182,182,182,182,182,182,183,182,182, + 182,182,182,182,184,184,184,185,186, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,187,188,189, + 190,191,191,192, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96,193,194, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,195,196, 59,197, + 198,199,200,201,202, 96,203,204,205, 59, 59,206, 59,207,208,208, + 208,208,208,209, 96, 96, 96, 96, 96, 96, 96, 96,210, 96,211,212, + 213, 96, 96,214, 96, 96, 96,215, 96, 96, 96, 96, 96,216,217,218, + 219, 96, 96, 96, 96, 96,220,221,222, 96,223,224, 96, 96,225,226, + 59,227,228, 96, 59, 59, 59, 59, 59, 59, 59,229,230,231,232,233, + 59, 59,234,235, 59,236, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,237, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,238, 70,239, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70,240, 70, 70, 70, 70, 70, 70, 70, 70, 70,241, 96, 96, - 96, 96, 96, 96, 96, 96, 70, 70, 70, 70,242, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 70, 70, 70, 70, 70, 70,243, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,244, 96, 96, - 96, 96, 96, 96, 96, 96,245, 96,246,247, 0, 1, 2, 2, 0, 1, - 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 19, 19, - 19, 19, 19, 19, 19, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 19, - 19, 19, 19, 0, 0, 0, 0, 0, 26, 26, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, - 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, - 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 2, 9, 9, 9, 9, 9, 9, 9, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 1, 1, 6, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, - 4, 2, 2, 4, 4, 4, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2, 2, 2, 2, 14, 14, - 14, 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 2, 2, 2, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 0, 0, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 2, 37, 37, 37, - 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 2, 2, 2, 2, 2, 2, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 2, 2, 90, 90, 90, 90, 90, 90, 90, 2, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 2, 2, 95, 2, 37, 37, - 37, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, - 2, 2, 2, 2, 2, 2, 3, 3, 0, 3, 3, 3, 3, 3, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 7, 7, - 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, - 5, 5, 5, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 2, - 5, 2, 2, 2, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 2, - 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, - 2, 2, 5, 5, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 2, 2, 11, 11, 11, 2, 11, 11, 11, 11, 11, - 11, 2, 2, 2, 2, 11, 11, 2, 2, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 11, 11, 11, 11, 11, 2, - 11, 11, 2, 11, 11, 2, 11, 11, 2, 2, 11, 2, 11, 11, 11, 2, - 2, 11, 11, 11, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, - 11, 11, 11, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 2, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 2, - 10, 10, 2, 10, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, 10, 10, - 2, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 10, 10, - 10, 10, 2, 2, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 10, - 10, 10, 10, 10, 10, 10, 2, 21, 21, 21, 2, 21, 21, 21, 21, 21, - 21, 21, 21, 2, 2, 21, 21, 2, 2, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 2, - 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 21, 21, 2, - 2, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 2, 2, - 2, 2, 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, 2, - 22, 22, 2, 22, 22, 22, 22, 22, 22, 2, 2, 2, 22, 22, 22, 2, - 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 22, 2, 22, 22, 2, 2, - 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 2, 2, 2, 2, 22, 22, 22, 2, 2, 2, 2, 2, 2, 22, 2, 2, - 2, 2, 2, 2, 22, 22, 22, 22, 22, 2, 2, 2, 2, 2, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 2, 23, 23, 23, 2, - 23, 23, 23, 23, 23, 23, 23, 23, 2, 2, 23, 23, 23, 23, 23, 2, - 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 2, 23, 23, - 23, 2, 2, 23, 2, 2, 23, 23, 23, 23, 2, 2, 23, 23, 2, 2, - 2, 2, 2, 2, 2, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 2, 16, 16, 16, 2, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 2, 16, 16, 16, 16, 16, 2, 2, 16, 16, 16, 16, 16, 2, - 16, 16, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 16, 2, 16, 16, - 16, 16, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, 2, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, - 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, - 20, 20, 2, 2, 20, 20, 2, 36, 36, 36, 2, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, - 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 36, - 36, 36, 36, 2, 36, 2, 2, 2, 2, 2, 2, 2, 36, 36, 2, 2, - 36, 36, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 2, 2, 2, 2, 0, 24, 24, - 24, 24, 2, 2, 2, 2, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, - 18, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 2, 18, 18, - 18, 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 18, 18, - 2, 2, 18, 18, 18, 18, 25, 25, 25, 25, 25, 25, 25, 25, 2, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 2, 2, 2, 25, 25, - 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 0, 0, 0, 0, 25, - 25, 2, 2, 2, 2, 2, 33, 33, 33, 33, 33, 33, 33, 33, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 8, 2, 2, - 2, 2, 2, 8, 2, 2, 8, 8, 8, 0, 8, 8, 8, 8, 12, 12, - 12, 12, 12, 12, 12, 12, 30, 30, 30, 30, 30, 30, 30, 30, 30, 2, - 30, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, - 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 2, 2, 2, 30, 30, - 2, 2, 2, 2, 2, 2, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 2, 2, 28, 28, 28, 28, 28, 28, 28, 28, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 2, 2, 2, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, - 2, 2, 2, 2, 2, 2, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 2, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 2, 2, 2, 2, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 2, 46, 46, 46, 2, - 46, 46, 2, 2, 2, 2, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 2, 2, 31, 31, 2, 2, 2, 2, 2, 2, 32, 32, - 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 2, 2, 2, 2, 2, 2, 32, 2, 2, 2, 2, 2, 2, 2, 32, 32, - 32, 2, 2, 2, 2, 2, 28, 28, 28, 28, 28, 28, 2, 2, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 2, 48, 48, - 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 48, 48, 48, 48, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 2, 2, 52, 52, - 52, 52, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 2, 2, 2, 2, 58, 58, 2, 2, 2, 2, 2, 2, 58, 58, - 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 2, 2, 91, 91, 91, - 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 2, 2, 1, 1, - 1, 1, 1, 1, 1, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 2, 76, 76, - 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, - 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, - 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 2, - 2, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, - 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, - 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, - 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, - 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, - 9, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, 2, 9, 9, 9, - 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, - 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 2, 19, 19, - 19, 19, 19, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, - 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, - 0, 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 27, 27, - 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, 56, 56, 56, 56, 56, 56, 56, 56, 55, 55, - 55, 55, 2, 2, 2, 2, 2, 55, 55, 55, 55, 55, 55, 55, 61, 61, - 61, 61, 61, 61, 61, 61, 2, 2, 2, 2, 2, 2, 2, 61, 61, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 2, 0, 0, - 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, - 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, - 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, - 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, - 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 0, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, - 39, 39, 39, 39, 39, 2, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 2, 2, 2, 2, 79, 79, - 79, 79, 79, 79, 79, 79, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, - 0, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 19, 19, - 2, 19, 2, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 2, 2, 2, - 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 65, 65, - 65, 65, 65, 65, 65, 65, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 2, 2, 75, 75, 75, 75, - 2, 2, 2, 2, 2, 2, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 74, 12, 12, - 12, 12, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, - 33, 33, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 2, 68, 68, 68, 68, 68, 68, 2, 2, 68, 68, - 2, 2, 68, 68, 68, 68, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, - 92, 2, 2, 2, 2, 2, 2, 2, 2, 92, 92, 92, 92, 92, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 2, 2, 30, - 30, 30, 30, 30, 30, 2, 19, 19, 19, 0, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 87, 87, - 87, 87, 87, 87, 2, 2, 87, 87, 2, 2, 2, 2, 2, 2, 12, 12, - 12, 12, 2, 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 13, 13, - 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, - 2, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 2, 14, 14, 14, 14, 14, 2, 14, 2, 14, 14, - 2, 14, 14, 2, 14, 14, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, - 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 2, 2, - 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, - 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 3, 3, - 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, - 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, - 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, - 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, - 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, - 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, - 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, - 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, - 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, - 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, - 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, - 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, - 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, - 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,110,110, - 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, - 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, - 19, 19, 19, 19, 19, 19, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81,120,120, - 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116, - 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,128,128, - 128,128,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, - 2,128,128,128,128,128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, - 97, 97, 97, 97, 97, 97, 2, 2, 2, 2, 97, 97, 97, 97, 2, 2, - 97, 97, 97, 97, 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 2, - 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 2, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 57, 2, - 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88,117,117, - 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112, - 112,112,112,112,112, 2, 2, 2, 2,112,112,112,112,112, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, - 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 2, 2, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, - 82, 2, 2, 2, 2, 2,122,122,122,122,122,122,122,122,122,122, - 2, 2, 2, 2, 2, 2, 2,122,122,122,122, 2, 2, 2, 2,122, - 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, - 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, - 130, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,144,144, - 144,144,144,144,144,144,144,144, 2, 2, 2, 2, 2, 2,156,156, - 156,156,156,156,156,156,156,156, 2,156,156,156, 2, 2,156,156, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,147,147, - 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148, - 2, 2, 2, 2, 2, 2,158,158,158,158,158,158,158,158,158,158, - 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, - 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, - 149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, - 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, 2, - 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 85, 2, 2,101,101, - 101,101,101,101,101,101,101, 2, 2, 2, 2, 2, 2, 2,101,101, - 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 2, 96, 96,111,111,111,111,111,111,111,111,111,111, - 111,111,111,111,111, 2,100,100,100,100,100,100,100,100, 2, 36, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,108,108, - 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, - 2, 2, 2, 2, 2, 2,129,129,129,129,129,129,129, 2,129, 2, - 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, - 129,129,129,129, 2,129,129,129, 2, 2, 2, 2, 2, 2,109,109, - 109,109,109,109,109,109,109,109,109, 2, 2, 2, 2, 2,109,109, - 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, - 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, - 107,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, 2, - 107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, 2, - 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, - 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, - 107,107,107, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, - 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2,124,124, - 124,124,124,124,124,124,124,124, 2, 2, 2, 2, 2, 2,123,123, - 123,123,123,123,123,123,123,123,123,123,123,123, 2, 2,114,114, - 114,114,114,114,114,114,114,114,114,114,114, 2, 2, 2,114,114, - 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 2, 2, 2,102,102, - 102,102,102,102,102,102,102,102, 2, 2, 2, 2, 2, 2,126,126, - 126,126,126,126,126,126,126,126,126, 2, 2,126,126,126,126,126, - 126,126, 2, 2, 2, 2,126,126,126,126,126,126,126, 2,142,142, - 142,142,142,142,142,142,142,142,142,142, 2, 2, 2, 2,125,125, - 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, - 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, - 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, - 2,154,154,154,154,154,154,154, 2, 2, 2, 2, 2, 2,150,150, - 150,150,150,150,150,150, 2, 2,150,150,150,150,150,150,150,150, - 150,150,150, 2, 2, 2,141,141,141,141,141,141,141,141,140,140, - 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2,121,121, - 121,121,121,121,121,121,121, 2, 2, 2, 2, 2, 2, 2, 7, 7, - 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133, 2, - 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133, - 133,133,133,133, 2, 2,133,133,133,133,133, 2, 2, 2,134,134, - 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134, 2,134, - 134,134,134,134,134,134,134,134,134,134,134,134,134, 2,138,138, - 138,138,138,138,138, 2,138,138, 2,138,138,138,138,138,138,138, - 138,138,138,138,138,138, 2, 2,138, 2,138,138, 2,138,138,138, - 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, 2, - 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, - 143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, - 2, 2, 2, 2, 2, 2,143,143, 2, 2, 2, 2, 2, 2,145,145, - 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, - 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, - 163, 2, 2, 2,163,163,163,163, 2, 2, 2, 2, 2, 2, 86, 2, - 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, - 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, - 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2,127,127, - 127,127,127,127,127,127,127,127,127,127,127,127,127, 2, 79, 2, - 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,240, 70, 70, 70, 70, + 70, 70, 70, 70, 70,241, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, + 70, 70,242, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, + 70, 70, 70, 70,243, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70,244, 96, 96, 96, 96, 96, 96, 96, 96,245, 96, + 246,247, 0, 1, 2, 2, 0, 1, 2, 2, 2, 3, 4, 5, 0, 0, + 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, + 19, 0, 19, 0, 0, 0, 0, 0, 26, 26, 1, 1, 1, 1, 9, 9, + 9, 9, 0, 9, 9, 9, 2, 2, 9, 9, 9, 9, 0, 9, 2, 2, + 2, 2, 9, 0, 9, 0, 9, 9, 9, 2, 9, 2, 9, 9, 9, 9, + 2, 9, 9, 9, 55, 55, 55, 55, 55, 55, 6, 6, 6, 6, 6, 1, + 1, 6, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, 2, 2, 14, 14, 2, + 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, + 3, 0, 3, 3, 3, 1, 1, 1, 3, 3, 1, 3, 3, 3, 37, 37, + 37, 37, 37, 37, 2, 37, 37, 37, 37, 2, 2, 37, 37, 37, 38, 38, + 38, 38, 38, 38, 2, 2, 64, 64, 64, 64, 64, 64, 64, 2, 2, 64, + 64, 64, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, 90, 2, 95, 95, + 95, 95, 2, 2, 95, 2, 3, 3, 3, 2, 3, 3, 2, 2, 3, 3, + 0, 3, 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 0, 0, + 7, 7, 5, 5, 5, 5, 2, 5, 5, 5, 5, 2, 2, 5, 5, 2, + 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, 2, 2, 5, 5, + 5, 2, 2, 2, 2, 5, 5, 5, 2, 5, 2, 11, 11, 11, 11, 11, + 11, 2, 2, 2, 2, 11, 11, 2, 2, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 2, 11, 11, 2, 11, 11, 2, 2, 2, 11, 2, 2, 11, 2, + 11, 2, 2, 2, 11, 11, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 2, 10, 10, 2, 10, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, + 10, 10, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 21, 21, 21, 21, 2, 2, 21, 21, 2, 21, 2, 2, 21, 21, 2, 2, + 22, 22, 2, 22, 22, 22, 22, 22, 22, 2, 22, 2, 22, 22, 22, 22, + 2, 2, 2, 22, 22, 2, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, + 22, 22, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 23, + 23, 2, 23, 23, 23, 2, 2, 23, 2, 2, 2, 2, 23, 23, 2, 2, + 2, 23, 16, 16, 16, 16, 16, 2, 16, 16, 2, 16, 16, 16, 16, 16, + 2, 2, 2, 16, 16, 2, 2, 2, 16, 16, 20, 20, 20, 20, 20, 2, + 20, 20, 2, 2, 20, 20, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 2, 2, 2, 36, 36, 36, 36, 2, 36, 2, 36, 2, 2, 2, 2, + 36, 2, 2, 2, 2, 36, 36, 2, 36, 2, 36, 2, 2, 2, 2, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 2, 2, 2, 2, 0, 2, 18, + 18, 2, 18, 2, 18, 18, 18, 18, 18, 2, 18, 18, 18, 18, 2, 18, + 2, 18, 18, 18, 2, 2, 18, 2, 18, 2, 25, 25, 25, 25, 2, 25, + 25, 25, 25, 2, 2, 2, 25, 2, 25, 25, 25, 0, 0, 0, 0, 25, + 25, 2, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 2, 8, 2, 8, + 2, 2, 8, 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, 30, 2, + 30, 30, 30, 30, 2, 2, 30, 30, 30, 2, 2, 30, 30, 30, 30, 2, + 2, 2, 29, 29, 29, 29, 29, 29, 2, 2, 28, 28, 28, 28, 34, 34, + 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 0, 0, 0, + 35, 35, 35, 2, 2, 2, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, + 2, 45, 44, 44, 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 46, 46, + 46, 46, 46, 2, 46, 46, 31, 31, 31, 31, 31, 31, 2, 2, 32, 32, + 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 2, 2, 32, 2, + 2, 2, 32, 32, 32, 2, 28, 28, 2, 2, 48, 48, 48, 48, 48, 48, + 48, 2, 48, 2, 2, 2, 52, 52, 52, 52, 52, 52, 2, 2, 52, 2, + 2, 2, 58, 58, 58, 58, 58, 58, 2, 2, 58, 58, 58, 2, 2, 2, + 58, 58, 54, 54, 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, + 91, 2, 91, 2, 2, 91, 91, 91, 2, 2, 1, 1, 1, 2, 62, 62, + 62, 62, 62, 2, 2, 2, 62, 62, 62, 2, 76, 76, 76, 76, 93, 93, + 93, 93, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 2, 2, 2, 70, + 70, 70, 73, 73, 73, 73, 6, 2, 2, 2, 8, 8, 8, 2, 2, 8, + 8, 8, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, + 0, 0, 1, 1, 0, 2, 19, 19, 9, 9, 9, 9, 9, 6, 19, 9, + 9, 9, 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, 19, 19, + 19, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, 9, 9, 1, 1, + 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, 0, 19, 0, 0, + 0, 2, 19, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2, 0, 0, + 9, 0, 0, 0, 19, 19, 27, 27, 27, 27, 2, 2, 0, 0, 0, 0, + 2, 0, 56, 56, 56, 56, 2, 55, 55, 55, 61, 61, 61, 61, 2, 2, + 2, 61, 61, 2, 2, 2, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, + 2, 13, 13, 13, 2, 2, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, + 1, 1, 1, 1, 12, 12, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 0, 2, 26, 26, 26, 26, 26, 26, 26, 2, 12, + 12, 12, 12, 12, 12, 2, 12, 12, 12, 0, 39, 39, 39, 39, 39, 2, + 2, 2, 39, 39, 39, 2, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, + 79, 79, 19, 19, 19, 2, 19, 19, 2, 19, 2, 19, 19, 19, 19, 19, + 2, 2, 2, 2, 19, 19, 60, 60, 60, 60, 60, 2, 2, 2, 65, 65, + 65, 65, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, 75, 75, 69, 69, + 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 2, 2, 2, 74, 12, 2, + 2, 2, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, + 84, 84, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, + 2, 2, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 92, 87, 87, + 87, 87, 87, 87, 87, 2, 19, 9, 19, 19, 19, 19, 0, 0, 87, 87, + 2, 2, 2, 2, 2, 12, 2, 2, 2, 4, 14, 2, 14, 2, 14, 14, + 2, 14, 14, 2, 14, 14, 2, 2, 2, 3, 3, 3, 0, 0, 2, 2, + 3, 3, 1, 1, 6, 6, 3, 2, 3, 3, 3, 2, 2, 0, 2, 0, + 0, 0, 0, 0, 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 49, 49, + 49, 49, 2, 49, 49, 49, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, + 2, 2, 9, 2, 2, 2, 0, 1, 2, 2, 71, 71, 71, 71, 71, 2, + 2, 2, 67, 67, 67, 67, 67, 2, 2, 2, 42, 42, 42, 42, 2, 42, + 42, 42, 41, 41, 41, 41, 41, 41, 41, 2,118,118,118,118,118,118, + 118, 2, 53, 53, 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, + 2, 2, 40, 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, + 2, 2,135,135,135,135,106,106,106,106,104,104,104,104, 2, 2, + 2,104,161,161,161,161,161,161,161, 2,161,161, 2,161,161, 2, + 2, 2,110,110,110,110,110,110,110, 2,110,110, 2, 2, 19, 2, + 19, 19, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, + 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, + 2, 81,120,120,120,120,116,116,116,116,116,116,116, 2, 2, 2, + 2,116,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, + 2,128, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, + 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 97, 97, 97, 97, 2, 2, + 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 57, 57, 57, 57, 57, + 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 88, 88, + 88, 88,117,117,117,117,112,112,112,112,112,112,112, 2, 2, 2, + 2,112, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 83, 83, + 83, 83, 83, 83, 2, 2, 82, 82, 82, 82, 82, 82, 82, 2,122,122, + 122,122,122,122, 2, 2, 2,122,122,122,122, 2, 2, 2, 89, 89, + 89, 89, 89, 2, 2, 2,130,130,130,130,130,130,130, 2, 2, 2, + 130,130,144,144,144,144,144,144, 2, 2,156,156,156,156,156,156, + 2,156,156,156, 2, 2, 2, 3, 3, 3,147,147,147,147,148,148, + 148,148,148,148, 2, 2,158,158,158,158,158,158, 2, 2,153,153, + 153,153,149,149,149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, + 2, 2, 2, 2, 94, 94, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, + 85, 2, 2, 85, 2, 2,101,101,101,101,101, 2, 2, 2,101,101, + 2, 2, 96, 96, 96, 96, 96, 2, 96, 96,111,111,111,111,111,111, + 111, 2,100,100,100,100,108,108,108,108,108,108, 2,108,108,108, + 2, 2,129,129,129,129,129,129,129, 2,129, 2,129,129,129,129, + 2,129,129,129, 2, 2,109,109,109,109,109,109,109, 2,109,109, + 2, 2,107,107,107,107, 2,107,107,107,107, 2, 2,107,107, 2, + 107,107,107,107, 2, 1,107,107, 2, 2,107, 2, 2, 2, 2, 2, + 2,107, 2, 2,107,107,137,137,137,137, 2,137,137,137,137,137, + 2, 2,124,124,124,124,124,124, 2, 2,123,123,123,123,123,123, + 2, 2,114,114,114,114,114, 2, 2, 2,114,114, 2, 2,102,102, + 102,102,102,102, 2, 2,126,126,126,126,126,126,126, 2, 2,126, + 126,126,142,142,142,142,125,125,125,125,125,125,125, 2, 2, 2, + 2,125,154,154,154,154,154,154,154, 2, 2,154, 2, 2, 2,154, + 154, 2,154,154, 2,154,154, 2, 2,154,154,154, 2, 2,150,150, + 150,150, 2, 2,150,150,150, 2, 2, 2,141,141,141,141,140,140, + 140,140,140,140,140, 2,121,121,121,121,121, 2, 2, 2, 7, 7, + 2, 2,133,133,133,133,133, 2,133,133,133,133,133, 2,133,133, + 2, 2,133, 2, 2, 2,134,134,134,134, 2, 2,134,134, 2,134, + 134,134,134,134,134, 2,138,138,138,138,138,138,138, 2,138,138, + 2,138, 2, 2,138, 2,138,138, 2, 2,143,143,143,143,143,143, + 2,143,143, 2,143,143,143,143,143, 2,143, 2, 2, 2,143,143, + 2, 2,145,145,145,145,145, 2, 2, 2,163,163,163,163,163, 2, + 163,163,163,163,163, 2, 2, 2,163,163,163,163, 2, 2, 86, 2, + 2, 2, 63, 63, 63, 63, 63, 63, 2, 2, 63, 63, 63, 2, 63, 2, + 2, 2,157,157,157,157,157,157,157, 2, 80, 80, 80, 80, 80, 80, + 2, 2,127,127,127,127,127,127,127, 2, 79, 2, 2, 2,115,115, 115,115,115,115,115, 2,115,115, 2, 2, 2, 2,115,115,159,159, - 159,159,159,159,159,159,159,159,159,159,159,159,159, 2,159,159, - 2, 2, 2, 2, 2, 2,103,103,103,103,103,103,103,103,103,103, - 103,103,103,103, 2, 2,119,119,119,119,119,119,119,119,119,119, - 119,119,119,119, 2, 2,119,119, 2,119,119,119,119,119, 2, 2, - 2, 2, 2,119,119,119,146,146,146,146,146,146,146,146,146,146, - 146, 2, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 2, 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 99,136,139, - 13, 13,155, 2, 2, 2,136,136,136,136,136,136,136,136,155,155, - 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2,136, 2, - 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 17, 17, 17, 17, 17, - 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 15, 15, 15, 15, 17, 17, - 17, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 15, 15, - 15, 2, 2, 17, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17,139,139, - 139,139,139,139,139,139,139,139,139,139, 2, 2, 2, 2,105,105, - 105,105,105,105,105,105,105,105,105, 2, 2, 2, 2, 2,105,105, - 105,105,105, 2, 2, 2,105, 2, 2, 2, 2, 2, 2, 2,105,105, - 2, 2,105,105,105,105, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, - 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, - 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, - 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, - 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, - 0, 0, 0, 0, 0, 0,131,131,131,131,131,131,131,131,131,131, - 131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131, - 131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, 56, - 56, 56, 56, 56, 56, 2, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56, - 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2, 6, 6, - 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6,151,151, - 151,151,151,151,151,151,151,151,151,151,151, 2, 2, 2,151,151, - 151,151,151,151, 2, 2,151,151, 2, 2, 2, 2,151,151,160,160, - 160,160,160,160,160,160,160,160,160,160,160,160,160, 2,152,152, - 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164, - 164,164,164,164,164,164,164,164, 2, 2, 2, 2, 2, 2, 30, 30, - 30, 30, 2, 30, 30, 2,113,113,113,113,113,113,113,113,113,113, - 113,113,113, 2, 2,113,113,113,113,113,113,113,113, 2,132,132, - 132,132,132,132,132,132,132,132,132,132, 2, 2, 2, 2,132,132, - 2, 2, 2, 2,132,132, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, - 3, 2, 3, 2, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 2, 2, - 3, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 2, 3, - 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 3, - 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, - 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 15, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 0, 0, 0, 13, 2, 2, 2, 2, 2, 2, 2, 13, 13, - 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, - 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 159,159,159,159,159, 2,159,159, 2, 2,103,103,103,103,103,103, + 2, 2,119,119,119,119,119,119, 2, 2,119,119, 2,119, 2,119, + 119,119,146,146,146,146,146,146,146, 2, 99, 99, 99, 99, 99, 99, + 99, 2, 2, 2, 2, 99,136,139, 13, 13,155, 2, 2, 2,136,136, + 136,136,155,155,155,155,155,155, 2, 2,136, 2, 2, 2, 2, 17, + 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 17, 17, 17, 2, 2, 2, + 15, 2, 2, 17, 2, 2,139,139,139,139,105,105,105,105,105,105, + 105, 2,105, 2, 2, 2,105,105, 2, 2, 1, 1, 2, 2, 0, 0, + 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 2, 2, 0, 2, 2, 0, + 0, 2, 0, 2, 0, 2,131,131,131,131, 2, 2, 2,131, 2,131, + 131,131, 56, 56, 56, 2, 56, 2, 2, 56, 56, 56, 2, 56, 56, 2, + 56, 56, 6, 6, 2, 2, 2, 2, 2, 6,151,151,151,151,151, 2, + 2, 2,151,151, 2, 2, 2, 2,151,151,160,160,160,160,160,160, + 160, 2,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164, + 164,164,164,164, 2, 2, 2, 30, 30, 2,113,113,113,113,113, 2, + 2,113,113,113,113, 2,132,132,132,132,132,132, 2, 2, 2, 2, + 132,132, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, 2, 3, 2, 2, + 3, 2, 3, 2, 3, 2, 3, 3, 2, 3, 15, 0, 0, 2, 13, 2, + 2, 2, 13, 13, 13, 2, 2, 0, 2, 2, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, + 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 16, 17, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 18, 19, 20, 9, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -3922,60 +3702,60 @@ _hb_ucd_u8[17868] = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, 31, 32, 0, 33, - 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, - 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, - 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, - 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, 0, 0, 66, 0, + 9, 9, 9, 9, 9, 9, 23, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, + 25, 26, 27, 28, 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, + 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, + 0, 0, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, + 0, 52, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, + 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, + 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, + 62, 63, 64, 65, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 69, - 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, - 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0,108, 0,109, 0, - 110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99,100,101,102,103, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, + 0,105,106, 0,107, 0, 0, 0,108, 0,109, 0,110, 0,111,112, + 113, 0,114, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 118,119,120,121, 0,122,123,124,125,126, 0,127, 0, 0, 0, 0, + 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118,119,120,121, + 0,122,123,124,125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,129,130,131, - 132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147, - 148,149,150,151,152,153,154,155,156,157, 0, 0, 0,158,159,160, - 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,162,163, 0, 0, 0, 0, 0, 0, 0,164, 0, + 0, 0, 0, 0, 0, 0, 0, 0,128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151, + 152,153,154,155,156,157, 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,162,163, 0, 0, 0, 0, 0, 0, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,168, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,169,170, 0, 0, 0, 0,171,172, 0, 0, 0,173,174,175,176, - 177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192, - 193,194,195,196,197,198,199,200,201,202,203,204,205,206, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,169,170, 0, + 0, 0, 0,171,172, 0, 0, 0,173,174,175,176,177,178,179,180, + 181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196, + 197,198,199,200,201,202,203,204,205,206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 0, 1, 2, 3, 4, }; static const uint16_t -_hb_ucd_u16[9320] = +_hb_ucd_u16[10040] = { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, @@ -4104,255 +3884,300 @@ _hb_ucd_u16[9320] = 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 140, 140, 140, 140, 140, 683, 140, 570, 570, 570, 570, 570, 570, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, 391, 391, 391, 391, 391, 391, 391, 684, - 391, 391, 391, 391, 391, 391, 391, 685, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 2, 2, 3, 1, 2, 2, 3, 0, 0, 0, 0, 0, 4, 0, 4, - 2, 2, 5, 2, 2, 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, - 0, 0, 0, 0, 7, 8, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, - 14, 14, 14, 14, 16, 17, 14, 14, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 19, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 21, - 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, - 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, - 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 29, 31, 31, 31, 31, 37, 38, 37, 37, 37, 37, 37, 37, - 37, 39, 31, 31, 31, 31, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, - 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, - 47, 47, 47, 48, 37, 49, 31, 31, 31, 50, 51, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 52, 31, 31, 31, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, 59, 60, 61, 62, - 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, 71, 72, 73, 74, - 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, 83, 84, 85, 86, - 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, 95, 96, 97, 98, - 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, 107, 104, 108, 109, - 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, 113, 115, 113, 116, - 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, 122, 124, 125, 126, - 123, 127, 128, 128, 129, 122, 130, 26, 131, 132, 133, 131, 131, 131, 131, 131, - 132, 133, 134, 131, 135, 131, 131, 131, 136, 137, 138, 139, 137, 137, 140, 141, - 138, 142, 143, 137, 144, 137, 145, 26, 146, 147, 147, 147, 147, 147, 147, 148, - 147, 147, 147, 149, 26, 26, 26, 26, 150, 151, 152, 152, 153, 152, 152, 154, - 155, 156, 152, 157, 26, 26, 26, 26, 158, 158, 158, 158, 158, 158, 158, 158, - 158, 159, 158, 158, 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, - 158, 161, 162, 163, 26, 26, 26, 26, 164, 164, 164, 164, 164, 164, 164, 164, - 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, - 166, 167, 165, 165, 165, 165, 165, 168, 169, 169, 169, 169, 169, 169, 169, 169, - 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, - 170, 171, 172, 171, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 171, 172, - 171, 170, 172, 170, 170, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 170, - 170, 170, 170, 173, 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, - 176, 176, 176, 176, 176, 176, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, - 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, - 181, 181, 181, 181, 181, 182, 181, 183, 184, 184, 185, 186, 187, 187, 188, 26, - 189, 189, 190, 26, 191, 192, 193, 26, 194, 194, 194, 194, 194, 194, 194, 194, - 194, 194, 194, 195, 194, 196, 194, 196, 197, 198, 198, 199, 198, 198, 198, 198, - 198, 198, 198, 198, 198, 198, 198, 200, 198, 198, 198, 198, 198, 201, 178, 178, - 178, 178, 178, 178, 178, 178, 202, 26, 203, 203, 203, 204, 203, 205, 203, 205, - 206, 203, 207, 207, 207, 208, 209, 26, 210, 210, 210, 210, 210, 211, 210, 210, - 210, 212, 210, 213, 194, 194, 194, 194, 214, 214, 214, 215, 216, 216, 216, 216, - 216, 216, 216, 217, 216, 216, 216, 218, 216, 219, 216, 219, 216, 220, 9, 9, - 9, 221, 26, 26, 26, 26, 26, 26, 222, 222, 222, 222, 222, 222, 222, 222, - 222, 223, 222, 222, 222, 222, 222, 224, 225, 225, 225, 225, 225, 225, 225, 225, - 226, 226, 226, 226, 226, 226, 227, 228, 229, 229, 229, 229, 229, 229, 229, 230, - 229, 231, 232, 232, 232, 232, 232, 232, 18, 233, 165, 165, 165, 165, 165, 234, - 225, 26, 235, 9, 236, 237, 238, 239, 2, 2, 2, 2, 240, 241, 2, 2, - 2, 2, 2, 242, 243, 244, 2, 245, 2, 2, 2, 2, 2, 2, 2, 246, - 9, 9, 9, 9, 9, 9, 9, 9, 14, 14, 247, 247, 14, 14, 14, 14, - 247, 247, 14, 248, 14, 14, 14, 247, 14, 14, 14, 14, 14, 14, 249, 14, - 249, 14, 250, 251, 14, 14, 252, 253, 0, 254, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 0, 256, 257, 0, 258, 2, 259, 0, 0, 0, 0, - 260, 26, 9, 9, 9, 9, 261, 26, 0, 0, 0, 0, 262, 263, 4, 0, - 0, 264, 0, 0, 2, 2, 2, 2, 2, 265, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 26, 26, 26, - 0, 266, 26, 26, 0, 0, 0, 0, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 269, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 270, 270, 270, 270, 270, 270, 270, 270, - 270, 270, 270, 270, 2, 2, 2, 2, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 271, 272, 165, 165, 165, 165, 166, 167, 273, 273, - 273, 273, 273, 273, 273, 274, 275, 274, 170, 170, 172, 26, 172, 172, 172, 172, - 172, 172, 172, 172, 18, 18, 18, 18, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 276, 26, 26, 26, 26, 277, 277, 277, 278, 277, 277, 277, 277, - 277, 277, 277, 277, 277, 277, 279, 26, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 280, 26, 26, 26, 0, 281, 282, 0, 0, 0, 283, 284, 0, 285, - 286, 287, 287, 287, 287, 287, 287, 287, 287, 287, 288, 289, 290, 291, 291, 291, - 291, 291, 291, 291, 291, 291, 291, 292, 293, 294, 294, 294, 294, 294, 295, 169, - 169, 169, 169, 169, 169, 169, 169, 169, 169, 296, 0, 0, 294, 294, 294, 294, - 0, 0, 0, 0, 281, 26, 291, 291, 169, 169, 169, 296, 0, 0, 0, 0, - 0, 0, 0, 0, 169, 169, 169, 297, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 291, 291, 291, 291, 291, 298, 291, 291, 291, 291, 291, 291, 291, 291, - 291, 291, 291, 0, 0, 0, 0, 0, 277, 277, 277, 277, 277, 277, 277, 277, - 0, 0, 0, 0, 0, 0, 0, 0, 299, 299, 299, 299, 299, 299, 299, 299, - 299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 299, 299, 299, 299, 299, 299, - 301, 26, 302, 302, 302, 302, 302, 302, 303, 303, 303, 303, 303, 303, 303, 303, - 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 304, 26, 26, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 305, 305, 305, 305, - 305, 305, 305, 305, 305, 305, 305, 26, 0, 0, 0, 0, 306, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 307, 2, 2, 2, 2, 2, 2, - 2, 308, 309, 310, 26, 26, 311, 2, 312, 312, 312, 312, 312, 313, 0, 314, - 315, 315, 315, 315, 315, 315, 315, 26, 316, 316, 316, 316, 316, 316, 316, 316, - 317, 318, 316, 319, 53, 53, 53, 53, 320, 320, 320, 320, 320, 321, 322, 322, - 322, 322, 323, 324, 169, 169, 169, 325, 326, 326, 326, 326, 326, 326, 326, 326, - 326, 327, 326, 328, 164, 164, 164, 329, 330, 330, 330, 330, 330, 330, 331, 26, - 330, 332, 330, 333, 164, 164, 164, 164, 334, 334, 334, 334, 334, 334, 334, 334, - 335, 26, 26, 336, 337, 337, 338, 26, 339, 339, 339, 26, 172, 172, 2, 2, - 2, 2, 2, 340, 341, 342, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, - 337, 337, 337, 337, 337, 343, 337, 344, 169, 169, 169, 169, 345, 26, 169, 169, - 296, 346, 169, 169, 169, 169, 169, 345, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 277, 277, 280, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 347, 26, 26, 26, 26, 348, 26, 349, 350, 25, 25, 351, 352, - 353, 25, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 354, 26, 355, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 356, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 357, 31, 31, 31, 31, 31, - 31, 358, 26, 26, 26, 26, 31, 31, 9, 9, 0, 314, 9, 359, 0, 0, - 0, 0, 360, 0, 258, 281, 361, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 362, 363, 0, 0, 0, 1, 2, 2, 3, - 1, 2, 2, 3, 364, 291, 290, 291, 291, 291, 291, 365, 169, 169, 169, 296, - 366, 366, 366, 367, 258, 258, 26, 368, 369, 370, 369, 369, 371, 369, 369, 372, - 369, 373, 369, 373, 26, 26, 26, 26, 369, 369, 369, 369, 369, 369, 369, 369, - 369, 369, 369, 369, 369, 369, 369, 374, 375, 0, 0, 0, 0, 0, 376, 0, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 253, 0, 377, 378, 26, 26, 26, - 26, 26, 0, 0, 0, 0, 0, 379, 380, 380, 380, 381, 382, 382, 382, 382, - 382, 382, 383, 26, 384, 0, 0, 281, 385, 385, 385, 385, 386, 387, 388, 388, - 388, 389, 390, 390, 390, 390, 390, 391, 392, 392, 392, 393, 394, 394, 394, 394, - 395, 394, 396, 26, 26, 26, 26, 26, 397, 397, 397, 397, 397, 397, 397, 397, - 397, 397, 398, 398, 398, 398, 398, 398, 399, 399, 399, 400, 399, 401, 402, 402, - 402, 402, 403, 402, 402, 402, 402, 403, 404, 404, 404, 404, 404, 26, 405, 405, - 405, 405, 405, 405, 406, 407, 408, 409, 408, 409, 410, 408, 411, 408, 411, 412, - 26, 26, 26, 26, 26, 26, 26, 26, 413, 413, 413, 413, 413, 413, 413, 413, - 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 414, 26, - 413, 413, 415, 26, 413, 26, 26, 26, 416, 2, 2, 2, 2, 2, 417, 308, - 26, 26, 26, 26, 26, 26, 26, 26, 418, 419, 420, 420, 420, 420, 421, 422, - 423, 423, 424, 423, 425, 425, 425, 425, 426, 426, 426, 427, 428, 426, 26, 26, - 26, 26, 26, 26, 429, 429, 430, 431, 432, 432, 432, 433, 434, 434, 434, 435, - 26, 26, 26, 26, 26, 26, 26, 26, 436, 436, 436, 436, 437, 437, 437, 438, - 437, 437, 439, 437, 437, 437, 437, 437, 440, 441, 442, 443, 444, 444, 445, 446, - 444, 447, 444, 447, 448, 448, 448, 448, 449, 449, 449, 449, 26, 26, 26, 26, - 450, 450, 450, 450, 451, 452, 451, 26, 453, 453, 453, 453, 453, 453, 454, 455, - 456, 456, 457, 456, 458, 458, 459, 458, 460, 460, 461, 462, 26, 463, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 464, 464, 464, 464, 464, 464, 464, 464, - 464, 465, 26, 26, 26, 26, 26, 26, 466, 466, 466, 466, 466, 466, 467, 26, - 466, 466, 466, 466, 466, 466, 467, 468, 469, 469, 469, 469, 469, 26, 469, 470, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 31, 31, 31, 50, 471, 471, 471, 471, 471, 472, 473, 26, - 26, 26, 26, 26, 26, 26, 26, 474, 475, 475, 475, 475, 475, 26, 476, 476, - 476, 476, 476, 477, 26, 26, 478, 478, 478, 479, 26, 26, 26, 26, 480, 480, - 480, 481, 26, 26, 482, 482, 483, 26, 484, 484, 484, 484, 484, 484, 484, 484, - 484, 485, 486, 484, 484, 484, 485, 487, 488, 488, 488, 488, 488, 488, 488, 488, - 489, 490, 491, 491, 491, 492, 491, 493, 494, 494, 494, 494, 494, 494, 495, 494, - 494, 26, 496, 496, 496, 496, 497, 26, 498, 498, 498, 498, 498, 498, 498, 498, - 498, 498, 498, 498, 499, 137, 500, 26, 501, 501, 502, 501, 501, 501, 501, 501, - 503, 26, 26, 26, 26, 26, 26, 26, 504, 505, 506, 507, 506, 508, 509, 509, - 509, 509, 509, 509, 509, 510, 509, 511, 512, 513, 514, 515, 515, 516, 517, 518, - 513, 519, 520, 521, 522, 523, 523, 26, 524, 524, 524, 524, 524, 524, 524, 524, - 524, 524, 524, 525, 526, 26, 26, 26, 527, 527, 527, 527, 527, 527, 527, 527, - 527, 26, 527, 528, 26, 26, 26, 26, 529, 529, 529, 529, 529, 529, 530, 529, - 529, 529, 529, 530, 26, 26, 26, 26, 531, 531, 531, 531, 531, 531, 531, 531, - 532, 26, 531, 533, 198, 534, 26, 26, 535, 535, 535, 535, 535, 535, 535, 536, - 535, 536, 26, 26, 26, 26, 26, 26, 537, 537, 537, 538, 537, 539, 537, 537, - 540, 26, 26, 26, 26, 26, 26, 26, 541, 541, 541, 541, 541, 541, 541, 542, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 543, 543, 543, 543, - 543, 543, 543, 543, 543, 543, 544, 545, 546, 547, 548, 549, 549, 549, 550, 551, - 546, 26, 549, 552, 26, 26, 26, 26, 26, 26, 26, 26, 553, 554, 553, 553, - 553, 553, 553, 554, 555, 26, 26, 26, 556, 556, 556, 556, 556, 556, 556, 556, - 556, 26, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 558, 26, 178, 178, - 559, 559, 559, 559, 559, 559, 559, 560, 53, 561, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 562, 563, 562, 562, 562, 562, 564, 562, - 565, 26, 562, 562, 562, 566, 567, 567, 567, 567, 568, 567, 567, 569, 570, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 571, 572, 573, 573, 573, 573, 571, 574, - 573, 26, 573, 575, 576, 577, 578, 578, 578, 579, 580, 581, 578, 582, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 583, 583, 583, 584, 585, 585, 586, 585, 585, 585, 585, 587, - 585, 585, 585, 588, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 589, 26, - 108, 108, 108, 108, 108, 108, 590, 591, 592, 592, 592, 592, 592, 592, 592, 592, - 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 593, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 592, 592, 592, 592, 592, 592, 592, 592, - 592, 592, 592, 592, 592, 594, 595, 26, 592, 592, 592, 592, 592, 592, 592, 592, - 596, 26, 26, 26, 26, 26, 26, 26, 26, 26, 597, 597, 597, 597, 597, 597, - 597, 597, 597, 597, 597, 597, 598, 26, 599, 599, 599, 599, 599, 599, 599, 599, - 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, - 599, 599, 600, 26, 26, 26, 26, 26, 601, 601, 601, 601, 601, 601, 601, 601, - 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, - 602, 26, 26, 26, 26, 26, 26, 26, 305, 305, 305, 305, 305, 305, 305, 305, - 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 603, - 604, 604, 604, 605, 604, 606, 607, 607, 607, 607, 607, 607, 607, 607, 607, 608, - 607, 609, 610, 610, 610, 611, 611, 26, 612, 612, 612, 612, 612, 612, 612, 612, - 613, 26, 612, 614, 614, 612, 612, 615, 612, 612, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 617, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 618, 618, 618, 618, 618, 618, 618, 618, - 618, 619, 618, 618, 618, 618, 618, 618, 618, 620, 618, 618, 26, 26, 26, 26, - 26, 26, 26, 26, 621, 26, 347, 26, 622, 622, 622, 622, 622, 622, 622, 622, - 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, - 622, 622, 622, 622, 622, 622, 622, 26, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 624, 26, 26, 26, 26, 26, 622, 625, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 626, 627, 628, 287, 287, 287, 287, 287, 287, 287, - 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, - 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 629, 26, 630, 26, - 26, 26, 631, 26, 632, 26, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, - 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, - 633, 633, 633, 633, 633, 633, 633, 634, 635, 635, 635, 635, 635, 635, 635, 635, - 635, 635, 635, 635, 635, 636, 635, 637, 635, 638, 635, 639, 281, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 9, 9, 9, 9, 9, 640, 9, 9, - 221, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 281, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 276, 26, 0, 0, 0, 0, 258, 363, 0, 0, - 0, 0, 0, 0, 641, 642, 0, 643, 644, 645, 0, 0, 0, 646, 0, 0, - 0, 0, 0, 0, 0, 266, 26, 26, 14, 14, 14, 14, 14, 14, 14, 14, - 247, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 0, 0, 281, 26, 0, 0, 281, 26, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 258, 26, 0, 0, 0, 260, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 647, 648, 0, 649, - 650, 0, 0, 0, 0, 0, 0, 0, 269, 651, 255, 255, 0, 0, 0, 652, - 653, 654, 655, 0, 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 268, 0, 0, 0, 0, 0, 0, 656, 656, 656, 656, 656, 656, 656, 656, - 656, 656, 656, 656, 656, 656, 656, 656, 656, 657, 26, 658, 659, 656, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 348, 660, 308, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 661, 270, 270, 662, 663, 664, 18, 18, - 18, 18, 18, 18, 18, 665, 26, 26, 26, 666, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 667, 667, 667, 667, 667, 668, 667, 669, - 667, 670, 26, 26, 26, 26, 26, 26, 26, 26, 671, 671, 671, 672, 26, 26, - 673, 673, 673, 673, 673, 673, 673, 674, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 675, 675, 675, 675, 675, 676, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 172, 677, 170, 172, 678, 678, 678, 678, 678, 678, 678, 678, - 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, - 679, 678, 680, 26, 26, 26, 26, 26, 681, 681, 681, 681, 681, 681, 681, 681, - 681, 682, 681, 683, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 363, 0, 0, 0, 0, 0, 0, 0, 377, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 363, 0, 0, 0, 0, 0, 0, 276, - 26, 26, 26, 26, 26, 26, 26, 26, 684, 31, 31, 31, 685, 686, 687, 688, - 689, 690, 685, 691, 685, 687, 687, 692, 31, 693, 31, 694, 695, 693, 31, 694, - 26, 26, 26, 26, 26, 26, 51, 26, 0, 0, 0, 0, 0, 281, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 281, 26, 0, 258, 363, 0, - 363, 0, 363, 0, 0, 0, 276, 26, 0, 0, 0, 0, 0, 276, 26, 26, - 26, 26, 26, 26, 696, 0, 0, 0, 697, 26, 0, 0, 0, 0, 0, 281, - 0, 260, 314, 26, 276, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 698, 0, 377, 0, 377, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 258, 699, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 314, 0, 281, 260, 26, 0, 281, 0, 0, 0, 0, 0, 0, - 0, 26, 0, 314, 0, 0, 0, 0, 0, 26, 0, 0, 0, 276, 314, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 281, 26, 0, 276, 0, 377, 0, 260, 0, 0, 0, 0, 0, 269, - 276, 696, 0, 281, 0, 260, 0, 260, 0, 0, 360, 0, 0, 0, 0, 0, - 0, 266, 26, 26, 26, 26, 0, 314, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 277, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 347, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 277, 277, 277, 277, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 347, 26, 277, 277, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 277, 700, 26, 26, 26, 277, 277, 277, 280, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 701, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 702, 26, 26, 26, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 391, 391, 391, 391, 391, 391, 391, 685, 0, 0, 0, 0, 1, 2, 1, 2, + 0, 0, 3, 3, 4, 5, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 6, 0, 0, 7, 0, 8, 8, 8, 8, 8, 8, 8, 9, + 10, 11, 12, 11, 11, 11, 13, 11, 14, 14, 14, 14, 14, 14, 14, 14, + 15, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 16, 17, 18, 17, 17, + 19, 20, 21, 21, 22, 21, 23, 24, 25, 26, 27, 27, 28, 29, 27, 30, + 27, 27, 27, 27, 27, 31, 27, 27, 32, 33, 33, 33, 34, 27, 27, 27, + 35, 35, 35, 36, 37, 37, 37, 38, 39, 39, 40, 41, 42, 43, 44, 27, + 45, 46, 27, 27, 27, 27, 47, 27, 48, 48, 48, 48, 48, 49, 50, 48, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 109, 110, 111, 112, 109, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 122, 123, 122, 124, 125, 125, + 126, 127, 128, 129, 130, 131, 125, 125, 132, 132, 132, 132, 133, 132, 134, 135, + 132, 133, 132, 136, 136, 137, 125, 125, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 139, 139, 140, 139, 139, 141, 142, 142, 142, 142, 142, 142, 142, 142, + 143, 143, 143, 143, 144, 145, 143, 143, 144, 143, 143, 146, 147, 148, 143, 143, + 143, 147, 143, 143, 143, 149, 143, 150, 143, 151, 152, 152, 152, 152, 152, 153, + 154, 154, 154, 154, 154, 154, 154, 154, 155, 156, 157, 157, 157, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 168, 168, 168, 168, 169, 170, 170, + 171, 172, 173, 173, 173, 173, 173, 174, 173, 173, 175, 154, 154, 154, 154, 176, + 177, 178, 179, 179, 180, 181, 182, 183, 184, 184, 185, 184, 186, 187, 168, 168, + 188, 189, 190, 190, 190, 191, 190, 192, 193, 193, 194, 8, 195, 125, 125, 125, + 196, 196, 196, 196, 197, 196, 196, 198, 199, 199, 199, 199, 200, 200, 200, 201, + 202, 202, 202, 203, 204, 205, 205, 205, 206, 139, 139, 207, 208, 209, 210, 211, + 4, 4, 212, 4, 4, 213, 214, 215, 4, 4, 4, 216, 8, 8, 8, 8, + 11, 217, 11, 11, 217, 218, 11, 219, 11, 11, 11, 220, 220, 221, 11, 222, + 223, 0, 0, 0, 0, 0, 224, 225, 226, 227, 0, 0, 228, 8, 8, 229, + 0, 0, 230, 231, 232, 0, 4, 4, 233, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 125, 235, 125, 0, 0, + 236, 236, 236, 236, 236, 236, 236, 236, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 238, 0, 0, 0, 0, 0, 0, 239, 239, 239, 239, 239, 239, 4, 4, + 240, 240, 240, 240, 240, 240, 240, 241, 139, 139, 140, 242, 242, 242, 243, 244, + 143, 245, 246, 246, 246, 246, 14, 14, 0, 0, 0, 0, 0, 247, 125, 125, + 248, 249, 248, 248, 248, 248, 248, 250, 248, 248, 248, 248, 248, 248, 248, 248, + 248, 248, 248, 248, 248, 251, 125, 252, 253, 0, 254, 255, 256, 257, 257, 257, + 257, 258, 259, 260, 260, 260, 260, 261, 262, 263, 263, 264, 142, 142, 142, 142, + 265, 0, 263, 263, 0, 0, 266, 260, 142, 265, 0, 0, 0, 0, 142, 267, + 0, 0, 0, 0, 0, 260, 260, 268, 260, 260, 260, 260, 260, 269, 0, 0, + 248, 248, 248, 248, 0, 0, 0, 0, 270, 270, 270, 270, 270, 270, 270, 270, + 271, 270, 270, 270, 272, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, + 274, 274, 275, 125, 14, 14, 14, 14, 14, 14, 276, 276, 276, 276, 276, 277, + 0, 0, 278, 4, 4, 4, 4, 4, 279, 4, 4, 4, 280, 281, 125, 282, + 283, 283, 284, 285, 286, 286, 286, 287, 288, 288, 288, 288, 289, 290, 48, 48, + 291, 291, 292, 293, 293, 294, 142, 295, 296, 296, 296, 296, 297, 298, 138, 299, + 300, 300, 300, 301, 302, 303, 138, 138, 304, 304, 304, 304, 305, 306, 307, 308, + 309, 310, 246, 4, 4, 311, 312, 152, 152, 152, 152, 152, 307, 307, 313, 314, + 142, 142, 315, 142, 316, 142, 142, 317, 125, 125, 125, 125, 125, 125, 125, 125, + 248, 248, 248, 248, 248, 248, 318, 248, 248, 248, 248, 248, 248, 319, 125, 125, + 320, 321, 21, 322, 323, 27, 27, 27, 27, 27, 27, 27, 324, 325, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 326, 27, 27, 27, 27, + 27, 327, 27, 27, 328, 125, 125, 27, 8, 285, 329, 0, 0, 330, 331, 332, + 27, 27, 27, 27, 27, 27, 27, 333, 334, 0, 1, 2, 1, 2, 335, 259, + 260, 336, 142, 265, 337, 338, 339, 340, 341, 342, 343, 344, 345, 345, 125, 125, + 342, 342, 342, 342, 342, 342, 342, 346, 347, 0, 0, 348, 11, 11, 11, 11, + 349, 350, 351, 125, 125, 0, 0, 352, 353, 354, 355, 355, 355, 356, 357, 252, + 358, 358, 359, 360, 361, 362, 362, 363, 364, 365, 366, 366, 367, 368, 125, 125, + 369, 369, 369, 369, 369, 370, 370, 370, 371, 372, 373, 374, 374, 375, 374, 376, + 377, 377, 378, 379, 379, 379, 380, 381, 381, 382, 383, 384, 125, 125, 125, 125, + 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 386, 385, 387, 388, 125, + 389, 4, 4, 390, 125, 125, 125, 125, 391, 392, 392, 393, 394, 395, 396, 396, + 397, 398, 399, 125, 125, 125, 400, 401, 402, 403, 404, 405, 125, 125, 125, 125, + 406, 406, 407, 408, 407, 409, 407, 407, 410, 411, 412, 413, 414, 414, 415, 415, + 416, 416, 125, 125, 417, 417, 418, 419, 420, 420, 420, 421, 422, 423, 424, 425, + 426, 427, 428, 125, 125, 125, 125, 125, 429, 429, 429, 429, 430, 125, 125, 125, + 431, 431, 431, 432, 431, 431, 431, 433, 434, 434, 435, 436, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 27, 45, 437, 437, 438, 439, 125, 125, 125, 440, + 441, 441, 442, 443, 443, 444, 125, 445, 446, 125, 125, 447, 448, 125, 449, 450, + 451, 451, 451, 451, 452, 453, 451, 454, 455, 455, 455, 455, 456, 457, 458, 459, + 460, 460, 460, 461, 462, 463, 463, 464, 465, 465, 465, 465, 465, 465, 466, 467, + 468, 469, 468, 468, 470, 125, 125, 125, 471, 472, 473, 474, 474, 474, 475, 476, + 477, 478, 479, 480, 481, 482, 483, 484, 485, 485, 485, 485, 485, 486, 487, 125, + 488, 488, 488, 488, 489, 490, 125, 125, 491, 491, 491, 492, 491, 493, 125, 125, + 494, 494, 494, 494, 495, 496, 497, 125, 498, 498, 498, 499, 499, 125, 125, 125, + 500, 501, 502, 500, 503, 125, 125, 125, 504, 504, 504, 505, 125, 125, 125, 125, + 125, 125, 506, 506, 506, 506, 506, 507, 508, 509, 510, 511, 512, 513, 125, 125, + 125, 125, 514, 515, 515, 514, 516, 125, 517, 517, 517, 517, 518, 519, 519, 519, + 519, 519, 520, 154, 521, 521, 521, 522, 523, 125, 125, 125, 125, 125, 125, 125, + 524, 525, 525, 526, 527, 525, 528, 529, 529, 530, 531, 532, 125, 125, 125, 125, + 533, 534, 534, 535, 536, 537, 538, 539, 540, 541, 542, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 543, 544, 545, 546, 545, 547, 545, 548, 125, 125, + 125, 125, 125, 549, 550, 550, 550, 551, 552, 552, 552, 552, 552, 552, 552, 552, + 552, 553, 125, 125, 125, 125, 125, 125, 552, 552, 552, 552, 552, 552, 554, 555, + 552, 552, 552, 552, 556, 125, 125, 125, 125, 557, 557, 557, 557, 557, 557, 558, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 560, 125, 125, + 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 562, 125, 125, 125, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 563, 564, 565, 566, 567, + 567, 567, 567, 568, 569, 570, 571, 572, 573, 573, 573, 573, 574, 575, 576, 577, + 573, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 578, 578, 578, 578, + 578, 579, 125, 125, 125, 125, 125, 125, 580, 580, 580, 580, 581, 580, 580, 580, + 582, 580, 125, 125, 125, 125, 583, 584, 585, 585, 585, 585, 585, 585, 585, 585, + 585, 585, 585, 585, 585, 585, 585, 586, 587, 587, 587, 587, 587, 587, 587, 587, + 587, 587, 587, 587, 587, 588, 125, 125, 589, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 590, 591, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 592, 593, 125, 594, 595, 596, + 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 597, + 598, 598, 598, 598, 598, 598, 599, 600, 601, 602, 266, 125, 125, 125, 125, 125, + 8, 8, 603, 8, 604, 0, 0, 0, 0, 0, 0, 0, 266, 125, 125, 125, + 0, 0, 0, 0, 0, 0, 0, 605, 0, 0, 606, 0, 0, 0, 607, 608, + 609, 0, 610, 0, 0, 0, 235, 125, 11, 11, 11, 11, 611, 125, 125, 125, + 125, 125, 125, 125, 0, 266, 0, 266, 0, 0, 0, 0, 0, 234, 0, 612, + 0, 0, 0, 0, 0, 224, 0, 0, 0, 613, 614, 615, 616, 0, 0, 0, + 617, 618, 0, 619, 620, 621, 0, 0, 0, 0, 622, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 623, 0, 0, 0, 624, 624, 624, 624, 624, 624, 624, 624, + 625, 626, 627, 125, 125, 125, 125, 125, 4, 628, 629, 125, 125, 125, 125, 125, + 630, 631, 632, 14, 14, 14, 633, 125, 634, 125, 125, 125, 125, 125, 125, 125, + 635, 635, 636, 637, 638, 125, 125, 125, 125, 639, 640, 125, 641, 641, 641, 642, + 125, 125, 125, 125, 125, 643, 643, 644, 125, 125, 125, 125, 125, 125, 645, 646, + 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 648, 649, 125, 125, + 650, 650, 650, 650, 651, 652, 125, 125, 125, 125, 125, 125, 125, 125, 125, 334, + 0, 0, 0, 653, 125, 125, 125, 125, 334, 0, 0, 247, 125, 125, 125, 125, + 654, 27, 655, 656, 657, 658, 659, 660, 661, 662, 663, 662, 125, 125, 125, 664, + 0, 0, 252, 0, 0, 0, 0, 0, 0, 266, 226, 334, 334, 334, 0, 605, + 0, 0, 247, 125, 125, 125, 665, 0, 666, 0, 0, 252, 612, 667, 605, 125, + 0, 0, 0, 0, 0, 668, 350, 350, 0, 0, 0, 0, 0, 0, 0, 669, + 0, 0, 0, 0, 0, 285, 252, 228, 252, 0, 0, 0, 670, 285, 0, 0, + 670, 0, 247, 667, 125, 125, 125, 125, 0, 0, 0, 0, 0, 266, 247, 350, + 612, 0, 0, 671, 672, 252, 612, 612, 0, 330, 0, 0, 235, 125, 125, 285, + 248, 248, 248, 248, 248, 248, 125, 125, 248, 248, 248, 319, 248, 248, 248, 248, + 248, 318, 248, 248, 248, 248, 248, 248, 248, 248, 584, 248, 248, 248, 248, 248, + 248, 248, 248, 248, 248, 248, 673, 125, 248, 318, 125, 125, 125, 125, 125, 125, + 248, 248, 248, 248, 674, 248, 248, 248, 248, 248, 248, 125, 125, 125, 125, 125, + 675, 125, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 1, 2, 2, 2, + 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, + 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 8, 8, 8, 8, 16, 8, 8, 8, 17, 18, 18, 18, + 19, 19, 19, 19, 19, 20, 19, 19, 21, 22, 22, 22, 22, 22, 22, 22, + 22, 23, 21, 22, 22, 22, 23, 21, 24, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 12, 12, 25, 25, 26, 27, 25, 28, 12, 12, 29, 30, 29, 31, + 29, 29, 32, 32, 29, 29, 29, 29, 31, 29, 33, 7, 7, 34, 29, 29, + 35, 29, 29, 29, 29, 29, 29, 30, 36, 36, 36, 37, 36, 36, 36, 36, + 36, 36, 38, 39, 40, 40, 40, 40, 41, 12, 12, 12, 42, 42, 42, 42, + 42, 42, 43, 44, 45, 45, 45, 45, 45, 45, 45, 46, 45, 45, 45, 47, + 48, 48, 48, 48, 48, 48, 48, 49, 36, 36, 38, 12, 29, 29, 29, 50, + 51, 12, 29, 29, 52, 29, 29, 29, 53, 53, 53, 53, 54, 55, 53, 53, + 53, 56, 53, 53, 57, 58, 57, 59, 59, 57, 57, 57, 57, 57, 60, 57, + 61, 62, 63, 57, 57, 59, 59, 64, 12, 65, 12, 66, 57, 62, 57, 57, + 57, 57, 57, 64, 67, 67, 68, 69, 70, 71, 71, 71, 71, 71, 72, 71, + 72, 73, 74, 72, 68, 69, 70, 74, 75, 12, 67, 76, 12, 77, 71, 71, + 71, 68, 12, 12, 78, 78, 79, 80, 80, 79, 79, 79, 79, 79, 81, 79, + 81, 78, 82, 79, 79, 80, 80, 82, 83, 12, 12, 12, 79, 84, 79, 79, + 82, 12, 78, 79, 85, 85, 86, 87, 87, 86, 86, 86, 86, 86, 88, 86, + 88, 85, 89, 86, 86, 87, 87, 89, 12, 85, 12, 90, 86, 91, 86, 86, + 86, 86, 12, 12, 92, 93, 94, 92, 95, 96, 97, 95, 98, 99, 94, 92, + 100, 100, 96, 92, 94, 92, 95, 96, 99, 98, 12, 12, 12, 92, 100, 100, + 100, 100, 94, 12, 101, 101, 101, 102, 102, 101, 101, 101, 101, 101, 102, 101, + 101, 101, 103, 101, 101, 102, 102, 103, 12, 104, 105, 106, 101, 107, 101, 101, + 12, 108, 101, 101, 109, 109, 109, 110, 110, 109, 109, 109, 109, 109, 110, 109, + 109, 111, 112, 109, 109, 110, 110, 112, 12, 113, 12, 113, 109, 114, 109, 109, + 111, 12, 12, 12, 115, 115, 115, 116, 116, 115, 115, 115, 115, 115, 115, 115, + 115, 116, 116, 115, 12, 115, 115, 115, 115, 117, 115, 115, 118, 118, 119, 119, + 119, 120, 121, 119, 119, 119, 119, 119, 122, 119, 119, 123, 119, 120, 124, 125, + 119, 126, 119, 119, 12, 121, 119, 119, 121, 127, 12, 12, 128, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 130, 131, 129, 129, 129, 12, 12, 12, 12, 12, + 132, 133, 134, 135, 135, 135, 135, 135, 135, 136, 135, 135, 135, 135, 135, 137, + 135, 138, 135, 134, 135, 135, 137, 135, 139, 139, 139, 139, 139, 139, 140, 139, + 139, 139, 139, 141, 140, 139, 139, 139, 139, 139, 139, 142, 139, 143, 144, 12, + 145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 12, 148, 146, 146, 149, 146, + 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 152, 153, 151, 154, 152, 153, + 152, 153, 151, 154, 152, 153, 151, 151, 151, 154, 151, 151, 151, 151, 154, 155, + 151, 151, 151, 156, 151, 151, 153, 12, 157, 157, 157, 157, 157, 158, 157, 158, + 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 160, 161, 162, 162, 162, 162, + 162, 162, 163, 164, 162, 162, 165, 12, 166, 166, 166, 166, 166, 167, 12, 168, + 169, 169, 169, 169, 169, 170, 12, 12, 171, 171, 171, 171, 171, 12, 12, 12, + 172, 172, 172, 173, 173, 12, 12, 12, 174, 174, 174, 174, 174, 174, 174, 175, + 174, 174, 175, 12, 176, 177, 178, 178, 178, 178, 179, 12, 178, 178, 178, 178, + 178, 178, 180, 12, 178, 178, 181, 12, 159, 182, 12, 12, 183, 183, 183, 183, + 183, 183, 183, 184, 183, 183, 183, 12, 185, 183, 183, 183, 186, 186, 186, 186, + 186, 186, 186, 187, 186, 188, 12, 12, 189, 189, 189, 189, 189, 189, 189, 12, + 189, 189, 190, 12, 189, 189, 191, 192, 193, 193, 193, 193, 193, 193, 193, 194, + 195, 195, 195, 195, 195, 195, 195, 196, 195, 195, 195, 197, 195, 195, 198, 12, + 195, 195, 195, 198, 7, 7, 7, 199, 200, 200, 200, 200, 200, 200, 200, 201, + 200, 200, 200, 202, 203, 203, 203, 203, 204, 204, 204, 204, 204, 12, 12, 204, + 205, 205, 205, 205, 205, 205, 206, 205, 205, 205, 207, 208, 209, 209, 209, 209, + 19, 19, 210, 12, 146, 146, 211, 212, 203, 203, 12, 12, 213, 7, 7, 7, + 214, 7, 215, 216, 0, 215, 217, 12, 2, 218, 219, 2, 2, 2, 2, 220, + 221, 218, 222, 2, 2, 2, 223, 2, 2, 2, 2, 224, 8, 225, 8, 225, + 8, 8, 226, 226, 8, 8, 8, 225, 8, 15, 8, 8, 8, 10, 8, 227, + 10, 15, 8, 14, 0, 0, 0, 228, 0, 229, 0, 0, 230, 0, 0, 231, + 0, 0, 0, 232, 2, 2, 2, 233, 234, 12, 12, 12, 235, 12, 12, 12, + 0, 236, 237, 0, 4, 0, 0, 0, 0, 0, 0, 4, 2, 2, 5, 12, + 0, 232, 12, 12, 0, 0, 232, 12, 238, 238, 238, 238, 0, 239, 0, 0, + 0, 240, 0, 0, 241, 241, 241, 241, 18, 18, 18, 18, 18, 12, 242, 18, + 243, 243, 243, 243, 243, 243, 12, 244, 245, 12, 12, 244, 151, 154, 12, 12, + 151, 154, 151, 154, 0, 0, 0, 246, 247, 247, 247, 247, 247, 247, 248, 247, + 247, 12, 12, 12, 247, 249, 12, 12, 0, 0, 0, 12, 0, 250, 0, 0, + 251, 247, 252, 253, 0, 0, 247, 0, 254, 255, 255, 255, 255, 255, 255, 255, + 255, 256, 257, 258, 259, 260, 260, 260, 260, 260, 260, 260, 260, 260, 261, 259, + 12, 262, 263, 263, 263, 263, 263, 263, 264, 150, 150, 150, 150, 150, 150, 265, + 0, 12, 12, 12, 150, 150, 150, 266, 260, 260, 260, 261, 260, 260, 0, 0, + 267, 267, 267, 267, 267, 267, 267, 268, 267, 269, 12, 12, 270, 270, 270, 270, + 271, 271, 271, 271, 271, 271, 271, 12, 272, 272, 272, 272, 272, 272, 12, 12, + 237, 2, 2, 2, 2, 2, 231, 2, 2, 2, 273, 12, 274, 275, 276, 12, + 277, 2, 2, 2, 278, 278, 278, 278, 278, 278, 278, 279, 0, 0, 246, 12, + 280, 280, 280, 280, 280, 280, 12, 12, 281, 281, 281, 281, 281, 282, 12, 283, + 281, 281, 282, 12, 284, 284, 284, 284, 284, 284, 284, 285, 286, 286, 286, 286, + 286, 12, 12, 287, 150, 150, 150, 288, 289, 289, 289, 289, 289, 289, 289, 290, + 289, 289, 291, 292, 145, 145, 145, 293, 294, 294, 294, 294, 294, 295, 12, 12, + 294, 294, 294, 296, 294, 294, 296, 294, 297, 297, 297, 297, 298, 12, 12, 12, + 12, 12, 299, 297, 300, 300, 300, 300, 300, 301, 12, 12, 155, 154, 155, 154, + 155, 154, 12, 12, 2, 2, 3, 2, 2, 302, 303, 12, 300, 300, 300, 304, + 300, 300, 304, 12, 150, 12, 12, 12, 150, 265, 305, 150, 150, 150, 150, 12, + 247, 247, 247, 249, 247, 247, 249, 12, 2, 273, 12, 12, 306, 22, 12, 24, + 25, 26, 25, 307, 308, 309, 25, 25, 50, 12, 12, 12, 310, 29, 29, 29, + 29, 29, 29, 311, 312, 29, 29, 29, 29, 29, 12, 310, 7, 7, 7, 313, + 232, 0, 0, 0, 0, 232, 0, 12, 29, 314, 29, 29, 29, 29, 29, 315, + 316, 0, 0, 0, 0, 317, 260, 260, 260, 260, 260, 318, 319, 150, 319, 150, + 319, 150, 319, 288, 0, 232, 0, 232, 12, 12, 316, 246, 320, 320, 320, 321, + 320, 320, 320, 320, 320, 322, 320, 320, 320, 320, 322, 323, 320, 320, 320, 324, + 320, 320, 322, 12, 232, 131, 0, 0, 0, 131, 0, 0, 8, 8, 8, 14, + 0, 0, 0, 234, 325, 12, 12, 12, 0, 0, 0, 326, 327, 327, 327, 327, + 327, 327, 327, 328, 329, 329, 329, 329, 330, 12, 12, 12, 215, 0, 0, 0, + 331, 331, 331, 331, 331, 12, 12, 332, 333, 333, 333, 333, 333, 333, 334, 12, + 335, 335, 335, 335, 335, 335, 336, 12, 337, 337, 337, 337, 337, 337, 337, 338, + 339, 339, 339, 339, 339, 12, 339, 339, 339, 340, 12, 12, 341, 341, 341, 341, + 342, 342, 342, 342, 343, 343, 343, 343, 343, 343, 343, 344, 343, 343, 344, 12, + 345, 345, 345, 345, 345, 12, 345, 345, 345, 345, 345, 12, 346, 346, 346, 346, + 346, 346, 12, 12, 347, 347, 347, 347, 347, 12, 12, 348, 349, 349, 350, 349, + 350, 351, 349, 349, 351, 349, 349, 349, 351, 349, 351, 352, 353, 353, 353, 353, + 353, 354, 12, 12, 353, 355, 12, 12, 353, 353, 12, 12, 2, 274, 2, 2, + 356, 2, 273, 12, 357, 358, 359, 357, 357, 357, 357, 357, 357, 360, 361, 362, + 363, 363, 363, 363, 363, 364, 363, 363, 365, 365, 365, 365, 366, 366, 366, 366, + 366, 366, 366, 367, 12, 368, 366, 366, 369, 369, 369, 369, 370, 371, 372, 369, + 373, 373, 373, 373, 373, 373, 373, 374, 375, 375, 375, 375, 375, 375, 376, 377, + 378, 378, 378, 378, 379, 379, 379, 379, 379, 379, 12, 379, 380, 379, 379, 379, + 381, 382, 12, 381, 381, 383, 383, 381, 381, 381, 381, 381, 381, 384, 385, 386, + 381, 381, 387, 12, 388, 388, 388, 388, 389, 389, 389, 389, 390, 390, 390, 390, + 390, 391, 392, 390, 390, 391, 12, 12, 393, 393, 393, 393, 393, 394, 395, 393, + 396, 396, 396, 396, 396, 397, 396, 396, 398, 398, 398, 398, 399, 12, 398, 398, + 400, 400, 400, 400, 401, 12, 402, 403, 12, 12, 402, 400, 404, 404, 404, 404, + 404, 404, 405, 12, 406, 406, 406, 406, 407, 12, 12, 12, 407, 12, 408, 406, + 409, 409, 409, 409, 409, 409, 12, 12, 409, 409, 410, 12, 411, 411, 411, 411, + 411, 411, 412, 413, 413, 12, 12, 12, 12, 12, 12, 414, 415, 415, 415, 415, + 415, 415, 12, 12, 416, 416, 416, 416, 416, 416, 417, 12, 418, 418, 418, 418, + 418, 418, 419, 12, 420, 420, 420, 420, 420, 420, 420, 12, 421, 421, 421, 421, + 421, 422, 12, 12, 423, 423, 423, 423, 423, 423, 423, 424, 425, 423, 423, 423, + 423, 424, 12, 426, 427, 427, 427, 427, 428, 12, 12, 429, 430, 430, 430, 430, + 430, 430, 431, 12, 430, 430, 432, 12, 433, 433, 433, 433, 433, 434, 433, 433, + 433, 433, 12, 12, 435, 435, 435, 435, 435, 436, 12, 12, 437, 437, 437, 437, + 118, 119, 119, 119, 119, 127, 12, 12, 438, 438, 438, 438, 439, 438, 438, 438, + 440, 12, 12, 12, 441, 442, 443, 444, 441, 441, 441, 444, 441, 441, 445, 12, + 446, 446, 446, 446, 446, 446, 447, 12, 446, 446, 448, 12, 449, 450, 449, 451, + 451, 449, 449, 449, 449, 449, 452, 449, 452, 450, 453, 449, 449, 451, 451, 454, + 455, 456, 12, 450, 449, 457, 449, 455, 449, 455, 12, 12, 458, 458, 458, 458, + 458, 458, 458, 459, 460, 12, 12, 12, 461, 461, 461, 461, 461, 461, 12, 12, + 461, 461, 462, 12, 463, 463, 463, 463, 463, 464, 463, 463, 463, 463, 463, 464, + 465, 465, 465, 465, 465, 466, 12, 12, 465, 465, 467, 12, 178, 178, 178, 180, + 468, 468, 468, 468, 468, 468, 469, 12, 470, 470, 470, 470, 470, 470, 471, 472, + 470, 470, 470, 12, 470, 471, 12, 12, 473, 473, 473, 473, 473, 473, 473, 12, + 474, 474, 474, 474, 475, 12, 12, 476, 477, 478, 479, 477, 477, 480, 477, 477, + 477, 477, 477, 477, 477, 481, 482, 477, 477, 478, 12, 12, 477, 477, 483, 12, + 484, 484, 485, 484, 484, 484, 484, 484, 484, 486, 12, 12, 487, 487, 487, 487, + 487, 487, 12, 12, 488, 488, 488, 488, 489, 12, 12, 12, 490, 490, 490, 490, + 490, 490, 491, 12, 53, 53, 492, 12, 493, 493, 494, 493, 493, 493, 493, 493, + 493, 495, 493, 493, 493, 496, 12, 12, 493, 493, 493, 497, 498, 498, 498, 498, + 499, 498, 498, 498, 498, 498, 500, 498, 498, 501, 12, 12, 502, 503, 504, 502, + 502, 502, 502, 502, 502, 503, 505, 504, 502, 502, 12, 12, 502, 502, 506, 12, + 507, 508, 509, 507, 507, 507, 507, 507, 507, 507, 507, 510, 508, 507, 511, 12, + 507, 507, 512, 12, 513, 513, 513, 513, 513, 513, 514, 12, 515, 515, 515, 515, + 516, 515, 515, 515, 515, 515, 517, 518, 515, 515, 519, 12, 520, 12, 12, 12, + 100, 100, 100, 100, 96, 12, 12, 98, 521, 521, 521, 521, 521, 521, 522, 12, + 521, 521, 521, 523, 521, 524, 12, 12, 521, 12, 12, 12, 525, 525, 525, 525, + 526, 12, 12, 12, 527, 527, 527, 527, 527, 528, 12, 12, 529, 529, 529, 529, + 529, 530, 12, 12, 272, 272, 531, 12, 532, 532, 532, 532, 532, 532, 532, 533, + 532, 532, 534, 535, 536, 536, 536, 536, 536, 536, 536, 537, 536, 536, 538, 12, + 539, 539, 539, 539, 539, 539, 539, 540, 539, 540, 12, 12, 541, 541, 541, 541, + 541, 542, 12, 12, 541, 541, 543, 541, 543, 541, 541, 541, 541, 541, 12, 544, + 545, 545, 545, 545, 545, 545, 546, 12, 547, 547, 547, 547, 547, 547, 548, 549, + 547, 547, 12, 549, 550, 551, 12, 12, 249, 12, 12, 12, 552, 552, 552, 552, + 552, 552, 12, 12, 553, 553, 553, 553, 553, 554, 12, 12, 552, 552, 555, 12, + 260, 556, 260, 557, 558, 255, 255, 255, 559, 12, 12, 12, 560, 12, 12, 12, + 256, 561, 12, 12, 12, 260, 12, 12, 562, 562, 562, 562, 562, 562, 562, 12, + 563, 563, 563, 563, 563, 563, 564, 12, 563, 563, 563, 565, 563, 563, 565, 12, + 563, 563, 566, 563, 7, 7, 7, 567, 7, 199, 12, 12, 0, 246, 12, 12, + 0, 232, 316, 0, 0, 568, 228, 0, 0, 0, 568, 7, 213, 569, 7, 0, + 0, 0, 570, 228, 8, 225, 12, 12, 0, 0, 234, 12, 0, 0, 0, 229, + 571, 572, 316, 229, 0, 0, 240, 316, 0, 316, 0, 0, 0, 240, 232, 316, + 0, 229, 0, 229, 0, 0, 240, 232, 0, 573, 239, 0, 229, 0, 0, 0, + 0, 246, 0, 0, 0, 0, 0, 239, 574, 574, 574, 574, 574, 574, 574, 12, + 12, 12, 575, 574, 576, 574, 574, 574, 2, 2, 2, 273, 12, 275, 273, 12, + 241, 577, 241, 241, 241, 241, 578, 241, 579, 580, 577, 12, 19, 19, 19, 581, + 12, 12, 12, 582, 583, 583, 583, 583, 583, 583, 583, 584, 583, 583, 583, 585, + 583, 583, 585, 586, 587, 587, 587, 587, 587, 587, 587, 588, 589, 589, 589, 589, + 589, 589, 590, 591, 592, 592, 592, 592, 592, 592, 593, 12, 151, 154, 151, 594, + 151, 151, 151, 154, 595, 595, 595, 595, 595, 596, 595, 595, 595, 597, 12, 12, + 598, 598, 598, 598, 598, 598, 598, 12, 598, 598, 599, 600, 0, 234, 12, 12, + 29, 414, 29, 29, 601, 602, 414, 29, 50, 29, 603, 12, 604, 310, 603, 414, + 601, 602, 603, 603, 601, 602, 50, 29, 50, 29, 414, 605, 29, 29, 606, 29, + 29, 29, 29, 12, 414, 414, 606, 29, 51, 12, 12, 12, 12, 239, 0, 0, + 607, 12, 12, 12, 246, 12, 12, 12, 0, 0, 12, 0, 0, 232, 131, 0, + 0, 0, 12, 12, 0, 0, 0, 240, 0, 246, 12, 239, 608, 12, 12, 12, + 247, 247, 609, 12, 610, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, @@ -4562,21 +4387,14 @@ _hb_ucd_u16[9320] = 817, 818, 819, 820, 821, 935, 0, 0, }; static const int16_t -_hb_ucd_i16[196] = +_hb_ucd_i16[92] = { - 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, - 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, - 0, 0, 0, 1, -1, 0, 0, 0, 0, 1, -1, 0, 3, 3, 3, -3, - -3, -3, 0, 0, 0, 2016, 0, 0, 0, 0, 0, 2527, 1923, 1914, 1918, 0, - 2250, 0, 0, 0, 0, 0, 0, 138, 0, 7, 0, 0, -7, 0, 0, 0, - 1, -1, 1, -1, -1, 1, -1, 0, 1824, 0, 0, 0, 0, 0, 2104, 0, - 2108, 2106, 0, 2106, 1316, 0, 0, 0, 0, 1, -1, 1, -1, -138, 0, 0, - 1, -1, 8, 8, 8, 0, 7, 7, 0, 0, -8, -8, -8, -7, -7, 0, - 1, -1, 0, 2,-1316, 1, -1, 0, -1, 1, -1, 1, -1, 3, 1, -1, - -3, 1, -1, 1, -1, 0, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0, 0, - 0,-2016, 0, 0, 1, -1, 0, 1, 0, 0,-2104, 0, 0, 0, 0,-2106, - -2108,-2106, 0, 0, 1, -1,-2250, 0, 0, 0,-2527, 0, 0, -2, 0, 1, - -1, 0, 1, -1, + 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, + 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, + 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, + -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, + 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, }; static inline uint_fast8_t @@ -4597,17 +4415,17 @@ _hb_ucd_b4 (const uint8_t* a, unsigned i) static inline int_fast16_t _hb_ucd_bmg (unsigned u) { - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9540+(((_hb_ucd_u8[9420+(((_hb_ucd_b4(9292+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9684+(((_hb_ucd_u8[9452+(((_hb_ucd_u8[9356+(((_hb_ucd_b4(9292+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; } static inline uint_fast8_t _hb_ucd_sc (unsigned u) { - return u<918000u?_hb_ucd_u8[11062+(((_hb_ucd_u16[2040+(((_hb_ucd_u8[10326+(((_hb_ucd_u8[9876+(u>>3>>4>>4)])<<4)+((u>>3>>4)&15u))])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:2; + return u<918000u?_hb_ucd_u8[11118+(((_hb_ucd_u16[4024+(((_hb_ucd_u16[2040+(((_hb_ucd_u8[10382+(((_hb_ucd_u8[9932+(u>>2>>2>>3>>4)])<<4)+((u>>2>>2>>3)&15u))])<<3)+((u>>2>>2)&7u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; } static inline uint_fast16_t _hb_ucd_dm (unsigned u) { - return u<195102u?_hb_ucd_u16[6008+(((_hb_ucd_u8[17068+(((_hb_ucd_u8[16686+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; + return u<195102u?_hb_ucd_u16[6728+(((_hb_ucd_u8[13944+(((_hb_ucd_u8[13562+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; } diff --git a/thirdparty/harfbuzz/src/hb-ucd.cc b/thirdparty/harfbuzz/src/hb-ucd.cc index baea224a25..4c8b1ee5e6 100644 --- a/thirdparty/harfbuzz/src/hb-ucd.cc +++ b/thirdparty/harfbuzz/src/hb-ucd.cc @@ -129,12 +129,16 @@ hb_ucd_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab, void *user_data HB_UNUSED) { + // Hangul is handled algorithmically. if (_hb_ucd_compose_hangul (a, b, ab)) return true; hb_codepoint_t u = 0; if ((a & 0xFFFFF800u) == 0x0000u && (b & 0xFFFFFF80) == 0x0300u) { + /* If "a" is small enough and "b" is in the U+0300 range, + * the composition data is encoded in a 32bit array sorted + * by "a,b" pair. */ uint32_t k = HB_CODEPOINT_ENCODE3_11_7_14 (a, b, 0); const uint32_t *v = hb_bsearch (k, _hb_ucd_dm2_u32_map, @@ -146,6 +150,8 @@ hb_ucd_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, } else { + /* Otherwise it is stored in a 64bit array sorted by + * "a,b" pair. */ uint64_t k = HB_CODEPOINT_ENCODE3 (a, b, 0); const uint64_t *v = hb_bsearch (k, _hb_ucd_dm2_u64_map, @@ -170,15 +176,22 @@ hb_ucd_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, unsigned i = _hb_ucd_dm (ab); + /* If no data, there's no decomposition. */ if (likely (!i)) return false; i--; + /* Check if it's a single-character decomposition. */ if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map)) { + /* Single-character decompositions currently are only in plane 0 or plane 2. */ if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map)) + { + /* Plane 0. */ *a = _hb_ucd_dm1_p0_map[i]; + } else { + /* Plane 2. */ i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map); *a = 0x20000 | _hb_ucd_dm1_p2_map[i]; } @@ -187,8 +200,10 @@ hb_ucd_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, } i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map); + /* Otherwise they are encoded either in a 32bit array or a 64bit array. */ if (i < ARRAY_LENGTH (_hb_ucd_dm2_u32_map)) { + /* 32bit array. */ uint32_t v = _hb_ucd_dm2_u32_map[i]; *a = HB_CODEPOINT_DECODE3_11_7_14_1 (v); *b = HB_CODEPOINT_DECODE3_11_7_14_2 (v); @@ -196,6 +211,7 @@ hb_ucd_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, } i -= ARRAY_LENGTH (_hb_ucd_dm2_u32_map); + /* 64bit array. */ uint64_t v = _hb_ucd_dm2_u64_map[i]; *a = HB_CODEPOINT_DECODE3_1 (v); *b = HB_CODEPOINT_DECODE3_2 (v); diff --git a/thirdparty/harfbuzz/src/hb-unicode.cc b/thirdparty/harfbuzz/src/hb-unicode.cc index c1795dc7f2..9a6471e52c 100644 --- a/thirdparty/harfbuzz/src/hb-unicode.cc +++ b/thirdparty/harfbuzz/src/hb-unicode.cc @@ -377,20 +377,30 @@ hb_unicode_funcs_set_##name##_func (hb_unicode_funcs_t *ufuncs, \ hb_destroy_func_t destroy) \ { \ if (hb_object_is_immutable (ufuncs)) \ - return; \ + goto fail; \ + \ + if (!func) \ + { \ + if (destroy) \ + destroy (user_data); \ + destroy = nullptr; \ + user_data = ufuncs->parent->user_data.name; \ + } \ \ if (ufuncs->destroy.name) \ ufuncs->destroy.name (ufuncs->user_data.name); \ \ - if (func) { \ + if (func) \ ufuncs->func.name = func; \ - ufuncs->user_data.name = user_data; \ - ufuncs->destroy.name = destroy; \ - } else { \ + else \ ufuncs->func.name = ufuncs->parent->func.name; \ - ufuncs->user_data.name = ufuncs->parent->user_data.name; \ - ufuncs->destroy.name = nullptr; \ - } \ + ufuncs->user_data.name = user_data; \ + ufuncs->destroy.name = destroy; \ + return; \ + \ +fail: \ + if (destroy) \ + destroy (user_data); \ } HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS diff --git a/thirdparty/harfbuzz/src/hb-uniscribe.cc b/thirdparty/harfbuzz/src/hb-uniscribe.cc index 50f71ce9ce..9648e02663 100644 --- a/thirdparty/harfbuzz/src/hb-uniscribe.cc +++ b/thirdparty/harfbuzz/src/hb-uniscribe.cc @@ -355,7 +355,7 @@ _hb_rename_font (hb_blob_t *blob, wchar_t *new_name) return nullptr; } - memcpy(new_sfnt_data, orig_sfnt_data, length); + hb_memcpy(new_sfnt_data, orig_sfnt_data, length); OT::name &name = StructAtOffset<OT::name> (new_sfnt_data, name_table_offset); name.format = 0; @@ -478,11 +478,11 @@ populate_log_font (LOGFONTW *lf, hb_font_t *font, unsigned int font_size) { - memset (lf, 0, sizeof (*lf)); + hb_memset (lf, 0, sizeof (*lf)); lf->lfHeight = - (int) font_size; lf->lfCharSet = DEFAULT_CHARSET; - memcpy (lf->lfFaceName, font->face->data.uniscribe->face_name, sizeof (lf->lfFaceName)); + hb_memcpy (lf->lfFaceName, font->face->data.uniscribe->face_name, sizeof (lf->lfFaceName)); return true; } diff --git a/thirdparty/harfbuzz/src/hb-vector.hh b/thirdparty/harfbuzz/src/hb-vector.hh index a6d9f6b3fb..9b52f5ca95 100644 --- a/thirdparty/harfbuzz/src/hb-vector.hh +++ b/thirdparty/harfbuzz/src/hb-vector.hh @@ -35,7 +35,7 @@ template <typename Type, bool sorted=false> -struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty_t>::type +struct hb_vector_t { typedef Type item_t; static constexpr unsigned item_size = hb_static_size (Type); @@ -53,9 +53,10 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty hb_requires (hb_is_iterable (Iterable))> hb_vector_t (const Iterable &o) : hb_vector_t () { - if (hb_iter (o).is_random_access_iterator) - alloc (hb_len (hb_iter (o))); - hb_copy (o, *this); + auto iter = hb_iter (o); + if (iter.is_random_access_iterator) + alloc (hb_len (iter)); + hb_copy (iter, *this); } hb_vector_t (const hb_vector_t &o) : hb_vector_t () { @@ -83,6 +84,9 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty allocated = length = 0; arrayZ = nullptr; } + void init0 () + { + } void fini () { @@ -94,7 +98,11 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty void reset () { if (unlikely (in_error ())) - allocated = length; // Big hack! + /* Big Hack! We don't know the true allocated size before + * an allocation failure happened. But we know it was at + * least as big as length. Restore it to that and continue + * as if error did not happen. */ + allocated = length; resize (0); } @@ -122,7 +130,7 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty } hb_bytes_t as_bytes () const - { return hb_bytes_t ((const char *) arrayZ, length * item_size); } + { return hb_bytes_t ((const char *) arrayZ, get_size ()); } bool operator == (const hb_vector_t &o) const { return as_array () == o.as_array (); } bool operator != (const hb_vector_t &o) const { return !(*this == o); } @@ -164,14 +172,10 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty operator iter_t () const { return iter (); } operator writer_t () { return writer (); } - c_array_t sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count); } - c_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count); } - array_t sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count); } - array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count); } + /* Faster range-based for loop. */ + Type *begin () const { return arrayZ; } + Type *end () const { return arrayZ + length; } + hb_sorted_array_t<Type> as_sorted_array () { return hb_sorted_array (arrayZ, length); } @@ -240,12 +244,11 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty if (likely (new_array)) { for (unsigned i = 0; i < length; i++) + { new (std::addressof (new_array[i])) Type (); - for (unsigned i = 0; i < (unsigned) length; i++) new_array[i] = std::move (arrayZ[i]); - unsigned old_length = length; - shrink_vector (0); - length = old_length; + arrayZ[i].~Type (); + } hb_free (arrayZ); } return new_array; @@ -277,7 +280,14 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty copy_vector (const hb_vector_t &other) { length = other.length; - hb_memcpy ((void *) arrayZ, (const void *) other.arrayZ, length * item_size); +#ifndef HB_OPTIMIZE_SIZE + if (sizeof (T) >= sizeof (long long)) + /* This runs faster because of alignment. */ + for (unsigned i = 0; i < length; i++) + arrayZ[i] = other.arrayZ[i]; + else +#endif + hb_memcpy ((void *) arrayZ, (const void *) other.arrayZ, length * item_size); } template <typename T = Type, hb_enable_if (!hb_is_trivially_copyable (T) && @@ -309,15 +319,6 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty } } - template <typename T = Type, - hb_enable_if (hb_is_trivially_destructible(T))> - void - shrink_vector (unsigned size) - { - length = size; - } - template <typename T = Type, - hb_enable_if (!hb_is_trivially_destructible(T))> void shrink_vector (unsigned size) { @@ -328,17 +329,6 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty } } - template <typename T = Type, - hb_enable_if (hb_is_trivially_copy_assignable(T))> - void - shift_down_vector (unsigned i) - { - memmove (static_cast<void *> (&arrayZ[i - 1]), - static_cast<void *> (&arrayZ[i]), - (length - i) * sizeof (Type)); - } - template <typename T = Type, - hb_enable_if (!hb_is_trivially_copy_assignable(T))> void shift_down_vector (unsigned i) { @@ -381,16 +371,22 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty return true; } - bool resize (int size_) + bool resize (int size_, bool initialize = true) { unsigned int size = size_ < 0 ? 0u : (unsigned int) size_; if (!alloc (size)) return false; if (size > length) - grow_vector (size); + { + if (initialize) + grow_vector (size); + } else if (size < length) - shrink_vector (size); + { + if (initialize) + shrink_vector (size); + } length = size; return true; @@ -399,13 +395,13 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty Type pop () { if (!length) return Null (Type); - Type v = arrayZ[length - 1]; + Type v {std::move (arrayZ[length - 1])}; arrayZ[length - 1].~Type (); length--; return v; } - void remove (unsigned int i) + void remove_ordered (unsigned int i) { if (unlikely (i >= length)) return; @@ -414,6 +410,18 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty length--; } + template <bool Sorted = sorted, + hb_enable_if (!Sorted)> + void remove_unordered (unsigned int i) + { + if (unlikely (i >= length)) + return; + if (i != length - 1) + arrayZ[i] = std::move (arrayZ[length - 1]); + arrayZ[length - 1].~Type (); + length--; + } + void shrink (int size_) { unsigned int size = size_ < 0 ? 0u : (unsigned int) size_; @@ -425,10 +433,8 @@ struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty /* Sorting API. */ - void qsort (int (*cmp)(const void*, const void*)) + void qsort (int (*cmp)(const void*, const void*) = Type::cmp) { as_array ().qsort (cmp); } - void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1) - { as_array ().qsort (start, end); } /* Unsorted search API. */ template <typename T> diff --git a/thirdparty/harfbuzz/src/hb-version.h b/thirdparty/harfbuzz/src/hb-version.h index 8d6ec66e13..1070262a3b 100644 --- a/thirdparty/harfbuzz/src/hb-version.h +++ b/thirdparty/harfbuzz/src/hb-version.h @@ -41,26 +41,26 @@ HB_BEGIN_DECLS * * The major component of the library version available at compile-time. */ -#define HB_VERSION_MAJOR 5 +#define HB_VERSION_MAJOR 6 /** * HB_VERSION_MINOR: * * The minor component of the library version available at compile-time. */ -#define HB_VERSION_MINOR 3 +#define HB_VERSION_MINOR 0 /** * HB_VERSION_MICRO: * * The micro component of the library version available at compile-time. */ -#define HB_VERSION_MICRO 1 +#define HB_VERSION_MICRO 0 /** * HB_VERSION_STRING: * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "5.3.1" +#define HB_VERSION_STRING "6.0.0" /** * HB_VERSION_ATLEAST: diff --git a/thirdparty/harfbuzz/src/hb.hh b/thirdparty/harfbuzz/src/hb.hh index 8ec638a2b4..410d090d36 100644 --- a/thirdparty/harfbuzz/src/hb.hh +++ b/thirdparty/harfbuzz/src/hb.hh @@ -126,6 +126,7 @@ /* Ignored intentionally. */ #ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_IGNORED #pragma GCC diagnostic ignored "-Wclass-memaccess" +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" // https://github.com/harfbuzz/harfbuzz/pull/3859#issuecomment-1295409126 #pragma GCC diagnostic ignored "-Wformat-nonliteral" #pragma GCC diagnostic ignored "-Wformat-zero-length" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" diff --git a/thirdparty/libpng/arm/arm_init.c b/thirdparty/libpng/arm/arm_init.c index ab22525b38..84d05556f8 100644 --- a/thirdparty/libpng/arm/arm_init.c +++ b/thirdparty/libpng/arm/arm_init.c @@ -36,7 +36,10 @@ #ifndef PNG_ARM_NEON_FILE # if defined(__aarch64__) || defined(_M_ARM64) /* ARM Neon is expected to be unconditionally available on ARM64. */ -# error "PNG_ARM_NEON_CHECK_SUPPORTED must not be defined on this platform" +# error "PNG_ARM_NEON_CHECK_SUPPORTED must not be defined on ARM64" +# elif defined(__ARM_NEON__) || defined(__ARM_NEON) + /* ARM Neon is expected to be available on the target CPU architecture. */ +# error "PNG_ARM_NEON_CHECK_SUPPORTED must not be defined on this CPU arch" # elif defined(__linux__) # define PNG_ARM_NEON_FILE "contrib/arm-neon/linux.c" # else diff --git a/thirdparty/libpng/png.c b/thirdparty/libpng/png.c index fc09564262..4f3e8bbd31 100644 --- a/thirdparty/libpng/png.c +++ b/thirdparty/libpng/png.c @@ -14,7 +14,7 @@ #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_6_38 Your_png_h_is_not_version_1_6_38; +typedef png_libpng_version_1_6_39 Your_png_h_is_not_version_1_6_39; #ifdef __GNUC__ /* The version tests may need to be added to, but the problem warning has @@ -815,7 +815,7 @@ png_get_copyright(png_const_structrp png_ptr) return PNG_STRING_COPYRIGHT #else return PNG_STRING_NEWLINE \ - "libpng version 1.6.38" PNG_STRING_NEWLINE \ + "libpng version 1.6.39" PNG_STRING_NEWLINE \ "Copyright (c) 2018-2022 Cosmin Truta" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \ PNG_STRING_NEWLINE \ @@ -2710,7 +2710,7 @@ png_check_IHDR(png_const_structrp png_ptr, int /* PRIVATE */ png_check_fp_number(png_const_charp string, size_t size, int *statep, - png_size_tp whereami) + size_t *whereami) { int state = *statep; size_t i = *whereami; diff --git a/thirdparty/libpng/png.h b/thirdparty/libpng/png.h index 5fb494fb1a..f109cdf336 100644 --- a/thirdparty/libpng/png.h +++ b/thirdparty/libpng/png.h @@ -1,7 +1,7 @@ /* png.h - header file for PNG reference library * - * libpng version 1.6.38 - September 14, 2022 + * libpng version 1.6.39 - November 20, 2022 * * Copyright (c) 2018-2022 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson @@ -15,7 +15,7 @@ * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger * libpng versions 0.97, January 1998, through 1.6.35, July 2018: * Glenn Randers-Pehrson - * libpng versions 1.6.36, December 2018, through 1.6.38, September 2022: + * libpng versions 1.6.36, December 2018, through 1.6.39, November 2022: * Cosmin Truta * See also "Contributing Authors", below. */ @@ -239,7 +239,7 @@ * ... * 1.5.30 15 10530 15.so.15.30[.0] * ... - * 1.6.38 16 10638 16.so.16.38[.0] + * 1.6.39 16 10639 16.so.16.39[.0] * * Henceforth the source version will match the shared-library major and * minor numbers; the shared-library major version number will be used for @@ -278,8 +278,8 @@ */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.6.38" -#define PNG_HEADER_VERSION_STRING " libpng version 1.6.38 - September 14, 2022\n" +#define PNG_LIBPNG_VER_STRING "1.6.39" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.39 - November 20, 2022\n" #define PNG_LIBPNG_VER_SONUM 16 #define PNG_LIBPNG_VER_DLLNUM 16 @@ -287,7 +287,7 @@ /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ #define PNG_LIBPNG_VER_MAJOR 1 #define PNG_LIBPNG_VER_MINOR 6 -#define PNG_LIBPNG_VER_RELEASE 38 +#define PNG_LIBPNG_VER_RELEASE 39 /* This should be zero for a public release, or non-zero for a * development version. [Deprecated] @@ -318,7 +318,7 @@ * From version 1.0.1 it is: * XXYYZZ, where XX=major, YY=minor, ZZ=release */ -#define PNG_LIBPNG_VER 10638 /* 1.6.38 */ +#define PNG_LIBPNG_VER 10639 /* 1.6.39 */ /* Library configuration: these options cannot be changed after * the library has been built. @@ -428,7 +428,7 @@ extern "C" { /* This triggers a compiler error in png.c, if png.c and png.h * do not agree upon the version number. */ -typedef char* png_libpng_version_1_6_38; +typedef char* png_libpng_version_1_6_39; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * diff --git a/thirdparty/libpng/pngconf.h b/thirdparty/libpng/pngconf.h index 89d28f83a0..fcb4b43069 100644 --- a/thirdparty/libpng/pngconf.h +++ b/thirdparty/libpng/pngconf.h @@ -1,7 +1,7 @@ /* pngconf.h - machine-configurable file for libpng * - * libpng version 1.6.38 + * libpng version 1.6.39 * * Copyright (c) 2018-2022 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson diff --git a/thirdparty/libpng/pnglibconf.h b/thirdparty/libpng/pnglibconf.h index 89d5b4c8ad..e5948c8ce1 100644 --- a/thirdparty/libpng/pnglibconf.h +++ b/thirdparty/libpng/pnglibconf.h @@ -1,6 +1,6 @@ /* pnglibconf.h - library build configuration */ -/* libpng version 1.6.38 */ +/* libpng version 1.6.39 */ /* Copyright (c) 2018-2022 Cosmin Truta */ /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ diff --git a/thirdparty/libpng/pngpriv.h b/thirdparty/libpng/pngpriv.h index 2e426cf47d..b8a73b685d 100644 --- a/thirdparty/libpng/pngpriv.h +++ b/thirdparty/libpng/pngpriv.h @@ -1946,7 +1946,7 @@ PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr, * the problem character.) This has not been tested within libpng. */ PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string, - size_t size, int *statep, png_size_tp whereami),PNG_EMPTY); + size_t size, int *statep, size_t *whereami),PNG_EMPTY); /* This is the same but it checks a complete string and returns true * only if it just contains a floating point number. As of 1.5.4 this diff --git a/thirdparty/libpng/pngread.c b/thirdparty/libpng/pngread.c index 5ab9224038..96996ced5b 100644 --- a/thirdparty/libpng/pngread.c +++ b/thirdparty/libpng/pngread.c @@ -3762,13 +3762,13 @@ png_image_read_direct(png_voidp argument) mode = PNG_ALPHA_PNG; output_gamma = PNG_DEFAULT_sRGB; } - + if ((change & PNG_FORMAT_FLAG_ASSOCIATED_ALPHA) != 0) { mode = PNG_ALPHA_OPTIMIZED; change &= ~PNG_FORMAT_FLAG_ASSOCIATED_ALPHA; } - + /* If 'do_local_background' is set check for the presence of gamma * correction; this is part of the work-round for the libpng bug * described above. diff --git a/thirdparty/libpng/pngrutil.c b/thirdparty/libpng/pngrutil.c index ca060dd15f..068ab193a3 100644 --- a/thirdparty/libpng/pngrutil.c +++ b/thirdparty/libpng/pngrutil.c @@ -3186,7 +3186,7 @@ png_check_chunk_length(png_const_structrp png_ptr, png_uint_32 length) { png_debug2(0," length = %lu, limit = %lu", (unsigned long)length,(unsigned long)limit); - png_chunk_error(png_ptr, "chunk data is too large"); + png_benign_error(png_ptr, "chunk data is too large"); } } diff --git a/thirdparty/libpng/pngwrite.c b/thirdparty/libpng/pngwrite.c index 06c45d16ab..4e58d776a9 100644 --- a/thirdparty/libpng/pngwrite.c +++ b/thirdparty/libpng/pngwrite.c @@ -75,10 +75,10 @@ write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr, * library. If you have a new chunk to add, make a function to write it, * and put it in the correct location here. If you want the chunk written * after the image data, put it in png_write_end(). I strongly encourage - * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing - * the chunk, as that will keep the code from breaking if you want to just - * write a plain PNG file. If you have long comments, I suggest writing - * them in png_write_end(), and compressing them. + * you to supply a PNG_INFO_<chunk> flag, and check info_ptr->valid before + * writing the chunk, as that will keep the code from breaking if you want + * to just write a plain PNG file. If you have long comments, I suggest + * writing them in png_write_end(), and compressing them. */ void PNGAPI png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr) diff --git a/thirdparty/libpng/pngwutil.c b/thirdparty/libpng/pngwutil.c index 16345e4c0b..01f0607c70 100644 --- a/thirdparty/libpng/pngwutil.c +++ b/thirdparty/libpng/pngwutil.c @@ -1,7 +1,7 @@ /* pngwutil.c - utilities to write a PNG file * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2022 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -1747,7 +1747,7 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, { png_uint_32 purpose_len; size_t units_len, total_len; - png_size_tp params_len; + size_t *params_len; png_byte buf[10]; png_byte new_purpose[80]; int i; @@ -1769,7 +1769,7 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, png_debug1(3, "pCAL units length = %d", (int)units_len); total_len = purpose_len + units_len + 10; - params_len = (png_size_tp)png_malloc(png_ptr, + params_len = (size_t *)png_malloc(png_ptr, (png_alloc_size_t)((png_alloc_size_t)nparams * (sizeof (size_t)))); /* Find the length of each parameter, making sure we don't count the diff --git a/thirdparty/mbedtls/include/mbedtls/asn1write.h b/thirdparty/mbedtls/include/mbedtls/asn1write.h index 44afae0e56..5da7654bb4 100644 --- a/thirdparty/mbedtls/include/mbedtls/asn1write.h +++ b/thirdparty/mbedtls/include/mbedtls/asn1write.h @@ -90,7 +90,7 @@ int mbedtls_asn1_write_raw_buffer( unsigned char **p, unsigned char *start, #if defined(MBEDTLS_BIGNUM_C) /** - * \brief Write a arbitrary-precision number (#MBEDTLS_ASN1_INTEGER) + * \brief Write an arbitrary-precision number (#MBEDTLS_ASN1_INTEGER) * in ASN.1 format. * * \note This function works backwards in data buffer. diff --git a/thirdparty/mbedtls/include/mbedtls/bignum.h b/thirdparty/mbedtls/include/mbedtls/bignum.h index dd594c512d..c61db82c6d 100644 --- a/thirdparty/mbedtls/include/mbedtls/bignum.h +++ b/thirdparty/mbedtls/include/mbedtls/bignum.h @@ -182,6 +182,20 @@ #endif /* !MBEDTLS_NO_UDBL_DIVISION */ #endif /* !MBEDTLS_HAVE_INT64 */ +/** \typedef mbedtls_mpi_uint + * \brief The type of machine digits in a bignum, called _limbs_. + * + * This is always an unsigned integer type with no padding bits. The size + * is platform-dependent. + */ + +/** \typedef mbedtls_mpi_sint + * \brief The signed type corresponding to #mbedtls_mpi_uint. + * + * This is always a signed integer type with no padding bits. The size + * is platform-dependent. + */ + #ifdef __cplusplus extern "C" { #endif @@ -191,9 +205,27 @@ extern "C" { */ typedef struct mbedtls_mpi { - int s; /*!< Sign: -1 if the mpi is negative, 1 otherwise */ - size_t n; /*!< total # of limbs */ - mbedtls_mpi_uint *p; /*!< pointer to limbs */ + /** Sign: -1 if the mpi is negative, 1 otherwise. + * + * The number 0 must be represented with `s = +1`. Although many library + * functions treat all-limbs-zero as equivalent to a valid representation + * of 0 regardless of the sign bit, there are exceptions, so bignum + * functions and external callers must always set \c s to +1 for the + * number zero. + * + * Note that this implies that calloc() or `... = {0}` does not create + * a valid MPI representation. You must call mbedtls_mpi_init(). + */ + int s; + + /** Total number of limbs in \c p. */ + size_t n; + + /** Pointer to limbs. + * + * This may be \c NULL if \c n is 0. + */ + mbedtls_mpi_uint *p; } mbedtls_mpi; @@ -280,7 +312,7 @@ void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y ); * \param Y The MPI to be assigned from. This must point to an * initialized MPI. * \param assign The condition deciding whether to perform the - * assignment or not. Possible values: + * assignment or not. Must be either 0 or 1: * * \c 1: Perform the assignment `X = Y`. * * \c 0: Keep the original value of \p X. * @@ -291,6 +323,10 @@ void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y ); * information through branch prediction and/or memory access * patterns analysis). * + * \warning If \p assign is neither 0 nor 1, the result of this function + * is indeterminate, and the resulting value in \p X might be + * neither its original value nor the value in \p Y. + * * \return \c 0 if successful. * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. * \return Another negative error code on other kinds of failure. @@ -303,24 +339,28 @@ int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned * * \param X The first MPI. This must be initialized. * \param Y The second MPI. This must be initialized. - * \param assign The condition deciding whether to perform - * the swap or not. Possible values: + * \param swap The condition deciding whether to perform + * the swap or not. Must be either 0 or 1: * * \c 1: Swap the values of \p X and \p Y. * * \c 0: Keep the original values of \p X and \p Y. * * \note This function is equivalent to - * if( assign ) mbedtls_mpi_swap( X, Y ); + * if( swap ) mbedtls_mpi_swap( X, Y ); * except that it avoids leaking any information about whether - * the assignment was done or not (the above code may leak + * the swap was done or not (the above code may leak * information through branch prediction and/or memory access * patterns analysis). * + * \warning If \p swap is neither 0 nor 1, the result of this function + * is indeterminate, and both \p X and \p Y might end up with + * values different to either of the original ones. + * * \return \c 0 if successful. * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. * \return Another negative error code on other kinds of failure. * */ -int mbedtls_mpi_safe_cond_swap( mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char assign ); +int mbedtls_mpi_safe_cond_swap( mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char swap ); /** * \brief Store integer value in MPI. @@ -753,11 +793,11 @@ int mbedtls_mpi_mul_int( mbedtls_mpi *X, const mbedtls_mpi *A, * * \param Q The destination MPI for the quotient. * This may be \c NULL if the value of the - * quotient is not needed. + * quotient is not needed. This must not alias A or B. * \param R The destination MPI for the remainder value. * This may be \c NULL if the value of the - * remainder is not needed. - * \param A The dividend. This must point to an initialized MPi. + * remainder is not needed. This must not alias A or B. + * \param A The dividend. This must point to an initialized MPI. * \param B The divisor. This must point to an initialized MPI. * * \return \c 0 if successful. @@ -774,10 +814,10 @@ int mbedtls_mpi_div_mpi( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, * * \param Q The destination MPI for the quotient. * This may be \c NULL if the value of the - * quotient is not needed. + * quotient is not needed. This must not alias A. * \param R The destination MPI for the remainder value. * This may be \c NULL if the value of the - * remainder is not needed. + * remainder is not needed. This must not alias A. * \param A The dividend. This must point to an initialized MPi. * \param b The divisor. * @@ -832,6 +872,7 @@ int mbedtls_mpi_mod_int( mbedtls_mpi_uint *r, const mbedtls_mpi *A, * \brief Perform a sliding-window exponentiation: X = A^E mod N * * \param X The destination MPI. This must point to an initialized MPI. + * This must not alias E or N. * \param A The base of the exponentiation. * This must point to an initialized MPI. * \param E The exponent MPI. This must point to an initialized MPI. diff --git a/thirdparty/mbedtls/include/mbedtls/bn_mul.h b/thirdparty/mbedtls/include/mbedtls/bn_mul.h index 31137cd4c2..a3fc363815 100644 --- a/thirdparty/mbedtls/include/mbedtls/bn_mul.h +++ b/thirdparty/mbedtls/include/mbedtls/bn_mul.h @@ -95,12 +95,28 @@ ( !defined(__ARMCC_VERSION) || __ARMCC_VERSION >= 6000000 ) /* + * GCC < 5.0 treated the x86 ebx (which is used for the GOT) as a + * fixed reserved register when building as PIC, leading to errors + * like: bn_mul.h:46:13: error: PIC register clobbered by 'ebx' in 'asm' + * + * This is fixed by an improved register allocator in GCC 5+. From the + * release notes: + * Register allocation improvements: Reuse of the PIC hard register, + * instead of using a fixed register, was implemented on x86/x86-64 + * targets. This improves generated PIC code performance as more hard + * registers can be used. + */ +#if defined(__GNUC__) && __GNUC__ < 5 && defined(__PIC__) +#define MULADDC_CANNOT_USE_EBX +#endif + +/* * Disable use of the i386 assembly code below if option -O0, to disable all * compiler optimisations, is passed, detected with __OPTIMIZE__ * This is done as the number of registers used in the assembly code doesn't * work with the -O0 option. */ -#if defined(__i386__) && defined(__OPTIMIZE__) +#if defined(__i386__) && defined(__OPTIMIZE__) && !defined(MULADDC_CANNOT_USE_EBX) #define MULADDC_INIT \ asm( \ @@ -563,10 +579,20 @@ "andi r7, r6, 0xffff \n\t" \ "bsrli r6, r6, 16 \n\t" -#define MULADDC_CORE \ +#if(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define MULADDC_LHUI \ + "lhui r9, r3, 0 \n\t" \ + "addi r3, r3, 2 \n\t" \ + "lhui r8, r3, 0 \n\t" +#else +#define MULADDC_LHUI \ "lhui r8, r3, 0 \n\t" \ "addi r3, r3, 2 \n\t" \ - "lhui r9, r3, 0 \n\t" \ + "lhui r9, r3, 0 \n\t" +#endif + +#define MULADDC_CORE \ + MULADDC_LHUI \ "addi r3, r3, 2 \n\t" \ "mul r10, r9, r6 \n\t" \ "mul r11, r8, r7 \n\t" \ diff --git a/thirdparty/mbedtls/include/mbedtls/check_config.h b/thirdparty/mbedtls/include/mbedtls/check_config.h index be5c548e56..7ae1ff94db 100644 --- a/thirdparty/mbedtls/include/mbedtls/check_config.h +++ b/thirdparty/mbedtls/include/mbedtls/check_config.h @@ -525,6 +525,20 @@ #error "MBEDTLS_PLATFORM_SNPRINTF_MACRO and MBEDTLS_PLATFORM_STD_SNPRINTF/MBEDTLS_PLATFORM_SNPRINTF_ALT cannot be defined simultaneously" #endif +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_VSNPRINTF_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_VSNPRINTF_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_VSNPRINTF) ||\ + defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) ) +#error "MBEDTLS_PLATFORM_VSNPRINTF_MACRO and MBEDTLS_PLATFORM_STD_VSNPRINTF/MBEDTLS_PLATFORM_VSNPRINTF_ALT cannot be defined simultaneously" +#endif + #if defined(MBEDTLS_PLATFORM_STD_MEM_HDR) &&\ !defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS) #error "MBEDTLS_PLATFORM_STD_MEM_HDR defined, but not all prerequisites" @@ -650,10 +664,9 @@ MBEDTLS_ECDSA_C requires MBEDTLS_PK_WRITE_C to be defined." #endif -#if defined(MBEDTLS_RSA_C) && defined(MBEDTLS_PKCS1_V15) && \ - !defined(MBEDTLS_PK_WRITE_C) && defined(MBEDTLS_PSA_CRYPTO_C) -#error "MBEDTLS_PSA_CRYPTO_C, MBEDTLS_RSA_C and MBEDTLS_PKCS1_V15 defined, \ - but not all prerequisites" +#if defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_RSA_C) && \ + !( defined(MBEDTLS_PK_PARSE_C) && defined(MBEDTLS_PK_WRITE_C) ) +#error "MBEDTLS_PSA_CRYPTO_C with MBEDTLS_RSA_C requires MBEDTLS_PK_PARSE_C and MBEDTLS_PK_WRITE_C" #endif #if defined(MBEDTLS_RSA_C) && ( !defined(MBEDTLS_BIGNUM_C) || \ @@ -812,6 +825,11 @@ #error "MBEDTLS_SSL_TICKET_C defined, but not all prerequisites" #endif +#if defined(MBEDTLS_SSL_TICKET_C) && \ + !( defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) || defined(MBEDTLS_CHACHAPOLY_C) ) +#error "MBEDTLS_SSL_TICKET_C defined, but not all prerequisites" +#endif + #if defined(MBEDTLS_SSL_CBC_RECORD_SPLITTING) && \ !defined(MBEDTLS_SSL_PROTO_SSL3) && !defined(MBEDTLS_SSL_PROTO_TLS1) #error "MBEDTLS_SSL_CBC_RECORD_SPLITTING defined, but not all prerequisites" @@ -926,6 +944,10 @@ #error "MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH defined, but not all prerequisites" #endif +#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) && !( defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) || defined(MBEDTLS_CHACHAPOLY_C) ) +#error "MBEDTLS_SSL_CONTEXT_SERIALIZATION defined, but not all prerequisites" +#endif + /* * Avoid warning from -pedantic. This is a convenient place for this * workaround since this is included by every single file before the diff --git a/thirdparty/mbedtls/include/mbedtls/config.h b/thirdparty/mbedtls/include/mbedtls/config.h index 1cd6eb6634..61db79362f 100644 --- a/thirdparty/mbedtls/include/mbedtls/config.h +++ b/thirdparty/mbedtls/include/mbedtls/config.h @@ -1329,7 +1329,7 @@ * Include backtrace information with each allocated block. * * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C - * GLIBC-compatible backtrace() an backtrace_symbols() support + * GLIBC-compatible backtrace() and backtrace_symbols() support * * Uncomment this macro to include backtrace information */ @@ -1620,6 +1620,8 @@ * saved after the handshake to allow for more efficient serialization, so if * you don't need this feature you'll save RAM by disabling it. * + * Requires: MBEDTLS_GCM_C or MBEDTLS_CCM_C or MBEDTLS_CHACHAPOLY_C + * * Comment to disable the context serialization APIs. */ #define MBEDTLS_SSL_CONTEXT_SERIALIZATION @@ -2425,7 +2427,7 @@ * MBEDTLS_TLS_PSK_WITH_RC4_128_SHA * * \warning ARC4 is considered a weak cipher and its use constitutes a - * security risk. If possible, we recommend avoidng dependencies on + * security risk. If possible, we recommend avoiding dependencies on * it, and considering stronger ciphers instead. * */ @@ -3030,7 +3032,7 @@ * * \note See also our Knowledge Base article about porting to a new * environment: - * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS + * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS * * Module: library/net_sockets.c * @@ -3400,7 +3402,8 @@ * Module: library/ssl_ticket.c * Caller: * - * Requires: MBEDTLS_CIPHER_C + * Requires: MBEDTLS_CIPHER_C && + * ( MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C ) */ #define MBEDTLS_SSL_TICKET_C @@ -3456,7 +3459,7 @@ * contexts are not shared between threads. If you do intend to use contexts * between threads, you will need to enable this layer to prevent race * conditions. See also our Knowledge Base article about threading: - * https://tls.mbed.org/kb/development/thread-safety-and-multi-threading + * https://mbed-tls.readthedocs.io/en/latest/kb/development/thread-safety-and-multi-threading * * Module: library/threading.c * @@ -3488,7 +3491,7 @@ * * \note See also our Knowledge Base article about porting to a new * environment: - * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS + * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS * * Module: library/timing.c * Caller: library/havege.c diff --git a/thirdparty/mbedtls/include/mbedtls/ecdsa.h b/thirdparty/mbedtls/include/mbedtls/ecdsa.h index 264a638bb5..118f7cedb1 100644 --- a/thirdparty/mbedtls/include/mbedtls/ecdsa.h +++ b/thirdparty/mbedtls/include/mbedtls/ecdsa.h @@ -309,10 +309,8 @@ int mbedtls_ecdsa_sign_det_ext( mbedtls_ecp_group *grp, mbedtls_mpi *r, * This must be initialized. * * \return \c 0 on success. - * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the signature - * is invalid. * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX - * error code on failure for any other reason. + * error code on failure. */ int mbedtls_ecdsa_verify( mbedtls_ecp_group *grp, const unsigned char *buf, size_t blen, diff --git a/thirdparty/mbedtls/include/mbedtls/md.h b/thirdparty/mbedtls/include/mbedtls/md.h index 84fafd2ac7..9cea40a89c 100644 --- a/thirdparty/mbedtls/include/mbedtls/md.h +++ b/thirdparty/mbedtls/include/mbedtls/md.h @@ -215,7 +215,7 @@ MBEDTLS_CHECK_RETURN_TYPICAL int mbedtls_md_setup( mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_info, int hmac ); /** - * \brief This function clones the state of an message-digest + * \brief This function clones the state of a message-digest * context. * * \note You must call mbedtls_md_setup() on \c dst before calling diff --git a/thirdparty/mbedtls/include/mbedtls/platform.h b/thirdparty/mbedtls/include/mbedtls/platform.h index 06dd192eab..eaf5122bec 100644 --- a/thirdparty/mbedtls/include/mbedtls/platform.h +++ b/thirdparty/mbedtls/include/mbedtls/platform.h @@ -11,6 +11,13 @@ * implementations of these functions, or implementations specific to * their platform, which can be statically linked to the library or * dynamically configured at runtime. + * + * When all compilation options related to platform abstraction are + * disabled, this header just defines `mbedtls_xxx` function names + * as aliases to the standard `xxx` function. + * + * Most modules in the library and example programs are expected to + * include this header. */ /* * Copyright The Mbed TLS Contributors diff --git a/thirdparty/mbedtls/include/mbedtls/ripemd160.h b/thirdparty/mbedtls/include/mbedtls/ripemd160.h index 63270d1239..f890aefaee 100644 --- a/thirdparty/mbedtls/include/mbedtls/ripemd160.h +++ b/thirdparty/mbedtls/include/mbedtls/ripemd160.h @@ -74,7 +74,7 @@ void mbedtls_ripemd160_init( mbedtls_ripemd160_context *ctx ); void mbedtls_ripemd160_free( mbedtls_ripemd160_context *ctx ); /** - * \brief Clone (the state of) an RIPEMD-160 context + * \brief Clone (the state of) a RIPEMD-160 context * * \param dst The destination context * \param src The context to be cloned diff --git a/thirdparty/mbedtls/include/mbedtls/rsa.h b/thirdparty/mbedtls/include/mbedtls/rsa.h index 062df73aa0..8559f67bb9 100644 --- a/thirdparty/mbedtls/include/mbedtls/rsa.h +++ b/thirdparty/mbedtls/include/mbedtls/rsa.h @@ -491,7 +491,7 @@ int mbedtls_rsa_check_pubkey( const mbedtls_rsa_context *ctx ); * the current function does not have access to them, * and therefore cannot check them. See mbedtls_rsa_complete(). * If you want to check the consistency of the entire - * content of an PKCS1-encoded RSA private key, for example, you + * content of a PKCS1-encoded RSA private key, for example, you * should use mbedtls_rsa_validate_params() before setting * up the RSA context. * Additionally, if the implementation performs empirical checks, diff --git a/thirdparty/mbedtls/include/mbedtls/ssl.h b/thirdparty/mbedtls/include/mbedtls/ssl.h index 5064ec5689..aecac93f33 100644 --- a/thirdparty/mbedtls/include/mbedtls/ssl.h +++ b/thirdparty/mbedtls/include/mbedtls/ssl.h @@ -624,7 +624,7 @@ typedef int mbedtls_ssl_recv_t( void *ctx, * \param ctx Context for the receive callback (typically a file descriptor) * \param buf Buffer to write the received data to * \param len Length of the receive buffer - * \param timeout Maximum nomber of millisecondes to wait for data + * \param timeout Maximum number of milliseconds to wait for data * 0 means no timeout (potentially waiting forever) * * \return The callback must return the number of bytes received, @@ -652,7 +652,7 @@ typedef int mbedtls_ssl_recv_timeout_t( void *ctx, * for the associated \c mbedtls_ssl_get_timer_t callback to * return correct information. * - * \note If using a event-driven style of programming, an event must + * \note If using an event-driven style of programming, an event must * be generated when the final delay is passed. The event must * cause a call to \c mbedtls_ssl_handshake() with the proper * SSL context to be scheduled. Care must be taken to ensure @@ -2000,7 +2000,7 @@ int mbedtls_ssl_check_record( mbedtls_ssl_context const *ssl, * here, except if using an event-driven style. * * \note See also the "DTLS tutorial" article in our knowledge base. - * https://tls.mbed.org/kb/how-to/dtls-tutorial + * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/dtls-tutorial */ void mbedtls_ssl_set_timer_cb( mbedtls_ssl_context *ssl, void *p_timer, diff --git a/thirdparty/mbedtls/include/mbedtls/ssl_internal.h b/thirdparty/mbedtls/include/mbedtls/ssl_internal.h index 46ade67b9c..77ad755477 100644 --- a/thirdparty/mbedtls/include/mbedtls/ssl_internal.h +++ b/thirdparty/mbedtls/include/mbedtls/ssl_internal.h @@ -782,7 +782,7 @@ struct mbedtls_ssl_transform #if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) uint8_t in_cid_len; uint8_t out_cid_len; - unsigned char in_cid [ MBEDTLS_SSL_CID_OUT_LEN_MAX ]; + unsigned char in_cid [ MBEDTLS_SSL_CID_IN_LEN_MAX ]; unsigned char out_cid[ MBEDTLS_SSL_CID_OUT_LEN_MAX ]; #endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ diff --git a/thirdparty/mbedtls/include/mbedtls/version.h b/thirdparty/mbedtls/include/mbedtls/version.h index 44adcbfe03..b3b441d46b 100644 --- a/thirdparty/mbedtls/include/mbedtls/version.h +++ b/thirdparty/mbedtls/include/mbedtls/version.h @@ -38,16 +38,16 @@ */ #define MBEDTLS_VERSION_MAJOR 2 #define MBEDTLS_VERSION_MINOR 28 -#define MBEDTLS_VERSION_PATCH 1 +#define MBEDTLS_VERSION_PATCH 2 /** * The single version number has the following structure: * MMNNPP00 * Major version | Minor version | Patch version */ -#define MBEDTLS_VERSION_NUMBER 0x021C0100 -#define MBEDTLS_VERSION_STRING "2.28.1" -#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.28.1" +#define MBEDTLS_VERSION_NUMBER 0x021C0200 +#define MBEDTLS_VERSION_STRING "2.28.2" +#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.28.2" #if defined(MBEDTLS_VERSION_C) diff --git a/thirdparty/mbedtls/library/aes.c b/thirdparty/mbedtls/library/aes.c index 03d8b7ea61..74ea2672b0 100644 --- a/thirdparty/mbedtls/library/aes.c +++ b/thirdparty/mbedtls/library/aes.c @@ -40,14 +40,7 @@ #include "mbedtls/aesni.h" #endif -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_AES_ALT) @@ -1170,7 +1163,7 @@ int mbedtls_aes_crypt_xts( mbedtls_aes_xts_context *ctx, { /* We are on the last block in a decrypt operation that has * leftover bytes, so we need to use the next tweak for this block, - * and this tweak for the lefover bytes. Save the current tweak for + * and this tweak for the leftover bytes. Save the current tweak for * the leftovers and then update the current tweak for use on this, * the last full block. */ memcpy( prev_tweak, tweak, sizeof( tweak ) ); @@ -1770,7 +1763,8 @@ int mbedtls_aes_self_test( int verbose ) unsigned char key[32]; unsigned char buf[64]; const unsigned char *aes_tests; -#if defined(MBEDTLS_CIPHER_MODE_CBC) || defined(MBEDTLS_CIPHER_MODE_CFB) +#if defined(MBEDTLS_CIPHER_MODE_CBC) || defined(MBEDTLS_CIPHER_MODE_CFB) || \ + defined(MBEDTLS_CIPHER_MODE_OFB) unsigned char iv[16]; #endif #if defined(MBEDTLS_CIPHER_MODE_CBC) diff --git a/thirdparty/mbedtls/library/arc4.c b/thirdparty/mbedtls/library/arc4.c index b34dc5e754..dcc13d539a 100644 --- a/thirdparty/mbedtls/library/arc4.c +++ b/thirdparty/mbedtls/library/arc4.c @@ -31,14 +31,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_ARC4_ALT) diff --git a/thirdparty/mbedtls/library/aria.c b/thirdparty/mbedtls/library/aria.c index bc05c4a319..5e52eea91e 100644 --- a/thirdparty/mbedtls/library/aria.c +++ b/thirdparty/mbedtls/library/aria.c @@ -31,24 +31,12 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_ARIA_ALT) #include "mbedtls/platform_util.h" -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - /* Parameter validation macros */ #define ARIA_VALIDATE_RET( cond ) \ MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_ARIA_BAD_INPUT_DATA ) @@ -895,15 +883,17 @@ static const uint8_t aria_test2_ctr_ct[3][48] = // CTR ciphertext }; #endif /* MBEDTLS_CIPHER_MODE_CFB */ -#define ARIA_SELF_TEST_IF_FAIL \ - { \ - if( verbose ) \ - mbedtls_printf( "failed\n" ); \ - goto exit; \ - } else { \ - if( verbose ) \ - mbedtls_printf( "passed\n" ); \ - } +#define ARIA_SELF_TEST_ASSERT( cond ) \ + do { \ + if( cond ) { \ + if( verbose ) \ + mbedtls_printf( "failed\n" ); \ + goto exit; \ + } else { \ + if( verbose ) \ + mbedtls_printf( "passed\n" ); \ + } \ + } while( 0 ) /* * Checkup routine @@ -937,16 +927,18 @@ int mbedtls_aria_self_test( int verbose ) mbedtls_printf( " ARIA-ECB-%d (enc): ", 128 + 64 * i ); mbedtls_aria_setkey_enc( &ctx, aria_test1_ecb_key, 128 + 64 * i ); mbedtls_aria_crypt_ecb( &ctx, aria_test1_ecb_pt, blk ); - if( memcmp( blk, aria_test1_ecb_ct[i], MBEDTLS_ARIA_BLOCKSIZE ) != 0 ) - ARIA_SELF_TEST_IF_FAIL; + ARIA_SELF_TEST_ASSERT( + memcmp( blk, aria_test1_ecb_ct[i], MBEDTLS_ARIA_BLOCKSIZE ) + != 0 ); /* test ECB decryption */ if( verbose ) mbedtls_printf( " ARIA-ECB-%d (dec): ", 128 + 64 * i ); mbedtls_aria_setkey_dec( &ctx, aria_test1_ecb_key, 128 + 64 * i ); mbedtls_aria_crypt_ecb( &ctx, aria_test1_ecb_ct[i], blk ); - if( memcmp( blk, aria_test1_ecb_pt, MBEDTLS_ARIA_BLOCKSIZE ) != 0 ) - ARIA_SELF_TEST_IF_FAIL; + ARIA_SELF_TEST_ASSERT( + memcmp( blk, aria_test1_ecb_pt, MBEDTLS_ARIA_BLOCKSIZE ) + != 0 ); } if( verbose ) mbedtls_printf( "\n" ); @@ -965,8 +957,8 @@ int mbedtls_aria_self_test( int verbose ) memset( buf, 0x55, sizeof( buf ) ); mbedtls_aria_crypt_cbc( &ctx, MBEDTLS_ARIA_ENCRYPT, 48, iv, aria_test2_pt, buf ); - if( memcmp( buf, aria_test2_cbc_ct[i], 48 ) != 0 ) - ARIA_SELF_TEST_IF_FAIL; + ARIA_SELF_TEST_ASSERT( memcmp( buf, aria_test2_cbc_ct[i], 48 ) + != 0 ); /* Test CBC decryption */ if( verbose ) @@ -976,8 +968,7 @@ int mbedtls_aria_self_test( int verbose ) memset( buf, 0xAA, sizeof( buf ) ); mbedtls_aria_crypt_cbc( &ctx, MBEDTLS_ARIA_DECRYPT, 48, iv, aria_test2_cbc_ct[i], buf ); - if( memcmp( buf, aria_test2_pt, 48 ) != 0 ) - ARIA_SELF_TEST_IF_FAIL; + ARIA_SELF_TEST_ASSERT( memcmp( buf, aria_test2_pt, 48 ) != 0 ); } if( verbose ) mbedtls_printf( "\n" ); @@ -996,8 +987,7 @@ int mbedtls_aria_self_test( int verbose ) j = 0; mbedtls_aria_crypt_cfb128( &ctx, MBEDTLS_ARIA_ENCRYPT, 48, &j, iv, aria_test2_pt, buf ); - if( memcmp( buf, aria_test2_cfb_ct[i], 48 ) != 0 ) - ARIA_SELF_TEST_IF_FAIL; + ARIA_SELF_TEST_ASSERT( memcmp( buf, aria_test2_cfb_ct[i], 48 ) != 0 ); /* Test CFB decryption */ if( verbose ) @@ -1008,8 +998,7 @@ int mbedtls_aria_self_test( int verbose ) j = 0; mbedtls_aria_crypt_cfb128( &ctx, MBEDTLS_ARIA_DECRYPT, 48, &j, iv, aria_test2_cfb_ct[i], buf ); - if( memcmp( buf, aria_test2_pt, 48 ) != 0 ) - ARIA_SELF_TEST_IF_FAIL; + ARIA_SELF_TEST_ASSERT( memcmp( buf, aria_test2_pt, 48 ) != 0 ); } if( verbose ) mbedtls_printf( "\n" ); @@ -1027,8 +1016,7 @@ int mbedtls_aria_self_test( int verbose ) j = 0; mbedtls_aria_crypt_ctr( &ctx, 48, &j, iv, blk, aria_test2_pt, buf ); - if( memcmp( buf, aria_test2_ctr_ct[i], 48 ) != 0 ) - ARIA_SELF_TEST_IF_FAIL; + ARIA_SELF_TEST_ASSERT( memcmp( buf, aria_test2_ctr_ct[i], 48 ) != 0 ); /* Test CTR decryption */ if( verbose ) @@ -1039,8 +1027,7 @@ int mbedtls_aria_self_test( int verbose ) j = 0; mbedtls_aria_crypt_ctr( &ctx, 48, &j, iv, blk, aria_test2_ctr_ct[i], buf ); - if( memcmp( buf, aria_test2_pt, 48 ) != 0 ) - ARIA_SELF_TEST_IF_FAIL; + ARIA_SELF_TEST_ASSERT( memcmp( buf, aria_test2_pt, 48 ) != 0 ); } if( verbose ) mbedtls_printf( "\n" ); diff --git a/thirdparty/mbedtls/library/asn1parse.c b/thirdparty/mbedtls/library/asn1parse.c index 22747d3ba4..bf97585289 100644 --- a/thirdparty/mbedtls/library/asn1parse.c +++ b/thirdparty/mbedtls/library/asn1parse.c @@ -31,13 +31,7 @@ #include "mbedtls/bignum.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif /* * ASN.1 DER decoding routines diff --git a/thirdparty/mbedtls/library/asn1write.c b/thirdparty/mbedtls/library/asn1write.c index afa26a6be9..4b59927cbb 100644 --- a/thirdparty/mbedtls/library/asn1write.c +++ b/thirdparty/mbedtls/library/asn1write.c @@ -26,13 +26,7 @@ #include <string.h> -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif int mbedtls_asn1_write_len( unsigned char **p, unsigned char *start, size_t len ) { @@ -78,9 +72,11 @@ int mbedtls_asn1_write_len( unsigned char **p, unsigned char *start, size_t len return( 4 ); } + int len_is_valid = 1; #if SIZE_MAX > 0xFFFFFFFF - if( len <= 0xFFFFFFFF ) + len_is_valid = ( len <= 0xFFFFFFFF ); #endif + if( len_is_valid ) { if( *p - start < 5 ) return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL ); @@ -93,9 +89,7 @@ int mbedtls_asn1_write_len( unsigned char **p, unsigned char *start, size_t len return( 5 ); } -#if SIZE_MAX > 0xFFFFFFFF return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); -#endif } int mbedtls_asn1_write_tag( unsigned char **p, unsigned char *start, unsigned char tag ) diff --git a/thirdparty/mbedtls/library/base64.c b/thirdparty/mbedtls/library/base64.c index 83daa0bcc6..9021a041bb 100644 --- a/thirdparty/mbedtls/library/base64.c +++ b/thirdparty/mbedtls/library/base64.c @@ -28,12 +28,7 @@ #if defined(MBEDTLS_SELF_TEST) #include <string.h> -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ #endif /* MBEDTLS_SELF_TEST */ #define BASE64_SIZE_T_MAX ( (size_t) -1 ) /* SIZE_T_MAX is not standard */ diff --git a/thirdparty/mbedtls/library/bignum.c b/thirdparty/mbedtls/library/bignum.c index 32578e2c68..37193f55a8 100644 --- a/thirdparty/mbedtls/library/bignum.c +++ b/thirdparty/mbedtls/library/bignum.c @@ -46,15 +46,7 @@ #include <limits.h> #include <string.h> -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#include <stdlib.h> -#define mbedtls_printf printf -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #define MPI_VALIDATE_RET( cond ) \ MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_MPI_BAD_INPUT_DATA ) @@ -270,6 +262,17 @@ void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y ) memcpy( Y, &T, sizeof( mbedtls_mpi ) ); } +static inline mbedtls_mpi_uint mpi_sint_abs( mbedtls_mpi_sint z ) +{ + if( z >= 0 ) + return( z ); + /* Take care to handle the most negative value (-2^(biL-1)) correctly. + * A naive -z would have undefined behavior. + * Write this in a way that makes popular compilers happy (GCC, Clang, + * MSVC). */ + return( (mbedtls_mpi_uint) 0 - (mbedtls_mpi_uint) z ); +} + /* * Set value from integer */ @@ -281,7 +284,7 @@ int mbedtls_mpi_lset( mbedtls_mpi *X, mbedtls_mpi_sint z ) MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, 1 ) ); memset( X->p, 0, X->n * ciL ); - X->p[0] = ( z < 0 ) ? -z : z; + X->p[0] = mpi_sint_abs( z ); X->s = ( z < 0 ) ? -1 : 1; cleanup: @@ -1101,7 +1104,7 @@ int mbedtls_mpi_cmp_int( const mbedtls_mpi *X, mbedtls_mpi_sint z ) mbedtls_mpi_uint p[1]; MPI_VALIDATE_RET( X != NULL ); - *p = ( z < 0 ) ? -z : z; + *p = mpi_sint_abs( z ); Y.s = ( z < 0 ) ? -1 : 1; Y.n = 1; Y.p = p; @@ -1138,6 +1141,11 @@ int mbedtls_mpi_add_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi if( B->p[j - 1] != 0 ) break; + /* Exit early to avoid undefined behavior on NULL+0 when X->n == 0 + * and B is 0 (of any size). */ + if( j == 0 ) + return( 0 ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, j ) ); o = B->p; p = X->p; c = 0; @@ -1257,10 +1265,12 @@ cleanup: return( ret ); } -/* - * Signed addition: X = A + B +/* Common function for signed addition and subtraction. + * Calculate A + B * flip_B where flip_B is 1 or -1. */ -int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) +static int add_sub_mpi( mbedtls_mpi *X, + const mbedtls_mpi *A, const mbedtls_mpi *B, + int flip_B ) { int ret, s; MPI_VALIDATE_RET( X != NULL ); @@ -1268,16 +1278,21 @@ int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi MPI_VALIDATE_RET( B != NULL ); s = A->s; - if( A->s * B->s < 0 ) + if( A->s * B->s * flip_B < 0 ) { - if( mbedtls_mpi_cmp_abs( A, B ) >= 0 ) + int cmp = mbedtls_mpi_cmp_abs( A, B ); + if( cmp >= 0 ) { MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, A, B ) ); - X->s = s; + /* If |A| = |B|, the result is 0 and we must set the sign bit + * to +1 regardless of which of A or B was negative. Otherwise, + * since |A| > |B|, the sign is the sign of A. */ + X->s = cmp == 0 ? 1 : s; } else { MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, B, A ) ); + /* Since |A| < |B|, the sign is the opposite of A. */ X->s = -s; } } @@ -1293,38 +1308,19 @@ cleanup: } /* + * Signed addition: X = A + B + */ +int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) +{ + return( add_sub_mpi( X, A, B, 1 ) ); +} + +/* * Signed subtraction: X = A - B */ int mbedtls_mpi_sub_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) { - int ret, s; - MPI_VALIDATE_RET( X != NULL ); - MPI_VALIDATE_RET( A != NULL ); - MPI_VALIDATE_RET( B != NULL ); - - s = A->s; - if( A->s * B->s > 0 ) - { - if( mbedtls_mpi_cmp_abs( A, B ) >= 0 ) - { - MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, A, B ) ); - X->s = s; - } - else - { - MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, B, A ) ); - X->s = -s; - } - } - else - { - MBEDTLS_MPI_CHK( mbedtls_mpi_add_abs( X, A, B ) ); - X->s = s; - } - -cleanup: - - return( ret ); + return( add_sub_mpi( X, A, B, -1 ) ); } /* @@ -1337,7 +1333,7 @@ int mbedtls_mpi_add_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint MPI_VALIDATE_RET( X != NULL ); MPI_VALIDATE_RET( A != NULL ); - p[0] = ( b < 0 ) ? -b : b; + p[0] = mpi_sint_abs( b ); B.s = ( b < 0 ) ? -1 : 1; B.n = 1; B.p = p; @@ -1355,7 +1351,7 @@ int mbedtls_mpi_sub_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint MPI_VALIDATE_RET( X != NULL ); MPI_VALIDATE_RET( A != NULL ); - p[0] = ( b < 0 ) ? -b : b; + p[0] = mpi_sint_abs( b ); B.s = ( b < 0 ) ? -1 : 1; B.n = 1; B.p = p; @@ -1776,7 +1772,7 @@ int mbedtls_mpi_div_int( mbedtls_mpi *Q, mbedtls_mpi *R, mbedtls_mpi_uint p[1]; MPI_VALIDATE_RET( A != NULL ); - p[0] = ( b < 0 ) ? -b : b; + p[0] = mpi_sint_abs( b ); B.s = ( b < 0 ) ? -1 : 1; B.n = 1; B.p = p; @@ -2009,11 +2005,11 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi *prec_RR ) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - size_t wbits, wsize, one = 1; + size_t window_bitsize; size_t i, j, nblimbs; size_t bufsize, nbits; mbedtls_mpi_uint ei, mm, state; - mbedtls_mpi RR, T, W[ 1 << MBEDTLS_MPI_WINDOW_SIZE ], WW, Apos; + mbedtls_mpi RR, T, W[ (size_t) 1 << MBEDTLS_MPI_WINDOW_SIZE ], WW, Apos; int neg; MPI_VALIDATE_RET( X != NULL ); @@ -2042,21 +2038,59 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, i = mbedtls_mpi_bitlen( E ); - wsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : + window_bitsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; #if( MBEDTLS_MPI_WINDOW_SIZE < 6 ) - if( wsize > MBEDTLS_MPI_WINDOW_SIZE ) - wsize = MBEDTLS_MPI_WINDOW_SIZE; + if( window_bitsize > MBEDTLS_MPI_WINDOW_SIZE ) + window_bitsize = MBEDTLS_MPI_WINDOW_SIZE; #endif + const size_t w_table_used_size = (size_t) 1 << window_bitsize; + + /* + * This function is not constant-trace: its memory accesses depend on the + * exponent value. To defend against timing attacks, callers (such as RSA + * and DHM) should use exponent blinding. However this is not enough if the + * adversary can find the exponent in a single trace, so this function + * takes extra precautions against adversaries who can observe memory + * access patterns. + * + * This function performs a series of multiplications by table elements and + * squarings, and we want the prevent the adversary from finding out which + * table element was used, and from distinguishing between multiplications + * and squarings. Firstly, when multiplying by an element of the window + * W[i], we do a constant-trace table lookup to obfuscate i. This leaves + * squarings as having a different memory access patterns from other + * multiplications. So secondly, we put the accumulator X in the table as + * well, and also do a constant-trace table lookup to multiply by X. + * + * This way, all multiplications take the form of a lookup-and-multiply. + * The number of lookup-and-multiply operations inside each iteration of + * the main loop still depends on the bits of the exponent, but since the + * other operations in the loop don't have an easily recognizable memory + * trace, an adversary is unlikely to be able to observe the exact + * patterns. + * + * An adversary may still be able to recover the exponent if they can + * observe both memory accesses and branches. However, branch prediction + * exploitation typically requires many traces of execution over the same + * data, which is defeated by randomized blinding. + * + * To achieve this, we make a copy of X and we use the table entry in each + * calculation from this point on. + */ + const size_t x_index = 0; + mbedtls_mpi_init( &W[x_index] ); + mbedtls_mpi_copy( &W[x_index], X ); + j = N->n + 1; /* All W[i] and X must have at least N->n limbs for the mpi_montmul() * and mpi_montred() calls later. Here we ensure that W[1] and X are * large enough, and later we'll grow other W[i] to the same length. * They must not be shrunk midway through this function! */ - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, j ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[x_index], j ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[1], j ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &T, j * 2 ) ); @@ -2105,28 +2139,36 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, mpi_montmul( &W[1], &RR, N, mm, &T ); /* - * X = R^2 * R^-1 mod N = R mod N + * W[x_index] = R^2 * R^-1 mod N = R mod N */ - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, &RR ) ); - mpi_montred( X, N, mm, &T ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[x_index], &RR ) ); + mpi_montred( &W[x_index], N, mm, &T ); + - if( wsize > 1 ) + if( window_bitsize > 1 ) { /* - * W[1 << (wsize - 1)] = W[1] ^ (wsize - 1) + * W[i] = W[1] ^ i + * + * The first bit of the sliding window is always 1 and therefore we + * only need to store the second half of the table. + * + * (There are two special elements in the table: W[0] for the + * accumulator/result and W[1] for A in Montgomery form. Both of these + * are already set at this point.) */ - j = one << ( wsize - 1 ); + j = w_table_used_size / 2; MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[j], N->n + 1 ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[j], &W[1] ) ); - for( i = 0; i < wsize - 1; i++ ) + for( i = 0; i < window_bitsize - 1; i++ ) mpi_montmul( &W[j], &W[j], N, mm, &T ); /* * W[i] = W[i - 1] * W[1] */ - for( i = j + 1; i < ( one << wsize ); i++ ) + for( i = j + 1; i < w_table_used_size; i++ ) { MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[i], N->n + 1 ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[i], &W[i - 1] ) ); @@ -2138,7 +2180,7 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, nblimbs = E->n; bufsize = 0; nbits = 0; - wbits = 0; + size_t exponent_bits_in_window = 0; state = 0; while( 1 ) @@ -2166,9 +2208,10 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, if( ei == 0 && state == 1 ) { /* - * out of window, square X + * out of window, square W[x_index] */ - mpi_montmul( X, X, N, mm, &T ); + MBEDTLS_MPI_CHK( mpi_select( &WW, W, w_table_used_size, x_index ) ); + mpi_montmul( &W[x_index], &WW, N, mm, &T ); continue; } @@ -2178,25 +2221,30 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, state = 2; nbits++; - wbits |= ( ei << ( wsize - nbits ) ); + exponent_bits_in_window |= ( ei << ( window_bitsize - nbits ) ); - if( nbits == wsize ) + if( nbits == window_bitsize ) { /* - * X = X^wsize R^-1 mod N + * W[x_index] = W[x_index]^window_bitsize R^-1 mod N */ - for( i = 0; i < wsize; i++ ) - mpi_montmul( X, X, N, mm, &T ); + for( i = 0; i < window_bitsize; i++ ) + { + MBEDTLS_MPI_CHK( mpi_select( &WW, W, w_table_used_size, + x_index ) ); + mpi_montmul( &W[x_index], &WW, N, mm, &T ); + } /* - * X = X * W[wbits] R^-1 mod N + * W[x_index] = W[x_index] * W[exponent_bits_in_window] R^-1 mod N */ - MBEDTLS_MPI_CHK( mpi_select( &WW, W, (size_t) 1 << wsize, wbits ) ); - mpi_montmul( X, &WW, N, mm, &T ); + MBEDTLS_MPI_CHK( mpi_select( &WW, W, w_table_used_size, + exponent_bits_in_window ) ); + mpi_montmul( &W[x_index], &WW, N, mm, &T ); state--; nbits = 0; - wbits = 0; + exponent_bits_in_window = 0; } } @@ -2205,31 +2253,45 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, */ for( i = 0; i < nbits; i++ ) { - mpi_montmul( X, X, N, mm, &T ); + MBEDTLS_MPI_CHK( mpi_select( &WW, W, w_table_used_size, x_index ) ); + mpi_montmul( &W[x_index], &WW, N, mm, &T ); - wbits <<= 1; + exponent_bits_in_window <<= 1; - if( ( wbits & ( one << wsize ) ) != 0 ) - mpi_montmul( X, &W[1], N, mm, &T ); + if( ( exponent_bits_in_window & ( (size_t) 1 << window_bitsize ) ) != 0 ) + { + MBEDTLS_MPI_CHK( mpi_select( &WW, W, w_table_used_size, 1 ) ); + mpi_montmul( &W[x_index], &WW, N, mm, &T ); + } } /* - * X = A^E * R * R^-1 mod N = A^E mod N + * W[x_index] = A^E * R * R^-1 mod N = A^E mod N */ - mpi_montred( X, N, mm, &T ); + mpi_montred( &W[x_index], N, mm, &T ); if( neg && E->n != 0 && ( E->p[0] & 1 ) != 0 ) { - X->s = -1; - MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( X, N, X ) ); + W[x_index].s = -1; + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &W[x_index], N, &W[x_index] ) ); } + /* + * Load the result in the output variable. + */ + mbedtls_mpi_copy( X, &W[x_index] ); + cleanup: - for( i = ( one << ( wsize - 1 ) ); i < ( one << wsize ); i++ ) + /* The first bit of the sliding window is always 1 and therefore the first + * half of the table was unused. */ + for( i = w_table_used_size/2; i < w_table_used_size; i++ ) mbedtls_mpi_free( &W[i] ); - mbedtls_mpi_free( &W[1] ); mbedtls_mpi_free( &T ); mbedtls_mpi_free( &Apos ); + mbedtls_mpi_free( &W[x_index] ); + mbedtls_mpi_free( &W[1] ); + mbedtls_mpi_free( &T ); + mbedtls_mpi_free( &Apos ); mbedtls_mpi_free( &WW ); if( prec_RR == NULL || prec_RR->p == NULL ) @@ -2862,7 +2924,7 @@ int mbedtls_mpi_gen_prime( mbedtls_mpi *X, size_t nbits, int flags, else { /* - * An necessary condition for Y and X = 2Y + 1 to be prime + * A necessary condition for Y and X = 2Y + 1 to be prime * is X = 2 mod 3 (which is equivalent to Y = 2 mod 3). * Make sure it is satisfied, while keeping X = 3 mod 4 */ diff --git a/thirdparty/mbedtls/library/camellia.c b/thirdparty/mbedtls/library/camellia.c index 29d730ab53..e90cd7f134 100644 --- a/thirdparty/mbedtls/library/camellia.c +++ b/thirdparty/mbedtls/library/camellia.c @@ -32,14 +32,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_CAMELLIA_ALT) diff --git a/thirdparty/mbedtls/library/ccm.c b/thirdparty/mbedtls/library/ccm.c index a21a37f55f..e0d43334f9 100644 --- a/thirdparty/mbedtls/library/ccm.c +++ b/thirdparty/mbedtls/library/ccm.c @@ -36,14 +36,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ #if !defined(MBEDTLS_CCM_ALT) diff --git a/thirdparty/mbedtls/library/chacha20.c b/thirdparty/mbedtls/library/chacha20.c index 658f046901..bd0701482c 100644 --- a/thirdparty/mbedtls/library/chacha20.c +++ b/thirdparty/mbedtls/library/chacha20.c @@ -32,22 +32,10 @@ #include <stddef.h> #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_CHACHA20_ALT) -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - /* Parameter validation macros */ #define CHACHA20_VALIDATE_RET( cond ) \ MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA ) diff --git a/thirdparty/mbedtls/library/chachapoly.c b/thirdparty/mbedtls/library/chachapoly.c index dc75b2030a..4adf846857 100644 --- a/thirdparty/mbedtls/library/chachapoly.c +++ b/thirdparty/mbedtls/library/chachapoly.c @@ -28,14 +28,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_CHACHAPOLY_ALT) diff --git a/thirdparty/mbedtls/library/cipher.c b/thirdparty/mbedtls/library/cipher.c index f3b4bd29ce..67e3274587 100644 --- a/thirdparty/mbedtls/library/cipher.c +++ b/thirdparty/mbedtls/library/cipher.c @@ -63,12 +63,7 @@ #include "mbedtls/nist_kw.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #define CIPHER_VALIDATE_RET( cond ) \ MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ) diff --git a/thirdparty/mbedtls/library/cipher_wrap.c b/thirdparty/mbedtls/library/cipher_wrap.c index 57eb3cb67f..c76bdcc0f8 100644 --- a/thirdparty/mbedtls/library/cipher_wrap.c +++ b/thirdparty/mbedtls/library/cipher_wrap.c @@ -76,13 +76,7 @@ #include <string.h> #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #if defined(MBEDTLS_GCM_C) /* shared by all GCM ciphers */ diff --git a/thirdparty/mbedtls/library/common.h b/thirdparty/mbedtls/library/common.h index c06472418d..1663d50226 100644 --- a/thirdparty/mbedtls/library/common.h +++ b/thirdparty/mbedtls/library/common.h @@ -29,8 +29,15 @@ #include "mbedtls/config.h" #endif +#include <stddef.h> #include <stdint.h> +/* Define `inline` on some non-C99-compliant compilers. */ +#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ + !defined(inline) && !defined(__cplusplus) +#define inline __inline +#endif + /** Helper to define a function as static except when building invasive tests. * * If a function is only used inside its own source file and should be @@ -52,6 +59,44 @@ #define MBEDTLS_STATIC_TESTABLE static #endif +/** Return an offset into a buffer. + * + * This is just the addition of an offset to a pointer, except that this + * function also accepts an offset of 0 into a buffer whose pointer is null. + * (`p + n` has undefined behavior when `p` is null, even when `n == 0`. + * A null pointer is a valid buffer pointer when the size is 0, for example + * as the result of `malloc(0)` on some platforms.) + * + * \param p Pointer to a buffer of at least n bytes. + * This may be \p NULL if \p n is zero. + * \param n An offset in bytes. + * \return Pointer to offset \p n in the buffer \p p. + * Note that this is only a valid pointer if the size of the + * buffer is at least \p n + 1. + */ +static inline unsigned char *mbedtls_buffer_offset( + unsigned char *p, size_t n ) +{ + return( p == NULL ? NULL : p + n ); +} + +/** Return an offset into a read-only buffer. + * + * Similar to mbedtls_buffer_offset(), but for const pointers. + * + * \param p Pointer to a buffer of at least n bytes. + * This may be \p NULL if \p n is zero. + * \param n An offset in bytes. + * \return Pointer to offset \p n in the buffer \p p. + * Note that this is only a valid pointer if the size of the + * buffer is at least \p n + 1. + */ +static inline const unsigned char *mbedtls_buffer_offset_const( + const unsigned char *p, size_t n ) +{ + return( p == NULL ? NULL : p + n ); +} + /** Byte Reading Macros * * Given a multi-byte integer \p x, MBEDTLS_BYTE_n retrieves the n-th diff --git a/thirdparty/mbedtls/library/constant_time.c b/thirdparty/mbedtls/library/constant_time.c index e276d23ca0..2401b0434a 100644 --- a/thirdparty/mbedtls/library/constant_time.c +++ b/thirdparty/mbedtls/library/constant_time.c @@ -81,7 +81,7 @@ unsigned mbedtls_ct_uint_mask( unsigned value ) #endif } -#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC) +#if defined(MBEDTLS_SSL_SOME_MODES_USE_MAC) size_t mbedtls_ct_size_mask( size_t value ) { @@ -97,7 +97,7 @@ size_t mbedtls_ct_size_mask( size_t value ) #endif } -#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ +#endif /* MBEDTLS_SSL_SOME_MODES_USE_MAC */ #if defined(MBEDTLS_BIGNUM_C) @@ -272,7 +272,7 @@ unsigned mbedtls_ct_uint_if( unsigned condition, * \note if1 and if0 must be either 1 or -1, otherwise the result * is undefined. * - * \param condition Condition to test. + * \param condition Condition to test; must be either 0 or 1. * \param if1 The first sign; must be either +1 or -1. * \param if0 The second sign; must be either +1 or -1. * @@ -404,8 +404,7 @@ static void mbedtls_ct_mem_move_to_left( void *start, #endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */ -#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC) - +#if defined(MBEDTLS_SSL_SOME_MODES_USE_MAC) void mbedtls_ct_memcpy_if_eq( unsigned char *dest, const unsigned char *src, size_t len, @@ -527,7 +526,7 @@ cleanup: return( ret ); } -#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ +#endif /* MBEDTLS_SSL_SOME_MODES_USE_MAC */ #if defined(MBEDTLS_BIGNUM_C) diff --git a/thirdparty/mbedtls/library/constant_time_internal.h b/thirdparty/mbedtls/library/constant_time_internal.h index a550b38fa5..ff2d0ff92c 100644 --- a/thirdparty/mbedtls/library/constant_time_internal.h +++ b/thirdparty/mbedtls/library/constant_time_internal.h @@ -32,7 +32,6 @@ #include <stddef.h> - /** Turn a value into a mask: * - if \p value == 0, return the all-bits 0 mask, aka 0 * - otherwise, return the all-bits 1 mask, aka (unsigned) -1 @@ -46,7 +45,7 @@ */ unsigned mbedtls_ct_uint_mask( unsigned value ); -#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC) +#if defined(MBEDTLS_SSL_SOME_MODES_USE_MAC) /** Turn a value into a mask: * - if \p value == 0, return the all-bits 0 mask, aka 0 @@ -61,7 +60,7 @@ unsigned mbedtls_ct_uint_mask( unsigned value ); */ size_t mbedtls_ct_size_mask( size_t value ); -#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ +#endif /* MBEDTLS_SSL_SOME_MODES_USE_MAC */ #if defined(MBEDTLS_BIGNUM_C) @@ -196,7 +195,7 @@ signed char mbedtls_ct_base64_dec_value( unsigned char c ); #endif /* MBEDTLS_BASE64_C */ -#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC) +#if defined(MBEDTLS_SSL_SOME_MODES_USE_MAC) /** Conditional memcpy without branches. * @@ -292,7 +291,7 @@ int mbedtls_ct_hmac( mbedtls_md_context_t *ctx, size_t max_data_len, unsigned char *output ); -#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ +#endif /* MBEDTLS_SSL_SOME_MODES_USE_MAC */ #if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) diff --git a/thirdparty/mbedtls/library/ctr_drbg.c b/thirdparty/mbedtls/library/ctr_drbg.c index a00d66ce87..ed31576a7b 100644 --- a/thirdparty/mbedtls/library/ctr_drbg.c +++ b/thirdparty/mbedtls/library/ctr_drbg.c @@ -36,14 +36,7 @@ #include <stdio.h> #endif -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ /* * CTR_DRBG context initialization diff --git a/thirdparty/mbedtls/library/debug.c b/thirdparty/mbedtls/library/debug.c index e1086008af..353b4bf07b 100644 --- a/thirdparty/mbedtls/library/debug.c +++ b/thirdparty/mbedtls/library/debug.c @@ -21,16 +21,7 @@ #if defined(MBEDTLS_DEBUG_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#define mbedtls_time_t time_t -#define mbedtls_snprintf snprintf -#define mbedtls_vsnprintf vsnprintf -#endif #include "mbedtls/debug.h" #include "mbedtls/error.h" @@ -39,11 +30,6 @@ #include <stdio.h> #include <string.h> -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - #define DEBUG_BUF_SIZE 512 static int debug_threshold = 0; diff --git a/thirdparty/mbedtls/library/des.c b/thirdparty/mbedtls/library/des.c index 91d22b5d90..65f5681cf1 100644 --- a/thirdparty/mbedtls/library/des.c +++ b/thirdparty/mbedtls/library/des.c @@ -33,14 +33,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_DES_ALT) diff --git a/thirdparty/mbedtls/library/dhm.c b/thirdparty/mbedtls/library/dhm.c index 88e148bb80..4d2e33e689 100644 --- a/thirdparty/mbedtls/library/dhm.c +++ b/thirdparty/mbedtls/library/dhm.c @@ -43,15 +43,7 @@ #include "mbedtls/asn1.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#include <stdio.h> -#define mbedtls_printf printf -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #if !defined(MBEDTLS_DHM_ALT) diff --git a/thirdparty/mbedtls/library/ecdh.c b/thirdparty/mbedtls/library/ecdh.c index 60c6e429de..724c938a71 100644 --- a/thirdparty/mbedtls/library/ecdh.c +++ b/thirdparty/mbedtls/library/ecdh.c @@ -77,10 +77,12 @@ static int ecdh_gen_public_restartable( mbedtls_ecp_group *grp, { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - /* If multiplication is in progress, we already generated a privkey */ + int restarting = 0; #if defined(MBEDTLS_ECP_RESTARTABLE) - if( rs_ctx == NULL || rs_ctx->rsm == NULL ) + restarting = ( rs_ctx != NULL && rs_ctx->rsm != NULL ); #endif + /* If multiplication is in progress, we already generated a privkey */ + if( !restarting ) MBEDTLS_MPI_CHK( mbedtls_ecp_gen_privkey( grp, d, f_rng, p_rng ) ); MBEDTLS_MPI_CHK( mbedtls_ecp_mul_restartable( grp, Q, d, &grp->G, diff --git a/thirdparty/mbedtls/library/ecdsa.c b/thirdparty/mbedtls/library/ecdsa.c index 640eb24a26..4bae6a93fd 100644 --- a/thirdparty/mbedtls/library/ecdsa.c +++ b/thirdparty/mbedtls/library/ecdsa.c @@ -36,13 +36,7 @@ #include "mbedtls/hmac_drbg.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include "mbedtls/platform_util.h" #include "mbedtls/error.h" diff --git a/thirdparty/mbedtls/library/ecjpake.c b/thirdparty/mbedtls/library/ecjpake.c index 0b9bffb93e..fe0b9d93e9 100644 --- a/thirdparty/mbedtls/library/ecjpake.c +++ b/thirdparty/mbedtls/library/ecjpake.c @@ -794,12 +794,7 @@ cleanup: #if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif #if !defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) || \ !defined(MBEDTLS_SHA256_C) diff --git a/thirdparty/mbedtls/library/ecp.c b/thirdparty/mbedtls/library/ecp.c index 890f364a08..ad19e05fb2 100644 --- a/thirdparty/mbedtls/library/ecp.c +++ b/thirdparty/mbedtls/library/ecp.c @@ -90,15 +90,7 @@ #define ECP_VALIDATE( cond ) \ MBEDTLS_INTERNAL_VALIDATE( cond ) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#include <stdio.h> -#define mbedtls_printf printf -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include "mbedtls/ecp_internal.h" @@ -112,11 +104,6 @@ #endif #endif /* MBEDTLS_ECP_NO_INTERNAL_RNG */ -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - #if defined(MBEDTLS_SELF_TEST) /* * Counts of point addition and doubling, and field multiplications. @@ -2056,9 +2043,13 @@ static int ecp_mul_comb_core( const mbedtls_ecp_group *grp, mbedtls_ecp_point *R i = d; MBEDTLS_MPI_CHK( ecp_select_comb( grp, R, T, T_size, x[i] ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &R->Z, 1 ) ); + + int have_rng = 1; #if defined(MBEDTLS_ECP_NO_INTERNAL_RNG) - if( f_rng != 0 ) + if( f_rng == NULL ) + have_rng = 0; #endif + if( have_rng ) MBEDTLS_MPI_CHK( ecp_randomize_jac( grp, R, f_rng, p_rng ) ); } @@ -2192,9 +2183,12 @@ final_norm: * * Avoid the leak by randomizing coordinates before we normalize them. */ + int have_rng = 1; #if defined(MBEDTLS_ECP_NO_INTERNAL_RNG) - if( f_rng != 0 ) + if( f_rng == NULL ) + have_rng = 0; #endif + if( have_rng ) MBEDTLS_MPI_CHK( ecp_randomize_jac( grp, RR, f_rng, p_rng ) ); MBEDTLS_MPI_CHK( ecp_normalize_jac( grp, RR ) ); @@ -2403,12 +2397,14 @@ cleanup: mbedtls_free( T ); } - /* don't free R while in progress in case R == P */ + /* prevent caller from using invalid value */ + int should_free_R = ( ret != 0 ); #if defined(MBEDTLS_ECP_RESTARTABLE) - if( ret != MBEDTLS_ERR_ECP_IN_PROGRESS ) + /* don't free R while in progress in case R == P */ + if( ret == MBEDTLS_ERR_ECP_IN_PROGRESS ) + should_free_R = 0; #endif - /* prevent caller from using invalid value */ - if( ret != 0 ) + if( should_free_R ) mbedtls_ecp_point_free( R ); ECP_RS_LEAVE( rsm ); @@ -2596,13 +2592,16 @@ static int ecp_mul_mxz( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, MOD_ADD( RP.X ); /* Randomize coordinates of the starting point */ + int have_rng = 1; #if defined(MBEDTLS_ECP_NO_INTERNAL_RNG) - if( f_rng != NULL ) + if( f_rng == NULL ) + have_rng = 0; #endif + if( have_rng ) MBEDTLS_MPI_CHK( ecp_randomize_mxz( grp, &RP, f_rng, p_rng ) ); /* Loop invariant: R = result so far, RP = R + P */ - i = mbedtls_mpi_bitlen( m ); /* one past the (zero-based) most significant bit */ + i = grp->nbits + 1; /* one past the (zero-based) required msb for private keys */ while( i-- > 0 ) { b = mbedtls_mpi_get_bit( m, i ); @@ -2631,9 +2630,12 @@ static int ecp_mul_mxz( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, * * Avoid the leak by randomizing coordinates before we normalize them. */ + have_rng = 1; #if defined(MBEDTLS_ECP_NO_INTERNAL_RNG) - if( f_rng != NULL ) + if( f_rng == NULL ) + have_rng = 0; #endif + if( have_rng ) MBEDTLS_MPI_CHK( ecp_randomize_mxz( grp, R, f_rng, p_rng ) ); MBEDTLS_MPI_CHK( ecp_normalize_mxz( grp, R ) ); @@ -2680,10 +2682,12 @@ int mbedtls_ecp_mul_restartable( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, MBEDTLS_MPI_CHK( mbedtls_internal_ecp_init( grp ) ); #endif /* MBEDTLS_ECP_INTERNAL_ALT */ + int restarting = 0; #if defined(MBEDTLS_ECP_RESTARTABLE) - /* skip argument check when restarting */ - if( rs_ctx == NULL || rs_ctx->rsm == NULL ) + restarting = ( rs_ctx != NULL && rs_ctx->rsm != NULL ); #endif + /* skip argument check when restarting */ + if( !restarting ) { /* check_privkey is free */ MBEDTLS_ECP_BUDGET( MBEDTLS_ECP_OPS_CHK ); @@ -2797,14 +2801,17 @@ static int mbedtls_ecp_mul_shortcuts( mbedtls_ecp_group *grp, if( mbedtls_mpi_cmp_int( m, 0 ) == 0 ) { + MBEDTLS_MPI_CHK( mbedtls_ecp_check_pubkey( grp, P ) ); MBEDTLS_MPI_CHK( mbedtls_ecp_set_zero( R ) ); } else if( mbedtls_mpi_cmp_int( m, 1 ) == 0 ) { + MBEDTLS_MPI_CHK( mbedtls_ecp_check_pubkey( grp, P ) ); MBEDTLS_MPI_CHK( mbedtls_ecp_copy( R, P ) ); } else if( mbedtls_mpi_cmp_int( m, -1 ) == 0 ) { + MBEDTLS_MPI_CHK( mbedtls_ecp_check_pubkey( grp, P ) ); MBEDTLS_MPI_CHK( mbedtls_ecp_copy( R, P ) ); if( mbedtls_mpi_cmp_int( &R->Y, 0 ) != 0 ) MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &R->Y, &grp->P, &R->Y ) ); diff --git a/thirdparty/mbedtls/library/ecp_curves.c b/thirdparty/mbedtls/library/ecp_curves.c index 2199be6461..47761eef4e 100644 --- a/thirdparty/mbedtls/library/ecp_curves.c +++ b/thirdparty/mbedtls/library/ecp_curves.c @@ -38,11 +38,6 @@ #define ECP_VALIDATE( cond ) \ MBEDTLS_INTERNAL_VALIDATE( cond ) -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - #define ECP_MPI_INIT(s, n, p) {s, (n), (mbedtls_mpi_uint *)(p)} #define ECP_MPI_INIT_ARRAY(x) \ diff --git a/thirdparty/mbedtls/library/entropy.c b/thirdparty/mbedtls/library/entropy.c index 12fd3b9b5f..e3b337ff35 100644 --- a/thirdparty/mbedtls/library/entropy.c +++ b/thirdparty/mbedtls/library/entropy.c @@ -38,18 +38,9 @@ #include <stdio.h> #endif -#if defined(MBEDTLS_ENTROPY_NV_SEED) #include "mbedtls/platform.h" -#endif -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if defined(MBEDTLS_HAVEGE_C) #include "mbedtls/havege.h" diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c index 40f23fd2a6..69ac29e4f7 100644 --- a/thirdparty/mbedtls/library/entropy_poll.c +++ b/thirdparty/mbedtls/library/entropy_poll.c @@ -38,9 +38,7 @@ #if defined(MBEDTLS_HAVEGE_C) #include "mbedtls/havege.h" #endif -#if defined(MBEDTLS_ENTROPY_NV_SEED) #include "mbedtls/platform.h" -#endif #if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) diff --git a/thirdparty/mbedtls/library/error.c b/thirdparty/mbedtls/library/error.c index afad38904f..8573369a32 100644 --- a/thirdparty/mbedtls/library/error.c +++ b/thirdparty/mbedtls/library/error.c @@ -25,11 +25,7 @@ #if defined(MBEDTLS_ERROR_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#define mbedtls_snprintf snprintf -#endif #include <stdio.h> #include <string.h> @@ -961,7 +957,7 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen ) #else /* MBEDTLS_ERROR_C */ /* - * Provide an non-function in case MBEDTLS_ERROR_C is not defined + * Provide a dummy implementation when MBEDTLS_ERROR_C is not defined */ void mbedtls_strerror( int ret, char *buf, size_t buflen ) { diff --git a/thirdparty/mbedtls/library/gcm.c b/thirdparty/mbedtls/library/gcm.c index 43a5e1bec6..d0b73379a6 100644 --- a/thirdparty/mbedtls/library/gcm.c +++ b/thirdparty/mbedtls/library/gcm.c @@ -32,6 +32,7 @@ #if defined(MBEDTLS_GCM_C) #include "mbedtls/gcm.h" +#include "mbedtls/platform.h" #include "mbedtls/platform_util.h" #include "mbedtls/error.h" @@ -41,15 +42,6 @@ #include "mbedtls/aesni.h" #endif -#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) -#include "mbedtls/aes.h" -#include "mbedtls/platform.h" -#if !defined(MBEDTLS_PLATFORM_C) -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ - #if !defined(MBEDTLS_GCM_ALT) /* Parameter validation macros */ diff --git a/thirdparty/mbedtls/library/hmac_drbg.c b/thirdparty/mbedtls/library/hmac_drbg.c index de9706885c..69272fa73d 100644 --- a/thirdparty/mbedtls/library/hmac_drbg.c +++ b/thirdparty/mbedtls/library/hmac_drbg.c @@ -37,14 +37,7 @@ #include <stdio.h> #endif -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_SELF_TEST */ -#endif /* MBEDTLS_PLATFORM_C */ /* * HMAC_DRBG context initialization diff --git a/thirdparty/mbedtls/library/md.c b/thirdparty/mbedtls/library/md.c index a10a835634..53a84b01c6 100644 --- a/thirdparty/mbedtls/library/md.c +++ b/thirdparty/mbedtls/library/md.c @@ -38,13 +38,7 @@ #include "mbedtls/sha256.h" #include "mbedtls/sha512.h" -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include <string.h> diff --git a/thirdparty/mbedtls/library/md2.c b/thirdparty/mbedtls/library/md2.c index 7264e30313..f8293a1df3 100644 --- a/thirdparty/mbedtls/library/md2.c +++ b/thirdparty/mbedtls/library/md2.c @@ -33,14 +33,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_MD2_ALT) diff --git a/thirdparty/mbedtls/library/md4.c b/thirdparty/mbedtls/library/md4.c index eaa679a0a6..a412213480 100644 --- a/thirdparty/mbedtls/library/md4.c +++ b/thirdparty/mbedtls/library/md4.c @@ -33,14 +33,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_MD4_ALT) diff --git a/thirdparty/mbedtls/library/md5.c b/thirdparty/mbedtls/library/md5.c index 4b53fcf367..e53bfe6306 100644 --- a/thirdparty/mbedtls/library/md5.c +++ b/thirdparty/mbedtls/library/md5.c @@ -32,14 +32,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_MD5_ALT) diff --git a/thirdparty/mbedtls/library/mps_reader.c b/thirdparty/mbedtls/library/mps_reader.c index 9af5073cc9..0c30a75d05 100644 --- a/thirdparty/mbedtls/library/mps_reader.c +++ b/thirdparty/mbedtls/library/mps_reader.c @@ -29,11 +29,6 @@ #include <string.h> -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - #if defined(MBEDTLS_MPS_ENABLE_TRACE) static int mbedtls_mps_trace_id = MBEDTLS_MPS_TRACE_BIT_READER; #endif /* MBEDTLS_MPS_ENABLE_TRACE */ @@ -535,7 +530,7 @@ int mbedtls_mps_reader_reclaim( mbedtls_mps_reader *rd, * of the accumulator. */ memmove( acc, acc + acc_backup_offset, acc_backup_len ); - /* Copy uncmmitted parts of the current fragment to the + /* Copy uncommitted parts of the current fragment to the * accumulator. */ memcpy( acc + acc_backup_len, frag + frag_backup_offset, frag_backup_len ); diff --git a/thirdparty/mbedtls/library/mps_trace.h b/thirdparty/mbedtls/library/mps_trace.h index 7c2360118a..820a1b66c2 100644 --- a/thirdparty/mbedtls/library/mps_trace.h +++ b/thirdparty/mbedtls/library/mps_trace.h @@ -30,13 +30,7 @@ #include "mps_common.h" #include "mps_trace.h" -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#define mbedtls_vsnprintf vsnprintf -#endif /* MBEDTLS_PLATFORM_C */ #if defined(MBEDTLS_MPS_ENABLE_TRACE) diff --git a/thirdparty/mbedtls/library/net_sockets.c b/thirdparty/mbedtls/library/net_sockets.c index 8c765e1c8c..b2cab503f2 100644 --- a/thirdparty/mbedtls/library/net_sockets.c +++ b/thirdparty/mbedtls/library/net_sockets.c @@ -37,11 +37,7 @@ #error "This module only works on Unix and Windows, see MBEDTLS_NET_C in config.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#endif #include "mbedtls/net_sockets.h" #include "mbedtls/error.h" diff --git a/thirdparty/mbedtls/library/nist_kw.c b/thirdparty/mbedtls/library/nist_kw.c index 1aea0b6345..495c23d06a 100644 --- a/thirdparty/mbedtls/library/nist_kw.c +++ b/thirdparty/mbedtls/library/nist_kw.c @@ -39,14 +39,7 @@ #include <stdint.h> #include <string.h> -#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ #if !defined(MBEDTLS_NIST_KW_ALT) diff --git a/thirdparty/mbedtls/library/oid.c b/thirdparty/mbedtls/library/oid.c index 19c8ac207c..53e5350eb3 100644 --- a/thirdparty/mbedtls/library/oid.c +++ b/thirdparty/mbedtls/library/oid.c @@ -30,11 +30,7 @@ #include <stdio.h> #include <string.h> -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#define mbedtls_snprintf snprintf -#endif /* * Macro to automatically add the size of #define'd OIDs diff --git a/thirdparty/mbedtls/library/pem.c b/thirdparty/mbedtls/library/pem.c index fcfde94799..cb1c82b3e4 100644 --- a/thirdparty/mbedtls/library/pem.c +++ b/thirdparty/mbedtls/library/pem.c @@ -32,13 +32,7 @@ #include <string.h> -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #if defined(MBEDTLS_PEM_PARSE_C) void mbedtls_pem_init( mbedtls_pem_context *ctx ) diff --git a/thirdparty/mbedtls/library/pk_wrap.c b/thirdparty/mbedtls/library/pk_wrap.c index 107e912ace..f35abf21a4 100644 --- a/thirdparty/mbedtls/library/pk_wrap.c +++ b/thirdparty/mbedtls/library/pk_wrap.c @@ -50,13 +50,7 @@ #include "mbedtls/asn1.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include <limits.h> #include <stdint.h> @@ -872,7 +866,7 @@ static void *pk_opaque_alloc_wrap( void ) { void *ctx = mbedtls_calloc( 1, sizeof( psa_key_id_t ) ); - /* no _init() function to call, an calloc() already zeroized */ + /* no _init() function to call, as calloc() already zeroized */ return( ctx ); } diff --git a/thirdparty/mbedtls/library/pkcs11.c b/thirdparty/mbedtls/library/pkcs11.c index 4deccf3f60..a7207cfc93 100644 --- a/thirdparty/mbedtls/library/pkcs11.c +++ b/thirdparty/mbedtls/library/pkcs11.c @@ -29,13 +29,7 @@ #include "mbedtls/oid.h" #include "mbedtls/x509_crt.h" -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include <string.h> diff --git a/thirdparty/mbedtls/library/pkcs5.c b/thirdparty/mbedtls/library/pkcs5.c index 2b014d91c8..f9d01371a7 100644 --- a/thirdparty/mbedtls/library/pkcs5.c +++ b/thirdparty/mbedtls/library/pkcs5.c @@ -42,12 +42,7 @@ #include <string.h> -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif #if defined(MBEDTLS_ASN1_PARSE_C) static int pkcs5_parse_pbkdf2_params( const mbedtls_asn1_buf *params, diff --git a/thirdparty/mbedtls/library/pkparse.c b/thirdparty/mbedtls/library/pkparse.c index ea5c6b69cb..6170d6d012 100644 --- a/thirdparty/mbedtls/library/pkparse.c +++ b/thirdparty/mbedtls/library/pkparse.c @@ -48,13 +48,7 @@ #include "mbedtls/pkcs12.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif /* Parameter validation macros based on platform_util.h */ #define PK_VALIDATE_RET( cond ) \ diff --git a/thirdparty/mbedtls/library/pkwrite.c b/thirdparty/mbedtls/library/pkwrite.c index 566153dd93..c1ce0e3f02 100644 --- a/thirdparty/mbedtls/library/pkwrite.c +++ b/thirdparty/mbedtls/library/pkwrite.c @@ -48,13 +48,7 @@ #include "psa/crypto.h" #include "mbedtls/psa_util.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif /* Parameter validation macros based on platform_util.h */ #define PK_VALIDATE_RET( cond ) \ diff --git a/thirdparty/mbedtls/library/poly1305.c b/thirdparty/mbedtls/library/poly1305.c index 7375a0c572..a1c5b19d8e 100644 --- a/thirdparty/mbedtls/library/poly1305.c +++ b/thirdparty/mbedtls/library/poly1305.c @@ -28,22 +28,10 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_POLY1305_ALT) -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - /* Parameter validation macros */ #define POLY1305_VALIDATE_RET( cond ) \ MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ) diff --git a/thirdparty/mbedtls/library/ripemd160.c b/thirdparty/mbedtls/library/ripemd160.c index aed7322cff..55e259e192 100644 --- a/thirdparty/mbedtls/library/ripemd160.c +++ b/thirdparty/mbedtls/library/ripemd160.c @@ -33,14 +33,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_RIPEMD160_ALT) diff --git a/thirdparty/mbedtls/library/rsa.c b/thirdparty/mbedtls/library/rsa.c index d1f6ddb177..9c39fa5d91 100644 --- a/thirdparty/mbedtls/library/rsa.c +++ b/thirdparty/mbedtls/library/rsa.c @@ -57,14 +57,7 @@ #include <stdlib.h> #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #if !defined(MBEDTLS_RSA_ALT) diff --git a/thirdparty/mbedtls/library/sha1.c b/thirdparty/mbedtls/library/sha1.c index 0a5edafaff..7f0c8757db 100644 --- a/thirdparty/mbedtls/library/sha1.c +++ b/thirdparty/mbedtls/library/sha1.c @@ -32,14 +32,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #define SHA1_VALIDATE_RET(cond) \ MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_SHA1_BAD_INPUT_DATA ) diff --git a/thirdparty/mbedtls/library/sha256.c b/thirdparty/mbedtls/library/sha256.c index db675efd1b..6f1306ee66 100644 --- a/thirdparty/mbedtls/library/sha256.c +++ b/thirdparty/mbedtls/library/sha256.c @@ -32,17 +32,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#include <stdlib.h> -#define mbedtls_printf printf -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #define SHA256_VALIDATE_RET(cond) \ MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_SHA256_BAD_INPUT_DATA ) diff --git a/thirdparty/mbedtls/library/sha512.c b/thirdparty/mbedtls/library/sha512.c index 02a135ca92..1a6872c8aa 100644 --- a/thirdparty/mbedtls/library/sha512.c +++ b/thirdparty/mbedtls/library/sha512.c @@ -38,17 +38,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#include <stdlib.h> -#define mbedtls_printf printf -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #define SHA512_VALIDATE_RET(cond) \ MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_SHA512_BAD_INPUT_DATA ) @@ -428,9 +418,11 @@ int mbedtls_sha512_finish_ret( mbedtls_sha512_context *ctx, sha512_put_uint64_be( ctx->state[4], output, 32 ); sha512_put_uint64_be( ctx->state[5], output, 40 ); + int truncated = 0; #if !defined(MBEDTLS_SHA512_NO_SHA384) - if( ctx->is384 == 0 ) + truncated = ctx->is384; #endif + if( !truncated ) { sha512_put_uint64_be( ctx->state[6], output, 48 ); sha512_put_uint64_be( ctx->state[7], output, 56 ); diff --git a/thirdparty/mbedtls/library/ssl_cache.c b/thirdparty/mbedtls/library/ssl_cache.c index 32188cf3f6..7a600cad18 100644 --- a/thirdparty/mbedtls/library/ssl_cache.c +++ b/thirdparty/mbedtls/library/ssl_cache.c @@ -25,13 +25,7 @@ #if defined(MBEDTLS_SSL_CACHE_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include "mbedtls/ssl_cache.h" #include "mbedtls/ssl_internal.h" diff --git a/thirdparty/mbedtls/library/ssl_ciphersuites.c b/thirdparty/mbedtls/library/ssl_ciphersuites.c index ceec77efb0..2bc8a9bba0 100644 --- a/thirdparty/mbedtls/library/ssl_ciphersuites.c +++ b/thirdparty/mbedtls/library/ssl_ciphersuites.c @@ -23,11 +23,7 @@ #if defined(MBEDTLS_SSL_TLS_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#endif #include "mbedtls/ssl_ciphersuites.h" #include "mbedtls/ssl.h" diff --git a/thirdparty/mbedtls/library/ssl_cli.c b/thirdparty/mbedtls/library/ssl_cli.c index 72351c9757..b40ddb70b4 100644 --- a/thirdparty/mbedtls/library/ssl_cli.c +++ b/thirdparty/mbedtls/library/ssl_cli.c @@ -21,13 +21,7 @@ #if defined(MBEDTLS_SSL_CLI_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include "mbedtls/ssl.h" #include "mbedtls/ssl_internal.h" @@ -174,7 +168,7 @@ static int ssl_write_renegotiation_ext( mbedtls_ssl_context *ssl, *olen = 0; - /* We're always including an TLS_EMPTY_RENEGOTIATION_INFO_SCSV in the + /* We're always including a TLS_EMPTY_RENEGOTIATION_INFO_SCSV in the * initial ClientHello, in which case also adding the renegotiation * info extension is NOT RECOMMENDED as per RFC 5746 Section 3.4. */ if( ssl->renego_status != MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS ) @@ -1004,9 +998,12 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl ) return( MBEDTLS_ERR_SSL_NO_RNG ); } + int renegotiating = 0; #if defined(MBEDTLS_SSL_RENEGOTIATION) - if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE ) + if( ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE ) + renegotiating = 1; #endif + if( !renegotiating ) { ssl->major_ver = ssl->conf->min_major_ver; ssl->minor_ver = ssl->conf->min_minor_ver; @@ -1092,9 +1089,7 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl ) * RFC 5077 section 3.4: "When presenting a ticket, the client MAY * generate and include a Session ID in the TLS ClientHello." */ -#if defined(MBEDTLS_SSL_RENEGOTIATION) - if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE ) -#endif + if( !renegotiating ) { if( ssl->session_negotiate->ticket != NULL && ssl->session_negotiate->ticket_len != 0 ) @@ -1209,9 +1204,7 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl ) /* * Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ -#if defined(MBEDTLS_SSL_RENEGOTIATION) - if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE ) -#endif + if( !renegotiating ) { MBEDTLS_SSL_DEBUG_MSG( 3, ( "adding EMPTY_RENEGOTIATION_INFO_SCSV" ) ); MBEDTLS_SSL_CHK_BUF_PTR( p, end, 2 ); @@ -2062,6 +2055,30 @@ static int ssl_parse_hello_verify_request( mbedtls_ssl_context *ssl ) } #endif /* MBEDTLS_SSL_PROTO_DTLS */ +static int is_compression_bad( mbedtls_ssl_context *ssl, unsigned char comp ) +{ + int bad_comp = 0; + + /* Suppress warnings in some configurations */ + (void) ssl; +#if defined(MBEDTLS_ZLIB_SUPPORT) + /* See comments in ssl_write_client_hello() */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + comp != MBEDTLS_SSL_COMPRESS_NULL ) + bad_comp = 1; +#endif + + if( comp != MBEDTLS_SSL_COMPRESS_NULL && + comp != MBEDTLS_SSL_COMPRESS_DEFLATE ) + bad_comp = 1; +#else /* MBEDTLS_ZLIB_SUPPORT */ + if( comp != MBEDTLS_SSL_COMPRESS_NULL ) + bad_comp = 1; +#endif/* MBEDTLS_ZLIB_SUPPORT */ + return bad_comp; +} + MBEDTLS_CHECK_RETURN_CRITICAL static int ssl_parse_server_hello( mbedtls_ssl_context *ssl ) { @@ -2070,9 +2087,6 @@ static int ssl_parse_server_hello( mbedtls_ssl_context *ssl ) size_t ext_len; unsigned char *buf, *ext; unsigned char comp; -#if defined(MBEDTLS_ZLIB_SUPPORT) - int accept_comp; -#endif #if defined(MBEDTLS_SSL_RENEGOTIATION) int renegotiation_info_seen = 0; #endif @@ -2241,20 +2255,7 @@ static int ssl_parse_server_hello( mbedtls_ssl_context *ssl ) */ comp = buf[37 + n]; -#if defined(MBEDTLS_ZLIB_SUPPORT) - /* See comments in ssl_write_client_hello() */ -#if defined(MBEDTLS_SSL_PROTO_DTLS) - if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM ) - accept_comp = 0; - else -#endif - accept_comp = 1; - - if( comp != MBEDTLS_SSL_COMPRESS_NULL && - ( comp != MBEDTLS_SSL_COMPRESS_DEFLATE || accept_comp == 0 ) ) -#else /* MBEDTLS_ZLIB_SUPPORT */ - if( comp != MBEDTLS_SSL_COMPRESS_NULL ) -#endif/* MBEDTLS_ZLIB_SUPPORT */ + if( is_compression_bad( ssl, comp ) ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "server hello, bad compression: %d", comp ) ); @@ -2687,7 +2688,7 @@ static int ssl_check_server_ecdh_params( const mbedtls_ssl_context *ssl ) grp_id = ssl->handshake->ecdh_ctx.grp.id; #else grp_id = ssl->handshake->ecdh_ctx.grp_id; -#endif +#endif /* MBEDTLS_ECDH_LEGACY_CONTEXT */ curve_info = mbedtls_ecp_curve_info_from_grp_id( grp_id ); if( curve_info == NULL ) @@ -2700,11 +2701,12 @@ static int ssl_check_server_ecdh_params( const mbedtls_ssl_context *ssl ) #if defined(MBEDTLS_ECP_C) if( mbedtls_ssl_check_curve( ssl, grp_id ) != 0 ) + return( -1 ); #else if( ssl->handshake->ecdh_ctx.grp.nbits < 163 || ssl->handshake->ecdh_ctx.grp.nbits > 521 ) -#endif return( -1 ); +#endif /* MBEDTLS_ECP_C */ MBEDTLS_SSL_DEBUG_ECDH( 3, &ssl->handshake->ecdh_ctx, MBEDTLS_DEBUG_ECDH_QP ); @@ -2858,8 +2860,8 @@ static int ssl_parse_server_psk_hint( mbedtls_ssl_context *ssl, } /* - * Note: we currently ignore the PKS identity hint, as we only allow one - * PSK to be provisionned on the client. This could be changed later if + * Note: we currently ignore the PSK identity hint, as we only allow one + * PSK to be provisioned on the client. This could be changed later if * someone needs that feature. */ *p += len; @@ -3452,23 +3454,23 @@ start_processing: #if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) if( ssl->handshake->ecrs_enabled ) rs_ctx = &ssl->handshake->ecrs_ctx.pk; -#endif +#endif /* MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED */ if( ( ret = mbedtls_pk_verify_restartable( peer_pk, md_alg, hash, hashlen, p, sig_len, rs_ctx ) ) != 0 ) { #if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) - if( ret != MBEDTLS_ERR_ECP_IN_PROGRESS ) -#endif - mbedtls_ssl_send_alert_message( - ssl, - MBEDTLS_SSL_ALERT_LEVEL_FATAL, - MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR ); - MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_pk_verify", ret ); -#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) if( ret == MBEDTLS_ERR_ECP_IN_PROGRESS ) - ret = MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS; -#endif + { + MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_pk_verify", ret ); + return( MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS ); + } +#endif /* MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED */ + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR ); + MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_pk_verify", ret ); return( ret ); } diff --git a/thirdparty/mbedtls/library/ssl_cookie.c b/thirdparty/mbedtls/library/ssl_cookie.c index 3781796b72..f12f28e849 100644 --- a/thirdparty/mbedtls/library/ssl_cookie.c +++ b/thirdparty/mbedtls/library/ssl_cookie.c @@ -25,12 +25,7 @@ #if defined(MBEDTLS_SSL_COOKIE_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include "mbedtls/ssl_cookie.h" #include "mbedtls/ssl_internal.h" diff --git a/thirdparty/mbedtls/library/ssl_msg.c b/thirdparty/mbedtls/library/ssl_msg.c index e47c538888..d7bebe04d6 100644 --- a/thirdparty/mbedtls/library/ssl_msg.c +++ b/thirdparty/mbedtls/library/ssl_msg.c @@ -30,13 +30,7 @@ #if defined(MBEDTLS_SSL_TLS_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include "mbedtls/ssl.h" #include "mbedtls/ssl_internal.h" @@ -441,9 +435,12 @@ static void ssl_extract_add_data_from_record( unsigned char* add_data, unsigned char *cur = add_data; + int is_tls13 = 0; #if defined(MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL) - if( minor_ver != MBEDTLS_SSL_MINOR_VERSION_4 ) + if( minor_ver == MBEDTLS_SSL_MINOR_VERSION_4 ) + is_tls13 = 1; #endif /* MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL */ + if( !is_tls13 ) { ((void) minor_ver); memcpy( cur, rec->ctr, sizeof( rec->ctr ) ); @@ -1887,8 +1884,7 @@ int mbedtls_ssl_fetch_input( mbedtls_ssl_context *ssl, size_t nb_want ) if( ssl->f_recv == NULL && ssl->f_recv_timeout == NULL ) { - MBEDTLS_SSL_DEBUG_MSG( 1, ( "Bad usage of mbedtls_ssl_set_bio() " - "or mbedtls_ssl_set_bio()" ) ); + MBEDTLS_SSL_DEBUG_MSG( 1, ( "Bad usage of mbedtls_ssl_set_bio() " ) ); return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); } @@ -2103,8 +2099,7 @@ int mbedtls_ssl_flush_output( mbedtls_ssl_context *ssl ) if( ssl->f_send == NULL ) { - MBEDTLS_SSL_DEBUG_MSG( 1, ( "Bad usage of mbedtls_ssl_set_bio() " - "or mbedtls_ssl_set_bio()" ) ); + MBEDTLS_SSL_DEBUG_MSG( 1, ( "Bad usage of mbedtls_ssl_set_bio() " ) ); return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); } @@ -3950,8 +3945,8 @@ int mbedtls_ssl_read_record( mbedtls_ssl_context *ssl, if( ssl_record_is_in_progress( ssl ) == 0 ) { + int dtls_have_buffered = 0; #if defined(MBEDTLS_SSL_PROTO_DTLS) - int have_buffered = 0; /* We only check for buffered messages if the * current datagram is fully consumed. */ @@ -3959,11 +3954,11 @@ int mbedtls_ssl_read_record( mbedtls_ssl_context *ssl, ssl_next_record_is_in_datagram( ssl ) == 0 ) { if( ssl_load_buffered_message( ssl ) == 0 ) - have_buffered = 1; + dtls_have_buffered = 1; } - if( have_buffered == 0 ) #endif /* MBEDTLS_SSL_PROTO_DTLS */ + if( dtls_have_buffered == 0 ) { ret = ssl_get_next_record( ssl ); if( ret == MBEDTLS_ERR_SSL_CONTINUE_PROCESSING ) @@ -4037,7 +4032,7 @@ static int ssl_load_buffered_message( mbedtls_ssl_context *ssl ) if( hs == NULL ) return( -1 ); - MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> ssl_load_buffered_messsage" ) ); + MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> ssl_load_buffered_message" ) ); if( ssl->state == MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC || ssl->state == MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC ) diff --git a/thirdparty/mbedtls/library/ssl_srv.c b/thirdparty/mbedtls/library/ssl_srv.c index 2efb13cc33..0563c0b590 100644 --- a/thirdparty/mbedtls/library/ssl_srv.c +++ b/thirdparty/mbedtls/library/ssl_srv.c @@ -21,13 +21,7 @@ #if defined(MBEDTLS_SSL_SRV_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include "mbedtls/ssl.h" #include "mbedtls/ssl_internal.h" @@ -1460,6 +1454,7 @@ static int ssl_parse_client_hello( mbedtls_ssl_context *ssl ) MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse client hello" ) ); + int renegotiating = 0; #if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) read_record_header: #endif @@ -1469,8 +1464,10 @@ read_record_header: * ClientHello, which doesn't use the same record layer format. */ #if defined(MBEDTLS_SSL_RENEGOTIATION) - if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE ) + if( ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE ) + renegotiating = 1; #endif + if( !renegotiating ) { if( ( ret = mbedtls_ssl_fetch_input( ssl, 5 ) ) != 0 ) { @@ -1483,9 +1480,12 @@ read_record_header: buf = ssl->in_hdr; #if defined(MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO) + int is_dtls = 0; #if defined(MBEDTLS_SSL_PROTO_DTLS) - if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_STREAM ) + if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM ) + is_dtls = 1; #endif + if( !is_dtls ) if( ( buf[0] & 0x80 ) != 0 ) return( ssl_parse_client_hello_v2( ssl ) ); #endif @@ -3903,8 +3903,14 @@ static int ssl_decrypt_encrypted_pms( mbedtls_ssl_context *ssl, size_t peer_pmssize ) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_x509_crt *own_cert = mbedtls_ssl_own_cert( ssl ); + if( own_cert == NULL ) { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no local certificate" ) ); + return( MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE ); + } + mbedtls_pk_context *public_key = &own_cert->pk; mbedtls_pk_context *private_key = mbedtls_ssl_own_key( ssl ); - mbedtls_pk_context *public_key = &mbedtls_ssl_own_cert( ssl )->pk; size_t len = mbedtls_pk_get_len( public_key ); #if defined(MBEDTLS_SSL_ASYNC_PRIVATE) diff --git a/thirdparty/mbedtls/library/ssl_ticket.c b/thirdparty/mbedtls/library/ssl_ticket.c index e0126cc9d1..8a57789f10 100644 --- a/thirdparty/mbedtls/library/ssl_ticket.c +++ b/thirdparty/mbedtls/library/ssl_ticket.c @@ -21,13 +21,7 @@ #if defined(MBEDTLS_SSL_TICKET_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include "mbedtls/ssl_internal.h" #include "mbedtls/ssl_ticket.h" @@ -152,27 +146,45 @@ int mbedtls_ssl_ticket_setup( mbedtls_ssl_ticket_context *ctx, if( cipher_info->key_bitlen > 8 * MAX_KEY_BYTES ) return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + int do_mbedtls_cipher_setup = 1; #if defined(MBEDTLS_USE_PSA_CRYPTO) ret = mbedtls_cipher_setup_psa( &ctx->keys[0].ctx, cipher_info, TICKET_AUTH_TAG_BYTES ); - if( ret != 0 && ret != MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE ) - return( ret ); - /* We don't yet expect to support all ciphers through PSA, - * so allow fallback to ordinary mbedtls_cipher_setup(). */ - if( ret == MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE ) + + switch( ret ) + { + case 0: + do_mbedtls_cipher_setup = 0; + break; + case MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE: + /* We don't yet expect to support all ciphers through PSA, + * so allow fallback to ordinary mbedtls_cipher_setup(). */ + do_mbedtls_cipher_setup = 1; + break; + default: + return( ret ); + } #endif /* MBEDTLS_USE_PSA_CRYPTO */ - if( ( ret = mbedtls_cipher_setup( &ctx->keys[0].ctx, cipher_info ) ) != 0 ) - return( ret ); + if( do_mbedtls_cipher_setup ) + if( ( ret = mbedtls_cipher_setup( &ctx->keys[0].ctx, cipher_info ) ) + != 0 ) + return( ret ); + do_mbedtls_cipher_setup = 1; #if defined(MBEDTLS_USE_PSA_CRYPTO) + do_mbedtls_cipher_setup = 0; + ret = mbedtls_cipher_setup_psa( &ctx->keys[1].ctx, cipher_info, TICKET_AUTH_TAG_BYTES ); if( ret != 0 && ret != MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE ) return( ret ); if( ret == MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE ) + do_mbedtls_cipher_setup = 1; #endif /* MBEDTLS_USE_PSA_CRYPTO */ - if( ( ret = mbedtls_cipher_setup( &ctx->keys[1].ctx, cipher_info ) ) != 0 ) - return( ret ); + if( do_mbedtls_cipher_setup ) + if( ( ret = mbedtls_cipher_setup( &ctx->keys[1].ctx, cipher_info ) ) + != 0 ) + return( ret ); if( ( ret = ssl_ticket_gen_key( ctx, 0 ) ) != 0 || ( ret = ssl_ticket_gen_key( ctx, 1 ) ) != 0 ) diff --git a/thirdparty/mbedtls/library/ssl_tls.c b/thirdparty/mbedtls/library/ssl_tls.c index 7badec51ae..70196a4861 100644 --- a/thirdparty/mbedtls/library/ssl_tls.c +++ b/thirdparty/mbedtls/library/ssl_tls.c @@ -29,13 +29,7 @@ #if defined(MBEDTLS_SSL_TLS_C) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif #include "mbedtls/ssl.h" #include "mbedtls/ssl_internal.h" @@ -766,7 +760,9 @@ static int tls_prf_generic( mbedtls_md_type_t md_type, exit: mbedtls_md_free( &md_ctx ); - mbedtls_platform_zeroize( tmp, tmp_len ); + if ( tmp != NULL ) + mbedtls_platform_zeroize( tmp, tmp_len ); + mbedtls_platform_zeroize( h_i, sizeof( h_i ) ); mbedtls_free( tmp ); @@ -985,6 +981,7 @@ static int ssl_populate_transform( mbedtls_ssl_transform *transform, #if defined(MBEDTLS_USE_PSA_CRYPTO) int psa_fallthrough; #endif /* MBEDTLS_USE_PSA_CRYPTO */ + int do_mbedtls_cipher_setup; unsigned char keyblk[256]; unsigned char *key1; unsigned char *key2; @@ -1363,6 +1360,7 @@ static int ssl_populate_transform( mbedtls_ssl_transform *transform, } #endif + do_mbedtls_cipher_setup = 1; #if defined(MBEDTLS_USE_PSA_CRYPTO) /* Only use PSA-based ciphers for TLS-1.2. @@ -1398,15 +1396,18 @@ static int ssl_populate_transform( mbedtls_ssl_transform *transform, psa_fallthrough = 1; #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ - if( psa_fallthrough == 1 ) + if( psa_fallthrough == 0 ) + do_mbedtls_cipher_setup = 0; #endif /* MBEDTLS_USE_PSA_CRYPTO */ - if( ( ret = mbedtls_cipher_setup( &transform->cipher_ctx_enc, - cipher_info ) ) != 0 ) + if( do_mbedtls_cipher_setup && + ( ret = mbedtls_cipher_setup( &transform->cipher_ctx_enc, + cipher_info ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_cipher_setup", ret ); goto end; } + do_mbedtls_cipher_setup = 1; #if defined(MBEDTLS_USE_PSA_CRYPTO) /* Only use PSA-based ciphers for TLS-1.2. * That's relevant at least for TLS-1.0, where @@ -1441,10 +1442,12 @@ static int ssl_populate_transform( mbedtls_ssl_transform *transform, psa_fallthrough = 1; #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ - if( psa_fallthrough == 1 ) + if( psa_fallthrough == 0 ) + do_mbedtls_cipher_setup = 0; #endif /* MBEDTLS_USE_PSA_CRYPTO */ - if( ( ret = mbedtls_cipher_setup( &transform->cipher_ctx_dec, - cipher_info ) ) != 0 ) + if( do_mbedtls_cipher_setup && + ( ret = mbedtls_cipher_setup( &transform->cipher_ctx_dec, + cipher_info ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_cipher_setup", ret ); goto end; @@ -3411,7 +3414,7 @@ static void ssl_calc_finished_tls_sha384( sha512.state, sizeof( sha512.state ) ); #endif /* mbedtls_sha512_finish_ret's output parameter is declared as a - * 64-byte buffer, but sice we're using SHA-384, we know that the + * 64-byte buffer, but since we're using SHA-384, we know that the * output fits in 48 bytes. This is correct C, but GCC 11.1 warns * about it. */ @@ -4089,9 +4092,12 @@ int mbedtls_ssl_session_reset_int( mbedtls_ssl_context *ssl, int partial ) memset( ssl->out_buf, 0, out_buf_len ); + int clear_in_buf = 1; #if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) && defined(MBEDTLS_SSL_SRV_C) - if( partial == 0 ) + if( partial != 0 ) + clear_in_buf = 0; #endif /* MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE && MBEDTLS_SSL_SRV_C */ + if( clear_in_buf ) { ssl->in_left = 0; memset( ssl->in_buf, 0, in_buf_len ); @@ -4128,9 +4134,12 @@ int mbedtls_ssl_session_reset_int( mbedtls_ssl_context *ssl, int partial ) #endif #if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && defined(MBEDTLS_SSL_SRV_C) + int free_cli_id = 1; #if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) - if( partial == 0 ) + if( partial != 0 ) + free_cli_id = 0; #endif + if( free_cli_id ) { mbedtls_free( ssl->cli_id ); ssl->cli_id = NULL; @@ -4471,7 +4480,7 @@ static void ssl_conf_remove_psk( mbedtls_ssl_config *conf ) conf->psk_opaque = MBEDTLS_SVC_KEY_ID_INIT; } /* This and the following branch should never - * be taken simultaenously as we maintain the + * be taken simultaneously as we maintain the * invariant that raw and opaque PSKs are never * configured simultaneously. As a safeguard, * though, `else` is omitted here. */ @@ -6335,7 +6344,7 @@ int mbedtls_ssl_context_save( mbedtls_ssl_context *ssl, MBEDTLS_SSL_DEBUG_MSG( 1, ( "There is pending outgoing data" ) ); return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); } - /* Protocol must be DLTS, not TLS */ + /* Protocol must be DTLS, not TLS */ if( ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "Only DTLS is supported" ) ); @@ -6510,24 +6519,41 @@ int mbedtls_ssl_context_save( mbedtls_ssl_context *ssl, * Helper to get TLS 1.2 PRF from ciphersuite * (Duplicates bits of logic from ssl_set_handshake_prfs().) */ +#if defined(MBEDTLS_SHA256_C) || \ + (defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_SHA512_NO_SHA384)) typedef int (*tls_prf_fn)( const unsigned char *secret, size_t slen, const char *label, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen ); static tls_prf_fn ssl_tls12prf_from_cs( int ciphersuite_id ) { -#if defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_SHA512_NO_SHA384) const mbedtls_ssl_ciphersuite_t * const ciphersuite_info = mbedtls_ssl_ciphersuite_from_id( ciphersuite_id ); + if( ciphersuite_info == NULL ) + return( NULL ); + +#if defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_SHA512_NO_SHA384) if( ciphersuite_info->mac == MBEDTLS_MD_SHA384 ) return( tls_prf_sha384 ); -#else - (void) ciphersuite_id; + else +#endif +#if defined(MBEDTLS_SHA256_C) + { + if( ciphersuite_info->mac == MBEDTLS_MD_SHA256 ) + return( tls_prf_sha256 ); + } #endif - return( tls_prf_sha256 ); +#if !defined(MBEDTLS_SHA256_C) && \ + (!defined(MBEDTLS_SHA512_C) || defined(MBEDTLS_SHA512_NO_SHA384)) + (void) ciphersuite_info; +#endif + return( NULL ); } +#endif /* MBEDTLS_SHA256_C || + (MBEDTLS_SHA512_C && !MBEDTLS_SHA512_NO_SHA384) */ + /* * Deserialize context, see mbedtls_ssl_context_save() for format. * @@ -6543,6 +6569,7 @@ static int ssl_context_load( mbedtls_ssl_context *ssl, const unsigned char * const end = buf + len; size_t session_len; int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + tls_prf_fn prf_func = NULL; /* * The context should have been freshly setup or reset. @@ -6630,6 +6657,10 @@ static int ssl_context_load( mbedtls_ssl_context *ssl, ssl->transform_out = ssl->transform; ssl->transform_negotiate = NULL; + prf_func = ssl_tls12prf_from_cs( ssl->session->ciphersuite ); + if( prf_func == NULL ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + /* Read random bytes and populate structure */ if( (size_t)( end - p ) < sizeof( ssl->transform->randbytes ) ) return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); @@ -6648,7 +6679,7 @@ static int ssl_context_load( mbedtls_ssl_context *ssl, #if defined(MBEDTLS_ZLIB_SUPPORT) ssl->session->compression, #endif - ssl_tls12prf_from_cs( ssl->session->ciphersuite ), + prf_func, p, /* currently pointing to randbytes */ MBEDTLS_SSL_MINOR_VERSION_3, /* (D)TLS 1.2 is forced */ ssl->conf->endpoint, @@ -6921,7 +6952,7 @@ void mbedtls_ssl_free( mbedtls_ssl_context *ssl ) } /* - * Initialze mbedtls_ssl_config + * Initialize mbedtls_ssl_config */ void mbedtls_ssl_config_init( mbedtls_ssl_config *conf ) { diff --git a/thirdparty/mbedtls/library/ssl_tls13_keys.c b/thirdparty/mbedtls/library/ssl_tls13_keys.c index 3de6f03fb8..cc68773d3a 100644 --- a/thirdparty/mbedtls/library/ssl_tls13_keys.c +++ b/thirdparty/mbedtls/library/ssl_tls13_keys.c @@ -24,6 +24,7 @@ #include "mbedtls/hkdf.h" #include "mbedtls/ssl_internal.h" #include "ssl_tls13_keys.h" +#include "psa/crypto_sizes.h" #include <stdint.h> #include <string.h> @@ -31,6 +32,9 @@ #define MBEDTLS_SSL_TLS1_3_LABEL( name, string ) \ .name = string, +#define TLS1_3_EVOLVE_INPUT_SIZE ( PSA_HASH_MAX_SIZE > PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE ) ? \ + PSA_HASH_MAX_SIZE : PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE + struct mbedtls_ssl_tls1_3_labels_struct const mbedtls_ssl_tls1_3_labels = { /* This seems to work in C, despite the string literal being one @@ -292,8 +296,8 @@ int mbedtls_ssl_tls1_3_evolve_secret( { int ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; size_t hlen, ilen; - unsigned char tmp_secret[ MBEDTLS_MD_MAX_SIZE ] = { 0 }; - unsigned char tmp_input [ MBEDTLS_MD_MAX_SIZE ] = { 0 }; + unsigned char tmp_secret[ PSA_MAC_MAX_SIZE ] = { 0 }; + unsigned char tmp_input [ TLS1_3_EVOLVE_INPUT_SIZE ] = { 0 }; const mbedtls_md_info_t *md; md = mbedtls_md_info_from_type( hash_alg ); diff --git a/thirdparty/mbedtls/library/timing.c b/thirdparty/mbedtls/library/timing.c index 78bfa10cfb..151292e325 100644 --- a/thirdparty/mbedtls/library/timing.c +++ b/thirdparty/mbedtls/library/timing.c @@ -19,12 +19,7 @@ #include "common.h" -#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif #if defined(MBEDTLS_TIMING_C) @@ -269,7 +264,7 @@ static void TimerProc( void *TimerContext ) Sleep( alarmMs ); mbedtls_timing_alarmed = 1; /* _endthread will be called implicitly on return - * That ensures execution of thread funcition's epilogue */ + * That ensures execution of thread function's epilogue */ } void mbedtls_set_alarm( int seconds ) diff --git a/thirdparty/mbedtls/library/x509.c b/thirdparty/mbedtls/library/x509.c index 3997ebd1f3..54c8666d23 100644 --- a/thirdparty/mbedtls/library/x509.c +++ b/thirdparty/mbedtls/library/x509.c @@ -43,16 +43,7 @@ #include "mbedtls/pem.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#include <stdlib.h> -#define mbedtls_free free -#define mbedtls_calloc calloc -#define mbedtls_printf printf -#define mbedtls_snprintf snprintf -#endif #if defined(MBEDTLS_HAVE_TIME) #include "mbedtls/platform_time.h" @@ -198,7 +189,7 @@ static int x509_get_hash_alg( const mbedtls_x509_buf *alg, mbedtls_md_type_t *md * * RFC 4055 (which defines use of RSASSA-PSS in PKIX) states that the value * of trailerField MUST be 1, and PKCS#1 v2.2 doesn't even define any other - * option. Enfore this at parsing time. + * option. Enforce this at parsing time. */ int mbedtls_x509_get_rsassa_pss_params( const mbedtls_x509_buf *params, mbedtls_md_type_t *md_alg, mbedtls_md_type_t *mgf_md, @@ -424,6 +415,11 @@ static int x509_get_attr_type_value( unsigned char **p, * For the general case we still use a flat list, but we mark elements of the * same set so that they are "merged" together in the functions that consume * this list, eg mbedtls_x509_dn_gets(). + * + * On success, this function may allocate a linked list starting at cur->next + * that must later be free'd by the caller using mbedtls_free(). In error + * cases, this function frees all allocated memory internally and the caller + * has no freeing responsibilities. */ int mbedtls_x509_get_name( unsigned char **p, const unsigned char *end, mbedtls_x509_name *cur ) @@ -431,6 +427,8 @@ int mbedtls_x509_get_name( unsigned char **p, const unsigned char *end, int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; size_t set_len; const unsigned char *end_set; + mbedtls_x509_name *head = cur; + mbedtls_x509_name *prev, *allocated; /* don't use recursion, we'd risk stack overflow if not optimized */ while( 1 ) @@ -440,14 +438,17 @@ int mbedtls_x509_get_name( unsigned char **p, const unsigned char *end, */ if( ( ret = mbedtls_asn1_get_tag( p, end, &set_len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET ) ) != 0 ) - return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_X509_INVALID_NAME, ret ) ); + { + ret = MBEDTLS_ERROR_ADD( MBEDTLS_ERR_X509_INVALID_NAME, ret ); + goto error; + } end_set = *p + set_len; while( 1 ) { if( ( ret = x509_get_attr_type_value( p, end_set, cur ) ) != 0 ) - return( ret ); + goto error; if( *p == end_set ) break; @@ -458,7 +459,10 @@ int mbedtls_x509_get_name( unsigned char **p, const unsigned char *end, cur->next = mbedtls_calloc( 1, sizeof( mbedtls_x509_name ) ); if( cur->next == NULL ) - return( MBEDTLS_ERR_X509_ALLOC_FAILED ); + { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto error; + } cur = cur->next; } @@ -472,10 +476,30 @@ int mbedtls_x509_get_name( unsigned char **p, const unsigned char *end, cur->next = mbedtls_calloc( 1, sizeof( mbedtls_x509_name ) ); if( cur->next == NULL ) - return( MBEDTLS_ERR_X509_ALLOC_FAILED ); + { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto error; + } cur = cur->next; } + +error: + /* Skip the first element as we did not allocate it */ + allocated = head->next; + + while( allocated != NULL ) + { + prev = allocated; + allocated = allocated->next; + + mbedtls_platform_zeroize( prev, sizeof( *prev ) ); + mbedtls_free( prev ); + } + + mbedtls_platform_zeroize( head, sizeof( *head ) ); + + return( ret ); } static int x509_parse_int( unsigned char **p, size_t n, int *res ) diff --git a/thirdparty/mbedtls/library/x509_crl.c b/thirdparty/mbedtls/library/x509_crl.c index d2d8042029..b943a8d6da 100644 --- a/thirdparty/mbedtls/library/x509_crl.c +++ b/thirdparty/mbedtls/library/x509_crl.c @@ -1,5 +1,5 @@ /* - * X.509 Certidicate Revocation List (CRL) parsing + * X.509 Certificate Revocation List (CRL) parsing * * Copyright The Mbed TLS Contributors * SPDX-License-Identifier: Apache-2.0 @@ -42,15 +42,7 @@ #include "mbedtls/pem.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#include <stdio.h> -#define mbedtls_free free -#define mbedtls_calloc calloc -#define mbedtls_snprintf snprintf -#endif #if defined(MBEDTLS_HAVE_TIME) #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c index 96477e4c9d..4361f43ed0 100644 --- a/thirdparty/mbedtls/library/x509_crt.c +++ b/thirdparty/mbedtls/library/x509_crt.c @@ -49,15 +49,7 @@ #include "mbedtls/psa_util.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#include <stdlib.h> -#define mbedtls_free free -#define mbedtls_calloc calloc -#define mbedtls_snprintf snprintf -#endif #if defined(MBEDTLS_THREADING_C) #include "mbedtls/threading.h" @@ -90,6 +82,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> +#include <errno.h> #endif /* !_WIN32 || EFIX64 || EFI32 */ #endif @@ -1278,9 +1271,12 @@ static int x509_crt_parse_der_core( mbedtls_x509_crt *crt, } } + int extensions_allowed = 1; #if !defined(MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3) - if( crt->version == 3 ) + if( crt->version != 3 ) + extensions_allowed = 0; #endif + if( extensions_allowed ) { ret = x509_get_crt_ext( &p, end, crt, cb, p_ctx ); if( ret != 0 ) @@ -1668,8 +1664,22 @@ cleanup: } else if( stat( entry_name, &sb ) == -1 ) { - ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; - goto cleanup; + if( errno == ENOENT ) + { + /* Broken symbolic link - ignore this entry. + stat(2) will return this error for either (a) a dangling + symlink or (b) a missing file. + Given that we have just obtained the filename from readdir, + assume that it does exist and therefore treat this as a + dangling symlink. */ + continue; + } + else + { + /* Some other file error; report the error. */ + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; + goto cleanup; + } } if( !S_ISREG( sb.st_mode ) ) @@ -1798,6 +1808,7 @@ static int x509_info_subject_alt_name( char **buf, size_t *size, const char *prefix ) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i; size_t n = *size; char *p = *buf; const mbedtls_x509_sequence *cur = subject_alt_name; @@ -1850,18 +1861,11 @@ static int x509_info_subject_alt_name( char **buf, size_t *size, ret = mbedtls_snprintf( p, n, "\n%s hardware serial number : ", prefix ); MBEDTLS_X509_SAFE_SNPRINTF; - if( other_name->value.hardware_module_name.val.len >= n ) + for( i = 0; i < other_name->value.hardware_module_name.val.len; i++ ) { - *p = '\0'; - return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL ); + ret = mbedtls_snprintf( p, n, "%02X", other_name->value.hardware_module_name.val.p[i] ); + MBEDTLS_X509_SAFE_SNPRINTF; } - - memcpy( p, other_name->value.hardware_module_name.val.p, - other_name->value.hardware_module_name.val.len ); - p += other_name->value.hardware_module_name.val.len; - - n -= other_name->value.hardware_module_name.val.len; - }/* MBEDTLS_OID_ON_HW_MODULE_NAME */ } break; diff --git a/thirdparty/mbedtls/library/x509_csr.c b/thirdparty/mbedtls/library/x509_csr.c index e259410d07..1a22b77086 100644 --- a/thirdparty/mbedtls/library/x509_csr.c +++ b/thirdparty/mbedtls/library/x509_csr.c @@ -42,15 +42,7 @@ #include "mbedtls/pem.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#include <stdio.h> -#define mbedtls_free free -#define mbedtls_calloc calloc -#define mbedtls_snprintf snprintf -#endif #if defined(MBEDTLS_FS_IO) || defined(EFIX64) || defined(EFI32) #include <stdio.h> diff --git a/thirdparty/mbedtls/library/x509write_csr.c b/thirdparty/mbedtls/library/x509write_csr.c index afda950341..707dd001f0 100644 --- a/thirdparty/mbedtls/library/x509write_csr.c +++ b/thirdparty/mbedtls/library/x509write_csr.c @@ -44,13 +44,7 @@ #include "mbedtls/pem.h" #endif -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdlib.h> -#define mbedtls_calloc calloc -#define mbedtls_free free -#endif void mbedtls_x509write_csr_init( mbedtls_x509write_csr *ctx ) { diff --git a/thirdparty/mbedtls/library/xtea.c b/thirdparty/mbedtls/library/xtea.c index 77f6cb6f67..28e6972aa8 100644 --- a/thirdparty/mbedtls/library/xtea.c +++ b/thirdparty/mbedtls/library/xtea.c @@ -1,5 +1,5 @@ /* - * An 32-bit implementation of the XTEA algorithm + * A 32-bit implementation of the XTEA algorithm * * Copyright The Mbed TLS Contributors * SPDX-License-Identifier: Apache-2.0 @@ -26,14 +26,7 @@ #include <string.h> -#if defined(MBEDTLS_SELF_TEST) -#if defined(MBEDTLS_PLATFORM_C) #include "mbedtls/platform.h" -#else -#include <stdio.h> -#define mbedtls_printf printf -#endif /* MBEDTLS_PLATFORM_C */ -#endif /* MBEDTLS_SELF_TEST */ #if !defined(MBEDTLS_XTEA_ALT) diff --git a/thirdparty/mbedtls/patches/1453.diff b/thirdparty/mbedtls/patches/1453.diff index b1c9c43ed2..4a7ca1570b 100644 --- a/thirdparty/mbedtls/patches/1453.diff +++ b/thirdparty/mbedtls/patches/1453.diff @@ -1,8 +1,8 @@ -diff --git a/library/entropy_poll.c b/library/entropy_poll.c -index 4556f88a5..ba56b70f7 100644 ---- a/library/entropy_poll.c -+++ b/library/entropy_poll.c -@@ -61,28 +61,43 @@ +diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c +index a858c1892b..69ac29e4f7 100644 +--- a/thirdparty/mbedtls/library/entropy_poll.c ++++ b/thirdparty/mbedtls/library/entropy_poll.c +@@ -54,28 +54,43 @@ #define _WIN32_WINNT 0x0400 #endif #include <windows.h> @@ -53,12 +53,12 @@ index 4556f88a5..ba56b70f7 100644 *olen = len; return( 0 ); -diff --git a/library/x509_crt.c b/library/x509_crt.c -index 76558342e..35a134950 100644 ---- a/library/x509_crt.c -+++ b/library/x509_crt.c -@@ -65,6 +65,19 @@ - +diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c +index def1414eca..4361f43ed0 100644 +--- a/thirdparty/mbedtls/library/x509_crt.c ++++ b/thirdparty/mbedtls/library/x509_crt.c +@@ -58,6 +58,19 @@ + #if defined(MBEDTLS_HAVE_TIME) #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) #include <windows.h> +#if defined(_MSC_VER) && _MSC_VER <= 1600 @@ -77,7 +77,7 @@ index 76558342e..35a134950 100644 #else #include <time.h> #endif -@@ -1278,6 +1291,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) +@@ -1549,6 +1562,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) char filename[MAX_PATH]; char *p; size_t len = strlen( path ); @@ -85,7 +85,7 @@ index 76558342e..35a134950 100644 WIN32_FIND_DATAW file_data; HANDLE hFind; -@@ -1292,7 +1306,18 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) +@@ -1563,7 +1577,18 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) p = filename + len; filename[len++] = '*'; @@ -105,7 +105,7 @@ index 76558342e..35a134950 100644 MAX_PATH - 3 ); if( w_ret == 0 ) return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); -@@ -1309,8 +1334,11 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) +@@ -1580,8 +1605,11 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) continue; diff --git a/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff b/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff new file mode 100644 index 0000000000..c7c5a7e282 --- /dev/null +++ b/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff @@ -0,0 +1,16 @@ +diff --git a/thirdparty/mbedtls/library/timing.c b/thirdparty/mbedtls/library/timing.c +index 6c14a4fd01..151292e325 100644 +--- a/thirdparty/mbedtls/library/timing.c ++++ b/thirdparty/mbedtls/library/timing.c +@@ -190,8 +190,10 @@ unsigned long mbedtls_timing_hardclock( void ) + #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && + __GNUC__ && __ia64__ */ + +-#if !defined(HAVE_HARDCLOCK) && defined(_MSC_VER) && \ ++// -- GODOT start -- ++#if !defined(HAVE_HARDCLOCK) && defined(_WIN32) && \ + !defined(EFIX64) && !defined(EFI32) ++// -- GODOT end -- + + #define HAVE_HARDCLOCK + diff --git a/thirdparty/pcre2/src/config.h b/thirdparty/pcre2/src/config.h index 76dc5868b1..5548d18eb2 100644 --- a/thirdparty/pcre2/src/config.h +++ b/thirdparty/pcre2/src/config.h @@ -236,7 +236,7 @@ sure both macros are undefined; an emulation function will then be used. */ #define PACKAGE_NAME "PCRE2" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "PCRE2 10.40" +#define PACKAGE_STRING "PCRE2 10.42" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "pcre2" @@ -245,7 +245,7 @@ sure both macros are undefined; an emulation function will then be used. */ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "10.40" +#define PACKAGE_VERSION "10.42" /* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested parentheses (of any kind) in a pattern. This limits the amount of system @@ -438,7 +438,13 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* Version number of package */ -#define VERSION "10.40" +#define VERSION "10.42" + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ diff --git a/thirdparty/pcre2/src/pcre2.h b/thirdparty/pcre2/src/pcre2.h index 8adcede57c..1cbecd0e86 100644 --- a/thirdparty/pcre2/src/pcre2.h +++ b/thirdparty/pcre2/src/pcre2.h @@ -42,9 +42,9 @@ POSSIBILITY OF SUCH DAMAGE. /* The current PCRE version information. */ #define PCRE2_MAJOR 10 -#define PCRE2_MINOR 40 +#define PCRE2_MINOR 42 #define PCRE2_PRERELEASE -#define PCRE2_DATE 2022-04-14 +#define PCRE2_DATE 2022-12-11 /* When an application links to a PCRE DLL in Windows, the symbols that are imported have to be identified as such. When building PCRE2, the appropriate @@ -572,19 +572,19 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION pcre2_config(uint32_t, void *); /* Functions for manipulating contexts. */ #define PCRE2_GENERAL_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ - *pcre2_general_context_copy(pcre2_general_context *); \ -PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ - *pcre2_general_context_create(void *(*)(PCRE2_SIZE, void *), \ +PCRE2_EXP_DECL pcre2_general_context *PCRE2_CALL_CONVENTION \ + pcre2_general_context_copy(pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_general_context *PCRE2_CALL_CONVENTION \ + pcre2_general_context_create(void *(*)(PCRE2_SIZE, void *), \ void (*)(void *, void *), void *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_general_context_free(pcre2_general_context *); #define PCRE2_COMPILE_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL pcre2_compile_context PCRE2_CALL_CONVENTION \ - *pcre2_compile_context_copy(pcre2_compile_context *); \ -PCRE2_EXP_DECL pcre2_compile_context PCRE2_CALL_CONVENTION \ - *pcre2_compile_context_create(pcre2_general_context *);\ +PCRE2_EXP_DECL pcre2_compile_context *PCRE2_CALL_CONVENTION \ + pcre2_compile_context_copy(pcre2_compile_context *); \ +PCRE2_EXP_DECL pcre2_compile_context *PCRE2_CALL_CONVENTION \ + pcre2_compile_context_create(pcre2_general_context *);\ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_compile_context_free(pcre2_compile_context *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ @@ -604,10 +604,10 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ int (*)(uint32_t, void *), void *); #define PCRE2_MATCH_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL pcre2_match_context PCRE2_CALL_CONVENTION \ - *pcre2_match_context_copy(pcre2_match_context *); \ -PCRE2_EXP_DECL pcre2_match_context PCRE2_CALL_CONVENTION \ - *pcre2_match_context_create(pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_match_context *PCRE2_CALL_CONVENTION \ + pcre2_match_context_copy(pcre2_match_context *); \ +PCRE2_EXP_DECL pcre2_match_context *PCRE2_CALL_CONVENTION \ + pcre2_match_context_create(pcre2_general_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_match_context_free(pcre2_match_context *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ @@ -631,10 +631,10 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ void *(*)(PCRE2_SIZE, void *), void (*)(void *, void *), void *); #define PCRE2_CONVERT_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \ - *pcre2_convert_context_copy(pcre2_convert_context *); \ -PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \ - *pcre2_convert_context_create(pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_convert_context *PCRE2_CALL_CONVENTION \ + pcre2_convert_context_copy(pcre2_convert_context *); \ +PCRE2_EXP_DECL pcre2_convert_context *PCRE2_CALL_CONVENTION \ + pcre2_convert_context_create(pcre2_general_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_convert_context_free(pcre2_convert_context *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ @@ -646,15 +646,15 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ /* Functions concerned with compiling a pattern to PCRE internal code. */ #define PCRE2_COMPILE_FUNCTIONS \ -PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ - *pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, \ +PCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \ + pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, \ pcre2_compile_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_code_free(pcre2_code *); \ -PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ - *pcre2_code_copy(const pcre2_code *); \ -PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ - *pcre2_code_copy_with_tables(const pcre2_code *); +PCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \ + pcre2_code_copy(const pcre2_code *); \ +PCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \ + pcre2_code_copy_with_tables(const pcre2_code *); /* Functions that give information about a compiled pattern. */ @@ -670,10 +670,10 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ /* Functions for running a match and inspecting the result. */ #define PCRE2_MATCH_FUNCTIONS \ -PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ - *pcre2_match_data_create(uint32_t, pcre2_general_context *); \ -PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ - *pcre2_match_data_create_from_pattern(const pcre2_code *, \ +PCRE2_EXP_DECL pcre2_match_data *PCRE2_CALL_CONVENTION \ + pcre2_match_data_create(uint32_t, pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_match_data *PCRE2_CALL_CONVENTION \ + pcre2_match_data_create_from_pattern(const pcre2_code *, \ pcre2_general_context *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ @@ -689,8 +689,8 @@ PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ pcre2_get_match_data_size(pcre2_match_data *); \ PCRE2_EXP_DECL uint32_t PCRE2_CALL_CONVENTION \ pcre2_get_ovector_count(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ - *pcre2_get_ovector_pointer(pcre2_match_data *); \ +PCRE2_EXP_DECL PCRE2_SIZE *PCRE2_CALL_CONVENTION \ + pcre2_get_ovector_pointer(pcre2_match_data *); \ PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ pcre2_get_startchar(pcre2_match_data *); @@ -770,8 +770,8 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ uint32_t, pcre2_match_data *, pcre2_match_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_jit_free_unused_memory(pcre2_general_context *); \ -PCRE2_EXP_DECL pcre2_jit_stack PCRE2_CALL_CONVENTION \ - *pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_jit_stack *PCRE2_CALL_CONVENTION \ + pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, pcre2_general_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_jit_stack_assign(pcre2_match_context *, pcre2_jit_callback, void *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ @@ -783,8 +783,8 @@ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ #define PCRE2_OTHER_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \ -PCRE2_EXP_DECL const uint8_t PCRE2_CALL_CONVENTION \ - *pcre2_maketables(pcre2_general_context *); \ +PCRE2_EXP_DECL const uint8_t *PCRE2_CALL_CONVENTION \ + pcre2_maketables(pcre2_general_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_maketables_free(pcre2_general_context *, const uint8_t *); diff --git a/thirdparty/pcre2/src/pcre2_compile.c b/thirdparty/pcre2/src/pcre2_compile.c index de259c9c40..edf7e82e6e 100644 --- a/thirdparty/pcre2/src/pcre2_compile.c +++ b/thirdparty/pcre2/src/pcre2_compile.c @@ -1266,8 +1266,10 @@ PCRE2_SIZE* ref_count; if (code != NULL) { +#ifdef SUPPORT_JIT if (code->executable_jit != NULL) PRIV(jit_free)(code->executable_jit, &code->memctl); +#endif if ((code->flags & PCRE2_DEREF_TABLES) != 0) { @@ -2687,7 +2689,7 @@ if ((options & PCRE2_EXTENDED_MORE) != 0) options |= PCRE2_EXTENDED; while (ptr < ptrend) { int prev_expect_cond_assert; - uint32_t min_repeat, max_repeat; + uint32_t min_repeat = 0, max_repeat = 0; uint32_t set, unset, *optset; uint32_t terminator; uint32_t prev_meta_quantifier; @@ -8552,7 +8554,7 @@ do { op == OP_SCBRA || op == OP_SCBRAPOS) { int n = GET2(scode, 1+LINK_SIZE); - int new_map = bracket_map | ((n < 32)? (1u << n) : 1); + unsigned int new_map = bracket_map | ((n < 32)? (1u << n) : 1); if (!is_startline(scode, new_map, cb, atomcount, inassert)) return FALSE; } @@ -10620,4 +10622,10 @@ re = NULL; goto EXIT; } +/* These #undefs are here to enable unity builds with CMake. */ + +#undef NLBLOCK /* Block containing newline information */ +#undef PSSTART /* Field containing processed string start */ +#undef PSEND /* Field containing processed string end */ + /* End of pcre2_compile.c */ diff --git a/thirdparty/pcre2/src/pcre2_context.c b/thirdparty/pcre2/src/pcre2_context.c index f904a494a0..8e05ede50c 100644 --- a/thirdparty/pcre2/src/pcre2_context.c +++ b/thirdparty/pcre2/src/pcre2_context.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016-2018 University of Cambridge + New API code Copyright (c) 2016-2022 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -443,8 +443,11 @@ mcontext->offset_limit = limit; return 0; } -/* This function became obsolete at release 10.30. It is kept as a synonym for -backwards compatibility. */ +/* These functions became obsolete at release 10.30. The first is kept as a +synonym for backwards compatibility. The second now does nothing. Exclude both +from coverage reports. */ + +/* LCOV_EXCL_START */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_recursion_limit(pcre2_match_context *mcontext, uint32_t limit) @@ -464,6 +467,9 @@ pcre2_set_recursion_memory_management(pcre2_match_context *mcontext, return 0; } +/* LCOV_EXCL_STOP */ + + /* ------------ Convert context ------------ */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION diff --git a/thirdparty/pcre2/src/pcre2_convert.c b/thirdparty/pcre2/src/pcre2_convert.c index d45b6fee97..36466e4b91 100644 --- a/thirdparty/pcre2/src/pcre2_convert.c +++ b/thirdparty/pcre2/src/pcre2_convert.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016-2018 University of Cambridge + New API code Copyright (c) 2016-2022 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -65,9 +65,8 @@ POSSIBILITY OF SUCH DAMAGE. #define STR_QUERY_s STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_s STR_RIGHT_PARENTHESIS #define STR_STAR_NUL STR_LEFT_PARENTHESIS STR_ASTERISK STR_N STR_U STR_L STR_RIGHT_PARENTHESIS -/* States for range and POSIX processing */ +/* States for POSIX processing */ -enum { RANGE_NOT_STARTED, RANGE_STARTING, RANGE_STARTED }; enum { POSIX_START_REGEX, POSIX_ANCHORED, POSIX_NOT_BRACKET, POSIX_CLASS_NOT_STARTED, POSIX_CLASS_STARTING, POSIX_CLASS_STARTED }; diff --git a/thirdparty/pcre2/src/pcre2_dfa_match.c b/thirdparty/pcre2/src/pcre2_dfa_match.c index d29130f2d0..b16e594cc0 100644 --- a/thirdparty/pcre2/src/pcre2_dfa_match.c +++ b/thirdparty/pcre2/src/pcre2_dfa_match.c @@ -350,7 +350,7 @@ Returns: the return from the callout */ static int -do_callout(PCRE2_SPTR code, PCRE2_SIZE *offsets, PCRE2_SPTR current_subject, +do_callout_dfa(PCRE2_SPTR code, PCRE2_SIZE *offsets, PCRE2_SPTR current_subject, PCRE2_SPTR ptr, dfa_match_block *mb, PCRE2_SIZE extracode, PCRE2_SIZE *lengthptr) { @@ -2799,7 +2799,7 @@ for (;;) || code[LINK_SIZE + 1] == OP_CALLOUT_STR) { PCRE2_SIZE callout_length; - rrc = do_callout(code, offsets, current_subject, ptr, mb, + rrc = do_callout_dfa(code, offsets, current_subject, ptr, mb, 1 + LINK_SIZE, &callout_length); if (rrc < 0) return rrc; /* Abandon */ if (rrc > 0) break; /* Fail this thread */ @@ -3196,7 +3196,7 @@ for (;;) case OP_CALLOUT_STR: { PCRE2_SIZE callout_length; - rrc = do_callout(code, offsets, current_subject, ptr, mb, 0, + rrc = do_callout_dfa(code, offsets, current_subject, ptr, mb, 0, &callout_length); if (rrc < 0) return rrc; /* Abandon */ if (rrc == 0) @@ -4057,4 +4057,10 @@ while (rws->next != NULL) return rc; } +/* These #undefs are here to enable unity builds with CMake. */ + +#undef NLBLOCK /* Block containing newline information */ +#undef PSSTART /* Field containing processed string start */ +#undef PSEND /* Field containing processed string end */ + /* End of pcre2_dfa_match.c */ diff --git a/thirdparty/pcre2/src/pcre2_internal.h b/thirdparty/pcre2/src/pcre2_internal.h index fe7a0e005a..92dd3138d4 100644 --- a/thirdparty/pcre2/src/pcre2_internal.h +++ b/thirdparty/pcre2/src/pcre2_internal.h @@ -220,18 +220,17 @@ not rely on this. */ #define COMPILE_ERROR_BASE 100 -/* The initial frames vector for remembering backtracking points in -pcre2_match() is allocated on the system stack, of this size (bytes). The size -must be a multiple of sizeof(PCRE2_SPTR) in all environments, so making it a -multiple of 8 is best. Typical frame sizes are a few hundred bytes (it depends -on the number of capturing parentheses) so 20KiB handles quite a few frames. A -larger vector on the heap is obtained for patterns that need more frames. The -maximum size of this can be limited. */ +/* The initial frames vector for remembering pcre2_match() backtracking points +is allocated on the heap, of this size (bytes) or ten times the frame size if +larger, unless the heap limit is smaller. Typical frame sizes are a few hundred +bytes (it depends on the number of capturing parentheses) so 20KiB handles +quite a few frames. A larger vector on the heap is obtained for matches that +need more frames, subject to the heap limit. */ #define START_FRAMES_SIZE 20480 -/* Similarly, for DFA matching, an initial internal workspace vector is -allocated on the stack. */ +/* For DFA matching, an initial internal workspace vector is allocated on the +stack. The heap is used only if this turns out to be too small. */ #define DFA_START_RWS_SIZE 30720 diff --git a/thirdparty/pcre2/src/pcre2_intmodedep.h b/thirdparty/pcre2/src/pcre2_intmodedep.h index f8a3d25de6..390e737a6e 100644 --- a/thirdparty/pcre2/src/pcre2_intmodedep.h +++ b/thirdparty/pcre2/src/pcre2_intmodedep.h @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016-2018 University of Cambridge + New API code Copyright (c) 2016-2022 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -649,19 +649,23 @@ the size varies from call to call. As the maximum number of capturing subpatterns is 65535 we must allow for 65536 strings to include the overall match. (See also the heapframe structure below.) */ +struct heapframe; /* Forward reference */ + typedef struct pcre2_real_match_data { - pcre2_memctl memctl; - const pcre2_real_code *code; /* The pattern used for the match */ - PCRE2_SPTR subject; /* The subject that was matched */ - PCRE2_SPTR mark; /* Pointer to last mark */ - PCRE2_SIZE leftchar; /* Offset to leftmost code unit */ - PCRE2_SIZE rightchar; /* Offset to rightmost code unit */ - PCRE2_SIZE startchar; /* Offset to starting code unit */ - uint8_t matchedby; /* Type of match (normal, JIT, DFA) */ - uint8_t flags; /* Various flags */ - uint16_t oveccount; /* Number of pairs */ - int rc; /* The return code from the match */ - PCRE2_SIZE ovector[131072]; /* Must be last in the structure */ + pcre2_memctl memctl; /* Memory control fields */ + const pcre2_real_code *code; /* The pattern used for the match */ + PCRE2_SPTR subject; /* The subject that was matched */ + PCRE2_SPTR mark; /* Pointer to last mark */ + struct heapframe *heapframes; /* Backtracking frames heap memory */ + PCRE2_SIZE heapframes_size; /* Malloc-ed size */ + PCRE2_SIZE leftchar; /* Offset to leftmost code unit */ + PCRE2_SIZE rightchar; /* Offset to rightmost code unit */ + PCRE2_SIZE startchar; /* Offset to starting code unit */ + uint8_t matchedby; /* Type of match (normal, JIT, DFA) */ + uint8_t flags; /* Various flags */ + uint16_t oveccount; /* Number of pairs */ + int rc; /* The return code from the match */ + PCRE2_SIZE ovector[131072]; /* Must be last in the structure */ } pcre2_real_match_data; @@ -854,10 +858,6 @@ doing traditional NFA matching (pcre2_match() and friends). */ typedef struct match_block { pcre2_memctl memctl; /* For general use */ - PCRE2_SIZE frame_vector_size; /* Size of a backtracking frame */ - heapframe *match_frames; /* Points to vector of frames */ - heapframe *match_frames_top; /* Points after the end of the vector */ - heapframe *stack_frames; /* The original vector on the stack */ PCRE2_SIZE heap_limit; /* As it says */ uint32_t match_limit; /* As it says */ uint32_t match_limit_depth; /* As it says */ diff --git a/thirdparty/pcre2/src/pcre2_jit_compile.c b/thirdparty/pcre2/src/pcre2_jit_compile.c index d726c3ca04..0afd27c5ee 100644 --- a/thirdparty/pcre2/src/pcre2_jit_compile.c +++ b/thirdparty/pcre2/src/pcre2_jit_compile.c @@ -542,7 +542,7 @@ typedef struct compare_context { #undef CMP /* Used for accessing the elements of the stack. */ -#define STACK(i) ((i) * (int)sizeof(sljit_sw)) +#define STACK(i) ((i) * SSIZE_OF(sw)) #ifdef SLJIT_PREF_SHIFT_REG #if SLJIT_PREF_SHIFT_REG == SLJIT_R2 @@ -590,8 +590,8 @@ to characters. The vector data is divided into two groups: the first group contains the start / end character pointers, and the second is the start pointers when the end of the capturing group has not yet reached. */ #define OVECTOR_START (common->ovector_start) -#define OVECTOR(i) (OVECTOR_START + (i) * (sljit_sw)sizeof(sljit_sw)) -#define OVECTOR_PRIV(i) (common->cbra_ptr + (i) * (sljit_sw)sizeof(sljit_sw)) +#define OVECTOR(i) (OVECTOR_START + (i) * SSIZE_OF(sw)) +#define OVECTOR_PRIV(i) (common->cbra_ptr + (i) * SSIZE_OF(sw)) #define PRIVATE_DATA(cc) (common->private_data_ptrs[(cc) - common->start]) #if PCRE2_CODE_UNIT_WIDTH == 8 @@ -2151,9 +2151,9 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0)); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); setsom_found = TRUE; } cc += 1; @@ -2168,9 +2168,9 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); setmark_found = TRUE; } cc += 1 + 2 + cc[1]; @@ -2181,27 +2181,27 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0)); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); setsom_found = TRUE; } if (common->mark_ptr != 0 && !setmark_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); setmark_found = TRUE; } if (common->capture_last_ptr != 0 && !capture_last_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); capture_last_found = TRUE; } cc += 1 + LINK_SIZE; @@ -2215,20 +2215,20 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); capture_last_found = TRUE; } offset = (GET2(cc, 1 + LINK_SIZE)) << 1; OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, OVECTOR(offset)); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP2, 0); - stackpos -= (int)sizeof(sljit_sw); + stackpos -= SSIZE_OF(sw); cc += 1 + LINK_SIZE + IMM2_SIZE; break; @@ -3144,7 +3144,7 @@ static SLJIT_INLINE void allocate_stack(compiler_common *common, int size) DEFINE_COMPILER; SLJIT_ASSERT(size > 0); -OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); +OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * SSIZE_OF(sw)); #ifdef DESTROY_REGISTERS OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345); OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); @@ -3160,7 +3160,7 @@ static SLJIT_INLINE void free_stack(compiler_common *common, int size) DEFINE_COMPILER; SLJIT_ASSERT(size > 0); -OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); +OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * SSIZE_OF(sw)); } static sljit_uw * allocate_read_only_data(compiler_common *common, sljit_uw size) @@ -3200,12 +3200,12 @@ if (length < 8) } else { - if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)) == SLJIT_SUCCESS) + if (sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)) == SLJIT_SUCCESS) { GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START); OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1); loop = LABEL(); - sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)); + sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); } @@ -3261,8 +3261,8 @@ OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, size - uncleared_size); loop = LABEL(); OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), 0, src, 0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); -OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), -2 * (sljit_sw)sizeof(sljit_sw), src, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), -1 * (sljit_sw)sizeof(sljit_sw), src, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), -2 * SSIZE_OF(sw), src, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), -1 * SSIZE_OF(sw), src, 0); CMPTO(SLJIT_LESS, TMP1, 0, TMP2, 0, loop); if (uncleared_size >= sizeof(sljit_sw)) @@ -3289,12 +3289,12 @@ if (length < 8) } else { - if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)) == SLJIT_SUCCESS) + if (sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)) == SLJIT_SUCCESS) { GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw)); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); loop = LABEL(); - sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); + sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); } @@ -3386,7 +3386,7 @@ else OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, SLJIT_OFFSETOF(pcre2_match_data, ovector) - sizeof(PCRE2_SIZE)); } -has_pre = sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)) == SLJIT_SUCCESS; +has_pre = sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)) == SLJIT_SUCCESS; GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START - (has_pre ? sizeof(sljit_sw) : 0)); OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? SLJIT_R0 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin)); @@ -3394,7 +3394,7 @@ OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? SLJIT_R0 : ARGUME loop = LABEL(); if (has_pre) - sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)); + sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)); else { OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0); @@ -3417,14 +3417,14 @@ JUMPTO(SLJIT_NOT_ZERO, loop); /* Calculate the return value, which is the maximum ovector value. */ if (topbracket > 1) { - if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))) == SLJIT_SUCCESS) + if (sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * SSIZE_OF(sw))) == SLJIT_SUCCESS) { GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1); /* OVECTOR(0) is never equal to SLJIT_S2. */ loop = LABEL(); - sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))); + sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * SSIZE_OF(sw))); OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0); @@ -3437,7 +3437,7 @@ if (topbracket > 1) /* OVECTOR(0) is never equal to SLJIT_S2. */ loop = LABEL(); OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), 0); - OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, 2 * (sljit_sw)sizeof(sljit_sw)); + OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, 2 * SSIZE_OF(sw)); OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0); @@ -4652,8 +4652,8 @@ if (common->nltype != NLTYPE_ANY) /* All newlines are ascii, just skip intermediate octets. */ jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); loop = LABEL(); - if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, TMP2, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)) == SLJIT_SUCCESS) - sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, TMP2, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, TMP2, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)) == SLJIT_SUCCESS) + sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, TMP2, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); else { OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); @@ -5886,7 +5886,7 @@ static BOOL check_fast_forward_char_pair_simd(compiler_common *common, fast_forw while (j < i) { b_pri = chars[j].last_count; - if (b_pri > 2 && a_pri + b_pri >= max_pri) + if (b_pri > 2 && (sljit_u32)a_pri + (sljit_u32)b_pri >= max_pri) { b1 = chars[j].chars[0]; b2 = chars[j].chars[1]; @@ -6572,21 +6572,21 @@ GET_LOCAL_BASE(TMP1, 0, 0); /* Drop frames until we reach STACK_TOP. */ mainloop = LABEL(); -OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), -sizeof(sljit_sw)); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), -SSIZE_OF(sw)); jump = CMP(SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); if (HAS_VIRTUAL_REGISTERS) { - OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); - OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(STACK_TOP), -(3 * sizeof(sljit_sw))); - OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw))); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(STACK_TOP), -(3 * SSIZE_OF(sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * SSIZE_OF(sw)); } else { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); - OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(3 * sizeof(sljit_sw))); - OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw))); + OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(3 * SSIZE_OF(sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * SSIZE_OF(sw)); OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0); GET_LOCAL_BASE(TMP1, 0, 0); OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP3, 0); @@ -6603,13 +6603,13 @@ OP2(SLJIT_SUB, TMP2, 0, SLJIT_IMM, 0, TMP2, 0); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); if (HAS_VIRTUAL_REGISTERS) { - OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); - OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * SSIZE_OF(sw)); } else { - OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); - OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * SSIZE_OF(sw)); OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP3, 0); } JUMPTO(SLJIT_JUMP, mainloop); @@ -7159,11 +7159,11 @@ if (char1_reg == STR_END) OP1(SLJIT_MOV, RETURN_ADDR, 0, char2_reg, 0); } -if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) +if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) { label = LABEL(); - sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); - sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_NOT_ZERO, label); @@ -7171,14 +7171,14 @@ if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_ JUMPHERE(jump); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); } -else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) +else if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); label = LABEL(); - sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); - sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_NOT_ZERO, label); @@ -7232,9 +7232,9 @@ else lcc_table = TMP3; } -if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) +if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) opt_type = 1; -else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) +else if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) opt_type = 2; sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); @@ -7253,8 +7253,8 @@ OP1(SLJIT_MOV, lcc_table, 0, SLJIT_IMM, common->lcc); if (opt_type == 1) { label = LABEL(); - sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); - sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); } else if (opt_type == 2) { @@ -7262,8 +7262,8 @@ else if (opt_type == 2) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); label = LABEL(); - sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); - sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); } else { @@ -9689,7 +9689,7 @@ BACKTRACK_AS(recurse_backtrack)->matchingpath = LABEL(); return cc + 1 + LINK_SIZE; } -static sljit_s32 SLJIT_FUNC do_callout(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector) +static sljit_s32 SLJIT_FUNC do_callout_jit(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector) { PCRE2_SPTR begin; PCRE2_SIZE *ovector; @@ -9756,7 +9756,7 @@ unsigned int callout_length = (*cc == OP_CALLOUT) sljit_sw value1; sljit_sw value2; sljit_sw value3; -sljit_uw callout_arg_size = (common->re->top_bracket + 1) * 2 * sizeof(sljit_sw); +sljit_uw callout_arg_size = (common->re->top_bracket + 1) * 2 * SSIZE_OF(sw); PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); @@ -9806,7 +9806,7 @@ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0); /* SLJIT_R0 = arguments */ OP1(SLJIT_MOV, SLJIT_R1, 0, STACK_TOP, 0); GET_LOCAL_BASE(SLJIT_R2, 0, OVECTOR_START); -sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3(32, W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(do_callout)); +sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3(32, W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(do_callout_jit)); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); free_stack(common, callout_arg_size); @@ -11451,7 +11451,7 @@ struct sljit_label *label; int private_data_ptr = PRIVATE_DATA(cc); int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_SP); int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr; -int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + (int)sizeof(sljit_sw); +int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + SSIZE_OF(sw); int tmp_base, tmp_offset; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 BOOL use_tmp; @@ -11517,19 +11517,19 @@ if (exact > 1) } } else if (exact == 1) - { compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE); - if (early_fail_type == type_fail_range) - { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr); - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + (int)sizeof(sljit_sw)); - OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, TMP2, 0); - OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, TMP2, 0); - add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, TMP2, 0, TMP1, 0)); +if (early_fail_type == type_fail_range) + { + /* Range end first, followed by range start. */ + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + SSIZE_OF(sw)); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, TMP2, 0); + OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, TMP2, 0); + add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, TMP2, 0, TMP1, 0)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + (int)sizeof(sljit_sw), STR_PTR, 0); - } + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + SSIZE_OF(sw), STR_PTR, 0); } switch(opcode) @@ -12428,7 +12428,7 @@ PCRE2_SPTR end; int private_data_ptr = PRIVATE_DATA(cc); int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_SP); int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr; -int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + (int)sizeof(sljit_sw); +int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + SSIZE_OF(sw); cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &exact, &end); @@ -14148,7 +14148,7 @@ quit_label = common->quit_label; if (common->currententry != NULL) { /* A free bit for each private data. */ - common->recurse_bitset_size = ((private_data_size / (int)sizeof(sljit_sw)) + 7) >> 3; + common->recurse_bitset_size = ((private_data_size / SSIZE_OF(sw)) + 7) >> 3; SLJIT_ASSERT(common->recurse_bitset_size > 0); common->recurse_bitset = (sljit_u8*)SLJIT_MALLOC(common->recurse_bitset_size, allocator_data);; @@ -14384,7 +14384,7 @@ pcre2_jit_compile(pcre2_code *code, uint32_t options) pcre2_real_code *re = (pcre2_real_code *)code; #ifdef SUPPORT_JIT executable_functions *functions; -static int executable_allocator_is_working = 0; +static int executable_allocator_is_working = -1; #endif if (code == NULL) @@ -14447,23 +14447,21 @@ return PCRE2_ERROR_JIT_BADOPTION; if ((re->flags & PCRE2_NOJIT) != 0) return 0; -if (executable_allocator_is_working == 0) +if (executable_allocator_is_working == -1) { /* Checks whether the executable allocator is working. This check might run multiple times in multi-threaded environments, but the result should not be affected by it. */ void *ptr = SLJIT_MALLOC_EXEC(32, NULL); - - executable_allocator_is_working = -1; - if (ptr != NULL) { SLJIT_FREE_EXEC(((sljit_u8*)(ptr)) + SLJIT_EXEC_OFFSET(ptr), NULL); executable_allocator_is_working = 1; } + else executable_allocator_is_working = 0; } -if (executable_allocator_is_working < 0) +if (!executable_allocator_is_working) return PCRE2_ERROR_NOMEMORY; if ((re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0) diff --git a/thirdparty/pcre2/src/pcre2_jit_misc.c b/thirdparty/pcre2/src/pcre2_jit_misc.c index e57afad065..bb6a5589cb 100644 --- a/thirdparty/pcre2/src/pcre2_jit_misc.c +++ b/thirdparty/pcre2/src/pcre2_jit_misc.c @@ -110,8 +110,10 @@ pcre2_jit_free_unused_memory(pcre2_general_context *gcontext) (void)gcontext; /* Suppress warning */ #else /* SUPPORT_JIT */ SLJIT_UNUSED_ARG(gcontext); +#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) sljit_free_unused_memory_exec(); -#endif /* SUPPORT_JIT */ +#endif /* SLJIT_EXECUTABLE_ALLOCATOR */ +#endif /* SUPPORT_JIT */ } diff --git a/thirdparty/pcre2/src/pcre2_jit_neon_inc.h b/thirdparty/pcre2/src/pcre2_jit_neon_inc.h index 150da29eba..165602edc0 100644 --- a/thirdparty/pcre2/src/pcre2_jit_neon_inc.h +++ b/thirdparty/pcre2/src/pcre2_jit_neon_inc.h @@ -183,6 +183,8 @@ restart:; #endif #if defined(FFCPS) +if (str_ptr >= str_end) + return NULL; sljit_u8 *p1 = str_ptr - diff; #endif sljit_s32 align_offset = ((uint64_t)str_ptr & 0xf); @@ -327,7 +329,7 @@ match:; return NULL; #if defined(FF_UTF) - if (utf_continue(str_ptr + IN_UCHARS(-offs1))) + if (utf_continue((PCRE2_SPTR)str_ptr - offs1)) { /* Not a match. */ str_ptr += IN_UCHARS(1); diff --git a/thirdparty/pcre2/src/pcre2_jit_simd_inc.h b/thirdparty/pcre2/src/pcre2_jit_simd_inc.h index d99cfc5ce4..1a5ce4ed09 100644 --- a/thirdparty/pcre2/src/pcre2_jit_simd_inc.h +++ b/thirdparty/pcre2/src/pcre2_jit_simd_inc.h @@ -776,7 +776,7 @@ typedef union { } int_char; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 -static SLJIT_INLINE int utf_continue(sljit_u8 *s) +static SLJIT_INLINE int utf_continue(PCRE2_SPTR s) { #if PCRE2_CODE_UNIT_WIDTH == 8 return (*s & 0xc0) == 0x80; diff --git a/thirdparty/pcre2/src/pcre2_match.c b/thirdparty/pcre2/src/pcre2_match.c index 6354e1bb9e..168b9fad01 100644 --- a/thirdparty/pcre2/src/pcre2_match.c +++ b/thirdparty/pcre2/src/pcre2_match.c @@ -204,6 +204,7 @@ Arguments: P a previous frame of interest frame_size the frame size mb points to the match block + match_data points to the match data block s identification text Returns: nothing @@ -211,7 +212,7 @@ Returns: nothing static void display_frames(FILE *f, heapframe *F, heapframe *P, PCRE2_SIZE frame_size, - match_block *mb, const char *s, ...) + match_block *mb, pcre2_match_data *match_data, const char *s, ...) { uint32_t i; heapframe *Q; @@ -223,10 +224,10 @@ vfprintf(f, s, ap); va_end(ap); if (P != NULL) fprintf(f, " P=%lu", - ((char *)P - (char *)(mb->match_frames))/frame_size); + ((char *)P - (char *)(match_data->heapframes))/frame_size); fprintf(f, "\n"); -for (i = 0, Q = mb->match_frames; +for (i = 0, Q = match_data->heapframes; Q <= F; i++, Q = (heapframe *)((char *)Q + frame_size)) { @@ -490,10 +491,16 @@ A version did exist that used individual frames on the heap instead of calling match() recursively, but this ran substantially slower. The current version is a refactoring that uses a vector of frames to remember backtracking points. This runs no slower, and possibly even a bit faster than the original recursive -implementation. An initial vector of size START_FRAMES_SIZE (enough for maybe -50 frames) is allocated on the system stack. If this is not big enough, the -heap is used for a larger vector. - +implementation. + +At first, an initial vector of size START_FRAMES_SIZE (enough for maybe 50 +frames) was allocated on the system stack. If this was not big enough, the heap +was used for a larger vector. However, it turns out that there are environments +where taking as little as 20KiB from the system stack is an embarrassment. +After another refactoring, the heap is used exclusively, but a pointer the +frames vector and its size are cached in the match_data block, so that there is +no new memory allocation if the same match_data block is used for multiple +matches (unless the frames vector has to be extended). ******************************************************************************* ******************************************************************************/ @@ -566,10 +573,9 @@ made performance worse. Arguments: start_eptr starting character in subject start_ecode starting position in compiled code - ovector pointer to the final output vector - oveccount number of pairs in ovector top_bracket number of capturing parentheses in the pattern frame_size size of each backtracking frame + match_data pointer to the match_data block mb pointer to "static" variables block Returns: MATCH_MATCH if matched ) these values are >= 0 @@ -580,17 +586,19 @@ Returns: MATCH_MATCH if matched ) these values are >= 0 */ static int -match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, PCRE2_SIZE *ovector, - uint16_t oveccount, uint16_t top_bracket, PCRE2_SIZE frame_size, - match_block *mb) +match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, uint16_t top_bracket, + PCRE2_SIZE frame_size, pcre2_match_data *match_data, match_block *mb) { /* Frame-handling variables */ heapframe *F; /* Current frame pointer */ heapframe *N = NULL; /* Temporary frame pointers */ heapframe *P = NULL; + +heapframe *frames_top; /* End of frames vector */ heapframe *assert_accept_frame = NULL; /* For passing back a frame with captures */ -PCRE2_SIZE frame_copy_size; /* Amount to copy when creating a new frame */ +PCRE2_SIZE heapframes_size; /* Usable size of frames vector */ +PCRE2_SIZE frame_copy_size; /* Amount to copy when creating a new frame */ /* Local variables that do not need to be preserved over calls to RRMATCH(). */ @@ -627,10 +635,14 @@ copied when a new frame is created. */ frame_copy_size = frame_size - offsetof(heapframe, eptr); -/* Set up the first current frame at the start of the vector, and initialize -fields that are not reset for new frames. */ +/* Set up the first frame and the end of the frames vector. We set the local +heapframes_size to the usuable amount of the vector, that is, a whole number of +frames. */ + +F = match_data->heapframes; +heapframes_size = (match_data->heapframes_size / frame_size) * frame_size; +frames_top = (heapframe *)((char *)F + heapframes_size); -F = mb->match_frames; Frdepth = 0; /* "Recursion" depth */ Fcapture_last = 0; /* Number of most recent capture */ Fcurrent_recurse = RECURSE_UNSET; /* Not pattern recursing. */ @@ -646,34 +658,35 @@ backtracking point. */ MATCH_RECURSE: -/* Set up a new backtracking frame. If the vector is full, get a new one -on the heap, doubling the size, but constrained by the heap limit. */ +/* Set up a new backtracking frame. If the vector is full, get a new one, +doubling the size, but constrained by the heap limit (which is in KiB). */ N = (heapframe *)((char *)F + frame_size); -if (N >= mb->match_frames_top) +if (N >= frames_top) { - PCRE2_SIZE newsize = mb->frame_vector_size * 2; heapframe *new; + PCRE2_SIZE newsize = match_data->heapframes_size * 2; - if ((newsize / 1024) > mb->heap_limit) + if (newsize > mb->heap_limit) { - PCRE2_SIZE maxsize = ((mb->heap_limit * 1024)/frame_size) * frame_size; - if (mb->frame_vector_size >= maxsize) return PCRE2_ERROR_HEAPLIMIT; + PCRE2_SIZE maxsize = (mb->heap_limit/frame_size) * frame_size; + if (match_data->heapframes_size >= maxsize) return PCRE2_ERROR_HEAPLIMIT; newsize = maxsize; } - new = mb->memctl.malloc(newsize, mb->memctl.memory_data); + new = match_data->memctl.malloc(newsize, match_data->memctl.memory_data); if (new == NULL) return PCRE2_ERROR_NOMEMORY; - memcpy(new, mb->match_frames, mb->frame_vector_size); + memcpy(new, match_data->heapframes, heapframes_size); - F = (heapframe *)((char *)new + ((char *)F - (char *)mb->match_frames)); + F = (heapframe *)((char *)new + ((char *)F - (char *)match_data->heapframes)); N = (heapframe *)((char *)F + frame_size); - if (mb->match_frames != mb->stack_frames) - mb->memctl.free(mb->match_frames, mb->memctl.memory_data); - mb->match_frames = new; - mb->match_frames_top = (heapframe *)((char *)mb->match_frames + newsize); - mb->frame_vector_size = newsize; + match_data->memctl.free(match_data->heapframes, match_data->memctl.memory_data); + match_data->heapframes = new; + match_data->heapframes_size = newsize; + + heapframes_size = (newsize / frame_size) * frame_size; + frames_top = (heapframe *)((char *)new + heapframes_size); } #ifdef DEBUG_SHOW_RMATCH @@ -731,7 +744,7 @@ recursion value. */ if (group_frame_type != 0) { - Flast_group_offset = (char *)F - (char *)mb->match_frames; + Flast_group_offset = (char *)F - (char *)match_data->heapframes; if (GF_IDMASK(group_frame_type) == GF_RECURSE) Fcurrent_recurse = GF_DATAMASK(group_frame_type); group_frame_type = 0; @@ -773,7 +786,7 @@ fprintf(stderr, "++ op=%d\n", *Fecode); for(;;) { if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL; - N = (heapframe *)((char *)mb->match_frames + offset); + N = (heapframe *)((char *)match_data->heapframes + offset); P = (heapframe *)((char *)N - frame_size); if (N->group_frame_type == (GF_CAPTURE | number)) break; offset = P->last_group_offset; @@ -811,7 +824,7 @@ fprintf(stderr, "++ op=%d\n", *Fecode); for(;;) { if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL; - N = (heapframe *)((char *)mb->match_frames + offset); + N = (heapframe *)((char *)match_data->heapframes + offset); P = (heapframe *)((char *)N - frame_size); if (GF_IDMASK(N->group_frame_type) == GF_RECURSE) break; offset = P->last_group_offset; @@ -864,14 +877,15 @@ fprintf(stderr, "++ op=%d\n", *Fecode); mb->mark = Fmark; /* and the last success mark */ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; - ovector[0] = Fstart_match - mb->start_subject; - ovector[1] = Feptr - mb->start_subject; + match_data->ovector[0] = Fstart_match - mb->start_subject; + match_data->ovector[1] = Feptr - mb->start_subject; /* Set i to the smaller of the sizes of the external and frame ovectors. */ - i = 2 * ((top_bracket + 1 > oveccount)? oveccount : top_bracket + 1); - memcpy(ovector + 2, Fovector, (i - 2) * sizeof(PCRE2_SIZE)); - while (--i >= Foffset_top + 2) ovector[i] = PCRE2_UNSET; + i = 2 * ((top_bracket + 1 > match_data->oveccount)? + match_data->oveccount : top_bracket + 1); + memcpy(match_data->ovector + 2, Fovector, (i - 2) * sizeof(PCRE2_SIZE)); + while (--i >= Foffset_top + 2) match_data->ovector[i] = PCRE2_UNSET; return MATCH_MATCH; /* Note: NOT RRETURN */ @@ -5328,7 +5342,7 @@ fprintf(stderr, "++ op=%d\n", *Fecode); offset = Flast_group_offset; while (offset != PCRE2_UNSET) { - N = (heapframe *)((char *)mb->match_frames + offset); + N = (heapframe *)((char *)match_data->heapframes + offset); P = (heapframe *)((char *)N - frame_size); if (N->group_frame_type == (GF_RECURSE | number)) { @@ -5729,7 +5743,7 @@ fprintf(stderr, "++ op=%d\n", *Fecode); if (*bracode != OP_BRA && *bracode != OP_COND) { - N = (heapframe *)((char *)mb->match_frames + Flast_group_offset); + N = (heapframe *)((char *)match_data->heapframes + Flast_group_offset); P = (heapframe *)((char *)N - frame_size); Flast_group_offset = P->last_group_offset; @@ -6346,6 +6360,7 @@ BOOL jit_checked_utf = FALSE; #endif /* SUPPORT_UNICODE */ PCRE2_SIZE frame_size; +PCRE2_SIZE heapframes_size; /* We need to have mb as a pointer to a match block, because the IS_NEWLINE macro is used below, and it expects NLBLOCK to be defined as a pointer. */ @@ -6354,15 +6369,6 @@ pcre2_callout_block cb; match_block actual_match_block; match_block *mb = &actual_match_block; -/* Allocate an initial vector of backtracking frames on the stack. If this -proves to be too small, it is replaced by a larger one on the heap. To get a -vector of the size required that is aligned for pointers, allocate it as a -vector of pointers. */ - -PCRE2_SPTR stack_frames_vector[START_FRAMES_SIZE/sizeof(PCRE2_SPTR)] - PCRE2_KEEP_UNINITIALIZED; -mb->stack_frames = (heapframe *)stack_frames_vector; - /* Recognize NULL, length 0 as an empty string. */ if (subject == NULL && length == 0) subject = (PCRE2_SPTR)""; @@ -6793,15 +6799,11 @@ switch(re->newline_convention) vector at the end, whose size depends on the number of capturing parentheses in the pattern. It is not used at all if there are no capturing parentheses. - frame_size is the total size of each frame - mb->frame_vector_size is the total usable size of the vector (rounded down - to a whole number of frames) - -The last of these is changed within the match() function if the frame vector -has to be expanded. We therefore put it into the match block so that it is -correct when calling match() more than once for non-anchored patterns. + frame_size is the total size of each frame + match_data->heapframes is the pointer to the frames vector + match_data->heapframes_size is the total size of the vector -We must also pad frame_size for alignment to ensure subsequent frames are as +We must pad the frame_size for alignment to ensure subsequent frames are as aligned as heapframe. Whilst ovector is word-aligned due to being a PCRE2_SIZE array, that does not guarantee it is suitably aligned for pointers, as some architectures have pointers that are larger than a size_t. */ @@ -6813,8 +6815,8 @@ frame_size = (offsetof(heapframe, ovector) + /* Limits set in the pattern override the match context only if they are smaller. */ -mb->heap_limit = (mcontext->heap_limit < re->limit_heap)? - mcontext->heap_limit : re->limit_heap; +mb->heap_limit = ((mcontext->heap_limit < re->limit_heap)? + mcontext->heap_limit : re->limit_heap) * 1024; mb->match_limit = (mcontext->match_limit < re->limit_match)? mcontext->match_limit : re->limit_match; @@ -6823,35 +6825,40 @@ mb->match_limit_depth = (mcontext->depth_limit < re->limit_depth)? mcontext->depth_limit : re->limit_depth; /* If a pattern has very many capturing parentheses, the frame size may be very -large. Ensure that there are at least 10 available frames by getting an initial -vector on the heap if necessary, except when the heap limit prevents this. Get -fewer if possible. (The heap limit is in kibibytes.) */ - -if (frame_size <= START_FRAMES_SIZE/10) +large. Set the initial frame vector size to ensure that there are at least 10 +available frames, but enforce a minimum of START_FRAMES_SIZE. If this is +greater than the heap limit, get as large a vector as possible. Always round +the size to a multiple of the frame size. */ + +heapframes_size = frame_size * 10; +if (heapframes_size < START_FRAMES_SIZE) heapframes_size = START_FRAMES_SIZE; +if (heapframes_size > mb->heap_limit) { - mb->match_frames = mb->stack_frames; /* Initial frame vector on the stack */ - mb->frame_vector_size = ((START_FRAMES_SIZE/frame_size) * frame_size); + if (frame_size > mb->heap_limit ) return PCRE2_ERROR_HEAPLIMIT; + heapframes_size = mb->heap_limit; } -else + +/* If an existing frame vector in the match_data block is large enough, we can +use it.Otherwise, free any pre-existing vector and get a new one. */ + +if (match_data->heapframes_size < heapframes_size) { - mb->frame_vector_size = frame_size * 10; - if ((mb->frame_vector_size / 1024) > mb->heap_limit) + match_data->memctl.free(match_data->heapframes, + match_data->memctl.memory_data); + match_data->heapframes = match_data->memctl.malloc(heapframes_size, + match_data->memctl.memory_data); + if (match_data->heapframes == NULL) { - if (frame_size > mb->heap_limit * 1024) return PCRE2_ERROR_HEAPLIMIT; - mb->frame_vector_size = ((mb->heap_limit * 1024)/frame_size) * frame_size; + match_data->heapframes_size = 0; + return PCRE2_ERROR_NOMEMORY; } - mb->match_frames = mb->memctl.malloc(mb->frame_vector_size, - mb->memctl.memory_data); - if (mb->match_frames == NULL) return PCRE2_ERROR_NOMEMORY; + match_data->heapframes_size = heapframes_size; } -mb->match_frames_top = - (heapframe *)((char *)mb->match_frames + mb->frame_vector_size); - /* Write to the ovector within the first frame to mark every capture unset and to avoid uninitialized memory read errors when it is copied to a new frame. */ -memset((char *)(mb->match_frames) + offsetof(heapframe, ovector), 0xff, +memset((char *)(match_data->heapframes) + offsetof(heapframe, ovector), 0xff, frame_size - offsetof(heapframe, ovector)); /* Pointers to the individual character tables */ @@ -7279,8 +7286,8 @@ for(;;) mb->end_offset_top = 0; mb->skip_arg_count = 0; - rc = match(start_match, mb->start_code, match_data->ovector, - match_data->oveccount, re->top_bracket, frame_size, mb); + rc = match(start_match, mb->start_code, re->top_bracket, frame_size, + match_data, mb); if (mb->hitend && start_partial == NULL) { @@ -7463,11 +7470,6 @@ if (utf && end_subject != true_end_subject && } #endif /* SUPPORT_UNICODE */ -/* Release an enlarged frame vector that is on the heap. */ - -if (mb->match_frames != mb->stack_frames) - mb->memctl.free(mb->match_frames, mb->memctl.memory_data); - /* Fill in fields that are always returned in the match data. */ match_data->code = re; @@ -7533,4 +7535,10 @@ else match_data->rc = PCRE2_ERROR_NOMATCH; return match_data->rc; } +/* These #undefs are here to enable unity builds with CMake. */ + +#undef NLBLOCK /* Block containing newline information */ +#undef PSSTART /* Field containing processed string start */ +#undef PSEND /* Field containing processed string end */ + /* End of pcre2_match.c */ diff --git a/thirdparty/pcre2/src/pcre2_match_data.c b/thirdparty/pcre2/src/pcre2_match_data.c index 53e4698707..fa129b8bc5 100644 --- a/thirdparty/pcre2/src/pcre2_match_data.c +++ b/thirdparty/pcre2/src/pcre2_match_data.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016-2019 University of Cambridge + New API code Copyright (c) 2016-2022 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -51,19 +51,23 @@ POSSIBILITY OF SUCH DAMAGE. * Create a match data block given ovector size * *************************************************/ -/* A minimum of 1 is imposed on the number of ovector pairs. */ +/* A minimum of 1 is imposed on the number of ovector pairs. A maximum is also +imposed because the oveccount field in a match data block is uintt6_t. */ PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION pcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext) { pcre2_match_data *yield; if (oveccount < 1) oveccount = 1; +if (oveccount > UINT16_MAX) oveccount = UINT16_MAX; yield = PRIV(memctl_malloc)( offsetof(pcre2_match_data, ovector) + 2*oveccount*sizeof(PCRE2_SIZE), (pcre2_memctl *)gcontext); if (yield == NULL) return NULL; yield->oveccount = oveccount; yield->flags = 0; +yield->heapframes = NULL; +yield->heapframes_size = 0; return yield; } @@ -95,6 +99,9 @@ pcre2_match_data_free(pcre2_match_data *match_data) { if (match_data != NULL) { + if (match_data->heapframes != NULL) + match_data->memctl.free(match_data->heapframes, + match_data->memctl.memory_data); if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0) match_data->memctl.free((void *)match_data->subject, match_data->memctl.memory_data); diff --git a/thirdparty/pcre2/src/pcre2_substitute.c b/thirdparty/pcre2/src/pcre2_substitute.c index 8b2c369ccc..edbb78c6d7 100644 --- a/thirdparty/pcre2/src/pcre2_substitute.c +++ b/thirdparty/pcre2/src/pcre2_substitute.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016-2021 University of Cambridge + New API code Copyright (c) 2016-2022 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -259,16 +259,16 @@ PCRE2_UNSET, so as not to imply an offset in the replacement. */ if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0) return PCRE2_ERROR_BADOPTION; - -/* Validate length and find the end of the replacement. A NULL replacement of + +/* Validate length and find the end of the replacement. A NULL replacement of zero length is interpreted as an empty string. */ -if (replacement == NULL) +if (replacement == NULL) { if (rlength != 0) return PCRE2_ERROR_NULL; - replacement = (PCRE2_SPTR)""; - } - + replacement = (PCRE2_SPTR)""; + } + if (rlength == PCRE2_ZERO_TERMINATED) rlength = PRIV(strlen)(replacement); repend = replacement + rlength; @@ -282,8 +282,9 @@ replacement_only = ((options & PCRE2_SUBSTITUTE_REPLACEMENT_ONLY) != 0); match data block. We create an internal match_data block in two cases: (a) an external one is not supplied (and we are not starting from an existing match); (b) an existing match is to be used for the first substitution. In the latter -case, we copy the existing match into the internal block. This ensures that no -changes are made to the existing match data block. */ +case, we copy the existing match into the internal block, except for any cached +heap frame size and pointer. This ensures that no changes are made to the +external match data block. */ if (match_data == NULL) { @@ -309,6 +310,8 @@ else if (use_existing_match) if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY; memcpy(internal_match_data, match_data, offsetof(pcre2_match_data, ovector) + 2*pairs*sizeof(PCRE2_SIZE)); + internal_match_data->heapframes = NULL; + internal_match_data->heapframes_size = 0; match_data = internal_match_data; } @@ -328,9 +331,9 @@ scb.ovector = ovector; if (subject == NULL) { - if (length != 0) return PCRE2_ERROR_NULL; + if (length != 0) return PCRE2_ERROR_NULL; subject = (PCRE2_SPTR)""; - } + } /* Find length of zero-terminated subject */ diff --git a/thirdparty/pcre2/src/sljit/sljitConfig.h b/thirdparty/pcre2/src/sljit/sljitConfig.h index 1c821d287d..5fba7aa638 100644 --- a/thirdparty/pcre2/src/sljit/sljitConfig.h +++ b/thirdparty/pcre2/src/sljit/sljitConfig.h @@ -53,7 +53,8 @@ extern "C" { /* #define SLJIT_CONFIG_PPC_64 1 */ /* #define SLJIT_CONFIG_MIPS_32 1 */ /* #define SLJIT_CONFIG_MIPS_64 1 */ -/* #define SLJIT_CONFIG_SPARC_32 1 */ +/* #define SLJIT_CONFIG_RISCV_32 1 */ +/* #define SLJIT_CONFIG_RISCV_64 1 */ /* #define SLJIT_CONFIG_S390X 1 */ /* #define SLJIT_CONFIG_AUTO 1 */ @@ -127,17 +128,6 @@ extern "C" { #endif /* !SLJIT_EXECUTABLE_ALLOCATOR */ -/* Force cdecl calling convention even if a better calling - convention (e.g. fastcall) is supported by the C compiler. - If this option is disabled (this is the default), functions - called from JIT should be defined with SLJIT_FUNC attribute. - Standard C functions can still be called by using the - SLJIT_CALL_CDECL jump type. */ -#ifndef SLJIT_USE_CDECL_CALLING_CONVENTION -/* Disabled by default */ -#define SLJIT_USE_CDECL_CALLING_CONVENTION 0 -#endif - /* Return with error when an invalid argument is passed. */ #ifndef SLJIT_ARGUMENT_CHECKS /* Disabled by default */ diff --git a/thirdparty/pcre2/src/sljit/sljitConfigInternal.h b/thirdparty/pcre2/src/sljit/sljitConfigInternal.h index 55e4e39f13..cd3ce69734 100644 --- a/thirdparty/pcre2/src/sljit/sljitConfigInternal.h +++ b/thirdparty/pcre2/src/sljit/sljitConfigInternal.h @@ -59,7 +59,8 @@ extern "C" { SLJIT_64BIT_ARCHITECTURE : 64 bit architecture SLJIT_LITTLE_ENDIAN : little endian architecture SLJIT_BIG_ENDIAN : big endian architecture - SLJIT_UNALIGNED : allows unaligned memory accesses for non-fpu operations (only!) + SLJIT_UNALIGNED : unaligned memory accesses for non-fpu operations are supported + SLJIT_FPU_UNALIGNED : unaligned memory accesses for fpu operations are supported SLJIT_INDIRECT_CALL : see SLJIT_FUNC_ADDR() for more information Constants: @@ -98,7 +99,8 @@ extern "C" { + (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \ + (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \ + (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \ - + (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \ + + (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) \ + + (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \ + (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \ + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \ + (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2 @@ -115,7 +117,8 @@ extern "C" { && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \ && !(defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \ - && !(defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \ + && !(defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) \ + && !(defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \ && !(defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \ && !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) \ && !(defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) @@ -156,8 +159,10 @@ extern "C" { #define SLJIT_CONFIG_MIPS_32 1 #elif defined(__mips64) #define SLJIT_CONFIG_MIPS_64 1 -#elif (defined(__sparc__) || defined(__sparc)) && !defined(_LP64) -#define SLJIT_CONFIG_SPARC_32 1 +#elif defined (__riscv_xlen) && (__riscv_xlen == 32) +#define SLJIT_CONFIG_RISCV_32 1 +#elif defined (__riscv_xlen) && (__riscv_xlen == 64) +#define SLJIT_CONFIG_RISCV_64 1 #elif defined(__s390x__) #define SLJIT_CONFIG_S390X 1 #else @@ -205,8 +210,8 @@ extern "C" { #define SLJIT_CONFIG_PPC 1 #elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) #define SLJIT_CONFIG_MIPS 1 -#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) || (defined SLJIT_CONFIG_SPARC_64 && SLJIT_CONFIG_SPARC_64) -#define SLJIT_CONFIG_SPARC 1 +#elif (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) || (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) +#define SLJIT_CONFIG_RISCV 1 #endif /***********************************************************/ @@ -330,8 +335,14 @@ extern "C" { * older versions are known to abort in some targets * https://github.com/PhilipHazel/pcre2/issues/92 * - * beware APPLE is known to have removed the code in iOS so - * it will need to be excempted or result in broken builds + * beware some vendors (ex: Microsoft, Apple) are known to have + * removed the code to support this builtin even if the call for + * __has_builtin reports it is available. + * + * make sure linking doesn't fail because __clear_cache() is + * missing before changing it or add an exception so that the + * system provided method that should be defined below is used + * instead. */ #if (!defined SLJIT_CACHE_FLUSH && defined __has_builtin) #if __has_builtin(__builtin___clear_cache) && !defined(__clang__) @@ -339,9 +350,9 @@ extern "C" { /* * https://gcc.gnu.org/bugzilla//show_bug.cgi?id=91248 * https://gcc.gnu.org/bugzilla//show_bug.cgi?id=93811 - * gcc's clear_cache builtin for power and sparc are broken + * gcc's clear_cache builtin for power is broken */ -#if !defined(SLJIT_CONFIG_PPC) && !defined(SLJIT_CONFIG_SPARC_32) +#if !defined(SLJIT_CONFIG_PPC) #define SLJIT_CACHE_FLUSH(from, to) \ __builtin___clear_cache((char*)(from), (char*)(to)) #endif @@ -373,12 +384,10 @@ extern "C" { ppc_cache_flush((from), (to)) #define SLJIT_CACHE_FLUSH_OWN_IMPL 1 -#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) +#elif defined(_WIN32) -/* The __clear_cache() implementation of GCC is a dummy function on Sparc. */ #define SLJIT_CACHE_FLUSH(from, to) \ - sparc_cache_flush((from), (to)) -#define SLJIT_CACHE_FLUSH_OWN_IMPL 1 + FlushInstructionCache(GetCurrentProcess(), (void*)(from), (char*)(to) - (char*)(from)) #elif (defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) || defined(__clang__) @@ -392,11 +401,6 @@ extern "C" { #define SLJIT_CACHE_FLUSH(from, to) \ cacheflush((long)(from), (long)(to), 0) -#elif defined _WIN32 - -#define SLJIT_CACHE_FLUSH(from, to) \ - FlushInstructionCache(GetCurrentProcess(), (void*)(from), (char*)(to) - (char*)(from)) - #else /* Call __ARM_NR_cacheflush on ARM-Linux or the corresponding MIPS syscall. */ @@ -435,6 +439,7 @@ typedef long int sljit_sw; && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \ && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \ + && !(defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \ && !(defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) #define SLJIT_32BIT_ARCHITECTURE 1 #define SLJIT_WORD_SHIFT 2 @@ -495,8 +500,7 @@ typedef double sljit_f64; #if !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) /* These macros are mostly useful for the applications. */ -#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \ - || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +#if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) #ifdef __LITTLE_ENDIAN__ #define SLJIT_LITTLE_ENDIAN 1 @@ -504,8 +508,7 @@ typedef double sljit_f64; #define SLJIT_BIG_ENDIAN 1 #endif -#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \ - || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) +#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) #ifdef __MIPSEL__ #define SLJIT_LITTLE_ENDIAN 1 @@ -532,8 +535,7 @@ typedef double sljit_f64; #endif /* !SLJIT_MIPS_REV */ -#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \ - || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) +#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) #define SLJIT_BIG_ENDIAN 1 @@ -554,19 +556,30 @@ typedef double sljit_f64; #ifndef SLJIT_UNALIGNED -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \ - || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \ +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \ - || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \ - || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \ + || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \ + || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) #define SLJIT_UNALIGNED 1 #endif #endif /* !SLJIT_UNALIGNED */ +#ifndef SLJIT_FPU_UNALIGNED + +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \ + || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \ + || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \ + || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \ + || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) +#define SLJIT_FPU_UNALIGNED 1 +#endif + +#endif /* !SLJIT_FPU_UNALIGNED */ + #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) /* Auto detect SSE2 support using CPUID. On 64 bit x86 cpus, sse2 must be present. */ @@ -578,38 +591,7 @@ typedef double sljit_f64; /*****************************************************************************************/ #ifndef SLJIT_FUNC - -#if (defined SLJIT_USE_CDECL_CALLING_CONVENTION && SLJIT_USE_CDECL_CALLING_CONVENTION) \ - || !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - #define SLJIT_FUNC - -#elif defined(__GNUC__) && !defined(__APPLE__) - -#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -#define SLJIT_FUNC __attribute__ ((fastcall)) -#define SLJIT_X86_32_FASTCALL 1 -#else -#define SLJIT_FUNC -#endif /* gcc >= 3.4 */ - -#elif defined(_MSC_VER) - -#define SLJIT_FUNC __fastcall -#define SLJIT_X86_32_FASTCALL 1 - -#elif defined(__BORLANDC__) - -#define SLJIT_FUNC __msfastcall -#define SLJIT_X86_32_FASTCALL 1 - -#else /* Unknown compiler. */ - -/* The cdecl calling convention is usually the x86 default. */ -#define SLJIT_FUNC - -#endif /* SLJIT_USE_CDECL_CALLING_CONVENTION */ - #endif /* !SLJIT_FUNC */ #ifndef SLJIT_INDIRECT_CALL @@ -621,14 +603,10 @@ typedef double sljit_f64; #endif #endif /* SLJIT_INDIRECT_CALL */ -/* The offset which needs to be substracted from the return address to +/* The offset which needs to be subtracted from the return address to determine the next executed instruction after return. */ #ifndef SLJIT_RETURN_ADDRESS_OFFSET -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) -#define SLJIT_RETURN_ADDRESS_OFFSET 8 -#else #define SLJIT_RETURN_ADDRESS_OFFSET 0 -#endif #endif /* SLJIT_RETURN_ADDRESS_OFFSET */ /***************************************************/ @@ -666,10 +644,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr); #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) #define SLJIT_NUMBER_OF_REGISTERS 12 -#define SLJIT_NUMBER_OF_SAVED_REGISTERS 9 +#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7 #define SLJIT_NUMBER_OF_FLOAT_REGISTERS 7 #define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0 -#define SLJIT_LOCALS_OFFSET_BASE (compiler->locals_offset) +#define SLJIT_LOCALS_OFFSET_BASE (8 * SSIZE_OF(sw)) #define SLJIT_PREF_SHIFT_REG SLJIT_R2 #elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) @@ -683,7 +661,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr); #else /* _WIN64 */ #define SLJIT_NUMBER_OF_SAVED_REGISTERS 8 #define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 10 -#define SLJIT_LOCALS_OFFSET_BASE (4 * (sljit_s32)sizeof(sljit_sw)) +#define SLJIT_LOCALS_OFFSET_BASE (4 * SSIZE_OF(sw)) #endif /* !_WIN64 */ #define SLJIT_PREF_SHIFT_REG SLJIT_R3 @@ -740,17 +718,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr); #define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8 #endif -#elif (defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC) +#elif (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) -#define SLJIT_NUMBER_OF_REGISTERS 18 -#define SLJIT_NUMBER_OF_SAVED_REGISTERS 14 -#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 14 -#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0 -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) -/* saved registers (16), return struct pointer (1), space for 6 argument words (1), - 4th double arg (2), double alignment (1). */ -#define SLJIT_LOCALS_OFFSET_BASE ((16 + 1 + 6 + 2 + 1) * (sljit_s32)sizeof(sljit_sw)) -#endif +#define SLJIT_NUMBER_OF_REGISTERS 23 +#define SLJIT_NUMBER_OF_SAVED_REGISTERS 12 +#define SLJIT_LOCALS_OFFSET_BASE 0 +#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30 +#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 12 #elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) @@ -806,7 +780,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr); #if (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM) \ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \ || (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) \ - || (defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC) \ + || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) #define SLJIT_HAS_STATUS_FLAGS_STATE 1 #endif diff --git a/thirdparty/pcre2/src/sljit/sljitExecAllocator.c b/thirdparty/pcre2/src/sljit/sljitExecAllocator.c index 6359848cd5..92d940ddc2 100644 --- a/thirdparty/pcre2/src/sljit/sljitExecAllocator.c +++ b/thirdparty/pcre2/src/sljit/sljitExecAllocator.c @@ -152,9 +152,6 @@ static SLJIT_INLINE void apple_update_wx_flags(sljit_s32 enable_exec) { #if MAC_OS_X_VERSION_MIN_REQUIRED >= 110000 pthread_jit_write_protect_np(enable_exec); -#elif defined(__clang__) - if (__builtin_available(macOS 11.0, *)) - pthread_jit_write_protect_np(enable_exec); #else #error "Must target Big Sur or newer" #endif /* BigSur */ diff --git a/thirdparty/pcre2/src/sljit/sljitLir.c b/thirdparty/pcre2/src/sljit/sljitLir.c index 313a061dd3..abafe1add9 100644 --- a/thirdparty/pcre2/src/sljit/sljitLir.c +++ b/thirdparty/pcre2/src/sljit/sljitLir.c @@ -133,6 +133,14 @@ #define SLJIT_ARG_MASK 0x7 #define SLJIT_ARG_FULL_MASK (SLJIT_ARG_MASK | SLJIT_ARG_TYPE_SCRATCH_REG) +/* Mask for sljit_emit_mem. */ +#define REG_PAIR_MASK 0xff00 +#define REG_PAIR_FIRST(reg) ((reg) & 0xff) +#define REG_PAIR_SECOND(reg) ((reg) >> 8) + +/* Mask for sljit_emit_enter. */ +#define SLJIT_KEPT_SAVEDS_COUNT(options) ((options) & 0x3) + /* Jump flags. */ #define JUMP_LABEL 0x1 #define JUMP_ADDR 0x2 @@ -145,16 +153,16 @@ # define PATCH_MD 0x10 #endif # define TYPE_SHIFT 13 -#endif +#endif /* SLJIT_CONFIG_X86 */ #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) # define IS_BL 0x4 # define PATCH_B 0x8 -#endif +#endif /* SLJIT_CONFIG_ARM_V5 || SLJIT_CONFIG_ARM_V7 */ #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) # define CPOOL_SIZE 512 -#endif +#endif /* SLJIT_CONFIG_ARM_V5 */ #if (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) # define IS_COND 0x04 @@ -172,7 +180,7 @@ /* BL + imm24 */ # define PATCH_BL 0x60 /* 0xf00 cc code for branches */ -#endif +#endif /* SLJIT_CONFIG_ARM_THUMB2 */ #if (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) # define IS_COND 0x004 @@ -182,7 +190,7 @@ # define PATCH_COND 0x040 # define PATCH_ABS48 0x080 # define PATCH_ABS64 0x100 -#endif +#endif /* SLJIT_CONFIG_ARM_64 */ #if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) # define IS_COND 0x004 @@ -192,9 +200,9 @@ #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) # define PATCH_ABS32 0x040 # define PATCH_ABS48 0x080 -#endif +#endif /* SLJIT_CONFIG_PPC_64 */ # define REMOVE_COND 0x100 -#endif +#endif /* SLJIT_CONFIG_PPC */ #if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) # define IS_MOVABLE 0x004 @@ -212,7 +220,7 @@ #if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) # define PATCH_ABS32 0x400 # define PATCH_ABS48 0x800 -#endif +#endif /* SLJIT_CONFIG_MIPS_64 */ /* instruction types */ # define MOVABLE_INS 0 @@ -221,28 +229,24 @@ # define UNMOVABLE_INS 32 /* FPU status register */ # define FCSR_FCC 33 -#endif - -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) -# define IS_MOVABLE 0x04 -# define IS_COND 0x08 -# define IS_CALL 0x10 +#endif /* SLJIT_CONFIG_MIPS */ -# define PATCH_B 0x20 -# define PATCH_CALL 0x40 - - /* instruction types */ -# define MOVABLE_INS 0 - /* 1 - 31 last destination register */ - /* no destination (i.e: store) */ -# define UNMOVABLE_INS 32 - -# define DST_INS_MASK 0xff +#if (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) +# define IS_COND 0x004 +# define IS_CALL 0x008 - /* ICC_SET is the same as SET_FLAGS. */ -# define ICC_IS_SET (1 << 23) -# define FCC_IS_SET (1 << 24) -#endif +# define PATCH_B 0x010 +# define PATCH_J 0x020 + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) +# define PATCH_REL32 0x040 +# define PATCH_ABS32 0x080 +# define PATCH_ABS44 0x100 +# define PATCH_ABS52 0x200 +#else /* !SLJIT_CONFIG_RISCV_64 */ +# define PATCH_REL32 0x0 +#endif /* SLJIT_CONFIG_RISCV_64 */ +#endif /* SLJIT_CONFIG_RISCV */ /* Stack management. */ @@ -385,7 +389,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allo invalid_integer_types); SLJIT_COMPILE_ASSERT(SLJIT_REWRITABLE_JUMP != SLJIT_32, rewritable_jump_and_single_op_must_not_be_the_same); - SLJIT_COMPILE_ASSERT(!(SLJIT_EQUAL & 0x1) && !(SLJIT_LESS & 0x1) && !(SLJIT_EQUAL_F64 & 0x1) && !(SLJIT_JUMP & 0x1), + SLJIT_COMPILE_ASSERT(!(SLJIT_EQUAL & 0x1) && !(SLJIT_LESS & 0x1) && !(SLJIT_F_EQUAL & 0x1) && !(SLJIT_JUMP & 0x1), conditional_flags_must_be_even_numbers); /* Only the non-zero members must be set. */ @@ -437,10 +441,6 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allo compiler->delay_slot = UNMOVABLE_INS; #endif -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - compiler->delay_slot = UNMOVABLE_INS; -#endif - #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ || (defined SLJIT_DEBUG && SLJIT_DEBUG) compiler->last_flags = 0; @@ -822,6 +822,9 @@ static sljit_s32 function_check_src_mem(struct sljit_compiler *compiler, sljit_s if (!(p & SLJIT_MEM)) return 0; + if (p == SLJIT_MEM1(SLJIT_SP)) + return (i >= 0 && i < compiler->logical_local_size); + if (!(!(p & REG_MASK) || FUNCTION_CHECK_IS_REG(p & REG_MASK))) return 0; @@ -859,9 +862,6 @@ static sljit_s32 function_check_src(struct sljit_compiler *compiler, sljit_s32 p if (p == SLJIT_IMM) return 1; - if (p == SLJIT_MEM1(SLJIT_SP)) - return (i >= 0 && i < compiler->logical_local_size); - return function_check_src_mem(compiler, p, i); } @@ -876,9 +876,6 @@ static sljit_s32 function_check_dst(struct sljit_compiler *compiler, sljit_s32 p if (FUNCTION_CHECK_IS_REG(p)) return (i == 0); - if (p == SLJIT_MEM1(SLJIT_SP)) - return (i >= 0 && i < compiler->logical_local_size); - return function_check_src_mem(compiler, p, i); } @@ -893,9 +890,6 @@ static sljit_s32 function_fcheck(struct sljit_compiler *compiler, sljit_s32 p, s if (FUNCTION_CHECK_IS_FREG(p)) return (i == 0); - if (p == SLJIT_MEM1(SLJIT_SP)) - return (i >= 0 && i < compiler->logical_local_size); - return function_check_src_mem(compiler, p, i); } @@ -913,7 +907,11 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *comp #if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) #ifdef _WIN64 +#ifdef __GNUC__ +# define SLJIT_PRINT_D "ll" +#else # define SLJIT_PRINT_D "I64" +#endif #else # define SLJIT_PRINT_D "l" #endif @@ -995,13 +993,14 @@ static const char* op0_names[] = { static const char* op1_names[] = { "", ".u8", ".s8", ".u16", ".s16", ".u32", ".s32", "32", - ".p", "not", "clz", + ".p", "not", "clz", "ctz" }; static const char* op2_names[] = { "add", "addc", "sub", "subc", "mul", "and", "or", "xor", - "shl", "lshr", "ashr", + "shl", "mshl", "lshr", "mlshr", + "ashr", "mashr", "rotl", "rotr" }; static const char* op_src_names[] = { @@ -1020,10 +1019,6 @@ static const char* fop2_names[] = { "add", "sub", "mul", "div" }; -#define JUMP_POSTFIX(type) \ - ((type & 0xff) <= SLJIT_NOT_OVERFLOW ? ((type & SLJIT_32) ? "32" : "") \ - : ((type & 0xff) <= SLJIT_ORDERED_F64 ? ((type & SLJIT_32) ? ".f32" : ".f64") : "")) - static const char* jump_names[] = { "equal", "not_equal", "less", "greater_equal", @@ -1032,12 +1027,18 @@ static const char* jump_names[] = { "sig_greater", "sig_less_equal", "overflow", "not_overflow", "carry", "", - "equal", "not_equal", - "less", "greater_equal", - "greater", "less_equal", + "f_equal", "f_not_equal", + "f_less", "f_greater_equal", + "f_greater", "f_less_equal", "unordered", "ordered", + "ordered_equal", "unordered_or_not_equal", + "ordered_less", "unordered_or_greater_equal", + "ordered_greater", "unordered_or_less_equal", + "unordered_or_equal", "ordered_not_equal", + "unordered_or_less", "ordered_greater_equal", + "unordered_or_greater", "ordered_less_equal", "jump", "fast_call", - "call", "call.cdecl" + "call", "call_reg_arg" }; static const char* call_arg_names[] = { @@ -1053,6 +1054,8 @@ static const char* call_arg_names[] = { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ || (defined SLJIT_VERBOSE && SLJIT_VERBOSE) +#define SLJIT_SKIP_CHECKS(compiler) (compiler)->skip_checks = 1 + static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_generate_code(struct sljit_compiler *compiler) { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) @@ -1080,7 +1083,12 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_enter(struct sljit_compil SLJIT_UNUSED_ARG(compiler); #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(!(options & ~SLJIT_ENTER_CDECL)); + if (options & SLJIT_ENTER_REG_ARG) { + CHECK_ARGUMENT(!(options & ~(0x3 | SLJIT_ENTER_REG_ARG))); + } else { + CHECK_ARGUMENT(options == 0); + } + CHECK_ARGUMENT(SLJIT_KEPT_SAVEDS_COUNT(options) <= 3 && SLJIT_KEPT_SAVEDS_COUNT(options) <= saveds); CHECK_ARGUMENT(scratches >= 0 && scratches <= SLJIT_NUMBER_OF_REGISTERS); CHECK_ARGUMENT(saveds >= 0 && saveds <= SLJIT_NUMBER_OF_SAVED_REGISTERS); CHECK_ARGUMENT(scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS); @@ -1088,8 +1096,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_enter(struct sljit_compil CHECK_ARGUMENT(fsaveds >= 0 && fsaveds <= SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS); CHECK_ARGUMENT(fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS); CHECK_ARGUMENT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE); - CHECK_ARGUMENT((arg_types & SLJIT_ARG_FULL_MASK) < SLJIT_ARG_TYPE_F64); - CHECK_ARGUMENT(function_check_arguments(arg_types, scratches, saveds, fscratches)); + CHECK_ARGUMENT((arg_types & SLJIT_ARG_FULL_MASK) <= SLJIT_ARG_TYPE_F32); + CHECK_ARGUMENT(function_check_arguments(arg_types, scratches, (options & SLJIT_ENTER_REG_ARG) ? 0 : saveds, fscratches)); compiler->last_flags = 0; #endif @@ -1109,8 +1117,16 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_enter(struct sljit_compil } while (arg_types); } - fprintf(compiler->verbose, "],%s scratches:%d, saveds:%d, fscratches:%d, fsaveds:%d, local_size:%d\n", - (options & SLJIT_ENTER_CDECL) ? " enter:cdecl," : "", + fprintf(compiler->verbose, "],"); + + if (options & SLJIT_ENTER_REG_ARG) { + fprintf(compiler->verbose, " enter:reg_arg,"); + + if (SLJIT_KEPT_SAVEDS_COUNT(options) > 0) + fprintf(compiler->verbose, " keep:%d,", SLJIT_KEPT_SAVEDS_COUNT(options)); + } + + fprintf(compiler->verbose, "scratches:%d, saveds:%d, fscratches:%d, fsaveds:%d, local_size:%d\n", scratches, saveds, fscratches, fsaveds, local_size); } #endif @@ -1124,7 +1140,12 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_set_context(struct sljit_compi SLJIT_UNUSED_ARG(compiler); #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(!(options & ~SLJIT_ENTER_CDECL)); + if (options & SLJIT_ENTER_REG_ARG) { + CHECK_ARGUMENT(!(options & ~(0x3 | SLJIT_ENTER_REG_ARG))); + } else { + CHECK_ARGUMENT(options == 0); + } + CHECK_ARGUMENT(SLJIT_KEPT_SAVEDS_COUNT(options) <= 3 && SLJIT_KEPT_SAVEDS_COUNT(options) <= saveds); CHECK_ARGUMENT(scratches >= 0 && scratches <= SLJIT_NUMBER_OF_REGISTERS); CHECK_ARGUMENT(saveds >= 0 && saveds <= SLJIT_NUMBER_OF_SAVED_REGISTERS); CHECK_ARGUMENT(scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS); @@ -1133,7 +1154,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_set_context(struct sljit_compi CHECK_ARGUMENT(fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS); CHECK_ARGUMENT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE); CHECK_ARGUMENT((arg_types & SLJIT_ARG_FULL_MASK) < SLJIT_ARG_TYPE_F64); - CHECK_ARGUMENT(function_check_arguments(arg_types, scratches, saveds, fscratches)); + CHECK_ARGUMENT(function_check_arguments(arg_types, scratches, (options & SLJIT_ENTER_REG_ARG) ? 0 : saveds, fscratches)); compiler->last_flags = 0; #endif @@ -1153,8 +1174,16 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_set_context(struct sljit_compi } while (arg_types); } - fprintf(compiler->verbose, "],%s scratches:%d, saveds:%d, fscratches:%d, fsaveds:%d, local_size:%d\n", - (options & SLJIT_ENTER_CDECL) ? " enter:cdecl," : "", + fprintf(compiler->verbose, "],"); + + if (options & SLJIT_ENTER_REG_ARG) { + fprintf(compiler->verbose, " enter:reg_arg,"); + + if (SLJIT_KEPT_SAVEDS_COUNT(options) > 0) + fprintf(compiler->verbose, " keep:%d,", SLJIT_KEPT_SAVEDS_COUNT(options)); + } + + fprintf(compiler->verbose, " scratches:%d, saveds:%d, fscratches:%d, fsaveds:%d, local_size:%d\n", scratches, saveds, fscratches, fsaveds, local_size); } #endif @@ -1195,18 +1224,52 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return(struct sljit_compi case SLJIT_ARG_TYPE_P: CHECK_ARGUMENT(op == SLJIT_MOV_P); break; + case SLJIT_ARG_TYPE_F64: + CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU)); + CHECK_ARGUMENT(op == SLJIT_MOV_F64); + break; + case SLJIT_ARG_TYPE_F32: + CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU)); + CHECK_ARGUMENT(op == SLJIT_MOV_F32); + break; default: /* Context not initialized, void, etc. */ CHECK_ARGUMENT(0); break; } - FUNCTION_CHECK_SRC(src, srcw); + + if (GET_OPCODE(op) < SLJIT_MOV_F64) { + FUNCTION_CHECK_SRC(src, srcw); + } else { + FUNCTION_FCHECK(src, srcw); + } compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { - fprintf(compiler->verbose, " return%s%s ", !(op & SLJIT_32) ? "" : "32", - op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE]); + if (GET_OPCODE(op) < SLJIT_MOV_F64) { + fprintf(compiler->verbose, " return%s%s ", !(op & SLJIT_32) ? "" : "32", + op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE]); + sljit_verbose_param(compiler, src, srcw); + } else { + fprintf(compiler->verbose, " return%s ", !(op & SLJIT_32) ? ".f64" : ".f32"); + sljit_verbose_fparam(compiler, src, srcw); + } + fprintf(compiler->verbose, "\n"); + } +#endif + CHECK_RETURN_OK; +} + +static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + FUNCTION_CHECK_SRC(src, srcw); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " return_to "); sljit_verbose_param(compiler, src, srcw); fprintf(compiler->verbose, "\n"); } @@ -1263,7 +1326,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op1(struct sljit_compiler } #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_CLZ); + CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_CTZ); switch (GET_OPCODE(op)) { case SLJIT_NOT: @@ -1324,15 +1387,18 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op2(struct sljit_compiler } #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_ADD && GET_OPCODE(op) <= SLJIT_ASHR); + CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_ADD && GET_OPCODE(op) <= SLJIT_ROTR); switch (GET_OPCODE(op)) { case SLJIT_AND: case SLJIT_OR: case SLJIT_XOR: case SLJIT_SHL: + case SLJIT_MSHL: case SLJIT_LSHR: + case SLJIT_MLSHR: case SLJIT_ASHR: + case SLJIT_MASHR: CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)); break; case SLJIT_MUL: @@ -1357,6 +1423,10 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op2(struct sljit_compiler CHECK_ARGUMENT((compiler->last_flags & 0xff) == GET_FLAG_TYPE(SLJIT_SET_CARRY)); CHECK_ARGUMENT((op & SLJIT_32) == (compiler->last_flags & SLJIT_32)); break; + case SLJIT_ROTL: + case SLJIT_ROTR: + CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK))); + break; default: SLJIT_UNREACHABLE(); break; @@ -1390,6 +1460,35 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op2(struct sljit_compiler CHECK_RETURN_OK; } +static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + CHECK_ARGUMENT(GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_LSHR + || GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR); + CHECK_ARGUMENT((op & ~(0xff | SLJIT_32 | SLJIT_SHIFT_INTO_NON_ZERO)) == 0); + CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(src_dst)); + FUNCTION_CHECK_SRC(src1, src1w); + FUNCTION_CHECK_SRC(src2, src2w); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " %s%s.into%s ", op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], !(op & SLJIT_32) ? "" : "32", + (op & SLJIT_SHIFT_INTO_NON_ZERO) ? ".nz" : ""); + + sljit_verbose_reg(compiler, src_dst); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(compiler, src1, src1w); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(compiler, src2, src2w); + fprintf(compiler->verbose, "\n"); + } +#endif + CHECK_RETURN_OK; +} + static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { @@ -1510,7 +1609,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_cmp(struct sljit_com sljit_s32 src2, sljit_sw src2w) { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_32 | SLJIT_SET_Z)); + compiler->last_flags = GET_FLAG_TYPE(op) | (op & SLJIT_32); #endif if (SLJIT_UNLIKELY(compiler->skip_checks)) { @@ -1523,7 +1622,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_cmp(struct sljit_com CHECK_ARGUMENT(GET_OPCODE(op) == SLJIT_CMP_F64); CHECK_ARGUMENT(!(op & SLJIT_SET_Z)); CHECK_ARGUMENT((op & VARIABLE_FLAG_MASK) - || (GET_FLAG_TYPE(op) >= SLJIT_EQUAL_F64 && GET_FLAG_TYPE(op) <= SLJIT_ORDERED_F64)); + || (GET_FLAG_TYPE(op) >= SLJIT_F_EQUAL && GET_FLAG_TYPE(op) <= SLJIT_ORDERED_LESS_EQUAL)); FUNCTION_FCHECK(src1, src1w); FUNCTION_FCHECK(src2, src2w); #endif @@ -1531,7 +1630,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_cmp(struct sljit_com if (SLJIT_UNLIKELY(!!compiler->verbose)) { fprintf(compiler->verbose, " %s%s", fop1_names[SLJIT_CMP_F64 - SLJIT_FOP1_BASE], (op & SLJIT_32) ? ".f32" : ".f64"); if (op & VARIABLE_FLAG_MASK) { - fprintf(compiler->verbose, ".%s_f", jump_names[GET_FLAG_TYPE(op)]); + fprintf(compiler->verbose, ".%s", jump_names[GET_FLAG_TYPE(op)]); } fprintf(compiler->verbose, " "); sljit_verbose_fparam(compiler, src1, src1w); @@ -1650,6 +1749,17 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_label(struct sljit_compil CHECK_RETURN_OK; } +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \ + || (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM) +#define CHECK_UNORDERED(type, last_flags) \ + ((((type) & 0xff) == SLJIT_UNORDERED || ((type) & 0xff) == SLJIT_ORDERED) && \ + ((last_flags) & 0xff) >= SLJIT_UNORDERED && ((last_flags) & 0xff) <= SLJIT_ORDERED_LESS_EQUAL) +#else +#define CHECK_UNORDERED(type, last_flags) 0 +#endif +#endif /* SLJIT_ARGUMENT_CHECKS */ + static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type) { if (SLJIT_UNLIKELY(compiler->skip_checks)) { @@ -1658,9 +1768,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_jump(struct sljit_compile } #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_32))); + CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP))); CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_FAST_CALL); - CHECK_ARGUMENT((type & 0xff) < SLJIT_JUMP || !(type & SLJIT_32)); if ((type & 0xff) < SLJIT_JUMP) { if ((type & 0xff) <= SLJIT_NOT_ZERO) @@ -1670,13 +1779,14 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_jump(struct sljit_compile compiler->last_flags = 0; } else CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff) - || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)); + || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW) + || CHECK_UNORDERED(type, compiler->last_flags)); } #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) - fprintf(compiler->verbose, " jump%s %s%s\n", !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", - jump_names[type & 0xff], JUMP_POSTFIX(type)); + fprintf(compiler->verbose, " jump%s %s\n", !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", + jump_names[type & 0xff]); #endif CHECK_RETURN_OK; } @@ -1686,11 +1796,17 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_call(struct sljit_compile { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_CALL_RETURN))); - CHECK_ARGUMENT((type & 0xff) == SLJIT_CALL || (type & 0xff) == SLJIT_CALL_CDECL); + CHECK_ARGUMENT((type & 0xff) >= SLJIT_CALL && (type & 0xff) <= SLJIT_CALL_REG_ARG); CHECK_ARGUMENT(function_check_arguments(arg_types, compiler->scratches, -1, compiler->fscratches)); if (type & SLJIT_CALL_RETURN) { CHECK_ARGUMENT((arg_types & SLJIT_ARG_MASK) == compiler->last_return); + + if (compiler->options & SLJIT_ENTER_REG_ARG) { + CHECK_ARGUMENT((type & 0xff) == SLJIT_CALL_REG_ARG); + } else { + CHECK_ARGUMENT((type & 0xff) != SLJIT_CALL_REG_ARG); + } } #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) @@ -1729,8 +1845,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmp(struct sljit_compiler #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { - fprintf(compiler->verbose, " cmp%s %s%s, ", !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", - jump_names[type & 0xff], (type & SLJIT_32) ? "32" : ""); + fprintf(compiler->verbose, " cmp%s%s %s, ", (type & SLJIT_32) ? "32" : "", + !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", jump_names[type & 0xff]); sljit_verbose_param(compiler, src1, src1w); fprintf(compiler->verbose, ", "); sljit_verbose_param(compiler, src2, src2w); @@ -1747,15 +1863,16 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fcmp(struct sljit_compile #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU)); CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_32))); - CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL_F64 && (type & 0xff) <= SLJIT_ORDERED_F64); + CHECK_ARGUMENT((type & 0xff) >= SLJIT_F_EQUAL && (type & 0xff) <= SLJIT_ORDERED_LESS_EQUAL + && ((type & 0xff) <= SLJIT_ORDERED || sljit_cmp_info(type & 0xff))); FUNCTION_FCHECK(src1, src1w); FUNCTION_FCHECK(src2, src2w); compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { - fprintf(compiler->verbose, " fcmp%s %s%s, ", !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", - jump_names[type & 0xff], (type & SLJIT_32) ? ".f32" : ".f64"); + fprintf(compiler->verbose, " fcmp%s%s %s, ", (type & SLJIT_32) ? ".f32" : ".f64", + !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", jump_names[type & 0xff]); sljit_verbose_fparam(compiler, src1, src1w); fprintf(compiler->verbose, ", "); sljit_verbose_fparam(compiler, src2, src2w); @@ -1793,12 +1910,18 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_icall(struct sljit_compil { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_CALL_RETURN))); - CHECK_ARGUMENT((type & 0xff) == SLJIT_CALL || (type & 0xff) == SLJIT_CALL_CDECL); + CHECK_ARGUMENT((type & 0xff) >= SLJIT_CALL && (type & 0xff) <= SLJIT_CALL_REG_ARG); CHECK_ARGUMENT(function_check_arguments(arg_types, compiler->scratches, -1, compiler->fscratches)); FUNCTION_CHECK_SRC(src, srcw); if (type & SLJIT_CALL_RETURN) { CHECK_ARGUMENT((arg_types & SLJIT_ARG_MASK) == compiler->last_return); + + if (compiler->options & SLJIT_ENTER_REG_ARG) { + CHECK_ARGUMENT((type & 0xff) == SLJIT_CALL_REG_ARG); + } else { + CHECK_ARGUMENT((type & 0xff) != SLJIT_CALL_REG_ARG); + } } #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) @@ -1830,18 +1953,18 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_flags(struct sljit_com sljit_s32 type) { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_32))); - CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_ORDERED_F64); + CHECK_ARGUMENT(type >= SLJIT_EQUAL && type <= SLJIT_ORDERED_LESS_EQUAL); CHECK_ARGUMENT(op == SLJIT_MOV || op == SLJIT_MOV32 || (GET_OPCODE(op) >= SLJIT_AND && GET_OPCODE(op) <= SLJIT_XOR)); CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)); - if ((type & 0xff) <= SLJIT_NOT_ZERO) + if (type <= SLJIT_NOT_ZERO) CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z); else - CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff) - || ((type & 0xff) == SLJIT_NOT_CARRY && (compiler->last_flags & 0xff) == SLJIT_CARRY) - || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)); + CHECK_ARGUMENT(type == (compiler->last_flags & 0xff) + || (type == SLJIT_NOT_CARRY && (compiler->last_flags & 0xff) == SLJIT_CARRY) + || (type == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW) + || CHECK_UNORDERED(type, compiler->last_flags)); FUNCTION_CHECK_DST(dst, dstw); @@ -1850,12 +1973,12 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_flags(struct sljit_com #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { - fprintf(compiler->verbose, " flags%s %s%s, ", - !(op & SLJIT_SET_Z) ? "" : ".z", + fprintf(compiler->verbose, " flags.%s%s%s ", GET_OPCODE(op) < SLJIT_OP2_BASE ? "mov" : op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], - GET_OPCODE(op) < SLJIT_OP2_BASE ? op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE] : ((op & SLJIT_32) ? "32" : "")); + GET_OPCODE(op) < SLJIT_OP2_BASE ? op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE] : ((op & SLJIT_32) ? "32" : ""), + !(op & SLJIT_SET_Z) ? "" : ".z"); sljit_verbose_param(compiler, dst, dstw); - fprintf(compiler->verbose, ", %s%s\n", jump_names[type & 0xff], JUMP_POSTFIX(type)); + fprintf(compiler->verbose, ", %s\n", jump_names[type]); } #endif CHECK_RETURN_OK; @@ -1866,28 +1989,31 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmov(struct sljit_compile sljit_s32 src, sljit_sw srcw) { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_32))); - CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_ORDERED_F64); + sljit_s32 cond = type & ~SLJIT_32; + + CHECK_ARGUMENT(cond >= SLJIT_EQUAL && cond <= SLJIT_ORDERED_LESS_EQUAL); CHECK_ARGUMENT(compiler->scratches != -1 && compiler->saveds != -1); - CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(dst_reg & ~SLJIT_32)); + CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(dst_reg)); if (src != SLJIT_IMM) { CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(src)); CHECK_ARGUMENT(srcw == 0); } - if ((type & 0xff) <= SLJIT_NOT_ZERO) + if (cond <= SLJIT_NOT_ZERO) CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z); else - CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff) - || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)); + CHECK_ARGUMENT(cond == (compiler->last_flags & 0xff) + || (cond == SLJIT_NOT_CARRY && (compiler->last_flags & 0xff) == SLJIT_CARRY) + || (cond == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW) + || CHECK_UNORDERED(cond, compiler->last_flags)); #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { - fprintf(compiler->verbose, " cmov%s %s%s, ", - !(dst_reg & SLJIT_32) ? "" : "32", - jump_names[type & 0xff], JUMP_POSTFIX(type)); - sljit_verbose_reg(compiler, dst_reg & ~SLJIT_32); + fprintf(compiler->verbose, " cmov%s %s, ", + !(type & SLJIT_32) ? "" : "32", + jump_names[type & ~SLJIT_32]); + sljit_verbose_reg(compiler, dst_reg); fprintf(compiler->verbose, ", "); sljit_verbose_param(compiler, src, srcw); fprintf(compiler->verbose, "\n"); @@ -1900,28 +2026,123 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_mem(struct sljit_compiler sljit_s32 reg, sljit_s32 mem, sljit_sw memw) { + if (SLJIT_UNLIKELY(compiler->skip_checks)) { + compiler->skip_checks = 0; + CHECK_RETURN_OK; + } + #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT((type & 0xff) >= SLJIT_MOV && (type & 0xff) <= SLJIT_MOV_P); - CHECK_ARGUMENT(!(type & SLJIT_32) || ((type & 0xff) != SLJIT_MOV && (type & 0xff) != SLJIT_MOV_U32 && (type & 0xff) != SLJIT_MOV_P)); - CHECK_ARGUMENT((type & SLJIT_MEM_PRE) || (type & SLJIT_MEM_POST)); - CHECK_ARGUMENT((type & (SLJIT_MEM_PRE | SLJIT_MEM_POST)) != (SLJIT_MEM_PRE | SLJIT_MEM_POST)); - CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_PRE | SLJIT_MEM_POST)) == 0); + sljit_s32 allowed_flags; + + if (type & SLJIT_MEM_UNALIGNED) { + CHECK_ARGUMENT(!(type & (SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32))); + } else if (type & SLJIT_MEM_UNALIGNED_16) { + CHECK_ARGUMENT(!(type & SLJIT_MEM_UNALIGNED_32)); + } else { + CHECK_ARGUMENT((reg & REG_PAIR_MASK) || (type & SLJIT_MEM_UNALIGNED_32)); + } + + allowed_flags = SLJIT_MEM_UNALIGNED; + + switch (type & 0xff) { + case SLJIT_MOV_U32: + case SLJIT_MOV_S32: + case SLJIT_MOV32: + allowed_flags = SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16; + break; + case SLJIT_MOV: + case SLJIT_MOV_P: + allowed_flags = SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32; + break; + } + + CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | allowed_flags)) == 0); + + if (reg & REG_PAIR_MASK) { + CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV); + CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(REG_PAIR_FIRST(reg))); + CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(REG_PAIR_SECOND(reg))); + CHECK_ARGUMENT(REG_PAIR_FIRST(reg) != REG_PAIR_SECOND(reg)); + } else { + CHECK_ARGUMENT((type & 0xff) >= SLJIT_MOV && (type & 0xff) <= SLJIT_MOV_P); + CHECK_ARGUMENT(!(type & SLJIT_32) || ((type & 0xff) >= SLJIT_MOV_U8 && (type & 0xff) <= SLJIT_MOV_S16)); + CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(reg)); + } FUNCTION_CHECK_SRC_MEM(mem, memw); - CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(reg)); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + if ((type & 0xff) == SLJIT_MOV32) + fprintf(compiler->verbose, " %s32", + (type & SLJIT_MEM_STORE) ? "store" : "load"); + else + fprintf(compiler->verbose, " %s%s%s", + (type & SLJIT_MEM_STORE) ? "store" : "load", + !(type & SLJIT_32) ? "" : "32", + op1_names[(type & 0xff) - SLJIT_OP1_BASE]); + + if (type & SLJIT_MEM_UNALIGNED) + printf(".un"); + else if (type & SLJIT_MEM_UNALIGNED_16) + printf(".un16"); + else if (type & SLJIT_MEM_UNALIGNED_32) + printf(".un32"); + + if (reg & REG_PAIR_MASK) { + fprintf(compiler->verbose, " {"); + sljit_verbose_reg(compiler, REG_PAIR_FIRST(reg)); + fprintf(compiler->verbose, ", "); + sljit_verbose_reg(compiler, REG_PAIR_SECOND(reg)); + fprintf(compiler->verbose, "}, "); + } else { + fprintf(compiler->verbose, " "); + sljit_verbose_reg(compiler, reg); + fprintf(compiler->verbose, ", "); + } + sljit_verbose_param(compiler, mem, memw); + fprintf(compiler->verbose, "\n"); + } +#endif + CHECK_RETURN_OK; +} +static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + if (SLJIT_UNLIKELY(compiler->skip_checks)) { + compiler->skip_checks = 0; + CHECK_RETURN_OK; + } + +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + CHECK_ARGUMENT((type & 0xff) >= SLJIT_MOV && (type & 0xff) <= SLJIT_MOV_P); + CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_POST)) == 0); CHECK_ARGUMENT((mem & REG_MASK) != 0 && (mem & REG_MASK) != reg); + + FUNCTION_CHECK_SRC_MEM(mem, memw); #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) - if (!(type & SLJIT_MEM_SUPP) && SLJIT_UNLIKELY(!!compiler->verbose)) { - if (sljit_emit_mem(compiler, type | SLJIT_MEM_SUPP, reg, mem, memw) == SLJIT_ERR_UNSUPPORTED) - fprintf(compiler->verbose, " //"); + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + if (type & SLJIT_MEM_SUPP) + CHECK_RETURN_OK; + if (sljit_emit_mem_update(compiler, type | SLJIT_MEM_SUPP, reg, mem, memw) == SLJIT_ERR_UNSUPPORTED) { + fprintf(compiler->verbose, " # mem: unsupported form, no instructions are emitted\n"); + CHECK_RETURN_OK; + } + + if ((type & 0xff) == SLJIT_MOV32) + fprintf(compiler->verbose, " %s32.%s ", + (type & SLJIT_MEM_STORE) ? "store" : "load", + (type & SLJIT_MEM_POST) ? "post" : "pre"); + else + fprintf(compiler->verbose, " %s%s%s.%s ", + (type & SLJIT_MEM_STORE) ? "store" : "load", + !(type & SLJIT_32) ? "" : "32", + op1_names[(type & 0xff) - SLJIT_OP1_BASE], + (type & SLJIT_MEM_POST) ? "post" : "pre"); - fprintf(compiler->verbose, " mem%s.%s%s%s ", - !(type & SLJIT_32) ? "" : "32", - (type & SLJIT_MEM_STORE) ? "st" : "ld", - op1_names[(type & 0xff) - SLJIT_OP1_BASE], - (type & SLJIT_MEM_PRE) ? ".pre" : ".post"); sljit_verbose_reg(compiler, reg); fprintf(compiler->verbose, ", "); sljit_verbose_param(compiler, mem, memw); @@ -1937,22 +2158,67 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fmem(struct sljit_compile { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV_F64); - CHECK_ARGUMENT((type & SLJIT_MEM_PRE) || (type & SLJIT_MEM_POST)); - CHECK_ARGUMENT((type & (SLJIT_MEM_PRE | SLJIT_MEM_POST)) != (SLJIT_MEM_PRE | SLJIT_MEM_POST)); - CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_PRE | SLJIT_MEM_POST)) == 0); + if (type & SLJIT_MEM_UNALIGNED) { + CHECK_ARGUMENT(!(type & (SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32))); + } else if (type & SLJIT_MEM_UNALIGNED_16) { + CHECK_ARGUMENT(!(type & SLJIT_MEM_UNALIGNED_32)); + } else { + CHECK_ARGUMENT(type & SLJIT_MEM_UNALIGNED_32); + CHECK_ARGUMENT(!(type & SLJIT_32)); + } + + CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32))); + CHECK_ARGUMENT(FUNCTION_CHECK_IS_FREG(freg)); + FUNCTION_CHECK_SRC_MEM(mem, memw); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " %s.%s", + (type & SLJIT_MEM_STORE) ? "store" : "load", + !(type & SLJIT_32) ? "f64" : "f32"); + + if (type & SLJIT_MEM_UNALIGNED) + printf(".un"); + else if (type & SLJIT_MEM_UNALIGNED_16) + printf(".un16"); + else if (type & SLJIT_MEM_UNALIGNED_32) + printf(".un32"); + + fprintf(compiler->verbose, " "); + sljit_verbose_freg(compiler, freg); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(compiler, mem, memw); + fprintf(compiler->verbose, "\n"); + } +#endif + CHECK_RETURN_OK; +} + +static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw) +{ +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV_F64); + CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_POST)) == 0); FUNCTION_CHECK_SRC_MEM(mem, memw); CHECK_ARGUMENT(FUNCTION_CHECK_IS_FREG(freg)); #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) - if (!(type & SLJIT_MEM_SUPP) && SLJIT_UNLIKELY(!!compiler->verbose)) { - if (sljit_emit_fmem(compiler, type | SLJIT_MEM_SUPP, freg, mem, memw) == SLJIT_ERR_UNSUPPORTED) - fprintf(compiler->verbose, " //"); - - fprintf(compiler->verbose, " fmem.%s%s%s ", - (type & SLJIT_MEM_STORE) ? "st" : "ld", - !(type & SLJIT_32) ? ".f64" : ".f32", - (type & SLJIT_MEM_PRE) ? ".pre" : ".post"); + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + if (type & SLJIT_MEM_SUPP) + CHECK_RETURN_OK; + if (sljit_emit_fmem_update(compiler, type | SLJIT_MEM_SUPP, freg, mem, memw) == SLJIT_ERR_UNSUPPORTED) { + fprintf(compiler->verbose, " # fmem: unsupported form, no instructions are emitted\n"); + CHECK_RETURN_OK; + } + + fprintf(compiler->verbose, " %s.%s.%s ", + (type & SLJIT_MEM_STORE) ? "store" : "load", + !(type & SLJIT_32) ? "f64" : "f32", + (type & SLJIT_MEM_POST) ? "post" : "pre"); + sljit_verbose_freg(compiler, freg); fprintf(compiler->verbose, ", "); sljit_verbose_param(compiler, mem, memw); @@ -1960,6 +2226,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fmem(struct sljit_compile } #endif CHECK_RETURN_OK; + } static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset) @@ -2012,6 +2279,10 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_put_label(struct sljit_co CHECK_RETURN_OK; } +#else /* !SLJIT_ARGUMENT_CHECKS && !SLJIT_VERBOSE */ + +#define SLJIT_SKIP_CHECKS(compiler) + #endif /* SLJIT_ARGUMENT_CHECKS || SLJIT_VERBOSE */ #define SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw) \ @@ -2039,46 +2310,11 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_put_label(struct sljit_co ADJUST_LOCAL_OFFSET(dst, dstw); \ ADJUST_LOCAL_OFFSET(src, srcw); -static SLJIT_INLINE sljit_s32 emit_mov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) -{ -#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) - /* At the moment the pointer size is always equal to sljit_sw. May be changed in the future. */ - if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_P)) - return SLJIT_SUCCESS; -#else - if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_U32 || op == SLJIT_MOV_S32 || op == SLJIT_MOV_P)) - return SLJIT_SUCCESS; -#endif - -#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ - || (defined SLJIT_VERBOSE && SLJIT_VERBOSE) - compiler->skip_checks = 1; -#endif - return sljit_emit_op1(compiler, op, SLJIT_RETURN_REG, 0, src, srcw); -} - -#if !(defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC) - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) -{ - CHECK_ERROR(); - CHECK(check_sljit_emit_return(compiler, op, src, srcw)); - - FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); - -#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ - || (defined SLJIT_VERBOSE && SLJIT_VERBOSE) - compiler->skip_checks = 1; -#endif - return sljit_emit_return_void(compiler); -} - -#endif - #if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \ - || (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \ - || ((defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) && !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1 && SLJIT_MIPS_REV < 6)) + || ((defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) && !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1 && SLJIT_MIPS_REV < 6)) \ + || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \ + || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) static SLJIT_INLINE sljit_s32 sljit_emit_cmov_generic(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 dst_reg, @@ -2086,33 +2322,57 @@ static SLJIT_INLINE sljit_s32 sljit_emit_cmov_generic(struct sljit_compiler *com { struct sljit_label *label; struct sljit_jump *jump; - sljit_s32 op = (dst_reg & SLJIT_32) ? SLJIT_MOV32 : SLJIT_MOV; + sljit_s32 op = (type & SLJIT_32) ? SLJIT_MOV32 : SLJIT_MOV; -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - jump = sljit_emit_jump(compiler, type ^ 0x1); + SLJIT_SKIP_CHECKS(compiler); + jump = sljit_emit_jump(compiler, (type & ~SLJIT_32) ^ 0x1); FAIL_IF(!jump); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - FAIL_IF(sljit_emit_op1(compiler, op, dst_reg & ~SLJIT_32, 0, src, srcw)); + SLJIT_SKIP_CHECKS(compiler); + FAIL_IF(sljit_emit_op1(compiler, op, dst_reg, 0, src, srcw)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); label = sljit_emit_label(compiler); FAIL_IF(!label); + sljit_set_label(jump, label); return SLJIT_SUCCESS; } #endif +#if (!(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) || (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)) \ + && !(defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + +static sljit_s32 sljit_emit_mem_unaligned(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + SLJIT_SKIP_CHECKS(compiler); + + if (type & SLJIT_MEM_STORE) + return sljit_emit_op1(compiler, type & (0xff | SLJIT_32), mem, memw, reg, 0); + return sljit_emit_op1(compiler, type & (0xff | SLJIT_32), reg, 0, mem, memw); +} + +#endif /* (!SLJIT_CONFIG_MIPS || SLJIT_MIPS_REV >= 6) && !SLJIT_CONFIG_ARM_V5 */ + +#if (!(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) || (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)) \ + && !(defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) + +static sljit_s32 sljit_emit_fmem_unaligned(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw) +{ + SLJIT_SKIP_CHECKS(compiler); + + if (type & SLJIT_MEM_STORE) + return sljit_emit_fop1(compiler, type & (0xff | SLJIT_32), mem, memw, freg, 0); + return sljit_emit_fop1(compiler, type & (0xff | SLJIT_32), freg, 0, mem, memw); +} + +#endif /* (!SLJIT_CONFIG_MIPS || SLJIT_MIPS_REV >= 6) && !SLJIT_CONFIG_ARM */ + /* CPU description section */ #if (defined SLJIT_32BIT_ARCHITECTURE && SLJIT_32BIT_ARCHITECTURE) @@ -2153,13 +2413,58 @@ static SLJIT_INLINE sljit_s32 sljit_emit_cmov_generic(struct sljit_compiler *com # include "sljitNativePPC_common.c" #elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) # include "sljitNativeMIPS_common.c" -#elif (defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC) -# include "sljitNativeSPARC_common.c" +#elif (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) +# include "sljitNativeRISCV_common.c" #elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) # include "sljitNativeS390X.c" #endif -#if !(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) +static SLJIT_INLINE sljit_s32 emit_mov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) +{ +#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) + /* At the moment the pointer size is always equal to sljit_sw. May be changed in the future. */ + if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_P)) + return SLJIT_SUCCESS; +#else + if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_U32 || op == SLJIT_MOV_S32 || op == SLJIT_MOV_P)) + return SLJIT_SUCCESS; +#endif + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_op1(compiler, op, SLJIT_RETURN_REG, 0, src, srcw); +} + +#if !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \ + && !((defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) && defined __SOFTFP__) + +static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) +{ + if (src == SLJIT_FR0) + return SLJIT_SUCCESS; + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw); +} + +#endif /* !SLJIT_CONFIG_X86_32 && !(SLJIT_CONFIG_ARM_32 && __SOFTFP__) */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_return(compiler, op, src, srcw)); + + if (GET_OPCODE(op) < SLJIT_MOV_F64) { + FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); + } else { + FAIL_IF(emit_fmov_before_return(compiler, op, src, srcw)); + } + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_return_void(compiler); +} + +#if !(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) \ + && !(defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src1, sljit_sw src1w, @@ -2229,20 +2534,33 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler else flags = condition << VARIABLE_FLAG_SHIFT; -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); PTR_FAIL_IF(sljit_emit_op2u(compiler, SLJIT_SUB | flags | (type & SLJIT_32), src1, src1w, src2, src2w)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_jump(compiler, condition | (type & (SLJIT_REWRITABLE_JUMP | SLJIT_32))); } -#endif +#endif /* !SLJIT_CONFIG_MIPS */ + +#if (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM) + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type) +{ + if (type < SLJIT_UNORDERED || type > SLJIT_ORDERED_LESS_EQUAL) + return 0; + + switch (type) { + case SLJIT_UNORDERED_OR_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: + return 0; + } + + return 1; +} + +#endif /* SLJIT_CONFIG_ARM */ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src1, sljit_sw src1w, @@ -2251,61 +2569,65 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compile CHECK_ERROR_PTR(); CHECK_PTR(check_sljit_emit_fcmp(compiler, type, src1, src1w, src2, src2w)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); sljit_emit_fop1(compiler, SLJIT_CMP_F64 | ((type & 0xff) << VARIABLE_FLAG_SHIFT) | (type & SLJIT_32), src1, src1w, src2, src2w); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_jump(compiler, type); } -#if !(defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) \ - && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \ +#if !(defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM) \ && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw) { - SLJIT_UNUSED_ARG(compiler); + CHECK_ERROR(); + CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw)); SLJIT_UNUSED_ARG(type); SLJIT_UNUSED_ARG(reg); SLJIT_UNUSED_ARG(mem); SLJIT_UNUSED_ARG(memw); + return SLJIT_ERR_UNSUPPORTED; +} + +#endif /* !SLJIT_CONFIG_ARM && !SLJIT_CONFIG_PPC */ + +#if !(defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) \ + && !(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw) +{ CHECK_ERROR(); - CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw)); - return SLJIT_ERR_UNSUPPORTED; + return sljit_emit_fmem_unaligned(compiler, type, freg, mem, memw); } -#endif +#endif /* !SLJIT_CONFIG_ARM_32 && !SLJIT_CONFIG_MIPS */ #if !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \ && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw) { - SLJIT_UNUSED_ARG(compiler); + CHECK_ERROR(); + CHECK(check_sljit_emit_fmem_update(compiler, type, freg, mem, memw)); SLJIT_UNUSED_ARG(type); SLJIT_UNUSED_ARG(freg); SLJIT_UNUSED_ARG(mem); SLJIT_UNUSED_ARG(memw); - CHECK_ERROR(); - CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw)); - return SLJIT_ERR_UNSUPPORTED; } -#endif +#endif /* !SLJIT_CONFIG_ARM_64 && !SLJIT_CONFIG_PPC */ #if !(defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) @@ -2316,10 +2638,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *c CHECK(check_sljit_get_local_base(compiler, dst, dstw, offset)); ADJUST_LOCAL_OFFSET(SLJIT_MEM1(SLJIT_SP), offset); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + + SLJIT_SKIP_CHECKS(compiler); + if (offset != 0) return sljit_emit_op2(compiler, SLJIT_ADD, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset); return sljit_emit_op1(compiler, SLJIT_MOV, dst, dstw, SLJIT_SP, 0); @@ -2387,6 +2708,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) return 0; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type) +{ + SLJIT_UNUSED_ARG(type); + SLJIT_UNREACHABLE(); + return 0; +} + SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data) { SLJIT_UNUSED_ARG(code); @@ -2426,6 +2754,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp return SLJIT_ERR_UNSUPPORTED; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNREACHABLE(); + return SLJIT_ERR_UNSUPPORTED; +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { SLJIT_UNUSED_ARG(compiler); @@ -2436,9 +2771,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp return SLJIT_ERR_UNSUPPORTED; } -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler) +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw) { SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } @@ -2505,6 +2842,22 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compil return SLJIT_ERR_UNSUPPORTED; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(src_dst); + SLJIT_UNUSED_ARG(src1); + SLJIT_UNUSED_ARG(src1w); + SLJIT_UNUSED_ARG(src2); + SLJIT_UNUSED_ARG(src2w); + SLJIT_UNREACHABLE(); + return SLJIT_ERR_UNSUPPORTED; +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { @@ -2703,6 +3056,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compile return SLJIT_ERR_UNSUPPORTED; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(reg); + SLJIT_UNUSED_ARG(mem); + SLJIT_UNUSED_ARG(memw); + SLJIT_UNREACHABLE(); + return SLJIT_ERR_UNSUPPORTED; +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw) { SLJIT_UNUSED_ARG(compiler); @@ -2714,6 +3078,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compil return SLJIT_ERR_UNSUPPORTED; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(freg); + SLJIT_UNUSED_ARG(mem); + SLJIT_UNUSED_ARG(memw); + SLJIT_UNREACHABLE(); + return SLJIT_ERR_UNSUPPORTED; +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset) { SLJIT_UNUSED_ARG(compiler); diff --git a/thirdparty/pcre2/src/sljit/sljitLir.h b/thirdparty/pcre2/src/sljit/sljitLir.h index 1162658156..c6a0832ef8 100644 --- a/thirdparty/pcre2/src/sljit/sljitLir.h +++ b/thirdparty/pcre2/src/sljit/sljitLir.h @@ -36,26 +36,24 @@ Advantages: - The execution can be continued from any LIR instruction. In other words, it is possible to jump to any label from anywhere, even from - a code fragment, which is compiled later, if both compiled code - shares the same context. See sljit_emit_enter for more details - - Supports self modifying code: target of (conditional) jump and call + a code fragment, which is compiled later, as long as the compiling + context is the same. See sljit_emit_enter for more details. + - Supports self modifying code: target of any jump and call instructions and some constant values can be dynamically modified - during runtime + during runtime. See SLJIT_REWRITABLE_JUMP. - although it is not suggested to do it frequently - can be used for inline caching: save an important value once in the instruction stream - - since this feature limits the optimization possibilities, a - special flag must be passed at compile time when these - instructions are emitted - A fixed stack space can be allocated for local variables - The compiler is thread-safe - The compiler is highly configurable through preprocessor macros. You can disable unneeded features (multithreading in single threaded applications), and you can use your own system functions - (including memory allocators). See sljitConfig.h + (including memory allocators). See sljitConfig.h. Disadvantages: - - No automatic register allocation, and temporary results are - not stored on the stack. (hence the name comes) + - The compiler is more like a platform independent assembler, so + there is no built-in variable management. Registers and stack must + be managed manually (the name of the compiler refers to this). In practice: - This approach is very effective for interpreters - One of the saved registers typically points to a stack interface @@ -77,7 +75,7 @@ #include "sljitConfig.h" /* The following header file defines useful macros for fine tuning -sljit based code generators. They are listed in the beginning +SLJIT based code generators. They are listed in the beginning of sljitConfigInternal.h */ #include "sljitConfigInternal.h" @@ -90,6 +88,10 @@ of sljitConfigInternal.h */ extern "C" { #endif +/* Version numbers. */ +#define SLJIT_MAJOR_VERSION 0 +#define SLJIT_MINOR_VERSION 95 + /* --------------------------------------------------------------------- */ /* Error codes */ /* --------------------------------------------------------------------- */ @@ -97,33 +99,31 @@ extern "C" { /* Indicates no error. */ #define SLJIT_SUCCESS 0 /* After the call of sljit_generate_code(), the error code of the compiler - is set to this value to avoid future sljit calls (in debug mode at least). + is set to this value to avoid further code generation. The complier should be freed after sljit_generate_code(). */ #define SLJIT_ERR_COMPILED 1 -/* Cannot allocate non executable memory. */ +/* Cannot allocate non-executable memory. */ #define SLJIT_ERR_ALLOC_FAILED 2 /* Cannot allocate executable memory. - Only for sljit_generate_code() */ + Only sljit_generate_code() returns with this error code. */ #define SLJIT_ERR_EX_ALLOC_FAILED 3 /* Return value for SLJIT_CONFIG_UNSUPPORTED placeholder architecture. */ #define SLJIT_ERR_UNSUPPORTED 4 /* An ivalid argument is passed to any SLJIT function. */ #define SLJIT_ERR_BAD_ARGUMENT 5 -/* Dynamic code modification is not enabled. */ -#define SLJIT_ERR_DYN_CODE_MOD 6 /* --------------------------------------------------------------------- */ /* Registers */ /* --------------------------------------------------------------------- */ /* - Scratch (R) registers: registers whose may not preserve their values + Scratch (R) registers: registers which may not preserve their values across function calls. - Saved (S) registers: registers whose preserve their values across + Saved (S) registers: registers which preserve their values across function calls. - The scratch and saved register sets are overlap. The last scratch register + The scratch and saved register sets overlap. The last scratch register is the first saved register, the one before the last is the second saved register, and so on. @@ -209,7 +209,7 @@ extern "C" { /* The SLJIT_SP provides direct access to the linear stack space allocated by sljit_emit_enter. It can only be used in the following form: SLJIT_MEM1(SLJIT_SP). The immediate offset is extended by the relative stack offset automatically. - The sljit_get_local_base can be used to obtain the absolute offset. */ + The sljit_get_local_base can be used to obtain the real address of a value. */ #define SLJIT_SP (SLJIT_NUMBER_OF_REGISTERS + 1) /* Return with machine word. */ @@ -249,6 +249,10 @@ extern "C" { /* Float registers >= SLJIT_FIRST_SAVED_FLOAT_REG are saved registers. */ #define SLJIT_FIRST_SAVED_FLOAT_REG (SLJIT_FS0 - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS + 1) +/* Return with floating point arg. */ + +#define SLJIT_RETURN_FREG SLJIT_FR0 + /* --------------------------------------------------------------------- */ /* Argument type definitions */ /* --------------------------------------------------------------------- */ @@ -386,6 +390,7 @@ struct sljit_label { struct sljit_jump { struct sljit_jump *next; sljit_uw addr; + /* Architecture dependent flags. */ sljit_uw flags; union { sljit_uw target; @@ -423,17 +428,17 @@ struct sljit_compiler { struct sljit_memory_fragment *buf; struct sljit_memory_fragment *abuf; - /* Used scratch registers. */ + /* Available scratch registers. */ sljit_s32 scratches; - /* Used saved registers. */ + /* Available saved registers. */ sljit_s32 saveds; - /* Used float scratch registers. */ + /* Available float scratch registers. */ sljit_s32 fscratches; - /* Used float saved registers. */ + /* Available float saved registers. */ sljit_s32 fsaveds; /* Local stack size. */ sljit_s32 local_size; - /* Code size. */ + /* Maximum code size. */ sljit_uw size; /* Relative offset of the executable mapping from the writable mapping. */ sljit_sw executable_offset; @@ -446,8 +451,6 @@ struct sljit_compiler { #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) sljit_s32 args_size; - sljit_s32 locals_offset; - sljit_s32 scratches_offset; #endif #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) @@ -488,8 +491,7 @@ struct sljit_compiler { sljit_uw args_size; #endif -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - sljit_s32 delay_slot; +#if (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) sljit_s32 cache_arg; sljit_sw cache_argw; #endif @@ -517,7 +519,8 @@ struct sljit_compiler { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ || (defined SLJIT_DEBUG && SLJIT_DEBUG) \ || (defined SLJIT_VERBOSE && SLJIT_VERBOSE) - /* Trust arguments when the API function is called. */ + /* Trust arguments when an API function is called. + Used internally for calling API functions. */ sljit_s32 skip_checks; #endif }; @@ -526,7 +529,7 @@ struct sljit_compiler { /* Main functions */ /* --------------------------------------------------------------------- */ -/* Creates an sljit compiler. The allocator_data is required by some +/* Creates an SLJIT compiler. The allocator_data is required by some custom memory managers. This pointer is passed to SLJIT_MALLOC and SLJIT_FREE macros. Most allocators (including the default one) ignores this value, and it is recommended to pass NULL @@ -540,19 +543,19 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allo /* Frees everything except the compiled machine code. */ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler); -/* Returns the current error code. If an error is occurred, future sljit - calls which uses the same compiler argument returns early with the same +/* Returns the current error code. If an error occurres, future calls + which uses the same compiler argument returns early with the same error code. Thus there is no need for checking the error after every - call, it is enough to do it before the code is compiled. Removing + call, it is enough to do it after the code is compiled. Removing these checks increases the performance of the compiling process. */ static SLJIT_INLINE sljit_s32 sljit_get_compiler_error(struct sljit_compiler *compiler) { return compiler->error; } /* Sets the compiler error code to SLJIT_ERR_ALLOC_FAILED except if an error was detected before. After the error code is set the compiler behaves as if the allocation failure happened - during an sljit function call. This can greatly simplify error - checking, since only the compiler status needs to be checked - after the compilation. */ + during an SLJIT function call. This can greatly simplify error + checking, since it is enough to check the compiler status + after the code is compiled. */ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_compiler_memory_error(struct sljit_compiler *compiler); /* @@ -560,8 +563,8 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_compiler_memory_error(struct sljit_compi and <= 128 bytes on 64 bit architectures. The memory area is owned by the compiler, and freed by sljit_free_compiler. The returned pointer is sizeof(sljit_sw) aligned. Excellent for allocating small blocks during - the compiling, and no need to worry about freeing them. The size is - enough to contain at most 16 pointers. If the size is outside of the range, + compiling, and no need to worry about freeing them. The size is enough + to contain at most 16 pointers. If the size is outside of the range, the function will return with NULL. However, this return value does not indicate that there is no more memory (does not set the current error code of the compiler to out-of-memory status). @@ -574,8 +577,8 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *comp #endif /* - Create executable code from the sljit instruction stream. This is the final step - of the code generation so no more instructions can be added after this call. + Create executable code from the instruction stream. This is the final step + of the code generation so no more instructions can be emitted after this call. */ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler); @@ -606,13 +609,14 @@ static SLJIT_INLINE sljit_sw sljit_get_executable_offset(struct sljit_compiler * static SLJIT_INLINE sljit_uw sljit_get_generated_code_size(struct sljit_compiler *compiler) { return compiler->executable_size; } /* Returns with non-zero if the feature or limitation type passed as its - argument is present on the current CPU. + argument is present on the current CPU. The return value is one, if a + feature is fully supported, and it is two, if partially supported. Some features (e.g. floating point operations) require hardware (CPU) support while others (e.g. move with update) are emulated if not available. - However even if a feature is emulated, specialized code paths can be faster - than the emulation. Some limitations are emulated as well so their general - case is supported but it has extra performance costs. */ + However, even when a feature is emulated, specialized code paths may be + faster than the emulation. Some limitations are emulated as well so their + general case is supported but it has extra performance costs. */ /* [Not emulated] Floating-point support is available. */ #define SLJIT_HAS_FPU 0 @@ -622,10 +626,14 @@ static SLJIT_INLINE sljit_uw sljit_get_generated_code_size(struct sljit_compiler #define SLJIT_HAS_ZERO_REGISTER 2 /* [Emulated] Count leading zero is supported. */ #define SLJIT_HAS_CLZ 3 +/* [Emulated] Count trailing zero is supported. */ +#define SLJIT_HAS_CTZ 4 +/* [Emulated] Rotate left/right is supported. */ +#define SLJIT_HAS_ROT 5 /* [Emulated] Conditional move is supported. */ -#define SLJIT_HAS_CMOV 4 -/* [Emulated] Conditional move is supported. */ -#define SLJIT_HAS_PREFETCH 5 +#define SLJIT_HAS_CMOV 6 +/* [Emulated] Prefetch instruction is available (emulated as a nop). */ +#define SLJIT_HAS_PREFETCH 7 #if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) /* [Not emulated] SSE2 support is available on x86. */ @@ -634,8 +642,23 @@ static SLJIT_INLINE sljit_uw sljit_get_generated_code_size(struct sljit_compiler SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type); -/* Instruction generation. Returns with any error code. If there is no - error, they return with SLJIT_SUCCESS. */ +/* If type is between SLJIT_ORDERED_EQUAL and SLJIT_ORDERED_LESS_EQUAL, + sljit_cmp_info returns one, if the cpu supports the passed floating + point comparison type. + + If type is SLJIT_UNORDERED or SLJIT_ORDERED, sljit_cmp_info returns + one, if the cpu supports checking the unordered comparison result + regardless of the comparison type passed to the comparison instruction. + The returned value is always one, if there is at least one type between + SLJIT_ORDERED_EQUAL and SLJIT_ORDERED_LESS_EQUAL where sljit_cmp_info + returns with a zero value. + + Otherwise it returns zero. */ +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type); + +/* The following functions generate machine code. If there is no + error, they return with SLJIT_SUCCESS, otherwise they return + with an error code. */ /* The executable code is a function from the viewpoint of the C @@ -643,30 +666,29 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) Binary Interface) of the platform, which specify the purpose of machine registers and stack handling among other things. The sljit_emit_enter function emits the necessary instructions for - setting up a new context for the executable code and moves function - arguments to the saved registers. Furthermore the options argument + setting up a new context for the executable code. This is often + called as function prologue. Furthermore the options argument can be used to pass configuration options to the compiler. The available options are listed before sljit_emit_enter. - The function argument list is the combination of SLJIT_ARGx - (SLJIT_DEF_ARG1) macros. Currently maximum 4 arguments are - supported. The first integer argument is loaded into SLJIT_S0, - the second one is loaded into SLJIT_S1, and so on. Similarly, - the first floating point argument is loaded into SLJIT_FR0, - the second one is loaded into SLJIT_FR1, and so on. Furthermore - the register set used by the function must be declared as well. - The number of scratch and saved registers used by the function - must be passed to sljit_emit_enter. Only R registers between R0 + The function argument list is specified by the SLJIT_ARGSx + (SLJIT_ARGS0 .. SLJIT_ARGS4) macros. Currently maximum four + arguments are supported. See the description of SLJIT_ARGSx + macros about argument passing. Furthermore the register set + used by the function must be declared as well. The number of + scratch and saved registers available to the function must + be passed to sljit_emit_enter. Only R registers between R0 and "scratches" argument can be used later. E.g. if "scratches" - is set to 2, the scratch register set will be limited to SLJIT_R0 - and SLJIT_R1. The S registers and the floating point registers - ("fscratches" and "fsaveds") are specified in a similar manner. - The sljit_emit_enter is also capable of allocating a stack space - for local variables. The "local_size" argument contains the size - in bytes of this local area and its staring address is stored - in SLJIT_SP. The memory area between SLJIT_SP (inclusive) and - SLJIT_SP + local_size (exclusive) can be modified freely until - the function returns. The stack space is not initialized. + is set to two, the scratch register set will be limited to + SLJIT_R0 and SLJIT_R1. The S registers and the floating point + registers ("fscratches" and "fsaveds") are specified in a + similar manner. The sljit_emit_enter is also capable of + allocating a stack space for local data. The "local_size" + argument contains the size in bytes of this local area, and + it can be accessed using SLJIT_MEM1(SLJIT_SP). The memory + area between SLJIT_SP (inclusive) and SLJIT_SP + local_size + (exclusive) can be modified freely until the function returns. + The stack space is not initialized to zero. Note: the following conditions must met: 0 <= scratches <= SLJIT_NUMBER_OF_REGISTERS @@ -683,9 +705,20 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) overwrites the previous context. */ -/* The compiled function uses cdecl calling - * convention instead of SLJIT_FUNC. */ -#define SLJIT_ENTER_CDECL 0x00000001 +/* Saved registers between SLJIT_S0 and SLJIT_S(n - 1) (inclusive) + are not saved / restored on function enter / return. Instead, + these registers can be used to pass / return data (such as + global / local context pointers) across function calls. The + value of n must be between 1 and 3. This option is only + supported by SLJIT_ENTER_REG_ARG calling convention. */ +#define SLJIT_ENTER_KEEP(n) (n) + +/* The compiled function uses an SLJIT specific register argument + calling convention. This is a lightweight function call type where + both the caller and the called functions must be compiled by + SLJIT. The type argument of the call must be SLJIT_CALL_REG_ARG + and all arguments must be stored in scratch registers. */ +#define SLJIT_ENTER_REG_ARG 0x00000004 /* The local_size must be >= 0 and <= SLJIT_MAX_LOCAL_SIZE. */ #define SLJIT_MAX_LOCAL_SIZE 65536 @@ -694,12 +727,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size); -/* The machine code has a context (which contains the local stack space size, - number of used registers, etc.) which initialized by sljit_emit_enter. Several - functions (such as sljit_emit_return) requres this context to be able to generate - the appropriate code. However, some code fragments (like inline cache) may have - no normal entry point so their context is unknown for the compiler. Their context - can be provided to the compiler by the sljit_set_context function. +/* The SLJIT compiler has a current context (which contains the local + stack space size, number of used registers, etc.) which is initialized + by sljit_emit_enter. Several functions (such as sljit_emit_return) + requires this context to be able to generate the appropriate code. + However, some code fragments (compiled separately) may have no + normal entry point so their context is unknown for the compiler. + + The sljit_set_context and sljit_emit_enter have the same arguments, + but sljit_set_context does not generate any machine code. Note: every call of sljit_emit_enter and sljit_set_context overwrites the previous context. */ @@ -708,16 +744,29 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size); -/* Return from machine code. The sljit_emit_return_void function does not return with - any value. The sljit_emit_return function returns with a single value which stores - the result of a data move instruction. The instruction is specified by the op - argument, and must be between SLJIT_MOV and SLJIT_MOV_P (see sljit_emit_op1). */ +/* Return to the caller function. The sljit_emit_return_void function + does not return with any value. The sljit_emit_return function returns + with a single value loaded from its source operand. The load operation + can be between SLJIT_MOV and SLJIT_MOV_P (see sljit_emit_op1) and + SLJIT_MOV_F32/SLJIT_MOV_F64 (see sljit_emit_fop1) depending on the + return value specified by sljit_emit_enter/sljit_set_context. */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler); SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw); +/* Restores the saved registers and free the stack area, then the execution + continues from the address specified by the source operand. This + operation is similar to sljit_emit_return, but it ignores the return + address. The code where the exection continues should use the same context + as the caller function (see sljit_set_context). A word (pointer) value + can be passed in the SLJIT_RETURN_REG register. This function can be used + to jump to exception handlers. */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw); + /* Generating entry and exit points for fast call functions (see SLJIT_FAST_CALL). Both sljit_emit_fast_enter and SLJIT_FAST_RETURN operations preserve the values of all registers and stack frame. The return address is stored in the @@ -726,9 +775,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp Fast calls are cheap operations (usually only a single call instruction is emitted) but they do not preserve any registers. However the callee function - can freely use / update any registers and stack values which can be + can freely use / update any registers and the local area which can be efficiently exploited by various optimizations. Registers can be saved - manually by the callee function if needed. + and restored manually if needed. Although returning to different address by SLJIT_FAST_RETURN is possible, this address usually cannot be predicted by the return address predictor of @@ -743,16 +792,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * /* Source and destination operands for arithmetical instructions imm - a simple immediate value (cannot be used as a destination) - reg - any of the registers (immediate argument must be 0) - [imm] - absolute immediate memory address + reg - any of the available registers (immediate argument must be 0) + [imm] - absolute memory address [reg+imm] - indirect memory address [reg+(reg<<imm)] - indirect indexed memory address (shift must be between 0 and 3) - useful for (byte, half, int, sljit_sw) array access - (fully supported by both x86 and ARM architectures, and cheap operation on others) + useful for accessing arrays (fully supported by both x86 and + ARM architectures, and cheap operation on others) */ /* - IMPORTANT NOTE: memory access MUST be naturally aligned unless + IMPORTANT NOTE: memory accesses MUST be naturally aligned unless SLJIT_UNALIGNED macro is defined and its value is 1. length | alignment @@ -792,8 +841,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * Write-back is supported except for one instruction: 32 bit signed load with [reg+imm] addressing mode on 64 bit. mips: [reg+imm], -65536 <= imm <= 65535 - sparc: [reg+imm], -4096 <= imm <= 4095 - [reg+reg] is supported + Write-back is not supported + riscv: [reg+imm], -2048 <= imm <= 2047 + Write-back is not supported s390x: [reg+imm], -2^19 <= imm < 2^19 [reg+reg] is supported Write-back is not supported @@ -805,20 +855,22 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * #define SLJIT_MEM1(r1) (SLJIT_MEM | (r1)) #define SLJIT_MEM2(r1, r2) (SLJIT_MEM | (r1) | ((r2) << 8)) #define SLJIT_IMM 0x40 +#define SLJIT_REG_PAIR(r1, r2) ((r1) | ((r2) << 8)) /* Sets 32 bit operation mode on 64 bit CPUs. This option is ignored on 32 bit CPUs. When this option is set for an arithmetic operation, only - the lower 32 bit of the input registers are used, and the CPU status + the lower 32 bits of the input registers are used, and the CPU status flags are set according to the 32 bit result. Although the higher 32 bit of the input and the result registers are not defined by SLJIT, it might be defined by the CPU architecture (e.g. MIPS). To satisfy these CPU requirements all source registers must be the result of those operations where this option was also set. Memory loads read 32 bit values rather than 64 bit ones. In other words 32 bit and 64 bit operations cannot be - mixed. The only exception is SLJIT_MOV32 whose source register can hold + mixed. The only exception is SLJIT_MOV32 which source register can hold any 32 or 64 bit value, and it is converted to a 32 bit compatible format - first. This conversion is free (no instructions are emitted) on most CPUs. - A 32 bit value can also be converted to a 64 bit value by SLJIT_MOV_S32 + first. When the source and destination registers are the same, this + conversion is free (no instructions are emitted) on most CPUs. A 32 bit + value can also be converted to a 64 bit value by SLJIT_MOV_S32 (sign extension) or SLJIT_MOV_U32 (zero extension). As for floating-point operations, this option sets 32 bit single @@ -835,18 +887,20 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * SLJIT_ADD32 == (SLJIT_ADD | SLJIT_32) */ #define SLJIT_32 0x100 -/* Many CPUs (x86, ARM, PPC) have status flags which can be set according +/* Many CPUs (x86, ARM, PPC) have status flag bits which can be set according to the result of an operation. Other CPUs (MIPS) do not have status - flags, and results must be stored in registers. To cover both architecture - types efficiently only two flags are defined by SLJIT: + flag bits, and results must be stored in registers. To cover both + architecture types efficiently only two flags are defined by SLJIT: * Zero (equal) flag: it is set if the result is zero - * Variable flag: its value is defined by the last arithmetic operation + * Variable flag: its value is defined by the arithmetic operation SLJIT instructions can set any or both of these flags. The value of - these flags is undefined if the instruction does not specify their value. - The description of each instruction contains the list of allowed flag - types. + these flags is undefined if the instruction does not specify their + value. The description of each instruction contains the list of + allowed flag types. + + Note: the logical or operation can be used to set flags. Example: SLJIT_ADD can set the Z, OVERFLOW, CARRY flags hence @@ -867,32 +921,40 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * Sets the variable flag if unsigned overflow (carry) occurs, clears it otherwise. - If an instruction (e.g. SLJIT_MOV) does not modify flags the flags are - unchanged. + Certain instructions (e.g. SLJIT_MOV) does not modify flags, so + status flags are unchanged. - Using these flags can reduce the number of emitted instructions. E.g. a - fast loop can be implemented by decreasing a counter register and set the - zero flag to jump back if the counter register has not reached zero. + Example: - Motivation: although CPUs can set a large number of flags, usually their - values are ignored or only one of them is used. Emulating a large number - of flags on systems without flag register is complicated so SLJIT - instructions must specify the flag they want to use and only that flag - will be emulated. The last arithmetic instruction can be repeated if + sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z, ...) + sljit_op1(..., SLJIT_MOV, ...) + Zero flag is set according to the result of SLJIT_ADD. + + sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z, ...) + sljit_op2(..., SLJIT_ADD, ...) + Zero flag has unknown value. + + These flags can be used for code optimization. E.g. a fast loop can be + implemented by decreasing a counter register and set the zero flag + using a single instruction. The zero register can be used by a + conditional jump to restart the loop. A single comparison can set a + zero and less flags to check if a value is less, equal, or greater + than another value. + + Motivation: although some CPUs can set a large number of flag bits, + usually their values are ignored or only a few of them are used. Emulating + a large number of flags on systems without a flag register is complicated + so SLJIT instructions must specify the flag they want to use and only + that flag is computed. The last arithmetic instruction can be repeated if multiple flags need to be checked. */ /* Set Zero status flag. */ #define SLJIT_SET_Z 0x0200 /* Set the variable status flag if condition is true. - See comparison types. */ + See comparison types (e.g. SLJIT_SET_LESS, SLJIT_SET_F_EQUAL). */ #define SLJIT_SET(condition) ((condition) << 10) -/* Notes: - - you cannot postpone conditional jump instructions except if noted that - the instruction does not set flags (See: SLJIT_KEEP_FLAGS). - - flag combinations: '|' means 'logical or'. */ - /* Starting index of opcodes for sljit_emit_op0. */ #define SLJIT_OP0_BASE 0 @@ -943,10 +1005,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * /* Flags: - (does not modify flags) ENDBR32 instruction for x86-32 and ENDBR64 instruction for x86-64 when Intel Control-flow Enforcement Technology (CET) is enabled. - No instruction for other architectures. */ + No instructions are emitted for other architectures. */ #define SLJIT_ENDBR (SLJIT_OP0_BASE + 8) /* Flags: - (may destroy flags) - Skip stack frames before return. */ + Skip stack frames before return when Intel Control-flow + Enforcement Technology (CET) is enabled. No instructions + are emitted for other architectures. */ #define SLJIT_SKIP_FRAMES_BEFORE_RETURN (SLJIT_OP0_BASE + 9) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op); @@ -990,9 +1054,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile /* Flags: - (does not modify flags) */ #define SLJIT_MOV32 (SLJIT_OP1_BASE + 7) /* Flags: - (does not modify flags) - Note: load a pointer sized data, useful on x32 (a 32 bit mode on x86-64 - where all x64 features are available, e.g. 16 register) or similar - compiling modes */ + Note: loads a pointer sized data, useful on x32 mode (a 64 bit mode + on x86-64 which uses 32 bit pointers) or similar compiling modes */ #define SLJIT_MOV_P (SLJIT_OP1_BASE + 8) /* Flags: Z Note: immediate source argument is not supported */ @@ -1003,6 +1066,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile Note: immediate source argument is not supported */ #define SLJIT_CLZ (SLJIT_OP1_BASE + 10) #define SLJIT_CLZ32 (SLJIT_CLZ | SLJIT_32) +/* Count trailing zeroes + Flags: - (may destroy flags) + Note: immediate source argument is not supported */ +#define SLJIT_CTZ (SLJIT_OP1_BASE + 11) +#define SLJIT_CTZ32 (SLJIT_CTZ | SLJIT_32) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, @@ -1019,7 +1087,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile #define SLJIT_ADDC32 (SLJIT_ADDC | SLJIT_32) /* Flags: Z | LESS | GREATER_EQUAL | GREATER | LESS_EQUAL SIG_LESS | SIG_GREATER_EQUAL | SIG_GREATER - SIG_LESS_EQUAL | CARRY */ + SIG_LESS_EQUAL | OVERFLOW | CARRY */ #define SLJIT_SUB (SLJIT_OP2_BASE + 2) #define SLJIT_SUB32 (SLJIT_SUB | SLJIT_32) /* Flags: CARRY */ @@ -1046,31 +1114,100 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile #define SLJIT_SHL (SLJIT_OP2_BASE + 8) #define SLJIT_SHL32 (SLJIT_SHL | SLJIT_32) /* Flags: Z + Same as SLJIT_SHL, except the the second operand is + always masked by the length of the shift operation. */ +#define SLJIT_MSHL (SLJIT_OP2_BASE + 9) +#define SLJIT_MSHL32 (SLJIT_MSHL | SLJIT_32) +/* Flags: Z Let bit_length be the length of the shift operation: 32 or 64. If src2 is immediate, src2w is masked by (bit_length - 1). Otherwise, if the content of src2 is outside the range from 0 to bit_length - 1, the result is undefined. */ -#define SLJIT_LSHR (SLJIT_OP2_BASE + 9) +#define SLJIT_LSHR (SLJIT_OP2_BASE + 10) #define SLJIT_LSHR32 (SLJIT_LSHR | SLJIT_32) /* Flags: Z + Same as SLJIT_LSHR, except the the second operand is + always masked by the length of the shift operation. */ +#define SLJIT_MLSHR (SLJIT_OP2_BASE + 11) +#define SLJIT_MLSHR32 (SLJIT_MLSHR | SLJIT_32) +/* Flags: Z Let bit_length be the length of the shift operation: 32 or 64. If src2 is immediate, src2w is masked by (bit_length - 1). Otherwise, if the content of src2 is outside the range from 0 to bit_length - 1, the result is undefined. */ -#define SLJIT_ASHR (SLJIT_OP2_BASE + 10) +#define SLJIT_ASHR (SLJIT_OP2_BASE + 12) #define SLJIT_ASHR32 (SLJIT_ASHR | SLJIT_32) +/* Flags: Z + Same as SLJIT_ASHR, except the the second operand is + always masked by the length of the shift operation. */ +#define SLJIT_MASHR (SLJIT_OP2_BASE + 13) +#define SLJIT_MASHR32 (SLJIT_MASHR | SLJIT_32) +/* Flags: - (may destroy flags) + Let bit_length be the length of the rotate operation: 32 or 64. + The second operand is always masked by (bit_length - 1). */ +#define SLJIT_ROTL (SLJIT_OP2_BASE + 14) +#define SLJIT_ROTL32 (SLJIT_ROTL | SLJIT_32) +/* Flags: - (may destroy flags) + Let bit_length be the length of the rotate operation: 32 or 64. + The second operand is always masked by (bit_length - 1). */ +#define SLJIT_ROTR (SLJIT_OP2_BASE + 15) +#define SLJIT_ROTR32 (SLJIT_ROTR | SLJIT_32) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w); -/* The sljit_emit_op2u function is the same as sljit_emit_op2 except the result is discarded. */ +/* The sljit_emit_op2u function is the same as sljit_emit_op2 + except the result is discarded. */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w); +/* Emit a left or right shift operation, where the bits shifted + in comes from a separate source operand. All operands are + interpreted as unsigned integers. + + In the followings the value_mask variable is 31 for 32 bit + operations and word_size - 1 otherwise. + + op must be one of the following operations: + SLJIT_SHL or SLJIT_SHL32: + src_dst <<= src2 + src_dst |= ((src1 >> 1) >> (src2 ^ value_mask)) + SLJIT_MSHL or SLJIT_MSHL32: + src2 &= value_mask + perform the SLJIT_SHL or SLJIT_SHL32 operation + SLJIT_LSHR or SLJIT_LSHR32: + src_dst >>= src2 + src_dst |= ((src1 << 1) << (src2 ^ value_mask)) + SLJIT_MLSHR or SLJIT_MLSHR32: + src2 &= value_mask + perform the SLJIT_LSHR or SLJIT_LSHR32 operation + + op can be combined (or'ed) with SLJIT_SHIFT_INTO_NON_ZERO + + src_dst must be a register which content is updated after + the operation is completed + src1 / src1w contains the bits which shifted into src_dst + src2 / src2w contains the shift amount + + Note: a rotate operation can be performed if src_dst and + src1 are set to the same register + + Flags: - (may destroy flags) */ + +/* The src2 contains a non-zero value. Improves the generated + code on certain architectures, which provides a small + performance improvement. */ +#define SLJIT_SHIFT_INTO_NON_ZERO 0x200 + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w); + /* Starting index of opcodes for sljit_emit_op2. */ #define SLJIT_OP_SRC_BASE 128 @@ -1116,8 +1253,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *comp #define SLJIT_MOV_F64 (SLJIT_FOP1_BASE + 0) #define SLJIT_MOV_F32 (SLJIT_MOV_F64 | SLJIT_32) /* Convert opcodes: CONV[DST_TYPE].FROM[SRC_TYPE] - SRC/DST TYPE can be: D - double, S - single, W - signed word, I - signed int - Rounding mode when the destination is W or I: round towards zero. */ + SRC/DST TYPE can be: F64, F32, S32, SW + Rounding mode when the destination is SW or S32: round towards zero. */ /* Flags: - (may destroy flags) */ #define SLJIT_CONV_F64_FROM_F32 (SLJIT_FOP1_BASE + 1) #define SLJIT_CONV_F32_FROM_F64 (SLJIT_CONV_F64_FROM_F32 | SLJIT_32) @@ -1133,7 +1270,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *comp /* Flags: - (may destroy flags) */ #define SLJIT_CONV_F64_FROM_S32 (SLJIT_FOP1_BASE + 5) #define SLJIT_CONV_F32_FROM_S32 (SLJIT_CONV_F64_FROM_S32 | SLJIT_32) -/* Note: dst is the left and src is the right operand for SLJIT_CMPD. +/* Note: dst is the left and src is the right operand for SLJIT_CMP_F32/64. Flags: EQUAL_F | LESS_F | GREATER_EQUAL_F | GREATER_F | LESS_EQUAL_F */ #define SLJIT_CMP_F64 (SLJIT_FOP1_BASE + 6) #define SLJIT_CMP_F32 (SLJIT_CMP_F64 | SLJIT_32) @@ -1202,46 +1339,75 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compi #define SLJIT_SET_OVERFLOW SLJIT_SET(SLJIT_OVERFLOW) #define SLJIT_NOT_OVERFLOW 11 -/* Unlike other flags, sljit_emit_jump may destroy this flag. */ +/* Unlike other flags, sljit_emit_jump may destroy the carry flag. */ #define SLJIT_CARRY 12 #define SLJIT_SET_CARRY SLJIT_SET(SLJIT_CARRY) #define SLJIT_NOT_CARRY 13 -/* Floating point comparison types. */ -#define SLJIT_EQUAL_F64 14 -#define SLJIT_EQUAL_F32 (SLJIT_EQUAL_F64 | SLJIT_32) -#define SLJIT_SET_EQUAL_F SLJIT_SET(SLJIT_EQUAL_F64) -#define SLJIT_NOT_EQUAL_F64 15 -#define SLJIT_NOT_EQUAL_F32 (SLJIT_NOT_EQUAL_F64 | SLJIT_32) -#define SLJIT_SET_NOT_EQUAL_F SLJIT_SET(SLJIT_NOT_EQUAL_F64) -#define SLJIT_LESS_F64 16 -#define SLJIT_LESS_F32 (SLJIT_LESS_F64 | SLJIT_32) -#define SLJIT_SET_LESS_F SLJIT_SET(SLJIT_LESS_F64) -#define SLJIT_GREATER_EQUAL_F64 17 -#define SLJIT_GREATER_EQUAL_F32 (SLJIT_GREATER_EQUAL_F64 | SLJIT_32) -#define SLJIT_SET_GREATER_EQUAL_F SLJIT_SET(SLJIT_GREATER_EQUAL_F64) -#define SLJIT_GREATER_F64 18 -#define SLJIT_GREATER_F32 (SLJIT_GREATER_F64 | SLJIT_32) -#define SLJIT_SET_GREATER_F SLJIT_SET(SLJIT_GREATER_F64) -#define SLJIT_LESS_EQUAL_F64 19 -#define SLJIT_LESS_EQUAL_F32 (SLJIT_LESS_EQUAL_F64 | SLJIT_32) -#define SLJIT_SET_LESS_EQUAL_F SLJIT_SET(SLJIT_LESS_EQUAL_F64) -#define SLJIT_UNORDERED_F64 20 -#define SLJIT_UNORDERED_F32 (SLJIT_UNORDERED_F64 | SLJIT_32) -#define SLJIT_SET_UNORDERED_F SLJIT_SET(SLJIT_UNORDERED_F64) -#define SLJIT_ORDERED_F64 21 -#define SLJIT_ORDERED_F32 (SLJIT_ORDERED_F64 | SLJIT_32) -#define SLJIT_SET_ORDERED_F SLJIT_SET(SLJIT_ORDERED_F64) +/* Basic floating point comparison types. + + Note: when the comparison result is unordered, their behaviour is unspecified. */ + +#define SLJIT_F_EQUAL 14 +#define SLJIT_SET_F_EQUAL SLJIT_SET(SLJIT_F_EQUAL) +#define SLJIT_F_NOT_EQUAL 15 +#define SLJIT_SET_F_NOT_EQUAL SLJIT_SET(SLJIT_F_NOT_EQUAL) +#define SLJIT_F_LESS 16 +#define SLJIT_SET_F_LESS SLJIT_SET(SLJIT_F_LESS) +#define SLJIT_F_GREATER_EQUAL 17 +#define SLJIT_SET_F_GREATER_EQUAL SLJIT_SET(SLJIT_F_GREATER_EQUAL) +#define SLJIT_F_GREATER 18 +#define SLJIT_SET_F_GREATER SLJIT_SET(SLJIT_F_GREATER) +#define SLJIT_F_LESS_EQUAL 19 +#define SLJIT_SET_F_LESS_EQUAL SLJIT_SET(SLJIT_F_LESS_EQUAL) + +/* Jumps when either argument contains a NaN value. */ +#define SLJIT_UNORDERED 20 +#define SLJIT_SET_UNORDERED SLJIT_SET(SLJIT_UNORDERED) +/* Jumps when neither argument contains a NaN value. */ +#define SLJIT_ORDERED 21 +#define SLJIT_SET_ORDERED SLJIT_SET(SLJIT_ORDERED) + +/* Ordered / unordered floating point comparison types. + + Note: each comparison type has an ordered and unordered form. Some + architectures supports only either of them (see: sljit_cmp_info). */ + +#define SLJIT_ORDERED_EQUAL 22 +#define SLJIT_SET_ORDERED_EQUAL SLJIT_SET(SLJIT_ORDERED_EQUAL) +#define SLJIT_UNORDERED_OR_NOT_EQUAL 23 +#define SLJIT_SET_UNORDERED_OR_NOT_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_NOT_EQUAL) +#define SLJIT_ORDERED_LESS 24 +#define SLJIT_SET_ORDERED_LESS SLJIT_SET(SLJIT_ORDERED_LESS) +#define SLJIT_UNORDERED_OR_GREATER_EQUAL 25 +#define SLJIT_SET_UNORDERED_OR_GREATER_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_GREATER_EQUAL) +#define SLJIT_ORDERED_GREATER 26 +#define SLJIT_SET_ORDERED_GREATER SLJIT_SET(SLJIT_ORDERED_GREATER) +#define SLJIT_UNORDERED_OR_LESS_EQUAL 27 +#define SLJIT_SET_UNORDERED_OR_LESS_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_LESS_EQUAL) + +#define SLJIT_UNORDERED_OR_EQUAL 28 +#define SLJIT_SET_UNORDERED_OR_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_EQUAL) +#define SLJIT_ORDERED_NOT_EQUAL 29 +#define SLJIT_SET_ORDERED_NOT_EQUAL SLJIT_SET(SLJIT_ORDERED_NOT_EQUAL) +#define SLJIT_UNORDERED_OR_LESS 30 +#define SLJIT_SET_UNORDERED_OR_LESS SLJIT_SET(SLJIT_UNORDERED_OR_LESS) +#define SLJIT_ORDERED_GREATER_EQUAL 31 +#define SLJIT_SET_ORDERED_GREATER_EQUAL SLJIT_SET(SLJIT_ORDERED_GREATER_EQUAL) +#define SLJIT_UNORDERED_OR_GREATER 32 +#define SLJIT_SET_UNORDERED_OR_GREATER SLJIT_SET(SLJIT_UNORDERED_OR_GREATER) +#define SLJIT_ORDERED_LESS_EQUAL 33 +#define SLJIT_SET_ORDERED_LESS_EQUAL SLJIT_SET(SLJIT_ORDERED_LESS_EQUAL) /* Unconditional jump types. */ -#define SLJIT_JUMP 22 - /* Fast calling method. See sljit_emit_fast_enter / SLJIT_FAST_RETURN. */ -#define SLJIT_FAST_CALL 23 - /* Called function must be declared with the SLJIT_FUNC attribute. */ -#define SLJIT_CALL 24 - /* Called function must be declared with cdecl attribute. - This is the default attribute for C functions. */ -#define SLJIT_CALL_CDECL 25 +#define SLJIT_JUMP 34 +/* Fast calling method. See sljit_emit_fast_enter / SLJIT_FAST_RETURN. */ +#define SLJIT_FAST_CALL 35 +/* Default C calling convention. */ +#define SLJIT_CALL 36 +/* Called function must be compiled by SLJIT. + See SLJIT_ENTER_REG_ARG option. */ +#define SLJIT_CALL_REG_ARG 37 /* The target can be changed during runtime (see: sljit_set_jump_addr). */ #define SLJIT_REWRITABLE_JUMP 0x1000 @@ -1249,11 +1415,8 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compi the called function returns to the caller of the current function. The stack usage is reduced before the call, but it is not necessarily reduced to zero. In the latter case the compiler needs to allocate space for some - arguments and the return register must be kept as well. - - This feature is highly experimental and not supported on SPARC platform - at the moment. */ -#define SLJIT_CALL_RETURN 0x2000 + arguments and the return address must be stored on the stack as well. */ +#define SLJIT_CALL_RETURN 0x2000 /* Emit a jump instruction. The destination is not set, only the type of the jump. type must be between SLJIT_EQUAL and SLJIT_FAST_CALL @@ -1263,18 +1426,18 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compi SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type); /* Emit a C compiler (ABI) compatible function call. - type must be SLJIT_CALL or SLJIT_CALL_CDECL - type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP and SLJIT_CALL_RETURN - arg_types is the combination of SLJIT_RET / SLJIT_ARGx (SLJIT_DEF_RET / SLJIT_DEF_ARGx) macros + type must be SLJIT_CALL or SLJIT_CALL_REG_ARG + type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP and/or SLJIT_CALL_RETURN + arg_types can be specified by SLJIT_ARGSx (SLJIT_ARG_RETURN / SLJIT_ARG_VALUE) macros Flags: destroy all flags. */ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 arg_types); /* Basic arithmetic comparison. In most architectures it is implemented as - an compare operation followed by a sljit_emit_jump. However some - architectures (i.e: ARM64 or MIPS) may employ special optimizations here. - It is suggested to use this comparison form when appropriate. - type must be between SLJIT_EQUAL and SLJIT_I_SIG_LESS_EQUAL + a compare operation followed by a sljit_emit_jump. However some + architectures (i.e: ARM64 or MIPS) may employ special optimizations + here. It is suggested to use this comparison form when appropriate. + type must be between SLJIT_EQUAL and SLJIT_SIG_LESS_EQUAL type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP Flags: may destroy flags. */ @@ -1283,15 +1446,14 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler sljit_s32 src2, sljit_sw src2w); /* Basic floating point comparison. In most architectures it is implemented as - an SLJIT_FCMP operation (setting appropriate flags) followed by a + a SLJIT_CMP_F32/64 operation (setting appropriate flags) followed by a sljit_emit_jump. However some architectures (i.e: MIPS) may employ special optimizations here. It is suggested to use this comparison form when appropriate. - type must be between SLJIT_EQUAL_F64 and SLJIT_ORDERED_F32 + type must be between SLJIT_F_EQUAL and SLJIT_ORDERED_LESS_EQUAL type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP Flags: destroy flags. - Note: if either operand is NaN, the behaviour is undefined for - types up to SLJIT_S_LESS_EQUAL. */ + Note: when an operand is NaN the behaviour depends on the comparison type. */ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w); @@ -1312,22 +1474,22 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi /* Emit a C compiler (ABI) compatible function call. Direct form: set src to SLJIT_IMM() and srcw to the address Indirect form: any other valid addressing mode - type must be SLJIT_CALL or SLJIT_CALL_CDECL + type must be SLJIT_CALL or SLJIT_CALL_REG_ARG type can be combined (or'ed) with SLJIT_CALL_RETURN - arg_types is the combination of SLJIT_RET / SLJIT_ARGx (SLJIT_DEF_RET / SLJIT_DEF_ARGx) macros + arg_types can be specified by SLJIT_ARGSx (SLJIT_ARG_RETURN / SLJIT_ARG_VALUE) macros Flags: destroy all flags. */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 arg_types, sljit_s32 src, sljit_sw srcw); -/* Perform the operation using the conditional flags as the second argument. - Type must always be between SLJIT_EQUAL and SLJIT_ORDERED_F64. The value - represented by the type is 1, if the condition represented by the type - is fulfilled, and 0 otherwise. +/* Perform an operation using the conditional flags as the second argument. + Type must always be between SLJIT_EQUAL and SLJIT_ORDERED_LESS_EQUAL. + The value represented by the type is 1, if the condition represented + by the type is fulfilled, and 0 otherwise. - If op == SLJIT_MOV, SLJIT_MOV32: + When op is SLJIT_MOV or SLJIT_MOV32: Set dst to the value represented by the type (0 or 1). Flags: - (does not modify flags) - If op == SLJIT_OR, op == SLJIT_AND, op == SLJIT_XOR + When op is SLJIT_AND, SLJIT_AND32, SLJIT_OR, SLJIT_OR32, SLJIT_XOR, or SLJIT_XOR32 Performs the binary operation using dst as the first, and the value represented by type as the second argument. Result is written into dst. Flags: Z (may destroy flags) */ @@ -1339,69 +1501,139 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co if the condition is satisfied. Unlike other arithmetic operations this instruction does not support memory access. - type must be between SLJIT_EQUAL and SLJIT_ORDERED_F64 - dst_reg must be a valid register and it can be combined - with SLJIT_32 to perform a 32 bit arithmetic operation - src must be register or immediate (SLJIT_IMM) + type must be between SLJIT_EQUAL and SLJIT_ORDERED_LESS_EQUAL + type can be combined (or'ed) with SLJIT_32 + dst_reg must be a valid register + src must be a valid register or immediate (SLJIT_IMM) Flags: - (does not modify flags) */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 dst_reg, sljit_s32 src, sljit_sw srcw); +/* The following flags are used by sljit_emit_mem(), sljit_emit_mem_update(), + sljit_emit_fmem(), and sljit_emit_fmem_update(). */ + +/* Memory load operation. This is the default. */ +#define SLJIT_MEM_LOAD 0x000000 +/* Memory store operation. */ +#define SLJIT_MEM_STORE 0x000200 + /* The following flags are used by sljit_emit_mem() and sljit_emit_fmem(). */ +/* Load or stora data from an unaligned (byte aligned) address. */ +#define SLJIT_MEM_UNALIGNED 0x000400 +/* Load or stora data from a 16 bit aligned address. */ +#define SLJIT_MEM_UNALIGNED_16 0x000800 +/* Load or stora data from a 32 bit aligned address. */ +#define SLJIT_MEM_UNALIGNED_32 0x001000 + +/* The following flags are used by sljit_emit_mem_update(), + and sljit_emit_fmem_update(). */ + +/* Base register is updated before the memory access (default). */ +#define SLJIT_MEM_PRE 0x000000 +/* Base register is updated after the memory access. */ +#define SLJIT_MEM_POST 0x000400 + /* When SLJIT_MEM_SUPP is passed, no instructions are emitted. Instead the function returns with SLJIT_SUCCESS if the instruction form is supported and SLJIT_ERR_UNSUPPORTED otherwise. This flag allows runtime checking of available instruction forms. */ -#define SLJIT_MEM_SUPP 0x0200 -/* Memory load operation. This is the default. */ -#define SLJIT_MEM_LOAD 0x0000 -/* Memory store operation. */ -#define SLJIT_MEM_STORE 0x0400 -/* Base register is updated before the memory access. */ -#define SLJIT_MEM_PRE 0x0800 -/* Base register is updated after the memory access. */ -#define SLJIT_MEM_POST 0x1000 - -/* Emit a single memory load or store with update instruction. When the - requested instruction form is not supported by the CPU, it returns - with SLJIT_ERR_UNSUPPORTED instead of emulating the instruction. This - allows specializing tight loops based on the supported instruction - forms (see SLJIT_MEM_SUPP flag). +#define SLJIT_MEM_SUPP 0x000800 + +/* The sljit_emit_mem emits instructions for various memory operations: + + When SLJIT_MEM_UNALIGNED / SLJIT_MEM_UNALIGNED_16 / + SLJIT_MEM_UNALIGNED_32 is set in type argument: + Emit instructions for unaligned memory loads or stores. When + SLJIT_UNALIGNED is not defined, the only way to access unaligned + memory data is using sljit_emit_mem. Otherwise all operations (e.g. + sljit_emit_op1/2, or sljit_emit_fop1/2) supports unaligned access. + In general, the performance of unaligned memory accesses are often + lower than aligned and should be avoided. + + When a pair of registers is passed in reg argument: + Emit instructions for moving data between a register pair and + memory. The register pair can be specified by the SLJIT_REG_PAIR + macro. The first register is loaded from or stored into the + location specified by the mem/memw arguments, and the end address + of this operation is the starting address of the data transfer + between the second register and memory. The type argument must + be SLJIT_MOV. The SLJIT_MEM_UNALIGNED* options are allowed for + this operation. type must be between SLJIT_MOV and SLJIT_MOV_P and can be - combined with SLJIT_MEM_* flags. Either SLJIT_MEM_PRE - or SLJIT_MEM_POST must be specified. - reg is the source or destination register, and must be - different from the base register of the mem operand - mem must be a SLJIT_MEM1() or SLJIT_MEM2() operand + combined (or'ed) with SLJIT_MEM_* flags + reg is a register or register pair, which is the source or + destination of the operation + mem must be a memory operand Flags: - (does not modify flags) */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw); +/* Emit a single memory load or store with update instruction. + When the requested instruction form is not supported by the CPU, + it returns with SLJIT_ERR_UNSUPPORTED instead of emulating the + instruction. This allows specializing tight loops based on + the supported instruction forms (see SLJIT_MEM_SUPP flag). + Absolute address (SLJIT_MEM0) forms are never supported + and the base (first) register specified by the mem argument + must not be SLJIT_SP and must also be different from the + register specified by the reg argument. + + type must be between SLJIT_MOV and SLJIT_MOV_P and can be + combined (or'ed) with SLJIT_MEM_* flags + reg is the source or destination register of the operation + mem must be a memory operand + + Flags: - (does not modify flags) */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw); + /* Same as sljit_emit_mem except the followings: + Loading or storing a pair of registers is not supported. + type must be SLJIT_MOV_F64 or SLJIT_MOV_F32 and can be - combined with SLJIT_MEM_* flags. Either SLJIT_MEM_PRE - or SLJIT_MEM_POST must be specified. - freg is the source or destination floating point register */ + combined (or'ed) with SLJIT_MEM_* flags. + freg is the source or destination floating point register + of the operation + mem must be a memory operand + + Flags: - (does not modify flags) */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw); -/* Copies the base address of SLJIT_SP + offset to dst. The offset can be - anything to negate the effect of relative addressing. For example if an - array of sljit_sw values is stored on the stack from offset 0x40, and R0 - contains the offset of an array item plus 0x120, this item can be - overwritten by two SLJIT instructions: +/* Same as sljit_emit_mem_update except the followings: + + type must be SLJIT_MOV_F64 or SLJIT_MOV_F32 and can be + combined (or'ed) with SLJIT_MEM_* flags + freg is the source or destination floating point register + of the operation + mem must be a memory operand + + Flags: - (does not modify flags) */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw); + +/* Copies the base address of SLJIT_SP + offset to dst. The offset can + represent the starting address of a value in the local data (stack). + The offset is not limited by the local data limits, it can be any value. + For example if an array of bytes are stored on the stack from + offset 0x40, and R0 contains the offset of an array item plus 0x120, + this item can be changed by two SLJIT instructions: sljit_get_local_base(compiler, SLJIT_R1, 0, 0x40 - 0x120); - sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_MEM2(SLJIT_R1, SLJIT_R0), 0, SLJIT_IMM, 0x5); + sljit_emit_op1(compiler, SLJIT_MOV_U8, SLJIT_MEM2(SLJIT_R1, SLJIT_R0), 0, SLJIT_IMM, 0x5); Flags: - (may destroy flags) */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset); @@ -1430,15 +1662,67 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_ta SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset); /* --------------------------------------------------------------------- */ -/* Miscellaneous utility functions */ +/* CPU specific functions */ /* --------------------------------------------------------------------- */ -#define SLJIT_MAJOR_VERSION 0 -#define SLJIT_MINOR_VERSION 94 +/* The following function is a helper function for sljit_emit_op_custom. + It returns with the real machine register index ( >=0 ) of any SLJIT_R, + SLJIT_S and SLJIT_SP registers. + + Note: it returns with -1 for virtual registers (only on x86-32). */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg); + +/* The following function is a helper function for sljit_emit_op_custom. + It returns with the real machine register ( >= 0 ) index of any SLJIT_FR, + and SLJIT_FS register. + + Note: the index is always an even number on ARM-32, MIPS. */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg); + +/* Any instruction can be inserted into the instruction stream by + sljit_emit_op_custom. It has a similar purpose as inline assembly. + The size parameter must match to the instruction size of the target + architecture: + + x86: 0 < size <= 15. The instruction argument can be byte aligned. + Thumb2: if size == 2, the instruction argument must be 2 byte aligned. + if size == 4, the instruction argument must be 4 byte aligned. + Otherwise: size must be 4 and instruction argument must be 4 byte aligned. */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, sljit_u32 size); + +/* Flags were set by a 32 bit operation. */ +#define SLJIT_CURRENT_FLAGS_32 SLJIT_32 + +/* Flags were set by an ADD or ADDC operations. */ +#define SLJIT_CURRENT_FLAGS_ADD 0x01 +/* Flags were set by a SUB, SUBC, or NEG operation. */ +#define SLJIT_CURRENT_FLAGS_SUB 0x02 + +/* Flags were set by sljit_emit_op2u with SLJIT_SUB opcode. + Must be combined with SLJIT_CURRENT_FLAGS_SUB. */ +#define SLJIT_CURRENT_FLAGS_COMPARE 0x04 + +/* Define the currently available CPU status flags. It is usually used after + an sljit_emit_label or sljit_emit_op_custom operations to define which CPU + status flags are available. + + The current_flags must be a valid combination of SLJIT_SET_* and + SLJIT_CURRENT_FLAGS_* constants. */ + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, + sljit_s32 current_flags); + +/* --------------------------------------------------------------------- */ +/* Miscellaneous utility functions */ +/* --------------------------------------------------------------------- */ /* Get the human readable name of the platform. Can be useful on platforms - like ARM, where ARM and Thumb2 functions can be mixed, and - it is useful to know the type of the code generator. */ + like ARM, where ARM and Thumb2 functions can be mixed, and it is useful + to know the type of the code generator. */ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void); /* Portable helper function to get an offset of a member. */ @@ -1532,60 +1816,6 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void); #endif -/* --------------------------------------------------------------------- */ -/* CPU specific functions */ -/* --------------------------------------------------------------------- */ - -/* The following function is a helper function for sljit_emit_op_custom. - It returns with the real machine register index ( >=0 ) of any SLJIT_R, - SLJIT_S and SLJIT_SP registers. - - Note: it returns with -1 for virtual registers (only on x86-32). */ - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg); - -/* The following function is a helper function for sljit_emit_op_custom. - It returns with the real machine register index of any SLJIT_FLOAT register. - - Note: the index is always an even number on ARM (except ARM-64), MIPS, and SPARC. */ - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg); - -/* Any instruction can be inserted into the instruction stream by - sljit_emit_op_custom. It has a similar purpose as inline assembly. - The size parameter must match to the instruction size of the target - architecture: - - x86: 0 < size <= 15. The instruction argument can be byte aligned. - Thumb2: if size == 2, the instruction argument must be 2 byte aligned. - if size == 4, the instruction argument must be 4 byte aligned. - Otherwise: size must be 4 and instruction argument must be 4 byte aligned. */ - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, - void *instruction, sljit_u32 size); - -/* Flags were set by a 32 bit operation. */ -#define SLJIT_CURRENT_FLAGS_32 SLJIT_32 - -/* Flags were set by an ADD or ADDC operations. */ -#define SLJIT_CURRENT_FLAGS_ADD 0x01 -/* Flags were set by a SUB, SUBC, or NEG operation. */ -#define SLJIT_CURRENT_FLAGS_SUB 0x02 - -/* Flags were set by sljit_emit_op2u with SLJIT_SUB opcode. - Must be combined with SLJIT_CURRENT_FLAGS_SUB. */ -#define SLJIT_CURRENT_FLAGS_COMPARE 0x04 - -/* Define the currently available CPU status flags. It is usually used after - an sljit_emit_label or sljit_emit_op_custom operations to define which CPU - status flags are available. - - The current_flags must be a valid combination of SLJIT_SET_* and - SLJIT_CURRENT_FLAGS_* constants. */ - -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, - sljit_s32 current_flags); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/thirdparty/pcre2/src/sljit/sljitNativeARM_32.c b/thirdparty/pcre2/src/sljit/sljitNativeARM_32.c index 7b87f5907a..54b8ade063 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativeARM_32.c +++ b/thirdparty/pcre2/src/sljit/sljitNativeARM_32.c @@ -100,6 +100,8 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define CMP 0xe1400000 #define BKPT 0xe1200070 #define EOR 0xe0200000 +#define LDR 0xe5100000 +#define LDR_POST 0xe4100000 #define MOV 0xe1a00000 #define MUL 0xe0000090 #define MVN 0xe1e00000 @@ -107,10 +109,12 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define ORR 0xe1800000 #define PUSH 0xe92d0000 #define POP 0xe8bd0000 +#define RBIT 0xe6ff0f30 #define RSB 0xe0600000 #define RSC 0xe0e00000 #define SBC 0xe0c00000 #define SMULL 0xe0c00090 +#define STR 0xe5000000 #define SUB 0xe0400000 #define TST 0xe1000000 #define UMULL 0xe0800090 @@ -564,6 +568,7 @@ static SLJIT_INLINE void inline_set_jump_addr(sljit_uw jump_ptr, sljit_sw execut static sljit_uw get_imm(sljit_uw imm); static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_uw imm); +static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg); static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_sw executable_offset, sljit_uw new_constant, sljit_s32 flush_cache) { @@ -955,12 +960,19 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) #endif case SLJIT_HAS_CLZ: + case SLJIT_HAS_ROT: case SLJIT_HAS_CMOV: #if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) + case SLJIT_HAS_CTZ: case SLJIT_HAS_PREFETCH: #endif return 1; +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + case SLJIT_HAS_CTZ: + return 2; +#endif + default: return 0; } @@ -1049,7 +1061,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { sljit_uw imm, offset; - sljit_s32 i, tmp, size, word_arg_count, saved_arg_count; + sljit_s32 i, tmp, size, word_arg_count; + sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options); #ifdef __SOFTFP__ sljit_u32 float_arg_count; #else @@ -1065,7 +1078,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi imm = 0; tmp = SLJIT_S0 - saveds; - for (i = SLJIT_S0; i > tmp; i--) + for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) imm |= (sljit_uw)1 << reg_map[i]; for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) @@ -1082,7 +1095,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi FAIL_IF(push_inst(compiler, 0xe52d0004 | RD(TMP_REG2))); /* Stack must be aligned to 8 bytes: */ - size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); + size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1); if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) { if ((size & SSIZE_OF(sw)) != 0) { @@ -1103,6 +1116,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi local_size = ((size + local_size + 0x7) & ~0x7) - size; compiler->local_size = local_size; + if (options & SLJIT_ENTER_REG_ARG) + arg_types = 0; + arg_types >>= SLJIT_ARG_SHIFT; word_arg_count = 0; saved_arg_count = 0; @@ -1148,8 +1164,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi if (offset < 4 * sizeof(sljit_sw)) FAIL_IF(push_inst(compiler, MOV | RD(tmp) | (offset >> 2))); else - FAIL_IF(push_inst(compiler, data_transfer_insts[WORD_SIZE | LOAD_DATA] | 0x800000 - | RN(SLJIT_SP) | RD(tmp) | (offset + (sljit_uw)size - 4 * sizeof(sljit_sw)))); + FAIL_IF(push_inst(compiler, LDR | 0x800000 | RN(SLJIT_SP) | RD(tmp) | (offset + (sljit_uw)size - 4 * sizeof(sljit_sw)))); break; } @@ -1217,7 +1232,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); + size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1); if ((size & SSIZE_OF(sw)) != 0 && (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG)) size += SSIZE_OF(sw); @@ -1231,8 +1246,11 @@ static sljit_s32 emit_add_sp(struct sljit_compiler *compiler, sljit_uw imm) sljit_uw imm2 = get_imm(imm); if (imm2 == 0) { - FAIL_IF(load_immediate(compiler, TMP_REG2, imm)); - imm2 = RM(TMP_REG2); + imm2 = (imm & ~(sljit_uw)0x3ff) >> 10; + imm = (imm & 0x3ff) >> 2; + + FAIL_IF(push_inst(compiler, ADD | SRC2_IMM | RD(SLJIT_SP) | RN(SLJIT_SP) | 0xb00 | imm2)); + return push_inst(compiler, ADD | SRC2_IMM | RD(SLJIT_SP) | RN(SLJIT_SP) | 0xf00 | (imm & 0xff)); } return push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | imm2); @@ -1241,10 +1259,11 @@ static sljit_s32 emit_add_sp(struct sljit_compiler *compiler, sljit_uw imm) static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size) { sljit_s32 local_size, fscratches, fsaveds, i, tmp; + sljit_s32 restored_reg = 0; sljit_s32 lr_dst = TMP_PC; - sljit_uw reg_list; + sljit_uw reg_list = 0; - SLJIT_ASSERT(reg_map[TMP_REG2] == 14); + SLJIT_ASSERT(reg_map[TMP_REG2] == 14 && frame_size <= 128); local_size = compiler->local_size; fscratches = compiler->fscratches; @@ -1269,45 +1288,84 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit if (frame_size < 0) { lr_dst = TMP_REG2; frame_size = 0; - } else if (frame_size > 0) + } else if (frame_size > 0) { + SLJIT_ASSERT(frame_size == 1 || (frame_size & 0x7) == 0); lr_dst = 0; + frame_size &= ~0x7; + } - reg_list = 0; if (lr_dst != 0) reg_list |= (sljit_uw)1 << reg_map[lr_dst]; tmp = SLJIT_S0 - compiler->saveds; - for (i = SLJIT_S0; i > tmp; i--) - reg_list |= (sljit_uw)1 << reg_map[i]; + i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); + if (tmp < i) { + restored_reg = i; + do { + reg_list |= (sljit_uw)1 << reg_map[i]; + } while (--i > tmp); + } - for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) - reg_list |= (sljit_uw)1 << reg_map[i]; + i = compiler->scratches; + if (i >= SLJIT_FIRST_SAVED_REG) { + restored_reg = i; + do { + reg_list |= (sljit_uw)1 << reg_map[i]; + } while (--i >= SLJIT_FIRST_SAVED_REG); + } + + if (lr_dst == TMP_REG2 && reg_list == 0) { + restored_reg = TMP_REG2; + lr_dst = 0; + } if (lr_dst == 0 && (reg_list & (reg_list - 1)) == 0) { /* The local_size does not include the saved registers. */ - local_size += SSIZE_OF(sw); + tmp = 0; + if (reg_list != 0) { + tmp = 2; + if (local_size <= 0xfff) { + if (local_size == 0) { + SLJIT_ASSERT(restored_reg != TMP_REG2); + if (frame_size == 0) + return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(restored_reg) | 0x800008); + if (frame_size > 2 * SSIZE_OF(sw)) + return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)(frame_size - (2 * SSIZE_OF(sw)))); + } - if (reg_list != 0) - local_size += SSIZE_OF(sw); + FAIL_IF(push_inst(compiler, LDR | 0x800000 | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)local_size)); + tmp = 1; + } else if (frame_size == 0) { + frame_size = (restored_reg == TMP_REG2) ? SSIZE_OF(sw) : 2 * SSIZE_OF(sw); + tmp = 3; + } + + /* Place for the saved register. */ + if (restored_reg != TMP_REG2) + local_size += SSIZE_OF(sw); + } + + /* Place for the lr register. */ + local_size += SSIZE_OF(sw); if (frame_size > local_size) FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 25) | (sljit_uw)(frame_size - local_size))); else if (frame_size < local_size) FAIL_IF(emit_add_sp(compiler, (sljit_uw)(local_size - frame_size))); - if (reg_list == 0) + if (tmp <= 1) return SLJIT_SUCCESS; - if (compiler->saveds > 0) { - SLJIT_ASSERT(reg_list == ((sljit_uw)1 << reg_map[SLJIT_S0])); - lr_dst = SLJIT_S0; - } else { - SLJIT_ASSERT(reg_list == ((sljit_uw)1 << reg_map[SLJIT_FIRST_SAVED_REG])); - lr_dst = SLJIT_FIRST_SAVED_REG; + if (tmp == 2) { + frame_size -= SSIZE_OF(sw); + if (restored_reg != TMP_REG2) + frame_size -= SSIZE_OF(sw); + + return push_inst(compiler, LDR | 0x800000 | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)frame_size); } - return push_inst(compiler, data_transfer_insts[WORD_SIZE | LOAD_DATA] | 0x800000 - | RN(SLJIT_SP) | RD(lr_dst) | (sljit_uw)(frame_size - 2 * SSIZE_OF(sw))); + tmp = (restored_reg == TMP_REG2) ? 0x800004 : 0x800008; + return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)tmp); } if (local_size > 0) @@ -1320,13 +1378,18 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit SLJIT_ASSERT(lr_dst != 0); SLJIT_ASSERT(reg_list == (sljit_uw)1 << reg_map[lr_dst]); - return push_inst(compiler, 0xe49d0004 | RD(lr_dst)); + return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(lr_dst) | 0x800004); } FAIL_IF(push_inst(compiler, POP | reg_list)); + if (frame_size > 0) return push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 25) | ((sljit_uw)frame_size - sizeof(sljit_sw))); - return SLJIT_SUCCESS; + + if (lr_dst != 0) + return SLJIT_SUCCESS; + + return push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 25) | sizeof(sljit_sw)); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler) @@ -1337,28 +1400,38 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler return emit_stack_frame_release(compiler, 0); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_return_to(compiler, src, srcw)); + + if (src & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1)); + src = TMP_REG1; + srcw = 0; + } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | RM(src))); + src = TMP_REG1; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, 1)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); +} + /* --------------------------------------------------------------------- */ /* Operators */ /* --------------------------------------------------------------------- */ -#define EMIT_SHIFT_INS_AND_RETURN(opcode) \ - SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM)); \ - if (compiler->shift_imm != 0x20) { \ - SLJIT_ASSERT(src1 == TMP_REG1); \ - SLJIT_ASSERT(!(flags & ARGS_SWAPPED)); \ - \ - if (compiler->shift_imm != 0) \ - return push_inst(compiler, MOV | (flags & SET_FLAGS) | \ - RD(dst) | (compiler->shift_imm << 7) | (opcode << 5) | RM(src2)); \ - return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst) | RM(src2)); \ - } \ - return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst) \ - | RM8((flags & ARGS_SWAPPED) ? src1 : src2) | (sljit_uw)(opcode << 5) \ - | 0x10 | RM((flags & ARGS_SWAPPED) ? src2 : src1)); - static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, sljit_uw dst, sljit_uw src1, sljit_uw src2) { + sljit_s32 is_masked; + sljit_uw shift_type; + switch (GET_OPCODE(op)) { case SLJIT_MOV: SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED)); @@ -1413,11 +1486,24 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl return push_inst(compiler, MVN | (flags & SET_FLAGS) | RD(dst) | RM(src2)); case SLJIT_CLZ: - SLJIT_ASSERT(!(flags & INV_IMM)); - SLJIT_ASSERT(!(src2 & SRC2_IMM)); + SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM)); FAIL_IF(push_inst(compiler, CLZ | RD(dst) | RM(src2))); return SLJIT_SUCCESS; + case SLJIT_CTZ: + SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM)); + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED)); +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + FAIL_IF(push_inst(compiler, RSB | SRC2_IMM | RD(TMP_REG1) | RN(src2) | 0)); + FAIL_IF(push_inst(compiler, AND | RD(TMP_REG2) | RN(src2) | RM(TMP_REG1))); + FAIL_IF(push_inst(compiler, CLZ | RD(dst) | RM(TMP_REG2))); + FAIL_IF(push_inst(compiler, CMP | SET_FLAGS | SRC2_IMM | RN(dst) | 32)); + return push_inst(compiler, (EOR ^ 0xf0000000) | SRC2_IMM | RD(dst) | RN(dst) | 0x1f); +#else /* !SLJIT_CONFIG_ARM_V5 */ + FAIL_IF(push_inst(compiler, RBIT | RD(dst) | RM(src2))); + return push_inst(compiler, CLZ | RD(dst) | RM(dst)); +#endif /* SLJIT_CONFIG_ARM_V5 */ + case SLJIT_ADD: SLJIT_ASSERT(!(flags & INV_IMM)); @@ -1471,17 +1557,61 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl return push_inst(compiler, EOR | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2))); case SLJIT_SHL: - EMIT_SHIFT_INS_AND_RETURN(0); + case SLJIT_MSHL: + shift_type = 0; + is_masked = GET_OPCODE(op) == SLJIT_MSHL; + break; case SLJIT_LSHR: - EMIT_SHIFT_INS_AND_RETURN(1); + case SLJIT_MLSHR: + shift_type = 1; + is_masked = GET_OPCODE(op) == SLJIT_MLSHR; + break; case SLJIT_ASHR: - EMIT_SHIFT_INS_AND_RETURN(2); + case SLJIT_MASHR: + shift_type = 2; + is_masked = GET_OPCODE(op) == SLJIT_MASHR; + break; + + case SLJIT_ROTL: + if (compiler->shift_imm == 0x20) { + FAIL_IF(push_inst(compiler, RSB | SRC2_IMM | RD(TMP_REG2) | RN(src2) | 0)); + src2 = TMP_REG2; + } else + compiler->shift_imm = (sljit_uw)(-(sljit_sw)compiler->shift_imm) & 0x1f; + /* fallthrough */ + + case SLJIT_ROTR: + shift_type = 3; + is_masked = 0; + break; + + default: + SLJIT_UNREACHABLE(); + return SLJIT_SUCCESS; } - SLJIT_UNREACHABLE(); - return SLJIT_SUCCESS; + SLJIT_ASSERT(!(flags & ARGS_SWAPPED) && !(flags & INV_IMM) && !(src2 & SRC2_IMM)); + + if (compiler->shift_imm != 0x20) { + SLJIT_ASSERT(src1 == TMP_REG1); + + if (compiler->shift_imm != 0) + return push_inst(compiler, MOV | (flags & SET_FLAGS) | + RD(dst) | (compiler->shift_imm << 7) | (shift_type << 5) | RM(src2)); + return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst) | RM(src2)); + } + + SLJIT_ASSERT(src1 != TMP_REG2); + + if (is_masked) { + FAIL_IF(push_inst(compiler, AND | RD(TMP_REG2) | RN(src2) | SRC2_IMM | 0x1f)); + src2 = TMP_REG2; + } + + return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst) + | RM8(src2) | (sljit_uw)(shift_type << 5) | 0x10 | RM(src1)); } #undef EMIT_SHIFT_INS_AND_RETURN @@ -1670,27 +1800,32 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, #endif } -static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, +static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg) { - sljit_uw imm, offset_reg; - sljit_uw is_type1_transfer = IS_TYPE1_TRANSFER(flags); + sljit_uw imm, offset_reg, tmp; + sljit_sw mask = IS_TYPE1_TRANSFER(flags) ? 0xfff : 0xff; + sljit_sw sign = IS_TYPE1_TRANSFER(flags) ? 0x1000 : 0x100; - SLJIT_ASSERT (arg & SLJIT_MEM); - SLJIT_ASSERT((arg & REG_MASK) != tmp_reg); + SLJIT_ASSERT(arg & SLJIT_MEM); + SLJIT_ASSERT((arg & REG_MASK) != tmp_reg || (arg == SLJIT_MEM1(tmp_reg) && argw >= -mask && argw <= mask)); - if (!(arg & REG_MASK)) { - if (is_type1_transfer) { - FAIL_IF(load_immediate(compiler, tmp_reg, (sljit_uw)argw & ~(sljit_uw)0xfff)); - argw &= 0xfff; - } - else { - FAIL_IF(load_immediate(compiler, tmp_reg, (sljit_uw)argw & ~(sljit_uw)0xff)); - argw &= 0xff; + if (SLJIT_UNLIKELY(!(arg & REG_MASK))) { + tmp = (sljit_uw)(argw & (sign | mask)); + tmp = (sljit_uw)((argw + (tmp <= (sljit_uw)sign ? 0 : sign)) & ~mask); + + FAIL_IF(load_immediate(compiler, tmp_reg, tmp)); + + argw -= (sljit_sw)tmp; + tmp = 1; + + if (argw < 0) { + argw = -argw; + tmp = 0; } - return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, tmp_reg, - is_type1_transfer ? argw : TYPE2_TRANSFER_IMM(argw))); + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, tmp, reg, tmp_reg, + (mask == 0xff) ? TYPE2_TRANSFER_IMM(argw) : argw)); } if (arg & OFFS_REG_MASK) { @@ -1698,72 +1833,62 @@ static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit arg &= REG_MASK; argw &= 0x3; - if (argw != 0 && !is_type1_transfer) { + if (argw != 0 && (mask == 0xff)) { FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | RM(offset_reg) | ((sljit_uw)argw << 7))); return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, tmp_reg, TYPE2_TRANSFER_IMM(0))); } /* Bit 25: RM is offset. */ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, - RM(offset_reg) | (is_type1_transfer ? (1 << 25) : 0) | ((sljit_uw)argw << 7))); + RM(offset_reg) | (mask == 0xff ? 0 : (1 << 25)) | ((sljit_uw)argw << 7))); } arg &= REG_MASK; - if (is_type1_transfer) { - if (argw > 0xfff) { - imm = get_imm((sljit_uw)argw & ~(sljit_uw)0xfff); - if (imm) { - FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | imm)); - argw = argw & 0xfff; - arg = tmp_reg; - } - } - else if (argw < -0xfff) { - imm = get_imm((sljit_uw)-argw & ~(sljit_uw)0xfff); - if (imm) { - FAIL_IF(push_inst(compiler, SUB | RD(tmp_reg) | RN(arg) | imm)); - argw = -(-argw & 0xfff); - arg = tmp_reg; - } + if (argw > mask) { + tmp = (sljit_uw)(argw & (sign | mask)); + tmp = (sljit_uw)((argw + (tmp <= (sljit_uw)sign ? 0 : sign)) & ~mask); + imm = get_imm(tmp); + + if (imm) { + FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | imm)); + argw -= (sljit_sw)tmp; + arg = tmp_reg; + + SLJIT_ASSERT(argw >= -mask && argw <= mask); } + } else if (argw < -mask) { + tmp = (sljit_uw)(-argw & (sign | mask)); + tmp = (sljit_uw)((-argw + (tmp <= (sljit_uw)sign ? 0 : sign)) & ~mask); + imm = get_imm(tmp); - if (argw >= 0 && argw <= 0xfff) - return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, argw)); + if (imm) { + FAIL_IF(push_inst(compiler, SUB | RD(tmp_reg) | RN(arg) | imm)); + argw += (sljit_sw)tmp; + arg = tmp_reg; - if (argw < 0 && argw >= -0xfff) - return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 0, reg, arg, -argw)); - } - else { - if (argw > 0xff) { - imm = get_imm((sljit_uw)argw & ~(sljit_uw)0xff); - if (imm) { - FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | imm)); - argw = argw & 0xff; - arg = tmp_reg; - } + SLJIT_ASSERT(argw >= -mask && argw <= mask); } - else if (argw < -0xff) { - imm = get_imm((sljit_uw)-argw & ~(sljit_uw)0xff); - if (imm) { - FAIL_IF(push_inst(compiler, SUB | RD(tmp_reg) | RN(arg) | imm)); - argw = -(-argw & 0xff); - arg = tmp_reg; - } + } + + if (argw <= mask && argw >= -mask) { + if (argw >= 0) { + if (mask == 0xff) + argw = TYPE2_TRANSFER_IMM(argw); + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, argw)); } - if (argw >= 0 && argw <= 0xff) - return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, TYPE2_TRANSFER_IMM(argw))); + argw = -argw; - if (argw < 0 && argw >= -0xff) { - argw = -argw; - return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 0, reg, arg, TYPE2_TRANSFER_IMM(argw))); - } + if (mask == 0xff) + argw = TYPE2_TRANSFER_IMM(argw); + + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 0, reg, arg, argw)); } FAIL_IF(load_immediate(compiler, tmp_reg, (sljit_uw)argw)); return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, - RM(tmp_reg) | (is_type1_transfer ? (1 << 25) : 0))); + RM(tmp_reg) | (mask == 0xff ? 0 : (1 << 25)))); } static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 inp_flags, @@ -1961,15 +2086,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile saved_reg_list[saved_reg_count++] = 1; if (saved_reg_count > 0) { - FAIL_IF(push_inst(compiler, 0xe52d0000 | (saved_reg_count >= 3 ? 16 : 8) + FAIL_IF(push_inst(compiler, STR | 0x2d0000 | (saved_reg_count >= 3 ? 16 : 8) | (saved_reg_list[0] << 12) /* str rX, [sp, #-8/-16]! */)); if (saved_reg_count >= 2) { SLJIT_ASSERT(saved_reg_list[1] < 8); - FAIL_IF(push_inst(compiler, 0xe58d0004 | (saved_reg_list[1] << 12) /* str rX, [sp, #4] */)); + FAIL_IF(push_inst(compiler, STR | 0x8d0004 | (saved_reg_list[1] << 12) /* str rX, [sp, #4] */)); } if (saved_reg_count >= 3) { SLJIT_ASSERT(saved_reg_list[2] < 8); - FAIL_IF(push_inst(compiler, 0xe58d0008 | (saved_reg_list[2] << 12) /* str rX, [sp, #8] */)); + FAIL_IF(push_inst(compiler, STR | 0x8d0008 | (saved_reg_list[2] << 12) /* str rX, [sp, #8] */)); } } @@ -1983,13 +2108,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile if (saved_reg_count > 0) { if (saved_reg_count >= 3) { SLJIT_ASSERT(saved_reg_list[2] < 8); - FAIL_IF(push_inst(compiler, 0xe59d0008 | (saved_reg_list[2] << 12) /* ldr rX, [sp, #8] */)); + FAIL_IF(push_inst(compiler, LDR | 0x8d0008 | (saved_reg_list[2] << 12) /* ldr rX, [sp, #8] */)); } if (saved_reg_count >= 2) { SLJIT_ASSERT(saved_reg_list[1] < 8); - FAIL_IF(push_inst(compiler, 0xe59d0004 | (saved_reg_list[1] << 12) /* ldr rX, [sp, #4] */)); + FAIL_IF(push_inst(compiler, LDR | 0x8d0004 | (saved_reg_list[1] << 12) /* ldr rX, [sp, #4] */)); } - return push_inst(compiler, 0xe49d0000 | (sljit_uw)(saved_reg_count >= 3 ? 16 : 8) + return push_inst(compiler, (LDR ^ (1 << 24)) | 0x8d0000 | (sljit_uw)(saved_reg_count >= 3 ? 16 : 8) | (saved_reg_list[0] << 12) /* ldr rX, [sp], #8/16 */); } return SLJIT_SUCCESS; @@ -2034,6 +2159,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw); case SLJIT_CLZ: + case SLJIT_CTZ: return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src, srcw); } @@ -2069,13 +2195,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, src1, src1w, src2, src2w); case SLJIT_SHL: + case SLJIT_MSHL: case SLJIT_LSHR: + case SLJIT_MLSHR: case SLJIT_ASHR: + case SLJIT_MASHR: + case SLJIT_ROTL: + case SLJIT_ROTR: if (src2 & SLJIT_IMM) { compiler->shift_imm = src2w & 0x1f; return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src1, src1w); - } - else { + } else { compiler->shift_imm = 0x20; return emit_op(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w); } @@ -2091,13 +2221,67 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compil CHECK_ERROR(); CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_s32 is_left; + + CHECK_ERROR(); + CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w)); + + op = GET_OPCODE(op); + is_left = (op == SLJIT_SHL || op == SLJIT_MSHL); + + if (src_dst == src1) { + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_op2(compiler, is_left ? SLJIT_ROTL : SLJIT_ROTR, src_dst, 0, src_dst, 0, src2, src2w); + } + + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + + /* Shift type of ROR is 3. */ + if (src2 & SLJIT_IMM) { + src2w &= 0x1f; + + if (src2w == 0) + return SLJIT_SUCCESS; + } else if (src2 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, src2, src2w, TMP_REG2)); + src2 = TMP_REG2; + } + + if (src1 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1)); + src1 = TMP_REG1; + } else if (src1 & SLJIT_IMM) { + FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)src1w)); + src1 = TMP_REG1; + } + + if (src2 & SLJIT_IMM) { + FAIL_IF(push_inst(compiler, MOV | RD(src_dst) | RM(src_dst) | ((sljit_uw)(is_left ? 0 : 1) << 5) | ((sljit_uw)src2w << 7))); + src2w = (src2w ^ 0x1f) + 1; + return push_inst(compiler, ORR | RD(src_dst) | RN(src_dst) | RM(src1) | ((sljit_uw)(is_left ? 1 : 0) << 5) | ((sljit_uw)src2w << 7)); + } + + if (op == SLJIT_MSHL || op == SLJIT_MLSHR) { + FAIL_IF(push_inst(compiler, AND | SRC2_IMM | RD(TMP_REG2) | RN(src2) | 0x1f)); + src2 = TMP_REG2; + } + + FAIL_IF(push_inst(compiler, MOV | RD(src_dst) | RM8(src2) | ((sljit_uw)(is_left ? 0 : 1) << 5) | 0x10 | RM(src_dst))); + FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | RM(src1) | ((sljit_uw)(is_left ? 1 : 0) << 5) | (1 << 7))); + FAIL_IF(push_inst(compiler, EOR | SRC2_IMM | RD(TMP_REG2) | RN(src2) | 0x1f)); + return push_inst(compiler, ORR | RD(src_dst) | RN(src_dst) | RM(TMP_REG1) | ((sljit_uw)(is_left ? 1 : 0) << 5) | 0x10 | RM8(TMP_REG2)); +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { @@ -2370,7 +2554,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil return SLJIT_SUCCESS; } -#undef FPU_LOAD #undef EMIT_FPU_DATA_TRANSFER /* --------------------------------------------------------------------- */ @@ -2400,11 +2583,15 @@ static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type) { switch (type) { case SLJIT_EQUAL: - case SLJIT_EQUAL_F64: + case SLJIT_F_EQUAL: + case SLJIT_ORDERED_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */ return 0x00000000; case SLJIT_NOT_EQUAL: - case SLJIT_NOT_EQUAL_F64: + case SLJIT_F_NOT_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */ return 0x10000000; case SLJIT_CARRY: @@ -2413,7 +2600,6 @@ static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type) /* fallthrough */ case SLJIT_LESS: - case SLJIT_LESS_F64: return 0x30000000; case SLJIT_NOT_CARRY: @@ -2422,27 +2608,33 @@ static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type) /* fallthrough */ case SLJIT_GREATER_EQUAL: - case SLJIT_GREATER_EQUAL_F64: return 0x20000000; case SLJIT_GREATER: - case SLJIT_GREATER_F64: + case SLJIT_UNORDERED_OR_GREATER: return 0x80000000; case SLJIT_LESS_EQUAL: - case SLJIT_LESS_EQUAL_F64: + case SLJIT_F_LESS_EQUAL: + case SLJIT_ORDERED_LESS_EQUAL: return 0x90000000; case SLJIT_SIG_LESS: + case SLJIT_UNORDERED_OR_LESS: return 0xb0000000; case SLJIT_SIG_GREATER_EQUAL: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: return 0xa0000000; case SLJIT_SIG_GREATER: + case SLJIT_F_GREATER: + case SLJIT_ORDERED_GREATER: return 0xc0000000; case SLJIT_SIG_LESS_EQUAL: + case SLJIT_UNORDERED_OR_LESS_EQUAL: return 0xd0000000; case SLJIT_OVERFLOW: @@ -2450,7 +2642,7 @@ static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type) return 0x10000000; /* fallthrough */ - case SLJIT_UNORDERED_F64: + case SLJIT_UNORDERED: return 0x60000000; case SLJIT_NOT_OVERFLOW: @@ -2458,11 +2650,18 @@ static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type) return 0x00000000; /* fallthrough */ - case SLJIT_ORDERED_F64: + case SLJIT_ORDERED: return 0x70000000; + case SLJIT_F_LESS: + case SLJIT_ORDERED_LESS: + return 0x40000000; + + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + return 0x50000000; + default: - SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_CDECL); + SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_REG_ARG); return 0xe0000000; } } @@ -2639,7 +2838,7 @@ static sljit_s32 softfloat_call_with_args(struct sljit_compiler *compiler, sljit } FAIL_IF(push_inst(compiler, MOV | (offset << 10) | (word_arg_offset >> 2))); } else - FAIL_IF(push_inst(compiler, data_transfer_insts[WORD_SIZE] | 0x800000 | RN(SLJIT_SP) | (word_arg_offset << 10) | (offset - 4 * sizeof(sljit_sw)))); + FAIL_IF(push_inst(compiler, STR | 0x800000 | RN(SLJIT_SP) | (word_arg_offset << 10) | (offset - 4 * sizeof(sljit_sw)))); } break; } @@ -2718,51 +2917,48 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); #ifdef __SOFTFP__ - PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL, &extra_space)); - SLJIT_ASSERT((extra_space & 0x7) == 0); - - if ((type & SLJIT_CALL_RETURN) && extra_space == 0) - type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); + if ((type & 0xff) != SLJIT_CALL_REG_ARG) { + PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL, &extra_space)); + SLJIT_ASSERT((extra_space & 0x7) == 0); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + if ((type & SLJIT_CALL_RETURN) && extra_space == 0) + type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); - jump = sljit_emit_jump(compiler, type); - PTR_FAIL_IF(jump == NULL); + SLJIT_SKIP_CHECKS(compiler); + jump = sljit_emit_jump(compiler, type); + PTR_FAIL_IF(jump == NULL); - if (extra_space > 0) { - if (type & SLJIT_CALL_RETURN) - PTR_FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, - TMP_REG2, SLJIT_SP, extra_space - sizeof(sljit_sw)))); + if (extra_space > 0) { + if (type & SLJIT_CALL_RETURN) + PTR_FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, + TMP_REG2, SLJIT_SP, extra_space - sizeof(sljit_sw)))); - PTR_FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | extra_space)); + PTR_FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | extra_space)); - if (type & SLJIT_CALL_RETURN) { - PTR_FAIL_IF(push_inst(compiler, BX | RM(TMP_REG2))); - return jump; + if (type & SLJIT_CALL_RETURN) { + PTR_FAIL_IF(push_inst(compiler, BX | RM(TMP_REG2))); + return jump; + } } + + SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN)); + PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types)); + return jump; } +#endif /* __SOFTFP__ */ - SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN)); - PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types)); - return jump; -#else /* !__SOFTFP__ */ if (type & SLJIT_CALL_RETURN) { PTR_FAIL_IF(emit_stack_frame_release(compiler, -1)); type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); } - PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); - -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif +#ifndef __SOFTFP__ + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); +#endif /* !__SOFTFP__ */ + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_jump(compiler, type); -#endif /* __SOFTFP__ */ } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) @@ -2822,55 +3018,79 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi src = TMP_REG1; } - if ((type & SLJIT_CALL_RETURN) && (src >= SLJIT_FIRST_SAVED_REG && src <= SLJIT_S0)) { + if ((type & SLJIT_CALL_RETURN) && (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) { FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | RM(src))); src = TMP_REG1; } #ifdef __SOFTFP__ - FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src, &extra_space)); - SLJIT_ASSERT((extra_space & 0x7) == 0); + if ((type & 0xff) != SLJIT_CALL_REG_ARG) { + FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src, &extra_space)); + SLJIT_ASSERT((extra_space & 0x7) == 0); - if ((type & SLJIT_CALL_RETURN) && extra_space == 0) - type = SLJIT_JUMP; + if ((type & SLJIT_CALL_RETURN) && extra_space == 0) + type = SLJIT_JUMP; -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); + FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); - FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); + if (extra_space > 0) { + if (type & SLJIT_CALL_RETURN) + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, + TMP_REG2, SLJIT_SP, extra_space - sizeof(sljit_sw)))); - if (extra_space > 0) { - if (type & SLJIT_CALL_RETURN) - FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, - TMP_REG2, SLJIT_SP, extra_space - sizeof(sljit_sw)))); + FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | extra_space)); - FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | extra_space)); + if (type & SLJIT_CALL_RETURN) + return push_inst(compiler, BX | RM(TMP_REG2)); + } - if (type & SLJIT_CALL_RETURN) - return push_inst(compiler, BX | RM(TMP_REG2)); + SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN)); + return softfloat_post_call_with_args(compiler, arg_types); } +#endif /* __SOFTFP__ */ - SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN)); - return softfloat_post_call_with_args(compiler, arg_types); -#else /* !__SOFTFP__ */ if (type & SLJIT_CALL_RETURN) { FAIL_IF(emit_stack_frame_release(compiler, -1)); type = SLJIT_JUMP; } - FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); - -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif +#ifndef __SOFTFP__ + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); +#endif /* !__SOFTFP__ */ + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_ijump(compiler, type, src, srcw); -#endif /* __SOFTFP__ */ } +#ifdef __SOFTFP__ + +static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) +{ + if (compiler->options & SLJIT_ENTER_REG_ARG) { + if (src == SLJIT_FR0) + return SLJIT_SUCCESS; + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw); + } + + if (FAST_IS_REG(src)) { + if (op & SLJIT_32) + return push_inst(compiler, VMOV | (1 << 20) | RD(SLJIT_R0) | VN(src)); + return push_inst(compiler, VMOV2 | (1 << 20) | RD(SLJIT_R0) | RN(SLJIT_R1) | VM(src)); + } + + SLJIT_SKIP_CHECKS(compiler); + + if (op & SLJIT_32) + return sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, src, srcw); + return sljit_emit_mem(compiler, SLJIT_MOV, SLJIT_REG_PAIR(SLJIT_R0, SLJIT_R1), src, srcw); +} + +#endif /* __SOFTFP__ */ + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 type) @@ -2883,7 +3103,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co ADJUST_LOCAL_OFFSET(dst, dstw); op = GET_OPCODE(op); - cc = get_cc(compiler, type & 0xff); + cc = get_cc(compiler, type); dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG1; if (op < SLJIT_ADD) { @@ -2921,9 +3141,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil CHECK_ERROR(); CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); - dst_reg &= ~SLJIT_32; - - cc = get_cc(compiler, type & 0xff); + cc = get_cc(compiler, type & ~SLJIT_32); if (SLJIT_UNLIKELY(src & SLJIT_IMM)) { tmp = get_imm((sljit_uw)srcw); @@ -2949,16 +3167,285 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil return push_inst(compiler, ((MOV | RD(dst_reg) | RM(src)) & ~COND_MASK) | cc); } +static sljit_s32 update_mem_addr(struct sljit_compiler *compiler, sljit_s32 *mem, sljit_sw *memw, sljit_s32 max_offset) +{ + sljit_s32 arg = *mem; + sljit_sw argw = *memw; + sljit_uw imm, tmp; +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + sljit_sw mask = max_offset >= 0xf00 ? 0xfff : 0xff; + sljit_sw sign = max_offset >= 0xf00 ? 0x1000 : 0x100; +#else /* !SLJIT_CONFIG_ARM_V5 */ + sljit_sw mask = 0xfff; + sljit_sw sign = 0x1000; + + SLJIT_ASSERT(max_offset >= 0xf00); +#endif /* SLJIT_CONFIG_ARM_V5 */ + + *mem = TMP_REG1; + + if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { + *memw = 0; + return push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | ((sljit_uw)(argw & 0x3) << 7)); + } + + arg &= REG_MASK; + + if (arg) { + if (argw <= max_offset && argw >= -mask) { + *mem = arg; + return SLJIT_SUCCESS; + } + + if (argw >= 0) { + tmp = (sljit_uw)(argw & (sign | mask)); + tmp = (sljit_uw)((argw + ((tmp <= (sljit_uw)max_offset || tmp == (sljit_uw)sign) ? 0 : sign)) & ~mask); + imm = get_imm(tmp); + + if (imm) { + *memw = argw - (sljit_sw)tmp; + SLJIT_ASSERT(*memw >= -mask && *memw <= max_offset); + + return push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg) | imm); + } + } else { + tmp = (sljit_uw)(-argw & (sign | mask)); + tmp = (sljit_uw)((-argw + ((tmp <= (sljit_uw)((sign << 1) - max_offset - 1)) ? 0 : sign)) & ~mask); + imm = get_imm(tmp); + + if (imm) { + *memw = argw + (sljit_sw)tmp; + SLJIT_ASSERT(*memw >= -mask && *memw <= max_offset); + + return push_inst(compiler, SUB | RD(TMP_REG1) | RN(arg) | imm); + } + } + } + + tmp = (sljit_uw)(argw & (sign | mask)); + tmp = (sljit_uw)((argw + ((tmp <= (sljit_uw)max_offset || tmp == (sljit_uw)sign) ? 0 : sign)) & ~mask); + *memw = argw - (sljit_sw)tmp; + + FAIL_IF(load_immediate(compiler, TMP_REG1, tmp)); + + if (arg == 0) + return SLJIT_SUCCESS; + + return push_inst(compiler, ADD | RD(TMP_REG1) | RN(TMP_REG1) | RM(arg)); +} + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + +static sljit_s32 sljit_emit_mem_unaligned(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_s32 flags, steps, tmp_reg; + sljit_uw add, shift; + + switch (type & 0xff) { + case SLJIT_MOV_U8: + case SLJIT_MOV_S8: + flags = BYTE_SIZE; + if (!(type & SLJIT_MEM_STORE)) + flags |= LOAD_DATA; + if ((type & 0xff) == SLJIT_MOV_S8) + flags |= SIGNED; + + return emit_op_mem(compiler, flags, reg, mem, memw, TMP_REG1); + + case SLJIT_MOV_U16: + FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 1)); + flags = BYTE_SIZE; + steps = 1; + break; + + case SLJIT_MOV_S16: + FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xff - 1)); + flags = BYTE_SIZE | SIGNED; + steps = 1; + break; + + default: + if (type & SLJIT_MEM_UNALIGNED_32) { + flags = WORD_SIZE; + if (!(type & SLJIT_MEM_STORE)) + flags |= LOAD_DATA; + + return emit_op_mem(compiler, flags, reg, mem, memw, TMP_REG1); + } + + if (!(type & SLJIT_MEM_UNALIGNED_16)) { + FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 3)); + flags = BYTE_SIZE; + steps = 3; + break; + } + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xff - 2)); + + add = 1; + if (memw < 0) { + add = 0; + memw = -memw; + } + + tmp_reg = reg; + + if (type & SLJIT_MEM_STORE) { + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE, add, reg, mem, TYPE2_TRANSFER_IMM(memw)))); + FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(reg) | (16 << 7) | (2 << 4))); + } else { + if (reg == mem) { + SLJIT_ASSERT(reg != TMP_REG1); + tmp_reg = TMP_REG1; + } + + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE | LOAD_DATA, add, tmp_reg, mem, TYPE2_TRANSFER_IMM(memw)))); + } + + if (!add) { + memw -= 2; + if (memw <= 0) { + memw = -memw; + add = 1; + } + } else + memw += 2; + + if (type & SLJIT_MEM_STORE) + return push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE, add, TMP_REG2, mem, TYPE2_TRANSFER_IMM(memw))); + + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE | LOAD_DATA, add, TMP_REG2, mem, TYPE2_TRANSFER_IMM(memw)))); + return push_inst(compiler, ORR | RD(reg) | RN(tmp_reg) | RM(TMP_REG2) | (16 << 7)); + } + + SLJIT_ASSERT(steps > 0); + + add = 1; + if (memw < 0) { + add = 0; + memw = -memw; + } + + if (type & SLJIT_MEM_STORE) { + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE, add, reg, mem, memw))); + FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(reg) | (8 << 7) | (2 << 4))); + + while (1) { + if (!add) { + memw -= 1; + if (memw == 0) + add = 1; + } else + memw += 1; + + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE, add, TMP_REG2, mem, memw))); + + if (--steps == 0) + return SLJIT_SUCCESS; + + FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(TMP_REG2) | (8 << 7) | (2 << 4))); + } + } + + tmp_reg = reg; + + if (reg == mem) { + SLJIT_ASSERT(reg != TMP_REG1); + tmp_reg = TMP_REG1; + } + + shift = 8; + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE | LOAD_DATA, add, tmp_reg, mem, memw))); + + do { + if (!add) { + memw -= 1; + if (memw == 0) + add = 1; + } else + memw += 1; + + if (steps > 1) { + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE | LOAD_DATA, add, TMP_REG2, mem, memw))); + FAIL_IF(push_inst(compiler, ORR | RD(tmp_reg) | RN(tmp_reg) | RM(TMP_REG2) | (shift << 7))); + shift += 8; + } + } while (--steps != 0); + + flags |= LOAD_DATA; + + if (flags & SIGNED) + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(flags, add, TMP_REG2, mem, TYPE2_TRANSFER_IMM(memw)))); + else + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(flags, add, TMP_REG2, mem, memw))); + + return push_inst(compiler, ORR | RD(reg) | RN(tmp_reg) | RM(TMP_REG2) | (shift << 7)); +} + +#endif /* SLJIT_CONFIG_ARM_V5 */ + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw) { sljit_s32 flags; - sljit_uw is_type1_transfer, inst; CHECK_ERROR(); CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + if (!(reg & REG_PAIR_MASK)) { +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + ADJUST_LOCAL_OFFSET(mem, memw); +#endif /* SLJIT_CONFIG_ARM_V5 */ + + return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw); + } + + ADJUST_LOCAL_OFFSET(mem, memw); + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + if (type & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16)) { + FAIL_IF(update_mem_addr(compiler, &mem, &memw, (type & SLJIT_MEM_UNALIGNED_16) ? 0xfff - 6 : 0xfff - 7)); + + if (!(type & SLJIT_MEM_STORE) && REG_PAIR_FIRST(reg) == (mem & REG_MASK)) { + FAIL_IF(sljit_emit_mem_unaligned(compiler, type, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw))); + return sljit_emit_mem_unaligned(compiler, type, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw); + } + + FAIL_IF(sljit_emit_mem_unaligned(compiler, type, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw)); + return sljit_emit_mem_unaligned(compiler, type, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw)); + } +#endif /* SLJIT_CONFIG_ARM_V5 */ + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4)); + + flags = WORD_SIZE; + + if (!(type & SLJIT_MEM_STORE)) { + if (REG_PAIR_FIRST(reg) == (mem & REG_MASK)) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw), TMP_REG1)); + return emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw, TMP_REG1); + } + + flags = WORD_SIZE | LOAD_DATA; + } + + FAIL_IF(emit_op_mem(compiler, flags, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw, TMP_REG1)); + return emit_op_mem(compiler, flags, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw), TMP_REG1); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_s32 flags; + sljit_uw is_type1_transfer, inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw)); + is_type1_transfer = 1; switch (type & 0xff) { @@ -2999,16 +3486,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compile if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) { if (!is_type1_transfer && memw != 0) return SLJIT_ERR_UNSUPPORTED; - } - else { + } else { if (is_type1_transfer) { if (memw > 4095 || memw < -4095) return SLJIT_ERR_UNSUPPORTED; - } - else { - if (memw > 255 || memw < -255) - return SLJIT_ERR_UNSUPPORTED; - } + } else if (memw > 255 || memw < -255) + return SLJIT_ERR_UNSUPPORTED; } if (type & SLJIT_MEM_SUPP) @@ -3022,20 +3505,20 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compile if (is_type1_transfer) inst |= (1 << 25); - if (type & SLJIT_MEM_PRE) - inst |= (1 << 21); - else + if (type & SLJIT_MEM_POST) inst ^= (1 << 24); + else + inst |= (1 << 21); return push_inst(compiler, inst); } inst = EMIT_DATA_TRANSFER(flags, 0, reg, mem & REG_MASK, 0); - if (type & SLJIT_MEM_PRE) - inst |= (1 << 21); - else + if (type & SLJIT_MEM_POST) inst ^= (1 << 24); + else + inst |= (1 << 21); if (is_type1_transfer) { if (memw >= 0) @@ -3054,6 +3537,103 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compile return push_inst(compiler, inst | TYPE2_TRANSFER_IMM((sljit_uw)memw)); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw) +{ +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + sljit_s32 max_offset; + sljit_s32 dst; +#endif /* SLJIT_CONFIG_ARM_V5 */ + + CHECK_ERROR(); + CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw)); + + if (type & SLJIT_MEM_UNALIGNED_32) + return emit_fop_mem(compiler, ((type ^ SLJIT_32) & SLJIT_32) | ((type & SLJIT_MEM_STORE) ? 0 : FPU_LOAD), freg, mem, memw); + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + if (type & SLJIT_MEM_STORE) { + FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | RD(TMP_REG2))); + + if (type & SLJIT_32) + return sljit_emit_mem_unaligned(compiler, SLJIT_MOV | SLJIT_MEM_STORE | (type & SLJIT_MEM_UNALIGNED_16), TMP_REG2, mem, memw); + + max_offset = 0xfff - 7; + if (type & SLJIT_MEM_UNALIGNED_16) + max_offset++; + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, max_offset)); + mem |= SLJIT_MEM; + + FAIL_IF(sljit_emit_mem_unaligned(compiler, SLJIT_MOV | SLJIT_MEM_STORE | (type & SLJIT_MEM_UNALIGNED_16), TMP_REG2, mem, memw)); + + FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | 0x80 | RD(TMP_REG2))); + return sljit_emit_mem_unaligned(compiler, SLJIT_MOV | SLJIT_MEM_STORE | (type & SLJIT_MEM_UNALIGNED_16), TMP_REG2, mem, memw + 4); + } + + max_offset = (type & SLJIT_32) ? 0xfff - 3 : 0xfff - 7; + if (type & SLJIT_MEM_UNALIGNED_16) + max_offset++; + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, max_offset)); + + dst = TMP_REG1; + + /* Stack offset adjustment is not needed because dst + is not stored on the stack when mem is SLJIT_SP. */ + + if (mem == TMP_REG1) { + dst = SLJIT_R3; + + if (compiler->scratches >= 4) + FAIL_IF(push_inst(compiler, STR | (1 << 21) | RN(SLJIT_SP) | RD(SLJIT_R3) | 8)); + } + + mem |= SLJIT_MEM; + + FAIL_IF(sljit_emit_mem_unaligned(compiler, SLJIT_MOV | (type & SLJIT_MEM_UNALIGNED_16), dst, mem, memw)); + FAIL_IF(push_inst(compiler, VMOV | VN(freg) | RD(dst))); + + if (!(type & SLJIT_32)) { + FAIL_IF(sljit_emit_mem_unaligned(compiler, SLJIT_MOV | (type & SLJIT_MEM_UNALIGNED_16), dst, mem, memw + 4)); + FAIL_IF(push_inst(compiler, VMOV | VN(freg) | 0x80 | RD(dst))); + } + + if (dst == SLJIT_R3 && compiler->scratches >= 4) + FAIL_IF(push_inst(compiler, (LDR ^ (0x1 << 24)) | (0x1 << 23) | RN(SLJIT_SP) | RD(SLJIT_R3) | 8)); + return SLJIT_SUCCESS; +#else /* !SLJIT_CONFIG_ARM_V5 */ + if (type & SLJIT_MEM_STORE) { + FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | RD(TMP_REG2))); + + if (type & SLJIT_32) + return emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1); + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4)); + mem |= SLJIT_MEM; + + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1)); + FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | 0x80 | RD(TMP_REG2))); + return emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw + 4, TMP_REG1); + } + + if (type & SLJIT_32) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, mem, memw, TMP_REG1)); + return push_inst(compiler, VMOV | VN(freg) | RD(TMP_REG2)); + } + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4)); + mem |= SLJIT_MEM; + + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, mem, memw, TMP_REG1)); + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, mem, memw + 4, TMP_REG1)); + return push_inst(compiler, VMOV2 | VM(freg) | RD(TMP_REG2) | RN(TMP_REG1)); +#endif /* SLJIT_CONFIG_ARM_V5 */ +} + +#undef FPU_LOAD + SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) { struct sljit_const *const_; diff --git a/thirdparty/pcre2/src/sljit/sljitNativeARM_64.c b/thirdparty/pcre2/src/sljit/sljitNativeARM_64.c index 96453b4abe..89f747e7c8 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativeARM_64.c +++ b/thirdparty/pcre2/src/sljit/sljitNativeARM_64.c @@ -86,6 +86,7 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define CSINC 0x9a800400 #define EOR 0xca000000 #define EORI 0xd2000000 +#define EXTR 0x93c00000 #define FABS 0x1e60c000 #define FADD 0x1e602800 #define FCMP 0x1e602000 @@ -98,6 +99,7 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define FSUB 0x1e603800 #define LDRI 0xf9400000 #define LDRI_F64 0xfd400000 +#define LDRI_POST 0xf8400400 #define LDP 0xa9400000 #define LDP_F64 0x6d400000 #define LDP_POST 0xa8c00000 @@ -112,7 +114,9 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define ORN 0xaa200000 #define ORR 0xaa000000 #define ORRI 0xb2000000 +#define RBIT 0xdac00000 #define RET 0xd65f0000 +#define RORV 0x9ac02c00 #define SBC 0xda000000 #define SBFM 0x93000000 #define SCVTF 0x9e620000 @@ -137,8 +141,6 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define UDIV 0x9ac00800 #define UMULH 0x9bc03c00 -/* dest_reg is the absolute name of the register - Useful for reordering instructions in the delay slot. */ static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins) { sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins)); @@ -296,8 +298,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } next_addr = compute_next_addr(label, jump, const_, put_label); } - code_ptr ++; - word_count ++; + code_ptr++; + word_count++; } while (buf_ptr < buf_end); buf = buf->next; @@ -391,6 +393,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) #endif case SLJIT_HAS_CLZ: + case SLJIT_HAS_CTZ: + case SLJIT_HAS_ROT: case SLJIT_HAS_CMOV: case SLJIT_HAS_PREFETCH: return 1; @@ -631,6 +635,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s switch (op) { case SLJIT_MUL: case SLJIT_CLZ: + case SLJIT_CTZ: case SLJIT_ADDC: case SLJIT_SUBC: /* No form with immediate operand (except imm 0, which @@ -701,36 +706,50 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s FAIL_IF(push_inst(compiler, (inst_bits ^ inv_bits) | RD(dst) | RN(reg))); goto set_flags; case SLJIT_SHL: + case SLJIT_MSHL: if (flags & ARG1_IMM) break; + if (flags & INT_OP) { imm &= 0x1f; - FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(dst) | RN(arg1) - | (((sljit_ins)-imm & 0x1f) << 16) | ((31 - (sljit_ins)imm) << 10))); - } - else { + inst_bits = (((sljit_ins)-imm & 0x1f) << 16) | ((31 - (sljit_ins)imm) << 10); + } else { imm &= 0x3f; - FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(dst) | RN(arg1) | (1 << 22) - | (((sljit_ins)-imm & 0x3f) << 16) | ((63 - (sljit_ins)imm) << 10))); + inst_bits = ((sljit_ins)1 << 22) | (((sljit_ins)-imm & 0x3f) << 16) | ((63 - (sljit_ins)imm) << 10); } + + FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(dst) | RN(arg1) | inst_bits)); goto set_flags; case SLJIT_LSHR: + case SLJIT_MLSHR: case SLJIT_ASHR: + case SLJIT_MASHR: if (flags & ARG1_IMM) break; - if (op == SLJIT_ASHR) + + if (op >= SLJIT_ASHR) inv_bits |= 1 << 30; + if (flags & INT_OP) { imm &= 0x1f; - FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(dst) | RN(arg1) - | ((sljit_ins)imm << 16) | (31 << 10))); - } - else { + inst_bits = ((sljit_ins)imm << 16) | (31 << 10); + } else { imm &= 0x3f; - FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(dst) | RN(arg1) - | (1 << 22) | ((sljit_ins)imm << 16) | (63 << 10))); + inst_bits = ((sljit_ins)1 << 22) | ((sljit_ins)imm << 16) | (63 << 10); } + + FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(dst) | RN(arg1) | inst_bits)); goto set_flags; + case SLJIT_ROTL: + case SLJIT_ROTR: + if (flags & ARG1_IMM) + break; + + if (op == SLJIT_ROTL) + imm = -imm; + + imm &= (flags & INT_OP) ? 0x1f : 0x3f; + return push_inst(compiler, (EXTR ^ (inv_bits | (inv_bits >> 9))) | RD(dst) | RN(arg1) | RM(arg1) | ((sljit_ins)imm << 10)); default: SLJIT_UNREACHABLE(); break; @@ -796,6 +815,10 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s case SLJIT_CLZ: SLJIT_ASSERT(arg1 == TMP_REG1); return push_inst(compiler, (CLZ ^ inv_bits) | RD(dst) | RN(arg2)); + case SLJIT_CTZ: + SLJIT_ASSERT(arg1 == TMP_REG1); + FAIL_IF(push_inst(compiler, (RBIT ^ inv_bits) | RD(dst) | RN(arg2))); + return push_inst(compiler, (CLZ ^ inv_bits) | RD(dst) | RN(dst)); case SLJIT_ADD: compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD; CHECK_FLAGS(1 << 29); @@ -834,14 +857,23 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s FAIL_IF(push_inst(compiler, (EOR ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2))); break; /* Set flags. */ case SLJIT_SHL: + case SLJIT_MSHL: FAIL_IF(push_inst(compiler, (LSLV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2))); break; /* Set flags. */ case SLJIT_LSHR: + case SLJIT_MLSHR: FAIL_IF(push_inst(compiler, (LSRV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2))); break; /* Set flags. */ case SLJIT_ASHR: + case SLJIT_MASHR: FAIL_IF(push_inst(compiler, (ASRV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2))); break; /* Set flags. */ + case SLJIT_ROTL: + FAIL_IF(push_inst(compiler, (SUB ^ inv_bits) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(arg2))); + arg2 = TMP_REG2; + /* fallthrough */ + case SLJIT_ROTR: + return push_inst(compiler, (RORV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)); default: SLJIT_UNREACHABLE(); return SLJIT_SUCCESS; @@ -895,21 +927,37 @@ static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, s return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | ((sljit_ins)argw << 10)); } - if (argw >= 0 && (argw & ((1 << shift) - 1)) == 0) { - if ((argw >> shift) <= 0xfff) - return push_inst(compiler, STRBI | type | RT(reg) | RN(arg) | ((sljit_ins)argw << (10 - shift))); + if ((argw & ((1 << shift) - 1)) == 0) { + if (argw >= 0) { + if ((argw >> shift) <= 0xfff) + return push_inst(compiler, STRBI | type | RT(reg) | RN(arg) | ((sljit_ins)argw << (10 - shift))); - if (argw <= 0xffffff) { - FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)argw >> 12) << 10))); + if (argw <= 0xffffff) { + FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)argw >> 12) << 10))); - argw = ((argw & 0xfff) >> shift); + argw = ((argw & 0xfff) >> shift); + return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | ((sljit_ins)argw << 10)); + } + } else if (argw < -256 && argw >= -0xfff000) { + FAIL_IF(push_inst(compiler, SUBI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)(-argw + 0xfff) >> 12) << 10))); + argw = ((0x1000 + argw) & 0xfff) >> shift; return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | ((sljit_ins)argw << 10)); } } - if (argw <= 255 && argw >= -256) + if (argw <= 0xff && argw >= -0x100) return push_inst(compiler, STURBI | type | RT(reg) | RN(arg) | (((sljit_ins)argw & 0x1ff) << 12)); + if (argw >= 0) { + if (argw <= 0xfff0ff && ((argw + 0x100) & 0xfff) <= 0x1ff) { + FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)argw >> 12) << 10))); + return push_inst(compiler, STURBI | type | RT(reg) | RN(tmp_reg) | (((sljit_ins)argw & 0x1ff) << 12)); + } + } else if (argw >= -0xfff100 && ((-argw + 0xff) & 0xfff) <= 0x1ff) { + FAIL_IF(push_inst(compiler, SUBI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)-argw >> 12) << 10))); + return push_inst(compiler, STURBI | type | RT(reg) | RN(tmp_reg) | (((sljit_ins)argw & 0x1ff) << 12)); + } + FAIL_IF(load_immediate(compiler, tmp_reg, argw)); return push_inst(compiler, STRB | type | RT(reg) | RN(arg) | RM(tmp_reg)); @@ -924,14 +972,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { sljit_s32 prev, fprev, saved_regs_size, i, tmp; - sljit_s32 word_arg_count = 0; + sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options); sljit_ins offs; CHECK_ERROR(); CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 2); + saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 2); saved_regs_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, SSIZE_OF(f64)); local_size = (local_size + saved_regs_size + 0xf) & ~0xf; @@ -954,7 +1002,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi prev = -1; tmp = SLJIT_S0 - saveds; - for (i = SLJIT_S0; i > tmp; i--) { + for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) { if (prev == -1) { prev = i; continue; @@ -1003,23 +1051,27 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi if (prev != -1) FAIL_IF(push_inst(compiler, STRI | RT(prev) | RN(SLJIT_SP) | (offs >> 5) | ((fprev == -1) ? (1 << 10) : 0))); - arg_types >>= SLJIT_ARG_SHIFT; #ifdef _WIN32 if (local_size > 4096) FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 10) | (1 << 22))); #endif /* _WIN32 */ - tmp = 0; - while (arg_types > 0) { - if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) { - if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) { - FAIL_IF(push_inst(compiler, ORR | RD(SLJIT_S0 - tmp) | RN(TMP_ZERO) | RM(SLJIT_R0 + word_arg_count))); + if (!(options & SLJIT_ENTER_REG_ARG)) { + arg_types >>= SLJIT_ARG_SHIFT; + saved_arg_count = 0; + tmp = SLJIT_R0; + + while (arg_types) { + if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) { + if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) { + FAIL_IF(push_inst(compiler, ORR | RD(SLJIT_S0 - saved_arg_count) | RN(TMP_ZERO) | RM(tmp))); + saved_arg_count++; + } tmp++; } - word_arg_count++; + arg_types >>= SLJIT_ARG_SHIFT; } - arg_types >>= SLJIT_ARG_SHIFT; } #ifdef _WIN32 @@ -1100,26 +1152,34 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 2); + saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 2); saved_regs_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, SSIZE_OF(f64)); compiler->local_size = (local_size + saved_regs_size + 0xf) & ~0xf; return SLJIT_SUCCESS; } -static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) +static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to) { sljit_s32 local_size, prev, fprev, i, tmp; sljit_ins offs; local_size = compiler->local_size; - if (local_size > 512 && local_size <= 512 + 496) { - FAIL_IF(push_inst(compiler, LDP_POST | RT(TMP_FP) | RT2(TMP_LR) - | RN(SLJIT_SP) | ((sljit_ins)(local_size - 512) << (15 - 3)))); - local_size = 512; - } else - FAIL_IF(push_inst(compiler, LDP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP))); + if (!is_return_to) { + if (local_size > 512 && local_size <= 512 + 496) { + FAIL_IF(push_inst(compiler, LDP_POST | RT(TMP_FP) | RT2(TMP_LR) + | RN(SLJIT_SP) | ((sljit_ins)(local_size - 512) << (15 - 3)))); + local_size = 512; + } else + FAIL_IF(push_inst(compiler, LDP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP))); + } else { + if (local_size > 512 && local_size <= 512 + 248) { + FAIL_IF(push_inst(compiler, LDRI_POST | RT(TMP_FP) | RN(SLJIT_SP) | ((sljit_ins)(local_size - 512) << 12))); + local_size = 512; + } else + FAIL_IF(push_inst(compiler, LDRI | RT(TMP_FP) | RN(SLJIT_SP) | 0)); + } if (local_size > 512) { local_size -= 512; @@ -1137,7 +1197,7 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) prev = -1; tmp = SLJIT_S0 - compiler->saveds; - for (i = SLJIT_S0; i > tmp; i--) { + for (i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); i > tmp; i--) { if (prev == -1) { prev = i; continue; @@ -1195,11 +1255,34 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler CHECK_ERROR(); CHECK(check_sljit_emit_return_void(compiler)); - FAIL_IF(emit_stack_frame_release(compiler)); + FAIL_IF(emit_stack_frame_release(compiler, 0)); return push_inst(compiler, RET | RN(TMP_LR)); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_return_to(compiler, src, srcw)); + + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1)); + src = TMP_REG1; + srcw = 0; + } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + FAIL_IF(push_inst(compiler, ORR | RD(TMP_REG1) | RN(TMP_ZERO) | RM(src))); + src = TMP_REG1; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, 1)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); +} + /* --------------------------------------------------------------------- */ /* Operators */ /* --------------------------------------------------------------------- */ @@ -1392,13 +1475,84 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compil CHECK_ERROR(); CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_op2(compiler, op, TMP_REG1, 0, src1, src1w, src2, src2w); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_ins inv_bits, imm; + sljit_s32 is_left; + sljit_sw mask; + + CHECK_ERROR(); + CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w)); + + is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL); + + if (src_dst == src1) { + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_op2(compiler, (is_left ? SLJIT_ROTL : SLJIT_ROTR) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w); + } + + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + + inv_bits = (op & SLJIT_32) ? W_OP : 0; + mask = inv_bits ? 0x1f : 0x3f; + + if (src2 & SLJIT_IMM) { + src2w &= mask; + + if (src2w == 0) + return SLJIT_SUCCESS; + } else if (src2 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG2, src2, src2w, TMP_REG2)); + src2 = TMP_REG2; + } + + if (src1 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG1, src1, src1w, TMP_REG1)); + src1 = TMP_REG1; + } else if (src1 & SLJIT_IMM) { + FAIL_IF(load_immediate(compiler, TMP_REG1, src1w)); + src1 = TMP_REG1; + } + + if (src2 & SLJIT_IMM) { + if (is_left) + src2w = (src2w ^ mask) + 1; + + return push_inst(compiler, (EXTR ^ (inv_bits | (inv_bits >> 9))) | RD(src_dst) + | RN(is_left ? src_dst : src1) | RM(is_left ? src1 : src_dst) | ((sljit_ins)src2w << 10)); + } + + FAIL_IF(push_inst(compiler, ((is_left ? LSLV : LSRV) ^ inv_bits) | RD(src_dst) | RN(src_dst) | RM(src2))); + + if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) { + /* Shift left/right by 1. */ + if (is_left) + imm = (sljit_ins)(inv_bits ? ((1 << 16) | (31 << 10)) : ((1 << 16) | (63 << 10) | (1 << 22))); + else + imm = (sljit_ins)(inv_bits ? ((31 << 16) | (30 << 10)) : ((63 << 16) | (62 << 10) | (1 << 22))); + + FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(TMP_REG1) | RN(src1) | imm)); + + /* Set imm to mask. */ + imm = (sljit_ins)(inv_bits ? (4 << 10) : ((5 << 10) | (1 << 22))); + FAIL_IF(push_inst(compiler, (EORI ^ inv_bits) | RD(TMP_REG2) | RN(src2) | imm)); + + src1 = TMP_REG1; + } else + FAIL_IF(push_inst(compiler, (SUB ^ inv_bits) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(src2))); + + FAIL_IF(push_inst(compiler, ((is_left ? LSRV : LSLV) ^ inv_bits) | RD(TMP_REG1) | RN(src1) | RM(TMP_REG2))); + return push_inst(compiler, (ORR ^ inv_bits) | RD(src_dst) | RN(src_dst) | RM(TMP_REG1)); +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { @@ -1550,10 +1704,9 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) ? INT_SIZE : WORD_SIZE), TMP_REG1, src, srcw, TMP_REG1); src = TMP_REG1; } else if (src & SLJIT_IMM) { -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) srcw = (sljit_s32)srcw; -#endif + FAIL_IF(load_immediate(compiler, TMP_REG1, srcw)); src = TMP_REG1; } @@ -1699,11 +1852,15 @@ static sljit_ins get_cc(struct sljit_compiler *compiler, sljit_s32 type) { switch (type) { case SLJIT_EQUAL: - case SLJIT_EQUAL_F64: + case SLJIT_F_EQUAL: + case SLJIT_ORDERED_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */ return 0x1; case SLJIT_NOT_EQUAL: - case SLJIT_NOT_EQUAL_F64: + case SLJIT_F_NOT_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */ return 0x0; case SLJIT_CARRY: @@ -1712,7 +1869,6 @@ static sljit_ins get_cc(struct sljit_compiler *compiler, sljit_s32 type) /* fallthrough */ case SLJIT_LESS: - case SLJIT_LESS_F64: return 0x2; case SLJIT_NOT_CARRY: @@ -1721,27 +1877,33 @@ static sljit_ins get_cc(struct sljit_compiler *compiler, sljit_s32 type) /* fallthrough */ case SLJIT_GREATER_EQUAL: - case SLJIT_GREATER_EQUAL_F64: return 0x3; case SLJIT_GREATER: - case SLJIT_GREATER_F64: + case SLJIT_UNORDERED_OR_GREATER: return 0x9; case SLJIT_LESS_EQUAL: - case SLJIT_LESS_EQUAL_F64: + case SLJIT_F_LESS_EQUAL: + case SLJIT_ORDERED_LESS_EQUAL: return 0x8; case SLJIT_SIG_LESS: + case SLJIT_UNORDERED_OR_LESS: return 0xa; case SLJIT_SIG_GREATER_EQUAL: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: return 0xb; case SLJIT_SIG_GREATER: + case SLJIT_F_GREATER: + case SLJIT_ORDERED_GREATER: return 0xd; case SLJIT_SIG_LESS_EQUAL: + case SLJIT_UNORDERED_OR_LESS_EQUAL: return 0xc; case SLJIT_OVERFLOW: @@ -1749,7 +1911,7 @@ static sljit_ins get_cc(struct sljit_compiler *compiler, sljit_s32 type) return 0x0; /* fallthrough */ - case SLJIT_UNORDERED_F64: + case SLJIT_UNORDERED: return 0x7; case SLJIT_NOT_OVERFLOW: @@ -1757,9 +1919,16 @@ static sljit_ins get_cc(struct sljit_compiler *compiler, sljit_s32 type) return 0x1; /* fallthrough */ - case SLJIT_ORDERED_F64: + case SLJIT_ORDERED: return 0x6; + case SLJIT_F_LESS: + case SLJIT_ORDERED_LESS: + return 0x5; + + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + return 0x4; + default: SLJIT_UNREACHABLE(); return 0xe; @@ -1816,15 +1985,11 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); if (type & SLJIT_CALL_RETURN) { - PTR_FAIL_IF(emit_stack_frame_release(compiler)); + PTR_FAIL_IF(emit_stack_frame_release(compiler, 0)); type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); } -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_jump(compiler, type); } @@ -1869,10 +2034,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi CHECK_ERROR(); CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); - ADJUST_LOCAL_OFFSET(src, srcw); if (!(src & SLJIT_IMM)) { if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1)); src = TMP_REG1; } @@ -1897,28 +2062,24 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi SLJIT_UNUSED_ARG(arg_types); CHECK_ERROR(); CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); - ADJUST_LOCAL_OFFSET(src, srcw); if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1)); src = TMP_REG1; } if (type & SLJIT_CALL_RETURN) { - if (src >= SLJIT_FIRST_SAVED_REG && src <= SLJIT_S0) { + if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { FAIL_IF(push_inst(compiler, ORR | RD(TMP_REG1) | RN(TMP_ZERO) | RM(src))); src = TMP_REG1; } - FAIL_IF(emit_stack_frame_release(compiler)); + FAIL_IF(emit_stack_frame_release(compiler, 0)); type = SLJIT_JUMP; } -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_ijump(compiler, type, src, srcw); } @@ -1933,7 +2094,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); ADJUST_LOCAL_OFFSET(dst, dstw); - cc = get_cc(compiler, type & 0xff); + cc = get_cc(compiler, type); dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; if (GET_OPCODE(op) < SLJIT_ADD) { @@ -1974,22 +2135,21 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil sljit_s32 dst_reg, sljit_s32 src, sljit_sw srcw) { - sljit_ins inv_bits = (dst_reg & SLJIT_32) ? W_OP : 0; + sljit_ins inv_bits = (type & SLJIT_32) ? W_OP : 0; sljit_ins cc; CHECK_ERROR(); CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); if (SLJIT_UNLIKELY(src & SLJIT_IMM)) { - if (dst_reg & SLJIT_32) + if (type & SLJIT_32) srcw = (sljit_s32)srcw; FAIL_IF(load_immediate(compiler, TMP_REG1, srcw)); src = TMP_REG1; srcw = 0; } - cc = get_cc(compiler, type & 0xff); - dst_reg &= ~SLJIT_32; + cc = get_cc(compiler, type & ~SLJIT_32); return push_inst(compiler, (CSEL ^ inv_bits) | (cc << 12) | RD(dst_reg) | RN(dst_reg) | RM(src)); } @@ -1998,11 +2158,82 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compile sljit_s32 reg, sljit_s32 mem, sljit_sw memw) { - sljit_u32 sign = 0, inst; + sljit_u32 inst; CHECK_ERROR(); CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + if (!(reg & REG_PAIR_MASK)) + return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw); + + ADJUST_LOCAL_OFFSET(mem, memw); + + if (!(mem & REG_MASK)) { + FAIL_IF(load_immediate(compiler, TMP_REG1, memw & ~0x1f8)); + + mem = SLJIT_MEM1(TMP_REG1); + memw &= 0x1f8; + } else if (mem & OFFS_REG_MASK) { + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RN(mem & REG_MASK) | RM(OFFS_REG(mem)) | ((sljit_ins)(memw & 0x3) << 10))); + + mem = SLJIT_MEM1(TMP_REG1); + memw = 0; + } else if ((memw & 0x7) != 0 || memw > 0x1f8 || memw < -0x200) { + inst = ADDI; + + if (memw < 0) { + /* Remains negative for integer min. */ + memw = -memw; + inst = SUBI; + } else if ((memw & 0x7) == 0 && memw <= 0x7ff0) { + if (!(type & SLJIT_MEM_STORE) && (mem & REG_MASK) == REG_PAIR_FIRST(reg)) { + FAIL_IF(push_inst(compiler, LDRI | RD(REG_PAIR_SECOND(reg)) | RN(mem & REG_MASK) | ((sljit_ins)memw << 7))); + return push_inst(compiler, LDRI | RD(REG_PAIR_FIRST(reg)) | RN(mem & REG_MASK) | ((sljit_ins)(memw + 0x8) << 7)); + } + + inst = (type & SLJIT_MEM_STORE) ? STRI : LDRI; + + FAIL_IF(push_inst(compiler, inst | RD(REG_PAIR_FIRST(reg)) | RN(mem & REG_MASK) | ((sljit_ins)memw << 7))); + return push_inst(compiler, inst | RD(REG_PAIR_SECOND(reg)) | RN(mem & REG_MASK) | ((sljit_ins)(memw + 0x8) << 7)); + } + + if ((sljit_uw)memw <= 0xfff) { + FAIL_IF(push_inst(compiler, inst | RD(TMP_REG1) | RN(mem & REG_MASK) | ((sljit_ins)memw << 10))); + memw = 0; + } else if ((sljit_uw)memw <= 0xffffff) { + FAIL_IF(push_inst(compiler, inst | (1 << 22) | RD(TMP_REG1) | RN(mem & REG_MASK) | (((sljit_ins)memw >> 12) << 10))); + + if ((memw & 0xe07) != 0) { + FAIL_IF(push_inst(compiler, inst | RD(TMP_REG1) | RN(TMP_REG1) | (((sljit_ins)memw & 0xfff) << 10))); + memw = 0; + } else { + memw &= 0xfff; + } + } else { + FAIL_IF(load_immediate(compiler, TMP_REG1, memw)); + FAIL_IF(push_inst(compiler, (inst == ADDI ? ADD : SUB) | RD(TMP_REG1) | RN(mem & REG_MASK) | RM(TMP_REG1))); + memw = 0; + } + + mem = SLJIT_MEM1(TMP_REG1); + + if (inst == SUBI) + memw = -memw; + } + + SLJIT_ASSERT((memw & 0x7) == 0 && memw <= 0x1f8 && memw >= -0x200); + return push_inst(compiler, ((type & SLJIT_MEM_STORE) ? STP : LDP) | RT(REG_PAIR_FIRST(reg)) | RT2(REG_PAIR_SECOND(reg)) | RN(mem & REG_MASK) | (sljit_ins)((memw & 0x3f8) << 12)); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_u32 sign = 0, inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw)); + if ((mem & OFFS_REG_MASK) || (memw > 255 || memw < -256)) return SLJIT_ERR_UNSUPPORTED; @@ -2042,20 +2273,20 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compile if (!(type & SLJIT_MEM_STORE)) inst |= sign ? 0x00800000 : 0x00400000; - if (type & SLJIT_MEM_PRE) + if (!(type & SLJIT_MEM_POST)) inst |= 0x800; return push_inst(compiler, inst | RT(reg) | RN(mem & REG_MASK) | (sljit_ins)((memw & 0x1ff) << 12)); } -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw) { sljit_u32 inst; CHECK_ERROR(); - CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw)); + CHECK(check_sljit_emit_fmem_update(compiler, type, freg, mem, memw)); if ((mem & OFFS_REG_MASK) || (memw > 255 || memw < -256)) return SLJIT_ERR_UNSUPPORTED; @@ -2071,7 +2302,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compil if (!(type & SLJIT_MEM_STORE)) inst |= 0x00400000; - if (type & SLJIT_MEM_PRE) + if (!(type & SLJIT_MEM_POST)) inst |= 0x800; return push_inst(compiler, inst | VT(freg) | RN(mem & REG_MASK) | (sljit_ins)((memw & 0x1ff) << 12)); diff --git a/thirdparty/pcre2/src/sljit/sljitNativeARM_T2_32.c b/thirdparty/pcre2/src/sljit/sljitNativeARM_T2_32.c index ed21ea7daa..7d6bac077e 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativeARM_T2_32.c +++ b/thirdparty/pcre2/src/sljit/sljitNativeARM_T2_32.c @@ -100,7 +100,6 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define ADDS 0x1800 #define ADDSI3 0x1c00 #define ADDSI8 0x3000 -#define ADD_W 0xeb000000 #define ADDWI 0xf2000000 #define ADD_SP 0x4485 #define ADD_SP_I 0xb000 @@ -131,6 +130,7 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define IT 0xbf00 #define LDR_SP 0x9800 #define LDR 0xf8d00000 +#define LDRD 0xe9500000 #define LDRI 0xf8500800 #define LSLS 0x4080 #define LSLSI 0x0000 @@ -160,6 +160,10 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define POP_W 0xe8bd0000 #define PUSH 0xb400 #define PUSH_W 0xe92d0000 +#define RBIT 0xfa90f0a0 +#define RORS 0x41c0 +#define ROR_W 0xfa60f000 +#define ROR_WI 0xea4f0030 #define RSB_WI 0xf1c00000 #define RSBSI 0x4240 #define SBCI 0xf1600000 @@ -167,6 +171,7 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define SBC_W 0xeb600000 #define SDIV 0xfb90f0f0 #define SMULL 0xfb800000 +#define STRD 0xe9400000 #define STR_SP 0x9000 #define SUBS 0x1a00 #define SUBSI3 0x1e00 @@ -434,8 +439,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } next_addr = compute_next_addr(label, jump, const_, put_label); } - code_ptr ++; - half_count ++; + code_ptr++; + half_count++; } while (buf_ptr < buf_end); buf = buf->next; @@ -491,6 +496,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) #endif case SLJIT_HAS_CLZ: + case SLJIT_HAS_CTZ: + case SLJIT_HAS_ROT: case SLJIT_HAS_CMOV: case SLJIT_HAS_PREFETCH: return 1; @@ -592,7 +599,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s arg1 must be register, imm arg2 must be register, imm */ sljit_s32 reg; - sljit_uw imm, nimm; + sljit_uw imm, imm2; if (SLJIT_UNLIKELY((flags & (ARG1_IMM | ARG2_IMM)) == (ARG1_IMM | ARG2_IMM))) { /* Both are immediates, no temporaries are used. */ @@ -607,6 +614,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s switch (flags & 0xffff) { case SLJIT_CLZ: + case SLJIT_CTZ: case SLJIT_MUL: /* No form with immediate operand. */ break; @@ -621,31 +629,31 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s break; case SLJIT_ADD: compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD; - nimm = NEGATE(imm); + imm2 = NEGATE(imm); if (IS_2_LO_REGS(reg, dst)) { if (imm <= 0x7) return push_inst16(compiler, ADDSI3 | IMM3(imm) | RD3(dst) | RN3(reg)); - if (nimm <= 0x7) - return push_inst16(compiler, SUBSI3 | IMM3(nimm) | RD3(dst) | RN3(reg)); + if (imm2 <= 0x7) + return push_inst16(compiler, SUBSI3 | IMM3(imm2) | RD3(dst) | RN3(reg)); if (reg == dst) { if (imm <= 0xff) return push_inst16(compiler, ADDSI8 | IMM8(imm) | RDN3(dst)); - if (nimm <= 0xff) - return push_inst16(compiler, SUBSI8 | IMM8(nimm) | RDN3(dst)); + if (imm2 <= 0xff) + return push_inst16(compiler, SUBSI8 | IMM8(imm2) | RDN3(dst)); } } if (!(flags & SET_FLAGS)) { if (imm <= 0xfff) return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(imm)); - if (nimm <= 0xfff) - return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(nimm)); + if (imm2 <= 0xfff) + return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(imm2)); } - nimm = get_imm(imm); - if (nimm != INVALID_IMM) - return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm); - nimm = get_imm(NEGATE(imm)); - if (nimm != INVALID_IMM) - return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm); + imm2 = get_imm(imm); + if (imm2 != INVALID_IMM) + return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2); + imm = get_imm(NEGATE(imm)); + if (imm != INVALID_IMM) + return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); break; case SLJIT_ADDC: compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD; @@ -666,39 +674,39 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s if (flags & UNUSED_RETURN) { if (imm <= 0xff && reg_map[reg] <= 7) return push_inst16(compiler, CMPI | IMM8(imm) | RDN3(reg)); - nimm = get_imm(imm); - if (nimm != INVALID_IMM) - return push_inst32(compiler, CMPI_W | RN4(reg) | nimm); - nimm = get_imm(NEGATE(imm)); - if (nimm != INVALID_IMM) - return push_inst32(compiler, CMNI_W | RN4(reg) | nimm); + imm2 = get_imm(imm); + if (imm2 != INVALID_IMM) + return push_inst32(compiler, CMPI_W | RN4(reg) | imm2); + imm = get_imm(NEGATE(imm)); + if (imm != INVALID_IMM) + return push_inst32(compiler, CMNI_W | RN4(reg) | imm); break; } - nimm = NEGATE(imm); + imm2 = NEGATE(imm); if (IS_2_LO_REGS(reg, dst)) { if (imm <= 0x7) return push_inst16(compiler, SUBSI3 | IMM3(imm) | RD3(dst) | RN3(reg)); - if (nimm <= 0x7) - return push_inst16(compiler, ADDSI3 | IMM3(nimm) | RD3(dst) | RN3(reg)); + if (imm2 <= 0x7) + return push_inst16(compiler, ADDSI3 | IMM3(imm2) | RD3(dst) | RN3(reg)); if (reg == dst) { if (imm <= 0xff) return push_inst16(compiler, SUBSI8 | IMM8(imm) | RDN3(dst)); - if (nimm <= 0xff) - return push_inst16(compiler, ADDSI8 | IMM8(nimm) | RDN3(dst)); + if (imm2 <= 0xff) + return push_inst16(compiler, ADDSI8 | IMM8(imm2) | RDN3(dst)); } } if (!(flags & SET_FLAGS)) { if (imm <= 0xfff) return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(imm)); - if (nimm <= 0xfff) - return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(nimm)); + if (imm2 <= 0xfff) + return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(imm2)); } - nimm = get_imm(imm); - if (nimm != INVALID_IMM) - return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm); - nimm = get_imm(NEGATE(imm)); - if (nimm != INVALID_IMM) - return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm); + imm2 = get_imm(imm); + if (imm2 != INVALID_IMM) + return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2); + imm = get_imm(NEGATE(imm)); + if (imm != INVALID_IMM) + return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); break; case SLJIT_SUBC: compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB; @@ -709,17 +717,17 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s return push_inst32(compiler, SBCI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); break; case SLJIT_AND: - nimm = get_imm(imm); - if (nimm != INVALID_IMM) - return push_inst32(compiler, ((flags & UNUSED_RETURN) ? TSTI : ANDI) | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm); + imm2 = get_imm(imm); + if (imm2 != INVALID_IMM) + return push_inst32(compiler, ((flags & UNUSED_RETURN) ? TSTI : ANDI) | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2); imm = get_imm(~imm); if (imm != INVALID_IMM) return push_inst32(compiler, BICI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); break; case SLJIT_OR: - nimm = get_imm(imm); - if (nimm != INVALID_IMM) - return push_inst32(compiler, ORRI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm); + imm2 = get_imm(imm); + if (imm2 != INVALID_IMM) + return push_inst32(compiler, ORRI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2); imm = get_imm(~imm); if (imm != INVALID_IMM) return push_inst32(compiler, ORNI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); @@ -730,11 +738,17 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s return push_inst32(compiler, EORI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); break; case SLJIT_SHL: + case SLJIT_MSHL: case SLJIT_LSHR: + case SLJIT_MLSHR: case SLJIT_ASHR: + case SLJIT_MASHR: + case SLJIT_ROTL: + case SLJIT_ROTR: if (flags & ARG1_IMM) break; imm &= 0x1f; + if (imm == 0) { if (!(flags & SET_FLAGS)) return push_inst16(compiler, MOV | SET_REGS44(dst, reg)); @@ -742,19 +756,28 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s return push_inst16(compiler, MOVS | RD3(dst) | RN3(reg)); return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(dst) | RM4(reg)); } + switch (flags & 0xffff) { case SLJIT_SHL: + case SLJIT_MSHL: if (IS_2_LO_REGS(dst, reg)) return push_inst16(compiler, LSLSI | RD3(dst) | RN3(reg) | (imm << 6)); return push_inst32(compiler, LSL_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm)); case SLJIT_LSHR: + case SLJIT_MLSHR: if (IS_2_LO_REGS(dst, reg)) return push_inst16(compiler, LSRSI | RD3(dst) | RN3(reg) | (imm << 6)); return push_inst32(compiler, LSR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm)); - default: /* SLJIT_ASHR */ + case SLJIT_ASHR: + case SLJIT_MASHR: if (IS_2_LO_REGS(dst, reg)) return push_inst16(compiler, ASRSI | RD3(dst) | RN3(reg) | (imm << 6)); return push_inst32(compiler, ASR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm)); + case SLJIT_ROTL: + imm = (imm ^ 0x1f) + 1; + /* fallthrough */ + default: /* SLJIT_ROTR */ + return push_inst32(compiler, ROR_WI | RD4(dst) | RM4(reg) | IMM5(imm)); } default: SLJIT_UNREACHABLE(); @@ -813,8 +836,11 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s return push_inst32(compiler, MVN_W | (flags & SET_FLAGS) | RD4(dst) | RM4(arg2)); case SLJIT_CLZ: SLJIT_ASSERT(arg1 == TMP_REG2); - FAIL_IF(push_inst32(compiler, CLZ | RN4(arg2) | RD4(dst) | RM4(arg2))); - return SLJIT_SUCCESS; + return push_inst32(compiler, CLZ | RN4(arg2) | RD4(dst) | RM4(arg2)); + case SLJIT_CTZ: + SLJIT_ASSERT(arg1 == TMP_REG2); + FAIL_IF(push_inst32(compiler, RBIT | RN4(arg2) | RD4(dst) | RM4(arg2))); + return push_inst32(compiler, CLZ | RN4(dst) | RD4(dst) | RM4(dst)); case SLJIT_ADD: compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD; if (IS_3_LO_REGS(dst, arg1, arg2)) @@ -864,18 +890,38 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, EORS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, EOR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_MSHL: + FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(arg2) | 0x1f)); + arg2 = TMP_REG2; + /* fallthrough */ case SLJIT_SHL: if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, LSLS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, LSL_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_MLSHR: + FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(arg2) | 0x1f)); + arg2 = TMP_REG2; + /* fallthrough */ case SLJIT_LSHR: if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, LSRS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, LSR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_MASHR: + FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(arg2) | 0x1f)); + arg2 = TMP_REG2; + /* fallthrough */ case SLJIT_ASHR: if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, ASRS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, ASR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_ROTL: + FAIL_IF(push_inst32(compiler, RSB_WI | RD4(TMP_REG2) | RN4(arg2) | 0)); + arg2 = TMP_REG2; + /* fallthrough */ + case SLJIT_ROTR: + if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, RORS | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, ROR_W | RD4(dst) | RN4(arg1) | RM4(arg2)); } SLJIT_UNREACHABLE(); @@ -890,8 +936,8 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s #define HALF_SIZE 0x08 #define PRELOAD 0x0c -#define IS_WORD_SIZE(flags) (!(flags & (BYTE_SIZE | HALF_SIZE))) -#define OFFSET_CHECK(imm, shift) (!(argw & ~(imm << shift))) +#define IS_WORD_SIZE(flags) (!((flags) & (BYTE_SIZE | HALF_SIZE))) +#define ALIGN_CHECK(argw, imm, shift) (!((argw) & ~((imm) << (shift)))) /* 1st letter: @@ -990,16 +1036,15 @@ static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg) { sljit_s32 other_r; - sljit_uw tmp; + sljit_uw imm, tmp; SLJIT_ASSERT(arg & SLJIT_MEM); - SLJIT_ASSERT((arg & REG_MASK) != tmp_reg); - arg &= ~SLJIT_MEM; + SLJIT_ASSERT((arg & REG_MASK) != tmp_reg || (arg == SLJIT_MEM1(tmp_reg) && argw >= -0xff && argw <= 0xfff)); if (SLJIT_UNLIKELY(!(arg & REG_MASK))) { - tmp = get_imm((sljit_uw)argw & ~(sljit_uw)0xfff); - if (tmp != INVALID_IMM) { - FAIL_IF(push_inst32(compiler, MOV_WI | RD4(tmp_reg) | tmp)); + imm = get_imm((sljit_uw)argw & ~(sljit_uw)0xfff); + if (imm != INVALID_IMM) { + FAIL_IF(push_inst32(compiler, MOV_WI | RD4(tmp_reg) | imm)); return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(tmp_reg) | (argw & 0xfff)); } @@ -1012,51 +1057,59 @@ static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { argw &= 0x3; other_r = OFFS_REG(arg); - arg &= 0xf; + arg &= REG_MASK; if (!argw && IS_3_LO_REGS(reg, arg, other_r)) return push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(other_r)); return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(other_r) | ((sljit_ins)argw << 4)); } + arg &= REG_MASK; + if (argw > 0xfff) { - tmp = get_imm((sljit_uw)argw & ~(sljit_uw)0xfff); - if (tmp != INVALID_IMM) { - push_inst32(compiler, ADD_WI | RD4(tmp_reg) | RN4(arg) | tmp); + imm = get_imm((sljit_uw)(argw & ~0xfff)); + if (imm != INVALID_IMM) { + push_inst32(compiler, ADD_WI | RD4(tmp_reg) | RN4(arg) | imm); arg = tmp_reg; argw = argw & 0xfff; } } else if (argw < -0xff) { - tmp = get_imm((sljit_uw)-argw & ~(sljit_uw)0xff); - if (tmp != INVALID_IMM) { - push_inst32(compiler, SUB_WI | RD4(tmp_reg) | RN4(arg) | tmp); + tmp = (sljit_uw)((-argw + 0xfff) & ~0xfff); + SLJIT_ASSERT(tmp >= (sljit_uw)-argw); + imm = get_imm(tmp); + + if (imm != INVALID_IMM) { + push_inst32(compiler, SUB_WI | RD4(tmp_reg) | RN4(arg) | imm); arg = tmp_reg; - argw = -(-argw & 0xff); + argw += (sljit_sw)tmp; + + SLJIT_ASSERT(argw >= 0 && argw <= 0xfff); } } + /* 16 bit instruction forms. */ if (IS_2_LO_REGS(reg, arg) && sljit_mem16_imm5[flags]) { tmp = 3; if (IS_WORD_SIZE(flags)) { - if (OFFSET_CHECK(0x1f, 2)) + if (ALIGN_CHECK(argw, 0x1f, 2)) tmp = 2; } else if (flags & BYTE_SIZE) { - if (OFFSET_CHECK(0x1f, 0)) + if (ALIGN_CHECK(argw, 0x1f, 0)) tmp = 0; } else { SLJIT_ASSERT(flags & HALF_SIZE); - if (OFFSET_CHECK(0x1f, 1)) + if (ALIGN_CHECK(argw, 0x1f, 1)) tmp = 1; } if (tmp < 3) return push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(arg) | ((sljit_ins)argw << (6 - tmp))); } - else if (SLJIT_UNLIKELY(arg == SLJIT_SP) && IS_WORD_SIZE(flags) && OFFSET_CHECK(0xff, 2) && reg_map[reg] <= 7) { + else if (SLJIT_UNLIKELY(arg == SLJIT_SP) && IS_WORD_SIZE(flags) && ALIGN_CHECK(argw, 0xff, 2) && reg_map[reg] <= 7) { /* SP based immediate. */ return push_inst16(compiler, STR_SP | (sljit_ins)((flags & STORE) ? 0 : 0x800) | RDN3(reg) | ((sljit_ins)argw >> 2)); } @@ -1074,6 +1127,9 @@ static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(tmp_reg)); } +#undef ALIGN_CHECK +#undef IS_WORD_SIZE + /* --------------------------------------------------------------------- */ /* Entry, exit */ /* --------------------------------------------------------------------- */ @@ -1082,7 +1138,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { - sljit_s32 size, i, tmp, word_arg_count, saved_arg_count; + sljit_s32 size, i, tmp, word_arg_count; + sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options); sljit_uw offset; sljit_uw imm = 0; #ifdef __SOFTFP__ @@ -1098,7 +1155,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); tmp = SLJIT_S0 - saveds; - for (i = SLJIT_S0; i > tmp; i--) + for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) imm |= (sljit_uw)1 << reg_map[i]; for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) @@ -1110,7 +1167,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi : push_inst16(compiler, PUSH | (1 << 8) | imm)); /* Stack must be aligned to 8 bytes: (LR, R4) */ - size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); + size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1); if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) { if ((size & SSIZE_OF(sw)) != 0) { @@ -1131,6 +1188,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi local_size = ((size + local_size + 0x7) & ~0x7) - size; compiler->local_size = local_size; + if (options & SLJIT_ENTER_REG_ARG) + arg_types = 0; + arg_types >>= SLJIT_ARG_SHIFT; word_arg_count = 0; saved_arg_count = 0; @@ -1173,13 +1233,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi else break; - SLJIT_ASSERT(reg_map[tmp] <= 7); - if (offset < 4 * sizeof(sljit_sw)) - FAIL_IF(push_inst16(compiler, MOV | RD3(tmp) | (offset << 1))); - else + FAIL_IF(push_inst16(compiler, MOV | ((sljit_ins)reg_map[tmp] & 0x7) | (((sljit_ins)reg_map[tmp] & 0x8) << 4) | (offset << 1))); + else if (reg_map[tmp] <= 7) FAIL_IF(push_inst16(compiler, LDR_SP | RDN3(tmp) | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2))); + else + FAIL_IF(push_inst32(compiler, LDR | RT4(tmp) | RN4(SLJIT_SP) + | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw))))); break; } @@ -1293,7 +1354,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); + size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1); if ((size & SSIZE_OF(sw)) != 0 && (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG)) size += SSIZE_OF(sw); @@ -1325,8 +1386,9 @@ static sljit_s32 emit_add_sp(struct sljit_compiler *compiler, sljit_uw imm) static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size) { sljit_s32 local_size, fscratches, fsaveds, i, tmp; + sljit_s32 restored_reg = 0; sljit_s32 lr_dst = TMP_PC; - sljit_uw reg_list; + sljit_uw reg_list = 0; SLJIT_ASSERT(reg_map[TMP_REG2] == 14 && frame_size <= 128); @@ -1353,46 +1415,88 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit if (frame_size < 0) { lr_dst = TMP_REG2; frame_size = 0; - } else if (frame_size > 0) + } else if (frame_size > 0) { + SLJIT_ASSERT(frame_size == 1 || (frame_size & 0x7) == 0); lr_dst = 0; + frame_size &= ~0x7; + } - reg_list = 0; tmp = SLJIT_S0 - compiler->saveds; - for (i = SLJIT_S0; i > tmp; i--) - reg_list |= (sljit_uw)1 << reg_map[i]; + i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); + if (tmp < i) { + restored_reg = i; + do { + reg_list |= (sljit_uw)1 << reg_map[i]; + } while (--i > tmp); + } + + i = compiler->scratches; + if (i >= SLJIT_FIRST_SAVED_REG) { + restored_reg = i; + do { + reg_list |= (sljit_uw)1 << reg_map[i]; + } while (--i >= SLJIT_FIRST_SAVED_REG); + } - for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) - reg_list |= (sljit_uw)1 << reg_map[i]; + if (lr_dst == TMP_REG2 && reg_list == 0) { + reg_list |= (sljit_uw)1 << reg_map[TMP_REG2]; + restored_reg = TMP_REG2; + lr_dst = 0; + } if (lr_dst == 0 && (reg_list & (reg_list - 1)) == 0) { /* The local_size does not include the saved registers. */ - local_size += SSIZE_OF(sw); + tmp = 0; + if (reg_list != 0) { + tmp = 2; + if (local_size <= 0xfff) { + if (local_size == 0) { + SLJIT_ASSERT(restored_reg != TMP_REG2); + if (frame_size == 0) + return push_inst32(compiler, LDRI | RT4(restored_reg) | RN4(SLJIT_SP) | 0x308); + if (frame_size > 2 * SSIZE_OF(sw)) + return push_inst32(compiler, LDRI | RT4(restored_reg) | RN4(SLJIT_SP) | 0x100 | (sljit_ins)(frame_size - (2 * SSIZE_OF(sw)))); + } + + if (reg_map[restored_reg] <= 7 && local_size <= 0x3fc) + FAIL_IF(push_inst16(compiler, STR_SP | 0x800 | RDN3(restored_reg) | (sljit_ins)(local_size >> 2))); + else + FAIL_IF(push_inst32(compiler, LDR | RT4(restored_reg) | RN4(SLJIT_SP) | (sljit_ins)local_size)); + tmp = 1; + } else if (frame_size == 0) { + frame_size = (restored_reg == TMP_REG2) ? SSIZE_OF(sw) : 2 * SSIZE_OF(sw); + tmp = 3; + } + + /* Place for the saved register. */ + if (restored_reg != TMP_REG2) + local_size += SSIZE_OF(sw); + } - if (reg_list != 0) - local_size += SSIZE_OF(sw); + /* Place for the lr register. */ + local_size += SSIZE_OF(sw); if (frame_size > local_size) - FAIL_IF(push_inst16(compiler, SUB_SP_I | ((sljit_uw)(frame_size - local_size) >> 2))); + FAIL_IF(push_inst16(compiler, SUB_SP_I | ((sljit_ins)(frame_size - local_size) >> 2))); else if (frame_size < local_size) FAIL_IF(emit_add_sp(compiler, (sljit_uw)(local_size - frame_size))); - if (reg_list == 0) + if (tmp <= 1) return SLJIT_SUCCESS; - if (compiler->saveds > 0) { - SLJIT_ASSERT(reg_list == ((sljit_uw)1 << reg_map[SLJIT_S0])); - lr_dst = SLJIT_S0; - } else { - SLJIT_ASSERT(reg_list == ((sljit_uw)1 << reg_map[SLJIT_FIRST_SAVED_REG])); - lr_dst = SLJIT_FIRST_SAVED_REG; - } + if (tmp == 2) { + frame_size -= SSIZE_OF(sw); + if (restored_reg != TMP_REG2) + frame_size -= SSIZE_OF(sw); - frame_size -= 2 * SSIZE_OF(sw); + if (reg_map[restored_reg] <= 7) + return push_inst16(compiler, STR_SP | 0x800 | RDN3(restored_reg) | (sljit_ins)(frame_size >> 2)); - if (reg_map[lr_dst] <= 7) - return push_inst16(compiler, STR_SP | 0x800 | RDN3(lr_dst) | (sljit_uw)(frame_size >> 2)); + return push_inst32(compiler, LDR | RT4(restored_reg) | RN4(SLJIT_SP) | (sljit_ins)frame_size); + } - return push_inst32(compiler, LDR | RT4(lr_dst) | RN4(SLJIT_SP) | (sljit_uw)frame_size); + tmp = (restored_reg == TMP_REG2) ? 0x304 : 0x308; + return push_inst32(compiler, LDRI | RT4(restored_reg) | RN4(SLJIT_SP) | (sljit_ins)tmp); } if (local_size > 0) @@ -1407,12 +1511,8 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit FAIL_IF(push_inst16(compiler, POP | reg_list)); } else { - if (lr_dst != 0) { - if (reg_list == 0) - return push_inst32(compiler, 0xf85d0b04 | RT4(lr_dst)); - + if (lr_dst != 0) reg_list |= (sljit_uw)1 << reg_map[lr_dst]; - } /* At least two registers must be set for POP_W instruction. */ SLJIT_ASSERT((reg_list & (reg_list - 1)) != 0); @@ -1421,8 +1521,12 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit } if (frame_size > 0) - return push_inst16(compiler, SUB_SP_I | (((sljit_uw)frame_size - sizeof(sljit_sw)) >> 2)); - return SLJIT_SUCCESS; + return push_inst16(compiler, SUB_SP_I | (((sljit_ins)frame_size - sizeof(sljit_sw)) >> 2)); + + if (lr_dst != 0) + return SLJIT_SUCCESS; + + return push_inst16(compiler, ADD_SP_I | 1); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler) @@ -1433,6 +1537,28 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler return emit_stack_frame_release(compiler, 0); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_return_to(compiler, src, srcw)); + + if (src & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1)); + src = TMP_REG1; + srcw = 0; + } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, src))); + src = TMP_REG1; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, 1)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); +} + /* --------------------------------------------------------------------- */ /* Operators */ /* --------------------------------------------------------------------- */ @@ -1685,13 +1811,75 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compil CHECK_ERROR(); CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_op2(compiler, op, TMP_REG1, 0, src1, src1w, src2, src2w); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_s32 is_left; + + CHECK_ERROR(); + CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w)); + + op = GET_OPCODE(op); + is_left = (op == SLJIT_SHL || op == SLJIT_MSHL); + + if (src_dst == src1) { + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_op2(compiler, is_left ? SLJIT_ROTL : SLJIT_ROTR, src_dst, 0, src_dst, 0, src2, src2w); + } + + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + + if (src2 & SLJIT_IMM) { + src2w &= 0x1f; + + if (src2w == 0) + return SLJIT_SUCCESS; + } else if (src2 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, src2, src2w, TMP_REG2)); + src2 = TMP_REG2; + } + + if (src1 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src1, src1w, TMP_REG1)); + src1 = TMP_REG1; + } else if (src1 & SLJIT_IMM) { + FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)src1w)); + src1 = TMP_REG1; + } + + if (src2 & SLJIT_IMM) { + if (reg_map[src_dst] <= 7) + FAIL_IF(push_inst16(compiler, (is_left ? LSLSI : LSRSI) | RD3(src_dst) | RN3(src_dst) | ((sljit_ins)src2w << 6))); + else + FAIL_IF(push_inst32(compiler, (is_left ? LSL_WI : LSR_WI) | RD4(src_dst) | RM4(src_dst) | IMM5(src2w))); + + src2w = (src2w ^ 0x1f) + 1; + return push_inst32(compiler, ORR_W | RD4(src_dst) | RN4(src_dst) | RM4(src1) | (is_left ? 0x10 : 0x0) | IMM5(src2w)); + } + + if (op == SLJIT_MSHL || op == SLJIT_MLSHR) { + FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(src2) | 0x1f)); + src2 = TMP_REG2; + } + + if (IS_2_LO_REGS(src_dst, src2)) + FAIL_IF(push_inst16(compiler, (is_left ? LSLS : LSRS) | RD3(src_dst) | RN3(src2))); + else + FAIL_IF(push_inst32(compiler, (is_left ? LSL_W : LSR_W) | RD4(src_dst) | RN4(src_dst) | RM4(src2))); + + FAIL_IF(push_inst32(compiler, (is_left ? LSR_WI : LSL_WI) | RD4(TMP_REG1) | RM4(src1) | (1 << 6))); + FAIL_IF(push_inst32(compiler, EORI | RD4(TMP_REG2) | RN4(src2) | 0x1f)); + FAIL_IF(push_inst32(compiler, (is_left ? LSR_W : LSL_W) | RD4(TMP_REG1) | RN4(TMP_REG1) | RM4(TMP_REG2))); + return push_inst32(compiler, ORR_W | RD4(src_dst) | RN4(src_dst) | RM4(TMP_REG1)); +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { @@ -1955,8 +2143,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil return emit_fop_mem(compiler, (op & SLJIT_32), TMP_FREG1, dst, dstw); } -#undef FPU_LOAD - /* --------------------------------------------------------------------- */ /* Other instructions */ /* --------------------------------------------------------------------- */ @@ -1984,11 +2170,15 @@ static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type) { switch (type) { case SLJIT_EQUAL: - case SLJIT_EQUAL_F64: + case SLJIT_F_EQUAL: + case SLJIT_ORDERED_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */ return 0x0; case SLJIT_NOT_EQUAL: - case SLJIT_NOT_EQUAL_F64: + case SLJIT_F_NOT_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */ return 0x1; case SLJIT_CARRY: @@ -1997,7 +2187,6 @@ static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type) /* fallthrough */ case SLJIT_LESS: - case SLJIT_LESS_F64: return 0x3; case SLJIT_NOT_CARRY: @@ -2006,27 +2195,33 @@ static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type) /* fallthrough */ case SLJIT_GREATER_EQUAL: - case SLJIT_GREATER_EQUAL_F64: return 0x2; case SLJIT_GREATER: - case SLJIT_GREATER_F64: + case SLJIT_UNORDERED_OR_GREATER: return 0x8; case SLJIT_LESS_EQUAL: - case SLJIT_LESS_EQUAL_F64: + case SLJIT_F_LESS_EQUAL: + case SLJIT_ORDERED_LESS_EQUAL: return 0x9; case SLJIT_SIG_LESS: + case SLJIT_UNORDERED_OR_LESS: return 0xb; case SLJIT_SIG_GREATER_EQUAL: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: return 0xa; case SLJIT_SIG_GREATER: + case SLJIT_F_GREATER: + case SLJIT_ORDERED_GREATER: return 0xc; case SLJIT_SIG_LESS_EQUAL: + case SLJIT_UNORDERED_OR_LESS_EQUAL: return 0xd; case SLJIT_OVERFLOW: @@ -2034,7 +2229,7 @@ static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type) return 0x1; /* fallthrough */ - case SLJIT_UNORDERED_F64: + case SLJIT_UNORDERED: return 0x6; case SLJIT_NOT_OVERFLOW: @@ -2042,9 +2237,16 @@ static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type) return 0x0; /* fallthrough */ - case SLJIT_ORDERED_F64: + case SLJIT_ORDERED: return 0x7; + case SLJIT_F_LESS: + case SLJIT_ORDERED_LESS: + return 0x4; + + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + return 0x5; + default: /* SLJIT_JUMP */ SLJIT_UNREACHABLE(); return 0xe; @@ -2289,52 +2491,49 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); #ifdef __SOFTFP__ - PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL, &extra_space)); - SLJIT_ASSERT((extra_space & 0x7) == 0); + if ((type & 0xff) != SLJIT_CALL_REG_ARG) { + PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL, &extra_space)); + SLJIT_ASSERT((extra_space & 0x7) == 0); - if ((type & SLJIT_CALL_RETURN) && extra_space == 0) - type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); + if ((type & SLJIT_CALL_RETURN) && extra_space == 0) + type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); + jump = sljit_emit_jump(compiler, type); + PTR_FAIL_IF(jump == NULL); - jump = sljit_emit_jump(compiler, type); - PTR_FAIL_IF(jump == NULL); + if (extra_space > 0) { + if (type & SLJIT_CALL_RETURN) + PTR_FAIL_IF(push_inst32(compiler, LDR | RT4(TMP_REG2) + | RN4(SLJIT_SP) | (extra_space - sizeof(sljit_sw)))); - if (extra_space > 0) { - if (type & SLJIT_CALL_RETURN) - PTR_FAIL_IF(push_inst32(compiler, LDR | RT4(TMP_REG2) - | RN4(SLJIT_SP) | (extra_space - sizeof(sljit_sw)))); + PTR_FAIL_IF(push_inst16(compiler, ADD_SP_I | (extra_space >> 2))); - PTR_FAIL_IF(push_inst16(compiler, ADD_SP_I | (extra_space >> 2))); - - if (type & SLJIT_CALL_RETURN) { - PTR_FAIL_IF(push_inst16(compiler, BX | RN3(TMP_REG2))); - return jump; + if (type & SLJIT_CALL_RETURN) { + PTR_FAIL_IF(push_inst16(compiler, BX | RN3(TMP_REG2))); + return jump; + } } + + SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN)); + PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types)); + return jump; } +#endif /* __SOFTFP__ */ - SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN)); - PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types)); - return jump; -#else if (type & SLJIT_CALL_RETURN) { /* ldmia sp!, {..., lr} */ PTR_FAIL_IF(emit_stack_frame_release(compiler, -1)); type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); } - PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); - -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif +#ifndef __SOFTFP__ + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); +#endif /* !__SOFTFP__ */ + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_jump(compiler, type); -#endif } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) @@ -2385,56 +2584,80 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi src = TMP_REG1; } - if ((type & SLJIT_CALL_RETURN) && (src >= SLJIT_FIRST_SAVED_REG && src <= SLJIT_S0)) { + if ((type & SLJIT_CALL_RETURN) && (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) { FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, src))); src = TMP_REG1; } #ifdef __SOFTFP__ - FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src, &extra_space)); - SLJIT_ASSERT((extra_space & 0x7) == 0); + if ((type & 0xff) != SLJIT_CALL_REG_ARG) { + FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src, &extra_space)); + SLJIT_ASSERT((extra_space & 0x7) == 0); - if ((type & SLJIT_CALL_RETURN) && extra_space == 0) - type = SLJIT_JUMP; + if ((type & SLJIT_CALL_RETURN) && extra_space == 0) + type = SLJIT_JUMP; -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); + FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); - FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); + if (extra_space > 0) { + if (type & SLJIT_CALL_RETURN) + FAIL_IF(push_inst32(compiler, LDR | RT4(TMP_REG2) + | RN4(SLJIT_SP) | (extra_space - sizeof(sljit_sw)))); - if (extra_space > 0) { - if (type & SLJIT_CALL_RETURN) - FAIL_IF(push_inst32(compiler, LDR | RT4(TMP_REG2) - | RN4(SLJIT_SP) | (extra_space - sizeof(sljit_sw)))); + FAIL_IF(push_inst16(compiler, ADD_SP_I | (extra_space >> 2))); - FAIL_IF(push_inst16(compiler, ADD_SP_I | (extra_space >> 2))); + if (type & SLJIT_CALL_RETURN) + return push_inst16(compiler, BX | RN3(TMP_REG2)); + } - if (type & SLJIT_CALL_RETURN) - return push_inst16(compiler, BX | RN3(TMP_REG2)); + SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN)); + return softfloat_post_call_with_args(compiler, arg_types); } +#endif /* __SOFTFP__ */ - SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN)); - return softfloat_post_call_with_args(compiler, arg_types); -#else /* !__SOFTFP__ */ if (type & SLJIT_CALL_RETURN) { /* ldmia sp!, {..., lr} */ FAIL_IF(emit_stack_frame_release(compiler, -1)); type = SLJIT_JUMP; } - FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); - -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif +#ifndef __SOFTFP__ + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); +#endif /* !__SOFTFP__ */ + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_ijump(compiler, type, src, srcw); -#endif /* __SOFTFP__ */ } +#ifdef __SOFTFP__ + +static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) +{ + if (compiler->options & SLJIT_ENTER_REG_ARG) { + if (src == SLJIT_FR0) + return SLJIT_SUCCESS; + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw); + } + + if (FAST_IS_REG(src)) { + if (op & SLJIT_32) + return push_inst32(compiler, VMOV | (1 << 20) | DN4(src) | RT4(SLJIT_R0)); + return push_inst32(compiler, VMOV2 | (1 << 20) | DM4(src) | RT4(SLJIT_R0) | RN4(SLJIT_R1)); + } + + SLJIT_SKIP_CHECKS(compiler); + + if (op & SLJIT_32) + return sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, src, srcw); + return sljit_emit_mem(compiler, SLJIT_MOV, SLJIT_REG_PAIR(SLJIT_R0, SLJIT_R1), src, srcw); +} + +#endif /* __SOFTFP__ */ + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 type) @@ -2447,7 +2670,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co ADJUST_LOCAL_OFFSET(dst, dstw); op = GET_OPCODE(op); - cc = get_cc(compiler, type & 0xff); + cc = get_cc(compiler, type); dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; if (op < SLJIT_ADD) { @@ -2497,9 +2720,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil CHECK_ERROR(); CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); - dst_reg &= ~SLJIT_32; - - cc = get_cc(compiler, type & 0xff); + cc = get_cc(compiler, type & ~SLJIT_32); if (!(src & SLJIT_IMM)) { FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8)); @@ -2541,11 +2762,186 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compile sljit_s32 mem, sljit_sw memw) { sljit_s32 flags; - sljit_ins inst; + sljit_uw imm, tmp; CHECK_ERROR(); CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + if (!(reg & REG_PAIR_MASK)) + return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw); + + if (type & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32)) { + if ((mem & REG_MASK) == 0) { + if ((memw & 0xfff) >= (0x1000 - SSIZE_OF(sw))) { + imm = get_imm((sljit_uw)((memw + 0x1000) & ~0xfff)); + + if (imm != INVALID_IMM) + memw = (memw & 0xfff) - 0x1000; + } else { + imm = get_imm((sljit_uw)(memw & ~0xfff)); + + if (imm != INVALID_IMM) + memw &= 0xff; + } + + if (imm == INVALID_IMM) { + FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw)); + memw = 0; + } else + FAIL_IF(push_inst32(compiler, MOV_WI | RD4(TMP_REG1) | imm)); + + mem = SLJIT_MEM1(TMP_REG1); + } else if (mem & OFFS_REG_MASK) { + FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(mem & REG_MASK) | RM4(OFFS_REG(mem)) | ((sljit_uw)(memw & 0x3) << 6))); + memw = 0; + mem = SLJIT_MEM1(TMP_REG1); + } else if (memw < -0xff) { + /* Zero value can be included in the first case. */ + if ((-memw & 0xfff) <= SSIZE_OF(sw)) + tmp = (sljit_uw)((-memw + 0x7ff) & ~0x7ff); + else + tmp = (sljit_uw)((-memw + 0xfff) & ~0xfff); + + SLJIT_ASSERT(tmp >= (sljit_uw)-memw); + imm = get_imm(tmp); + + if (imm != INVALID_IMM) { + FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm)); + memw += (sljit_sw)tmp; + SLJIT_ASSERT(memw >= 0 && memw <= 0xfff - SSIZE_OF(sw)); + } else { + FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw)); + FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK))); + memw = 0; + } + + mem = SLJIT_MEM1(TMP_REG1); + } else if (memw >= (0x1000 - SSIZE_OF(sw))) { + if ((memw & 0xfff) >= (0x1000 - SSIZE_OF(sw))) { + imm = get_imm((sljit_uw)((memw + 0x1000) & ~0xfff)); + + if (imm != INVALID_IMM) + memw = (memw & 0xfff) - 0x1000; + } else { + imm = get_imm((sljit_uw)(memw & ~0xfff)); + + if (imm != INVALID_IMM) + memw &= 0xfff; + } + + if (imm != INVALID_IMM) { + SLJIT_ASSERT(memw >= -0xff && memw <= 0xfff); + FAIL_IF(push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm)); + } else { + FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw)); + FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK))); + memw = 0; + } + + mem = SLJIT_MEM1(TMP_REG1); + } + + flags = WORD_SIZE; + + SLJIT_ASSERT(memw <= 0xfff - SSIZE_OF(sw) && memw >= -0xff); + + if (type & SLJIT_MEM_STORE) { + flags |= STORE; + } else if (REG_PAIR_FIRST(reg) == (mem & REG_MASK)) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, REG_PAIR_SECOND(reg), mem, memw + SSIZE_OF(sw), TMP_REG2)); + return emit_op_mem(compiler, WORD_SIZE, REG_PAIR_FIRST(reg), mem, memw, TMP_REG2); + } + + FAIL_IF(emit_op_mem(compiler, flags, REG_PAIR_FIRST(reg), mem, memw, TMP_REG2)); + return emit_op_mem(compiler, flags, REG_PAIR_SECOND(reg), mem, memw + SSIZE_OF(sw), TMP_REG2); + } + + flags = 1 << 23; + + if ((mem & REG_MASK) == 0) { + tmp = (sljit_uw)(memw & 0x7fc); + imm = get_imm((sljit_uw)((memw + (tmp <= 0x400 ? 0 : 0x400)) & ~0x3fc)); + + if (imm == INVALID_IMM) { + FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw)); + memw = 0; + } else { + FAIL_IF(push_inst32(compiler, MOV_WI | RD4(TMP_REG1) | imm)); + memw = (memw & 0x3fc) >> 2; + + if (tmp > 0x400) { + memw = 0x100 - memw; + flags = 0; + } + + SLJIT_ASSERT(memw >= 0 && memw <= 0xff); + } + + mem = SLJIT_MEM1(TMP_REG1); + } else if (mem & OFFS_REG_MASK) { + FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(mem & REG_MASK) | RM4(OFFS_REG(mem)) | ((sljit_uw)(memw & 0x3) << 6))); + memw = 0; + mem = SLJIT_MEM1(TMP_REG1); + } else if (memw < 0) { + if ((-memw & ~0x3fc) == 0) { + flags = 0; + memw = -memw >> 2; + } else { + tmp = (sljit_uw)(-memw & 0x7fc); + imm = get_imm((sljit_uw)((-memw + (tmp <= 0x400 ? 0 : 0x400)) & ~0x3fc)); + + if (imm != INVALID_IMM) { + FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm)); + memw = (-memw & 0x3fc) >> 2; + + if (tmp <= 0x400) + flags = 0; + else + memw = 0x100 - memw; + } else { + FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw)); + FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK))); + memw = 0; + } + + mem = SLJIT_MEM1(TMP_REG1); + } + } else if ((memw & ~0x3fc) != 0) { + tmp = (sljit_uw)(memw & 0x7fc); + imm = get_imm((sljit_uw)((memw + (tmp <= 0x400 ? 0 : 0x400)) & ~0x3fc)); + + if (imm != INVALID_IMM) { + FAIL_IF(push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm)); + memw = (memw & 0x3fc) >> 2; + + if (tmp > 0x400) { + memw = 0x100 - memw; + flags = 0; + } + } else { + FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw)); + FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK))); + memw = 0; + } + + mem = SLJIT_MEM1(TMP_REG1); + } else + memw >>= 2; + + SLJIT_ASSERT(memw >= 0 && memw <= 0xff); + return push_inst32(compiler, ((type & SLJIT_MEM_STORE) ? STRD : LDRD) | (sljit_ins)flags | RN4(mem & REG_MASK) | RT4(REG_PAIR_FIRST(reg)) | RD4(REG_PAIR_SECOND(reg)) | (sljit_ins)memw); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_s32 flags; + sljit_ins inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw)); + if ((mem & OFFS_REG_MASK) || (memw > 255 || memw < -255)) return SLJIT_ERR_UNSUPPORTED; @@ -2583,7 +2979,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compile inst = sljit_mem32[flags] | 0x900; - if (type & SLJIT_MEM_PRE) + if (!(type & SLJIT_MEM_POST)) inst |= 0x400; if (memw >= 0) @@ -2594,6 +2990,106 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compile return push_inst32(compiler, inst | RT4(reg) | RN4(mem & REG_MASK) | (sljit_ins)memw); } +static sljit_s32 update_mem_addr(struct sljit_compiler *compiler, sljit_s32 *mem, sljit_sw *memw, sljit_s32 max_offset) +{ + sljit_s32 arg = *mem; + sljit_sw argw = *memw; + sljit_uw imm; + + *mem = TMP_REG1; + + if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { + *memw = 0; + return push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(arg & REG_MASK) | RM4(OFFS_REG(arg)) | ((sljit_uw)(argw & 0x3) << 6)); + } + + arg &= REG_MASK; + + if (arg) { + if (argw <= max_offset && argw >= -0xff) { + *mem = arg; + return SLJIT_SUCCESS; + } + + if (argw < 0) { + imm = get_imm((sljit_uw)(-argw & ~0xff)); + + if (imm) { + *memw = -(-argw & 0xff); + return push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(arg) | imm); + } + } else if ((argw & 0xfff) <= max_offset) { + imm = get_imm((sljit_uw)(argw & ~0xfff)); + + if (imm) { + *memw = argw & 0xfff; + return push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(arg) | imm); + } + } else { + imm = get_imm((sljit_uw)((argw | 0xfff) + 1)); + + if (imm) { + *memw = (argw & 0xfff) - 0x1000; + return push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(arg) | imm); + } + } + } + + imm = (sljit_uw)(argw & ~0xfff); + + if ((argw & 0xfff) > max_offset) { + imm += 0x1000; + *memw = (argw & 0xfff) - 0x1000; + } else + *memw = argw & 0xfff; + + FAIL_IF(load_immediate(compiler, TMP_REG1, imm)); + + if (arg == 0) + return SLJIT_SUCCESS; + + return push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, arg)); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw)); + + if (type & SLJIT_MEM_UNALIGNED_32) + return emit_fop_mem(compiler, ((type ^ SLJIT_32) & SLJIT_32) | ((type & SLJIT_MEM_STORE) ? 0 : FPU_LOAD), freg, mem, memw); + + if (type & SLJIT_MEM_STORE) { + FAIL_IF(push_inst32(compiler, VMOV | (1 << 20) | DN4(freg) | RT4(TMP_REG2))); + + if (type & SLJIT_32) + return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, mem, memw, TMP_REG1); + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4)); + mem |= SLJIT_MEM; + + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, mem, memw, TMP_REG1)); + FAIL_IF(push_inst32(compiler, VMOV | (1 << 20) | DN4(freg) | 0x80 | RT4(TMP_REG2))); + return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, mem, memw + 4, TMP_REG1); + } + + if (type & SLJIT_32) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1)); + return push_inst32(compiler, VMOV | DN4(freg) | RT4(TMP_REG2)); + } + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4)); + mem |= SLJIT_MEM; + + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1)); + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, mem, memw + 4, TMP_REG1)); + return push_inst32(compiler, VMOV2 | DM4(freg) | RT4(TMP_REG2) | RN4(TMP_REG1)); +} + +#undef FPU_LOAD + SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) { struct sljit_const *const_; diff --git a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_32.c b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_32.c index 1a06b17d12..e6853c98f6 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_32.c +++ b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_32.c @@ -38,383 +38,6 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_a return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS; } -#define EMIT_LOGICAL(op_imm, op_norm) \ - if (flags & SRC2_IMM) { \ - if (op & SLJIT_SET_Z) \ - FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \ - if (!(flags & UNUSED_DEST)) \ - FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \ - } \ - else { \ - if (op & SLJIT_SET_Z) \ - FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ - if (!(flags & UNUSED_DEST)) \ - FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | D(dst), DR(dst))); \ - } - -#define EMIT_SHIFT(op_imm, op_v) \ - if (flags & SRC2_IMM) { \ - if (op & SLJIT_SET_Z) \ - FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); \ - if (!(flags & UNUSED_DEST)) \ - FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | SH_IMM(src2), DR(dst))); \ - } \ - else { \ - if (op & SLJIT_SET_Z) \ - FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ - if (!(flags & UNUSED_DEST)) \ - FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | D(dst), DR(dst))); \ - } - -static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, - sljit_s32 dst, sljit_s32 src1, sljit_sw src2) -{ - sljit_s32 is_overflow, is_carry, is_handled; - - switch (GET_OPCODE(op)) { - case SLJIT_MOV: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if (dst != src2) - return push_inst(compiler, ADDU | S(src2) | TA(0) | D(dst), DR(dst)); - return SLJIT_SUCCESS; - - case SLJIT_MOV_U8: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) - return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst)); - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_MOV_S8: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) - return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst)); -#else /* SLJIT_MIPS_REV < 1 */ - FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(24), DR(dst))); - return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(24), DR(dst)); -#endif /* SLJIT_MIPS_REV >= 1 */ - } - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_MOV_U16: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) - return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst)); - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_MOV_S16: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) - return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst)); -#else /* SLJIT_MIPS_REV < 1 */ - FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(16), DR(dst))); - return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(16), DR(dst)); -#endif /* SLJIT_MIPS_REV >= 1 */ - } - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_NOT: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (!(flags & UNUSED_DEST)) - FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst))); - return SLJIT_SUCCESS; - - case SLJIT_CLZ: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, CLZ | S(src2) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (!(flags & UNUSED_DEST)) - FAIL_IF(push_inst(compiler, CLZ | S(src2) | T(dst) | D(dst), DR(dst))); -#else /* SLJIT_MIPS_REV < 1 */ - if (SLJIT_UNLIKELY(flags & UNUSED_DEST)) { - FAIL_IF(push_inst(compiler, SRL | T(src2) | DA(EQUAL_FLAG) | SH_IMM(31), EQUAL_FLAG)); - return push_inst(compiler, XORI | SA(EQUAL_FLAG) | TA(EQUAL_FLAG) | IMM(1), EQUAL_FLAG); - } - /* Nearly all instructions are unmovable in the following sequence. */ - FAIL_IF(push_inst(compiler, ADDU | S(src2) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); - /* Check zero. */ - FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG1) | TA(0) | IMM(5), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, ORI | SA(0) | T(dst) | IMM(32), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(dst) | IMM(-1), DR(dst))); - /* Loop for searching the highest bit. */ - FAIL_IF(push_inst(compiler, ADDIU | S(dst) | T(dst) | IMM(1), DR(dst))); - FAIL_IF(push_inst(compiler, BGEZ | S(TMP_REG1) | IMM(-2), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, SLL | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), UNMOVABLE_INS)); -#endif /* SLJIT_MIPS_REV >= 1 */ - return SLJIT_SUCCESS; - - case SLJIT_ADD: - is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; - is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); - - if (flags & SRC2_IMM) { - if (is_overflow) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); - else - FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); - } - else if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); - - if (is_overflow || is_carry) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); - else { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); - FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); - } - } - /* dst may be the same as src1 or src2. */ - if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(src2), DR(dst))); - } - else { - if (is_overflow) - FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - else if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - - if (is_overflow || is_carry) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); - /* dst may be the same as src1 or src2. */ - if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) - FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | D(dst), DR(dst))); - } - - /* a + b >= a | b (otherwise, the carry should be set to 1). */ - if (is_overflow || is_carry) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); - if (!is_overflow) - return SLJIT_SUCCESS; - FAIL_IF(push_inst(compiler, SLL | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); - return push_inst(compiler, SRL | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG); - - case SLJIT_ADDC: - is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); - - if (flags & SRC2_IMM) { - if (is_carry) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); - else { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, OR | S(src1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - } - } - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(src2), DR(dst))); - } else { - if (is_carry) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - /* dst may be the same as src1 or src2. */ - FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | D(dst), DR(dst))); - } - if (is_carry) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - - FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst))); - if (!is_carry) - return SLJIT_SUCCESS; - - /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */ - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); - /* Set carry flag. */ - return push_inst(compiler, OR | SA(OTHER_FLAG) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); - - case SLJIT_SUB: - if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); - src2 = TMP_REG2; - flags &= ~SRC2_IMM; - } - - is_handled = 0; - - if (flags & SRC2_IMM) { - if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) { - FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); - is_handled = 1; - } - else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) { - FAIL_IF(push_inst(compiler, SLTI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); - is_handled = 1; - } - } - - if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) { - is_handled = 1; - - if (flags & SRC2_IMM) { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); - src2 = TMP_REG2; - flags &= ~SRC2_IMM; - } - - if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) { - FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); - } - else if (GET_FLAG_TYPE(op) == SLJIT_GREATER || GET_FLAG_TYPE(op) == SLJIT_LESS_EQUAL) - { - FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); - } - else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) { - FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); - } - else if (GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER || GET_FLAG_TYPE(op) == SLJIT_SIG_LESS_EQUAL) - { - FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); - } - } - - if (is_handled) { - if (flags & SRC2_IMM) { - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); - if (!(flags & UNUSED_DEST)) - return push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst)); - } - else { - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (!(flags & UNUSED_DEST)) - return push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst)); - } - return SLJIT_SUCCESS; - } - - is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; - is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); - - if (flags & SRC2_IMM) { - if (is_overflow) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); - else - FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); - } - else if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); - - if (is_overflow || is_carry) - FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); - /* dst may be the same as src1 or src2. */ - if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst))); - } - else { - if (is_overflow) - FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - else if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - - if (is_overflow || is_carry) - FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); - /* dst may be the same as src1 or src2. */ - if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) - FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst))); - } - - if (!is_overflow) - return SLJIT_SUCCESS; - FAIL_IF(push_inst(compiler, SLL | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); - return push_inst(compiler, SRL | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG); - - case SLJIT_SUBC: - if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); - src2 = TMP_REG2; - flags &= ~SRC2_IMM; - } - - is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); - - if (flags & SRC2_IMM) { - if (is_carry) - FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); - /* dst may be the same as src1 or src2. */ - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst))); - } - else { - if (is_carry) - FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - /* dst may be the same as src1 or src2. */ - FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst))); - } - - if (is_carry) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | D(TMP_REG1), DR(TMP_REG1))); - - FAIL_IF(push_inst(compiler, SUBU | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst))); - return (is_carry) ? push_inst(compiler, OR | SA(EQUAL_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG) : SLJIT_SUCCESS; - - case SLJIT_MUL: - SLJIT_ASSERT(!(flags & SRC2_IMM)); - - if (GET_FLAG_TYPE(op) != SLJIT_OVERFLOW) { -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) - return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst)); -#else /* SLJIT_MIPS_REV < 1 */ - FAIL_IF(push_inst(compiler, MULT | S(src1) | T(src2), MOVABLE_INS)); - return push_inst(compiler, MFLO | D(dst), DR(dst)); -#endif /* SLJIT_MIPS_REV >= 1 */ - } - -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) - FAIL_IF(push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst))); - FAIL_IF(push_inst(compiler, MUH | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); -#else /* SLJIT_MIPS_REV < 6 */ - FAIL_IF(push_inst(compiler, MULT | S(src1) | T(src2), MOVABLE_INS)); - FAIL_IF(push_inst(compiler, MFHI | DA(EQUAL_FLAG), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst))); -#endif /* SLJIT_MIPS_REV >= 6 */ - FAIL_IF(push_inst(compiler, SRA | T(dst) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG)); - return push_inst(compiler, SUBU | SA(EQUAL_FLAG) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); - - case SLJIT_AND: - EMIT_LOGICAL(ANDI, AND); - return SLJIT_SUCCESS; - - case SLJIT_OR: - EMIT_LOGICAL(ORI, OR); - return SLJIT_SUCCESS; - - case SLJIT_XOR: - EMIT_LOGICAL(XORI, XOR); - return SLJIT_SUCCESS; - - case SLJIT_SHL: - EMIT_SHIFT(SLL, SLLV); - return SLJIT_SUCCESS; - - case SLJIT_LSHR: - EMIT_SHIFT(SRL, SRLV); - return SLJIT_SUCCESS; - - case SLJIT_ASHR: - EMIT_SHIFT(SRA, SRAV); - return SLJIT_SUCCESS; - } - - SLJIT_UNREACHABLE(); - return SLJIT_SUCCESS; -} - static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value) { FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 16), DR(dst))); @@ -573,8 +196,8 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile sljit_s32 arg_types) { struct sljit_jump *jump; - sljit_u32 extra_space = (sljit_u32)type; - sljit_ins ins; + sljit_u32 extra_space = 0; + sljit_ins ins = NOP; CHECK_ERROR_PTR(); CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); @@ -583,14 +206,23 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile PTR_FAIL_IF(!jump); set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); - PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins, &extra_space)); + if ((type & 0xff) != SLJIT_CALL_REG_ARG) { + extra_space = (sljit_u32)type; + PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins, &extra_space)); + } else if (type & SLJIT_CALL_RETURN) + PTR_FAIL_IF(emit_stack_frame_release(compiler, 0, &ins)); SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); - PTR_FAIL_IF(emit_const(compiler, PIC_ADDR_REG, 0)); + if (ins == NOP && compiler->delay_slot != UNMOVABLE_INS) + jump->flags |= IS_MOVABLE; if (!(type & SLJIT_CALL_RETURN) || extra_space > 0) { - jump->flags |= IS_JAL | IS_CALL; + jump->flags |= IS_JAL; + + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + jump->flags |= IS_CALL; + PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); } else PTR_FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS)); @@ -598,6 +230,9 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile jump->addr = compiler->size; PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS)); + /* Maximum number of instructions required for generating a constant. */ + compiler->size += 2; + if (extra_space == 0) return jump; @@ -623,16 +258,37 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi CHECK_ERROR(); CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw)); + src = PIC_ADDR_REG; + srcw = 0; + } + + if ((type & 0xff) == SLJIT_CALL_REG_ARG) { + if (type & SLJIT_CALL_RETURN) { + if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + FAIL_IF(push_inst(compiler, ADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG))); + src = PIC_ADDR_REG; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, 0, &ins)); + + if (ins != NOP) + FAIL_IF(push_inst(compiler, ins, MOVABLE_INS)); + } + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, type, src, srcw); + } + SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); if (src & SLJIT_IMM) FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw)); - else if (FAST_IS_REG(src)) + else if (src != PIC_ADDR_REG) FAIL_IF(push_inst(compiler, ADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG))); - else if (src & SLJIT_MEM) { - ADJUST_LOCAL_OFFSET(src, srcw); - FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw)); - } FAIL_IF(call_with_args(compiler, arg_types, &ins, &extra_space)); diff --git a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_64.c b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_64.c index c2b3d839c2..d2a5924f8e 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_64.c +++ b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_64.c @@ -118,421 +118,6 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_a return !(imm & 0xffff) ? SLJIT_SUCCESS : push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar); } -#define SELECT_OP(a, b) \ - (!(op & SLJIT_32) ? a : b) - -#define EMIT_LOGICAL(op_imm, op_norm) \ - if (flags & SRC2_IMM) { \ - if (op & SLJIT_SET_Z) \ - FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \ - if (!(flags & UNUSED_DEST)) \ - FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \ - } \ - else { \ - if (op & SLJIT_SET_Z) \ - FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ - if (!(flags & UNUSED_DEST)) \ - FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | D(dst), DR(dst))); \ - } - -#define EMIT_SHIFT(op_dimm, op_dimm32, op_imm, op_dv, op_v) \ - if (flags & SRC2_IMM) { \ - if (src2 >= 32) { \ - SLJIT_ASSERT(!(op & SLJIT_32)); \ - ins = op_dimm32; \ - src2 -= 32; \ - } \ - else \ - ins = (op & SLJIT_32) ? op_imm : op_dimm; \ - if (op & SLJIT_SET_Z) \ - FAIL_IF(push_inst(compiler, ins | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); \ - if (!(flags & UNUSED_DEST)) \ - FAIL_IF(push_inst(compiler, ins | T(src1) | D(dst) | SH_IMM(src2), DR(dst))); \ - } \ - else { \ - ins = (op & SLJIT_32) ? op_v : op_dv; \ - if (op & SLJIT_SET_Z) \ - FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ - if (!(flags & UNUSED_DEST)) \ - FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | D(dst), DR(dst))); \ - } - -static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, - sljit_s32 dst, sljit_s32 src1, sljit_sw src2) -{ - sljit_ins ins; - sljit_s32 is_overflow, is_carry, is_handled; - - switch (GET_OPCODE(op)) { - case SLJIT_MOV: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if (dst != src2) - return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src2) | TA(0) | D(dst), DR(dst)); - return SLJIT_SUCCESS; - - case SLJIT_MOV_U8: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) - return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst)); - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_MOV_S8: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) - if (op & SLJIT_32) - return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst)); -#endif /* SLJIT_MIPS_REV >= 1 */ - FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(24), DR(dst))); - return push_inst(compiler, DSRA32 | T(dst) | D(dst) | SH_IMM(24), DR(dst)); - } - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_MOV_U16: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) - return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst)); - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_MOV_S16: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) - if (op & SLJIT_32) - return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst)); -#endif /* SLJIT_MIPS_REV >= 1 */ - FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(16), DR(dst))); - return push_inst(compiler, DSRA32 | T(dst) | D(dst) | SH_IMM(16), DR(dst)); - } - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_MOV_U32: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM) && !(op & SLJIT_32)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2) - if (dst == src2) - return push_inst(compiler, DINSU | T(src2) | SA(0) | (31 << 11) | (0 << 11), DR(dst)); -#endif /* SLJIT_MIPS_REV >= 2 */ - FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(0), DR(dst))); - return push_inst(compiler, DSRL32 | T(dst) | D(dst) | SH_IMM(0), DR(dst)); - } - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_MOV_S32: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM) && !(op & SLJIT_32)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { - return push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(0), DR(dst)); - } - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_NOT: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (!(flags & UNUSED_DEST)) - FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst))); - return SLJIT_SUCCESS; - - case SLJIT_CLZ: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (!(flags & UNUSED_DEST)) - FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | T(dst) | D(dst), DR(dst))); -#else /* SLJIT_MIPS_REV < 1 */ - if (SLJIT_UNLIKELY(flags & UNUSED_DEST)) { - FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(src2) | DA(EQUAL_FLAG) | SH_IMM(31), EQUAL_FLAG)); - return push_inst(compiler, XORI | SA(EQUAL_FLAG) | TA(EQUAL_FLAG) | IMM(1), EQUAL_FLAG); - } - /* Nearly all instructions are unmovable in the following sequence. */ - FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src2) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); - /* Check zero. */ - FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG1) | TA(0) | IMM(5), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, ORI | SA(0) | T(dst) | IMM((op & SLJIT_32) ? 32 : 64), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | T(dst) | IMM(-1), DR(dst))); - /* Loop for searching the highest bit. */ - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(dst) | T(dst) | IMM(1), DR(dst))); - FAIL_IF(push_inst(compiler, BGEZ | S(TMP_REG1) | IMM(-2), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, SELECT_OP(DSLL, SLL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), UNMOVABLE_INS)); -#endif /* SLJIT_MIPS_REV >= 1 */ - return SLJIT_SUCCESS; - - case SLJIT_ADD: - is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; - is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); - - if (flags & SRC2_IMM) { - if (is_overflow) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); - else - FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); - } - else if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); - - if (is_overflow || is_carry) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); - else { - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); - FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); - } - } - /* dst may be the same as src1 or src2. */ - if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst))); - } - else { - if (is_overflow) - FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - else if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - - if (is_overflow || is_carry) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); - /* dst may be the same as src1 or src2. */ - if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) - FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst))); - } - - /* a + b >= a | b (otherwise, the carry should be set to 1). */ - if (is_overflow || is_carry) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); - if (!is_overflow) - return SLJIT_SUCCESS; - FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); - return push_inst(compiler, SELECT_OP(DSRL32, SRL) | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG); - - case SLJIT_ADDC: - is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); - - if (flags & SRC2_IMM) { - if (is_carry) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); - else { - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, OR | S(src1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - } - } - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst))); - } else { - if (is_carry) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - /* dst may be the same as src1 or src2. */ - FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst))); - } - if (is_carry) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - - FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst))); - if (!is_carry) - return SLJIT_SUCCESS; - - /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */ - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); - /* Set carry flag. */ - return push_inst(compiler, OR | SA(OTHER_FLAG) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); - - case SLJIT_SUB: - if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); - src2 = TMP_REG2; - flags &= ~SRC2_IMM; - } - - is_handled = 0; - - if (flags & SRC2_IMM) { - if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) { - FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); - is_handled = 1; - } - else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) { - FAIL_IF(push_inst(compiler, SLTI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); - is_handled = 1; - } - } - - if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) { - is_handled = 1; - - if (flags & SRC2_IMM) { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); - src2 = TMP_REG2; - flags &= ~SRC2_IMM; - } - - if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) { - FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); - } - else if (GET_FLAG_TYPE(op) == SLJIT_GREATER || GET_FLAG_TYPE(op) == SLJIT_LESS_EQUAL) - { - FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); - } - else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) { - FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); - } - else if (GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER || GET_FLAG_TYPE(op) == SLJIT_SIG_LESS_EQUAL) - { - FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); - } - } - - if (is_handled) { - if (flags & SRC2_IMM) { - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); - if (!(flags & UNUSED_DEST)) - return push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)); - } - else { - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (!(flags & UNUSED_DEST)) - return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)); - } - return SLJIT_SUCCESS; - } - - is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; - is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); - - if (flags & SRC2_IMM) { - if (is_overflow) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); - else - FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); - } - else if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); - - if (is_overflow || is_carry) - FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); - /* dst may be the same as src1 or src2. */ - if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst))); - } - else { - if (is_overflow) - FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - else if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - - if (is_overflow || is_carry) - FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); - /* dst may be the same as src1 or src2. */ - if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) - FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst))); - } - - if (!is_overflow) - return SLJIT_SUCCESS; - FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); - if (op & SLJIT_SET_Z) - FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); - return push_inst(compiler, SELECT_OP(DSRL32, SRL) | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG); - - case SLJIT_SUBC: - if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); - src2 = TMP_REG2; - flags &= ~SRC2_IMM; - } - - is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); - - if (flags & SRC2_IMM) { - if (is_carry) - FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); - /* dst may be the same as src1 or src2. */ - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst))); - } - else { - if (is_carry) - FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - /* dst may be the same as src1 or src2. */ - FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst))); - } - - if (is_carry) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | D(TMP_REG1), DR(TMP_REG1))); - - FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst))); - return (is_carry) ? push_inst(compiler, OR | SA(EQUAL_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG) : SLJIT_SUCCESS; - - case SLJIT_MUL: - SLJIT_ASSERT(!(flags & SRC2_IMM)); - - if (GET_FLAG_TYPE(op) != SLJIT_OVERFLOW) { -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) - return push_inst(compiler, SELECT_OP(DMUL, MUL) | S(src1) | T(src2) | D(dst), DR(dst)); -#elif (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) - if (op & SLJIT_32) - return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst)); - FAIL_IF(push_inst(compiler, DMULT | S(src1) | T(src2), MOVABLE_INS)); - return push_inst(compiler, MFLO | D(dst), DR(dst)); -#else /* SLJIT_MIPS_REV < 1 */ - FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS)); - return push_inst(compiler, MFLO | D(dst), DR(dst)); -#endif /* SLJIT_MIPS_REV >= 6 */ - } - -#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) - FAIL_IF(push_inst(compiler, SELECT_OP(DMUL, MUL) | S(src1) | T(src2) | D(dst), DR(dst))); - FAIL_IF(push_inst(compiler, SELECT_OP(DMUH, MUH) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); -#else /* SLJIT_MIPS_REV < 6 */ - FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS)); - FAIL_IF(push_inst(compiler, MFHI | DA(EQUAL_FLAG), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst))); -#endif /* SLJIT_MIPS_REV >= 6 */ - FAIL_IF(push_inst(compiler, SELECT_OP(DSRA32, SRA) | T(dst) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG)); - return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(EQUAL_FLAG) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); - - case SLJIT_AND: - EMIT_LOGICAL(ANDI, AND); - return SLJIT_SUCCESS; - - case SLJIT_OR: - EMIT_LOGICAL(ORI, OR); - return SLJIT_SUCCESS; - - case SLJIT_XOR: - EMIT_LOGICAL(XORI, XOR); - return SLJIT_SUCCESS; - - case SLJIT_SHL: - EMIT_SHIFT(DSLL, DSLL32, SLL, DSLLV, SLLV); - return SLJIT_SUCCESS; - - case SLJIT_LSHR: - EMIT_SHIFT(DSRL, DSRL32, SRL, DSRLV, SRLV); - return SLJIT_SUCCESS; - - case SLJIT_ASHR: - EMIT_SHIFT(DSRA, DSRA32, SRA, DSRAV, SRAV); - return SLJIT_SUCCESS; - } - - SLJIT_UNREACHABLE(); - return SLJIT_SUCCESS; -} - static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value) { FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 48), DR(dst))); @@ -653,14 +238,20 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile if (type & SLJIT_CALL_RETURN) PTR_FAIL_IF(emit_stack_frame_release(compiler, 0, &ins)); - PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins)); + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins)); SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); - PTR_FAIL_IF(emit_const(compiler, PIC_ADDR_REG, 0)); + if (ins == NOP && compiler->delay_slot != UNMOVABLE_INS) + jump->flags |= IS_MOVABLE; if (!(type & SLJIT_CALL_RETURN)) { - jump->flags |= IS_JAL | IS_CALL; + jump->flags |= IS_JAL; + + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + jump->flags |= IS_CALL; + PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); } else PTR_FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS)); @@ -668,6 +259,8 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile jump->addr = compiler->size; PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS)); + /* Maximum number of instructions required for generating a constant. */ + compiler->size += 6; return jump; } @@ -680,16 +273,37 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi CHECK_ERROR(); CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw)); + src = PIC_ADDR_REG; + srcw = 0; + } + + if ((type & 0xff) == SLJIT_CALL_REG_ARG) { + if (type & SLJIT_CALL_RETURN) { + if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + FAIL_IF(push_inst(compiler, DADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG))); + src = PIC_ADDR_REG; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, 0, &ins)); + + if (ins != NOP) + FAIL_IF(push_inst(compiler, ins, MOVABLE_INS)); + } + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, type, src, srcw); + } + SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); if (src & SLJIT_IMM) FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw)); - else if (FAST_IS_REG(src)) + else if (src != PIC_ADDR_REG) FAIL_IF(push_inst(compiler, DADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG))); - else if (src & SLJIT_MEM) { - ADJUST_LOCAL_OFFSET(src, srcw); - FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw)); - } if (type & SLJIT_CALL_RETURN) FAIL_IF(emit_stack_frame_release(compiler, 0, &ins)); diff --git a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_common.c b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_common.c index be5cb22a23..9afe901c38 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativeMIPS_common.c +++ b/thirdparty/pcre2/src/sljit/sljitNativeMIPS_common.c @@ -42,6 +42,14 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) return "MIPS64-R6" SLJIT_CPUINFO; #endif /* SLJIT_CONFIG_MIPS_32 */ +#elif (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2) + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + return "MIPS32-R2" SLJIT_CPUINFO; +#else /* !SLJIT_CONFIG_MIPS_32 */ + return "MIPS64-R2" SLJIT_CPUINFO; +#endif /* SLJIT_CONFIG_MIPS_32 */ + #elif (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) @@ -151,12 +159,18 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 4] = { #define BREAK (HI(0) | LO(13)) #define CFC1 (HI(17) | (2 << 21)) #if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) +#define C_EQ_S (HI(17) | CMP_FMT_S | LO(2)) +#define C_OLE_S (HI(17) | CMP_FMT_S | LO(6)) +#define C_OLT_S (HI(17) | CMP_FMT_S | LO(4)) #define C_UEQ_S (HI(17) | CMP_FMT_S | LO(3)) #define C_ULE_S (HI(17) | CMP_FMT_S | LO(7)) #define C_ULT_S (HI(17) | CMP_FMT_S | LO(5)) #define C_UN_S (HI(17) | CMP_FMT_S | LO(1)) #define C_FD (FD(TMP_FREG3)) #else /* SLJIT_MIPS_REV < 6 */ +#define C_EQ_S (HI(17) | FMT_S | LO(50)) +#define C_OLE_S (HI(17) | FMT_S | LO(54)) +#define C_OLT_S (HI(17) | FMT_S | LO(52)) #define C_UEQ_S (HI(17) | FMT_S | LO(51)) #define C_ULE_S (HI(17) | FMT_S | LO(55)) #define C_ULT_S (HI(17) | FMT_S | LO(53)) @@ -187,6 +201,9 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 4] = { #endif /* SLJIT_MIPS_REV >= 6 */ #define DIV_S (HI(17) | FMT_S | LO(3)) #define DINSU (HI(31) | LO(6)) +#define DROTR (HI(0) | (1 << 21) | LO(58)) +#define DROTR32 (HI(0) | (1 << 21) | LO(62)) +#define DROTRV (HI(0) | (1 << 6) | LO(22)) #define DSLL (HI(0) | LO(56)) #define DSLL32 (HI(0) | LO(60)) #define DSLLV (HI(0) | LO(20)) @@ -206,9 +223,13 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 4] = { #define JR (HI(0) | LO(8)) #endif /* SLJIT_MIPS_REV >= 6 */ #define LD (HI(55)) +#define LDL (HI(26)) +#define LDR (HI(27)) #define LDC1 (HI(53)) #define LUI (HI(15)) #define LW (HI(35)) +#define LWL (HI(34)) +#define LWR (HI(38)) #define LWC1 (HI(49)) #define MFC1 (HI(17)) #if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) @@ -235,7 +256,11 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 4] = { #define NOR (HI(0) | LO(39)) #define OR (HI(0) | LO(37)) #define ORI (HI(13)) +#define ROTR (HI(0) | (1 << 21) | LO(2)) +#define ROTRV (HI(0) | (1 << 6) | LO(6)) #define SD (HI(63)) +#define SDL (HI(44)) +#define SDR (HI(45)) #define SDC1 (HI(61)) #define SLT (HI(0) | LO(42)) #define SLTI (HI(10)) @@ -250,6 +275,8 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 4] = { #define SUB_S (HI(17) | FMT_S | LO(1)) #define SUBU (HI(0) | LO(35)) #define SW (HI(43)) +#define SWL (HI(42)) +#define SWR (HI(46)) #define SWC1 (HI(57)) #define TRUNC_W_S (HI(17) | FMT_S | LO(13)) #define XOR (HI(0) | LO(38)) @@ -277,12 +304,18 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 4] = { #define ADDU_W ADDU #define ADDIU_W ADDIU #define SLL_W SLL +#define SRA_W SRA #define SUBU_W SUBU +#define STORE_W SW +#define LOAD_W LW #else #define ADDU_W DADDU #define ADDIU_W DADDIU #define SLL_W DSLL +#define SRA_W DSRA #define SUBU_W DSUBU +#define STORE_W SD +#define LOAD_W LD #endif #define SIMM_MAX (0x7fff) @@ -315,19 +348,21 @@ static SLJIT_INLINE sljit_ins invert_branch(sljit_uw flags) return (1 << 16); } -static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code, sljit_sw executable_offset) +static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code, sljit_sw executable_offset) { sljit_sw diff; sljit_uw target_addr; sljit_ins *inst; sljit_ins saved_inst; + inst = (sljit_ins *)jump->addr; + #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) if (jump->flags & (SLJIT_REWRITABLE_JUMP | IS_CALL)) - return code_ptr; + goto exit; #else if (jump->flags & SLJIT_REWRITABLE_JUMP) - return code_ptr; + goto exit; #endif if (jump->flags & JUMP_ADDR) @@ -337,13 +372,12 @@ static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_i target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset; } - inst = (sljit_ins *)jump->addr; if (jump->flags & IS_COND) inst--; #if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) if (jump->flags & IS_CALL) - goto keep_address; + goto preserve_addr; #endif /* B instructions. */ @@ -364,15 +398,14 @@ static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_i jump->addr -= 2 * sizeof(sljit_ins); return inst; } - } - else { + } else { diff = ((sljit_sw)target_addr - (sljit_sw)(inst + 1) - executable_offset) >> 2; if (diff <= SIMM_MAX && diff >= SIMM_MIN) { jump->flags |= PATCH_B; if (!(jump->flags & IS_COND)) { inst[0] = (jump->flags & IS_JAL) ? BAL : B; - inst[1] = NOP; + /* Keep inst[1] */ return inst + 1; } inst[0] ^= invert_branch(jump->flags); @@ -415,36 +448,46 @@ static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_i if ((target_addr & ~(sljit_uw)0xfffffff) == ((jump->addr + sizeof(sljit_ins)) & ~(sljit_uw)0xfffffff)) { jump->flags |= PATCH_J; inst[0] = (jump->flags & IS_JAL) ? JAL : J; - inst[1] = NOP; + /* Keep inst[1] */ return inst + 1; } } + if (jump->flags & IS_COND) + inst++; + #if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) -keep_address: +preserve_addr: if (target_addr <= 0x7fffffff) { jump->flags |= PATCH_ABS32; - if (jump->flags & IS_COND) { - inst[0] -= 4; - inst++; - } - inst[2] = inst[6]; - inst[3] = inst[7]; + if (jump->flags & IS_COND) + inst[-1] -= 4; + + inst[2] = inst[0]; + inst[3] = inst[1]; return inst + 3; } if (target_addr <= 0x7fffffffffffl) { jump->flags |= PATCH_ABS48; - if (jump->flags & IS_COND) { - inst[0] -= 2; - inst++; - } - inst[4] = inst[6]; - inst[5] = inst[7]; + if (jump->flags & IS_COND) + inst[-1] -= 2; + + inst[4] = inst[0]; + inst[5] = inst[1]; return inst + 5; } #endif - return code_ptr; +exit: +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + inst[2] = inst[0]; + inst[3] = inst[1]; + return inst + 3; +#else + inst[6] = inst[0]; + inst[7] = inst[1]; + return inst + 7; +#endif } #ifdef __GNUC__ @@ -459,30 +502,52 @@ static __attribute__ ((noinline)) void sljit_cache_flush(void* code, void* code_ static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label) { if (max_label < 0x80000000l) { - put_label->flags = 0; + put_label->flags = PATCH_ABS32; return 1; } if (max_label < 0x800000000000l) { - put_label->flags = 1; + put_label->flags = PATCH_ABS48; return 3; } - put_label->flags = 2; + put_label->flags = 0; return 5; } -static SLJIT_INLINE void put_label_set(struct sljit_put_label *put_label) +#endif /* SLJIT_CONFIG_MIPS_64 */ + +static SLJIT_INLINE void load_addr_to_reg(void *dst, sljit_u32 reg) { - sljit_uw addr = put_label->label->addr; - sljit_ins *inst = (sljit_ins *)put_label->addr; - sljit_u32 reg = *inst; + struct sljit_jump *jump; + struct sljit_put_label *put_label; + sljit_uw flags; + sljit_ins *inst; + sljit_uw addr; - if (put_label->flags == 0) { + if (reg != 0) { + jump = (struct sljit_jump*)dst; + flags = jump->flags; + inst = (sljit_ins*)jump->addr; + addr = (flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; + } else { + put_label = (struct sljit_put_label*)dst; +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) + flags = put_label->flags; +#endif + inst = (sljit_ins*)put_label->addr; + addr = put_label->label->addr; + reg = *inst; + } + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + inst[0] = LUI | T(reg) | IMM(addr >> 16); +#else /* !SLJIT_CONFIG_MIPS_32 */ + if (flags & PATCH_ABS32) { SLJIT_ASSERT(addr < 0x80000000l); inst[0] = LUI | T(reg) | IMM(addr >> 16); } - else if (put_label->flags == 1) { + else if (flags & PATCH_ABS48) { SLJIT_ASSERT(addr < 0x800000000000l); inst[0] = LUI | T(reg) | IMM(addr >> 32); inst[1] = ORI | S(reg) | T(reg) | IMM((addr >> 16) & 0xffff); @@ -497,12 +562,11 @@ static SLJIT_INLINE void put_label_set(struct sljit_put_label *put_label) inst[4] = DSLL | T(reg) | D(reg) | SH_IMM(16); inst += 4; } +#endif /* SLJIT_CONFIG_MIPS_32 */ inst[1] = ORI | S(reg) | T(reg) | IMM(addr & 0xffff); } -#endif - SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) { struct sljit_memory_fragment *buf; @@ -557,11 +621,12 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } if (jump && jump->addr == word_count) { #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) - jump->addr = (sljit_uw)(code_ptr - 3); + word_count += 2; #else - jump->addr = (sljit_uw)(code_ptr - 7); + word_count += 6; #endif - code_ptr = detect_jump_type(jump, code_ptr, code, executable_offset); + jump->addr = (sljit_uw)(code_ptr - 1); + code_ptr = detect_jump_type(jump, code, executable_offset); jump = jump->next; } if (const_ && const_->addr == word_count) { @@ -571,7 +636,10 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil if (put_label && put_label->addr == word_count) { SLJIT_ASSERT(put_label->label); put_label->addr = (sljit_uw)code_ptr; -#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + code_ptr += 1; + word_count += 1; +#else code_ptr += put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size)); word_count += 5; #endif @@ -579,8 +647,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } next_addr = compute_next_addr(label, jump, const_, put_label); } - code_ptr ++; - word_count ++; + code_ptr++; + word_count++; } while (buf_ptr < buf_end); buf = buf->next; @@ -617,51 +685,14 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil break; } - /* Set the fields of immediate loads. */ -#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) - SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1]) & 0xffff) == 0); - buf_ptr[0] |= (sljit_ins)(addr >> 16) & 0xffff; - buf_ptr[1] |= (sljit_ins)addr & 0xffff; -#else - if (jump->flags & PATCH_ABS32) { - SLJIT_ASSERT(addr <= 0x7fffffff); - SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1]) & 0xffff) == 0); - buf_ptr[0] |= (sljit_ins)(addr >> 16) & 0xffff; - buf_ptr[1] |= (sljit_ins)addr & 0xffff; - break; - } - - if (jump->flags & PATCH_ABS48) { - SLJIT_ASSERT(addr <= 0x7fffffffffffl); - SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1] | buf_ptr[3]) & 0xffff) == 0); - buf_ptr[0] |= (sljit_ins)(addr >> 32) & 0xffff; - buf_ptr[1] |= (sljit_ins)(addr >> 16) & 0xffff; - buf_ptr[3] |= (sljit_ins)addr & 0xffff; - break; - } - - SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1] | buf_ptr[3] | buf_ptr[5]) & 0xffff) == 0); - buf_ptr[0] |= (sljit_ins)(addr >> 48) & 0xffff; - buf_ptr[1] |= (sljit_ins)(addr >> 32) & 0xffff; - buf_ptr[3] |= (sljit_ins)(addr >> 16) & 0xffff; - buf_ptr[5] |= (sljit_ins)addr & 0xffff; -#endif + load_addr_to_reg(jump, PIC_ADDR_REG); } while (0); jump = jump->next; } put_label = compiler->put_labels; while (put_label) { -#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) - addr = put_label->label->addr; - buf_ptr = (sljit_ins *)put_label->addr; - - SLJIT_ASSERT((buf_ptr[0] & 0xffe00000) == LUI && (buf_ptr[1] & 0xfc000000) == ORI); - buf_ptr[0] |= (addr >> 16) & 0xffff; - buf_ptr[1] |= addr & 0xffff; -#else - put_label_set(put_label); -#endif + load_addr_to_reg(put_label, 0); put_label = put_label->next; } @@ -700,19 +731,29 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) #endif case SLJIT_HAS_ZERO_REGISTER: return 1; - #if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) case SLJIT_HAS_CLZ: case SLJIT_HAS_CMOV: case SLJIT_HAS_PREFETCH: return 1; -#endif /* SLJIT_MIPS_REV >= 1 */ + case SLJIT_HAS_CTZ: + return 2; +#endif /* SLJIT_MIPS_REV >= 1 */ +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2) + case SLJIT_HAS_ROT: + return 1; +#endif /* SLJIT_MIPS_REV >= 2 */ default: return 0; } } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type) +{ + return (type >= SLJIT_ORDERED_EQUAL && type <= SLJIT_ORDERED_LESS_EQUAL); +} + /* --------------------------------------------------------------------- */ /* Entry, exit */ /* --------------------------------------------------------------------- */ @@ -747,14 +788,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) #define SLOW_SRC2 0x20000 #define SLOW_DEST 0x40000 -#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) -#define STACK_STORE SW -#define STACK_LOAD LW -#else -#define STACK_STORE SD -#define STACK_LOAD LD -#endif - static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw); static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size, sljit_ins *ins_ptr); @@ -770,13 +803,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi { sljit_ins base; sljit_s32 i, tmp, offset; - sljit_s32 arg_count, word_arg_count, saved_arg_count, float_arg_count; + sljit_s32 arg_count, word_arg_count, float_arg_count; + sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options); CHECK_ERROR(); CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); + local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1); #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) { if ((local_size & SSIZE_OF(sw)) != 0) @@ -791,27 +825,27 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi #endif compiler->local_size = local_size; -#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) - tmp = arg_types >> SLJIT_ARG_SHIFT; - arg_count = 0; offset = 0; - - while (tmp) { - offset = arg_count; - if ((tmp & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F64) { - if ((arg_count & 0x1) != 0) +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + if (!(options & SLJIT_ENTER_REG_ARG)) { + tmp = arg_types >> SLJIT_ARG_SHIFT; + arg_count = 0; + + while (tmp) { + offset = arg_count; + if ((tmp & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F64) { + if ((arg_count & 0x1) != 0) + arg_count++; arg_count++; + } + arg_count++; + tmp >>= SLJIT_ARG_SHIFT; } - arg_count++; - tmp >>= SLJIT_ARG_SHIFT; + compiler->args_size = (sljit_uw)arg_count << 2; + offset = (offset >= 4) ? (offset << 2) : 0; } - - compiler->args_size = (sljit_uw)arg_count << 2; - offset = (offset >= 4) ? (offset << 2) : 0; -#else /* !SLJIT_CONFIG_MIPS_32 */ - offset = 0; #endif /* SLJIT_CONFIG_MIPS_32 */ if (local_size + offset <= -SIMM_MIN) { @@ -820,9 +854,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi base = S(SLJIT_SP); offset = local_size - SSIZE_OF(sw); } else { - FAIL_IF(load_immediate(compiler, DR(OTHER_FLAG), local_size)); + FAIL_IF(load_immediate(compiler, OTHER_FLAG, local_size)); FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_SP) | TA(0) | D(TMP_REG2), DR(TMP_REG2))); - FAIL_IF(push_inst(compiler, SUBU_W | S(SLJIT_SP) | T(OTHER_FLAG) | D(SLJIT_SP), DR(SLJIT_SP))); + FAIL_IF(push_inst(compiler, SUBU_W | S(SLJIT_SP) | TA(OTHER_FLAG) | D(SLJIT_SP), DR(SLJIT_SP))); base = S(TMP_REG2); offset = -SSIZE_OF(sw); #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) @@ -830,17 +864,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi #endif } - FAIL_IF(push_inst(compiler, STACK_STORE | base | TA(RETURN_ADDR_REG) | IMM(offset), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, STORE_W | base | TA(RETURN_ADDR_REG) | IMM(offset), UNMOVABLE_INS)); tmp = SLJIT_S0 - saveds; - for (i = SLJIT_S0; i > tmp; i--) { + for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) { offset -= SSIZE_OF(sw); - FAIL_IF(push_inst(compiler, STACK_STORE | base | T(i) | IMM(offset), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, STORE_W | base | T(i) | IMM(offset), MOVABLE_INS)); } for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) { offset -= SSIZE_OF(sw); - FAIL_IF(push_inst(compiler, STACK_STORE | base | T(i) | IMM(offset), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, STORE_W | base | T(i) | IMM(offset), MOVABLE_INS)); } #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) @@ -860,10 +894,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi FAIL_IF(push_inst(compiler, SDC1 | base | FT(i) | IMM(offset), MOVABLE_INS)); } + if (options & SLJIT_ENTER_REG_ARG) + return SLJIT_SUCCESS; + arg_types >>= SLJIT_ARG_SHIFT; arg_count = 0; word_arg_count = 0; - saved_arg_count = 0; float_arg_count = 0; #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) @@ -970,7 +1006,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); + local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1); #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) { if ((local_size & SSIZE_OF(sw)) != 0) @@ -989,14 +1025,19 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size, sljit_ins *ins_ptr) { sljit_s32 local_size, i, tmp, offset; + sljit_s32 load_return_addr = (frame_size == 0); sljit_s32 scratches = compiler->scratches; sljit_s32 saveds = compiler->saveds; sljit_s32 fsaveds = compiler->fsaveds; sljit_s32 fscratches = compiler->fscratches; + sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options); + + SLJIT_ASSERT(frame_size == 1 || (frame_size & 0xf) == 0); + frame_size &= ~0xf; local_size = compiler->local_size; - tmp = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); + tmp = GET_SAVED_REGISTERS_SIZE(scratches, saveds - kept_saveds_count, 1); #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) { if ((tmp & SSIZE_OF(sw)) != 0) @@ -1024,18 +1065,18 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit SLJIT_ASSERT(local_size >= frame_size); offset = local_size - SSIZE_OF(sw); - if (frame_size == 0) - FAIL_IF(push_inst(compiler, STACK_LOAD | S(SLJIT_SP) | TA(RETURN_ADDR_REG) | IMM(offset), RETURN_ADDR_REG)); + if (load_return_addr) + FAIL_IF(push_inst(compiler, LOAD_W | S(SLJIT_SP) | TA(RETURN_ADDR_REG) | IMM(offset), RETURN_ADDR_REG)); tmp = SLJIT_S0 - saveds; - for (i = SLJIT_S0; i > tmp; i--) { + for (i = SLJIT_S0 - kept_saveds_count; i > tmp; i--) { offset -= SSIZE_OF(sw); - FAIL_IF(push_inst(compiler, STACK_LOAD | S(SLJIT_SP) | T(i) | IMM(offset), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, LOAD_W | S(SLJIT_SP) | T(i) | IMM(offset), MOVABLE_INS)); } for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) { offset -= SSIZE_OF(sw); - FAIL_IF(push_inst(compiler, STACK_LOAD | S(SLJIT_SP) | T(i) | IMM(offset), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, LOAD_W | S(SLJIT_SP) | T(i) | IMM(offset), MOVABLE_INS)); } #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) @@ -1076,8 +1117,38 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler return push_inst(compiler, ins, UNMOVABLE_INS); } -#undef STACK_STORE -#undef STACK_LOAD +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ + sljit_ins ins; + + CHECK_ERROR(); + CHECK(check_sljit_emit_return_to(compiler, src, srcw)); + + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw)); + src = PIC_ADDR_REG; + srcw = 0; + } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG))); + src = PIC_ADDR_REG; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, 1, &ins)); + + if (!(src & SLJIT_IMM)) { + FAIL_IF(push_inst(compiler, JR | S(src), UNMOVABLE_INS)); + return push_inst(compiler, ins, UNMOVABLE_INS); + } + + if (ins != NOP) + FAIL_IF(push_inst(compiler, ins, MOVABLE_INS)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); +} /* --------------------------------------------------------------------- */ /* Operators */ @@ -1134,9 +1205,10 @@ static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flag return 0; } +#define TO_ARGW_HI(argw) (((argw) & ~0xffff) + (((argw) & 0x8000) ? 0x10000 : 0)) + /* See getput_arg below. - Note: can_cache is called only for binary operators. Those - operators always uses word arguments without write back. */ + Note: can_cache is called only for binary operators. */ static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) { SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM)); @@ -1151,7 +1223,8 @@ static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, slj } if (arg == next_arg) { - if (((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN)) + if (((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN) + || TO_ARGW_HI(argw) == TO_ARGW_HI(next_argw)) return 1; return 0; } @@ -1163,6 +1236,7 @@ static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, slj static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) { sljit_s32 tmp_ar, base, delay_slot; + sljit_sw offset, argw_hi; SLJIT_ASSERT(arg & SLJIT_MEM); if (!(next_arg & SLJIT_MEM)) { @@ -1170,6 +1244,8 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl next_argw = 0; } + /* Since tmp can be the same as base or offset registers, + * these might be unavailable after modifying tmp. */ if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) { tmp_ar = reg_ar; delay_slot = reg_ar; @@ -1217,35 +1293,39 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot); } - if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) { - if (argw != compiler->cache_argw) { - FAIL_IF(push_inst(compiler, ADDIU_W | S(TMP_REG3) | T(TMP_REG3) | IMM(argw - compiler->cache_argw), DR(TMP_REG3))); - compiler->cache_argw = argw; - } - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot); - } + if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar) | IMM(argw - compiler->cache_argw), delay_slot); - if (compiler->cache_arg == SLJIT_MEM && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) { - if (argw != compiler->cache_argw) - FAIL_IF(push_inst(compiler, ADDIU_W | S(TMP_REG3) | T(TMP_REG3) | IMM(argw - compiler->cache_argw), DR(TMP_REG3))); - } - else { + if (compiler->cache_arg == SLJIT_MEM && (argw - compiler->cache_argw) <= SIMM_MAX && (argw - compiler->cache_argw) >= SIMM_MIN) { + offset = argw - compiler->cache_argw; + } else { compiler->cache_arg = SLJIT_MEM; - FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw)); + + argw_hi = TO_ARGW_HI(argw); + + if (next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN && argw_hi != TO_ARGW_HI(next_argw)) { + FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw)); + compiler->cache_argw = argw; + offset = 0; + } else { + FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw_hi)); + compiler->cache_argw = argw_hi; + offset = argw & 0xffff; + argw = argw_hi; + } } - compiler->cache_argw = argw; if (!base) - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar) | IMM(offset), delay_slot); if (arg == next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN) { compiler->cache_arg = arg; FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | T(base) | D(TMP_REG3), DR(TMP_REG3))); - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar) | IMM(offset), delay_slot); } FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | T(base) | DA(tmp_ar), tmp_ar)); - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar) | IMM(offset), delay_slot); } static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw) @@ -1270,19 +1350,19 @@ static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, s if (SLJIT_UNLIKELY(argw)) { FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | DA(tmp_ar) | SH_IMM(argw), tmp_ar)); - FAIL_IF(push_inst(compiler, ADDU_W | S(base) | TA(tmp_ar) | DA(tmp_ar), tmp_ar)); + FAIL_IF(push_inst(compiler, ADDU_W | SA(tmp_ar) | T(base) | DA(tmp_ar), tmp_ar)); } else FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(OFFS_REG(arg)) | DA(tmp_ar), tmp_ar)); return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot); } - FAIL_IF(load_immediate(compiler, tmp_ar, argw)); + FAIL_IF(load_immediate(compiler, tmp_ar, TO_ARGW_HI(argw))); if (base != 0) - FAIL_IF(push_inst(compiler, ADDU_W | S(base) | TA(tmp_ar) | DA(tmp_ar), tmp_ar)); + FAIL_IF(push_inst(compiler, ADDU_W | SA(tmp_ar) | T(base) | DA(tmp_ar), tmp_ar)); - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar) | IMM(argw), delay_slot); } static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w) @@ -1292,6 +1372,649 @@ static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, slji return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w); } +#define EMIT_LOGICAL(op_imm, op_reg) \ + if (flags & SRC2_IMM) { \ + if (op & SLJIT_SET_Z) \ + FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \ + if (!(flags & UNUSED_DEST)) \ + FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \ + } \ + else { \ + if (op & SLJIT_SET_Z) \ + FAIL_IF(push_inst(compiler, op_reg | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ + if (!(flags & UNUSED_DEST)) \ + FAIL_IF(push_inst(compiler, op_reg | S(src1) | T(src2) | D(dst), DR(dst))); \ + } + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + +#define SELECT_OP(a, b) (b) + +#define EMIT_SHIFT(dimm, dimm32, imm, dv, v) \ + op_imm = (imm); \ + op_v = (v); + +#else /* !SLJIT_CONFIG_MIPS_32 */ + +#define SELECT_OP(a, b) \ + (!(op & SLJIT_32) ? a : b) + +#define EMIT_SHIFT(dimm, dimm32, imm, dv, v) \ + op_dimm = (dimm); \ + op_dimm32 = (dimm32); \ + op_imm = (imm); \ + op_dv = (dv); \ + op_v = (v); + +#endif /* SLJIT_CONFIG_MIPS_32 */ + +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV < 1) + +static sljit_s32 emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw src) +{ + sljit_s32 is_clz = (GET_OPCODE(op) == SLJIT_CLZ); +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) + sljit_ins max = (op & SLJIT_32) ? 32 : 64; +#else /* !SLJIT_CONFIG_RISCV_64 */ + sljit_ins max = 32; +#endif /* SLJIT_CONFIG_RISCV_64 */ + + /* The TMP_REG2 is the next value. */ + if (src != TMP_REG2) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src) | TA(0) | D(TMP_REG2), DR(TMP_REG2))); + + FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG2) | TA(0) | IMM(is_clz ? 13 : 14), UNMOVABLE_INS)); + /* The OTHER_FLAG is the counter. Delay slot. */ + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OTHER_FLAG) | IMM(max), OTHER_FLAG)); + + if (!is_clz) { + FAIL_IF(push_inst(compiler, ANDI | S(TMP_REG2) | T(TMP_REG1) | IMM(1), DR(TMP_REG1))); + FAIL_IF(push_inst(compiler, BNE | S(TMP_REG1) | TA(0) | IMM(11), UNMOVABLE_INS)); + } else + FAIL_IF(push_inst(compiler, BLTZ | S(TMP_REG2) | TA(0) | IMM(11), UNMOVABLE_INS)); + + /* Delay slot. */ + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OTHER_FLAG) | IMM(0), OTHER_FLAG)); + + /* The TMP_REG1 is the next shift. */ + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | T(TMP_REG1) | IMM(max), DR(TMP_REG1))); + + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(TMP_REG2) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, SELECT_OP(DSRL, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), DR(TMP_REG1))); + + FAIL_IF(push_inst(compiler, (is_clz ? SELECT_OP(DSRLV, SRLV) : SELECT_OP(DSLLV, SLLV)) | S(TMP_REG1) | TA(EQUAL_FLAG) | D(TMP_REG2), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, BNE | S(TMP_REG2) | TA(0) | IMM(-4), UNMOVABLE_INS)); + /* Delay slot. */ + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); + + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(TMP_REG1) | T(TMP_REG2) | IMM(-1), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, (is_clz ? SELECT_OP(DSRLV, SRLV) : SELECT_OP(DSLLV, SLLV)) | S(TMP_REG2) | TA(EQUAL_FLAG) | D(TMP_REG2), DR(TMP_REG2))); + + FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG2) | TA(0) | IMM(-7), UNMOVABLE_INS)); + /* Delay slot. */ + FAIL_IF(push_inst(compiler, OR | SA(OTHER_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG)); + + return push_inst(compiler, SELECT_OP(DADDU, ADDU) | SA(OTHER_FLAG) | TA(0) | D(dst), DR(dst)); +} + +#endif /* SLJIT_MIPS_REV < 1 */ + +static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, + sljit_s32 dst, sljit_s32 src1, sljit_sw src2) +{ + sljit_s32 is_overflow, is_carry, carry_src_ar, is_handled; + sljit_ins op_imm, op_v; +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) + sljit_ins ins, op_dimm, op_dimm32, op_dv; +#endif + + switch (GET_OPCODE(op)) { + case SLJIT_MOV: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if (dst != src2) + return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src2) | TA(0) | D(dst), DR(dst)); + return SLJIT_SUCCESS; + + case SLJIT_MOV_U8: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) + return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst)); + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; + + case SLJIT_MOV_S8: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) + return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst)); +#else /* SLJIT_MIPS_REV < 1 */ + FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(24), DR(dst))); + return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(24), DR(dst)); +#endif /* SLJIT_MIPS_REV >= 1 */ +#else /* !SLJIT_CONFIG_MIPS_32 */ +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) + if (op & SLJIT_32) + return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst)); +#endif /* SLJIT_MIPS_REV >= 1 */ + FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(24), DR(dst))); + return push_inst(compiler, DSRA32 | T(dst) | D(dst) | SH_IMM(24), DR(dst)); +#endif /* SLJIT_CONFIG_MIPS_32 */ + } + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; + + case SLJIT_MOV_U16: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) + return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst)); + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; + + case SLJIT_MOV_S16: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) + return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst)); +#else /* SLJIT_MIPS_REV < 1 */ + FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(16), DR(dst))); + return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(16), DR(dst)); +#endif /* SLJIT_MIPS_REV >= 1 */ +#else /* !SLJIT_CONFIG_MIPS_32 */ +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) + if (op & SLJIT_32) + return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst)); +#endif /* SLJIT_MIPS_REV >= 1 */ + FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(16), DR(dst))); + return push_inst(compiler, DSRA32 | T(dst) | D(dst) | SH_IMM(16), DR(dst)); +#endif /* SLJIT_CONFIG_MIPS_32 */ + } + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; + +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) + case SLJIT_MOV_U32: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM) && !(op & SLJIT_32)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2) + if (dst == src2) + return push_inst(compiler, DINSU | T(src2) | SA(0) | (31 << 11) | (0 << 11), DR(dst)); +#endif /* SLJIT_MIPS_REV >= 2 */ + FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(0), DR(dst))); + return push_inst(compiler, DSRL32 | T(dst) | D(dst) | SH_IMM(0), DR(dst)); + } + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; + + case SLJIT_MOV_S32: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM) && !(op & SLJIT_32)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + return push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(0), DR(dst)); + } + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; +#endif /* SLJIT_CONFIG_MIPS_64 */ + + case SLJIT_NOT: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + if (!(flags & UNUSED_DEST)) + FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst))); + return SLJIT_SUCCESS; + +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) + case SLJIT_CLZ: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) + return push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | D(dst), DR(dst)); +#else /* SLJIT_MIPS_REV < 6 */ + return push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | T(dst) | D(dst), DR(dst)); +#endif /* SLJIT_MIPS_REV >= 6 */ + case SLJIT_CTZ: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(0) | T(src2) | D(TMP_REG1), DR(TMP_REG1))); + FAIL_IF(push_inst(compiler, AND | S(src2) | T(TMP_REG1) | D(dst), DR(dst))); +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) + FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(dst) | D(dst), DR(dst))); +#else /* SLJIT_MIPS_REV < 6 */ + FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(dst) | T(dst) | D(dst), DR(dst))); +#endif /* SLJIT_MIPS_REV >= 6 */ + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(dst) | T(TMP_REG1) | IMM(SELECT_OP(-64, -32)), DR(TMP_REG1))); + FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(SELECT_OP(26, 27)), DR(TMP_REG1))); + return push_inst(compiler, XOR | S(dst) | T(TMP_REG1) | D(dst), DR(dst)); +#else /* SLJIT_MIPS_REV < 1 */ + case SLJIT_CLZ: + case SLJIT_CTZ: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + return emit_clz_ctz(compiler, op, dst, src2); +#endif /* SLJIT_MIPS_REV >= 1 */ + + case SLJIT_ADD: + /* Overflow computation (both add and sub): overflow = src1_sign ^ src2_sign ^ result_sign ^ carry_flag */ + is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; + carry_src_ar = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_overflow) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else + FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + } + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); + + /* Only the zero flag is needed. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst))); + } + else { + if (is_overflow) + FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + + if (is_overflow || carry_src_ar != 0) { + if (src1 != dst) + carry_src_ar = DR(src1); + else if (src2 != dst) + carry_src_ar = DR(src2); + else { + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | TA(0) | DA(OTHER_FLAG), OTHER_FLAG)); + carry_src_ar = OTHER_FLAG; + } + } + + /* Only the zero flag is needed. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst))); + } + + /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */ + if (is_overflow || carry_src_ar != 0) { + if (flags & SRC2_IMM) + FAIL_IF(push_inst(compiler, SLTIU | S(dst) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + else + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(carry_src_ar) | DA(OTHER_FLAG), OTHER_FLAG)); + } + + if (!is_overflow) + return SLJIT_SUCCESS; + + FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | D(TMP_REG1), DR(TMP_REG1))); + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); + return push_inst(compiler, XOR | S(TMP_REG1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); + + case SLJIT_ADDC: + carry_src_ar = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst))); + } else { + if (carry_src_ar != 0) { + if (src1 != dst) + carry_src_ar = DR(src1); + else if (src2 != dst) + carry_src_ar = DR(src2); + else { + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); + carry_src_ar = EQUAL_FLAG; + } + } + + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst))); + } + + /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */ + if (carry_src_ar != 0) { + if (flags & SRC2_IMM) + FAIL_IF(push_inst(compiler, SLTIU | S(dst) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); + else + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(carry_src_ar) | DA(EQUAL_FLAG), EQUAL_FLAG)); + } + + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst))); + + if (carry_src_ar == 0) + return SLJIT_SUCCESS; + + /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */ + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); + /* Set carry flag. */ + return push_inst(compiler, OR | SA(OTHER_FLAG) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); + + case SLJIT_SUB: + if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + is_handled = 0; + + if (flags & SRC2_IMM) { + if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + is_handled = 1; + } + else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLTI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + is_handled = 1; + } + } + + if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) { + is_handled = 1; + + if (flags & SRC2_IMM) { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + switch (GET_FLAG_TYPE(op)) { + case SLJIT_LESS: + case SLJIT_GREATER_EQUAL: + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); + break; + case SLJIT_GREATER: + case SLJIT_LESS_EQUAL: + FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); + break; + case SLJIT_SIG_LESS: + case SLJIT_SIG_GREATER_EQUAL: + FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); + break; + case SLJIT_SIG_GREATER: + case SLJIT_SIG_LESS_EQUAL: + FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); + break; + } + } + + if (is_handled) { + if (flags & SRC2_IMM) { + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); + if (!(flags & UNUSED_DEST)) + return push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)); + } + else { + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + if (!(flags & UNUSED_DEST)) + return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)); + } + return SLJIT_SUCCESS; + } + + is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_overflow) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else + FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + } + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); + + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + + /* Only the zero flag is needed. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst))); + } + else { + if (is_overflow) + FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); + + /* Only the zero flag is needed. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst))); + } + + if (!is_overflow) + return SLJIT_SUCCESS; + + FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | D(TMP_REG1), DR(TMP_REG1))); + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); + return push_inst(compiler, XOR | S(TMP_REG1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); + + case SLJIT_SUBC: + if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_carry) + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); + + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst))); + } + else { + if (is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + + FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst))); + } + + if (is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | D(TMP_REG1), DR(TMP_REG1))); + + FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst))); + + if (!is_carry) + return SLJIT_SUCCESS; + + return push_inst(compiler, OR | SA(EQUAL_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG); + + case SLJIT_MUL: + SLJIT_ASSERT(!(flags & SRC2_IMM)); + + if (GET_FLAG_TYPE(op) != SLJIT_OVERFLOW) { +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) + return push_inst(compiler, SELECT_OP(DMUL, MUL) | S(src1) | T(src2) | D(dst), DR(dst)); +#elif (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1) +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst)); +#else /* !SLJIT_CONFIG_MIPS_32 */ + if (op & SLJIT_32) + return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst)); + FAIL_IF(push_inst(compiler, DMULT | S(src1) | T(src2), MOVABLE_INS)); + return push_inst(compiler, MFLO | D(dst), DR(dst)); +#endif /* SLJIT_CONFIG_MIPS_32 */ +#else /* SLJIT_MIPS_REV < 1 */ + FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS)); + return push_inst(compiler, MFLO | D(dst), DR(dst)); +#endif /* SLJIT_MIPS_REV >= 6 */ + } + +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) + FAIL_IF(push_inst(compiler, SELECT_OP(DMUL, MUL) | S(src1) | T(src2) | D(dst), DR(dst))); + FAIL_IF(push_inst(compiler, SELECT_OP(DMUH, MUH) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); +#else /* SLJIT_MIPS_REV < 6 */ + FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, MFHI | DA(EQUAL_FLAG), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst))); +#endif /* SLJIT_MIPS_REV >= 6 */ + FAIL_IF(push_inst(compiler, SELECT_OP(DSRA32, SRA) | T(dst) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG)); + return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(EQUAL_FLAG) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); + + case SLJIT_AND: + EMIT_LOGICAL(ANDI, AND); + return SLJIT_SUCCESS; + + case SLJIT_OR: + EMIT_LOGICAL(ORI, OR); + return SLJIT_SUCCESS; + + case SLJIT_XOR: + EMIT_LOGICAL(XORI, XOR); + return SLJIT_SUCCESS; + + case SLJIT_SHL: + case SLJIT_MSHL: + EMIT_SHIFT(DSLL, DSLL32, SLL, DSLLV, SLLV); + break; + + case SLJIT_LSHR: + case SLJIT_MLSHR: + EMIT_SHIFT(DSRL, DSRL32, SRL, DSRLV, SRLV); + break; + + case SLJIT_ASHR: + case SLJIT_MASHR: + EMIT_SHIFT(DSRA, DSRA32, SRA, DSRAV, SRAV); + break; + +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2) + case SLJIT_ROTL: + if ((flags & SRC2_IMM) || src2 == 0) { +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + src2 = -src2 & 0x1f; +#else /* !SLJIT_CONFIG_MIPS_32 */ + src2 = -src2 & ((op & SLJIT_32) ? 0x1f : 0x3f); +#endif /* SLJIT_CONFIG_MIPS_32 */ + } else { + FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(0) | T(src2) | D(TMP_REG2), DR(TMP_REG2))); + src2 = TMP_REG2; + } + /* fallthrough */ + + case SLJIT_ROTR: + EMIT_SHIFT(DROTR, DROTR32, ROTR, DROTRV, ROTRV); + break; +#else /* SLJIT_MIPS_REV < 1 */ + case SLJIT_ROTL: + case SLJIT_ROTR: + if (flags & SRC2_IMM) { + SLJIT_ASSERT(src2 != 0); +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) + if (!(op & SLJIT_32)) { + if (GET_OPCODE(op) == SLJIT_ROTL) + op_imm = ((src2 < 32) ? DSLL : DSLL32); + else + op_imm = ((src2 < 32) ? DSRL : DSRL32); + + FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(OTHER_FLAG) | (((sljit_ins)src2 & 0x1f) << 6), OTHER_FLAG)); + + src2 = 64 - src2; + if (GET_OPCODE(op) == SLJIT_ROTL) + op_imm = ((src2 < 32) ? DSRL : DSRL32); + else + op_imm = ((src2 < 32) ? DSLL : DSLL32); + + FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | (((sljit_ins)src2 & 0x1f) << 6), DR(dst))); + return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)); + } +#endif /* SLJIT_CONFIG_MIPS_64 */ + + op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SLL : SRL; + FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(OTHER_FLAG) | ((sljit_ins)src2 << 6), OTHER_FLAG)); + + src2 = 32 - src2; + op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SRL : SLL; + FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | (((sljit_ins)src2 & 0x1f) << 6), DR(dst))); + return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)); + } + + if (src2 == 0) { + if (dst != src1) + return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | TA(0) | D(dst), DR(dst)); + return SLJIT_SUCCESS; + } + + FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(0) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) + if (!(op & SLJIT_32)) { + op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? DSLLV : DSRLV; + FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); + op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? DSRLV : DSLLV; + FAIL_IF(push_inst(compiler, op_v | SA(EQUAL_FLAG) | T(src1) | D(dst), DR(dst))); + return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)); + } +#endif /* SLJIT_CONFIG_MIPS_64 */ + + op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? SLLV : SRLV; + FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); + op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? SRLV : SLLV; + FAIL_IF(push_inst(compiler, op_v | SA(EQUAL_FLAG) | T(src1) | D(dst), DR(dst))); + return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)); +#endif /* SLJIT_MIPS_REV >= 2 */ + + default: + SLJIT_UNREACHABLE(); + return SLJIT_SUCCESS; + } + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + if ((flags & SRC2_IMM) || src2 == 0) { + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); + + if (flags & UNUSED_DEST) + return SLJIT_SUCCESS; + return push_inst(compiler, op_imm | T(src1) | D(dst) | SH_IMM(src2), DR(dst)); + } + + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + + if (flags & UNUSED_DEST) + return SLJIT_SUCCESS; + return push_inst(compiler, op_v | S(src2) | T(src1) | D(dst), DR(dst)); +#else /* !SLJIT_CONFIG_MIPS_32 */ + if ((flags & SRC2_IMM) || src2 == 0) { + if (src2 >= 32) { + SLJIT_ASSERT(!(op & SLJIT_32)); + ins = op_dimm32; + src2 -= 32; + } + else + ins = (op & SLJIT_32) ? op_imm : op_dimm; + + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ins | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); + + if (flags & UNUSED_DEST) + return SLJIT_SUCCESS; + return push_inst(compiler, ins | T(src1) | D(dst) | SH_IMM(src2), DR(dst)); + } + + ins = (op & SLJIT_32) ? op_v : op_dv; + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + + if (flags & UNUSED_DEST) + return SLJIT_SUCCESS; + return push_inst(compiler, ins | S(src2) | T(src1) | D(dst), DR(dst)); +#endif /* SLJIT_CONFIG_MIPS_32 */ +} + +#define CHECK_IMM(flags, srcw) \ + ((!((flags) & LOGICAL_OP) && ((srcw) <= SIMM_MAX && (srcw) >= SIMM_MIN)) \ + || (((flags) & LOGICAL_OP) && !((srcw) & ~UIMM_MAX))) + static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, sljit_s32 dst, sljit_sw dstw, sljit_s32 src1, sljit_sw src1w, @@ -1325,25 +2048,18 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 flags |= SLOW_DEST; if (flags & IMM_OP) { - if ((src2 & SLJIT_IMM) && src2w) { - if ((!(flags & LOGICAL_OP) && (src2w <= SIMM_MAX && src2w >= SIMM_MIN)) - || ((flags & LOGICAL_OP) && !(src2w & ~UIMM_MAX))) { - flags |= SRC2_IMM; - src2_r = src2w; - } - } - if (!(flags & SRC2_IMM) && (flags & CUMULATIVE_OP) && (src1 & SLJIT_IMM) && src1w) { - if ((!(flags & LOGICAL_OP) && (src1w <= SIMM_MAX && src1w >= SIMM_MIN)) - || ((flags & LOGICAL_OP) && !(src1w & ~UIMM_MAX))) { - flags |= SRC2_IMM; - src2_r = src1w; - - /* And swap arguments. */ - src1 = src2; - src1w = src2w; - src2 = SLJIT_IMM; - /* src2w = src2_r unneeded. */ - } + if ((src2 & SLJIT_IMM) && src2w != 0 && CHECK_IMM(flags, src2w)) { + flags |= SRC2_IMM; + src2_r = src2w; + } else if ((flags & CUMULATIVE_OP) && (src1 & SLJIT_IMM) && src1w != 0 && CHECK_IMM(flags, src1w)) { + flags |= SRC2_IMM; + src2_r = src1w; + + /* And swap arguments. */ + src1 = src2; + src1w = src2w; + src2 = SLJIT_IMM; + /* src2w = src2_r unneeded. */ } } @@ -1429,6 +2145,8 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 return SLJIT_SUCCESS; } +#undef CHECK_IMM + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op) { #if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) @@ -1584,6 +2302,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw); case SLJIT_CLZ: + case SLJIT_CTZ: return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw); } @@ -1635,8 +2354,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile return emit_op(compiler, op, flags | CUMULATIVE_OP | LOGICAL_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w); case SLJIT_SHL: + case SLJIT_MSHL: case SLJIT_LSHR: + case SLJIT_MLSHR: case SLJIT_ASHR: + case SLJIT_MASHR: + case SLJIT_ROTL: + case SLJIT_ROTR: #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) if (src2 & SLJIT_IMM) src2w &= 0x1f; @@ -1662,13 +2386,106 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compil CHECK_ERROR(); CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w); } +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) +#define SELECT_OP3(op, src2w, D, D32, W) (((op & SLJIT_32) ? (W) : ((src2w) < 32) ? (D) : (D32)) | (((sljit_ins)src2w & 0x1f) << 6)) +#define SELECT_OP2(op, D, W) ((op & SLJIT_32) ? (W) : (D)) +#else /* !SLJIT_CONFIG_MIPS_64 */ +#define SELECT_OP3(op, src2w, D, D32, W) ((W) | ((sljit_ins)(src2w) << 6)) +#define SELECT_OP2(op, D, W) (W) +#endif /* SLJIT_CONFIG_MIPS_64 */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_s32 is_left; + sljit_ins ins1, ins2, ins3; +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) + sljit_s32 inp_flags = ((op & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA; + sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64; +#else /* !SLJIT_CONFIG_MIPS_64 */ + sljit_s32 inp_flags = WORD_DATA | LOAD_DATA; + sljit_sw bit_length = 32; +#endif /* SLJIT_CONFIG_MIPS_64 */ + + CHECK_ERROR(); + CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w)); + + is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL); + + if (src_dst == src1) { + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_op2(compiler, (is_left ? SLJIT_ROTL : SLJIT_ROTR) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w); + } + + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + + if (src2 & SLJIT_IMM) { + src2w &= bit_length - 1; + + if (src2w == 0) + return SLJIT_SUCCESS; + } else if (src2 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, inp_flags, DR(TMP_REG2), src2, src2w)); + src2 = TMP_REG2; + } + + if (src1 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, inp_flags, DR(TMP_REG1), src1, src1w)); + src1 = TMP_REG1; + } else if (src1 & SLJIT_IMM) { + FAIL_IF(load_immediate(compiler, DR(TMP_REG1), src1w)); + src1 = TMP_REG1; + } + + if (src2 & SLJIT_IMM) { + if (is_left) { + ins1 = SELECT_OP3(op, src2w, DSLL, DSLL32, SLL); + src2w = bit_length - src2w; + ins2 = SELECT_OP3(op, src2w, DSRL, DSRL32, SRL); + } else { + ins1 = SELECT_OP3(op, src2w, DSRL, DSRL32, SRL); + src2w = bit_length - src2w; + ins2 = SELECT_OP3(op, src2w, DSLL, DSLL32, SLL); + } + + FAIL_IF(push_inst(compiler, ins1 | T(src_dst) | D(src_dst), DR(src_dst))); + FAIL_IF(push_inst(compiler, ins2 | T(src1) | D(TMP_REG1), DR(TMP_REG1))); + return push_inst(compiler, OR | S(src_dst) | T(TMP_REG1) | D(src_dst), DR(src_dst)); + } + + if (is_left) { + ins1 = SELECT_OP2(op, DSRL, SRL); + ins2 = SELECT_OP2(op, DSLLV, SLLV); + ins3 = SELECT_OP2(op, DSRLV, SRLV); + } else { + ins1 = SELECT_OP2(op, DSLL, SLL); + ins2 = SELECT_OP2(op, DSRLV, SRLV); + ins3 = SELECT_OP2(op, DSLLV, SLLV); + } + + FAIL_IF(push_inst(compiler, ins2 | S(src2) | T(src_dst) | D(src_dst), DR(src_dst))); + + if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) { + FAIL_IF(push_inst(compiler, ins1 | T(src1) | D(TMP_REG1) | (1 << 6), DR(TMP_REG1))); + FAIL_IF(push_inst(compiler, XORI | S(src2) | T(TMP_REG2) | ((sljit_ins)bit_length - 1), DR(TMP_REG2))); + src1 = TMP_REG1; + } else + FAIL_IF(push_inst(compiler, SELECT_OP2(op, DSUBU, SUBU) | SA(0) | T(src2) | D(TMP_REG2), DR(TMP_REG2))); + + FAIL_IF(push_inst(compiler, ins3 | S(TMP_REG2) | T(src1) | D(TMP_REG1), DR(TMP_REG1))); + return push_inst(compiler, OR | S(src_dst) | T(TMP_REG1) | D(src_dst), DR(src_dst)); +} + +#undef SELECT_OP3 +#undef SELECT_OP2 + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { @@ -1746,14 +2563,19 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp FAIL_IF(push_inst(compiler, (TRUNC_W_S ^ (flags >> 19)) | FMT(op) | FS(src) | FD(TMP_FREG1), MOVABLE_INS)); - if (FAST_IS_REG(dst)) - return push_inst(compiler, MFC1 | flags | T(dst) | FS(TMP_FREG1), MOVABLE_INS); + if (FAST_IS_REG(dst)) { + FAIL_IF(push_inst(compiler, MFC1 | flags | T(dst) | FS(TMP_FREG1), MOVABLE_INS)); +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif + return SLJIT_SUCCESS; + } /* Store the integer value from a VFP register. */ return emit_op_mem2(compiler, flags ? DOUBLE_DATA : SINGLE_DATA, FR(TMP_FREG1), dst, dstw, 0, 0); #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) -# undef is_long +# undef flags #endif } @@ -1769,19 +2591,25 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; - if (FAST_IS_REG(src)) + if (FAST_IS_REG(src)) { FAIL_IF(push_inst(compiler, MTC1 | flags | T(src) | FS(TMP_FREG1), MOVABLE_INS)); - else if (src & SLJIT_MEM) { +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif + } else if (src & SLJIT_MEM) { /* Load the integer value into a VFP register. */ - FAIL_IF(emit_op_mem2(compiler, ((flags) ? DOUBLE_DATA : SINGLE_DATA) | LOAD_DATA, FR(TMP_FREG1), src, srcw, dst, dstw)); + FAIL_IF(emit_op_mem2(compiler, (flags ? DOUBLE_DATA : SINGLE_DATA) | LOAD_DATA, FR(TMP_FREG1), src, srcw, dst, dstw)); } else { -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) srcw = (sljit_s32)srcw; #endif FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw)); FAIL_IF(push_inst(compiler, MTC1 | flags | T(TMP_REG1) | FS(TMP_FREG1), MOVABLE_INS)); +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif } FAIL_IF(push_inst(compiler, CVT_S_S | flags | (4 << 21) | ((((sljit_ins)op & SLJIT_32) ^ SLJIT_32) >> 8) | FS(TMP_FREG1) | FD(dst_r), MOVABLE_INS)); @@ -1812,20 +2640,38 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile } switch (GET_FLAG_TYPE(op)) { - case SLJIT_EQUAL_F64: - case SLJIT_NOT_EQUAL_F64: + case SLJIT_F_EQUAL: + case SLJIT_ORDERED_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + inst = C_EQ_S; + break; + case SLJIT_F_NOT_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: inst = C_UEQ_S; break; - case SLJIT_LESS_F64: - case SLJIT_GREATER_EQUAL_F64: + case SLJIT_F_LESS: + case SLJIT_ORDERED_LESS: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + inst = C_OLT_S; + break; + case SLJIT_F_GREATER_EQUAL: + case SLJIT_UNORDERED_OR_LESS: + case SLJIT_ORDERED_GREATER_EQUAL: inst = C_ULT_S; break; - case SLJIT_GREATER_F64: - case SLJIT_LESS_EQUAL_F64: + case SLJIT_F_GREATER: + case SLJIT_ORDERED_GREATER: + case SLJIT_UNORDERED_OR_LESS_EQUAL: inst = C_ULE_S; break; + case SLJIT_F_LESS_EQUAL: + case SLJIT_UNORDERED_OR_GREATER: + case SLJIT_ORDERED_LESS_EQUAL: + inst = C_OLE_S; + break; default: - SLJIT_ASSERT(GET_FLAG_TYPE(op) == SLJIT_UNORDERED_F64 || GET_FLAG_TYPE(op) == SLJIT_ORDERED_F64); + SLJIT_ASSERT(GET_FLAG_TYPE(op) == SLJIT_UNORDERED || GET_FLAG_TYPE(op) == SLJIT_ORDERED); inst = C_UN_S; break; } @@ -1871,6 +2717,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil FAIL_IF(push_inst(compiler, ABS_S | FMT(op) | FS(src) | FD(dst_r), MOVABLE_INS)); break; case SLJIT_CONV_F64_FROM_F32: + /* The SLJIT_32 bit is inverted because sljit_f32 needs to be loaded from the memory. */ FAIL_IF(push_inst(compiler, CVT_S_S | (sljit_ins)((op & SLJIT_32) ? 1 : (1 << 21)) | FS(src) | FD(dst_r), MOVABLE_INS)); op ^= SLJIT_32; break; @@ -1959,6 +2806,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil return SLJIT_SUCCESS; } +#undef FLOAT_DATA +#undef FMT + /* --------------------------------------------------------------------- */ /* Other instructions */ /* --------------------------------------------------------------------- */ @@ -2000,18 +2850,18 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compi } #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) -#define JUMP_LENGTH 4 +#define BRANCH_LENGTH 4 #else -#define JUMP_LENGTH 8 +#define BRANCH_LENGTH 8 #endif #define BR_Z(src) \ - inst = BEQ | SA(src) | TA(0) | JUMP_LENGTH; \ + inst = BEQ | SA(src) | TA(0) | BRANCH_LENGTH; \ flags = IS_BIT26_COND; \ delay_check = src; #define BR_NZ(src) \ - inst = BNE | SA(src) | TA(0) | JUMP_LENGTH; \ + inst = BNE | SA(src) | TA(0) | BRANCH_LENGTH; \ flags = IS_BIT26_COND; \ delay_check = src; @@ -2029,11 +2879,11 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compi #else /* SLJIT_MIPS_REV < 6 */ #define BR_T() \ - inst = BC1T | JUMP_LENGTH; \ + inst = BC1T | BRANCH_LENGTH; \ flags = IS_BIT16_COND; \ delay_check = FCSR_FCC; #define BR_F() \ - inst = BC1F | JUMP_LENGTH; \ + inst = BC1F | BRANCH_LENGTH; \ flags = IS_BIT16_COND; \ delay_check = FCSR_FCC; @@ -2077,16 +2927,28 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile case SLJIT_NOT_CARRY: BR_NZ(OTHER_FLAG); break; - case SLJIT_NOT_EQUAL_F64: - case SLJIT_GREATER_EQUAL_F64: - case SLJIT_GREATER_F64: - case SLJIT_ORDERED_F64: + case SLJIT_F_NOT_EQUAL: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_F_GREATER: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER: + case SLJIT_UNORDERED_OR_GREATER: + case SLJIT_ORDERED: BR_T(); break; - case SLJIT_EQUAL_F64: - case SLJIT_LESS_F64: - case SLJIT_LESS_EQUAL_F64: - case SLJIT_UNORDERED_F64: + case SLJIT_F_EQUAL: + case SLJIT_F_LESS: + case SLJIT_F_LESS_EQUAL: + case SLJIT_ORDERED_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: + case SLJIT_ORDERED_LESS: + case SLJIT_UNORDERED_OR_LESS: + case SLJIT_UNORDERED_OR_LESS_EQUAL: + case SLJIT_ORDERED_LESS_EQUAL: + case SLJIT_UNORDERED: BR_F(); break; default: @@ -2102,8 +2964,6 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile if (inst) PTR_FAIL_IF(push_inst(compiler, inst, UNMOVABLE_INS)); - PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0)); - if (type <= SLJIT_JUMP) PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS)); else { @@ -2113,6 +2973,13 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile jump->addr = compiler->size; PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); + + /* Maximum number of instructions required for generating a constant. */ +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + compiler->size += 2; +#else + compiler->size += 6; +#endif return jump; } @@ -2151,11 +3018,17 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler compiler->cache_arg = 0; compiler->cache_argw = 0; +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + flags = WORD_DATA | LOAD_DATA; +#else /* !SLJIT_CONFIG_MIPS_32 */ flags = ((type & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA; +#endif /* SLJIT_CONFIG_MIPS_32 */ + if (src1 & SLJIT_MEM) { PTR_FAIL_IF(emit_op_mem2(compiler, flags, DR(TMP_REG1), src1, src1w, src2, src2w)); src1 = TMP_REG1; } + if (src2 & SLJIT_MEM) { PTR_FAIL_IF(emit_op_mem2(compiler, flags, DR(TMP_REG2), src2, src2w, 0, 0)); src2 = TMP_REG2; @@ -2172,7 +3045,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler jump->flags |= IS_BIT26_COND; if (compiler->delay_slot == MOVABLE_INS || (compiler->delay_slot != UNMOVABLE_INS && compiler->delay_slot != DR(src1) && compiler->delay_slot != DR(src2))) jump->flags |= IS_MOVABLE; - PTR_FAIL_IF(push_inst(compiler, (type == SLJIT_EQUAL ? BNE : BEQ) | S(src1) | T(src2) | JUMP_LENGTH, UNMOVABLE_INS)); + PTR_FAIL_IF(push_inst(compiler, (type == SLJIT_EQUAL ? BNE : BEQ) | S(src1) | T(src2) | BRANCH_LENGTH, UNMOVABLE_INS)); } else if (type >= SLJIT_SIG_LESS && (((src1 & SLJIT_IMM) && (src1w == 0)) || ((src2 & SLJIT_IMM) && (src2w == 0)))) { inst = NOP; @@ -2219,7 +3092,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler break; } } - PTR_FAIL_IF(push_inst(compiler, inst | S(src1) | JUMP_LENGTH, UNMOVABLE_INS)); + PTR_FAIL_IF(push_inst(compiler, inst | S(src1) | BRANCH_LENGTH, UNMOVABLE_INS)); } else { if (type == SLJIT_LESS || type == SLJIT_GREATER_EQUAL || type == SLJIT_SIG_LESS || type == SLJIT_SIG_GREATER_EQUAL) { @@ -2244,20 +3117,26 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler } jump->flags |= IS_BIT26_COND; - PTR_FAIL_IF(push_inst(compiler, (type == SLJIT_EQUAL ? BNE : BEQ) | S(TMP_REG1) | TA(0) | JUMP_LENGTH, UNMOVABLE_INS)); + PTR_FAIL_IF(push_inst(compiler, (type == SLJIT_EQUAL ? BNE : BEQ) | S(TMP_REG1) | TA(0) | BRANCH_LENGTH, UNMOVABLE_INS)); } - PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0)); PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS)); jump->addr = compiler->size; PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); + + /* Maximum number of instructions required for generating a constant. */ +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + compiler->size += 2; +#else + compiler->size += 6; +#endif return jump; } #undef RESOLVE_IMM1 #undef RESOLVE_IMM2 -#undef JUMP_LENGTH +#undef BRANCH_LENGTH #undef BR_Z #undef BR_NZ #undef BR_T @@ -2272,7 +3151,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi CHECK_ERROR(); CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); - ADJUST_LOCAL_OFFSET(src, srcw); if (src & SLJIT_IMM) { jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); @@ -2283,17 +3161,29 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi if (compiler->delay_slot != UNMOVABLE_INS) jump->flags |= IS_MOVABLE; - FAIL_IF(emit_const(compiler, TMP_REG2, 0)); src = TMP_REG2; - } - else if (src & SLJIT_MEM) { + } else if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(TMP_REG2), src, srcw)); src = TMP_REG2; } - FAIL_IF(push_inst(compiler, JR | S(src), UNMOVABLE_INS)); - if (jump) + if (type <= SLJIT_JUMP) + FAIL_IF(push_inst(compiler, JR | S(src), UNMOVABLE_INS)); + else + FAIL_IF(push_inst(compiler, JALR | S(src) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); + + if (jump != NULL) { jump->addr = compiler->size; + + /* Maximum number of instructions required for generating a constant. */ +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + compiler->size += 2; +#else + compiler->size += 6; +#endif + } + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); return SLJIT_SUCCESS; } @@ -2302,7 +3192,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co sljit_s32 dst, sljit_sw dstw, sljit_s32 type) { - sljit_s32 src_ar, dst_ar; + sljit_s32 src_ar, dst_ar, invert; sljit_s32 saved_op = op; #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) sljit_s32 mem_type = WORD_DATA; @@ -2323,32 +3213,45 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co if (op >= SLJIT_ADD && (dst & SLJIT_MEM)) FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, DR(TMP_REG1), dst, dstw, dst, dstw)); - switch (type & 0xff) { - case SLJIT_EQUAL: - case SLJIT_NOT_EQUAL: - FAIL_IF(push_inst(compiler, SLTIU | SA(EQUAL_FLAG) | TA(dst_ar) | IMM(1), dst_ar)); - src_ar = dst_ar; - break; - case SLJIT_OVERFLOW: - case SLJIT_NOT_OVERFLOW: - if (compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)) { - src_ar = OTHER_FLAG; + if (type < SLJIT_F_EQUAL) { + src_ar = OTHER_FLAG; + invert = type & 0x1; + + switch (type) { + case SLJIT_EQUAL: + case SLJIT_NOT_EQUAL: + FAIL_IF(push_inst(compiler, SLTIU | SA(EQUAL_FLAG) | TA(dst_ar) | IMM(1), dst_ar)); + src_ar = dst_ar; + break; + case SLJIT_OVERFLOW: + case SLJIT_NOT_OVERFLOW: + if (compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)) { + src_ar = OTHER_FLAG; + break; + } + FAIL_IF(push_inst(compiler, SLTIU | SA(OTHER_FLAG) | TA(dst_ar) | IMM(1), dst_ar)); + src_ar = dst_ar; + invert ^= 0x1; break; } - FAIL_IF(push_inst(compiler, SLTIU | SA(OTHER_FLAG) | TA(dst_ar) | IMM(1), dst_ar)); - src_ar = dst_ar; - type ^= 0x1; /* Flip type bit for the XORI below. */ - break; - case SLJIT_GREATER_F64: - case SLJIT_LESS_EQUAL_F64: - type ^= 0x1; /* Flip type bit for the XORI below. */ - /* fallthrough */ - case SLJIT_EQUAL_F64: - case SLJIT_NOT_EQUAL_F64: - case SLJIT_LESS_F64: - case SLJIT_GREATER_EQUAL_F64: - case SLJIT_UNORDERED_F64: - case SLJIT_ORDERED_F64: + } else { + invert = 0; + + switch (type) { + case SLJIT_F_NOT_EQUAL: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_F_GREATER: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER: + case SLJIT_UNORDERED_OR_GREATER: + case SLJIT_ORDERED: + invert = 1; + break; + } + #if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) FAIL_IF(push_inst(compiler, MFC1 | TA(dst_ar) | FS(TMP_FREG3), dst_ar)); #else /* SLJIT_MIPS_REV < 6 */ @@ -2357,14 +3260,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co FAIL_IF(push_inst(compiler, SRL | TA(dst_ar) | DA(dst_ar) | SH_IMM(23), dst_ar)); FAIL_IF(push_inst(compiler, ANDI | SA(dst_ar) | TA(dst_ar) | IMM(1), dst_ar)); src_ar = dst_ar; - break; - - default: - src_ar = OTHER_FLAG; - break; } - if (type & 0x1) { + if (invert) { FAIL_IF(push_inst(compiler, XORI | SA(src_ar) | TA(dst_ar) | IMM(1), dst_ar)); src_ar = dst_ar; } @@ -2404,7 +3302,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil if (SLJIT_UNLIKELY(src & SLJIT_IMM)) { #if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) - if (dst_reg & SLJIT_32) + if (type & SLJIT_32) srcw = (sljit_s32)srcw; #endif FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw)); @@ -2412,9 +3310,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil srcw = 0; } - dst_reg &= ~SLJIT_32; - - switch (type & 0xff) { + switch (type & ~SLJIT_32) { case SLJIT_EQUAL: ins = MOVZ | TA(EQUAL_FLAG); break; @@ -2435,16 +3331,28 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil case SLJIT_NOT_OVERFLOW: ins = MOVZ | TA(OTHER_FLAG); break; - case SLJIT_EQUAL_F64: - case SLJIT_LESS_F64: - case SLJIT_LESS_EQUAL_F64: - case SLJIT_UNORDERED_F64: + case SLJIT_F_EQUAL: + case SLJIT_F_LESS: + case SLJIT_F_LESS_EQUAL: + case SLJIT_ORDERED_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: + case SLJIT_ORDERED_LESS: + case SLJIT_UNORDERED_OR_LESS: + case SLJIT_UNORDERED_OR_LESS_EQUAL: + case SLJIT_ORDERED_LESS_EQUAL: + case SLJIT_UNORDERED: ins = MOVT; break; - case SLJIT_NOT_EQUAL_F64: - case SLJIT_GREATER_EQUAL_F64: - case SLJIT_GREATER_F64: - case SLJIT_ORDERED_F64: + case SLJIT_F_NOT_EQUAL: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_F_GREATER: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER: + case SLJIT_UNORDERED_OR_GREATER: + case SLJIT_ORDERED: ins = MOVF; break; default: @@ -2460,6 +3368,308 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil #endif /* SLJIT_MIPS_REV >= 1 */ } +static sljit_s32 update_mem_addr(struct sljit_compiler *compiler, sljit_s32 *mem, sljit_sw *memw, sljit_s16 max_offset) +{ + sljit_s32 arg = *mem; + sljit_sw argw = *memw; + + if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { + argw &= 0x3; + + if (SLJIT_UNLIKELY(argw)) { + FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | D(TMP_REG1) | SH_IMM(argw), DR(TMP_REG1))); + FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG1) | T(arg & REG_MASK) | D(TMP_REG1), DR(TMP_REG1))); + } else + FAIL_IF(push_inst(compiler, ADDU_W | S(arg & REG_MASK) | T(OFFS_REG(arg)) | D(TMP_REG1), DR(TMP_REG1))); + + *mem = TMP_REG1; + *memw = 0; + + return SLJIT_SUCCESS; + } + + if (argw <= max_offset && argw >= SIMM_MIN) { + *mem = arg & REG_MASK; + return SLJIT_SUCCESS; + } + + *mem = TMP_REG1; + + if ((sljit_s16)argw > max_offset) { + FAIL_IF(load_immediate(compiler, DR(TMP_REG1), argw)); + *memw = 0; + } else { + FAIL_IF(load_immediate(compiler, DR(TMP_REG1), TO_ARGW_HI(argw))); + *memw = (sljit_s16)argw; + } + + if ((arg & REG_MASK) == 0) + return SLJIT_SUCCESS; + + return push_inst(compiler, ADDU_W | S(TMP_REG1) | T(arg & REG_MASK) | D(TMP_REG1), DR(TMP_REG1)); +} + +#if (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN) +#define MEM16_IMM_FIRST(memw) IMM((memw) + 1) +#define MEM16_IMM_SECOND(memw) IMM(memw) +#define MEMF64_FS_FIRST(freg) FS(freg) +#define MEMF64_FS_SECOND(freg) (FS(freg) | ((sljit_ins)1 << 11)) +#else /* !SLJIT_LITTLE_ENDIAN */ +#define MEM16_IMM_FIRST(memw) IMM(memw) +#define MEM16_IMM_SECOND(memw) IMM((memw) + 1) +#define MEMF64_FS_FIRST(freg) (FS(freg) | ((sljit_ins)1 << 11)) +#define MEMF64_FS_SECOND(freg) FS(freg) +#endif /* SLJIT_LITTLE_ENDIAN */ + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) +#define MEM_CHECK_UNALIGNED(type) ((type) & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16)) +#else /* !SLJIT_CONFIG_MIPS_32 */ +#define MEM_CHECK_UNALIGNED(type) ((type) & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32)) +#endif /* SLJIT_CONFIG_MIPS_32 */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_s32 op = type & 0xff; + sljit_s32 flags = 0; + sljit_ins ins; +#if !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) + sljit_ins ins_right; +#endif /* !(SLJIT_MIPS_REV >= 6) */ + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + + if (reg & REG_PAIR_MASK) { + ADJUST_LOCAL_OFFSET(mem, memw); + +#if !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) + if (MEM_CHECK_UNALIGNED(type)) { + FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - (2 * SSIZE_OF(sw) - 1))); + + if (!(type & SLJIT_MEM_STORE) && (mem == REG_PAIR_FIRST(reg) || mem == REG_PAIR_SECOND(reg))) { + FAIL_IF(push_inst(compiler, ADDU_W | S(mem) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); + mem = TMP_REG1; + } + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + ins = ((type & SLJIT_MEM_STORE) ? SWL : LWL) | S(mem); + ins_right = ((type & SLJIT_MEM_STORE) ? SWR : LWR) | S(mem); +#else /* !SLJIT_CONFIG_MIPS_32 */ + ins = ((type & SLJIT_MEM_STORE) ? SDL : LDL) | S(mem); + ins_right = ((type & SLJIT_MEM_STORE) ? SDR : LDR) | S(mem); +#endif /* SLJIT_CONFIG_MIPS_32 */ + + FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_FIRST(reg)) | IMM(memw), DR(REG_PAIR_FIRST(reg)))); + FAIL_IF(push_inst(compiler, ins_right | T(REG_PAIR_FIRST(reg)) | IMM(memw + (SSIZE_OF(sw) - 1)), DR(REG_PAIR_FIRST(reg)))); + FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_SECOND(reg)) | IMM(memw + SSIZE_OF(sw)), DR(REG_PAIR_SECOND(reg)))); + return push_inst(compiler, ins_right | T(REG_PAIR_SECOND(reg)) | IMM((memw + 2 * SSIZE_OF(sw) - 1)), DR(REG_PAIR_SECOND(reg))); + } +#endif /* !(SLJIT_MIPS_REV >= 6) */ + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - SSIZE_OF(sw))); + + ins = ((type & SLJIT_MEM_STORE) ? STORE_W : LOAD_W) | S(mem); + + if (!(type & SLJIT_MEM_STORE) && mem == REG_PAIR_FIRST(reg)) { + FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_SECOND(reg)) | IMM(memw + SSIZE_OF(sw)), DR(REG_PAIR_SECOND(reg)))); + return push_inst(compiler, ins | T(REG_PAIR_FIRST(reg)) | IMM(memw), DR(REG_PAIR_FIRST(reg))); + } + + FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_FIRST(reg)) | IMM(memw), DR(REG_PAIR_FIRST(reg)))); + return push_inst(compiler, ins | T(REG_PAIR_SECOND(reg)) | IMM(memw + SSIZE_OF(sw)), DR(REG_PAIR_SECOND(reg))); + } + +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) + return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw); +#else /* !(SLJIT_MIPS_REV >= 6) */ + ADJUST_LOCAL_OFFSET(mem, memw); + + switch (op) { + case SLJIT_MOV_U8: + case SLJIT_MOV_S8: + flags = BYTE_DATA; + if (!(type & SLJIT_MEM_STORE)) + flags |= LOAD_DATA; + + if (op == SLJIT_MOV_S8) + flags |= SIGNED_DATA; + + return emit_op_mem(compiler, flags, DR(reg), mem, memw); + + case SLJIT_MOV_U16: + case SLJIT_MOV_S16: + FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - 1)); + SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2); + + if (type & SLJIT_MEM_STORE) { + FAIL_IF(push_inst(compiler, SRA_W | T(reg) | D(TMP_REG2) | SH_IMM(8), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, data_transfer_insts[BYTE_DATA] | S(mem) | T(TMP_REG2) | MEM16_IMM_FIRST(memw), MOVABLE_INS)); + return push_inst(compiler, data_transfer_insts[BYTE_DATA] | S(mem) | T(reg) | MEM16_IMM_SECOND(memw), MOVABLE_INS); + } + + flags = BYTE_DATA | LOAD_DATA; + + if (op == SLJIT_MOV_S16) + flags |= SIGNED_DATA; + + FAIL_IF(push_inst(compiler, data_transfer_insts[flags] | S(mem) | T(TMP_REG2) | MEM16_IMM_FIRST(memw), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, data_transfer_insts[BYTE_DATA | LOAD_DATA] | S(mem) | T(reg) | MEM16_IMM_SECOND(memw), DR(reg))); + FAIL_IF(push_inst(compiler, SLL_W | T(TMP_REG2) | D(TMP_REG2) | SH_IMM(8), DR(TMP_REG2))); + return push_inst(compiler, OR | S(reg) | T(TMP_REG2) | D(reg), DR(reg)); + + case SLJIT_MOV: + case SLJIT_MOV_P: +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + if (type & SLJIT_MEM_UNALIGNED_32) { + flags = WORD_DATA; + if (!(type & SLJIT_MEM_STORE)) + flags |= LOAD_DATA; + + return emit_op_mem(compiler, flags, DR(reg), mem, memw); + } +#else /* !SLJIT_CONFIG_MIPS_32 */ + FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - 7)); + SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2); + + if (type & SLJIT_MEM_STORE) { + FAIL_IF(push_inst(compiler, SDL | S(mem) | T(reg) | IMM(memw), MOVABLE_INS)); + return push_inst(compiler, SDR | S(mem) | T(reg) | IMM(memw + 7), MOVABLE_INS); + } + + if (mem == reg) { + FAIL_IF(push_inst(compiler, ADDU_W | S(mem) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); + mem = TMP_REG1; + } + + FAIL_IF(push_inst(compiler, LDL | S(mem) | T(reg) | IMM(memw), DR(reg))); + return push_inst(compiler, LDR | S(mem) | T(reg) | IMM(memw + 7), DR(reg)); +#endif /* SLJIT_CONFIG_MIPS_32 */ + } + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - 3)); + SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2); + + if (type & SLJIT_MEM_STORE) { + FAIL_IF(push_inst(compiler, SWL | S(mem) | T(reg) | IMM(memw), MOVABLE_INS)); + return push_inst(compiler, SWR | S(mem) | T(reg) | IMM(memw + 3), MOVABLE_INS); + } + + if (mem == reg) { + FAIL_IF(push_inst(compiler, ADDU_W | S(mem) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); + mem = TMP_REG1; + } + + FAIL_IF(push_inst(compiler, LWL | S(mem) | T(reg) | IMM(memw), DR(reg))); +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + return push_inst(compiler, LWR | S(mem) | T(reg) | IMM(memw + 3), DR(reg)); +#else /* !SLJIT_CONFIG_MIPS_32 */ + FAIL_IF(push_inst(compiler, LWR | S(mem) | T(reg) | IMM(memw + 3), DR(reg))); + + if (op != SLJIT_MOV_U32) + return SLJIT_SUCCESS; + +#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2) + return push_inst(compiler, DINSU | T(reg) | SA(0) | (31 << 11) | (0 << 11), DR(reg)); +#else /* SLJIT_MIPS_REV < 1 */ + FAIL_IF(push_inst(compiler, DSLL32 | T(reg) | D(reg) | SH_IMM(0), DR(reg))); + return push_inst(compiler, DSRL32 | T(reg) | D(reg) | SH_IMM(0), DR(reg)); +#endif /* SLJIT_MIPS_REV >= 2 */ +#endif /* SLJIT_CONFIG_MIPS_32 */ +#endif /* SLJIT_MIPS_REV >= 6 */ +} + +#if !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6) + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw)); + + FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - (type & SLJIT_32) ? 3 : 7)); + SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2); + + if (type & SLJIT_MEM_STORE) { + if (type & SLJIT_32) { + FAIL_IF(push_inst(compiler, MFC1 | T(TMP_REG2) | FS(freg), DR(TMP_REG2))); +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif + FAIL_IF(push_inst(compiler, SWL | S(mem) | T(TMP_REG2) | IMM(memw), MOVABLE_INS)); + return push_inst(compiler, SWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), MOVABLE_INS); + } + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + FAIL_IF(push_inst(compiler, MFC1 | T(TMP_REG2) | MEMF64_FS_FIRST(freg), DR(TMP_REG2))); +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif + FAIL_IF(push_inst(compiler, SWL | S(mem) | T(TMP_REG2) | IMM(memw), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, SWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), MOVABLE_INS)); + + FAIL_IF(push_inst(compiler, MFC1 | T(TMP_REG2) | MEMF64_FS_SECOND(freg), DR(TMP_REG2))); +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif + FAIL_IF(push_inst(compiler, SWL | S(mem) | T(TMP_REG2) | IMM(memw + 4), MOVABLE_INS)); + return push_inst(compiler, SWR | S(mem) | T(TMP_REG2) | IMM(memw + 7), MOVABLE_INS); +#else /* !SLJIT_CONFIG_MIPS_32 */ + FAIL_IF(push_inst(compiler, MFC1 | (1 << 21) | T(TMP_REG2) | FS(freg), DR(TMP_REG2))); +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif + FAIL_IF(push_inst(compiler, SDL | S(mem) | T(TMP_REG2) | IMM(memw), MOVABLE_INS)); + return push_inst(compiler, SDR | S(mem) | T(TMP_REG2) | IMM(memw + 7), MOVABLE_INS); +#endif /* SLJIT_CONFIG_MIPS_32 */ + } + + if (type & SLJIT_32) { + FAIL_IF(push_inst(compiler, LWL | S(mem) | T(TMP_REG2) | IMM(memw), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, LWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), DR(TMP_REG2))); + + FAIL_IF(push_inst(compiler, MTC1 | T(TMP_REG2) | FS(freg), MOVABLE_INS)); +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif + return SLJIT_SUCCESS; + } + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + FAIL_IF(push_inst(compiler, LWL | S(mem) | T(TMP_REG2) | IMM(memw), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, LWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, MTC1 | T(TMP_REG2) | MEMF64_FS_FIRST(freg), MOVABLE_INS)); + + FAIL_IF(push_inst(compiler, LWL | S(mem) | T(TMP_REG2) | IMM(memw + 4), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, LWR | S(mem) | T(TMP_REG2) | IMM(memw + 7), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, MTC1 | T(TMP_REG2) | MEMF64_FS_SECOND(freg), MOVABLE_INS)); +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif +#else /* !SLJIT_CONFIG_MIPS_32 */ + FAIL_IF(push_inst(compiler, LDL | S(mem) | T(TMP_REG2) | IMM(memw), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, LDR | S(mem) | T(TMP_REG2) | IMM(memw + 7), DR(TMP_REG2))); + + FAIL_IF(push_inst(compiler, MTC1 | (1 << 21) | T(TMP_REG2) | FS(freg), MOVABLE_INS)); +#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif +#endif /* SLJIT_CONFIG_MIPS_32 */ + return SLJIT_SUCCESS; +} + +#endif /* !SLJIT_MIPS_REV || SLJIT_MIPS_REV < 6 */ + +#undef MEM16_IMM_FIRST +#undef MEM16_IMM_SECOND +#undef MEMF64_FS_FIRST +#undef MEMF64_FS_SECOND +#undef MEM_CHECK_UNALIGNED + +#undef TO_ARGW_HI + SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) { struct sljit_const *const_; @@ -2477,7 +3687,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi PTR_FAIL_IF(emit_const(compiler, dst_r, init_value)); if (dst & SLJIT_MEM) - PTR_FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0)); + PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, DR(TMP_REG2), dst, dstw)); return const_; } @@ -2496,15 +3706,15 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct slj set_put_label(put_label, compiler, 0); dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; + PTR_FAIL_IF(push_inst(compiler, (sljit_ins)dst_r, UNMOVABLE_INS)); #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) - PTR_FAIL_IF(emit_const(compiler, dst_r, 0)); + compiler->size += 1; #else - PTR_FAIL_IF(push_inst(compiler, (sljit_ins)dst_r, UNMOVABLE_INS)); compiler->size += 5; #endif if (dst & SLJIT_MEM) - PTR_FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0)); + PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, DR(TMP_REG2), dst, dstw)); return put_label; } diff --git a/thirdparty/pcre2/src/sljit/sljitNativePPC_32.c b/thirdparty/pcre2/src/sljit/sljitNativePPC_32.c index 95fe6bbe0e..9449e4b9d7 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativePPC_32.c +++ b/thirdparty/pcre2/src/sljit/sljitNativePPC_32.c @@ -38,12 +38,15 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)) : SLJIT_SUCCESS; } +/* Simplified mnemonics: clrlwi. */ #define INS_CLEAR_LEFT(dst, src, from) \ - (RLWINM | S(src) | A(dst) | ((from) << 6) | (31 << 1)) + (RLWINM | S(src) | A(dst) | RLWI_MBE(from, 31)) static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, sljit_s32 dst, sljit_s32 src1, sljit_s32 src2) { + sljit_u32 imm; + switch (op) { case SLJIT_MOV: case SLJIT_MOV_U32: @@ -90,6 +93,16 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl SLJIT_ASSERT(src1 == TMP_REG1); return push_inst(compiler, CNTLZW | S(src2) | A(dst)); + case SLJIT_CTZ: + SLJIT_ASSERT(src1 == TMP_REG1); + FAIL_IF(push_inst(compiler, NEG | D(TMP_REG1) | A(src2))); + FAIL_IF(push_inst(compiler, AND | S(src2) | A(dst) | B(TMP_REG1))); + FAIL_IF(push_inst(compiler, CNTLZW | S(dst) | A(dst))); + FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG1) | A(dst) | IMM(-32))); + /* The highest bits are set, if dst < 32, zero otherwise. */ + FAIL_IF(push_inst(compiler, SRWI(27) | S(TMP_REG1) | A(TMP_REG1))); + return push_inst(compiler, XOR | S(dst) | A(dst) | B(TMP_REG1)); + case SLJIT_ADD: if (flags & ALT_FORM1) { /* Setting XER SO is not enough, CR SO is also needed. */ @@ -103,12 +116,14 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl if (flags & ALT_FORM3) return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm); + imm = compiler->imm; + if (flags & ALT_FORM4) { - FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1)))); + FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((imm >> 16) & 0xffff) + ((imm >> 15) & 0x1)))); src1 = dst; } - return push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff)); + return push_inst(compiler, ADDI | D(dst) | A(src1) | (imm & 0xffff)); } if (flags & ALT_FORM3) { SLJIT_ASSERT(src2 == TMP_REG2); @@ -208,8 +223,10 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl } if (flags & ALT_FORM3) { SLJIT_ASSERT(src2 == TMP_REG2); - FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(compiler->imm))); - return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(compiler->imm >> 16)); + imm = compiler->imm; + + FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(imm))); + return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(imm >> 16)); } return push_inst(compiler, OR | RC(flags) | S(src1) | A(dst) | B(src2)); @@ -224,34 +241,78 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl } if (flags & ALT_FORM3) { SLJIT_ASSERT(src2 == TMP_REG2); - FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(compiler->imm))); - return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(compiler->imm >> 16)); + imm = compiler->imm; + + FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(imm))); + return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(imm >> 16)); } return push_inst(compiler, XOR | RC(flags) | S(src1) | A(dst) | B(src2)); case SLJIT_SHL: + case SLJIT_MSHL: if (flags & ALT_FORM1) { SLJIT_ASSERT(src2 == TMP_REG2); - compiler->imm &= 0x1f; - return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11) | ((31 - compiler->imm) << 1)); + imm = compiler->imm & 0x1f; + return push_inst(compiler, SLWI(imm) | RC(flags) | S(src1) | A(dst)); + } + + if (op == SLJIT_MSHL) { + FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f)); + src2 = TMP_REG2; } + return push_inst(compiler, SLW | RC(flags) | S(src1) | A(dst) | B(src2)); case SLJIT_LSHR: + case SLJIT_MLSHR: if (flags & ALT_FORM1) { SLJIT_ASSERT(src2 == TMP_REG2); - compiler->imm &= 0x1f; - return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (((32 - compiler->imm) & 0x1f) << 11) | (compiler->imm << 6) | (31 << 1)); + imm = compiler->imm & 0x1f; + /* Since imm can be 0, SRWI() cannot be used. */ + return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | RLWI_SH((32 - imm) & 0x1f) | RLWI_MBE(imm, 31)); + } + + if (op == SLJIT_MLSHR) { + FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f)); + src2 = TMP_REG2; } + return push_inst(compiler, SRW | RC(flags) | S(src1) | A(dst) | B(src2)); case SLJIT_ASHR: + case SLJIT_MASHR: if (flags & ALT_FORM1) { SLJIT_ASSERT(src2 == TMP_REG2); - compiler->imm &= 0x1f; - return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11)); + imm = compiler->imm & 0x1f; + return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (imm << 11)); + } + + if (op == SLJIT_MASHR) { + FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f)); + src2 = TMP_REG2; } + return push_inst(compiler, SRAW | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_ROTL: + case SLJIT_ROTR: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + imm = compiler->imm; + + if (op == SLJIT_ROTR) + imm = (sljit_u32)(-(sljit_s32)imm); + + imm &= 0x1f; + return push_inst(compiler, RLWINM | S(src1) | A(dst) | RLWI_SH(imm) | RLWI_MBE(0, 31)); + } + + if (op == SLJIT_ROTR) { + FAIL_IF(push_inst(compiler, SUBFIC | D(TMP_REG2) | A(src2) | 0)); + src2 = TMP_REG2; + } + + return push_inst(compiler, RLWNM | S(src1) | A(dst) | B(src2) | RLWI_MBE(0, 31)); } SLJIT_UNREACHABLE(); @@ -277,8 +338,3 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_ta inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 2); } - -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) -{ - sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset); -} diff --git a/thirdparty/pcre2/src/sljit/sljitNativePPC_64.c b/thirdparty/pcre2/src/sljit/sljitNativePPC_64.c index d104f6d75f..80549108bf 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativePPC_64.c +++ b/thirdparty/pcre2/src/sljit/sljitNativePPC_64.c @@ -35,8 +35,9 @@ #error "Must implement count leading zeroes" #endif -#define PUSH_RLDICR(reg, shift) \ - push_inst(compiler, RLDI(reg, reg, 63 - shift, shift, 1)) +/* Computes SLDI(63 - shift). */ +#define PUSH_SLDI_NEG(reg, shift) \ + push_inst(compiler, RLDICR | S(reg) | A(reg) | RLDI_SH(63 - shift) | RLDI_ME(shift)) static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm) { @@ -66,14 +67,14 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, if ((tmp & ~0xffff000000000000ul) == 0) { FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48))); shift += 15; - return PUSH_RLDICR(reg, shift); + return PUSH_SLDI_NEG(reg, shift); } if ((tmp & ~0xffffffff00000000ul) == 0) { FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | (sljit_ins)(tmp >> 48))); FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(tmp >> 32))); shift += 31; - return PUSH_RLDICR(reg, shift); + return PUSH_SLDI_NEG(reg, shift); } /* Cut out the 16 bit from immediate. */ @@ -82,13 +83,13 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, if (tmp2 <= 0xffff) { FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48))); - FAIL_IF(PUSH_RLDICR(reg, shift)); + FAIL_IF(PUSH_SLDI_NEG(reg, shift)); return push_inst(compiler, ORI | S(reg) | A(reg) | (sljit_ins)tmp2); } if (tmp2 <= 0xffffffff) { FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | IMM(tmp >> 48))); - FAIL_IF(PUSH_RLDICR(reg, shift)); + FAIL_IF(PUSH_SLDI_NEG(reg, shift)); FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | (sljit_ins)(tmp2 >> 16))); return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(tmp2)) : SLJIT_SUCCESS; } @@ -100,22 +101,23 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48))); shift2 += 15; shift += (63 - shift2); - FAIL_IF(PUSH_RLDICR(reg, shift)); + FAIL_IF(PUSH_SLDI_NEG(reg, shift)); FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | (sljit_ins)(tmp2 >> 48))); - return PUSH_RLDICR(reg, shift2); + return PUSH_SLDI_NEG(reg, shift2); } /* The general version. */ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | (sljit_ins)((sljit_uw)imm >> 48))); FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm >> 32))); - FAIL_IF(PUSH_RLDICR(reg, 31)); + FAIL_IF(PUSH_SLDI_NEG(reg, 31)); FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | IMM(imm >> 16))); return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)); } -/* Simplified mnemonics: clrldi. */ -#define INS_CLEAR_LEFT(dst, src, from) \ - (RLDICL | S(src) | A(dst) | ((from) << 6) | (1 << 5)) +#undef PUSH_SLDI_NEG + +#define CLRLDI(dst, src, n) \ + (RLDICL | S(src) | A(dst) | RLDI_SH(0) | RLDI_MB(n)) /* Sign extension for integer operations. */ #define UN_EXTS() \ @@ -145,6 +147,8 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, sljit_s32 dst, sljit_s32 src1, sljit_s32 src2) { + sljit_u32 imm; + switch (op) { case SLJIT_MOV: case SLJIT_MOV_P: @@ -159,7 +163,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { if (op == SLJIT_MOV_S32) return push_inst(compiler, EXTSW | S(src2) | A(dst)); - return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 0)); + return push_inst(compiler, CLRLDI(dst, src2, 32)); } else { SLJIT_ASSERT(dst == src2); @@ -172,7 +176,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { if (op == SLJIT_MOV_S8) return push_inst(compiler, EXTSB | S(src2) | A(dst)); - return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 24)); + return push_inst(compiler, CLRLDI(dst, src2, 56)); } else if ((flags & REG_DEST) && op == SLJIT_MOV_S8) return push_inst(compiler, EXTSB | S(src2) | A(dst)); @@ -187,7 +191,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { if (op == SLJIT_MOV_S16) return push_inst(compiler, EXTSH | S(src2) | A(dst)); - return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 16)); + return push_inst(compiler, CLRLDI(dst, src2, 48)); } else { SLJIT_ASSERT(dst == src2); @@ -201,22 +205,30 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl case SLJIT_CLZ: SLJIT_ASSERT(src1 == TMP_REG1); - if (flags & ALT_FORM1) - return push_inst(compiler, CNTLZW | S(src2) | A(dst)); - return push_inst(compiler, CNTLZD | S(src2) | A(dst)); + return push_inst(compiler, ((flags & ALT_FORM1) ? CNTLZW : CNTLZD) | S(src2) | A(dst)); + + case SLJIT_CTZ: + SLJIT_ASSERT(src1 == TMP_REG1); + FAIL_IF(push_inst(compiler, NEG | D(TMP_REG1) | A(src2))); + FAIL_IF(push_inst(compiler, AND | S(src2) | A(dst) | B(TMP_REG1))); + FAIL_IF(push_inst(compiler, ((flags & ALT_FORM1) ? CNTLZW : CNTLZD) | S(dst) | A(dst))); + FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG1) | A(dst) | IMM((flags & ALT_FORM1) ? -32 : -64))); + /* The highest bits are set, if dst < bit width, zero otherwise. */ + FAIL_IF(push_inst(compiler, ((flags & ALT_FORM1) ? SRWI(27) : SRDI(58)) | S(TMP_REG1) | A(TMP_REG1))); + return push_inst(compiler, XOR | S(dst) | A(dst) | B(TMP_REG1)); case SLJIT_ADD: if (flags & ALT_FORM1) { if (flags & ALT_SIGN_EXT) { - FAIL_IF(push_inst(compiler, RLDI(TMP_REG1, src1, 32, 31, 1))); + FAIL_IF(push_inst(compiler, SLDI(32) | S(src1) | A(TMP_REG1))); src1 = TMP_REG1; - FAIL_IF(push_inst(compiler, RLDI(TMP_REG2, src2, 32, 31, 1))); + FAIL_IF(push_inst(compiler, SLDI(32) | S(src2) | A(TMP_REG2))); src2 = TMP_REG2; } /* Setting XER SO is not enough, CR SO is also needed. */ FAIL_IF(push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2))); if (flags & ALT_SIGN_EXT) - return push_inst(compiler, RLDI(dst, dst, 32, 32, 0)); + return push_inst(compiler, SRDI(32) | S(dst) | A(dst)); return SLJIT_SUCCESS; } @@ -227,12 +239,14 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl if (flags & ALT_FORM3) return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm); + imm = compiler->imm; + if (flags & ALT_FORM4) { - FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1)))); + FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((imm >> 16) & 0xffff) + ((imm >> 15) & 0x1)))); src1 = dst; } - return push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff)); + return push_inst(compiler, ADDI | D(dst) | A(src1) | (imm & 0xffff)); } if (flags & ALT_FORM3) { SLJIT_ASSERT(src2 == TMP_REG2); @@ -287,11 +301,11 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl if (flags & ALT_FORM3) { if (flags & ALT_SIGN_EXT) { if (src1 != TMP_ZERO) { - FAIL_IF(push_inst(compiler, RLDI(TMP_REG1, src1, 32, 31, 1))); + FAIL_IF(push_inst(compiler, SLDI(32) | S(src1) | A(TMP_REG1))); src1 = TMP_REG1; } if (src2 != TMP_ZERO) { - FAIL_IF(push_inst(compiler, RLDI(TMP_REG2, src2, 32, 31, 1))); + FAIL_IF(push_inst(compiler, SLDI(32) | S(src2) | A(TMP_REG2))); src2 = TMP_REG2; } } @@ -303,7 +317,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl FAIL_IF(push_inst(compiler, NEG | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2))); if (flags & ALT_SIGN_EXT) - return push_inst(compiler, RLDI(dst, dst, 32, 32, 0)); + return push_inst(compiler, SRDI(32) | S(dst) | A(dst)); return SLJIT_SUCCESS; } @@ -362,8 +376,10 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl } if (flags & ALT_FORM3) { SLJIT_ASSERT(src2 == TMP_REG2); - FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(compiler->imm))); - return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(compiler->imm >> 16)); + imm = compiler->imm; + + FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(imm))); + return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(imm >> 16)); } return push_inst(compiler, OR | RC(flags) | S(src1) | A(dst) | B(src2)); @@ -378,46 +394,105 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl } if (flags & ALT_FORM3) { SLJIT_ASSERT(src2 == TMP_REG2); - FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(compiler->imm))); - return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(compiler->imm >> 16)); + imm = compiler->imm; + + FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(imm))); + return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(imm >> 16)); } return push_inst(compiler, XOR | RC(flags) | S(src1) | A(dst) | B(src2)); case SLJIT_SHL: + case SLJIT_MSHL: if (flags & ALT_FORM1) { SLJIT_ASSERT(src2 == TMP_REG2); + imm = compiler->imm; + if (flags & ALT_FORM2) { - compiler->imm &= 0x1f; - return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11) | ((31 - compiler->imm) << 1)); + imm &= 0x1f; + return push_inst(compiler, SLWI(imm) | RC(flags) | S(src1) | A(dst)); } - compiler->imm &= 0x3f; - return push_inst(compiler, RLDI(dst, src1, compiler->imm, 63 - compiler->imm, 1) | RC(flags)); + + imm &= 0x3f; + return push_inst(compiler, SLDI(imm) | RC(flags) | S(src1) | A(dst)); } + + if (op == SLJIT_MSHL) { + FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f))); + src2 = TMP_REG2; + } + return push_inst(compiler, ((flags & ALT_FORM2) ? SLW : SLD) | RC(flags) | S(src1) | A(dst) | B(src2)); case SLJIT_LSHR: + case SLJIT_MLSHR: if (flags & ALT_FORM1) { SLJIT_ASSERT(src2 == TMP_REG2); + imm = compiler->imm; + if (flags & ALT_FORM2) { - compiler->imm &= 0x1f; - return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (((32 - compiler->imm) & 0x1f) << 11) | (compiler->imm << 6) | (31 << 1)); + imm &= 0x1f; + /* Since imm can be 0, SRWI() cannot be used. */ + return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | RLWI_SH((32 - imm) & 0x1f) | RLWI_MBE(imm, 31)); } - compiler->imm &= 0x3f; - return push_inst(compiler, RLDI(dst, src1, 64 - compiler->imm, compiler->imm, 0) | RC(flags)); + + imm &= 0x3f; + /* Since imm can be 0, SRDI() cannot be used. */ + return push_inst(compiler, RLDICL | RC(flags) | S(src1) | A(dst) | RLDI_SH((64 - imm) & 0x3f) | RLDI_MB(imm)); } + + if (op == SLJIT_MLSHR) { + FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f))); + src2 = TMP_REG2; + } + return push_inst(compiler, ((flags & ALT_FORM2) ? SRW : SRD) | RC(flags) | S(src1) | A(dst) | B(src2)); case SLJIT_ASHR: + case SLJIT_MASHR: if (flags & ALT_FORM1) { SLJIT_ASSERT(src2 == TMP_REG2); + imm = compiler->imm; + if (flags & ALT_FORM2) { - compiler->imm &= 0x1f; - return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11)); + imm &= 0x1f; + return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (imm << 11)); } - compiler->imm &= 0x3f; - return push_inst(compiler, SRADI | RC(flags) | S(src1) | A(dst) | ((compiler->imm & 0x1f) << 11) | ((compiler->imm & 0x20) >> 4)); + + imm &= 0x3f; + return push_inst(compiler, SRADI | RC(flags) | S(src1) | A(dst) | RLDI_SH(imm)); } + + if (op == SLJIT_MASHR) { + FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f))); + src2 = TMP_REG2; + } + return push_inst(compiler, ((flags & ALT_FORM2) ? SRAW : SRAD) | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_ROTL: + case SLJIT_ROTR: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + imm = compiler->imm; + + if (op == SLJIT_ROTR) + imm = (sljit_u32)(-(sljit_s32)imm); + + if (flags & ALT_FORM2) { + imm &= 0x1f; + return push_inst(compiler, RLWINM | S(src1) | A(dst) | RLWI_SH(imm) | RLWI_MBE(0, 31)); + } + + imm &= 0x3f; + return push_inst(compiler, RLDICL | S(src1) | A(dst) | RLDI_SH(imm)); + } + + if (op == SLJIT_ROTR) { + FAIL_IF(push_inst(compiler, SUBFIC | D(TMP_REG2) | A(src2) | 0)); + src2 = TMP_REG2; + } + + return push_inst(compiler, ((flags & ALT_FORM2) ? (RLWNM | RLWI_MBE(0, 31)) : (RLDCL | RLDI_MB(0))) | S(src1) | A(dst) | B(src2)); } SLJIT_UNREACHABLE(); @@ -483,7 +558,7 @@ static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_ { FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(init_value >> 48))); FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value >> 32))); - FAIL_IF(PUSH_RLDICR(reg, 31)); + FAIL_IF(push_inst(compiler, SLDI(32) | S(reg) | A(reg))); FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | IMM(init_value >> 16))); return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value)); } @@ -502,8 +577,3 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_ta inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 5); } - -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) -{ - sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset); -} diff --git a/thirdparty/pcre2/src/sljit/sljitNativePPC_common.c b/thirdparty/pcre2/src/sljit/sljitNativePPC_common.c index 8bfdc69522..f387114733 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativePPC_common.c +++ b/thirdparty/pcre2/src/sljit/sljitNativePPC_common.c @@ -203,8 +203,13 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define OR (HI(31) | LO(444)) #define ORI (HI(24)) #define ORIS (HI(25)) -#define RLDICL (HI(30)) +#define RLDCL (HI(30) | LO(8)) +#define RLDICL (HI(30) | LO(0 << 1)) +#define RLDICR (HI(30) | LO(1 << 1)) +#define RLDIMI (HI(30) | LO(3 << 1)) +#define RLWIMI (HI(20)) #define RLWINM (HI(21)) +#define RLWNM (HI(23)) #define SLD (HI(31) | LO(27)) #define SLW (HI(31) | LO(24)) #define SRAD (HI(31) | LO(794)) @@ -233,9 +238,24 @@ static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { #define SIMM_MIN (-0x8000) #define UIMM_MAX (0xffff) -#define RLDI(dst, src, sh, mb, type) \ - (HI(30) | S(src) | A(dst) | ((sljit_ins)(type) << 2) | (((sljit_ins)(sh) & 0x1f) << 11) \ - | (((sljit_ins)(sh) & 0x20) >> 4) | (((sljit_ins)(mb) & 0x1f) << 6) | ((sljit_ins)(mb) & 0x20)) +/* Shift helpers. */ +#define RLWI_SH(sh) ((sljit_ins)(sh) << 11) +#define RLWI_MBE(mb, me) (((sljit_ins)(mb) << 6) | ((sljit_ins)(me) << 1)) +#define RLDI_SH(sh) ((((sljit_ins)(sh) & 0x1f) << 11) | (((sljit_ins)(sh) & 0x20) >> 4)) +#define RLDI_MB(mb) ((((sljit_ins)(mb) & 0x1f) << 6) | ((sljit_ins)(mb) & 0x20)) +#define RLDI_ME(me) RLDI_MB(me) + +#define SLWI(shift) (RLWINM | RLWI_SH(shift) | RLWI_MBE(0, 31 - (shift))) +#define SLDI(shift) (RLDICR | RLDI_SH(shift) | RLDI_ME(63 - (shift))) +/* shift > 0 */ +#define SRWI(shift) (RLWINM | RLWI_SH(32 - (shift)) | RLWI_MBE((shift), 31)) +#define SRDI(shift) (RLDICL | RLDI_SH(64 - (shift)) | RLDI_MB(shift)) + +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) +#define SLWI_W(shift) SLWI(shift) +#else /* !SLJIT_CONFIG_PPC_32 */ +#define SLWI_W(shift) SLDI(shift) +#endif /* SLJIT_CONFIG_PPC_32 */ #if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct sljit_function_context* context, sljit_uw addr, void* func) @@ -368,10 +388,10 @@ static SLJIT_INLINE void put_label_set(struct sljit_put_label *put_label) else { inst[0] = ORIS | S(TMP_ZERO) | A(reg) | IMM(addr >> 48); inst[1] = ORI | S(reg) | A(reg) | IMM((addr >> 32) & 0xffff); - inst ++; + inst++; } - inst[1] = RLDI(reg, reg, 32, 31, 1); + inst[1] = SLDI(32) | S(reg) | A(reg); inst[2] = ORIS | S(reg) | A(reg) | IMM((addr >> 16) & 0xffff); inst += 2; } @@ -379,7 +399,7 @@ static SLJIT_INLINE void put_label_set(struct sljit_put_label *put_label) inst[1] = ORI | S(reg) | A(reg) | IMM(addr & 0xffff); } -#endif +#endif /* SLJIT_CONFIG_PPC_64 */ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) { @@ -497,8 +517,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } next_addr = compute_next_addr(label, jump, const_, put_label); } - code_ptr ++; - word_count ++; + code_ptr++; + word_count++; } while (buf_ptr < buf_end); buf = buf->next; @@ -641,14 +661,23 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) /* A saved register is set to a zero value. */ case SLJIT_HAS_ZERO_REGISTER: case SLJIT_HAS_CLZ: + case SLJIT_HAS_ROT: case SLJIT_HAS_PREFETCH: return 1; + case SLJIT_HAS_CTZ: + return 2; + default: return 0; } } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type) +{ + return (type >= SLJIT_UNORDERED && type <= SLJIT_ORDERED_LESS_EQUAL); +} + /* --------------------------------------------------------------------- */ /* Entry, exit */ /* --------------------------------------------------------------------- */ @@ -715,13 +744,16 @@ ALT_FORM5 0x010000 */ #define STACK_MAX_DISTANCE (0x8000 - SSIZE_OF(sw) - LR_SAVE_OFFSET) +static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg, + sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg); + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { sljit_s32 i, tmp, base, offset; sljit_s32 word_arg_count = 0; - sljit_s32 saved_arg_count = 0; + sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options); #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) sljit_s32 arg_count = 0; #endif @@ -730,8 +762,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1) + local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 0) + GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64)); + + if (!(options & SLJIT_ENTER_REG_ARG)) + local_size += SSIZE_OF(sw); + local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf; compiler->local_size = local_size; @@ -770,11 +806,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi FAIL_IF(push_inst(compiler, STFD | FS(i) | A(base) | IMM(offset))); } - offset -= SSIZE_OF(sw); - FAIL_IF(push_inst(compiler, STACK_STORE | S(TMP_ZERO) | A(base) | IMM(offset))); + if (!(options & SLJIT_ENTER_REG_ARG)) { + offset -= SSIZE_OF(sw); + FAIL_IF(push_inst(compiler, STACK_STORE | S(TMP_ZERO) | A(base) | IMM(offset))); + } tmp = SLJIT_S0 - saveds; - for (i = SLJIT_S0; i > tmp; i--) { + for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) { offset -= SSIZE_OF(sw); FAIL_IF(push_inst(compiler, STACK_STORE | S(i) | A(base) | IMM(offset))); } @@ -785,9 +823,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi } FAIL_IF(push_inst(compiler, STACK_STORE | S(0) | A(base) | IMM(local_size + LR_SAVE_OFFSET))); + + if (options & SLJIT_ENTER_REG_ARG) + return SLJIT_SUCCESS; + FAIL_IF(push_inst(compiler, ADDI | D(TMP_ZERO) | A(0) | 0)); arg_types >>= SLJIT_ARG_SHIFT; + saved_arg_count = 0; while (arg_types > 0) { if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) { @@ -829,14 +872,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1) + local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 0) + GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64)); + + if (!(options & SLJIT_ENTER_REG_ARG)) + local_size += SSIZE_OF(sw); + compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf; return SLJIT_SUCCESS; } - -static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) +static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to) { sljit_s32 i, tmp, base, offset; sljit_s32 local_size = compiler->local_size; @@ -854,7 +900,8 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) } offset = local_size; - FAIL_IF(push_inst(compiler, STACK_LOAD | S(0) | A(base) | IMM(offset + LR_SAVE_OFFSET))); + if (!is_return_to) + FAIL_IF(push_inst(compiler, STACK_LOAD | S(0) | A(base) | IMM(offset + LR_SAVE_OFFSET))); tmp = SLJIT_FS0 - compiler->fsaveds; for (i = SLJIT_FS0; i > tmp; i--) { @@ -867,11 +914,13 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) FAIL_IF(push_inst(compiler, LFD | FS(i) | A(base) | IMM(offset))); } - offset -= SSIZE_OF(sw); - FAIL_IF(push_inst(compiler, STACK_LOAD | S(TMP_ZERO) | A(base) | IMM(offset))); + if (!(compiler->options & SLJIT_ENTER_REG_ARG)) { + offset -= SSIZE_OF(sw); + FAIL_IF(push_inst(compiler, STACK_LOAD | S(TMP_ZERO) | A(base) | IMM(offset))); + } tmp = SLJIT_S0 - compiler->saveds; - for (i = SLJIT_S0; i > tmp; i--) { + for (i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); i > tmp; i--) { offset -= SSIZE_OF(sw); FAIL_IF(push_inst(compiler, STACK_LOAD | S(i) | A(base) | IMM(offset))); } @@ -881,7 +930,8 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) FAIL_IF(push_inst(compiler, STACK_LOAD | S(i) | A(base) | IMM(offset))); } - push_inst(compiler, MTLR | S(0)); + if (!is_return_to) + push_inst(compiler, MTLR | S(0)); if (local_size > 0) return push_inst(compiler, ADDI | D(SLJIT_SP) | A(base) | IMM(local_size)); @@ -890,17 +940,40 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) return push_inst(compiler, OR | S(base) | A(SLJIT_SP) | B(base)); } +#undef STACK_STORE +#undef STACK_LOAD + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler) { CHECK_ERROR(); CHECK(check_sljit_emit_return_void(compiler)); - FAIL_IF(emit_stack_frame_release(compiler)); + FAIL_IF(emit_stack_frame_release(compiler, 0)); return push_inst(compiler, BLR); } -#undef STACK_STORE -#undef STACK_LOAD +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_return_to(compiler, src, srcw)); + + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_CALL_REG, src, srcw, TMP_CALL_REG)); + src = TMP_CALL_REG; + srcw = 0; + } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + FAIL_IF(push_inst(compiler, OR | S(src) | A(TMP_CALL_REG) | B(src))); + src = TMP_CALL_REG; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, 1)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); +} /* --------------------------------------------------------------------- */ /* Operators */ @@ -1066,7 +1139,6 @@ static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flag { sljit_ins inst; sljit_s32 offs_reg; - sljit_sw high_short; /* Should work when (arg & REG_MASK) == 0. */ SLJIT_ASSERT(A(0) == 0); @@ -1077,11 +1149,7 @@ static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flag offs_reg = OFFS_REG(arg); if (argw != 0) { -#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) - FAIL_IF(push_inst(compiler, RLWINM | S(OFFS_REG(arg)) | A(tmp_reg) | ((sljit_ins)argw << 11) | ((31 - (sljit_ins)argw) << 1))); -#else - FAIL_IF(push_inst(compiler, RLDI(tmp_reg, OFFS_REG(arg), argw, 63 - argw, 1))); -#endif + FAIL_IF(push_inst(compiler, SLWI_W(argw) | S(OFFS_REG(arg)) | A(tmp_reg))); offs_reg = tmp_reg; } @@ -1089,7 +1157,7 @@ static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flag #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) SLJIT_ASSERT(!(inst & INT_ALIGNED)); -#endif +#endif /* SLJIT_CONFIG_PPC_64 */ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(offs_reg)); } @@ -1104,36 +1172,24 @@ static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flag inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | B(tmp_reg)); } -#endif +#endif /* SLJIT_CONFIG_PPC_64 */ if (argw <= SIMM_MAX && argw >= SIMM_MIN) return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | IMM(argw)); #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) if (argw <= 0x7fff7fffl && argw >= -0x80000000l) { -#endif - - high_short = (sljit_s32)(argw + ((argw & 0x8000) << 1)) & ~0xffff; - -#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - SLJIT_ASSERT(high_short && high_short <= 0x7fffffffl && high_short >= -0x80000000l); -#else - SLJIT_ASSERT(high_short); -#endif - - FAIL_IF(push_inst(compiler, ADDIS | D(tmp_reg) | A(arg) | IMM(high_short >> 16))); +#endif /* SLJIT_CONFIG_PPC_64 */ + FAIL_IF(push_inst(compiler, ADDIS | D(tmp_reg) | A(arg) | IMM((argw + 0x8000) >> 16))); return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(tmp_reg) | IMM(argw)); - #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) } - /* The rest is PPC-64 only. */ - FAIL_IF(load_immediate(compiler, tmp_reg, argw)); inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | B(tmp_reg)); -#endif +#endif /* SLJIT_CONFIG_PPC_64 */ } static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 input_flags, @@ -1273,11 +1329,7 @@ static sljit_s32 emit_prefetch(struct sljit_compiler *compiler, if (srcw == 0) return push_inst(compiler, DCBT | A(src & REG_MASK) | B(OFFS_REG(src))); -#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) - FAIL_IF(push_inst(compiler, RLWINM | S(OFFS_REG(src)) | A(TMP_REG1) | ((sljit_ins)srcw << 11) | ((31 - (sljit_ins)srcw) << 1))); -#else - FAIL_IF(push_inst(compiler, RLDI(TMP_REG1, OFFS_REG(src), srcw, 63 - srcw, 1))); -#endif + FAIL_IF(push_inst(compiler, SLWI_W(srcw) | S(OFFS_REG(src)) | A(TMP_REG1))); return push_inst(compiler, DCBT | A(src & REG_MASK) | B(TMP_REG1)); } @@ -1362,10 +1414,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile return emit_op(compiler, SLJIT_NOT, flags, dst, dstw, TMP_REG1, 0, src, srcw); case SLJIT_CLZ: + case SLJIT_CTZ: #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - return emit_op(compiler, SLJIT_CLZ, flags | (!(op_flags & SLJIT_32) ? 0 : ALT_FORM1), dst, dstw, TMP_REG1, 0, src, srcw); + return emit_op(compiler, op, flags | (!(op_flags & SLJIT_32) ? 0 : ALT_FORM1), dst, dstw, TMP_REG1, 0, src, srcw); #else - return emit_op(compiler, SLJIT_CLZ, flags, dst, dstw, TMP_REG1, 0, src, srcw); + return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw); #endif } @@ -1626,7 +1679,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0); } } - if (GET_OPCODE(op) != SLJIT_AND) { + if (!HAS_FLAGS(op) && GET_OPCODE(op) != SLJIT_AND) { /* Unlike or and xor, the and resets unwanted bits as well. */ if (TEST_UI_IMM(src2, src2w)) { compiler->imm = (sljit_ins)src2w; @@ -1640,8 +1693,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile return emit_op(compiler, GET_OPCODE(op), flags, dst, dstw, src1, src1w, src2, src2w); case SLJIT_SHL: + case SLJIT_MSHL: case SLJIT_LSHR: + case SLJIT_MLSHR: case SLJIT_ASHR: + case SLJIT_MASHR: + case SLJIT_ROTL: + case SLJIT_ROTR: #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) if (op & SLJIT_32) flags |= ALT_FORM2; @@ -1663,10 +1721,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compil CHECK_ERROR(); CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w); } @@ -1674,6 +1729,102 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compil #undef TEST_SUB_FORM2 #undef TEST_SUB_FORM3 +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_s32 is_right; +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + sljit_s32 inp_flags = ((op & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA; + sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64; +#else /* !SLJIT_CONFIG_PPC_64 */ + sljit_s32 inp_flags = WORD_DATA | LOAD_DATA; + sljit_sw bit_length = 32; +#endif /* SLJIT_CONFIG_PPC_64 */ + + CHECK_ERROR(); + CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w)); + + is_right = (GET_OPCODE(op) == SLJIT_LSHR || GET_OPCODE(op) == SLJIT_MLSHR); + + if (src_dst == src1) { + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_op2(compiler, (is_right ? SLJIT_ROTR : SLJIT_ROTL) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w); + } + + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + + if (src2 & SLJIT_IMM) { + src2w &= bit_length - 1; + + if (src2w == 0) + return SLJIT_SUCCESS; + } else if (src2 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG2, src2, src2w, TMP_REG2)); + src2 = TMP_REG2; + } + + if (src1 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG1, src1, src1w, TMP_REG1)); + src1 = TMP_REG1; + } else if (src1 & SLJIT_IMM) { + FAIL_IF(load_immediate(compiler, TMP_REG1, src1w)); + src1 = TMP_REG1; + } + + if (src2 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if (!(op & SLJIT_32)) { + if (is_right) { + FAIL_IF(push_inst(compiler, SRDI(src2w) | S(src_dst) | A(src_dst))); + return push_inst(compiler, RLDIMI | S(src1) | A(src_dst) | RLDI_SH(64 - src2w) | RLDI_MB(0)); + } + + FAIL_IF(push_inst(compiler, SLDI(src2w) | S(src_dst) | A(src_dst))); + /* Computes SRDI(64 - src2w). */ + FAIL_IF(push_inst(compiler, RLDICL | S(src1) | A(TMP_REG1) | RLDI_SH(src2w) | RLDI_MB(64 - src2w))); + return push_inst(compiler, OR | S(src_dst) | A(src_dst) | B(TMP_REG1)); + } +#endif /* SLJIT_CONFIG_PPC_64 */ + + if (is_right) { + FAIL_IF(push_inst(compiler, SRWI(src2w) | S(src_dst) | A(src_dst))); + return push_inst(compiler, RLWIMI | S(src1) | A(src_dst) | RLWI_SH(32 - src2w) | RLWI_MBE(0, src2w - 1)); + } + + FAIL_IF(push_inst(compiler, SLWI(src2w) | S(src_dst) | A(src_dst))); + return push_inst(compiler, RLWIMI | S(src1) | A(src_dst) | RLWI_SH(src2w) | RLWI_MBE(32 - src2w, 31)); + } + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if (!(op & SLJIT_32)) { + if (GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR) { + FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x3f)); + src2 = TMP_REG2; + } + + FAIL_IF(push_inst(compiler, (is_right ? SRD : SLD) | S(src_dst) | A(src_dst) | B(src2))); + FAIL_IF(push_inst(compiler, (is_right ? SLDI(1) : SRDI(1)) | S(src1) | A(TMP_REG1))); + FAIL_IF(push_inst(compiler, XORI | S(src2) | A(TMP_REG2) | 0x3f)); + FAIL_IF(push_inst(compiler, (is_right ? SLD : SRD) | S(TMP_REG1) | A(TMP_REG1) | B(TMP_REG2))); + return push_inst(compiler, OR | S(src_dst) | A(src_dst) | B(TMP_REG1)); + } +#endif /* SLJIT_CONFIG_PPC_64 */ + + if (GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR) { + FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f)); + src2 = TMP_REG2; + } + + FAIL_IF(push_inst(compiler, (is_right ? SRW : SLW) | S(src_dst) | A(src_dst) | B(src2))); + FAIL_IF(push_inst(compiler, (is_right ? SLWI(1) : SRWI(1)) | S(src1) | A(TMP_REG1))); + FAIL_IF(push_inst(compiler, XORI | S(src2) | A(TMP_REG2) | 0x1f)); + FAIL_IF(push_inst(compiler, (is_right ? SLW : SRW) | S(TMP_REG1) | A(TMP_REG1) | B(TMP_REG2))); + return push_inst(compiler, OR | S(src_dst) | A(src_dst) | B(TMP_REG1)); +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { @@ -1686,7 +1837,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *comp if (FAST_IS_REG(src)) FAIL_IF(push_inst(compiler, MTLR | S(src))); else { - FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw, TMP_REG2)); FAIL_IF(push_inst(compiler, MTLR | S(TMP_REG2))); } @@ -1782,11 +1933,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp if (dst & OFFS_REG_MASK) { dstw &= 0x3; if (dstw) { -#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) - FAIL_IF(push_inst(compiler, RLWINM | S(OFFS_REG(dst)) | A(TMP_REG1) | ((sljit_ins)dstw << 11) | ((31 - (sljit_ins)dstw) << 1))); -#else - FAIL_IF(push_inst(compiler, RLDI(TMP_REG1, OFFS_REG(dst), dstw, 63 - dstw, 1))); -#endif + FAIL_IF(push_inst(compiler, SLWI_W(dstw) | S(OFFS_REG(dst)) | A(TMP_REG1))); dstw = TMP_REG1; } else @@ -1818,6 +1965,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp if (src & SLJIT_IMM) { if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) srcw = (sljit_s32)srcw; + FAIL_IF(load_immediate(compiler, TMP_REG1, srcw)); src = TMP_REG1; } @@ -1863,7 +2011,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp The double precision format has exactly 53 bit precision, so the lower 32 bit represents the lower 32 bit of such value. The result of xor 2^31 is the same as adding 0x80000000 to the input, which shifts it into the 0 - 0xffffffff range. To get the converted floating - point value, we need to substract 2^53 + 2^31 from the constructed value. */ + point value, we need to subtract 2^53 + 2^31 from the constructed value. */ FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG2) | A(0) | 0x4330)); if (invert_sign) FAIL_IF(push_inst(compiler, XORIS | S(src) | A(TMP_REG1) | 0x8000)); @@ -1899,7 +2047,21 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile src2 = TMP_FREG2; } - return push_inst(compiler, FCMPU | CRD(4) | FA(src1) | FB(src2)); + FAIL_IF(push_inst(compiler, FCMPU | CRD(4) | FA(src1) | FB(src2))); + + switch (GET_FLAG_TYPE(op)) { + case SLJIT_UNORDERED_OR_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: + return push_inst(compiler, CROR | ((4 + 2) << 21) | ((4 + 2) << 16) | ((4 + 3) << 11)); + case SLJIT_UNORDERED_OR_LESS: + case SLJIT_ORDERED_GREATER_EQUAL: + return push_inst(compiler, CROR | ((4 + 0) << 21) | ((4 + 0) << 16) | ((4 + 3) << 11)); + case SLJIT_UNORDERED_OR_GREATER: + case SLJIT_ORDERED_LESS_EQUAL: + return push_inst(compiler, CROR | ((4 + 1) << 21) | ((4 + 1) << 16) | ((4 + 3) << 11)); + } + + return SLJIT_SUCCESS; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op, @@ -2076,38 +2238,50 @@ static sljit_ins get_bo_bi_flags(struct sljit_compiler *compiler, sljit_s32 type case SLJIT_SIG_LESS_EQUAL: return (4 << 21) | (1 << 16); - case SLJIT_LESS_F64: + case SLJIT_OVERFLOW: + return (12 << 21) | (3 << 16); + + case SLJIT_NOT_OVERFLOW: + return (4 << 21) | (3 << 16); + + case SLJIT_F_LESS: + case SLJIT_ORDERED_LESS: + case SLJIT_UNORDERED_OR_LESS: return (12 << 21) | ((4 + 0) << 16); - case SLJIT_GREATER_EQUAL_F64: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: return (4 << 21) | ((4 + 0) << 16); - case SLJIT_GREATER_F64: + case SLJIT_F_GREATER: + case SLJIT_ORDERED_GREATER: + case SLJIT_UNORDERED_OR_GREATER: return (12 << 21) | ((4 + 1) << 16); - case SLJIT_LESS_EQUAL_F64: + case SLJIT_F_LESS_EQUAL: + case SLJIT_ORDERED_LESS_EQUAL: + case SLJIT_UNORDERED_OR_LESS_EQUAL: return (4 << 21) | ((4 + 1) << 16); - case SLJIT_OVERFLOW: - return (12 << 21) | (3 << 16); - - case SLJIT_NOT_OVERFLOW: - return (4 << 21) | (3 << 16); - - case SLJIT_EQUAL_F64: + case SLJIT_F_EQUAL: + case SLJIT_ORDERED_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: return (12 << 21) | ((4 + 2) << 16); - case SLJIT_NOT_EQUAL_F64: + case SLJIT_F_NOT_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: return (4 << 21) | ((4 + 2) << 16); - case SLJIT_UNORDERED_F64: + case SLJIT_UNORDERED: return (12 << 21) | ((4 + 3) << 16); - case SLJIT_ORDERED_F64: + case SLJIT_ORDERED: return (4 << 21) | ((4 + 3) << 16); default: - SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_CDECL); + SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_REG_ARG); return (20 << 21); } } @@ -2154,19 +2328,16 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL)); + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL)); #endif if (type & SLJIT_CALL_RETURN) { - PTR_FAIL_IF(emit_stack_frame_release(compiler)); + PTR_FAIL_IF(emit_stack_frame_release(compiler, 0)); type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); } -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_jump(compiler, type); } @@ -2177,7 +2348,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi CHECK_ERROR(); CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); - ADJUST_LOCAL_OFFSET(src, srcw); if (FAST_IS_REG(src)) { #if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL) @@ -2204,9 +2374,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi FAIL_IF(emit_const(compiler, TMP_CALL_REG, 0)); src_r = TMP_CALL_REG; - } - else { - FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_CALL_REG, 0, TMP_REG1, 0, src, srcw)); + } else { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_CALL_REG, src, srcw, TMP_CALL_REG)); src_r = TMP_CALL_REG; } @@ -2225,29 +2395,26 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi if (src & SLJIT_MEM) { ADJUST_LOCAL_OFFSET(src, srcw); - FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_CALL_REG, 0, TMP_REG1, 0, src, srcw)); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_CALL_REG, src, srcw, TMP_CALL_REG)); src = TMP_CALL_REG; } if (type & SLJIT_CALL_RETURN) { - if (src >= SLJIT_FIRST_SAVED_REG && src <= SLJIT_S0) { + if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { FAIL_IF(push_inst(compiler, OR | S(src) | A(TMP_CALL_REG) | B(src))); src = TMP_CALL_REG; } - FAIL_IF(emit_stack_frame_release(compiler)); + FAIL_IF(emit_stack_frame_release(compiler, 0)); type = SLJIT_JUMP; } #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - FAIL_IF(call_with_args(compiler, arg_types, &src)); -#endif - -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + FAIL_IF(call_with_args(compiler, arg_types, &src)); #endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_ijump(compiler, type, src, srcw); } @@ -2279,7 +2446,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co bit = 0; from_xer = 0; - switch (type & 0xff) { + switch (type) { case SLJIT_LESS: case SLJIT_SIG_LESS: break; @@ -2332,38 +2499,50 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co invert = (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD) != 0; break; - case SLJIT_LESS_F64: + case SLJIT_F_LESS: + case SLJIT_ORDERED_LESS: + case SLJIT_UNORDERED_OR_LESS: bit = 4 + 0; break; - case SLJIT_GREATER_EQUAL_F64: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: bit = 4 + 0; invert = 1; break; - case SLJIT_GREATER_F64: + case SLJIT_F_GREATER: + case SLJIT_ORDERED_GREATER: + case SLJIT_UNORDERED_OR_GREATER: bit = 4 + 1; break; - case SLJIT_LESS_EQUAL_F64: + case SLJIT_F_LESS_EQUAL: + case SLJIT_ORDERED_LESS_EQUAL: + case SLJIT_UNORDERED_OR_LESS_EQUAL: bit = 4 + 1; invert = 1; break; - case SLJIT_EQUAL_F64: + case SLJIT_F_EQUAL: + case SLJIT_ORDERED_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: bit = 4 + 2; break; - case SLJIT_NOT_EQUAL_F64: + case SLJIT_F_NOT_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: bit = 4 + 2; invert = 1; break; - case SLJIT_UNORDERED_F64: + case SLJIT_UNORDERED: bit = 4 + 3; break; - case SLJIT_ORDERED_F64: + case SLJIT_ORDERED: bit = 4 + 3; invert = 1; break; @@ -2374,7 +2553,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co } FAIL_IF(push_inst(compiler, (from_xer ? MFXER : MFCR) | D(reg))); - FAIL_IF(push_inst(compiler, RLWINM | S(reg) | A(reg) | ((1 + bit) << 11) | (31 << 6) | (31 << 1))); + /* Simplified mnemonics: extrwi. */ + FAIL_IF(push_inst(compiler, RLWINM | S(reg) | A(reg) | RLWI_SH(1 + bit) | RLWI_MBE(31, 31))); if (invert) FAIL_IF(push_inst(compiler, XORI | S(reg) | A(reg) | 0x1)); @@ -2385,10 +2565,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co return emit_op_mem(compiler, input_flags, reg, dst, dstw, TMP_REG1); } -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); + if (dst & SLJIT_MEM) return sljit_emit_op2(compiler, saved_op, dst, saved_dstw, TMP_REG1, 0, TMP_REG2, 0); return sljit_emit_op2(compiler, saved_op, dst, 0, dst, 0, TMP_REG2, 0); @@ -2404,16 +2582,95 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);; } +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + +#define EMIT_MEM_LOAD_IMM(inst, mem, memw) \ + ((sljit_s16)(memw) > SIMM_MAX - SSIZE_OF(sw)) + +#else /* !SLJIT_CONFIG_PPC_32 */ + +#define EMIT_MEM_LOAD_IMM(inst, mem, memw) \ + ((((inst) & INT_ALIGNED) && ((memw) & 0x3) != 0) \ + || ((sljit_s16)(memw) > SIMM_MAX - SSIZE_OF(sw)) \ + || ((memw) > 0x7fff7fffl || (memw) < -0x80000000l)) \ + +#endif /* SLJIT_CONFIG_PPC_32 */ + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw) { - sljit_s32 mem_flags; sljit_ins inst; CHECK_ERROR(); CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + if (!(reg & REG_PAIR_MASK)) + return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw); + + ADJUST_LOCAL_OFFSET(mem, memw); + + inst = data_transfer_insts[WORD_DATA | ((type & SLJIT_MEM_STORE) ? 0 : LOAD_DATA)]; + + if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) { + memw &= 0x3; + + if (memw != 0) { + FAIL_IF(push_inst(compiler, SLWI_W(memw) | S(OFFS_REG(mem)) | A(TMP_REG1))); + FAIL_IF(push_inst(compiler, ADD | D(TMP_REG1) | A(TMP_REG1) | B(mem & REG_MASK))); + } else + FAIL_IF(push_inst(compiler, ADD | D(TMP_REG1) | A(mem & REG_MASK) | B(OFFS_REG(mem)))); + + mem = TMP_REG1; + memw = 0; + } else { + if (EMIT_MEM_LOAD_IMM(inst, mem, memw)) { + if ((mem & REG_MASK) != 0) { + SLJIT_SKIP_CHECKS(compiler); + FAIL_IF(sljit_emit_op2(compiler, SLJIT_ADD, TMP_REG1, 0, mem & REG_MASK, 0, SLJIT_IMM, memw)); + } else + FAIL_IF(load_immediate(compiler, TMP_REG1, memw)); + + memw = 0; + mem = TMP_REG1; + } else if (memw > SIMM_MAX || memw < SIMM_MIN) { + FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG1) | A(mem & REG_MASK) | IMM((memw + 0x8000) >> 16))); + + memw &= 0xffff; + mem = TMP_REG1; + } else { + memw &= 0xffff; + mem &= REG_MASK; + } + } + + SLJIT_ASSERT((memw >= 0 && memw <= SIMM_MAX - SSIZE_OF(sw)) || (memw >= 0x8000 && memw <= 0xffff)); + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + inst &= (sljit_ins)~INT_ALIGNED; +#endif /* SLJIT_CONFIG_PPC_64 */ + + if (!(type & SLJIT_MEM_STORE) && mem == REG_PAIR_FIRST(reg)) { + FAIL_IF(push_inst(compiler, inst | D(REG_PAIR_SECOND(reg)) | A(mem) | IMM(memw + SSIZE_OF(sw)))); + return push_inst(compiler, inst | D(REG_PAIR_FIRST(reg)) | A(mem) | IMM(memw)); + } + + FAIL_IF(push_inst(compiler, inst | D(REG_PAIR_FIRST(reg)) | A(mem) | IMM(memw))); + return push_inst(compiler, inst | D(REG_PAIR_SECOND(reg)) | A(mem) | IMM(memw + SSIZE_OF(sw))); +} + +#undef EMIT_MEM_LOAD_IMM + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_s32 mem_flags; + sljit_ins inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw)); + if (type & SLJIT_MEM_POST) return SLJIT_ERR_UNSUPPORTED; @@ -2500,7 +2757,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compile return SLJIT_SUCCESS; } -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw) { @@ -2508,7 +2765,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compil sljit_ins inst; CHECK_ERROR(); - CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw)); + CHECK(check_sljit_emit_fmem_update(compiler, type, freg, mem, memw)); if (type & SLJIT_MEM_POST) return SLJIT_ERR_UNSUPPORTED; @@ -2587,3 +2844,8 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct slj return put_label; } + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) +{ + sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset); +} diff --git a/thirdparty/pcre2/src/sljit/sljitNativeRISCV_32.c b/thirdparty/pcre2/src/sljit/sljitNativeRISCV_32.c new file mode 100644 index 0000000000..b38e6924c8 --- /dev/null +++ b/thirdparty/pcre2/src/sljit/sljitNativeRISCV_32.c @@ -0,0 +1,73 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_r, sljit_sw imm, sljit_s32 tmp_r) +{ + SLJIT_UNUSED_ARG(tmp_r); + SLJIT_ASSERT(dst_r != tmp_r); + + if (imm <= SIMM_MAX && imm >= SIMM_MIN) + return push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm)); + + if (imm & 0x800) + imm += 0x1000; + + FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff))); + + if ((imm & 0xfff) == 0) + return SLJIT_SUCCESS; + + return push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)); +} + +static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value, sljit_ins last_ins) +{ + if ((init_value & 0x800) != 0) + init_value += 0x1000; + + FAIL_IF(push_inst(compiler, LUI | RD(dst) | (sljit_ins)(init_value & ~0xfff))); + return push_inst(compiler, last_ins | RS1(dst) | IMM_I(init_value)); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) +{ + sljit_ins *inst = (sljit_ins*)addr; + SLJIT_UNUSED_ARG(executable_offset); + + if ((new_target & 0x800) != 0) + new_target += 0x1000; + + SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0); + + SLJIT_ASSERT((inst[0] & 0x7f) == LUI); + inst[0] = (inst[0] & 0xfff) | (sljit_ins)((sljit_sw)new_target & ~0xfff); + SLJIT_ASSERT((inst[1] & 0x707f) == ADDI || (inst[1] & 0x707f) == JALR); + inst[1] = (inst[1] & 0xfffff) | IMM_I(new_target); + + SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); + SLJIT_CACHE_FLUSH(inst, inst + 5); +} diff --git a/thirdparty/pcre2/src/sljit/sljitNativeRISCV_64.c b/thirdparty/pcre2/src/sljit/sljitNativeRISCV_64.c new file mode 100644 index 0000000000..32cec7848d --- /dev/null +++ b/thirdparty/pcre2/src/sljit/sljitNativeRISCV_64.c @@ -0,0 +1,183 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_r, sljit_sw imm, sljit_s32 tmp_r) +{ + sljit_sw high; + + SLJIT_ASSERT(dst_r != tmp_r); + + if (imm <= SIMM_MAX && imm >= SIMM_MIN) + return push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm)); + + if (imm <= 0x7fffffffl && imm >= S32_MIN) { + if (imm > S32_MAX) { + SLJIT_ASSERT((imm & 0x800) != 0); + FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u)); + return push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)); + } + + if ((imm & 0x800) != 0) + imm += 0x1000; + + FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff))); + + if ((imm & 0xfff) == 0) + return SLJIT_SUCCESS; + + return push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)); + } + + /* Trailing zeroes could be used to produce shifted immediates. */ + + if (imm <= 0x7ffffffffffl && imm >= -0x80000000000l) { + high = imm >> 12; + + if (imm & 0x800) + high = ~high; + + if (high > S32_MAX) { + SLJIT_ASSERT((high & 0x800) != 0); + FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u)); + FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(high))); + } else { + if ((high & 0x800) != 0) + high += 0x1000; + + FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(high & ~0xfff))); + + if ((high & 0xfff) != 0) + FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(high))); + } + + FAIL_IF(push_inst(compiler, SLLI | RD(dst_r) | RS1(dst_r) | IMM_I(12))); + + if ((imm & 0xfff) != 0) + return push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)); + + return SLJIT_SUCCESS; + } + + high = imm >> 32; + imm = (sljit_s32)imm; + + if ((imm & 0x80000000l) != 0) + high = ~high; + + if (high <= 0x7ffff && high >= -0x80000) { + FAIL_IF(push_inst(compiler, LUI | RD(tmp_r) | (sljit_ins)(high << 12))); + high = 0x1000; + } else { + if ((high & 0x800) != 0) + high += 0x1000; + + FAIL_IF(push_inst(compiler, LUI | RD(tmp_r) | (sljit_ins)(high & ~0xfff))); + high &= 0xfff; + } + + if (imm <= SIMM_MAX && imm >= SIMM_MIN) { + FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm))); + imm = 0; + } else if (imm > S32_MAX) { + SLJIT_ASSERT((imm & 0x800) != 0); + + FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u)); + imm = 0x1000 | (imm & 0xfff); + } else { + if ((imm & 0x800) != 0) + imm += 0x1000; + + FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff))); + imm &= 0xfff; + } + + if ((high & 0xfff) != 0) + FAIL_IF(push_inst(compiler, ADDI | RD(tmp_r) | RS1(tmp_r) | IMM_I(high))); + + if (imm & 0x1000) + FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm))); + else if (imm != 0) + FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm))); + + FAIL_IF(push_inst(compiler, SLLI | RD(tmp_r) | RS1(tmp_r) | IMM_I((high & 0x1000) ? 20 : 32))); + return push_inst(compiler, XOR | RD(dst_r) | RS1(dst_r) | RS2(tmp_r)); +} + +static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value, sljit_ins last_ins) +{ + sljit_sw high; + + if ((init_value & 0x800) != 0) + init_value += 0x1000; + + high = init_value >> 32; + + if ((init_value & 0x80000000l) != 0) + high = ~high; + + if ((high & 0x800) != 0) + high += 0x1000; + + FAIL_IF(push_inst(compiler, LUI | RD(TMP_REG3) | (sljit_ins)(high & ~0xfff))); + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(high))); + FAIL_IF(push_inst(compiler, LUI | RD(dst) | (sljit_ins)(init_value & ~0xfff))); + FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(32))); + FAIL_IF(push_inst(compiler, XOR | RD(dst) | RS1(dst) | RS2(TMP_REG3))); + return push_inst(compiler, last_ins | RS1(dst) | IMM_I(init_value)); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) +{ + sljit_ins *inst = (sljit_ins*)addr; + sljit_sw high; + SLJIT_UNUSED_ARG(executable_offset); + + if ((new_target & 0x800) != 0) + new_target += 0x1000; + + high = (sljit_sw)new_target >> 32; + + if ((new_target & 0x80000000l) != 0) + high = ~high; + + if ((high & 0x800) != 0) + high += 0x1000; + + SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0); + + SLJIT_ASSERT((inst[0] & 0x7f) == LUI); + inst[0] = (inst[0] & 0xfff) | (sljit_ins)(high & ~0xfff); + SLJIT_ASSERT((inst[1] & 0x707f) == ADDI); + inst[1] = (inst[1] & 0xfffff) | IMM_I(high); + SLJIT_ASSERT((inst[2] & 0x7f) == LUI); + inst[2] = (inst[2] & 0xfff) | (sljit_ins)((sljit_sw)new_target & ~0xfff); + SLJIT_ASSERT((inst[5] & 0x707f) == ADDI || (inst[5] & 0x707f) == JALR); + inst[5] = (inst[5] & 0xfffff) | IMM_I(new_target); + SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1); + + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); + SLJIT_CACHE_FLUSH(inst, inst + 5); +} diff --git a/thirdparty/pcre2/src/sljit/sljitNativeRISCV_common.c b/thirdparty/pcre2/src/sljit/sljitNativeRISCV_common.c new file mode 100644 index 0000000000..58a48c649c --- /dev/null +++ b/thirdparty/pcre2/src/sljit/sljitNativeRISCV_common.c @@ -0,0 +1,2762 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) +{ +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + return "RISC-V-32" SLJIT_CPUINFO; +#else /* !SLJIT_CONFIG_RISCV_32 */ + return "RISC-V-64" SLJIT_CPUINFO; +#endif /* SLJIT_CONFIG_RISCV_32 */ +} + +/* Length of an instruction word + Both for riscv-32 and riscv-64 */ +typedef sljit_u32 sljit_ins; + +#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2) +#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3) +#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4) +#define TMP_ZERO 0 + +/* Flags are kept in volatile registers. */ +#define EQUAL_FLAG (SLJIT_NUMBER_OF_REGISTERS + 5) +#define RETURN_ADDR_REG TMP_REG2 +#define OTHER_FLAG (SLJIT_NUMBER_OF_REGISTERS + 6) + +#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2) + +static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = { + 0, 10, 11, 12, 13, 14, 15, 16, 17, 29, 30, 31, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 9, 8, 2, 6, 1, 7, 5, 28 +}; + +static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { + 0, 10, 11, 12, 13, 14, 15, 16, 17, 2, 3, 4, 5, 6, 7, 28, 29, 30, 31, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 9, 8, 0, 1, +}; + +/* --------------------------------------------------------------------- */ +/* Instrucion forms */ +/* --------------------------------------------------------------------- */ + +#define RD(rd) ((sljit_ins)reg_map[rd] << 7) +#define RS1(rs1) ((sljit_ins)reg_map[rs1] << 15) +#define RS2(rs2) ((sljit_ins)reg_map[rs2] << 20) +#define FRD(rd) ((sljit_ins)freg_map[rd] << 7) +#define FRS1(rs1) ((sljit_ins)freg_map[rs1] << 15) +#define FRS2(rs2) ((sljit_ins)freg_map[rs2] << 20) +#define IMM_I(imm) ((sljit_ins)(imm) << 20) +#define IMM_S(imm) ((((sljit_ins)(imm) & 0xfe0) << 20) | (((sljit_ins)(imm) & 0x1f) << 7)) + +/* Represents funct(i) parts of the instructions. */ +#define OPC(o) ((sljit_ins)(o)) +#define F3(f) ((sljit_ins)(f) << 12) +#define F12(f) ((sljit_ins)(f) << 20) +#define F7(f) ((sljit_ins)(f) << 25) + +#define ADD (F7(0x0) | F3(0x0) | OPC(0x33)) +#define ADDI (F3(0x0) | OPC(0x13)) +#define AND (F7(0x0) | F3(0x7) | OPC(0x33)) +#define ANDI (F3(0x7) | OPC(0x13)) +#define AUIPC (OPC(0x17)) +#define BEQ (F3(0x0) | OPC(0x63)) +#define BNE (F3(0x1) | OPC(0x63)) +#define BLT (F3(0x4) | OPC(0x63)) +#define BGE (F3(0x5) | OPC(0x63)) +#define BLTU (F3(0x6) | OPC(0x63)) +#define BGEU (F3(0x7) | OPC(0x63)) +#define DIV (F7(0x1) | F3(0x4) | OPC(0x33)) +#define DIVU (F7(0x1) | F3(0x5) | OPC(0x33)) +#define EBREAK (F12(0x1) | F3(0x0) | OPC(0x73)) +#define FADD_S (F7(0x0) | F3(0x7) | OPC(0x53)) +#define FDIV_S (F7(0xc) | F3(0x7) | OPC(0x53)) +#define FEQ_S (F7(0x50) | F3(0x2) | OPC(0x53)) +#define FLD (F3(0x3) | OPC(0x7)) +#define FLE_S (F7(0x50) | F3(0x0) | OPC(0x53)) +#define FLT_S (F7(0x50) | F3(0x1) | OPC(0x53)) +#define FSD (F3(0x3) | OPC(0x27)) +/* These conversion opcodes are partly defined. */ +#define FCVT_S_D (F7(0x20) | OPC(0x53)) +#define FCVT_S_W (F7(0x68) | OPC(0x53)) +#define FCVT_W_S (F7(0x60) | F3(0x1) | OPC(0x53)) +#define FMUL_S (F7(0x8) | F3(0x7) | OPC(0x53)) +#define FSGNJ_S (F7(0x10) | F3(0x0) | OPC(0x53)) +#define FSGNJN_S (F7(0x10) | F3(0x1) | OPC(0x53)) +#define FSGNJX_S (F7(0x10) | F3(0x2) | OPC(0x53)) +#define FSUB_S (F7(0x4) | F3(0x7) | OPC(0x53)) +#define JAL (OPC(0x6f)) +#define JALR (F3(0x0) | OPC(0x67)) +#define LD (F3(0x3) | OPC(0x3)) +#define LUI (OPC(0x37)) +#define LW (F3(0x2) | OPC(0x3)) +#define MUL (F7(0x1) | F3(0x0) | OPC(0x33)) +#define MULH (F7(0x1) | F3(0x1) | OPC(0x33)) +#define MULHU (F7(0x1) | F3(0x3) | OPC(0x33)) +#define OR (F7(0x0) | F3(0x6) | OPC(0x33)) +#define ORI (F3(0x6) | OPC(0x13)) +#define REM (F7(0x1) | F3(0x6) | OPC(0x33)) +#define REMU (F7(0x1) | F3(0x7) | OPC(0x33)) +#define SD (F3(0x3) | OPC(0x23)) +#define SLL (F7(0x0) | F3(0x1) | OPC(0x33)) +#define SLLI (IMM_I(0x0) | F3(0x1) | OPC(0x13)) +#define SLT (F7(0x0) | F3(0x2) | OPC(0x33)) +#define SLTI (F3(0x2) | OPC(0x13)) +#define SLTU (F7(0x0) | F3(0x3) | OPC(0x33)) +#define SLTUI (F3(0x3) | OPC(0x13)) +#define SRL (F7(0x0) | F3(0x5) | OPC(0x33)) +#define SRLI (IMM_I(0x0) | F3(0x5) | OPC(0x13)) +#define SRA (F7(0x20) | F3(0x5) | OPC(0x33)) +#define SRAI (IMM_I(0x400) | F3(0x5) | OPC(0x13)) +#define SUB (F7(0x20) | F3(0x0) | OPC(0x33)) +#define SW (F3(0x2) | OPC(0x23)) +#define XOR (F7(0x0) | F3(0x4) | OPC(0x33)) +#define XORI (F3(0x4) | OPC(0x13)) + +#define SIMM_MAX (0x7ff) +#define SIMM_MIN (-0x800) +#define BRANCH_MAX (0xfff) +#define BRANCH_MIN (-0x1000) +#define JUMP_MAX (0xfffff) +#define JUMP_MIN (-0x100000) + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) +#define S32_MAX (0x7ffff7ffl) +#define S32_MIN (-0x80000000l) +#define S44_MAX (0x7fffffff7ffl) +#define S52_MAX (0x7ffffffffffffl) +#endif + +static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins) +{ + sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins)); + FAIL_IF(!ptr); + *ptr = ins; + compiler->size++; + return SLJIT_SUCCESS; +} + +static sljit_s32 push_imm_s_inst(struct sljit_compiler *compiler, sljit_ins ins, sljit_sw imm) +{ + return push_inst(compiler, ins | IMM_S(imm)); +} + +static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code, sljit_sw executable_offset) +{ + sljit_sw diff; + sljit_uw target_addr; + sljit_ins *inst; + + inst = (sljit_ins *)jump->addr; + + if (jump->flags & SLJIT_REWRITABLE_JUMP) + goto exit; + + if (jump->flags & JUMP_ADDR) + target_addr = jump->u.target; + else { + SLJIT_ASSERT(jump->flags & JUMP_LABEL); + target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset; + } + + diff = (sljit_sw)target_addr - (sljit_sw)inst - executable_offset; + + if (jump->flags & IS_COND) { + inst--; + diff += SSIZE_OF(ins); + + if (diff >= BRANCH_MIN && diff <= BRANCH_MAX) { + jump->flags |= PATCH_B; + inst[0] = (inst[0] & 0x1fff07f) ^ 0x1000; + jump->addr = (sljit_uw)inst; + return inst; + } + + inst++; + diff -= SSIZE_OF(ins); + } + + if (diff >= JUMP_MIN && diff <= JUMP_MAX) { + if (jump->flags & IS_COND) { +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + inst[-1] -= (sljit_ins)(1 * sizeof(sljit_ins)) << 7; +#else + inst[-1] -= (sljit_ins)(5 * sizeof(sljit_ins)) << 7; +#endif + } + + jump->flags |= PATCH_J; + return inst; + } + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + if (diff >= S32_MIN && diff <= S32_MAX) { + if (jump->flags & IS_COND) + inst[-1] -= (sljit_ins)(4 * sizeof(sljit_ins)) << 7; + + jump->flags |= PATCH_REL32; + inst[1] = inst[0]; + return inst + 1; + } + + if (target_addr <= (sljit_uw)S32_MAX) { + if (jump->flags & IS_COND) + inst[-1] -= (sljit_ins)(4 * sizeof(sljit_ins)) << 7; + + jump->flags |= PATCH_ABS32; + inst[1] = inst[0]; + return inst + 1; + } + + if (target_addr <= S44_MAX) { + if (jump->flags & IS_COND) + inst[-1] -= (sljit_ins)(2 * sizeof(sljit_ins)) << 7; + + jump->flags |= PATCH_ABS44; + inst[3] = inst[0]; + return inst + 3; + } + + if (target_addr <= S52_MAX) { + if (jump->flags & IS_COND) + inst[-1] -= (sljit_ins)(1 * sizeof(sljit_ins)) << 7; + + jump->flags |= PATCH_ABS52; + inst[4] = inst[0]; + return inst + 4; + } +#endif + +exit: +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + inst[1] = inst[0]; + return inst + 1; +#else + inst[5] = inst[0]; + return inst + 5; +#endif +} + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + +static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label) +{ + if (max_label <= (sljit_uw)S32_MAX) { + put_label->flags = PATCH_ABS32; + return 1; + } + + if (max_label <= S44_MAX) { + put_label->flags = PATCH_ABS44; + return 3; + } + + if (max_label <= S52_MAX) { + put_label->flags = PATCH_ABS52; + return 4; + } + + put_label->flags = 0; + return 5; +} + +#endif /* SLJIT_CONFIG_RISCV_64 */ + +static SLJIT_INLINE void load_addr_to_reg(void *dst, sljit_u32 reg) +{ + struct sljit_jump *jump = NULL; + struct sljit_put_label *put_label; + sljit_uw flags; + sljit_ins *inst; +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + sljit_sw high; +#endif + sljit_uw addr; + + if (reg != 0) { + jump = (struct sljit_jump*)dst; + flags = jump->flags; + inst = (sljit_ins*)jump->addr; + addr = (flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; + } else { + put_label = (struct sljit_put_label*)dst; +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + flags = put_label->flags; +#endif + inst = (sljit_ins*)put_label->addr; + addr = put_label->label->addr; + reg = *inst; + } + + if ((addr & 0x800) != 0) + addr += 0x1000; + +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + inst[0] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff); +#else /* !SLJIT_CONFIG_RISCV_32 */ + + if (flags & PATCH_ABS32) { + SLJIT_ASSERT(addr <= S32_MAX); + inst[0] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff); + } else if (flags & PATCH_ABS44) { + high = (sljit_sw)addr >> 12; + SLJIT_ASSERT((sljit_uw)high <= 0x7fffffff); + + if (high > S32_MAX) { + SLJIT_ASSERT((high & 0x800) != 0); + inst[0] = LUI | RD(reg) | (sljit_ins)0x80000000u; + inst[1] = XORI | RD(reg) | RS1(reg) | IMM_I(high); + } else { + if ((high & 0x800) != 0) + high += 0x1000; + + inst[0] = LUI | RD(reg) | (sljit_ins)(high & ~0xfff); + inst[1] = ADDI | RD(reg) | RS1(reg) | IMM_I(high); + } + + inst[2] = SLLI | RD(reg) | RS1(reg) | IMM_I(12); + inst += 2; + } else { + high = (sljit_sw)addr >> 32; + + if ((addr & 0x80000000l) != 0) + high = ~high; + + if ((high & 0x800) != 0) + high += 0x1000; + + if (flags & PATCH_ABS52) { + SLJIT_ASSERT(addr <= S52_MAX); + inst[0] = LUI | RD(TMP_REG3) | (sljit_ins)(high << 12); + } else { + inst[0] = LUI | RD(TMP_REG3) | (sljit_ins)(high & ~0xfff); + inst[1] = ADDI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(high); + inst++; + } + + inst[1] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff); + inst[2] = SLLI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I((flags & PATCH_ABS52) ? 20 : 32); + inst[3] = XOR | RD(reg) | RS1(reg) | RS2(TMP_REG3); + inst += 3; + } +#endif /* !SLJIT_CONFIG_RISCV_32 */ + + if (jump != NULL) { + SLJIT_ASSERT((inst[1] & 0x707f) == JALR); + inst[1] = (inst[1] & 0xfffff) | IMM_I(addr); + } else + inst[1] = ADDI | RD(reg) | RS1(reg) | IMM_I(addr); +} + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) +{ + struct sljit_memory_fragment *buf; + sljit_ins *code; + sljit_ins *code_ptr; + sljit_ins *buf_ptr; + sljit_ins *buf_end; + sljit_uw word_count; + sljit_uw next_addr; + sljit_sw executable_offset; + sljit_uw addr; + + struct sljit_label *label; + struct sljit_jump *jump; + struct sljit_const *const_; + struct sljit_put_label *put_label; + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_generate_code(compiler)); + reverse_buf(compiler); + + code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins), compiler->exec_allocator_data); + PTR_FAIL_WITH_EXEC_IF(code); + buf = compiler->buf; + + code_ptr = code; + word_count = 0; + next_addr = 0; + executable_offset = SLJIT_EXEC_OFFSET(code); + + label = compiler->labels; + jump = compiler->jumps; + const_ = compiler->consts; + put_label = compiler->put_labels; + + do { + buf_ptr = (sljit_ins*)buf->memory; + buf_end = buf_ptr + (buf->used_size >> 2); + do { + *code_ptr = *buf_ptr++; + if (next_addr == word_count) { + SLJIT_ASSERT(!label || label->size >= word_count); + SLJIT_ASSERT(!jump || jump->addr >= word_count); + SLJIT_ASSERT(!const_ || const_->addr >= word_count); + SLJIT_ASSERT(!put_label || put_label->addr >= word_count); + + /* These structures are ordered by their address. */ + if (label && label->size == word_count) { + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); + label->size = (sljit_uw)(code_ptr - code); + label = label->next; + } + if (jump && jump->addr == word_count) { +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + word_count += 1; +#else + word_count += 5; +#endif + jump->addr = (sljit_uw)code_ptr; + code_ptr = detect_jump_type(jump, code, executable_offset); + jump = jump->next; + } + if (const_ && const_->addr == word_count) { + const_->addr = (sljit_uw)code_ptr; + const_ = const_->next; + } + if (put_label && put_label->addr == word_count) { + SLJIT_ASSERT(put_label->label); + put_label->addr = (sljit_uw)code_ptr; +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + code_ptr += 1; + word_count += 1; +#else + code_ptr += put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size)); + word_count += 5; +#endif + put_label = put_label->next; + } + next_addr = compute_next_addr(label, jump, const_, put_label); + } + code_ptr++; + word_count++; + } while (buf_ptr < buf_end); + + buf = buf->next; + } while (buf); + + if (label && label->size == word_count) { + label->addr = (sljit_uw)code_ptr; + label->size = (sljit_uw)(code_ptr - code); + label = label->next; + } + + SLJIT_ASSERT(!label); + SLJIT_ASSERT(!jump); + SLJIT_ASSERT(!const_); + SLJIT_ASSERT(!put_label); + SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size); + + jump = compiler->jumps; + while (jump) { + do { + if (!(jump->flags & (PATCH_B | PATCH_J | PATCH_REL32))) { + load_addr_to_reg(jump, TMP_REG1); + break; + } + + addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; + buf_ptr = (sljit_ins *)jump->addr; + addr -= (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset); + + if (jump->flags & PATCH_B) { + SLJIT_ASSERT((sljit_sw)addr >= BRANCH_MIN && (sljit_sw)addr <= BRANCH_MAX); + addr = ((addr & 0x800) >> 4) | ((addr & 0x1e) << 7) | ((addr & 0x7e0) << 20) | ((addr & 0x1000) << 19); + buf_ptr[0] |= (sljit_ins)addr; + break; + } + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + if (jump->flags & PATCH_REL32) { + SLJIT_ASSERT((sljit_sw)addr >= S32_MIN && (sljit_sw)addr <= S32_MAX); + + if ((addr & 0x800) != 0) + addr += 0x1000; + + buf_ptr[0] = AUIPC | RD(TMP_REG1) | (sljit_ins)((sljit_sw)addr & ~0xfff); + SLJIT_ASSERT((buf_ptr[1] & 0x707f) == JALR); + buf_ptr[1] |= IMM_I(addr); + break; + } +#endif + + SLJIT_ASSERT((sljit_sw)addr >= JUMP_MIN && (sljit_sw)addr <= JUMP_MAX); + addr = (addr & 0xff000) | ((addr & 0x800) << 9) | ((addr & 0x7fe) << 20) | ((addr & 0x100000) << 11); + buf_ptr[0] = JAL | RD((jump->flags & IS_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | (sljit_ins)addr; + } while (0); + jump = jump->next; + } + + put_label = compiler->put_labels; + while (put_label) { + load_addr_to_reg(put_label, 0); + put_label = put_label->next; + } + + compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_offset = executable_offset; + compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins); + + code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset); + code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); + + SLJIT_CACHE_FLUSH(code, code_ptr); + SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1); + return code; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) +{ + switch (feature_type) { + case SLJIT_HAS_FPU: + case SLJIT_HAS_ZERO_REGISTER: + return 1; + default: + return 0; + } +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type) +{ + return (type >= SLJIT_ORDERED_EQUAL && type <= SLJIT_ORDERED_LESS_EQUAL); +} + +/* --------------------------------------------------------------------- */ +/* Entry, exit */ +/* --------------------------------------------------------------------- */ + +/* Creates an index in data_transfer_insts array. */ +#define LOAD_DATA 0x01 +#define WORD_DATA 0x00 +#define BYTE_DATA 0x02 +#define HALF_DATA 0x04 +#define INT_DATA 0x06 +#define SIGNED_DATA 0x08 +/* Separates integer and floating point registers */ +#define GPR_REG 0x0f +#define DOUBLE_DATA 0x10 +#define SINGLE_DATA 0x12 + +#define MEM_MASK 0x1f + +#define ARG_TEST 0x00020 +#define ALT_KEEP_CACHE 0x00040 +#define CUMULATIVE_OP 0x00080 +#define IMM_OP 0x00100 +#define MOVE_OP 0x00200 +#define SRC2_IMM 0x00400 + +#define UNUSED_DEST 0x00800 +#define REG_DEST 0x01000 +#define REG1_SOURCE 0x02000 +#define REG2_SOURCE 0x04000 +#define SLOW_SRC1 0x08000 +#define SLOW_SRC2 0x10000 +#define SLOW_DEST 0x20000 + +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) +#define STACK_STORE SW +#define STACK_LOAD LW +#else +#define STACK_STORE SD +#define STACK_LOAD LD +#endif + +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) +#include "sljitNativeRISCV_32.c" +#else +#include "sljitNativeRISCV_64.c" +#endif + +#define STACK_MAX_DISTANCE (-SIMM_MIN) + +static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw); + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) +{ + sljit_s32 i, tmp, offset; + sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options); + + CHECK_ERROR(); + CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); + + local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1); +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) { + if ((local_size & SSIZE_OF(sw)) != 0) + local_size += SSIZE_OF(sw); + local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64)); + } +#else + local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64)); +#endif + local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf; + compiler->local_size = local_size; + + if (local_size <= STACK_MAX_DISTANCE) { + /* Frequent case. */ + FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-local_size))); + offset = local_size - SSIZE_OF(sw); + local_size = 0; + } else { + FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(STACK_MAX_DISTANCE))); + local_size -= STACK_MAX_DISTANCE; + + if (local_size > STACK_MAX_DISTANCE) + FAIL_IF(load_immediate(compiler, TMP_REG1, local_size, TMP_REG3)); + offset = STACK_MAX_DISTANCE - SSIZE_OF(sw); + } + + FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(RETURN_ADDR_REG), offset)); + + tmp = SLJIT_S0 - saveds; + for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) { + offset -= SSIZE_OF(sw); + FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(i), offset)); + } + + for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) { + offset -= SSIZE_OF(sw); + FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(i), offset)); + } + +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + /* This alignment is valid because offset is not used after storing FPU regs. */ + if ((offset & SSIZE_OF(sw)) != 0) + offset -= SSIZE_OF(sw); +#endif + + tmp = SLJIT_FS0 - fsaveds; + for (i = SLJIT_FS0; i > tmp; i--) { + offset -= SSIZE_OF(f64); + FAIL_IF(push_imm_s_inst(compiler, FSD | RS1(SLJIT_SP) | FRS2(i), offset)); + } + + for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) { + offset -= SSIZE_OF(f64); + FAIL_IF(push_imm_s_inst(compiler, FSD | RS1(SLJIT_SP) | FRS2(i), offset)); + } + + if (local_size > STACK_MAX_DISTANCE) + FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RS1(SLJIT_SP) | RS2(TMP_REG1))); + else if (local_size > 0) + FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-local_size))); + + if (options & SLJIT_ENTER_REG_ARG) + return SLJIT_SUCCESS; + + arg_types >>= SLJIT_ARG_SHIFT; + saved_arg_count = 0; + tmp = SLJIT_R0; + + while (arg_types > 0) { + if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) { + if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) { + FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_S0 - saved_arg_count) | RS1(tmp) | IMM_I(0))); + saved_arg_count++; + } + tmp++; + } + + arg_types >>= SLJIT_ARG_SHIFT; + } + + return SLJIT_SUCCESS; +} + +#undef STACK_MAX_DISTANCE + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) +{ + CHECK_ERROR(); + CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); + + local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1); +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) { + if ((local_size & SSIZE_OF(sw)) != 0) + local_size += SSIZE_OF(sw); + local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64)); + } +#else + local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64)); +#endif + compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf; + + return SLJIT_SUCCESS; +} + +#define STACK_MAX_DISTANCE (-SIMM_MIN - 16) + +static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to) +{ + sljit_s32 i, tmp, offset; + sljit_s32 local_size = compiler->local_size; + + if (local_size > STACK_MAX_DISTANCE) { + local_size -= STACK_MAX_DISTANCE; + + if (local_size > STACK_MAX_DISTANCE) { + FAIL_IF(load_immediate(compiler, TMP_REG2, local_size, TMP_REG3)); + FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RS1(SLJIT_SP) | RS2(TMP_REG2))); + } else + FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(local_size))); + + local_size = STACK_MAX_DISTANCE; + } + + SLJIT_ASSERT(local_size > 0); + + offset = local_size - SSIZE_OF(sw); + if (!is_return_to) + FAIL_IF(push_inst(compiler, STACK_LOAD | RD(RETURN_ADDR_REG) | RS1(SLJIT_SP) | IMM_I(offset))); + + tmp = SLJIT_S0 - compiler->saveds; + for (i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); i > tmp; i--) { + offset -= SSIZE_OF(sw); + FAIL_IF(push_inst(compiler, STACK_LOAD | RD(i) | RS1(SLJIT_SP) | IMM_I(offset))); + } + + for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) { + offset -= SSIZE_OF(sw); + FAIL_IF(push_inst(compiler, STACK_LOAD | RD(i) | RS1(SLJIT_SP) | IMM_I(offset))); + } + +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + /* This alignment is valid because offset is not used after storing FPU regs. */ + if ((offset & SSIZE_OF(sw)) != 0) + offset -= SSIZE_OF(sw); +#endif + + tmp = SLJIT_FS0 - compiler->fsaveds; + for (i = SLJIT_FS0; i > tmp; i--) { + offset -= SSIZE_OF(f64); + FAIL_IF(push_inst(compiler, FLD | FRD(i) | RS1(SLJIT_SP) | IMM_I(offset))); + } + + for (i = compiler->fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) { + offset -= SSIZE_OF(f64); + FAIL_IF(push_inst(compiler, FLD | FRD(i) | RS1(SLJIT_SP) | IMM_I(offset))); + } + + return push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(local_size)); +} + +#undef STACK_MAX_DISTANCE + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_return_void(compiler)); + + FAIL_IF(emit_stack_frame_release(compiler, 0)); + return push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(RETURN_ADDR_REG) | IMM_I(0)); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_return_to(compiler, src, srcw)); + + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw)); + src = TMP_REG1; + srcw = 0; + } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(src) | IMM_I(0))); + src = TMP_REG1; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, 1)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); +} + +/* --------------------------------------------------------------------- */ +/* Operators */ +/* --------------------------------------------------------------------- */ + +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) +#define ARCH_32_64(a, b) a +#else +#define ARCH_32_64(a, b) b +#endif + +static const sljit_ins data_transfer_insts[16 + 4] = { +/* u w s */ ARCH_32_64(F3(0x2) | OPC(0x23) /* sw */, F3(0x3) | OPC(0x23) /* sd */), +/* u w l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x3) | OPC(0x3) /* ld */), +/* u b s */ F3(0x0) | OPC(0x23) /* sb */, +/* u b l */ F3(0x4) | OPC(0x3) /* lbu */, +/* u h s */ F3(0x1) | OPC(0x23) /* sh */, +/* u h l */ F3(0x5) | OPC(0x3) /* lhu */, +/* u i s */ F3(0x2) | OPC(0x23) /* sw */, +/* u i l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x6) | OPC(0x3) /* lwu */), + +/* s w s */ ARCH_32_64(F3(0x2) | OPC(0x23) /* sw */, F3(0x3) | OPC(0x23) /* sd */), +/* s w l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x3) | OPC(0x3) /* ld */), +/* s b s */ F3(0x0) | OPC(0x23) /* sb */, +/* s b l */ F3(0x0) | OPC(0x3) /* lb */, +/* s h s */ F3(0x1) | OPC(0x23) /* sh */, +/* s h l */ F3(0x1) | OPC(0x3) /* lh */, +/* s i s */ F3(0x2) | OPC(0x23) /* sw */, +/* s i l */ F3(0x2) | OPC(0x3) /* lw */, + +/* d s */ F3(0x3) | OPC(0x27) /* fsd */, +/* d l */ F3(0x3) | OPC(0x7) /* fld */, +/* s s */ F3(0x2) | OPC(0x27) /* fsw */, +/* s l */ F3(0x2) | OPC(0x7) /* flw */, +}; + +#undef ARCH_32_64 + +static sljit_s32 push_mem_inst(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 base, sljit_sw offset) +{ + sljit_ins ins; + + SLJIT_ASSERT(FAST_IS_REG(base) && offset <= 0xfff && offset >= SIMM_MIN); + + ins = data_transfer_insts[flags & MEM_MASK] | RS1(base); + if (flags & LOAD_DATA) + ins |= ((flags & MEM_MASK) <= GPR_REG ? RD(reg) : FRD(reg)) | IMM_I(offset); + else + ins |= ((flags & MEM_MASK) <= GPR_REG ? RS2(reg) : FRS2(reg)) | IMM_S(offset); + + return push_inst(compiler, ins); +} + +/* Can perform an operation using at most 1 instruction. */ +static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) +{ + + SLJIT_ASSERT(arg & SLJIT_MEM); + + if (!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) { + /* Works for both absoulte and relative addresses. */ + if (SLJIT_UNLIKELY(flags & ARG_TEST)) + return 1; + + FAIL_IF(push_mem_inst(compiler, flags, reg, arg & REG_MASK, argw)); + return -1; + } + return 0; +} + +#define TO_ARGW_HI(argw) (((argw) & ~0xfff) + (((argw) & 0x800) ? 0x1000 : 0)) + +/* See getput_arg below. + Note: can_cache is called only for binary operators. */ +static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) +{ + SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM)); + + /* Simple operation except for updates. */ + if (arg & OFFS_REG_MASK) { + argw &= 0x3; + next_argw &= 0x3; + if (argw && argw == next_argw && (arg == next_arg || (arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK))) + return 1; + return 0; + } + + if (arg == next_arg) { + if (((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN) + || TO_ARGW_HI(argw) == TO_ARGW_HI(next_argw)) + return 1; + return 0; + } + + return 0; +} + +/* Emit the necessary instructions. See can_cache above. */ +static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) +{ + sljit_s32 base = arg & REG_MASK; + sljit_s32 tmp_r = TMP_REG1; + sljit_sw offset, argw_hi; + + SLJIT_ASSERT(arg & SLJIT_MEM); + if (!(next_arg & SLJIT_MEM)) { + next_arg = 0; + next_argw = 0; + } + + /* Since tmp can be the same as base or offset registers, + * these might be unavailable after modifying tmp. */ + if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) + tmp_r = reg; + + if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { + argw &= 0x3; + + /* Using the cache. */ + if (argw == compiler->cache_argw) { + if (arg == compiler->cache_arg) + return push_mem_inst(compiler, flags, reg, TMP_REG3, 0); + + if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) { + if (arg == next_arg && argw == (next_argw & 0x3)) { + compiler->cache_arg = arg; + compiler->cache_argw = argw; + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(TMP_REG3) | RS2(base))); + return push_mem_inst(compiler, flags, reg, TMP_REG3, 0); + } + FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(TMP_REG3))); + return push_mem_inst(compiler, flags, reg, tmp_r, 0); + } + } + + if (SLJIT_UNLIKELY(argw)) { + compiler->cache_arg = SLJIT_MEM | (arg & OFFS_REG_MASK); + compiler->cache_argw = argw; + FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG3) | RS1(OFFS_REG(arg)) | IMM_I(argw))); + } + + if (arg == next_arg && argw == (next_argw & 0x3)) { + compiler->cache_arg = arg; + compiler->cache_argw = argw; + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(base) | RS2(!argw ? OFFS_REG(arg) : TMP_REG3))); + tmp_r = TMP_REG3; + } + else + FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(!argw ? OFFS_REG(arg) : TMP_REG3))); + return push_mem_inst(compiler, flags, reg, tmp_r, 0); + } + + if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) + return push_mem_inst(compiler, flags, reg, TMP_REG3, argw - compiler->cache_argw); + + if (compiler->cache_arg == SLJIT_MEM && (argw - compiler->cache_argw <= SIMM_MAX) && (argw - compiler->cache_argw >= SIMM_MIN)) { + offset = argw - compiler->cache_argw; + } else { + compiler->cache_arg = SLJIT_MEM; + + argw_hi = TO_ARGW_HI(argw); + + if (next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN && argw_hi != TO_ARGW_HI(next_argw)) { + FAIL_IF(load_immediate(compiler, TMP_REG3, argw, tmp_r)); + compiler->cache_argw = argw; + offset = 0; + } else { + FAIL_IF(load_immediate(compiler, TMP_REG3, argw_hi, tmp_r)); + compiler->cache_argw = argw_hi; + offset = argw & 0xfff; + argw = argw_hi; + } + } + + if (!base) + return push_mem_inst(compiler, flags, reg, TMP_REG3, offset); + + if (arg == next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN) { + compiler->cache_arg = arg; + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(TMP_REG3) | RS2(base))); + return push_mem_inst(compiler, flags, reg, TMP_REG3, offset); + } + + FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(TMP_REG3) | RS2(base))); + return push_mem_inst(compiler, flags, reg, tmp_r, offset); +} + +static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) +{ + sljit_s32 base = arg & REG_MASK; + sljit_s32 tmp_r = TMP_REG1; + + if (getput_arg_fast(compiler, flags, reg, arg, argw)) + return compiler->error; + + if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) + tmp_r = reg; + + if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { + argw &= 0x3; + + if (SLJIT_UNLIKELY(argw)) { + FAIL_IF(push_inst(compiler, SLLI | RD(tmp_r) | RS1(OFFS_REG(arg)) | IMM_I(argw))); + FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(tmp_r) | RS2(base))); + } + else + FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(OFFS_REG(arg)))); + + argw = 0; + } else { + FAIL_IF(load_immediate(compiler, tmp_r, TO_ARGW_HI(argw), TMP_REG3)); + + if (base != 0) + FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(tmp_r) | RS2(base))); + } + + return push_mem_inst(compiler, flags, reg, tmp_r, argw & 0xfff); +} + +static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w) +{ + if (getput_arg_fast(compiler, flags, reg, arg1, arg1w)) + return compiler->error; + return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w); +} + +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) +#define WORD 0 +#define IMM_EXTEND(v) (IMM_I(v)) +#else /* !SLJIT_CONFIG_RISCV_32 */ +#define WORD word +#define IMM_EXTEND(v) (IMM_I((op & SLJIT_32) ? (v) : (32 + (v)))) +#endif /* SLJIT_CONFIG_RISCV_32 */ + +static sljit_s32 emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw src) +{ + sljit_s32 is_clz = (GET_OPCODE(op) == SLJIT_CLZ); +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + sljit_ins word = (op & SLJIT_32) >> 5; + sljit_ins max = (op & SLJIT_32) ? 32 : 64; +#else /* !SLJIT_CONFIG_RISCV_64 */ + sljit_ins max = 32; +#endif /* SLJIT_CONFIG_RISCV_64 */ + + SLJIT_ASSERT(WORD == 0 || WORD == 0x8); + + /* The OTHER_FLAG is the counter. */ + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(OTHER_FLAG) | RS1(TMP_ZERO) | IMM_I(max))); + + /* The TMP_REG2 is the next value. */ + if (src != TMP_REG2) + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG2) | RS1(src) | IMM_I(0))); + + FAIL_IF(push_inst(compiler, BEQ | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)((is_clz ? 4 : 5) * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20))); + + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(OTHER_FLAG) | RS1(TMP_ZERO) | IMM_I(0))); + if (!is_clz) { + FAIL_IF(push_inst(compiler, ANDI | RD(TMP_REG1) | RS1(TMP_REG2) | IMM_I(1))); + FAIL_IF(push_inst(compiler, BNE | RS1(TMP_REG1) | RS2(TMP_ZERO) | ((sljit_ins)(2 * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20))); + } else + FAIL_IF(push_inst(compiler, BLT | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)(2 * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20))); + + /* The TMP_REG1 is the next shift. */ + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG1) | RS1(TMP_ZERO) | IMM_I(max))); + + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(TMP_REG2) | IMM_I(0))); + FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_I(1))); + + FAIL_IF(push_inst(compiler, (is_clz ? SRL : SLL) | WORD | RD(TMP_REG2) | RS1(EQUAL_FLAG) | RS2(TMP_REG1))); + FAIL_IF(push_inst(compiler, BNE | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)0xfe000e80 - ((2 * SSIZE_OF(ins)) << 7)))); + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG2) | RS1(TMP_REG1) | IMM_I(-1))); + FAIL_IF(push_inst(compiler, (is_clz ? SRL : SLL) | WORD | RD(TMP_REG2) | RS1(EQUAL_FLAG) | RS2(TMP_REG2))); + FAIL_IF(push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(TMP_REG1))); + FAIL_IF(push_inst(compiler, BEQ | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)0xfe000e80 - ((5 * SSIZE_OF(ins)) << 7)))); + + return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(OTHER_FLAG) | IMM_I(0)); +} + +#define EMIT_LOGICAL(op_imm, op_reg) \ + if (flags & SRC2_IMM) { \ + if (op & SLJIT_SET_Z) \ + FAIL_IF(push_inst(compiler, op_imm | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2))); \ + if (!(flags & UNUSED_DEST)) \ + FAIL_IF(push_inst(compiler, op_imm | RD(dst) | RS1(src1) | IMM_I(src2))); \ + } \ + else { \ + if (op & SLJIT_SET_Z) \ + FAIL_IF(push_inst(compiler, op_reg | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); \ + if (!(flags & UNUSED_DEST)) \ + FAIL_IF(push_inst(compiler, op_reg | RD(dst) | RS1(src1) | RS2(src2))); \ + } + +#define EMIT_SHIFT(imm, reg) \ + op_imm = (imm); \ + op_reg = (reg); + +static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, + sljit_s32 dst, sljit_s32 src1, sljit_sw src2) +{ + sljit_s32 is_overflow, is_carry, carry_src_r, is_handled; + sljit_ins op_imm, op_reg; +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + sljit_ins word = (op & SLJIT_32) >> 5; +#endif /* SLJIT_CONFIG_RISCV_64 */ + + SLJIT_ASSERT(WORD == 0 || WORD == 0x8); + + switch (GET_OPCODE(op)) { + case SLJIT_MOV: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if (dst != src2) + return push_inst(compiler, ADDI | RD(dst) | RS1(src2) | IMM_I(0)); + return SLJIT_SUCCESS; + + case SLJIT_MOV_U8: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) + return push_inst(compiler, ANDI | RD(dst) | RS1(src2) | IMM_I(0xff)); + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; + + case SLJIT_MOV_S8: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(24))); + return push_inst(compiler, SRAI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(24)); + } + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; + + case SLJIT_MOV_U16: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(16))); + return push_inst(compiler, SRLI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(16)); + } + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; + + case SLJIT_MOV_S16: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(16))); + return push_inst(compiler, SRAI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(16)); + } + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + case SLJIT_MOV_U32: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + FAIL_IF(push_inst(compiler, SLLI | RD(dst) | RS1(src2) | IMM_I(32))); + return push_inst(compiler, SRLI | RD(dst) | RS1(dst) | IMM_I(32)); + } + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; + + case SLJIT_MOV_S32: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) + return push_inst(compiler, ADDI | 0x8 | RD(dst) | RS1(src2) | IMM_I(0)); + SLJIT_ASSERT(dst == src2); + return SLJIT_SUCCESS; +#endif /* SLJIT_CONFIG_RISCV_64 */ + + case SLJIT_CLZ: + case SLJIT_CTZ: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + return emit_clz_ctz(compiler, op, dst, src2); + + case SLJIT_ADD: + /* Overflow computation (both add and sub): overflow = src1_sign ^ src2_sign ^ result_sign ^ carry_flag */ + is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; + carry_src_r = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_overflow) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0))); + else + FAIL_IF(push_inst(compiler, XORI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-1))); + } + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2))); + + /* Only the zero flag is needed. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(src2))); + } + else { + if (is_overflow) + FAIL_IF(push_inst(compiler, XOR | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADD | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); + + if (is_overflow || carry_src_r != 0) { + if (src1 != dst) + carry_src_r = (sljit_s32)src1; + else if (src2 != dst) + carry_src_r = (sljit_s32)src2; + else { + FAIL_IF(push_inst(compiler, ADDI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(0))); + carry_src_r = OTHER_FLAG; + } + } + + /* Only the zero flag is needed. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(src1) | RS2(src2))); + } + + /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */ + if (is_overflow || carry_src_r != 0) { + if (flags & SRC2_IMM) + FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(src2))); + else + FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(dst) | RS2(carry_src_r))); + } + + if (!is_overflow) + return SLJIT_SUCCESS; + + FAIL_IF(push_inst(compiler, XOR | RD(TMP_REG1) | RS1(dst) | RS2(EQUAL_FLAG))); + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(0))); + FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_EXTEND(31))); + return push_inst(compiler, XOR | RD(OTHER_FLAG) | RS1(TMP_REG1) | RS2(OTHER_FLAG)); + + case SLJIT_ADDC: + carry_src_r = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(src2))); + } else { + if (carry_src_r != 0) { + if (src1 != dst) + carry_src_r = (sljit_s32)src1; + else if (src2 != dst) + carry_src_r = (sljit_s32)src2; + else { + FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0))); + carry_src_r = EQUAL_FLAG; + } + } + + FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(src1) | RS2(src2))); + } + + /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */ + if (carry_src_r != 0) { + if (flags & SRC2_IMM) + FAIL_IF(push_inst(compiler, SLTUI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(src2))); + else + FAIL_IF(push_inst(compiler, SLTU | RD(EQUAL_FLAG) | RS1(dst) | RS2(carry_src_r))); + } + + FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(dst) | RS2(OTHER_FLAG))); + + if (carry_src_r == 0) + return SLJIT_SUCCESS; + + /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */ + FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(dst) | RS2(OTHER_FLAG))); + /* Set carry flag. */ + return push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(EQUAL_FLAG)); + + case SLJIT_SUB: + if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + is_handled = 0; + + if (flags & SRC2_IMM) { + if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2))); + is_handled = 1; + } + else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLTI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2))); + is_handled = 1; + } + } + + if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) { + is_handled = 1; + + if (flags & SRC2_IMM) { + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + switch (GET_FLAG_TYPE(op)) { + case SLJIT_LESS: + case SLJIT_GREATER_EQUAL: + FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src1) | RS2(src2))); + break; + case SLJIT_GREATER: + case SLJIT_LESS_EQUAL: + FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src2) | RS2(src1))); + break; + case SLJIT_SIG_LESS: + case SLJIT_SIG_GREATER_EQUAL: + FAIL_IF(push_inst(compiler, SLT | RD(OTHER_FLAG) | RS1(src1) | RS2(src2))); + break; + case SLJIT_SIG_GREATER: + case SLJIT_SIG_LESS_EQUAL: + FAIL_IF(push_inst(compiler, SLT | RD(OTHER_FLAG) | RS1(src2) | RS2(src1))); + break; + } + } + + if (is_handled) { + if (flags & SRC2_IMM) { + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-src2))); + if (!(flags & UNUSED_DEST)) + return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2)); + } + else { + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); + if (!(flags & UNUSED_DEST)) + return push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2)); + } + return SLJIT_SUCCESS; + } + + is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_overflow) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0))); + else + FAIL_IF(push_inst(compiler, XORI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-1))); + } + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-src2))); + + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2))); + + /* Only the zero flag is needed. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2))); + } + else { + if (is_overflow) + FAIL_IF(push_inst(compiler, XOR | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); + + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src1) | RS2(src2))); + + /* Only the zero flag is needed. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2))); + } + + if (!is_overflow) + return SLJIT_SUCCESS; + + FAIL_IF(push_inst(compiler, XOR | RD(TMP_REG1) | RS1(dst) | RS2(EQUAL_FLAG))); + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(0))); + FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_EXTEND(31))); + return push_inst(compiler, XOR | RD(OTHER_FLAG) | RS1(TMP_REG1) | RS2(OTHER_FLAG)); + + case SLJIT_SUBC: + if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_carry) + FAIL_IF(push_inst(compiler, SLTUI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2))); + + FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2))); + } + else { + if (is_carry) + FAIL_IF(push_inst(compiler, SLTU | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); + + FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2))); + } + + if (is_carry) + FAIL_IF(push_inst(compiler, SLTU | RD(TMP_REG1) | RS1(dst) | RS2(OTHER_FLAG))); + + FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(dst) | RS2(OTHER_FLAG))); + + if (!is_carry) + return SLJIT_SUCCESS; + + return push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(EQUAL_FLAG) | RS2(TMP_REG1)); + + case SLJIT_MUL: + SLJIT_ASSERT(!(flags & SRC2_IMM)); + + if (GET_FLAG_TYPE(op) != SLJIT_OVERFLOW) + return push_inst(compiler, MUL | WORD | RD(dst) | RS1(src1) | RS2(src2)); + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + if (word) { + FAIL_IF(push_inst(compiler, MUL | RD(OTHER_FLAG) | RS1(src1) | RS2(src2))); + FAIL_IF(push_inst(compiler, MUL | 0x8 | RD(dst) | RS1(src1) | RS2(src2))); + return push_inst(compiler, SUB | RD(OTHER_FLAG) | RS1(dst) | RS2(OTHER_FLAG)); + } +#endif /* SLJIT_CONFIG_RISCV_64 */ + + FAIL_IF(push_inst(compiler, MULH | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); + FAIL_IF(push_inst(compiler, MUL | RD(dst) | RS1(src1) | RS2(src2))); +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + FAIL_IF(push_inst(compiler, SRAI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(31))); +#else /* !SLJIT_CONFIG_RISCV_32 */ + FAIL_IF(push_inst(compiler, SRAI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(63))); +#endif /* SLJIT_CONFIG_RISCV_32 */ + return push_inst(compiler, SUB | RD(OTHER_FLAG) | RS1(EQUAL_FLAG) | RS2(OTHER_FLAG)); + + case SLJIT_AND: + EMIT_LOGICAL(ANDI, AND); + return SLJIT_SUCCESS; + + case SLJIT_OR: + EMIT_LOGICAL(ORI, OR); + return SLJIT_SUCCESS; + + case SLJIT_XOR: + EMIT_LOGICAL(XORI, XOR); + return SLJIT_SUCCESS; + + case SLJIT_SHL: + case SLJIT_MSHL: + EMIT_SHIFT(SLLI, SLL); + break; + + case SLJIT_LSHR: + case SLJIT_MLSHR: + EMIT_SHIFT(SRLI, SRL); + break; + + case SLJIT_ASHR: + case SLJIT_MASHR: + EMIT_SHIFT(SRAI, SRA); + break; + + case SLJIT_ROTL: + case SLJIT_ROTR: + if (flags & SRC2_IMM) { + SLJIT_ASSERT(src2 != 0); + + op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SLLI : SRLI; + FAIL_IF(push_inst(compiler, op_imm | WORD | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2))); + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + src2 = ((op & SLJIT_32) ? 32 : 64) - src2; +#else /* !SLJIT_CONFIG_RISCV_64 */ + src2 = 32 - src2; +#endif /* SLJIT_CONFIG_RISCV_64 */ + op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SRLI : SLLI; + FAIL_IF(push_inst(compiler, op_imm | WORD | RD(dst) | RS1(src1) | IMM_I(src2))); + return push_inst(compiler, OR | RD(dst) | RS1(dst) | RS2(OTHER_FLAG)); + } + + if (src2 == TMP_ZERO) { + if (dst != src1) + return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(0)); + return SLJIT_SUCCESS; + } + + FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(TMP_ZERO) | RS2(src2))); + op_reg = (GET_OPCODE(op) == SLJIT_ROTL) ? SLL : SRL; + FAIL_IF(push_inst(compiler, op_reg | WORD | RD(OTHER_FLAG) | RS1(src1) | RS2(src2))); + op_reg = (GET_OPCODE(op) == SLJIT_ROTL) ? SRL : SLL; + FAIL_IF(push_inst(compiler, op_reg | WORD | RD(dst) | RS1(src1) | RS2(EQUAL_FLAG))); + return push_inst(compiler, OR | RD(dst) | RS1(dst) | RS2(OTHER_FLAG)); + + default: + SLJIT_UNREACHABLE(); + return SLJIT_SUCCESS; + } + + if (flags & SRC2_IMM) { + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, op_imm | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2))); + + if (flags & UNUSED_DEST) + return SLJIT_SUCCESS; + return push_inst(compiler, op_imm | WORD | RD(dst) | RS1(src1) | IMM_I(src2)); + } + + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, op_reg | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); + + if (flags & UNUSED_DEST) + return SLJIT_SUCCESS; + return push_inst(compiler, op_reg | WORD | RD(dst) | RS1(src1) | RS2(src2)); +} + +#undef IMM_EXTEND + +static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, + sljit_s32 dst, sljit_sw dstw, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + /* arg1 goes to TMP_REG1 or src reg + arg2 goes to TMP_REG2, imm or src reg + TMP_REG3 can be used for caching + result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */ + sljit_s32 dst_r = TMP_REG2; + sljit_s32 src1_r; + sljit_sw src2_r = 0; + sljit_s32 sugg_src2_r = TMP_REG2; + + if (!(flags & ALT_KEEP_CACHE)) { + compiler->cache_arg = 0; + compiler->cache_argw = 0; + } + + if (dst == TMP_REG2) { + SLJIT_ASSERT(HAS_FLAGS(op)); + flags |= UNUSED_DEST; + } + else if (FAST_IS_REG(dst)) { + dst_r = dst; + flags |= REG_DEST; + if (flags & MOVE_OP) + sugg_src2_r = dst_r; + } + else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, TMP_REG1, dst, dstw)) + flags |= SLOW_DEST; + + if (flags & IMM_OP) { + if ((src2 & SLJIT_IMM) && src2w != 0 && src2w <= SIMM_MAX && src2w >= SIMM_MIN) { + flags |= SRC2_IMM; + src2_r = src2w; + } + else if ((flags & CUMULATIVE_OP) && (src1 & SLJIT_IMM) && src1w != 0 && src1w <= SIMM_MAX && src1w >= SIMM_MIN) { + flags |= SRC2_IMM; + src2_r = src1w; + + /* And swap arguments. */ + src1 = src2; + src1w = src2w; + src2 = SLJIT_IMM; + /* src2w = src2_r unneeded. */ + } + } + + /* Source 1. */ + if (FAST_IS_REG(src1)) { + src1_r = src1; + flags |= REG1_SOURCE; + } + else if (src1 & SLJIT_IMM) { + if (src1w) { + FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3)); + src1_r = TMP_REG1; + } + else + src1_r = TMP_ZERO; + } + else { + if (getput_arg_fast(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w)) + FAIL_IF(compiler->error); + else + flags |= SLOW_SRC1; + src1_r = TMP_REG1; + } + + /* Source 2. */ + if (FAST_IS_REG(src2)) { + src2_r = src2; + flags |= REG2_SOURCE; + if ((flags & (REG_DEST | MOVE_OP)) == MOVE_OP) + dst_r = (sljit_s32)src2_r; + } + else if (src2 & SLJIT_IMM) { + if (!(flags & SRC2_IMM)) { + if (src2w) { + FAIL_IF(load_immediate(compiler, sugg_src2_r, src2w, TMP_REG3)); + src2_r = sugg_src2_r; + } + else { + src2_r = TMP_ZERO; + if (flags & MOVE_OP) { + if (dst & SLJIT_MEM) + dst_r = 0; + else + op = SLJIT_MOV; + } + } + } + } + else { + if (getput_arg_fast(compiler, flags | LOAD_DATA, sugg_src2_r, src2, src2w)) + FAIL_IF(compiler->error); + else + flags |= SLOW_SRC2; + src2_r = sugg_src2_r; + } + + if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { + SLJIT_ASSERT(src2_r == TMP_REG2); + if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, src1, src1w)); + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); + } + else { + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw)); + } + } + else if (flags & SLOW_SRC1) + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); + else if (flags & SLOW_SRC2) + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw)); + + FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r)); + + if (dst & SLJIT_MEM) { + if (!(flags & SLOW_DEST)) { + getput_arg_fast(compiler, flags, dst_r, dst, dstw); + return compiler->error; + } + return getput_arg(compiler, flags, dst_r, dst, dstw, 0, 0); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op) +{ +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + sljit_ins word = (op & SLJIT_32) >> 5; + + SLJIT_ASSERT(word == 0 || word == 0x8); +#endif /* SLJIT_CONFIG_RISCV_64 */ + + CHECK_ERROR(); + CHECK(check_sljit_emit_op0(compiler, op)); + + switch (GET_OPCODE(op)) { + case SLJIT_BREAKPOINT: + return push_inst(compiler, EBREAK); + case SLJIT_NOP: + return push_inst(compiler, ADDI | RD(TMP_ZERO) | RS1(TMP_ZERO) | IMM_I(0)); + case SLJIT_LMUL_UW: + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R1) | IMM_I(0))); + FAIL_IF(push_inst(compiler, MULHU | RD(SLJIT_R1) | RS1(SLJIT_R0) | RS2(SLJIT_R1))); + return push_inst(compiler, MUL | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(TMP_REG1)); + case SLJIT_LMUL_SW: + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R1) | IMM_I(0))); + FAIL_IF(push_inst(compiler, MULH | RD(SLJIT_R1) | RS1(SLJIT_R0) | RS2(SLJIT_R1))); + return push_inst(compiler, MUL | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(TMP_REG1)); + case SLJIT_DIVMOD_UW: + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R0) | IMM_I(0))); + FAIL_IF(push_inst(compiler, DIVU | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1))); + return push_inst(compiler, REMU | WORD | RD(SLJIT_R1) | RS1(TMP_REG1) | RS2(SLJIT_R1)); + case SLJIT_DIVMOD_SW: + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R0) | IMM_I(0))); + FAIL_IF(push_inst(compiler, DIV | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1))); + return push_inst(compiler, REM | WORD | RD(SLJIT_R1) | RS1(TMP_REG1) | RS2(SLJIT_R1)); + case SLJIT_DIV_UW: + return push_inst(compiler, DIVU | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1)); + case SLJIT_DIV_SW: + return push_inst(compiler, DIV | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1)); + case SLJIT_ENDBR: + case SLJIT_SKIP_FRAMES_BEFORE_RETURN: + return SLJIT_SUCCESS; + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 dst, sljit_sw dstw, + sljit_s32 src, sljit_sw srcw) +{ + sljit_s32 flags = 0; + + CHECK_ERROR(); + CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw)); + ADJUST_LOCAL_OFFSET(dst, dstw); + ADJUST_LOCAL_OFFSET(src, srcw); + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + if (op & SLJIT_32) + flags = INT_DATA | SIGNED_DATA; +#endif + + switch (GET_OPCODE(op)) { + case SLJIT_MOV: +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + case SLJIT_MOV_U32: + case SLJIT_MOV_S32: + case SLJIT_MOV32: +#endif + case SLJIT_MOV_P: + return emit_op(compiler, SLJIT_MOV, WORD_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, srcw); + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + case SLJIT_MOV_U32: + return emit_op(compiler, SLJIT_MOV_U32, INT_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u32)srcw : srcw); + + case SLJIT_MOV_S32: + /* Logical operators have no W variant, so sign extended input is necessary for them. */ + case SLJIT_MOV32: + return emit_op(compiler, SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s32)srcw : srcw); +#endif + + case SLJIT_MOV_U8: + return emit_op(compiler, op, BYTE_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw); + + case SLJIT_MOV_S8: + return emit_op(compiler, op, BYTE_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw); + + case SLJIT_MOV_U16: + return emit_op(compiler, op, HALF_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw); + + case SLJIT_MOV_S16: + return emit_op(compiler, op, HALF_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw); + + case SLJIT_NOT: + return emit_op(compiler, SLJIT_XOR | (op & (SLJIT_32 | SLJIT_SET_Z)), flags, dst, dstw, src, srcw, SLJIT_IMM, -1); + + case SLJIT_CLZ: + case SLJIT_CTZ: + return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw); + } + + SLJIT_UNREACHABLE(); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 dst, sljit_sw dstw, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_s32 flags = 0; + + CHECK_ERROR(); + CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w)); + ADJUST_LOCAL_OFFSET(dst, dstw); + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + if (op & SLJIT_32) { + flags |= INT_DATA | SIGNED_DATA; + if (src1 & SLJIT_IMM) + src1w = (sljit_s32)src1w; + if (src2 & SLJIT_IMM) + src2w = (sljit_s32)src2w; + } +#endif + + switch (GET_OPCODE(op)) { + case SLJIT_ADD: + case SLJIT_ADDC: + compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD; + return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_SUB: + case SLJIT_SUBC: + compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB; + return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_MUL: + compiler->status_flags_state = 0; + return emit_op(compiler, op, flags | CUMULATIVE_OP, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_AND: + case SLJIT_OR: + case SLJIT_XOR: + return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_SHL: + case SLJIT_MSHL: + case SLJIT_LSHR: + case SLJIT_MLSHR: + case SLJIT_ASHR: + case SLJIT_MASHR: + case SLJIT_ROTL: + case SLJIT_ROTR: + if (src2 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + src2w &= 0x1f; +#else /* !SLJIT_CONFIG_RISCV_32 */ + if (op & SLJIT_32) + src2w &= 0x1f; + else + src2w &= 0x3f; +#endif /* SLJIT_CONFIG_RISCV_32 */ + } + + return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w); + } + + SLJIT_UNREACHABLE(); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_s32 is_left; + sljit_ins ins1, ins2, ins3; +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + sljit_ins word = (op & SLJIT_32) >> 5; + sljit_s32 inp_flags = ((op & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA; + sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64; +#else /* !SLJIT_CONFIG_RISCV_64 */ + sljit_s32 inp_flags = WORD_DATA | LOAD_DATA; + sljit_sw bit_length = 32; +#endif /* SLJIT_CONFIG_RISCV_64 */ + + SLJIT_ASSERT(WORD == 0 || WORD == 0x8); + + CHECK_ERROR(); + CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w)); + + is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL); + + if (src_dst == src1) { + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_op2(compiler, (is_left ? SLJIT_ROTL : SLJIT_ROTR) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w); + } + + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + + if (src2 & SLJIT_IMM) { + src2w &= bit_length - 1; + + if (src2w == 0) + return SLJIT_SUCCESS; + } else if (src2 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG2, src2, src2w)); + src2 = TMP_REG2; + } + + if (src1 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG1, src1, src1w)); + src1 = TMP_REG1; + } else if (src1 & SLJIT_IMM) { + FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3)); + src1 = TMP_REG1; + } + + if (src2 & SLJIT_IMM) { + if (is_left) { + ins1 = SLLI | WORD | IMM_I(src2w); + src2w = bit_length - src2w; + ins2 = SRLI | WORD | IMM_I(src2w); + } else { + ins1 = SRLI | WORD | IMM_I(src2w); + src2w = bit_length - src2w; + ins2 = SLLI | WORD | IMM_I(src2w); + } + + FAIL_IF(push_inst(compiler, ins1 | RD(src_dst) | RS1(src_dst))); + FAIL_IF(push_inst(compiler, ins2 | RD(TMP_REG1) | RS1(src1))); + return push_inst(compiler, OR | RD(src_dst) | RS1(src_dst) | RS2(TMP_REG1)); + } + + if (is_left) { + ins1 = SLL; + ins2 = SRLI; + ins3 = SRL; + } else { + ins1 = SRL; + ins2 = SLLI; + ins3 = SLL; + } + + FAIL_IF(push_inst(compiler, ins1 | WORD | RD(src_dst) | RS1(src_dst) | RS2(src2))); + + if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) { + FAIL_IF(push_inst(compiler, ins2 | WORD | RD(TMP_REG1) | RS1(src1) | IMM_I(1))); + FAIL_IF(push_inst(compiler, XORI | RD(TMP_REG2) | RS1(src2) | IMM_I((sljit_ins)bit_length - 1))); + src1 = TMP_REG1; + } else + FAIL_IF(push_inst(compiler, SUB | WORD | RD(TMP_REG2) | RS1(TMP_ZERO) | RS2(src2))); + + FAIL_IF(push_inst(compiler, ins3 | WORD | RD(TMP_REG1) | RS1(src1) | RS2(TMP_REG2))); + return push_inst(compiler, OR | RD(src_dst) | RS1(src_dst) | RS2(TMP_REG1)); +} + +#undef WORD + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_op_src(compiler, op, src, srcw)); + ADJUST_LOCAL_OFFSET(src, srcw); + + switch (op) { + case SLJIT_FAST_RETURN: + if (FAST_IS_REG(src)) + FAIL_IF(push_inst(compiler, ADDI | RD(RETURN_ADDR_REG) | RS1(src) | IMM_I(0))); + else + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG, src, srcw)); + + return push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(RETURN_ADDR_REG) | IMM_I(0)); + case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN: + return SLJIT_SUCCESS; + case SLJIT_PREFETCH_L1: + case SLJIT_PREFETCH_L2: + case SLJIT_PREFETCH_L3: + case SLJIT_PREFETCH_ONCE: + return SLJIT_SUCCESS; + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) +{ + CHECK_REG_INDEX(check_sljit_get_register_index(reg)); + return reg_map[reg]; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) +{ + CHECK_REG_INDEX(check_sljit_get_float_register_index(reg)); + return freg_map[reg]; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, sljit_u32 size) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_op_custom(compiler, instruction, size)); + + return push_inst(compiler, *(sljit_ins*)instruction); +} + +/* --------------------------------------------------------------------- */ +/* Floating point operators */ +/* --------------------------------------------------------------------- */ + +#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_32) >> 7)) +#define FMT(op) ((sljit_ins)((op & SLJIT_32) ^ SLJIT_32) << 17) + +static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 dst, sljit_sw dstw, + sljit_s32 src, sljit_sw srcw) +{ +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) +# define flags (sljit_u32)0 +#else + sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_SW_FROM_F64)) << 21; +#endif + sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; + + if (src & SLJIT_MEM) { + FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw)); + src = TMP_FREG1; + } + + FAIL_IF(push_inst(compiler, FCVT_W_S | FMT(op) | flags | RD(dst_r) | FRS1(src))); + + /* Store the integer value from a VFP register. */ + if (dst & SLJIT_MEM) { +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + return emit_op_mem2(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0); +#else + return emit_op_mem2(compiler, flags ? WORD_DATA : INT_DATA, TMP_REG2, dst, dstw, 0, 0); +#endif + } + return SLJIT_SUCCESS; + +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) +# undef flags +#endif +} + +static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 dst, sljit_sw dstw, + sljit_s32 src, sljit_sw srcw) +{ + sljit_ins inst; +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW)) << 21; +#endif + + sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; + + if (src & SLJIT_MEM) { +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw)); +#else + FAIL_IF(emit_op_mem2(compiler, (flags ? WORD_DATA : INT_DATA) | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw)); +#endif + src = TMP_REG1; + } else if (src & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) + if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) + srcw = (sljit_s32)srcw; +#endif + + FAIL_IF(load_immediate(compiler, TMP_REG1, srcw, TMP_REG3)); + src = TMP_REG1; + } + + inst = FCVT_S_W | FMT(op) | FRD(dst_r) | RS1(src); + +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + if (op & SLJIT_32) + inst |= F3(0x7); +#else + inst |= flags; + + if (op != SLJIT_CONV_F64_FROM_S32) + inst |= F3(0x7); +#endif + + FAIL_IF(push_inst(compiler, inst)); + + if (dst & SLJIT_MEM) + return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0); + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_ins inst; + + if (src1 & SLJIT_MEM) { + FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); + src1 = TMP_FREG1; + } + + if (src2 & SLJIT_MEM) { + FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0)); + src2 = TMP_FREG2; + } + + switch (GET_FLAG_TYPE(op)) { + case SLJIT_F_EQUAL: + case SLJIT_F_NOT_EQUAL: + case SLJIT_ORDERED_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + inst = FEQ_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2); + break; + case SLJIT_F_LESS: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_ORDERED_LESS: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + inst = FLT_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2); + break; + case SLJIT_ORDERED_GREATER: + case SLJIT_UNORDERED_OR_LESS_EQUAL: + inst = FLT_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src2) | FRS2(src1); + break; + case SLJIT_F_GREATER: + case SLJIT_F_LESS_EQUAL: + case SLJIT_UNORDERED_OR_GREATER: + case SLJIT_ORDERED_LESS_EQUAL: + inst = FLE_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2); + break; + case SLJIT_UNORDERED_OR_LESS: + case SLJIT_ORDERED_GREATER_EQUAL: + inst = FLE_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src2) | FRS2(src1); + break; + case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */ + case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */ + FAIL_IF(push_inst(compiler, FLT_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2))); + FAIL_IF(push_inst(compiler, FLT_S | FMT(op) | RD(TMP_REG1) | FRS1(src2) | FRS2(src1))); + inst = OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(TMP_REG1); + break; + default: /* SLJIT_UNORDERED, SLJIT_ORDERED */ + FAIL_IF(push_inst(compiler, FADD_S | FMT(op) | FRD(TMP_FREG1) | FRS1(src1) | FRS2(src2))); + inst = FEQ_S | FMT(op) | RD(OTHER_FLAG) | FRS1(TMP_FREG1) | FRS2(TMP_FREG1); + break; + } + + return push_inst(compiler, inst); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 dst, sljit_sw dstw, + sljit_s32 src, sljit_sw srcw) +{ + sljit_s32 dst_r; + + CHECK_ERROR(); + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100) && !(DOUBLE_DATA & 0x2), float_transfer_bit_error); + SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw); + + if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) + op ^= SLJIT_32; + + dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; + + if (src & SLJIT_MEM) { + FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw)); + src = dst_r; + } + + switch (GET_OPCODE(op)) { + case SLJIT_MOV_F64: + if (src != dst_r) { + if (dst_r != TMP_FREG1) + FAIL_IF(push_inst(compiler, FSGNJ_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src))); + else + dst_r = src; + } + break; + case SLJIT_NEG_F64: + FAIL_IF(push_inst(compiler, FSGNJN_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src))); + break; + case SLJIT_ABS_F64: + FAIL_IF(push_inst(compiler, FSGNJX_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src))); + break; + case SLJIT_CONV_F64_FROM_F32: + /* The SLJIT_32 bit is inverted because sljit_f32 needs to be loaded from the memory. */ + FAIL_IF(push_inst(compiler, FCVT_S_D | ((op & SLJIT_32) ? (1 << 25) : ((1 << 20) | F3(7))) | FRD(dst_r) | FRS1(src))); + op ^= SLJIT_32; + break; + } + + if (dst & SLJIT_MEM) + return emit_op_mem2(compiler, FLOAT_DATA(op), dst_r, dst, dstw, 0, 0); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 dst, sljit_sw dstw, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_s32 dst_r, flags = 0; + + CHECK_ERROR(); + CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w)); + ADJUST_LOCAL_OFFSET(dst, dstw); + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2; + + if (src1 & SLJIT_MEM) { + if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) { + FAIL_IF(compiler->error); + src1 = TMP_FREG1; + } else + flags |= SLOW_SRC1; + } + + if (src2 & SLJIT_MEM) { + if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) { + FAIL_IF(compiler->error); + src2 = TMP_FREG2; + } else + flags |= SLOW_SRC2; + } + + if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { + if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, src1, src1w)); + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw)); + } + else { + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw)); + } + } + else if (flags & SLOW_SRC1) + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw)); + else if (flags & SLOW_SRC2) + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw)); + + if (flags & SLOW_SRC1) + src1 = TMP_FREG1; + if (flags & SLOW_SRC2) + src2 = TMP_FREG2; + + switch (GET_OPCODE(op)) { + case SLJIT_ADD_F64: + FAIL_IF(push_inst(compiler, FADD_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2))); + break; + + case SLJIT_SUB_F64: + FAIL_IF(push_inst(compiler, FSUB_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2))); + break; + + case SLJIT_MUL_F64: + FAIL_IF(push_inst(compiler, FMUL_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2))); + break; + + case SLJIT_DIV_F64: + FAIL_IF(push_inst(compiler, FDIV_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2))); + break; + } + + if (dst_r == TMP_FREG2) + FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, 0, 0)); + + return SLJIT_SUCCESS; +} + +#undef FLOAT_DATA +#undef FMT + +/* --------------------------------------------------------------------- */ +/* Other instructions */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); + ADJUST_LOCAL_OFFSET(dst, dstw); + + if (FAST_IS_REG(dst)) + return push_inst(compiler, ADDI | RD(dst) | RS1(RETURN_ADDR_REG) | IMM_I(0)); + + /* Memory. */ + return emit_op_mem(compiler, WORD_DATA, RETURN_ADDR_REG, dst, dstw); +} + +/* --------------------------------------------------------------------- */ +/* Conditional instructions */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler) +{ + struct sljit_label *label; + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_label(compiler)); + + if (compiler->last_label && compiler->last_label->size == compiler->size) + return compiler->last_label; + + label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label)); + PTR_FAIL_IF(!label); + set_label(label, compiler); + return label; +} + +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) +#define BRANCH_LENGTH ((sljit_ins)(3 * sizeof(sljit_ins)) << 7) +#else +#define BRANCH_LENGTH ((sljit_ins)(7 * sizeof(sljit_ins)) << 7) +#endif + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type) +{ + struct sljit_jump *jump; + sljit_ins inst; + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_jump(compiler, type)); + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF(!jump); + set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); + type &= 0xff; + + switch (type) { + case SLJIT_EQUAL: + inst = BNE | RS1(EQUAL_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH; + break; + case SLJIT_NOT_EQUAL: + inst = BEQ | RS1(EQUAL_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH; + break; + case SLJIT_LESS: + case SLJIT_GREATER: + case SLJIT_SIG_LESS: + case SLJIT_SIG_GREATER: + case SLJIT_OVERFLOW: + case SLJIT_CARRY: + case SLJIT_F_EQUAL: + case SLJIT_ORDERED_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */ + case SLJIT_F_LESS: + case SLJIT_ORDERED_LESS: + case SLJIT_ORDERED_GREATER: + case SLJIT_F_LESS_EQUAL: + case SLJIT_ORDERED_LESS_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: + case SLJIT_ORDERED: + inst = BEQ | RS1(OTHER_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH; + break; + case SLJIT_GREATER_EQUAL: + case SLJIT_LESS_EQUAL: + case SLJIT_SIG_GREATER_EQUAL: + case SLJIT_SIG_LESS_EQUAL: + case SLJIT_NOT_OVERFLOW: + case SLJIT_NOT_CARRY: + case SLJIT_F_NOT_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */ + case SLJIT_F_GREATER_EQUAL: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + case SLJIT_UNORDERED_OR_LESS_EQUAL: + case SLJIT_F_GREATER: + case SLJIT_UNORDERED_OR_GREATER: + case SLJIT_UNORDERED_OR_LESS: + case SLJIT_UNORDERED: + inst = BNE | RS1(OTHER_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH; + break; + default: + /* Not conditional branch. */ + inst = 0; + break; + } + + if (inst != 0) { + PTR_FAIL_IF(push_inst(compiler, inst)); + jump->flags |= IS_COND; + } + + jump->addr = compiler->size; + inst = JALR | RS1(TMP_REG1) | IMM_I(0); + + if (type >= SLJIT_FAST_CALL) { + jump->flags |= IS_CALL; + inst |= RD(RETURN_ADDR_REG); + } + + PTR_FAIL_IF(push_inst(compiler, inst)); + + /* Maximum number of instructions required for generating a constant. */ +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + compiler->size += 1; +#else + compiler->size += 5; +#endif + return jump; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ + SLJIT_UNUSED_ARG(arg_types); + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); + + if (type & SLJIT_CALL_RETURN) { + PTR_FAIL_IF(emit_stack_frame_release(compiler, 0)); + type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); + } + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_jump(compiler, type); +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + struct sljit_jump *jump; + sljit_s32 flags; + sljit_ins inst; + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_cmp(compiler, type, src1, src1w, src2, src2w)); + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + flags = WORD_DATA | LOAD_DATA; +#else /* !SLJIT_CONFIG_RISCV_32 */ + flags = ((type & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA; +#endif /* SLJIT_CONFIG_RISCV_32 */ + + if (src1 & SLJIT_MEM) { + PTR_FAIL_IF(emit_op_mem2(compiler, flags, TMP_REG1, src1, src1w, src2, src2w)); + src1 = TMP_REG1; + } + + if (src2 & SLJIT_MEM) { + PTR_FAIL_IF(emit_op_mem2(compiler, flags, TMP_REG2, src2, src2w, 0, 0)); + src2 = TMP_REG2; + } + + if (src1 & SLJIT_IMM) { + if (src1w != 0) { + PTR_FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3)); + src1 = TMP_REG1; + } + else + src1 = TMP_ZERO; + } + + if (src2 & SLJIT_IMM) { + if (src2w != 0) { + PTR_FAIL_IF(load_immediate(compiler, TMP_REG2, src2w, TMP_REG3)); + src2 = TMP_REG2; + } + else + src2 = TMP_ZERO; + } + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF(!jump); + set_jump(jump, compiler, (sljit_u32)((type & SLJIT_REWRITABLE_JUMP) | IS_COND)); + type &= 0xff; + + switch (type) { + case SLJIT_EQUAL: + inst = BNE | RS1(src1) | RS2(src2) | BRANCH_LENGTH; + break; + case SLJIT_NOT_EQUAL: + inst = BEQ | RS1(src1) | RS2(src2) | BRANCH_LENGTH; + break; + case SLJIT_LESS: + inst = BGEU | RS1(src1) | RS2(src2) | BRANCH_LENGTH; + break; + case SLJIT_GREATER_EQUAL: + inst = BLTU | RS1(src1) | RS2(src2) | BRANCH_LENGTH; + break; + case SLJIT_GREATER: + inst = BGEU | RS1(src2) | RS2(src1) | BRANCH_LENGTH; + break; + case SLJIT_LESS_EQUAL: + inst = BLTU | RS1(src2) | RS2(src1) | BRANCH_LENGTH; + break; + case SLJIT_SIG_LESS: + inst = BGE | RS1(src1) | RS2(src2) | BRANCH_LENGTH; + break; + case SLJIT_SIG_GREATER_EQUAL: + inst = BLT | RS1(src1) | RS2(src2) | BRANCH_LENGTH; + break; + case SLJIT_SIG_GREATER: + inst = BGE | RS1(src2) | RS2(src1) | BRANCH_LENGTH; + break; + case SLJIT_SIG_LESS_EQUAL: + inst = BLT | RS1(src2) | RS2(src1) | BRANCH_LENGTH; + break; + } + + PTR_FAIL_IF(push_inst(compiler, inst)); + + jump->addr = compiler->size; + PTR_FAIL_IF(push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(TMP_REG1) | IMM_I(0))); + + /* Maximum number of instructions required for generating a constant. */ +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + compiler->size += 1; +#else + compiler->size += 5; +#endif + return jump; +} + +#undef BRANCH_LENGTH + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) +{ + struct sljit_jump *jump; + + CHECK_ERROR(); + CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); + + if (!(src & SLJIT_IMM)) { + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw)); + src = TMP_REG1; + } + return push_inst(compiler, JALR | RD((type >= SLJIT_FAST_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | RS1(src) | IMM_I(0)); + } + + /* These jumps are converted to jump/call instructions when possible. */ + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + FAIL_IF(!jump); + set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_CALL : 0)); + jump->u.target = (sljit_uw)srcw; + + jump->addr = compiler->size; + FAIL_IF(push_inst(compiler, JALR | RD((type >= SLJIT_FAST_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | RS1(TMP_REG1) | IMM_I(0))); + + /* Maximum number of instructions required for generating a constant. */ +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + compiler->size += 1; +#else + compiler->size += 5; +#endif + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + SLJIT_UNUSED_ARG(arg_types); + CHECK_ERROR(); + CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw)); + src = TMP_REG1; + } + + if (type & SLJIT_CALL_RETURN) { + if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(src) | IMM_I(0))); + src = TMP_REG1; + } + + FAIL_IF(emit_stack_frame_release(compiler, 0)); + type = SLJIT_JUMP; + } + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, type, src, srcw); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 dst, sljit_sw dstw, + sljit_s32 type) +{ + sljit_s32 src_r, dst_r, invert; + sljit_s32 saved_op = op; +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + sljit_s32 mem_type = WORD_DATA; +#else + sljit_s32 mem_type = ((op & SLJIT_32) || op == SLJIT_MOV32) ? (INT_DATA | SIGNED_DATA) : WORD_DATA; +#endif + + CHECK_ERROR(); + CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); + ADJUST_LOCAL_OFFSET(dst, dstw); + + op = GET_OPCODE(op); + dst_r = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2; + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + if (op >= SLJIT_ADD && (dst & SLJIT_MEM)) + FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, TMP_REG1, dst, dstw, dst, dstw)); + + if (type < SLJIT_F_EQUAL) { + src_r = OTHER_FLAG; + invert = type & 0x1; + + switch (type) { + case SLJIT_EQUAL: + case SLJIT_NOT_EQUAL: + FAIL_IF(push_inst(compiler, SLTUI | RD(dst_r) | RS1(EQUAL_FLAG) | IMM_I(1))); + src_r = dst_r; + break; + case SLJIT_OVERFLOW: + case SLJIT_NOT_OVERFLOW: + if (compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)) { + src_r = OTHER_FLAG; + break; + } + FAIL_IF(push_inst(compiler, SLTUI | RD(dst_r) | RS1(OTHER_FLAG) | IMM_I(1))); + src_r = dst_r; + invert ^= 0x1; + break; + } + } else { + invert = 0; + src_r = OTHER_FLAG; + + switch (type) { + case SLJIT_F_NOT_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */ + case SLJIT_F_GREATER_EQUAL: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + case SLJIT_UNORDERED_OR_LESS_EQUAL: + case SLJIT_F_GREATER: + case SLJIT_UNORDERED_OR_GREATER: + case SLJIT_UNORDERED_OR_LESS: + case SLJIT_UNORDERED: + invert = 1; + break; + } + } + + if (invert) { + FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(src_r) | IMM_I(1))); + src_r = dst_r; + } + + if (op < SLJIT_ADD) { + if (dst & SLJIT_MEM) + return emit_op_mem(compiler, mem_type, src_r, dst, dstw); + + if (src_r != dst_r) + return push_inst(compiler, ADDI | RD(dst_r) | RS1(src_r) | IMM_I(0)); + return SLJIT_SUCCESS; + } + + mem_type |= CUMULATIVE_OP | IMM_OP | ALT_KEEP_CACHE; + + if (dst & SLJIT_MEM) + return emit_op(compiler, saved_op, mem_type, dst, dstw, TMP_REG1, 0, src_r, 0); + return emit_op(compiler, saved_op, mem_type, dst, dstw, dst, dstw, src_r, 0); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); + + return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_s32 flags; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + + if (!(reg & REG_PAIR_MASK)) + return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw); + + if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) { + memw &= 0x3; + + if (SLJIT_UNLIKELY(memw != 0)) { + FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG1) | RS1(OFFS_REG(mem)) | IMM_I(memw))); + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(TMP_REG1) | RS2(mem & REG_MASK))); + } else + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(mem & REG_MASK) | RS2(OFFS_REG(mem)))); + + mem = TMP_REG1; + memw = 0; + } else if (memw > SIMM_MAX - SSIZE_OF(sw) || memw < SIMM_MIN) { + if (((memw + 0x800) & 0xfff) <= 0xfff - SSIZE_OF(sw)) { + FAIL_IF(load_immediate(compiler, TMP_REG1, TO_ARGW_HI(memw), TMP_REG3)); + memw &= 0xfff; + } else { + FAIL_IF(load_immediate(compiler, TMP_REG1, memw, TMP_REG3)); + memw = 0; + } + + if (mem & REG_MASK) + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(TMP_REG1) | RS2(mem & REG_MASK))); + + mem = TMP_REG1; + } else { + mem &= REG_MASK; + memw &= 0xfff; + } + + SLJIT_ASSERT((memw >= 0 && memw <= SIMM_MAX - SSIZE_OF(sw)) || (memw > SIMM_MAX && memw <= 0xfff)); + + if (!(type & SLJIT_MEM_STORE) && mem == REG_PAIR_FIRST(reg)) { + FAIL_IF(push_mem_inst(compiler, WORD_DATA | LOAD_DATA, REG_PAIR_SECOND(reg), mem, (memw + SSIZE_OF(sw)) & 0xfff)); + return push_mem_inst(compiler, WORD_DATA | LOAD_DATA, REG_PAIR_FIRST(reg), mem, memw); + } + + flags = WORD_DATA | (!(type & SLJIT_MEM_STORE) ? LOAD_DATA : 0); + + FAIL_IF(push_mem_inst(compiler, flags, REG_PAIR_FIRST(reg), mem, memw)); + return push_mem_inst(compiler, flags, REG_PAIR_SECOND(reg), mem, (memw + SSIZE_OF(sw)) & 0xfff); +} + +#undef TO_ARGW_HI + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) +{ + struct sljit_const *const_; + sljit_s32 dst_r; + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value)); + ADJUST_LOCAL_OFFSET(dst, dstw); + + const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const)); + PTR_FAIL_IF(!const_); + set_const(const_, compiler); + + dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; + PTR_FAIL_IF(emit_const(compiler, dst_r, init_value, ADDI | RD(dst_r))); + + if (dst & SLJIT_MEM) + PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw)); + + return const_; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) +{ + struct sljit_put_label *put_label; + sljit_s32 dst_r; + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw)); + ADJUST_LOCAL_OFFSET(dst, dstw); + + put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label)); + PTR_FAIL_IF(!put_label); + set_put_label(put_label, compiler, 0); + + dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; + PTR_FAIL_IF(push_inst(compiler, (sljit_ins)dst_r)); +#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) + compiler->size += 1; +#else + compiler->size += 5; +#endif + + if (dst & SLJIT_MEM) + PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw)); + + return put_label; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) +{ + sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset); +} diff --git a/thirdparty/pcre2/src/sljit/sljitNativeS390X.c b/thirdparty/pcre2/src/sljit/sljitNativeS390X.c index 8eef910c42..8b51bad9bc 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativeS390X.c +++ b/thirdparty/pcre2/src/sljit/sljitNativeS390X.c @@ -103,11 +103,8 @@ static const sljit_gpr r15 = 15; /* reg_map[SLJIT_NUMBER_OF_REGISTERS + 1]: stac /* When reg cannot be unused. */ #define IS_GPR_REG(reg) ((reg > 0) && (reg) <= SLJIT_SP) -/* Link registers. The normal link register is r14, but since - we use that for flags we need to use r0 instead to do fast - calls so that flags are preserved. */ +/* Link register. */ static const sljit_gpr link_r = 14; /* r14 */ -static const sljit_gpr fast_link_r = 0; /* r0 */ #define TMP_FREG1 (0) @@ -220,7 +217,8 @@ static SLJIT_INLINE sljit_u8 get_cc(struct sljit_compiler *compiler, sljit_s32 t } /* fallthrough */ - case SLJIT_EQUAL_F64: + case SLJIT_F_EQUAL: + case SLJIT_ORDERED_EQUAL: return cc0; case SLJIT_NOT_EQUAL: @@ -234,13 +232,14 @@ static SLJIT_INLINE sljit_u8 get_cc(struct sljit_compiler *compiler, sljit_s32 t } /* fallthrough */ - case SLJIT_NOT_EQUAL_F64: + case SLJIT_UNORDERED_OR_NOT_EQUAL: return (cc1 | cc2 | cc3); case SLJIT_LESS: return cc1; case SLJIT_GREATER_EQUAL: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: return (cc0 | cc2 | cc3); case SLJIT_GREATER: @@ -254,7 +253,8 @@ static SLJIT_INLINE sljit_u8 get_cc(struct sljit_compiler *compiler, sljit_s32 t return (cc0 | cc1 | cc2); case SLJIT_SIG_LESS: - case SLJIT_LESS_F64: + case SLJIT_F_LESS: + case SLJIT_ORDERED_LESS: return cc1; case SLJIT_NOT_CARRY: @@ -263,7 +263,8 @@ static SLJIT_INLINE sljit_u8 get_cc(struct sljit_compiler *compiler, sljit_s32 t /* fallthrough */ case SLJIT_SIG_LESS_EQUAL: - case SLJIT_LESS_EQUAL_F64: + case SLJIT_F_LESS_EQUAL: + case SLJIT_ORDERED_LESS_EQUAL: return (cc0 | cc1); case SLJIT_CARRY: @@ -272,6 +273,7 @@ static SLJIT_INLINE sljit_u8 get_cc(struct sljit_compiler *compiler, sljit_s32 t /* fallthrough */ case SLJIT_SIG_GREATER: + case SLJIT_UNORDERED_OR_GREATER: /* Overflow is considered greater, see SLJIT_SUB. */ return cc2 | cc3; @@ -283,7 +285,7 @@ static SLJIT_INLINE sljit_u8 get_cc(struct sljit_compiler *compiler, sljit_s32 t return (cc2 | cc3); /* fallthrough */ - case SLJIT_UNORDERED_F64: + case SLJIT_UNORDERED: return cc3; case SLJIT_NOT_OVERFLOW: @@ -291,14 +293,29 @@ static SLJIT_INLINE sljit_u8 get_cc(struct sljit_compiler *compiler, sljit_s32 t return (cc0 | cc1); /* fallthrough */ - case SLJIT_ORDERED_F64: + case SLJIT_ORDERED: return (cc0 | cc1 | cc2); - case SLJIT_GREATER_F64: + case SLJIT_F_NOT_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: + return (cc1 | cc2); + + case SLJIT_F_GREATER: + case SLJIT_ORDERED_GREATER: return cc2; - case SLJIT_GREATER_EQUAL_F64: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: return (cc0 | cc2); + + case SLJIT_UNORDERED_OR_LESS_EQUAL: + return (cc0 | cc1 | cc3); + + case SLJIT_UNORDERED_OR_EQUAL: + return (cc0 | cc3); + + case SLJIT_UNORDERED_OR_LESS: + return (cc1 | cc3); } SLJIT_UNREACHABLE(); @@ -978,7 +995,7 @@ static sljit_s32 make_addr_bx(struct sljit_compiler *compiler, (cond) ? EVAL(i1, r, addr) : EVAL(i2, r, addr) /* May clobber tmp1. */ -static sljit_s32 load_word(struct sljit_compiler *compiler, sljit_gpr dst, +static sljit_s32 load_word(struct sljit_compiler *compiler, sljit_gpr dst_r, sljit_s32 src, sljit_sw srcw, sljit_s32 is_32bit) { @@ -986,21 +1003,36 @@ static sljit_s32 load_word(struct sljit_compiler *compiler, sljit_gpr dst, sljit_ins ins; SLJIT_ASSERT(src & SLJIT_MEM); - if (have_ldisp() || !is_32bit) - FAIL_IF(make_addr_bxy(compiler, &addr, src, srcw, tmp1)); - else + + if (is_32bit && ((src & OFFS_REG_MASK) || is_u12(srcw) || !is_s20(srcw))) { FAIL_IF(make_addr_bx(compiler, &addr, src, srcw, tmp1)); + return push_inst(compiler, 0x58000000 /* l */ | R20A(dst_r) | R16A(addr.index) | R12A(addr.base) | (sljit_ins)addr.offset); + } - if (is_32bit) - ins = WHEN(is_u12(addr.offset), dst, l, ly, addr); - else - ins = lg(dst, addr.offset, addr.index, addr.base); + FAIL_IF(make_addr_bxy(compiler, &addr, src, srcw, tmp1)); - return push_inst(compiler, ins); + ins = is_32bit ? 0xe30000000058 /* ly */ : 0xe30000000004 /* lg */; + return push_inst(compiler, ins | R36A(dst_r) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset)); } /* May clobber tmp1. */ -static sljit_s32 store_word(struct sljit_compiler *compiler, sljit_gpr src, +static sljit_s32 load_unsigned_word(struct sljit_compiler *compiler, sljit_gpr dst_r, + sljit_s32 src, sljit_sw srcw, + sljit_s32 is_32bit) +{ + struct addr addr; + sljit_ins ins; + + SLJIT_ASSERT(src & SLJIT_MEM); + + FAIL_IF(make_addr_bxy(compiler, &addr, src, srcw, tmp1)); + + ins = is_32bit ? 0xe30000000016 /* llgf */ : 0xe30000000004 /* lg */; + return push_inst(compiler, ins | R36A(dst_r) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset)); +} + +/* May clobber tmp1. */ +static sljit_s32 store_word(struct sljit_compiler *compiler, sljit_gpr src_r, sljit_s32 dst, sljit_sw dstw, sljit_s32 is_32bit) { @@ -1008,17 +1040,16 @@ static sljit_s32 store_word(struct sljit_compiler *compiler, sljit_gpr src, sljit_ins ins; SLJIT_ASSERT(dst & SLJIT_MEM); - if (have_ldisp() || !is_32bit) - FAIL_IF(make_addr_bxy(compiler, &addr, dst, dstw, tmp1)); - else + + if (is_32bit && ((dst & OFFS_REG_MASK) || is_u12(dstw) || !is_s20(dstw))) { FAIL_IF(make_addr_bx(compiler, &addr, dst, dstw, tmp1)); + return push_inst(compiler, 0x50000000 /* st */ | R20A(src_r) | R16A(addr.index) | R12A(addr.base) | (sljit_ins)addr.offset); + } - if (is_32bit) - ins = WHEN(is_u12(addr.offset), src, st, sty, addr); - else - ins = stg(src, addr.offset, addr.index, addr.base); + FAIL_IF(make_addr_bxy(compiler, &addr, dst, dstw, tmp1)); - return push_inst(compiler, ins); + ins = is_32bit ? 0xe30000000050 /* sty */ : 0xe30000000024 /* stg */; + return push_inst(compiler, ins | R36A(src_r) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset)); } #undef WHEN @@ -1618,16 +1649,24 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) { /* TODO(mundaym): implement all */ switch (feature_type) { + case SLJIT_HAS_FPU: case SLJIT_HAS_CLZ: - return have_eimm() ? 1 : 0; /* FLOGR instruction */ + case SLJIT_HAS_ROT: + case SLJIT_HAS_PREFETCH: + return 1; + case SLJIT_HAS_CTZ: + return 2; case SLJIT_HAS_CMOV: return have_lscond1() ? 1 : 0; - case SLJIT_HAS_FPU: - return 1; } return 0; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type) +{ + return (type >= SLJIT_UNORDERED && type <= SLJIT_ORDERED_LESS_EQUAL); +} + /* --------------------------------------------------------------------- */ /* Entry, exit */ /* --------------------------------------------------------------------- */ @@ -1636,7 +1675,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { - sljit_s32 word_arg_count = 0; + sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options); sljit_s32 offset, i, tmp; CHECK_ERROR(); @@ -1648,8 +1687,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi offset = 2 * SSIZE_OF(sw); if (saveds + scratches >= SLJIT_NUMBER_OF_REGISTERS) { - FAIL_IF(push_inst(compiler, stmg(r6, r14, offset, r15))); /* save registers TODO(MGM): optimize */ - offset += 9 * SSIZE_OF(sw); + if (saved_arg_count == 0) { + FAIL_IF(push_inst(compiler, stmg(r6, r14, offset, r15))); + offset += 9 * SSIZE_OF(sw); + } else { + FAIL_IF(push_inst(compiler, stmg(r6, r13 - (sljit_gpr)saved_arg_count, offset, r15))); + offset += (8 - saved_arg_count) * SSIZE_OF(sw); + } } else { if (scratches == SLJIT_FIRST_SAVED_REG) { FAIL_IF(push_inst(compiler, stg(r6, offset, 0, r15))); @@ -1659,15 +1703,30 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi offset += (scratches - (SLJIT_FIRST_SAVED_REG - 1)) * SSIZE_OF(sw); } - if (saveds == 0) { - FAIL_IF(push_inst(compiler, stg(r14, offset, 0, r15))); - offset += SSIZE_OF(sw); - } else { - FAIL_IF(push_inst(compiler, stmg(r14 - (sljit_gpr)saveds, r14, offset, r15))); - offset += (saveds + 1) * SSIZE_OF(sw); + if (saved_arg_count == 0) { + if (saveds == 0) { + FAIL_IF(push_inst(compiler, stg(r14, offset, 0, r15))); + offset += SSIZE_OF(sw); + } else { + FAIL_IF(push_inst(compiler, stmg(r14 - (sljit_gpr)saveds, r14, offset, r15))); + offset += (saveds + 1) * SSIZE_OF(sw); + } + } else if (saveds > saved_arg_count) { + if (saveds == saved_arg_count + 1) { + FAIL_IF(push_inst(compiler, stg(r14 - (sljit_gpr)saveds, offset, 0, r15))); + offset += SSIZE_OF(sw); + } else { + FAIL_IF(push_inst(compiler, stmg(r14 - (sljit_gpr)saveds, r13 - (sljit_gpr)saved_arg_count, offset, r15))); + offset += (saveds - saved_arg_count) * SSIZE_OF(sw); + } } } + if (saved_arg_count > 0) { + FAIL_IF(push_inst(compiler, stg(r14, offset, 0, r15))); + offset += SSIZE_OF(sw); + } + tmp = SLJIT_FS0 - fsaveds; for (i = SLJIT_FS0; i > tmp; i--) { FAIL_IF(push_inst(compiler, 0x60000000 /* std */ | F20(i) | R12A(r15) | (sljit_ins)offset)); @@ -1684,15 +1743,19 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi FAIL_IF(push_inst(compiler, 0xe30000000071 /* lay */ | R36A(r15) | R28A(r15) | disp_s20(-local_size))); + if (options & SLJIT_ENTER_REG_ARG) + return SLJIT_SUCCESS; + arg_types >>= SLJIT_ARG_SHIFT; + saved_arg_count = 0; tmp = 0; while (arg_types > 0) { if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) { if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) { - FAIL_IF(push_inst(compiler, lgr(gpr(SLJIT_S0 - tmp), gpr(SLJIT_R0 + word_arg_count)))); - tmp++; + FAIL_IF(push_inst(compiler, lgr(gpr(SLJIT_S0 - saved_arg_count), gpr(SLJIT_R0 + tmp)))); + saved_arg_count++; } - word_arg_count++; + tmp++; } arg_types >>= SLJIT_ARG_SHIFT; @@ -1713,12 +1776,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp return SLJIT_SUCCESS; } -static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) +static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_gpr last_reg) { sljit_s32 offset, i, tmp; sljit_s32 local_size = compiler->local_size; sljit_s32 saveds = compiler->saveds; sljit_s32 scratches = compiler->scratches; + sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options); if (is_u12(local_size)) FAIL_IF(push_inst(compiler, 0x41000000 /* ly */ | R20A(r15) | R12A(r15) | (sljit_ins)local_size)); @@ -1727,8 +1791,13 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) offset = 2 * SSIZE_OF(sw); if (saveds + scratches >= SLJIT_NUMBER_OF_REGISTERS) { - FAIL_IF(push_inst(compiler, lmg(r6, r14, offset, r15))); /* save registers TODO(MGM): optimize */ - offset += 9 * SSIZE_OF(sw); + if (kept_saveds_count == 0) { + FAIL_IF(push_inst(compiler, lmg(r6, last_reg, offset, r15))); + offset += 9 * SSIZE_OF(sw); + } else { + FAIL_IF(push_inst(compiler, lmg(r6, r13 - (sljit_gpr)kept_saveds_count, offset, r15))); + offset += (8 - kept_saveds_count) * SSIZE_OF(sw); + } } else { if (scratches == SLJIT_FIRST_SAVED_REG) { FAIL_IF(push_inst(compiler, lg(r6, offset, 0, r15))); @@ -1738,15 +1807,35 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) offset += (scratches - (SLJIT_FIRST_SAVED_REG - 1)) * SSIZE_OF(sw); } - if (saveds == 0) { - FAIL_IF(push_inst(compiler, lg(r14, offset, 0, r15))); - offset += SSIZE_OF(sw); - } else { - FAIL_IF(push_inst(compiler, lmg(r14 - (sljit_gpr)saveds, r14, offset, r15))); - offset += (saveds + 1) * SSIZE_OF(sw); + if (kept_saveds_count == 0) { + if (saveds == 0) { + if (last_reg == r14) + FAIL_IF(push_inst(compiler, lg(r14, offset, 0, r15))); + offset += SSIZE_OF(sw); + } else if (saveds == 1 && last_reg == r13) { + FAIL_IF(push_inst(compiler, lg(r13, offset, 0, r15))); + offset += 2 * SSIZE_OF(sw); + } else { + FAIL_IF(push_inst(compiler, lmg(r14 - (sljit_gpr)saveds, last_reg, offset, r15))); + offset += (saveds + 1) * SSIZE_OF(sw); + } + } else if (saveds > kept_saveds_count) { + if (saveds == kept_saveds_count + 1) { + FAIL_IF(push_inst(compiler, lg(r14 - (sljit_gpr)saveds, offset, 0, r15))); + offset += SSIZE_OF(sw); + } else { + FAIL_IF(push_inst(compiler, lmg(r14 - (sljit_gpr)saveds, r13 - (sljit_gpr)kept_saveds_count, offset, r15))); + offset += (saveds - kept_saveds_count) * SSIZE_OF(sw); + } } } + if (kept_saveds_count > 0) { + if (last_reg == r14) + FAIL_IF(push_inst(compiler, lg(r14, offset, 0, r15))); + offset += SSIZE_OF(sw); + } + tmp = SLJIT_FS0 - compiler->fsaveds; for (i = SLJIT_FS0; i > tmp; i--) { FAIL_IF(push_inst(compiler, 0x68000000 /* ld */ | F20(i) | R12A(r15) | (sljit_ins)offset)); @@ -1766,10 +1855,33 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler CHECK_ERROR(); CHECK(check_sljit_emit_return_void(compiler)); - FAIL_IF(emit_stack_frame_release(compiler)); + FAIL_IF(emit_stack_frame_release(compiler, r14)); return push_inst(compiler, br(r14)); /* return */ } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_return_to(compiler, src, srcw)); + + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(load_word(compiler, tmp1, src, srcw, 0 /* 64-bit */)); + src = TMP_REG2; + srcw = 0; + } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + FAIL_IF(push_inst(compiler, lgr(tmp1, gpr(src)))); + src = TMP_REG2; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, r13)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); +} + /* --------------------------------------------------------------------- */ /* Operators */ /* --------------------------------------------------------------------- */ @@ -1858,6 +1970,47 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile return push_inst(compiler, lgr(arg1, tmp0)); } +static sljit_s32 sljit_emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 op, sljit_gpr dst_r, sljit_gpr src_r) +{ + sljit_s32 is_ctz = (GET_OPCODE(op) == SLJIT_CTZ); + + if ((op & SLJIT_32) && src_r != tmp0) { + FAIL_IF(push_inst(compiler, 0xb9160000 /* llgfr */ | R4A(tmp0) | R0A(src_r))); + src_r = tmp0; + } + + if (is_ctz) { + FAIL_IF(push_inst(compiler, ((op & SLJIT_32) ? 0x1300 /* lcr */ : 0xb9030000 /* lcgr */) | R4A(tmp1) | R0A(src_r))); + + if (src_r == tmp0) + FAIL_IF(push_inst(compiler, ((op & SLJIT_32) ? 0x1400 /* nr */ : 0xb9800000 /* ngr */) | R4A(tmp0) | R0A(tmp1))); + else + FAIL_IF(push_inst(compiler, 0xb9e40000 /* ngrk */ | R12A(tmp1) | R4A(tmp0) | R0A(src_r))); + + src_r = tmp0; + } + + FAIL_IF(push_inst(compiler, 0xb9830000 /* flogr */ | R4A(tmp0) | R0A(src_r))); + + if (is_ctz) + FAIL_IF(push_inst(compiler, 0xec00000000d9 /* aghik */ | R36A(tmp1) | R32A(tmp0) | ((sljit_ins)(-64 & 0xffff) << 16))); + + if (op & SLJIT_32) { + if (!is_ctz && dst_r != tmp0) + return push_inst(compiler, 0xec00000000d9 /* aghik */ | R36A(dst_r) | R32A(tmp0) | ((sljit_ins)(-32 & 0xffff) << 16)); + + FAIL_IF(push_inst(compiler, 0xc20800000000 /* agfi */ | R36A(tmp0) | (sljit_u32)-32)); + } + + if (is_ctz) + FAIL_IF(push_inst(compiler, 0xec0000000057 /* rxsbg */ | R36A(tmp0) | R32A(tmp1) | ((sljit_ins)((op & SLJIT_32) ? 59 : 58) << 24) | (63 << 16) | ((sljit_ins)((op & SLJIT_32) ? 5 : 6) << 8))); + + if (dst_r == tmp0) + return SLJIT_SUCCESS; + + return push_inst(compiler, ((op & SLJIT_32) ? 0x1800 /* lr */ : 0xb9040000 /* lgr */) | R4A(dst_r) | R0A(tmp0)); +} + /* LEVAL will be defined later with different parameters as needed */ #define WHEN2(cond, i1, i2) (cond) ? LEVAL(i1) : LEVAL(i2) @@ -2091,23 +2244,25 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile dst_r = FAST_IS_REG(dst) ? gpr(REG_MASK & dst) : tmp0; src_r = FAST_IS_REG(src) ? gpr(REG_MASK & src) : tmp0; - if (src & SLJIT_MEM) - FAIL_IF(load_word(compiler, src_r, src, srcw, src & SLJIT_32)); compiler->status_flags_state = op & (VARIABLE_FLAG_MASK | SLJIT_SET_Z); /* TODO(mundaym): optimize loads and stores */ - switch (opcode | (op & SLJIT_32)) { + switch (opcode) { case SLJIT_NOT: - /* emulate ~x with x^-1 */ - FAIL_IF(push_load_imm_inst(compiler, tmp1, -1)); - if (src_r != dst_r) - FAIL_IF(push_inst(compiler, lgr(dst_r, src_r))); + if (src & SLJIT_MEM) + FAIL_IF(load_word(compiler, src_r, src, srcw, op & SLJIT_32)); - FAIL_IF(push_inst(compiler, xgr(dst_r, tmp1))); - break; - case SLJIT_NOT32: /* emulate ~x with x^-1 */ + if (!(op & SLJIT_32)) { + FAIL_IF(push_load_imm_inst(compiler, tmp1, -1)); + if (src_r != dst_r) + FAIL_IF(push_inst(compiler, lgr(dst_r, src_r))); + + FAIL_IF(push_inst(compiler, xgr(dst_r, tmp1))); + break; + } + if (have_eimm()) FAIL_IF(push_inst(compiler, xilf(dst_r, 0xffffffff))); else { @@ -2119,24 +2274,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile } break; case SLJIT_CLZ: - if (have_eimm()) { - FAIL_IF(push_inst(compiler, flogr(tmp0, src_r))); /* clobbers tmp1 */ - if (dst_r != tmp0) - FAIL_IF(push_inst(compiler, lgr(dst_r, tmp0))); - } else { - abort(); /* TODO(mundaym): no eimm (?) */ - } - break; - case SLJIT_CLZ32: - if (have_eimm()) { - FAIL_IF(push_inst(compiler, sllg(tmp1, src_r, 32, 0))); - FAIL_IF(push_inst(compiler, iilf(tmp1, 0xffffffff))); - FAIL_IF(push_inst(compiler, flogr(tmp0, tmp1))); /* clobbers tmp1 */ - if (dst_r != tmp0) - FAIL_IF(push_inst(compiler, lr(dst_r, tmp0))); - } else { - abort(); /* TODO(mundaym): no eimm (?) */ - } + case SLJIT_CTZ: + if (src & SLJIT_MEM) + FAIL_IF(load_unsigned_word(compiler, src_r, src, srcw, op & SLJIT_32)); + + FAIL_IF(sljit_emit_clz_ctz(compiler, op, dst_r, src_r)); break; default: SLJIT_UNREACHABLE(); @@ -2145,9 +2287,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile if ((op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_SET_Z | SLJIT_SET_OVERFLOW)) FAIL_IF(update_zero_overflow(compiler, op, dst_r)); - /* TODO(carenas): doesn't need FAIL_IF */ if (dst & SLJIT_MEM) - FAIL_IF(store_word(compiler, dst_r, dst, dstw, op & SLJIT_32)); + return store_word(compiler, dst_r, dst, dstw, op & SLJIT_32); return SLJIT_SUCCESS; } @@ -2166,11 +2307,6 @@ static SLJIT_INLINE int is_commutative(sljit_s32 op) return 0; } -static SLJIT_INLINE int is_shift(sljit_s32 op) { - sljit_s32 v = GET_OPCODE(op); - return (v == SLJIT_SHL || v == SLJIT_ASHR || v == SLJIT_LSHR) ? 1 : 0; -} - static const struct ins_forms add_forms = { 0x1a00, /* ar */ 0xb9080000, /* agr */ @@ -2604,33 +2740,41 @@ static sljit_s32 sljit_emit_shift(struct sljit_compiler *compiler, sljit_s32 op, sljit_ins ins; if (FAST_IS_REG(src1)) - src_r = gpr(src1 & REG_MASK); + src_r = gpr(src1); else FAIL_IF(emit_move(compiler, tmp0, src1, src1w)); - if (src2 & SLJIT_IMM) + if (!(src2 & SLJIT_IMM)) { + if (FAST_IS_REG(src2)) + base_r = gpr(src2); + else { + FAIL_IF(emit_move(compiler, tmp1, src2, src2w)); + base_r = tmp1; + } + + if ((op & SLJIT_32) && (type == SLJIT_MSHL || type == SLJIT_MLSHR || type == SLJIT_MASHR)) { + if (base_r != tmp1) { + FAIL_IF(push_inst(compiler, 0xec0000000055 /* risbg */ | R36A(tmp1) | R32A(base_r) | (59 << 24) | (1 << 23) | (63 << 16))); + base_r = tmp1; + } else + FAIL_IF(push_inst(compiler, 0xa5070000 /* nill */ | R20A(tmp1) | 0x1f)); + } + } else imm = (sljit_ins)(src2w & ((op & SLJIT_32) ? 0x1f : 0x3f)); - else if (FAST_IS_REG(src2)) - base_r = gpr(src2 & REG_MASK); - else { - FAIL_IF(emit_move(compiler, tmp1, src2, src2w)); - base_r = tmp1; - } if ((op & SLJIT_32) && dst_r == src_r) { - if (type == SLJIT_SHL) + if (type == SLJIT_SHL || type == SLJIT_MSHL) ins = 0x89000000 /* sll */; - else if (type == SLJIT_LSHR) + else if (type == SLJIT_LSHR || type == SLJIT_MLSHR) ins = 0x88000000 /* srl */; else ins = 0x8a000000 /* sra */; FAIL_IF(push_inst(compiler, ins | R20A(dst_r) | R12A(base_r) | imm)); - } - else { - if (type == SLJIT_SHL) + } else { + if (type == SLJIT_SHL || type == SLJIT_MSHL) ins = (op & SLJIT_32) ? 0xeb00000000df /* sllk */ : 0xeb000000000d /* sllg */; - else if (type == SLJIT_LSHR) + else if (type == SLJIT_LSHR || type == SLJIT_MLSHR) ins = (op & SLJIT_32) ? 0xeb00000000de /* srlk */ : 0xeb000000000c /* srlg */; else ins = (op & SLJIT_32) ? 0xeb00000000dc /* srak */ : 0xeb000000000a /* srag */; @@ -2644,6 +2788,47 @@ static sljit_s32 sljit_emit_shift(struct sljit_compiler *compiler, sljit_s32 op, return SLJIT_SUCCESS; } +static sljit_s32 sljit_emit_rotate(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0; + sljit_gpr src_r = tmp0; + sljit_gpr base_r = tmp0; + sljit_ins imm = 0; + sljit_ins ins; + + if (FAST_IS_REG(src1)) + src_r = gpr(src1); + else + FAIL_IF(emit_move(compiler, tmp0, src1, src1w)); + + if (!(src2 & SLJIT_IMM)) { + if (FAST_IS_REG(src2)) + base_r = gpr(src2); + else { + FAIL_IF(emit_move(compiler, tmp1, src2, src2w)); + base_r = tmp1; + } + } + + if (GET_OPCODE(op) == SLJIT_ROTR) { + if (!(src2 & SLJIT_IMM)) { + ins = (op & SLJIT_32) ? 0x1300 /* lcr */ : 0xb9030000 /* lcgr */; + FAIL_IF(push_inst(compiler, ins | R4A(tmp1) | R0A(base_r))); + base_r = tmp1; + } else + src2w = -src2w; + } + + if (src2 & SLJIT_IMM) + imm = (sljit_ins)(src2w & ((op & SLJIT_32) ? 0x1f : 0x3f)); + + ins = (op & SLJIT_32) ? 0xeb000000001d /* rll */ : 0xeb000000001c /* rllg */; + return push_inst(compiler, ins | R36A(dst_r) | R32A(src_r) | R28A(base_r) | (imm << 16)); +} + static const struct ins_forms addc_forms = { 0xb9980000, /* alcr */ 0xb9880000, /* alcgr */ @@ -2716,10 +2901,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile FAIL_IF(sljit_emit_bitwise(compiler, op, dst, src1, src1w, src2, src2w)); break; case SLJIT_SHL: + case SLJIT_MSHL: case SLJIT_LSHR: + case SLJIT_MLSHR: case SLJIT_ASHR: + case SLJIT_MASHR: FAIL_IF(sljit_emit_shift(compiler, op, dst, src1, src1w, src2, src2w)); break; + case SLJIT_ROTL: + case SLJIT_ROTR: + FAIL_IF(sljit_emit_rotate(compiler, op, dst, src1, src1w, src2, src2w)); + break; } if (dst & SLJIT_MEM) @@ -2734,18 +2926,130 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compil CHECK_ERROR(); CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_op2(compiler, op, (sljit_s32)tmp0, 0, src1, src1w, src2, src2w); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_s32 is_right; + sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64; + sljit_gpr src_dst_r = gpr(src_dst); + sljit_gpr src1_r = tmp0; + sljit_gpr src2_r = tmp1; + sljit_ins ins; + + CHECK_ERROR(); + CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w)); + + is_right = (GET_OPCODE(op) == SLJIT_LSHR || GET_OPCODE(op) == SLJIT_MLSHR); + + if (src_dst == src1) { + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_op2(compiler, (is_right ? SLJIT_ROTR : SLJIT_ROTL) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w); + } + + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + + if (src1 & SLJIT_MEM) + FAIL_IF(load_word(compiler, tmp0, src1, src1w, op & SLJIT_32)); + else if (src1 & SLJIT_IMM) + FAIL_IF(push_load_imm_inst(compiler, tmp0, src1w)); + else + src1_r = gpr(src1); + + if (src2 & SLJIT_IMM) { + src2w &= bit_length - 1; + + if (src2w == 0) + return SLJIT_SUCCESS; + } else if (!(src2 & SLJIT_MEM)) + src2_r = gpr(src2); + else + FAIL_IF(load_word(compiler, tmp1, src2, src2w, op & SLJIT_32)); + + if (src2 & SLJIT_IMM) { + if (op & SLJIT_32) { + ins = is_right ? 0x88000000 /* srl */ : 0x89000000 /* sll */; + FAIL_IF(push_inst(compiler, ins | R20A(src_dst_r) | (sljit_ins)src2w)); + } else { + ins = is_right ? 0xeb000000000c /* srlg */ : 0xeb000000000d /* sllg */; + FAIL_IF(push_inst(compiler, ins | R36A(src_dst_r) | R32A(src_dst_r) | ((sljit_ins)src2w << 16))); + } + + ins = 0xec0000000055 /* risbg */; + + if (is_right) { + src2w = bit_length - src2w; + ins |= ((sljit_ins)(64 - bit_length) << 24) | ((sljit_ins)(63 - src2w) << 16) | ((sljit_ins)src2w << 8); + } else + ins |= ((sljit_ins)(64 - src2w) << 24) | ((sljit_ins)63 << 16) | ((sljit_ins)src2w << 8); + + return push_inst(compiler, ins | R36A(src_dst_r) | R32A(src1_r)); + } + + if (op & SLJIT_32) { + if (GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR) { + if (src2_r != tmp1) { + FAIL_IF(push_inst(compiler, 0xec0000000055 /* risbg */ | R36A(tmp1) | R32A(src2_r) | (59 << 24) | (1 << 23) | (63 << 16))); + src2_r = tmp1; + } else + FAIL_IF(push_inst(compiler, 0xa5070000 /* nill */ | R20A(tmp1) | 0x1f)); + } + + ins = is_right ? 0x88000000 /* srl */ : 0x89000000 /* sll */; + FAIL_IF(push_inst(compiler, ins | R20A(src_dst_r) | R12A(src2_r))); + + if (src2_r != tmp1) { + FAIL_IF(push_inst(compiler, 0xa50f0000 /* llill */ | R20A(tmp1) | 0x1f)); + FAIL_IF(push_inst(compiler, 0x1700 /* xr */ | R4A(tmp1) | R0A(src2_r))); + } else + FAIL_IF(push_inst(compiler, 0xc00700000000 /* xilf */ | R36A(tmp1) | 0x1f)); + + if (src1_r == tmp0) { + ins = is_right ? 0x89000000 /* sll */ : 0x88000000 /* srl */; + FAIL_IF(push_inst(compiler, ins | R20A(tmp0) | R12A(tmp1) | 0x1)); + } else { + ins = is_right ? 0xeb00000000df /* sllk */ : 0xeb00000000de /* srlk */; + FAIL_IF(push_inst(compiler, ins | R36A(tmp0) | R32A(src1_r) | R28A(tmp1) | (0x1 << 16))); + } + + return push_inst(compiler, 0x1600 /* or */ | R4A(src_dst_r) | R0A(tmp0)); + } + + ins = is_right ? 0xeb000000000c /* srlg */ : 0xeb000000000d /* sllg */; + FAIL_IF(push_inst(compiler, ins | R36A(src_dst_r) | R32A(src_dst_r) | R28A(src2_r))); + + ins = is_right ? 0xeb000000000d /* sllg */ : 0xeb000000000c /* srlg */; + + if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) { + if (src2_r != tmp1) + FAIL_IF(push_inst(compiler, 0xa50f0000 /* llill */ | R20A(tmp1) | 0x3f)); + + FAIL_IF(push_inst(compiler, ins | R36A(tmp0) | R32A(src1_r) | (0x1 << 16))); + src1_r = tmp0; + + if (src2_r != tmp1) + FAIL_IF(push_inst(compiler, 0xb9820000 /* xgr */ | R4A(tmp1) | R0A(src2_r))); + else + FAIL_IF(push_inst(compiler, 0xc00700000000 /* xilf */ | R36A(tmp1) | 0x3f)); + } else + FAIL_IF(push_inst(compiler, 0xb9030000 /* lcgr */ | R4A(tmp1) | R0A(src2_r))); + + FAIL_IF(push_inst(compiler, ins | R36A(tmp0) | R32A(src1_r) | R28A(tmp1))); + return push_inst(compiler, 0xb9810000 /* ogr */ | R4A(src_dst_r) | R0A(tmp0)); +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src( struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { sljit_gpr src_r; + struct addr addr; CHECK_ERROR(); CHECK(check_sljit_emit_op_src(compiler, op, src, srcw)); @@ -2759,16 +3063,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src( return push_inst(compiler, br(src_r)); case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN: - /* TODO(carenas): implement? */ return SLJIT_SUCCESS; case SLJIT_PREFETCH_L1: case SLJIT_PREFETCH_L2: case SLJIT_PREFETCH_L3: case SLJIT_PREFETCH_ONCE: - /* TODO(carenas): implement */ - return SLJIT_SUCCESS; + FAIL_IF(make_addr_bxy(compiler, &addr, src, srcw, tmp1)); + return push_inst(compiler, 0xe31000000036 /* pfd */ | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset)); default: - /* TODO(carenas): probably should not success by default */ return SLJIT_SUCCESS; } @@ -3064,10 +3366,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * ADJUST_LOCAL_OFFSET(dst, dstw); if (FAST_IS_REG(dst)) - return push_inst(compiler, lgr(gpr(dst), fast_link_r)); + return push_inst(compiler, lgr(gpr(dst), link_r)); /* memory */ - return store_word(compiler, fast_link_r, dst, dstw, 0); + return store_word(compiler, link_r, dst, dstw, 0); } /* --------------------------------------------------------------------- */ @@ -3107,7 +3409,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile /* emit jump instruction */ type &= 0xff; if (type >= SLJIT_FAST_CALL) - PTR_FAIL_IF(push_inst(compiler, brasl(type == SLJIT_FAST_CALL ? fast_link_r : link_r, 0))); + PTR_FAIL_IF(push_inst(compiler, brasl(link_r, 0))); else PTR_FAIL_IF(push_inst(compiler, brcl(mask, 0))); @@ -3117,19 +3419,16 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 arg_types) { + SLJIT_UNUSED_ARG(arg_types); CHECK_ERROR_PTR(); CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); if (type & SLJIT_CALL_RETURN) { - PTR_FAIL_IF(emit_stack_frame_release(compiler)); + PTR_FAIL_IF(emit_stack_frame_release(compiler, r14)); type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); } -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_jump(compiler, type); } @@ -3151,7 +3450,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi /* emit jump instruction */ if (type >= SLJIT_FAST_CALL) - return push_inst(compiler, basr(type == SLJIT_FAST_CALL ? fast_link_r : link_r, src_r)); + return push_inst(compiler, basr(link_r, src_r)); return push_inst(compiler, br(src_r)); } @@ -3169,23 +3468,21 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi ADJUST_LOCAL_OFFSET(src, srcw); FAIL_IF(load_word(compiler, tmp1, src, srcw, 0 /* 64-bit */)); src = TMP_REG2; + srcw = 0; } if (type & SLJIT_CALL_RETURN) { - if (src >= SLJIT_FIRST_SAVED_REG && src <= SLJIT_S0) { + if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { FAIL_IF(push_inst(compiler, lgr(tmp1, gpr(src)))); src = TMP_REG2; + srcw = 0; } - FAIL_IF(emit_stack_frame_release(compiler)); + FAIL_IF(emit_stack_frame_release(compiler, r14)); type = SLJIT_JUMP; } -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_ijump(compiler, type, src, srcw); } @@ -3193,7 +3490,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co sljit_s32 dst, sljit_sw dstw, sljit_s32 type) { - sljit_u8 mask = get_cc(compiler, type & 0xff); + sljit_u8 mask = get_cc(compiler, type); CHECK_ERROR(); CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); @@ -3263,27 +3560,92 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil sljit_s32 dst_reg, sljit_s32 src, sljit_sw srcw) { - sljit_u8 mask = get_cc(compiler, type & 0xff); - sljit_gpr dst_r = gpr(dst_reg & ~SLJIT_32); - sljit_gpr src_r = FAST_IS_REG(src) ? gpr(src) : tmp0; + sljit_ins mask = get_cc(compiler, type & ~SLJIT_32); + sljit_gpr src_r; + sljit_ins ins; CHECK_ERROR(); CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); + if (type & SLJIT_32) + srcw = (sljit_s32)srcw; + + if (have_lscond2() && (src & SLJIT_IMM) && is_s16(srcw)) { + ins = (type & SLJIT_32) ? 0xec0000000042 /* lochi */ : 0xec0000000046 /* locghi */; + return push_inst(compiler, ins | R36A(gpr(dst_reg)) | (mask << 32) | (sljit_ins)(srcw & 0xffff) << 16); + } + if (src & SLJIT_IMM) { - /* TODO(mundaym): fast path with lscond2 */ - FAIL_IF(push_load_imm_inst(compiler, src_r, srcw)); + FAIL_IF(push_load_imm_inst(compiler, tmp0, srcw)); + src_r = tmp0; + } else + src_r = gpr(src); + + if (have_lscond1()) { + ins = (type & SLJIT_32) ? 0xb9f20000 /* locr */ : 0xb9e20000 /* locgr */; + return push_inst(compiler, ins | (mask << 12) | R4A(gpr(dst_reg)) | R0A(src_r)); } - #define LEVAL(i) i(dst_r, src_r, mask) - if (have_lscond1()) - return push_inst(compiler, - WHEN2(dst_reg & SLJIT_32, locr, locgr)); + return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw); +} - #undef LEVAL +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_ins ins, reg1, reg2, base, offs = 0; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + + if (!(reg & REG_PAIR_MASK)) + return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw); + + ADJUST_LOCAL_OFFSET(mem, memw); + + base = gpr(mem & REG_MASK); + reg1 = gpr(REG_PAIR_FIRST(reg)); + reg2 = gpr(REG_PAIR_SECOND(reg)); + + if (mem & OFFS_REG_MASK) { + memw &= 0x3; + offs = gpr(OFFS_REG(mem)); + + if (memw != 0) { + FAIL_IF(push_inst(compiler, 0xeb000000000d /* sllg */ | R36A(tmp1) | R32A(offs) | ((sljit_ins)memw << 16))); + offs = tmp1; + } else if (!(type & SLJIT_MEM_STORE) && (base == reg1 || base == reg2) && (offs == reg1 || offs == reg2)) { + FAIL_IF(push_inst(compiler, 0xb9f80000 | R12A(tmp1) | R4A(base) | R0A(offs))); + base = tmp1; + offs = 0; + } + + memw = 0; + } else if (memw < -0x80000 || memw > 0x7ffff - ((reg2 == reg1 + 1) ? 0 : SSIZE_OF(sw))) { + FAIL_IF(push_load_imm_inst(compiler, tmp1, memw)); + + if (base == 0) + base = tmp1; + else + offs = tmp1; + + memw = 0; + } + + if (offs == 0 && reg2 == (reg1 + 1)) { + ins = (type & SLJIT_MEM_STORE) ? 0xeb0000000024 /* stmg */ : 0xeb0000000004 /* lmg */; + return push_inst(compiler, ins | R36A(reg1) | R32A(reg2) | R28A(base) | disp_s20((sljit_s32)memw)); + } + + ins = ((type & SLJIT_MEM_STORE) ? 0xe30000000024 /* stg */ : 0xe30000000004 /* lg */) | R32A(offs) | R28A(base); + + if (!(type & SLJIT_MEM_STORE) && base == reg1) { + FAIL_IF(push_inst(compiler, ins | R36A(reg2) | disp_s20((sljit_s32)memw + SSIZE_OF(sw)))); + return push_inst(compiler, ins | R36A(reg1) | disp_s20((sljit_s32)memw)); + } - /* TODO(mundaym): implement */ - return SLJIT_ERR_UNSUPPORTED; + FAIL_IF(push_inst(compiler, ins | R36A(reg1) | disp_s20((sljit_s32)memw))); + return push_inst(compiler, ins | R36A(reg2) | disp_s20((sljit_s32)memw + SSIZE_OF(sw))); } /* --------------------------------------------------------------------- */ diff --git a/thirdparty/pcre2/src/sljit/sljitNativeSPARC_32.c b/thirdparty/pcre2/src/sljit/sljitNativeSPARC_32.c deleted file mode 100644 index 218992b355..0000000000 --- a/thirdparty/pcre2/src/sljit/sljitNativeSPARC_32.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Stack-less Just-In-Time compiler - * - * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw imm) -{ - if (imm <= SIMM_MAX && imm >= SIMM_MIN) - return push_inst(compiler, OR | D(dst) | S1(0) | IMM(imm), DR(dst)); - - FAIL_IF(push_inst(compiler, SETHI | D(dst) | ((imm >> 10) & 0x3fffff), DR(dst))); - return (imm & 0x3ff) ? push_inst(compiler, OR | D(dst) | S1(dst) | IMM_ARG | (imm & 0x3ff), DR(dst)) : SLJIT_SUCCESS; -} - -#define ARG2(flags, src2) ((flags & SRC2_IMM) ? IMM(src2) : S2(src2)) - -static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_u32 flags, - sljit_s32 dst, sljit_s32 src1, sljit_sw src2) -{ - SLJIT_COMPILE_ASSERT(ICC_IS_SET == SET_FLAGS, icc_is_set_and_set_flags_must_be_the_same); - - switch (op) { - case SLJIT_MOV: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if (dst != src2) - return push_inst(compiler, OR | D(dst) | S1(0) | S2(src2), DR(dst)); - return SLJIT_SUCCESS; - - case SLJIT_MOV_U8: - case SLJIT_MOV_S8: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { - if (op == SLJIT_MOV_U8) - return push_inst(compiler, AND | D(dst) | S1(src2) | IMM(0xff), DR(dst)); - FAIL_IF(push_inst(compiler, SLL | D(dst) | S1(src2) | IMM(24), DR(dst))); - return push_inst(compiler, SRA | D(dst) | S1(dst) | IMM(24), DR(dst)); - } - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_MOV_U16: - case SLJIT_MOV_S16: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { - FAIL_IF(push_inst(compiler, SLL | D(dst) | S1(src2) | IMM(16), DR(dst))); - return push_inst(compiler, (op == SLJIT_MOV_S16 ? SRA : SRL) | D(dst) | S1(dst) | IMM(16), DR(dst)); - } - SLJIT_ASSERT(dst == src2); - return SLJIT_SUCCESS; - - case SLJIT_NOT: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - return push_inst(compiler, XNOR | (flags & SET_FLAGS) | D(dst) | S1(0) | S2(src2), DRF(dst, flags)); - - case SLJIT_CLZ: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - FAIL_IF(push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(src2) | S2(0), SET_FLAGS)); - FAIL_IF(push_inst(compiler, OR | D(TMP_REG1) | S1(0) | S2(src2), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, BICC | DA(0x1) | (7 & DISP_MASK), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, OR | D(dst) | S1(0) | IMM(32), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, OR | D(dst) | S1(0) | IMM(-1), DR(dst))); - - /* Loop. */ - FAIL_IF(push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(TMP_REG1) | S2(0), SET_FLAGS)); - FAIL_IF(push_inst(compiler, SLL | D(TMP_REG1) | S1(TMP_REG1) | IMM(1), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, BICC | DA(0xe) | ((sljit_ins)-2 & DISP_MASK), UNMOVABLE_INS)); - return push_inst(compiler, ADD | D(dst) | S1(dst) | IMM(1), UNMOVABLE_INS); - - case SLJIT_ADD: - compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD; - return push_inst(compiler, ADD | (flags & SET_FLAGS) | D(dst) | S1(src1) | ARG2(flags, src2), DRF(dst, flags)); - - case SLJIT_ADDC: - compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD; - return push_inst(compiler, ADDC | (flags & SET_FLAGS) | D(dst) | S1(src1) | ARG2(flags, src2), DRF(dst, flags)); - - case SLJIT_SUB: - compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB; - return push_inst(compiler, SUB | (flags & SET_FLAGS) | D(dst) | S1(src1) | ARG2(flags, src2), DRF(dst, flags)); - - case SLJIT_SUBC: - compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB; - return push_inst(compiler, SUBC | (flags & SET_FLAGS) | D(dst) | S1(src1) | ARG2(flags, src2), DRF(dst, flags)); - - case SLJIT_MUL: - compiler->status_flags_state = 0; - FAIL_IF(push_inst(compiler, SMUL | D(dst) | S1(src1) | ARG2(flags, src2), DR(dst))); - if (!(flags & SET_FLAGS)) - return SLJIT_SUCCESS; - FAIL_IF(push_inst(compiler, SRA | D(TMP_REG1) | S1(dst) | IMM(31), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, RDY | D(TMP_LINK), DR(TMP_LINK))); - return push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(TMP_REG1) | S2(TMP_LINK), MOVABLE_INS | SET_FLAGS); - - case SLJIT_AND: - return push_inst(compiler, AND | (flags & SET_FLAGS) | D(dst) | S1(src1) | ARG2(flags, src2), DRF(dst, flags)); - - case SLJIT_OR: - return push_inst(compiler, OR | (flags & SET_FLAGS) | D(dst) | S1(src1) | ARG2(flags, src2), DRF(dst, flags)); - - case SLJIT_XOR: - return push_inst(compiler, XOR | (flags & SET_FLAGS) | D(dst) | S1(src1) | ARG2(flags, src2), DRF(dst, flags)); - - case SLJIT_SHL: - FAIL_IF(push_inst(compiler, SLL | D(dst) | S1(src1) | ARG2(flags, src2), DR(dst))); - return !(flags & SET_FLAGS) ? SLJIT_SUCCESS : push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(dst) | S2(0), SET_FLAGS); - - case SLJIT_LSHR: - FAIL_IF(push_inst(compiler, SRL | D(dst) | S1(src1) | ARG2(flags, src2), DR(dst))); - return !(flags & SET_FLAGS) ? SLJIT_SUCCESS : push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(dst) | S2(0), SET_FLAGS); - - case SLJIT_ASHR: - FAIL_IF(push_inst(compiler, SRA | D(dst) | S1(src1) | ARG2(flags, src2), DR(dst))); - return !(flags & SET_FLAGS) ? SLJIT_SUCCESS : push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(dst) | S2(0), SET_FLAGS); - } - - SLJIT_UNREACHABLE(); - return SLJIT_SUCCESS; -} - -static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src) -{ - sljit_s32 reg_index = 8; - sljit_s32 word_reg_index = 8; - sljit_s32 float_arg_index = 1; - sljit_s32 double_arg_count = 0; - sljit_u32 float_offset = (16 + 6) * sizeof(sljit_sw); - sljit_s32 types = 0; - sljit_s32 reg = 0; - sljit_s32 move_to_tmp2 = 0; - - if (src) - reg = reg_map[*src & REG_MASK]; - - arg_types >>= SLJIT_ARG_SHIFT; - - while (arg_types) { - types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK); - - switch (arg_types & SLJIT_ARG_MASK) { - case SLJIT_ARG_TYPE_F64: - float_arg_index++; - double_arg_count++; - if (reg_index == reg || reg_index + 1 == reg) - move_to_tmp2 = 1; - reg_index += 2; - break; - case SLJIT_ARG_TYPE_F32: - float_arg_index++; - if (reg_index == reg) - move_to_tmp2 = 1; - reg_index++; - break; - default: - if (reg_index != word_reg_index && reg_index == reg) - move_to_tmp2 = 1; - reg_index++; - word_reg_index++; - break; - } - - arg_types >>= SLJIT_ARG_SHIFT; - } - - if (move_to_tmp2) { - if (reg < 14) - FAIL_IF(push_inst(compiler, OR | D(TMP_REG1) | S1(0) | S2A(reg), DR(TMP_REG1))); - *src = TMP_REG1; - } - - arg_types = types; - - while (arg_types) { - switch (arg_types & SLJIT_ARG_MASK) { - case SLJIT_ARG_TYPE_F64: - float_arg_index--; - if (float_arg_index == 4 && double_arg_count == 4) { - /* The address is not doubleword aligned, so two instructions are required to store the double. */ - FAIL_IF(push_inst(compiler, STF | FD(float_arg_index) | S1(SLJIT_SP) | IMM((16 + 7) * sizeof(sljit_sw)), MOVABLE_INS)); - FAIL_IF(push_inst(compiler, STF | FD(float_arg_index) | (1 << 25) | S1(SLJIT_SP) | IMM((16 + 8) * sizeof(sljit_sw)), MOVABLE_INS)); - } - else - FAIL_IF(push_inst(compiler, STDF | FD(float_arg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS)); - float_offset -= sizeof(sljit_f64); - break; - case SLJIT_ARG_TYPE_F32: - float_arg_index--; - FAIL_IF(push_inst(compiler, STF | FD(float_arg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS)); - float_offset -= sizeof(sljit_f64); - break; - default: - break; - } - - arg_types >>= SLJIT_ARG_SHIFT; - } - - float_offset = (16 + 6) * sizeof(sljit_sw); - - while (types) { - switch (types & SLJIT_ARG_MASK) { - case SLJIT_ARG_TYPE_F64: - reg_index -= 2; - if (reg_index < 14) { - if ((reg_index & 0x1) != 0) { - FAIL_IF(push_inst(compiler, LDUW | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), reg_index)); - if (reg_index < 8 + 6 - 1) - FAIL_IF(push_inst(compiler, LDUW | DA(reg_index + 1) | S1(SLJIT_SP) | IMM(float_offset + sizeof(sljit_sw)), reg_index + 1)); - } - else - FAIL_IF(push_inst(compiler, LDD | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), reg_index)); - } - float_offset -= sizeof(sljit_f64); - break; - case SLJIT_ARG_TYPE_F32: - reg_index--; - if (reg_index < 8 + 6) - FAIL_IF(push_inst(compiler, LDUW | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), reg_index)); - float_offset -= sizeof(sljit_f64); - break; - default: - reg_index--; - word_reg_index--; - - if (reg_index != word_reg_index) { - if (reg_index < 14) - FAIL_IF(push_inst(compiler, OR | DA(reg_index) | S1(0) | S2A(word_reg_index), reg_index)); - else - FAIL_IF(push_inst(compiler, STW | DA(word_reg_index) | S1(SLJIT_SP) | IMM(92), word_reg_index)); - } - break; - } - - types >>= SLJIT_ARG_SHIFT; - } - - return SLJIT_SUCCESS; -} - -static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value) -{ - FAIL_IF(push_inst(compiler, SETHI | D(dst) | ((init_value >> 10) & 0x3fffff), DR(dst))); - return push_inst(compiler, OR | D(dst) | S1(dst) | IMM_ARG | (init_value & 0x3ff), DR(dst)); -} - -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) -{ - sljit_ins *inst = (sljit_ins *)addr; - SLJIT_UNUSED_ARG(executable_offset); - - SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0); - SLJIT_ASSERT(((inst[0] & 0xc1c00000) == 0x01000000) && ((inst[1] & 0xc1f82000) == 0x80102000)); - inst[0] = (inst[0] & 0xffc00000) | ((new_target >> 10) & 0x3fffff); - inst[1] = (inst[1] & 0xfffffc00) | (new_target & 0x3ff); - SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1); - inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); - SLJIT_CACHE_FLUSH(inst, inst + 2); -} - -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) -{ - sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset); -} diff --git a/thirdparty/pcre2/src/sljit/sljitNativeSPARC_common.c b/thirdparty/pcre2/src/sljit/sljitNativeSPARC_common.c deleted file mode 100644 index c8d19e16c6..0000000000 --- a/thirdparty/pcre2/src/sljit/sljitNativeSPARC_common.c +++ /dev/null @@ -1,1673 +0,0 @@ -/* - * Stack-less Just-In-Time compiler - * - * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) -{ - return "SPARC" SLJIT_CPUINFO; -} - -/* Length of an instruction word - Both for sparc-32 and sparc-64 */ -typedef sljit_u32 sljit_ins; - -#if (defined SLJIT_CACHE_FLUSH_OWN_IMPL && SLJIT_CACHE_FLUSH_OWN_IMPL) - -static void sparc_cache_flush(sljit_ins *from, sljit_ins *to) -{ -#if defined(__SUNPRO_C) && __SUNPRO_C < 0x590 - __asm ( - /* if (from == to) return */ - "cmp %i0, %i1\n" - "be .leave\n" - "nop\n" - - /* loop until from >= to */ - ".mainloop:\n" - "flush %i0\n" - "add %i0, 8, %i0\n" - "cmp %i0, %i1\n" - "bcs .mainloop\n" - "nop\n" - - /* The comparison was done above. */ - "bne .leave\n" - /* nop is not necessary here, since the - sub operation has no side effect. */ - "sub %i0, 4, %i0\n" - "flush %i0\n" - ".leave:" - ); -#else - if (SLJIT_UNLIKELY(from == to)) - return; - - do { - __asm__ volatile ( - "flush %0\n" - : : "r"(from) - ); - /* Operates at least on doubleword. */ - from += 2; - } while (from < to); - - if (from == to) { - /* Flush the last word. */ - from --; - __asm__ volatile ( - "flush %0\n" - : : "r"(from) - ); - } -#endif -} - -#endif /* (defined SLJIT_CACHE_FLUSH_OWN_IMPL && SLJIT_CACHE_FLUSH_OWN_IMPL) */ - -/* TMP_REG2 is not used by getput_arg */ -#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2) -#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3) -#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4) -/* This register is modified by calls, which affects the instruction - in the delay slot if it is used as a source register. */ -#define TMP_LINK (SLJIT_NUMBER_OF_REGISTERS + 5) - -#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) -#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2) - -static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { - 0, 8, 9, 10, 11, 23, 22, 21, 20, 19, 18, 17, 16, 29, 28, 27, 26, 25, 24, 14, 1, 12, 13, 15 -}; - -static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { - 0, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 -}; - -/* --------------------------------------------------------------------- */ -/* Instrucion forms */ -/* --------------------------------------------------------------------- */ - -#define D(d) ((sljit_ins)reg_map[d] << 25) -#define FD(d) ((sljit_ins)freg_map[d] << 25) -#define FDN(d) (((sljit_ins)freg_map[d] | 0x1) << 25) -#define DA(d) ((sljit_ins)(d) << 25) -#define S1(s1) ((sljit_ins)reg_map[s1] << 14) -#define FS1(s1) ((sljit_ins)freg_map[s1] << 14) -#define S1A(s1) ((sljit_ins)(s1) << 14) -#define S2(s2) ((sljit_ins)reg_map[s2]) -#define FS2(s2) ((sljit_ins)freg_map[s2]) -#define FS2N(s2) ((sljit_ins)freg_map[s2] | 0x1) -#define S2A(s2) ((sljit_ins)(s2)) -#define IMM_ARG 0x2000 -#define DOP(op) ((sljit_ins)(op) << 5) -#define IMM(imm) (((sljit_ins)(imm) & 0x1fff) | IMM_ARG) - -#define DR(dr) (reg_map[dr]) -#define DRF(dr, flags) ((sljit_s32)(reg_map[dr] | ((flags) & SET_FLAGS))) -#define OPC1(opcode) ((sljit_ins)(opcode) << 30) -#define OPC2(opcode) ((sljit_ins)(opcode) << 22) -#define OPC3(opcode) ((sljit_ins)(opcode) << 19) -#define SET_FLAGS OPC3(0x10) - -#define ADD (OPC1(0x2) | OPC3(0x00)) -#define ADDC (OPC1(0x2) | OPC3(0x08)) -#define AND (OPC1(0x2) | OPC3(0x01)) -#define ANDN (OPC1(0x2) | OPC3(0x05)) -#define CALL (OPC1(0x1)) -#define FABSS (OPC1(0x2) | OPC3(0x34) | DOP(0x09)) -#define FADDD (OPC1(0x2) | OPC3(0x34) | DOP(0x42)) -#define FADDS (OPC1(0x2) | OPC3(0x34) | DOP(0x41)) -#define FCMPD (OPC1(0x2) | OPC3(0x35) | DOP(0x52)) -#define FCMPS (OPC1(0x2) | OPC3(0x35) | DOP(0x51)) -#define FDIVD (OPC1(0x2) | OPC3(0x34) | DOP(0x4e)) -#define FDIVS (OPC1(0x2) | OPC3(0x34) | DOP(0x4d)) -#define FDTOI (OPC1(0x2) | OPC3(0x34) | DOP(0xd2)) -#define FDTOS (OPC1(0x2) | OPC3(0x34) | DOP(0xc6)) -#define FITOD (OPC1(0x2) | OPC3(0x34) | DOP(0xc8)) -#define FITOS (OPC1(0x2) | OPC3(0x34) | DOP(0xc4)) -#define FMOVS (OPC1(0x2) | OPC3(0x34) | DOP(0x01)) -#define FMULD (OPC1(0x2) | OPC3(0x34) | DOP(0x4a)) -#define FMULS (OPC1(0x2) | OPC3(0x34) | DOP(0x49)) -#define FNEGS (OPC1(0x2) | OPC3(0x34) | DOP(0x05)) -#define FSTOD (OPC1(0x2) | OPC3(0x34) | DOP(0xc9)) -#define FSTOI (OPC1(0x2) | OPC3(0x34) | DOP(0xd1)) -#define FSUBD (OPC1(0x2) | OPC3(0x34) | DOP(0x46)) -#define FSUBS (OPC1(0x2) | OPC3(0x34) | DOP(0x45)) -#define JMPL (OPC1(0x2) | OPC3(0x38)) -#define LDD (OPC1(0x3) | OPC3(0x03)) -#define LDDF (OPC1(0x3) | OPC3(0x23)) -#define LDF (OPC1(0x3) | OPC3(0x20)) -#define LDUW (OPC1(0x3) | OPC3(0x00)) -#define NOP (OPC1(0x0) | OPC2(0x04)) -#define OR (OPC1(0x2) | OPC3(0x02)) -#define ORN (OPC1(0x2) | OPC3(0x06)) -#define RDY (OPC1(0x2) | OPC3(0x28) | S1A(0)) -#define RESTORE (OPC1(0x2) | OPC3(0x3d)) -#define SAVE (OPC1(0x2) | OPC3(0x3c)) -#define SETHI (OPC1(0x0) | OPC2(0x04)) -#define SLL (OPC1(0x2) | OPC3(0x25)) -#define SLLX (OPC1(0x2) | OPC3(0x25) | (1 << 12)) -#define SRA (OPC1(0x2) | OPC3(0x27)) -#define SRAX (OPC1(0x2) | OPC3(0x27) | (1 << 12)) -#define SRL (OPC1(0x2) | OPC3(0x26)) -#define SRLX (OPC1(0x2) | OPC3(0x26) | (1 << 12)) -#define STD (OPC1(0x3) | OPC3(0x07)) -#define STDF (OPC1(0x3) | OPC3(0x27)) -#define STF (OPC1(0x3) | OPC3(0x24)) -#define STW (OPC1(0x3) | OPC3(0x04)) -#define SUB (OPC1(0x2) | OPC3(0x04)) -#define SUBC (OPC1(0x2) | OPC3(0x0c)) -#define TA (OPC1(0x2) | OPC3(0x3a) | (8 << 25)) -#define WRY (OPC1(0x2) | OPC3(0x30) | DA(0)) -#define XOR (OPC1(0x2) | OPC3(0x03)) -#define XNOR (OPC1(0x2) | OPC3(0x07)) - -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) -#define MAX_DISP (0x1fffff) -#define MIN_DISP (-0x200000) -#define DISP_MASK ((sljit_ins)0x3fffff) - -#define BICC (OPC1(0x0) | OPC2(0x2)) -#define FBFCC (OPC1(0x0) | OPC2(0x6)) -#define SLL_W SLL -#define SDIV (OPC1(0x2) | OPC3(0x0f)) -#define SMUL (OPC1(0x2) | OPC3(0x0b)) -#define UDIV (OPC1(0x2) | OPC3(0x0e)) -#define UMUL (OPC1(0x2) | OPC3(0x0a)) -#else -#define SLL_W SLLX -#endif - -#define SIMM_MAX (0x0fff) -#define SIMM_MIN (-0x1000) - -/* dest_reg is the absolute name of the register - Useful for reordering instructions in the delay slot. */ -static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins, sljit_s32 delay_slot) -{ - sljit_ins *ptr; - SLJIT_ASSERT((delay_slot & DST_INS_MASK) == UNMOVABLE_INS - || (delay_slot & DST_INS_MASK) == MOVABLE_INS - || (delay_slot & DST_INS_MASK) == ((ins >> 25) & 0x1f)); - ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins)); - FAIL_IF(!ptr); - *ptr = ins; - compiler->size++; - compiler->delay_slot = delay_slot; - return SLJIT_SUCCESS; -} - -static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code, sljit_sw executable_offset) -{ - sljit_sw diff; - sljit_uw target_addr; - sljit_ins *inst; - sljit_ins saved_inst; - - if (jump->flags & SLJIT_REWRITABLE_JUMP) - return code_ptr; - - if (jump->flags & JUMP_ADDR) - target_addr = jump->u.target; - else { - SLJIT_ASSERT(jump->flags & JUMP_LABEL); - target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset; - } - inst = (sljit_ins*)jump->addr; - -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - if (jump->flags & IS_CALL) { - /* Call is always patchable on sparc 32. */ - jump->flags |= PATCH_CALL; - if (jump->flags & IS_MOVABLE) { - inst[0] = inst[-1]; - inst[-1] = CALL; - jump->addr -= sizeof(sljit_ins); - return inst; - } - inst[0] = CALL; - inst[1] = NOP; - return inst + 1; - } -#else - /* Both calls and BPr instructions shall not pass this point. */ -#error "Implementation required" -#endif - - if (jump->flags & IS_COND) - inst--; - - diff = ((sljit_sw)target_addr - (sljit_sw)(inst - 1) - executable_offset) >> 2; - - if (jump->flags & IS_MOVABLE) { - if (diff <= MAX_DISP && diff >= MIN_DISP) { - jump->flags |= PATCH_B; - inst--; - if (jump->flags & IS_COND) { - saved_inst = inst[0]; - inst[0] = inst[1] ^ (1 << 28); - inst[1] = saved_inst; - } else { - inst[1] = inst[0]; - inst[0] = BICC | DA(0x8); - } - jump->addr = (sljit_uw)inst; - return inst + 1; - } - } - - diff += SSIZE_OF(ins); - - if (diff <= MAX_DISP && diff >= MIN_DISP) { - jump->flags |= PATCH_B; - if (jump->flags & IS_COND) - inst[0] ^= (1 << 28); - else - inst[0] = BICC | DA(0x8); - inst[1] = NOP; - jump->addr = (sljit_uw)inst; - return inst + 1; - } - - return code_ptr; -} - -SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) -{ - struct sljit_memory_fragment *buf; - sljit_ins *code; - sljit_ins *code_ptr; - sljit_ins *buf_ptr; - sljit_ins *buf_end; - sljit_uw word_count; - sljit_uw next_addr; - sljit_sw executable_offset; - sljit_sw addr; - - struct sljit_label *label; - struct sljit_jump *jump; - struct sljit_const *const_; - struct sljit_put_label *put_label; - - CHECK_ERROR_PTR(); - CHECK_PTR(check_sljit_generate_code(compiler)); - reverse_buf(compiler); - - code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins), compiler->exec_allocator_data); - PTR_FAIL_WITH_EXEC_IF(code); - buf = compiler->buf; - - code_ptr = code; - word_count = 0; - next_addr = 0; - executable_offset = SLJIT_EXEC_OFFSET(code); - - label = compiler->labels; - jump = compiler->jumps; - const_ = compiler->consts; - put_label = compiler->put_labels; - - do { - buf_ptr = (sljit_ins*)buf->memory; - buf_end = buf_ptr + (buf->used_size >> 2); - do { - *code_ptr = *buf_ptr++; - if (next_addr == word_count) { - SLJIT_ASSERT(!label || label->size >= word_count); - SLJIT_ASSERT(!jump || jump->addr >= word_count); - SLJIT_ASSERT(!const_ || const_->addr >= word_count); - SLJIT_ASSERT(!put_label || put_label->addr >= word_count); - - /* These structures are ordered by their address. */ - if (label && label->size == word_count) { - /* Just recording the address. */ - label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); - label->size = (sljit_uw)(code_ptr - code); - label = label->next; - } - if (jump && jump->addr == word_count) { -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - jump->addr = (sljit_uw)(code_ptr - 3); -#else - jump->addr = (sljit_uw)(code_ptr - 6); -#endif - code_ptr = detect_jump_type(jump, code_ptr, code, executable_offset); - jump = jump->next; - } - if (const_ && const_->addr == word_count) { - /* Just recording the address. */ - const_->addr = (sljit_uw)code_ptr; - const_ = const_->next; - } - if (put_label && put_label->addr == word_count) { - SLJIT_ASSERT(put_label->label); - put_label->addr = (sljit_uw)code_ptr; - put_label = put_label->next; - } - next_addr = compute_next_addr(label, jump, const_, put_label); - } - code_ptr ++; - word_count ++; - } while (buf_ptr < buf_end); - - buf = buf->next; - } while (buf); - - if (label && label->size == word_count) { - label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); - label->size = (sljit_uw)(code_ptr - code); - label = label->next; - } - - SLJIT_ASSERT(!label); - SLJIT_ASSERT(!jump); - SLJIT_ASSERT(!const_); - SLJIT_ASSERT(!put_label); - SLJIT_ASSERT(code_ptr - code <= (sljit_s32)compiler->size); - - jump = compiler->jumps; - while (jump) { - do { - addr = (sljit_sw)((jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target); - buf_ptr = (sljit_ins *)jump->addr; - - if (jump->flags & PATCH_CALL) { - addr = (addr - (sljit_sw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset)) >> 2; - SLJIT_ASSERT(addr <= 0x1fffffff && addr >= -0x20000000); - buf_ptr[0] = CALL | ((sljit_ins)addr & 0x3fffffff); - break; - } - if (jump->flags & PATCH_B) { - addr = (addr - (sljit_sw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset)) >> 2; - SLJIT_ASSERT(addr <= MAX_DISP && addr >= MIN_DISP); - buf_ptr[0] = (buf_ptr[0] & ~DISP_MASK) | ((sljit_ins)addr & DISP_MASK); - break; - } - - /* Set the fields of immediate loads. */ -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - SLJIT_ASSERT(((buf_ptr[0] & 0xc1cfffff) == 0x01000000) && ((buf_ptr[1] & 0xc1f83fff) == 0x80102000)); - buf_ptr[0] |= (sljit_ins)(addr >> 10) & 0x3fffff; - buf_ptr[1] |= (sljit_ins)addr & 0x3ff; -#else -#error "Implementation required" -#endif - } while (0); - jump = jump->next; - } - - put_label = compiler->put_labels; - while (put_label) { - addr = (sljit_sw)put_label->label->addr; - buf_ptr = (sljit_ins *)put_label->addr; - -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - SLJIT_ASSERT(((buf_ptr[0] & 0xc1cfffff) == 0x01000000) && ((buf_ptr[1] & 0xc1f83fff) == 0x80102000)); - buf_ptr[0] |= (addr >> 10) & 0x3fffff; - buf_ptr[1] |= addr & 0x3ff; -#else -#error "Implementation required" -#endif - put_label = put_label->next; - } - - compiler->error = SLJIT_ERR_COMPILED; - compiler->executable_offset = executable_offset; - compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins); - - code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset); - code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); - - SLJIT_CACHE_FLUSH(code, code_ptr); - SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1); - return code; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) -{ - switch (feature_type) { - case SLJIT_HAS_FPU: -#ifdef SLJIT_IS_FPU_AVAILABLE - return SLJIT_IS_FPU_AVAILABLE; -#else - /* Available by default. */ - return 1; -#endif - - case SLJIT_HAS_ZERO_REGISTER: - return 1; - -#if (defined SLJIT_CONFIG_SPARC_64 && SLJIT_CONFIG_SPARC_64) - case SLJIT_HAS_CMOV: - return 1; -#endif - - default: - return 0; - } -} - -/* --------------------------------------------------------------------- */ -/* Entry, exit */ -/* --------------------------------------------------------------------- */ - -/* Creates an index in data_transfer_insts array. */ -#define LOAD_DATA 0x01 -#define WORD_DATA 0x00 -#define BYTE_DATA 0x02 -#define HALF_DATA 0x04 -#define INT_DATA 0x06 -#define SIGNED_DATA 0x08 -/* Separates integer and floating point registers */ -#define GPR_REG 0x0f -#define DOUBLE_DATA 0x10 -#define SINGLE_DATA 0x12 - -#define MEM_MASK 0x1f - -#define ARG_TEST 0x00020 -#define ALT_KEEP_CACHE 0x00040 -#define CUMULATIVE_OP 0x00080 -#define IMM_OP 0x00100 -#define MOVE_OP 0x00200 -#define SRC2_IMM 0x00400 - -#define REG_DEST 0x00800 -#define REG2_SOURCE 0x01000 -#define SLOW_SRC1 0x02000 -#define SLOW_SRC2 0x04000 -#define SLOW_DEST 0x08000 - -/* SET_FLAGS (0x10 << 19) also belong here! */ - -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) -#include "sljitNativeSPARC_32.c" -#else -#include "sljitNativeSPARC_64.c" -#endif - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, - sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) -{ - sljit_s32 reg_index, types, tmp; - sljit_u32 float_offset, args_offset; - sljit_s32 saved_arg_index, scratch_arg_index, float_arg_index; - - CHECK_ERROR(); - CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); - set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - - local_size = (local_size + SLJIT_LOCALS_OFFSET + 7) & ~0x7; - compiler->local_size = local_size; - - if (local_size <= -SIMM_MIN) { - FAIL_IF(push_inst(compiler, SAVE | D(SLJIT_SP) | S1(SLJIT_SP) | IMM(-local_size), UNMOVABLE_INS)); - } - else { - FAIL_IF(load_immediate(compiler, TMP_REG1, -local_size)); - FAIL_IF(push_inst(compiler, SAVE | D(SLJIT_SP) | S1(SLJIT_SP) | S2(TMP_REG1), UNMOVABLE_INS)); - } - - arg_types >>= SLJIT_ARG_SHIFT; - - types = arg_types; - float_offset = 16 * sizeof(sljit_sw); - reg_index = 24; - - while (types && reg_index < 24 + 6) { - switch (types & SLJIT_ARG_MASK) { - case SLJIT_ARG_TYPE_F64: - if (reg_index & 0x1) { - FAIL_IF(push_inst(compiler, STW | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS)); - if (reg_index >= 24 + 6 - 1) - break; - FAIL_IF(push_inst(compiler, STW | DA(reg_index + 1) | S1(SLJIT_SP) | IMM(float_offset + sizeof(sljit_sw)), MOVABLE_INS)); - } else - FAIL_IF(push_inst(compiler, STD | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS)); - - float_offset += sizeof(sljit_f64); - reg_index++; - break; - case SLJIT_ARG_TYPE_F32: - FAIL_IF(push_inst(compiler, STW | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS)); - float_offset += sizeof(sljit_f64); - break; - } - - reg_index++; - types >>= SLJIT_ARG_SHIFT; - } - - args_offset = (16 + 1 + 6) * sizeof(sljit_sw); - float_offset = 16 * sizeof(sljit_sw); - reg_index = 24; - saved_arg_index = 24; - scratch_arg_index = 8 - 1; - float_arg_index = 1; - - while (arg_types) { - switch (arg_types & SLJIT_ARG_MASK) { - case SLJIT_ARG_TYPE_F64: - if (reg_index < 24 + 6 - 1) { - FAIL_IF(push_inst(compiler, LDDF | FD(float_arg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS)); - } else if (reg_index < 24 + 6) { - FAIL_IF(push_inst(compiler, LDF | FD(float_arg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS)); - FAIL_IF(push_inst(compiler, LDF | FD(float_arg_index) | (1 << 25) | S1A(30) | IMM(args_offset), MOVABLE_INS)); - } else { - FAIL_IF(push_inst(compiler, LDF | FD(float_arg_index) | S1A(30) | IMM(args_offset), MOVABLE_INS)); - FAIL_IF(push_inst(compiler, LDF | FD(float_arg_index) | (1 << 25) | S1A(30) | IMM(args_offset + sizeof(sljit_sw)), MOVABLE_INS)); - } - - float_arg_index++; - float_offset += sizeof(sljit_f64); - reg_index++; - break; - case SLJIT_ARG_TYPE_F32: - if (reg_index < 24 + 6) - FAIL_IF(push_inst(compiler, LDF | FD(float_arg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS)); - else - FAIL_IF(push_inst(compiler, LDF | FD(float_arg_index) | S1A(30) | IMM(args_offset), MOVABLE_INS)); - float_arg_index++; - float_offset += sizeof(sljit_f64); - break; - default: - scratch_arg_index++; - - if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) { - tmp = saved_arg_index++; - if (tmp == reg_index) - break; - } else - tmp = scratch_arg_index; - - if (reg_index < 24 + 6) - FAIL_IF(push_inst(compiler, OR | DA(tmp) | S1(0) | S2A(reg_index), tmp)); - else - FAIL_IF(push_inst(compiler, LDUW | DA(tmp) | S1A(30) | IMM(args_offset), tmp)); - break; - } - - reg_index++; - arg_types >>= SLJIT_ARG_SHIFT; - } - - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, - sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) -{ - CHECK_ERROR(); - CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); - set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - - compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 7) & ~0x7; - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler) -{ - CHECK_ERROR(); - CHECK(check_sljit_emit_return_void(compiler)); - - FAIL_IF(push_inst(compiler, JMPL | D(0) | S1A(31) | IMM(8), UNMOVABLE_INS)); - return push_inst(compiler, RESTORE | D(SLJIT_R0) | S1(SLJIT_R0) | S2(0), UNMOVABLE_INS); -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) -{ - CHECK_ERROR(); - CHECK(check_sljit_emit_return(compiler, op, src, srcw)); - - if (TYPE_CAST_NEEDED(op) || !FAST_IS_REG(src)) { - FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); - src = SLJIT_R0; - } - - FAIL_IF(push_inst(compiler, JMPL | D(0) | S1A(31) | IMM(8), UNMOVABLE_INS)); - return push_inst(compiler, RESTORE | D(SLJIT_R0) | S1(src) | S2(0), UNMOVABLE_INS); -} - -/* --------------------------------------------------------------------- */ -/* Operators */ -/* --------------------------------------------------------------------- */ - -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) -#define ARCH_32_64(a, b) a -#else -#define ARCH_32_64(a, b) b -#endif - -static const sljit_ins data_transfer_insts[16 + 4] = { -/* u w s */ ARCH_32_64(OPC1(3) | OPC3(0x04) /* stw */, OPC1(3) | OPC3(0x0e) /* stx */), -/* u w l */ ARCH_32_64(OPC1(3) | OPC3(0x00) /* lduw */, OPC1(3) | OPC3(0x0b) /* ldx */), -/* u b s */ OPC1(3) | OPC3(0x05) /* stb */, -/* u b l */ OPC1(3) | OPC3(0x01) /* ldub */, -/* u h s */ OPC1(3) | OPC3(0x06) /* sth */, -/* u h l */ OPC1(3) | OPC3(0x02) /* lduh */, -/* u i s */ OPC1(3) | OPC3(0x04) /* stw */, -/* u i l */ OPC1(3) | OPC3(0x00) /* lduw */, - -/* s w s */ ARCH_32_64(OPC1(3) | OPC3(0x04) /* stw */, OPC1(3) | OPC3(0x0e) /* stx */), -/* s w l */ ARCH_32_64(OPC1(3) | OPC3(0x00) /* lduw */, OPC1(3) | OPC3(0x0b) /* ldx */), -/* s b s */ OPC1(3) | OPC3(0x05) /* stb */, -/* s b l */ OPC1(3) | OPC3(0x09) /* ldsb */, -/* s h s */ OPC1(3) | OPC3(0x06) /* sth */, -/* s h l */ OPC1(3) | OPC3(0x0a) /* ldsh */, -/* s i s */ OPC1(3) | OPC3(0x04) /* stw */, -/* s i l */ ARCH_32_64(OPC1(3) | OPC3(0x00) /* lduw */, OPC1(3) | OPC3(0x08) /* ldsw */), - -/* d s */ OPC1(3) | OPC3(0x27), -/* d l */ OPC1(3) | OPC3(0x23), -/* s s */ OPC1(3) | OPC3(0x24), -/* s l */ OPC1(3) | OPC3(0x20), -}; - -#undef ARCH_32_64 - -/* Can perform an operation using at most 1 instruction. */ -static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_u32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) -{ - SLJIT_ASSERT(arg & SLJIT_MEM); - - if ((!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) - || ((arg & OFFS_REG_MASK) && (argw & 0x3) == 0)) { - /* Works for both absoulte and relative addresses (immediate case). */ - if (SLJIT_UNLIKELY(flags & ARG_TEST)) - return 1; - FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] - | ((flags & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg)) - | S1(arg & REG_MASK) | ((arg & OFFS_REG_MASK) ? S2(OFFS_REG(arg)) : IMM(argw)), - ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? DR(reg) : MOVABLE_INS)); - return -1; - } - return 0; -} - -/* See getput_arg below. - Note: can_cache is called only for binary operators. Those - operators always uses word arguments without write back. */ -static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) -{ - SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM)); - - /* Simple operation except for updates. */ - if (arg & OFFS_REG_MASK) { - argw &= 0x3; - SLJIT_ASSERT(argw); - next_argw &= 0x3; - if ((arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK) && argw == next_argw) - return 1; - return 0; - } - - if (((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN)) - return 1; - return 0; -} - -/* Emit the necessary instructions. See can_cache above. */ -static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_u32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) -{ - sljit_s32 base, arg2, delay_slot; - sljit_ins dest; - - SLJIT_ASSERT(arg & SLJIT_MEM); - if (!(next_arg & SLJIT_MEM)) { - next_arg = 0; - next_argw = 0; - } - - base = arg & REG_MASK; - if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { - argw &= 0x3; - - /* Using the cache. */ - if (((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) && (argw == compiler->cache_argw)) - arg2 = TMP_REG3; - else { - if ((arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK) && argw == (next_argw & 0x3)) { - compiler->cache_arg = SLJIT_MEM | (arg & OFFS_REG_MASK); - compiler->cache_argw = argw; - arg2 = TMP_REG3; - } - else if ((flags & LOAD_DATA) && ((flags & MEM_MASK) <= GPR_REG) && reg != base && reg != OFFS_REG(arg)) - arg2 = reg; - else /* It must be a mov operation, so tmp1 must be free to use. */ - arg2 = TMP_REG1; - FAIL_IF(push_inst(compiler, SLL_W | D(arg2) | S1(OFFS_REG(arg)) | IMM_ARG | (sljit_ins)argw, DR(arg2))); - } - } - else { - /* Using the cache. */ - if ((compiler->cache_arg == SLJIT_MEM) && (argw - compiler->cache_argw) <= SIMM_MAX && (argw - compiler->cache_argw) >= SIMM_MIN) { - if (argw != compiler->cache_argw) { - FAIL_IF(push_inst(compiler, ADD | D(TMP_REG3) | S1(TMP_REG3) | IMM(argw - compiler->cache_argw), DR(TMP_REG3))); - compiler->cache_argw = argw; - } - arg2 = TMP_REG3; - } else { - if ((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN) { - compiler->cache_arg = SLJIT_MEM; - compiler->cache_argw = argw; - arg2 = TMP_REG3; - } - else if ((flags & LOAD_DATA) && ((flags & MEM_MASK) <= GPR_REG) && reg != base) - arg2 = reg; - else /* It must be a mov operation, so tmp1 must be free to use. */ - arg2 = TMP_REG1; - FAIL_IF(load_immediate(compiler, arg2, argw)); - } - } - - dest = ((flags & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg)); - delay_slot = ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? DR(reg) : MOVABLE_INS; - if (!base) - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(arg2) | IMM(0), delay_slot); - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(base) | S2(arg2), delay_slot); -} - -static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_u32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) -{ - if (getput_arg_fast(compiler, flags, reg, arg, argw)) - return compiler->error; - compiler->cache_arg = 0; - compiler->cache_argw = 0; - return getput_arg(compiler, flags, reg, arg, argw, 0, 0); -} - -static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_u32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w) -{ - if (getput_arg_fast(compiler, flags, reg, arg1, arg1w)) - return compiler->error; - return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w); -} - -static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_u32 flags, - sljit_s32 dst, sljit_sw dstw, - sljit_s32 src1, sljit_sw src1w, - sljit_s32 src2, sljit_sw src2w) -{ - /* arg1 goes to TMP_REG1 or src reg - arg2 goes to TMP_REG2, imm or src reg - TMP_REG3 can be used for caching - result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */ - sljit_s32 dst_r = TMP_REG2; - sljit_s32 src1_r; - sljit_sw src2_r = 0; - sljit_s32 sugg_src2_r = TMP_REG2; - - if (!(flags & ALT_KEEP_CACHE)) { - compiler->cache_arg = 0; - compiler->cache_argw = 0; - } - - if (dst != TMP_REG2) { - if (FAST_IS_REG(dst)) { - dst_r = dst; - flags |= REG_DEST; - if (flags & MOVE_OP) - sugg_src2_r = dst_r; - } - else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, TMP_REG1, dst, dstw)) - flags |= SLOW_DEST; - } - - if (flags & IMM_OP) { - if ((src2 & SLJIT_IMM) && src2w) { - if (src2w <= SIMM_MAX && src2w >= SIMM_MIN) { - flags |= SRC2_IMM; - src2_r = src2w; - } - } - if (!(flags & SRC2_IMM) && (flags & CUMULATIVE_OP) && (src1 & SLJIT_IMM) && src1w) { - if (src1w <= SIMM_MAX && src1w >= SIMM_MIN) { - flags |= SRC2_IMM; - src2_r = src1w; - - /* And swap arguments. */ - src1 = src2; - src1w = src2w; - src2 = SLJIT_IMM; - /* src2w = src2_r unneeded. */ - } - } - } - - /* Source 1. */ - if (FAST_IS_REG(src1)) - src1_r = src1; - else if (src1 & SLJIT_IMM) { - if (src1w) { - FAIL_IF(load_immediate(compiler, TMP_REG1, src1w)); - src1_r = TMP_REG1; - } - else - src1_r = 0; - } - else { - if (getput_arg_fast(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w)) - FAIL_IF(compiler->error); - else - flags |= SLOW_SRC1; - src1_r = TMP_REG1; - } - - /* Source 2. */ - if (FAST_IS_REG(src2)) { - src2_r = src2; - flags |= REG2_SOURCE; - if ((flags & (REG_DEST | MOVE_OP)) == MOVE_OP) - dst_r = src2_r; - } - else if (src2 & SLJIT_IMM) { - if (!(flags & SRC2_IMM)) { - if (src2w) { - FAIL_IF(load_immediate(compiler, sugg_src2_r, src2w)); - src2_r = sugg_src2_r; - } - else { - src2_r = 0; - if (flags & MOVE_OP) { - if (dst & SLJIT_MEM) - dst_r = 0; - else - op = SLJIT_MOV; - } - } - } - } - else { - if (getput_arg_fast(compiler, flags | LOAD_DATA, sugg_src2_r, src2, src2w)) - FAIL_IF(compiler->error); - else - flags |= SLOW_SRC2; - src2_r = sugg_src2_r; - } - - if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { - SLJIT_ASSERT(src2_r == TMP_REG2); - if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { - FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, src1, src1w)); - FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); - } - else { - FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); - FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw)); - } - } - else if (flags & SLOW_SRC1) - FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); - else if (flags & SLOW_SRC2) - FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw)); - - FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r)); - - if (dst & SLJIT_MEM) { - if (!(flags & SLOW_DEST)) { - getput_arg_fast(compiler, flags, dst_r, dst, dstw); - return compiler->error; - } - return getput_arg(compiler, flags, dst_r, dst, dstw, 0, 0); - } - - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op) -{ - CHECK_ERROR(); - CHECK(check_sljit_emit_op0(compiler, op)); - - op = GET_OPCODE(op); - switch (op) { - case SLJIT_BREAKPOINT: - return push_inst(compiler, TA, UNMOVABLE_INS); - case SLJIT_NOP: - return push_inst(compiler, NOP, UNMOVABLE_INS); - case SLJIT_LMUL_UW: - case SLJIT_LMUL_SW: -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? UMUL : SMUL) | D(SLJIT_R0) | S1(SLJIT_R0) | S2(SLJIT_R1), DR(SLJIT_R0))); - return push_inst(compiler, RDY | D(SLJIT_R1), DR(SLJIT_R1)); -#else -#error "Implementation required" -#endif - case SLJIT_DIVMOD_UW: - case SLJIT_DIVMOD_SW: - case SLJIT_DIV_UW: - case SLJIT_DIV_SW: - SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments); -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - if ((op | 0x2) == SLJIT_DIV_UW) - FAIL_IF(push_inst(compiler, WRY | S1(0), MOVABLE_INS)); - else { - FAIL_IF(push_inst(compiler, SRA | D(TMP_REG1) | S1(SLJIT_R0) | IMM(31), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, WRY | S1(TMP_REG1), MOVABLE_INS)); - } - if (op <= SLJIT_DIVMOD_SW) - FAIL_IF(push_inst(compiler, OR | D(TMP_REG2) | S1(0) | S2(SLJIT_R0), DR(TMP_REG2))); - FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? UDIV : SDIV) | D(SLJIT_R0) | S1(SLJIT_R0) | S2(SLJIT_R1), DR(SLJIT_R0))); - if (op >= SLJIT_DIV_UW) - return SLJIT_SUCCESS; - FAIL_IF(push_inst(compiler, SMUL | D(SLJIT_R1) | S1(SLJIT_R0) | S2(SLJIT_R1), DR(SLJIT_R1))); - return push_inst(compiler, SUB | D(SLJIT_R1) | S1(TMP_REG2) | S2(SLJIT_R1), DR(SLJIT_R1)); -#else -#error "Implementation required" -#endif - case SLJIT_ENDBR: - case SLJIT_SKIP_FRAMES_BEFORE_RETURN: - return SLJIT_SUCCESS; - } - - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op, - sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw) -{ - sljit_u32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0; - - CHECK_ERROR(); - CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw)); - ADJUST_LOCAL_OFFSET(dst, dstw); - ADJUST_LOCAL_OFFSET(src, srcw); - - op = GET_OPCODE(op); - switch (op) { - case SLJIT_MOV: -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - case SLJIT_MOV_U32: - case SLJIT_MOV_S32: - case SLJIT_MOV32: -#endif - case SLJIT_MOV_P: - return emit_op(compiler, SLJIT_MOV, flags | WORD_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, srcw); - - case SLJIT_MOV_U8: - return emit_op(compiler, SLJIT_MOV_U8, flags | BYTE_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw); - - case SLJIT_MOV_S8: - return emit_op(compiler, SLJIT_MOV_S8, flags | BYTE_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw); - - case SLJIT_MOV_U16: - return emit_op(compiler, SLJIT_MOV_U16, flags | HALF_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw); - - case SLJIT_MOV_S16: - return emit_op(compiler, SLJIT_MOV_S16, flags | HALF_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw); - - case SLJIT_NOT: - case SLJIT_CLZ: - return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw); - } - - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op, - sljit_s32 dst, sljit_sw dstw, - sljit_s32 src1, sljit_sw src1w, - sljit_s32 src2, sljit_sw src2w) -{ - sljit_u32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0; - - CHECK_ERROR(); - CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w)); - ADJUST_LOCAL_OFFSET(dst, dstw); - ADJUST_LOCAL_OFFSET(src1, src1w); - ADJUST_LOCAL_OFFSET(src2, src2w); - - op = GET_OPCODE(op); - switch (op) { - case SLJIT_ADD: - case SLJIT_ADDC: - case SLJIT_MUL: - case SLJIT_AND: - case SLJIT_OR: - case SLJIT_XOR: - return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w); - - case SLJIT_SUB: - case SLJIT_SUBC: - return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w); - - case SLJIT_SHL: - case SLJIT_LSHR: - case SLJIT_ASHR: -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - if (src2 & SLJIT_IMM) - src2w &= 0x1f; -#else - SLJIT_UNREACHABLE(); -#endif - return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w); - } - - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op, - sljit_s32 src1, sljit_sw src1w, - sljit_s32 src2, sljit_sw src2w) -{ - CHECK_ERROR(); - CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w)); - -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w); -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op, - sljit_s32 src, sljit_sw srcw) -{ - CHECK_ERROR(); - CHECK(check_sljit_emit_op_src(compiler, op, src, srcw)); - ADJUST_LOCAL_OFFSET(src, srcw); - - switch (op) { - case SLJIT_FAST_RETURN: - if (FAST_IS_REG(src)) - FAIL_IF(push_inst(compiler, OR | D(TMP_LINK) | S1(0) | S2(src), DR(TMP_LINK))); - else - FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_LINK, src, srcw)); - - FAIL_IF(push_inst(compiler, JMPL | D(0) | S1(TMP_LINK) | IMM(8), UNMOVABLE_INS)); - return push_inst(compiler, NOP, UNMOVABLE_INS); - case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN: - case SLJIT_PREFETCH_L1: - case SLJIT_PREFETCH_L2: - case SLJIT_PREFETCH_L3: - case SLJIT_PREFETCH_ONCE: - return SLJIT_SUCCESS; - } - - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) -{ - CHECK_REG_INDEX(check_sljit_get_register_index(reg)); - return reg_map[reg]; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) -{ - CHECK_REG_INDEX(check_sljit_get_float_register_index(reg)); - return freg_map[reg]; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, - void *instruction, sljit_u32 size) -{ - CHECK_ERROR(); - CHECK(check_sljit_emit_op_custom(compiler, instruction, size)); - - return push_inst(compiler, *(sljit_ins*)instruction, UNMOVABLE_INS); -} - -/* --------------------------------------------------------------------- */ -/* Floating point operators */ -/* --------------------------------------------------------------------- */ - -#define FLOAT_DATA(op) ((sljit_ins)DOUBLE_DATA | (((sljit_ins)(op) & SLJIT_32) >> 7)) -#define SELECT_FOP(op, single, double) ((op & SLJIT_32) ? single : double) -#define FLOAT_TMP_MEM_OFFSET (22 * sizeof(sljit_sw)) - -static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op, - sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw) -{ - if (src & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw)); - src = TMP_FREG1; - } - - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOI, FDTOI) | FD(TMP_FREG1) | FS2(src), MOVABLE_INS)); - - if (FAST_IS_REG(dst)) { - FAIL_IF(emit_op_mem2(compiler, SINGLE_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET)); - return emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET); - } - - /* Store the integer value from a VFP register. */ - return emit_op_mem2(compiler, SINGLE_DATA, TMP_FREG1, dst, dstw, 0, 0); -} - -static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op, - sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw) -{ - sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; - - if (src & SLJIT_IMM) { -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) - srcw = (sljit_s32)srcw; -#endif - FAIL_IF(load_immediate(compiler, TMP_REG1, srcw)); - src = TMP_REG1; - srcw = 0; - } - - if (FAST_IS_REG(src)) { - FAIL_IF(emit_op_mem2(compiler, WORD_DATA, src, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET)); - src = SLJIT_MEM1(SLJIT_SP); - srcw = FLOAT_TMP_MEM_OFFSET; - } - - FAIL_IF(emit_op_mem2(compiler, SINGLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw)); - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FITOS, FITOD) | FD(dst_r) | FS2(TMP_FREG1), MOVABLE_INS)); - - if (dst & SLJIT_MEM) - return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0); - return SLJIT_SUCCESS; -} - -static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op, - sljit_s32 src1, sljit_sw src1w, - sljit_s32 src2, sljit_sw src2w) -{ - if (src1 & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); - src1 = TMP_FREG1; - } - - if (src2 & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0)); - src2 = TMP_FREG2; - } - - return push_inst(compiler, SELECT_FOP(op, FCMPS, FCMPD) | FS1(src1) | FS2(src2), FCC_IS_SET | MOVABLE_INS); -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op, - sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw) -{ - sljit_s32 dst_r; - - CHECK_ERROR(); - compiler->cache_arg = 0; - compiler->cache_argw = 0; - - SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100) && !(DOUBLE_DATA & 0x2), float_transfer_bit_error); - SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw); - - if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) - op ^= SLJIT_32; - - dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; - - if (src & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw)); - src = dst_r; - } - - switch (GET_OPCODE(op)) { - case SLJIT_MOV_F64: - if (src != dst_r) { - if (dst_r != TMP_FREG1) { - FAIL_IF(push_inst(compiler, FMOVS | FD(dst_r) | FS2(src), MOVABLE_INS)); - if (!(op & SLJIT_32)) - FAIL_IF(push_inst(compiler, FMOVS | FDN(dst_r) | FS2N(src), MOVABLE_INS)); - } - else - dst_r = src; - } - break; - case SLJIT_NEG_F64: - FAIL_IF(push_inst(compiler, FNEGS | FD(dst_r) | FS2(src), MOVABLE_INS)); - if (dst_r != src && !(op & SLJIT_32)) - FAIL_IF(push_inst(compiler, FMOVS | FDN(dst_r) | FS2N(src), MOVABLE_INS)); - break; - case SLJIT_ABS_F64: - FAIL_IF(push_inst(compiler, FABSS | FD(dst_r) | FS2(src), MOVABLE_INS)); - if (dst_r != src && !(op & SLJIT_32)) - FAIL_IF(push_inst(compiler, FMOVS | FDN(dst_r) | FS2N(src), MOVABLE_INS)); - break; - case SLJIT_CONV_F64_FROM_F32: - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOD, FDTOS) | FD(dst_r) | FS2(src), MOVABLE_INS)); - op ^= SLJIT_32; - break; - } - - if (dst & SLJIT_MEM) - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), dst_r, dst, dstw, 0, 0)); - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op, - sljit_s32 dst, sljit_sw dstw, - sljit_s32 src1, sljit_sw src1w, - sljit_s32 src2, sljit_sw src2w) -{ - sljit_s32 dst_r, flags = 0; - - CHECK_ERROR(); - CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w)); - ADJUST_LOCAL_OFFSET(dst, dstw); - ADJUST_LOCAL_OFFSET(src1, src1w); - ADJUST_LOCAL_OFFSET(src2, src2w); - - compiler->cache_arg = 0; - compiler->cache_argw = 0; - - dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2; - - if (src1 & SLJIT_MEM) { - if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) { - FAIL_IF(compiler->error); - src1 = TMP_FREG1; - } else - flags |= SLOW_SRC1; - } - - if (src2 & SLJIT_MEM) { - if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) { - FAIL_IF(compiler->error); - src2 = TMP_FREG2; - } else - flags |= SLOW_SRC2; - } - - if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { - if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, src1, src1w)); - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw)); - } - else { - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw)); - } - } - else if (flags & SLOW_SRC1) - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw)); - else if (flags & SLOW_SRC2) - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw)); - - if (flags & SLOW_SRC1) - src1 = TMP_FREG1; - if (flags & SLOW_SRC2) - src2 = TMP_FREG2; - - switch (GET_OPCODE(op)) { - case SLJIT_ADD_F64: - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FADDS, FADDD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS)); - break; - - case SLJIT_SUB_F64: - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSUBS, FSUBD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS)); - break; - - case SLJIT_MUL_F64: - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FMULS, FMULD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS)); - break; - - case SLJIT_DIV_F64: - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FDIVS, FDIVD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS)); - break; - } - - if (dst_r == TMP_FREG2) - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, 0, 0)); - - return SLJIT_SUCCESS; -} - -#undef FLOAT_DATA -#undef SELECT_FOP - -/* --------------------------------------------------------------------- */ -/* Other instructions */ -/* --------------------------------------------------------------------- */ - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) -{ - CHECK_ERROR(); - CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); - ADJUST_LOCAL_OFFSET(dst, dstw); - - if (FAST_IS_REG(dst)) - return push_inst(compiler, OR | D(dst) | S1(0) | S2(TMP_LINK), UNMOVABLE_INS); - - /* Memory. */ - FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_LINK, dst, dstw)); - compiler->delay_slot = UNMOVABLE_INS; - return SLJIT_SUCCESS; -} - -/* --------------------------------------------------------------------- */ -/* Conditional instructions */ -/* --------------------------------------------------------------------- */ - -SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler) -{ - struct sljit_label *label; - - CHECK_ERROR_PTR(); - CHECK_PTR(check_sljit_emit_label(compiler)); - - if (compiler->last_label && compiler->last_label->size == compiler->size) - return compiler->last_label; - - label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label)); - PTR_FAIL_IF(!label); - set_label(label, compiler); - compiler->delay_slot = UNMOVABLE_INS; - return label; -} - -static sljit_ins get_cc(struct sljit_compiler *compiler, sljit_s32 type) -{ - switch (type) { - case SLJIT_EQUAL: - case SLJIT_NOT_EQUAL_F64: /* Unordered. */ - return DA(0x1); - - case SLJIT_NOT_EQUAL: - case SLJIT_EQUAL_F64: - return DA(0x9); - - case SLJIT_LESS: - case SLJIT_GREATER_F64: /* Unordered. */ - case SLJIT_CARRY: - return DA(0x5); - - case SLJIT_GREATER_EQUAL: - case SLJIT_LESS_EQUAL_F64: - case SLJIT_NOT_CARRY: - return DA(0xd); - - case SLJIT_GREATER: - case SLJIT_GREATER_EQUAL_F64: /* Unordered. */ - return DA(0xc); - - case SLJIT_LESS_EQUAL: - case SLJIT_LESS_F64: - return DA(0x4); - - case SLJIT_SIG_LESS: - return DA(0x3); - - case SLJIT_SIG_GREATER_EQUAL: - return DA(0xb); - - case SLJIT_SIG_GREATER: - return DA(0xa); - - case SLJIT_SIG_LESS_EQUAL: - return DA(0x2); - - case SLJIT_OVERFLOW: - if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB))) - return DA(0x9); - /* fallthrough */ - - case SLJIT_UNORDERED_F64: - return DA(0x7); - - case SLJIT_NOT_OVERFLOW: - if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB))) - return DA(0x1); - /* fallthrough */ - - case SLJIT_ORDERED_F64: - return DA(0xf); - - default: - SLJIT_UNREACHABLE(); - return DA(0x8); - } -} - -SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type) -{ - struct sljit_jump *jump; - - CHECK_ERROR_PTR(); - CHECK_PTR(check_sljit_emit_jump(compiler, type)); - - jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); - PTR_FAIL_IF(!jump); - set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); - type &= 0xff; - - if (type < SLJIT_EQUAL_F64) { - jump->flags |= IS_COND; - if (((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS) && !(compiler->delay_slot & ICC_IS_SET)) - jump->flags |= IS_MOVABLE; -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - PTR_FAIL_IF(push_inst(compiler, BICC | get_cc(compiler, type ^ 1) | 5, UNMOVABLE_INS)); -#else -#error "Implementation required" -#endif - } - else if (type < SLJIT_JUMP) { - jump->flags |= IS_COND; - if (((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS) && !(compiler->delay_slot & FCC_IS_SET)) - jump->flags |= IS_MOVABLE; -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - PTR_FAIL_IF(push_inst(compiler, FBFCC | get_cc(compiler, type ^ 1) | 5, UNMOVABLE_INS)); -#else -#error "Implementation required" -#endif - } - else { - if ((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS) - jump->flags |= IS_MOVABLE; - if (type >= SLJIT_FAST_CALL) - jump->flags |= IS_CALL; - } - - PTR_FAIL_IF(emit_const(compiler, TMP_REG1, 0)); - PTR_FAIL_IF(push_inst(compiler, JMPL | D(type >= SLJIT_FAST_CALL ? TMP_LINK : 0) | S1(TMP_REG1) | IMM(0), UNMOVABLE_INS)); - jump->addr = compiler->size; - PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); - - return jump; -} - -SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, - sljit_s32 arg_types) -{ - CHECK_ERROR_PTR(); - CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); - - PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL)); - -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - - return sljit_emit_jump(compiler, type); -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) -{ - struct sljit_jump *jump = NULL; - sljit_s32 src_r; - - CHECK_ERROR(); - CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); - ADJUST_LOCAL_OFFSET(src, srcw); - - if (FAST_IS_REG(src)) - src_r = src; - else if (src & SLJIT_IMM) { - jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); - FAIL_IF(!jump); - set_jump(jump, compiler, JUMP_ADDR); - jump->u.target = (sljit_uw)srcw; - - if ((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS) - jump->flags |= IS_MOVABLE; - if (type >= SLJIT_FAST_CALL) - jump->flags |= IS_CALL; - - FAIL_IF(emit_const(compiler, TMP_REG1, 0)); - src_r = TMP_REG1; - } - else { - FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw)); - src_r = TMP_REG1; - } - - FAIL_IF(push_inst(compiler, JMPL | D(type >= SLJIT_FAST_CALL ? TMP_LINK : 0) | S1(src_r) | IMM(0), UNMOVABLE_INS)); - if (jump) - jump->addr = compiler->size; - return push_inst(compiler, NOP, UNMOVABLE_INS); -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, - sljit_s32 arg_types, - sljit_s32 src, sljit_sw srcw) -{ - CHECK_ERROR(); - CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); - - if (src & SLJIT_MEM) { - ADJUST_LOCAL_OFFSET(src, srcw); - FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw)); - src = TMP_REG1; - } - - FAIL_IF(call_with_args(compiler, arg_types, &src)); - -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - - return sljit_emit_ijump(compiler, type, src, srcw); -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, - sljit_s32 dst, sljit_sw dstw, - sljit_s32 type) -{ - sljit_s32 reg; - sljit_u32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0; - - CHECK_ERROR(); - CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); - ADJUST_LOCAL_OFFSET(dst, dstw); - -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - op = GET_OPCODE(op); - reg = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2; - - compiler->cache_arg = 0; - compiler->cache_argw = 0; - - if (op >= SLJIT_ADD && (dst & SLJIT_MEM)) - FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, dst, dstw, dst, dstw)); - - type &= 0xff; - if (type < SLJIT_EQUAL_F64) - FAIL_IF(push_inst(compiler, BICC | get_cc(compiler, type) | 3, UNMOVABLE_INS)); - else - FAIL_IF(push_inst(compiler, FBFCC | get_cc(compiler, type) | 3, UNMOVABLE_INS)); - - FAIL_IF(push_inst(compiler, OR | D(reg) | S1(0) | IMM(1), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, OR | D(reg) | S1(0) | IMM(0), UNMOVABLE_INS)); - - if (op >= SLJIT_ADD) { - flags |= CUMULATIVE_OP | IMM_OP | ALT_KEEP_CACHE; - if (dst & SLJIT_MEM) - return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, TMP_REG2, 0); - return emit_op(compiler, op, flags, dst, 0, dst, 0, TMP_REG2, 0); - } - - if (!(dst & SLJIT_MEM)) - return SLJIT_SUCCESS; - - return emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw); -#else -#error "Implementation required" -#endif -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, - sljit_s32 dst_reg, - sljit_s32 src, sljit_sw srcw) -{ - CHECK_ERROR(); - CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); - -#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) - return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);; -#else -#error "Implementation required" -#endif -} - -SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) -{ - struct sljit_const *const_; - sljit_s32 dst_r; - - CHECK_ERROR_PTR(); - CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value)); - ADJUST_LOCAL_OFFSET(dst, dstw); - - const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const)); - PTR_FAIL_IF(!const_); - set_const(const_, compiler); - - dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; - PTR_FAIL_IF(emit_const(compiler, dst_r, init_value)); - - if (dst & SLJIT_MEM) - PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw)); - return const_; -} - -SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) -{ - struct sljit_put_label *put_label; - sljit_s32 dst_r; - - CHECK_ERROR_PTR(); - CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw)); - ADJUST_LOCAL_OFFSET(dst, dstw); - - put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label)); - PTR_FAIL_IF(!put_label); - set_put_label(put_label, compiler, 0); - - dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; - PTR_FAIL_IF(emit_const(compiler, dst_r, 0)); - - if (dst & SLJIT_MEM) - PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw)); - return put_label; -} diff --git a/thirdparty/pcre2/src/sljit/sljitNativeX86_32.c b/thirdparty/pcre2/src/sljit/sljitNativeX86_32.c index b9a7b39789..08da03026d 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativeX86_32.c +++ b/thirdparty/pcre2/src/sljit/sljitNativeX86_32.c @@ -80,21 +80,28 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw if (b & SLJIT_MEM) { if (!(b & REG_MASK)) inst_size += sizeof(sljit_sw); - else if (immb != 0 && !(b & OFFS_REG_MASK)) { - /* Immediate operand. */ - if (immb <= 127 && immb >= -128) - inst_size += sizeof(sljit_s8); - else - inst_size += sizeof(sljit_sw); - } - else if (reg_map[b & REG_MASK] == 5) - inst_size += sizeof(sljit_s8); + else { + if (immb != 0 && !(b & OFFS_REG_MASK)) { + /* Immediate operand. */ + if (immb <= 127 && immb >= -128) + inst_size += sizeof(sljit_s8); + else + inst_size += sizeof(sljit_sw); + } + else if (reg_map[b & REG_MASK] == 5) { + /* Swap registers if possible. */ + if ((b & OFFS_REG_MASK) && (immb & 0x3) == 0 && reg_map[OFFS_REG(b)] != 5) + b = SLJIT_MEM | OFFS_REG(b) | TO_OFFS_REG(b & REG_MASK); + else + inst_size += sizeof(sljit_s8); + } - if ((b & REG_MASK) == SLJIT_SP && !(b & OFFS_REG_MASK)) - b |= TO_OFFS_REG(SLJIT_SP); + if (reg_map[b & REG_MASK] == 4 && !(b & OFFS_REG_MASK)) + b |= TO_OFFS_REG(SLJIT_SP); - if (b & OFFS_REG_MASK) - inst_size += 1; /* SIB byte. */ + if (b & OFFS_REG_MASK) + inst_size += 1; /* SIB byte. */ + } } /* Calculate size of a. */ @@ -107,9 +114,9 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw inst_size += 4; } else if (flags & EX86_SHIFT_INS) { - imma &= 0x1f; + SLJIT_ASSERT(imma <= 0x1f); if (imma != 1) { - inst_size ++; + inst_size++; flags |= EX86_BYTE_ARG; } } else if (flags & EX86_BYTE_ARG) @@ -165,7 +172,7 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw } else if (b & REG_MASK) { reg_map_b = reg_map[b & REG_MASK]; - if (!(b & OFFS_REG_MASK) || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP) || reg_map_b == 5) { + if (!(b & OFFS_REG_MASK) || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP)) { if (immb != 0 || reg_map_b == 5) { if (immb <= 127 && immb >= -128) *buf_ptr |= 0x40; @@ -190,8 +197,14 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw } } else { + if (reg_map_b == 5) + *buf_ptr |= 0x40; + *buf_ptr++ |= 0x04; *buf_ptr++ = U8(reg_map_b | (reg_map[OFFS_REG(b)] << 3) | (immb << 6)); + + if (reg_map_b == 5) + *buf_ptr++ = 0; } } else { @@ -243,19 +256,16 @@ static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ return code_ptr; } -#define ENTER_R2_USED 0x00001 -#define ENTER_R2_TO_S 0x00002 -#define ENTER_R2_TO_R0 0x00004 -#define ENTER_R1_TO_S 0x00008 -#define ENTER_TMP_TO_R4 0x00010 -#define ENTER_TMP_TO_S 0x00020 +#define ENTER_TMP_TO_R4 0x00001 +#define ENTER_TMP_TO_S 0x00002 SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { sljit_s32 word_arg_count, saved_arg_count, float_arg_count; - sljit_s32 size, locals_offset, args_size, types, status; + sljit_s32 size, args_size, types, status; + sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(options); sljit_u8 *inst; #ifdef _WIN32 sljit_s32 r2_offset = -1; @@ -271,108 +281,97 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi SLJIT_COMPILE_ASSERT(SLJIT_FR0 == 1, float_register_index_start); arg_types >>= SLJIT_ARG_SHIFT; - types = arg_types; word_arg_count = 0; - saved_arg_count = 0; - float_arg_count = 0; - args_size = SSIZE_OF(sw); status = 0; - while (types) { - switch (types & SLJIT_ARG_MASK) { - case SLJIT_ARG_TYPE_F64: - float_arg_count++; - FAIL_IF(emit_sse2_load(compiler, 0, float_arg_count, SLJIT_MEM1(SLJIT_SP), args_size)); - args_size += SSIZE_OF(f64); - break; - case SLJIT_ARG_TYPE_F32: - float_arg_count++; - FAIL_IF(emit_sse2_load(compiler, 1, float_arg_count, SLJIT_MEM1(SLJIT_SP), args_size)); - args_size += SSIZE_OF(f32); - break; - default: - word_arg_count++; - if (!(types & SLJIT_ARG_TYPE_SCRATCH_REG)) { - saved_arg_count++; - if (saved_arg_count == 4) - status |= ENTER_TMP_TO_S; - } else { - if (word_arg_count == 4) + if (options & SLJIT_ENTER_REG_ARG) { + args_size = 3 * SSIZE_OF(sw); + + while (arg_types) { + if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) { + word_arg_count++; + if (word_arg_count >= 4) status |= ENTER_TMP_TO_R4; -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (word_arg_count == 3) - status |= ENTER_R2_USED; -#endif } -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (word_arg_count <= 2 && !(options & SLJIT_ENTER_CDECL)) - break; -#endif - - args_size += SSIZE_OF(sw); - break; + arg_types >>= SLJIT_ARG_SHIFT; } - types >>= SLJIT_ARG_SHIFT; - } - args_size -= SSIZE_OF(sw); - compiler->args_size = args_size; + compiler->args_size = 0; + } else { + types = arg_types; + saved_arg_count = 0; + float_arg_count = 0; + args_size = SSIZE_OF(sw); + while (types) { + switch (types & SLJIT_ARG_MASK) { + case SLJIT_ARG_TYPE_F64: + float_arg_count++; + FAIL_IF(emit_sse2_load(compiler, 0, float_arg_count, SLJIT_MEM1(SLJIT_SP), args_size)); + args_size += SSIZE_OF(f64); + break; + case SLJIT_ARG_TYPE_F32: + float_arg_count++; + FAIL_IF(emit_sse2_load(compiler, 1, float_arg_count, SLJIT_MEM1(SLJIT_SP), args_size)); + args_size += SSIZE_OF(f32); + break; + default: + word_arg_count++; - /* [esp+0] for saving temporaries and function calls. */ - locals_offset = 2 * SSIZE_OF(sw); + if (!(types & SLJIT_ARG_TYPE_SCRATCH_REG)) + saved_arg_count++; -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if ((options & SLJIT_ENTER_CDECL) && scratches >= 3) - locals_offset = 4 * SSIZE_OF(sw); -#else - if (scratches >= 3) - locals_offset = 4 * SSIZE_OF(sw); -#endif + if (word_arg_count == 4) { + if (types & SLJIT_ARG_TYPE_SCRATCH_REG) { + status |= ENTER_TMP_TO_R4; + arg_types &= ~(SLJIT_ARG_FULL_MASK << 3 * SLJIT_ARG_SHIFT); + } else if (saved_arg_count == 4) { + status |= ENTER_TMP_TO_S; + arg_types &= ~(SLJIT_ARG_FULL_MASK << 3 * SLJIT_ARG_SHIFT); + } + } - compiler->scratches_offset = locals_offset; + args_size += SSIZE_OF(sw); + break; + } + types >>= SLJIT_ARG_SHIFT; + } - if (scratches > 3) - locals_offset += ((scratches > (3 + 6)) ? 6 : (scratches - 3)) * SSIZE_OF(sw); + args_size -= SSIZE_OF(sw); + compiler->args_size = args_size; + } - if (saveds > 3) - locals_offset += (saveds - 3) * SSIZE_OF(sw); + size = (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3) - kept_saveds_count; + if (!(options & SLJIT_ENTER_REG_ARG)) + size++; - compiler->locals_offset = locals_offset; + if (size != 0) { + inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(size + 1)); + FAIL_IF(!inst); - size = 1 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3); - inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(size + 1)); - FAIL_IF(!inst); + INC_SIZE((sljit_uw)size); - INC_SIZE((sljit_uw)size); - PUSH_REG(reg_map[TMP_REG1]); - if (saveds > 2 || scratches > 9) - PUSH_REG(reg_map[SLJIT_S2]); - if (saveds > 1 || scratches > 10) - PUSH_REG(reg_map[SLJIT_S1]); - if (saveds > 0 || scratches > 11) - PUSH_REG(reg_map[SLJIT_S0]); + if (!(options & SLJIT_ENTER_REG_ARG)) + PUSH_REG(reg_map[TMP_REG1]); - size *= SSIZE_OF(sw); + if ((saveds > 2 && kept_saveds_count <= 2) || scratches > 9) + PUSH_REG(reg_map[SLJIT_S2]); + if ((saveds > 1 && kept_saveds_count <= 1) || scratches > 10) + PUSH_REG(reg_map[SLJIT_S1]); + if ((saveds > 0 && kept_saveds_count == 0) || scratches > 11) + PUSH_REG(reg_map[SLJIT_S0]); + + size *= SSIZE_OF(sw); + } if (status & (ENTER_TMP_TO_R4 | ENTER_TMP_TO_S)) EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), args_size + size); size += SSIZE_OF(sw); -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (!(options & SLJIT_ENTER_CDECL)) - size += args_size; -#endif - - local_size = ((locals_offset + local_size + size + 0xf) & ~0xf) - size; + local_size = ((SLJIT_LOCALS_OFFSET_BASE + local_size + size + 0xf) & ~0xf) - size; compiler->local_size = local_size; -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (!(options & SLJIT_ENTER_CDECL)) - size -= args_size; -#endif - word_arg_count = 0; saved_arg_count = 0; args_size = size; @@ -386,64 +385,27 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi break; default: word_arg_count++; + SLJIT_ASSERT(word_arg_count <= 3 || (word_arg_count == 4 && !(status & (ENTER_TMP_TO_R4 | ENTER_TMP_TO_S)))); -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (!(options & SLJIT_ENTER_CDECL) && word_arg_count <= 2) { - if (word_arg_count == 1) { - if (status & ENTER_R2_USED) { - EMIT_MOV(compiler, (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) ? SLJIT_R0 : SLJIT_S0, 0, SLJIT_R2, 0); - } else if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) { - status |= ENTER_R2_TO_S; - saved_arg_count++; - } else - status |= ENTER_R2_TO_R0; - } else if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) { - status |= ENTER_R1_TO_S; - saved_arg_count++; - } - break; - } -#endif if (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) { - SLJIT_ASSERT(word_arg_count <= 3 || (status & ENTER_TMP_TO_R4)); - - if (word_arg_count <= 3) { #ifdef _WIN32 - if (word_arg_count == 3 && local_size > 4 * 4096) - r2_offset = local_size + args_size; - else + if (word_arg_count == 3 && local_size > 4 * 4096) + r2_offset = local_size + args_size; + else #endif - EMIT_MOV(compiler, word_arg_count, 0, SLJIT_MEM1(SLJIT_SP), args_size); - } - } else { - SLJIT_ASSERT(saved_arg_count <= 3 || (status & ENTER_TMP_TO_S)); + EMIT_MOV(compiler, word_arg_count, 0, SLJIT_MEM1(SLJIT_SP), args_size); - if (saved_arg_count <= 3) - EMIT_MOV(compiler, SLJIT_S0 - saved_arg_count, 0, SLJIT_MEM1(SLJIT_SP), args_size); + } else { + EMIT_MOV(compiler, SLJIT_S0 - saved_arg_count, 0, SLJIT_MEM1(SLJIT_SP), args_size); saved_arg_count++; } + args_size += SSIZE_OF(sw); break; } arg_types >>= SLJIT_ARG_SHIFT; } -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (!(options & SLJIT_ENTER_CDECL)) { - if (status & ENTER_R2_TO_R0) - EMIT_MOV(compiler, SLJIT_R0, 0, SLJIT_R2, 0); - - saved_arg_count = 0; - if (status & ENTER_R2_TO_S) { - EMIT_MOV(compiler, SLJIT_S0, 0, SLJIT_R2, 0); - saved_arg_count++; - } - - if (status & ENTER_R1_TO_S) - EMIT_MOV(compiler, SLJIT_S0 - saved_arg_count, 0, SLJIT_R1, 0); - } -#endif - SLJIT_ASSERT(SLJIT_LOCALS_OFFSET > 0); #ifdef _WIN32 @@ -459,6 +421,18 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 3); } else { + if (options & SLJIT_ENTER_REG_ARG) { + SLJIT_ASSERT(r2_offset == -1); + + inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(1 + 1)); + FAIL_IF(!inst); + INC_SIZE(1); + PUSH_REG(reg_map[SLJIT_R2]); + + local_size -= SSIZE_OF(sw); + r2_offset = local_size; + } + EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_IMM, local_size >> 12); BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -4096); @@ -490,8 +464,20 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi #endif /* _WIN32 */ + size = SLJIT_LOCALS_OFFSET_BASE - SSIZE_OF(sw); + kept_saveds_count = SLJIT_R3 - kept_saveds_count; + + while (saved_arg_count > 3) { + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), size, kept_saveds_count, 0); + kept_saveds_count++; + size -= SSIZE_OF(sw); + saved_arg_count--; + } + if (status & (ENTER_TMP_TO_R4 | ENTER_TMP_TO_S)) { - size = (status & ENTER_TMP_TO_R4) ? compiler->scratches_offset : compiler->locals_offset - SSIZE_OF(sw); + if (status & ENTER_TMP_TO_R4) + size = 2 * SSIZE_OF(sw); + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), size, TMP_REG1, 0); } @@ -502,10 +488,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { - sljit_s32 args_size, locals_offset; -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - sljit_s32 word_arg_count = 0; -#endif + sljit_s32 args_size; CHECK_ERROR(); CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); @@ -513,87 +496,88 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp arg_types >>= SLJIT_ARG_SHIFT; args_size = 0; - while (arg_types) { - switch (arg_types & SLJIT_ARG_MASK) { - case SLJIT_ARG_TYPE_F64: - args_size += SSIZE_OF(f64); - break; - case SLJIT_ARG_TYPE_F32: - args_size += SSIZE_OF(f32); - break; - default: -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (word_arg_count >= 2) + + if (!(options & SLJIT_ENTER_REG_ARG)) { + while (arg_types) { + switch (arg_types & SLJIT_ARG_MASK) { + case SLJIT_ARG_TYPE_F64: + args_size += SSIZE_OF(f64); + break; + case SLJIT_ARG_TYPE_F32: + args_size += SSIZE_OF(f32); + break; + default: args_size += SSIZE_OF(sw); - word_arg_count++; -#else - args_size += SSIZE_OF(sw); -#endif - break; + break; + } + arg_types >>= SLJIT_ARG_SHIFT; } - arg_types >>= SLJIT_ARG_SHIFT; } compiler->args_size = args_size; - /* [esp+0] for saving temporaries and function calls. */ - locals_offset = 2 * SSIZE_OF(sw); + /* [esp+0] for saving temporaries and for function calls. */ -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if ((options & SLJIT_ENTER_CDECL) && scratches >= 3) - locals_offset = 4 * SSIZE_OF(sw); -#else - if (scratches >= 3) - locals_offset = 4 * SSIZE_OF(sw); -#endif + saveds = (1 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3) - SLJIT_KEPT_SAVEDS_COUNT(options)) * SSIZE_OF(sw); - compiler->scratches_offset = locals_offset; + /* Saving ebp. */ + if (!(options & SLJIT_ENTER_REG_ARG)) + saveds += SSIZE_OF(sw); - if (scratches > 3) - locals_offset += ((scratches > (3 + 6)) ? 6 : (scratches - 3)) * SSIZE_OF(sw); - - if (saveds > 3) - locals_offset += (saveds - 3) * SSIZE_OF(sw); - - compiler->locals_offset = locals_offset; - - saveds = (2 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3)) * SSIZE_OF(sw); - -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (!(options & SLJIT_ENTER_CDECL)) - saveds += args_size; -#endif - - compiler->local_size = ((locals_offset + local_size + saveds + 0xf) & ~0xf) - saveds; + compiler->local_size = ((SLJIT_LOCALS_OFFSET_BASE + local_size + saveds + 0xf) & ~0xf) - saveds; return SLJIT_SUCCESS; } -static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) +static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to) { + sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options); + sljit_s32 local_size, saveds; sljit_uw size; sljit_u8 *inst; - size = (sljit_uw)(1 + (compiler->scratches > 9 ? (compiler->scratches - 9) : 0) + - (compiler->saveds <= 3 ? compiler->saveds : 3)); + size = (sljit_uw)((compiler->scratches > 9 ? (compiler->scratches - 9) : 0) + + (compiler->saveds <= 3 ? compiler->saveds : 3) - kept_saveds_count); + + local_size = compiler->local_size; + + if (!(compiler->options & SLJIT_ENTER_REG_ARG)) + size++; + else if (is_return_to && size == 0) { + local_size += SSIZE_OF(sw); + is_return_to = 0; + } + + if (local_size > 0) + BINARY_IMM32(ADD, local_size, SLJIT_SP, 0); + + if (size == 0) + return SLJIT_SUCCESS; + inst = (sljit_u8*)ensure_buf(compiler, 1 + size); FAIL_IF(!inst); INC_SIZE(size); - if (compiler->saveds > 0 || compiler->scratches > 11) + saveds = compiler->saveds; + + if ((saveds > 0 && kept_saveds_count == 0) || compiler->scratches > 11) POP_REG(reg_map[SLJIT_S0]); - if (compiler->saveds > 1 || compiler->scratches > 10) + if ((saveds > 1 && kept_saveds_count <= 1) || compiler->scratches > 10) POP_REG(reg_map[SLJIT_S1]); - if (compiler->saveds > 2 || compiler->scratches > 9) + if ((saveds > 2 && kept_saveds_count <= 2) || compiler->scratches > 9) POP_REG(reg_map[SLJIT_S2]); - POP_REG(reg_map[TMP_REG1]); + + if (!(compiler->options & SLJIT_ENTER_REG_ARG)) + POP_REG(reg_map[TMP_REG1]); + + if (is_return_to) + BINARY_IMM32(ADD, sizeof(sljit_sw), SLJIT_SP, 0); return SLJIT_SUCCESS; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler) { - sljit_uw size; sljit_u8 *inst; CHECK_ERROR(); @@ -602,143 +586,45 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler SLJIT_ASSERT(compiler->args_size >= 0); SLJIT_ASSERT(compiler->local_size > 0); - BINARY_IMM32(ADD, compiler->local_size, SLJIT_SP, 0); - - FAIL_IF(emit_stack_frame_release(compiler)); + FAIL_IF(emit_stack_frame_release(compiler, 0)); - size = 1; -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (compiler->args_size > 0 && !(compiler->options & SLJIT_ENTER_CDECL)) - size = 3; -#endif - inst = (sljit_u8*)ensure_buf(compiler, 1 + size); + inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); FAIL_IF(!inst); - - INC_SIZE(size); - -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (compiler->args_size > 0 && !(compiler->options & SLJIT_ENTER_CDECL)) { - RET_I16(U8(compiler->args_size)); - return SLJIT_SUCCESS; - } -#endif - + INC_SIZE(1); RET(); return SLJIT_SUCCESS; } -/* --------------------------------------------------------------------- */ -/* Call / return instructions */ -/* --------------------------------------------------------------------- */ - -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - -static sljit_sw c_fast_call_get_stack_size(sljit_s32 arg_types, sljit_s32 *word_arg_count_ptr) -{ - sljit_sw stack_size = 0; - sljit_s32 word_arg_count = 0; - - arg_types >>= SLJIT_ARG_SHIFT; - - while (arg_types) { - switch (arg_types & SLJIT_ARG_MASK) { - case SLJIT_ARG_TYPE_F64: - stack_size += SSIZE_OF(f64); - break; - case SLJIT_ARG_TYPE_F32: - stack_size += SSIZE_OF(f32); - break; - default: - word_arg_count++; - if (word_arg_count > 2) - stack_size += SSIZE_OF(sw); - break; - } - - arg_types >>= SLJIT_ARG_SHIFT; - } - - if (word_arg_count_ptr) - *word_arg_count_ptr = word_arg_count; - - return stack_size; -} - -static sljit_s32 c_fast_call_with_args(struct sljit_compiler *compiler, - sljit_s32 arg_types, sljit_sw stack_size, sljit_s32 word_arg_count, sljit_s32 swap_args) +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) { - sljit_u8 *inst; - sljit_s32 float_arg_count; + sljit_s32 src_r; - if (stack_size == SSIZE_OF(sw) && word_arg_count == 3) { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); - FAIL_IF(!inst); - INC_SIZE(1); - PUSH_REG(reg_map[SLJIT_R2]); - } - else if (stack_size > 0) { - if (word_arg_count >= 4) - EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), compiler->scratches_offset); + CHECK_ERROR(); + CHECK(check_sljit_emit_return_to(compiler, src, srcw)); - BINARY_IMM32(SUB, stack_size, SLJIT_SP, 0); + if ((src & SLJIT_MEM) || (src > SLJIT_R2 && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) { + ADJUST_LOCAL_OFFSET(src, srcw); + CHECK_EXTRA_REGS(src, srcw, (void)0); - stack_size = 0; - arg_types >>= SLJIT_ARG_SHIFT; - word_arg_count = 0; - float_arg_count = 0; - while (arg_types) { - switch (arg_types & SLJIT_ARG_MASK) { - case SLJIT_ARG_TYPE_F64: - float_arg_count++; - FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count)); - stack_size += SSIZE_OF(f64); - break; - case SLJIT_ARG_TYPE_F32: - float_arg_count++; - FAIL_IF(emit_sse2_store(compiler, 1, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count)); - stack_size += SSIZE_OF(f32); - break; - default: - word_arg_count++; - if (word_arg_count == 3) { - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size, SLJIT_R2, 0); - stack_size += SSIZE_OF(sw); - } - else if (word_arg_count == 4) { - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size, TMP_REG1, 0); - stack_size += SSIZE_OF(sw); - } - break; - } + src_r = (compiler->options & SLJIT_ENTER_REG_ARG) ? TMP_REG1 : SLJIT_R1; - arg_types >>= SLJIT_ARG_SHIFT; - } + EMIT_MOV(compiler, src_r, 0, src, srcw); + src = src_r; + srcw = 0; } - if (word_arg_count > 0) { - if (swap_args) { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); - FAIL_IF(!inst); - INC_SIZE(1); - - *inst++ = U8(XCHG_EAX_r | reg_map[SLJIT_R2]); - } - else { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 2); - FAIL_IF(!inst); - INC_SIZE(2); - - *inst++ = MOV_r_rm; - *inst++ = U8(MOD_REG | (reg_map[SLJIT_R2] << 3) | reg_map[SLJIT_R0]); - } - } + FAIL_IF(emit_stack_frame_release(compiler, 1)); - return SLJIT_SUCCESS; + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); } -#endif +/* --------------------------------------------------------------------- */ +/* Call / return instructions */ +/* --------------------------------------------------------------------- */ -static sljit_s32 cdecl_call_get_stack_size(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *word_arg_count_ptr) +static sljit_s32 call_get_stack_size(sljit_s32 arg_types, sljit_s32 *word_arg_count_ptr) { sljit_sw stack_size = 0; sljit_s32 word_arg_count = 0; @@ -765,25 +651,31 @@ static sljit_s32 cdecl_call_get_stack_size(struct sljit_compiler *compiler, slji if (word_arg_count_ptr) *word_arg_count_ptr = word_arg_count; - if (stack_size <= compiler->scratches_offset) + if (stack_size <= 4 * SSIZE_OF(sw)) return 0; - return ((stack_size - compiler->scratches_offset + 0xf) & ~0xf); + return ((stack_size - (4 * SSIZE_OF(sw)) + 0xf) & ~0xf); } -static sljit_s32 cdecl_call_with_args(struct sljit_compiler *compiler, - sljit_s32 arg_types, sljit_sw stack_size, sljit_s32 word_arg_count) +static sljit_s32 call_with_args(struct sljit_compiler *compiler, + sljit_s32 arg_types, sljit_sw stack_size, sljit_s32 word_arg_count, sljit_s32 keep_tmp1) { - sljit_s32 float_arg_count = 0; + sljit_s32 float_arg_count = 0, arg4_reg = 0, arg_offset; sljit_u8 *inst; - if (word_arg_count >= 4) - EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), compiler->scratches_offset); + if (word_arg_count >= 4) { + arg4_reg = SLJIT_R0; + + if (!keep_tmp1) { + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw)); + arg4_reg = TMP_REG1; + } + } if (stack_size > 0) BINARY_IMM32(SUB, stack_size, SLJIT_SP, 0); - stack_size = 0; + arg_offset = 0; word_arg_count = 0; arg_types >>= SLJIT_ARG_SHIFT; @@ -791,18 +683,22 @@ static sljit_s32 cdecl_call_with_args(struct sljit_compiler *compiler, switch (arg_types & SLJIT_ARG_MASK) { case SLJIT_ARG_TYPE_F64: float_arg_count++; - FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count)); - stack_size += SSIZE_OF(f64); + FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), arg_offset, float_arg_count)); + arg_offset += SSIZE_OF(f64); break; case SLJIT_ARG_TYPE_F32: float_arg_count++; - FAIL_IF(emit_sse2_store(compiler, 1, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count)); - stack_size += SSIZE_OF(f32); + FAIL_IF(emit_sse2_store(compiler, 1, SLJIT_MEM1(SLJIT_SP), arg_offset, float_arg_count)); + arg_offset += SSIZE_OF(f32); break; default: word_arg_count++; - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size, (word_arg_count >= 4) ? TMP_REG1 : word_arg_count, 0); - stack_size += SSIZE_OF(sw); + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), arg_offset, (word_arg_count >= 4) ? arg4_reg : word_arg_count, 0); + + if (word_arg_count == 1 && arg4_reg == SLJIT_R0) + EMIT_MOV(compiler, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw) + stack_size); + + arg_offset += SSIZE_OF(sw); break; } @@ -840,21 +736,19 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, sljit_s32 *extra_space, sljit_s32 arg_types, sljit_s32 src, sljit_sw srcw) { - sljit_sw args_size, prev_args_size, saved_regs_size; + sljit_sw args_size, saved_regs_size; sljit_sw types, word_arg_count, float_arg_count; sljit_sw stack_size, prev_stack_size, min_size, offset; sljit_sw word_arg4_offset; sljit_u8 r2_offset = 0; -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - sljit_u8 fast_call = (*extra_space & 0xff) == SLJIT_CALL; -#endif + sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options); sljit_u8* inst; ADJUST_LOCAL_OFFSET(src, srcw); CHECK_EXTRA_REGS(src, srcw, (void)0); saved_regs_size = (1 + (compiler->scratches > 9 ? (compiler->scratches - 9) : 0) - + (compiler->saveds <= 3 ? compiler->saveds : 3)) * SSIZE_OF(sw); + + (compiler->saveds <= 3 ? compiler->saveds : 3) - kept_saveds_count) * SSIZE_OF(sw); word_arg_count = 0; float_arg_count = 0; @@ -876,30 +770,15 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, break; default: word_arg_count++; -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (!fast_call || word_arg_count > 2) - args_size += SSIZE_OF(sw); -#else args_size += SSIZE_OF(sw); -#endif break; } arg_types >>= SLJIT_ARG_SHIFT; } - if (args_size <= compiler->args_size -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - && (!(compiler->options & SLJIT_ENTER_CDECL) || args_size == 0 || !fast_call) -#endif /* SLJIT_X86_32_FASTCALL */ - && 1) { -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - *extra_space = fast_call ? 0 : args_size; - prev_args_size = compiler->args_size; - stack_size = prev_args_size + SSIZE_OF(sw) + saved_regs_size; -#else /* !SLJIT_X86_32_FASTCALL */ + if (args_size <= compiler->args_size) { *extra_space = 0; stack_size = args_size + SSIZE_OF(sw) + saved_regs_size; -#endif /* SLJIT_X86_32_FASTCALL */ offset = stack_size + compiler->local_size; @@ -911,37 +790,6 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, EMIT_MOV(compiler, SLJIT_R0, 0, src, srcw); } -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (!(compiler->options & SLJIT_ENTER_CDECL)) { - if (!fast_call) - offset -= SSIZE_OF(sw); - - if (word_arg_count >= 3) { - word_arg4_offset = SSIZE_OF(sw); - - if (word_arg_count + float_arg_count >= 4) { - word_arg4_offset = SSIZE_OF(sw) + SSIZE_OF(sw); - if ((types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F64) - word_arg4_offset = SSIZE_OF(sw) + SSIZE_OF(f64); - } - - /* In cdecl mode, at least one more word value must - * be present on the stack before the return address. */ - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset - word_arg4_offset, SLJIT_R2, 0); - } - - if (fast_call) { - if (args_size < prev_args_size) { - EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), offset - prev_args_size - SSIZE_OF(sw)); - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset - args_size - SSIZE_OF(sw), SLJIT_R2, 0); - } - } else if (prev_args_size > 0) { - EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), offset - prev_args_size); - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0); - } - } -#endif /* SLJIT_X86_32_FASTCALL */ - while (types != 0) { switch (types & SLJIT_ARG_MASK) { case SLJIT_ARG_TYPE_F64: @@ -957,12 +805,6 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, default: switch (word_arg_count) { case 1: -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (fast_call) { - EMIT_MOV(compiler, SLJIT_R2, 0, r2_offset != 0 ? SLJIT_MEM1(SLJIT_SP) : SLJIT_R0, 0); - break; - } -#endif offset -= SSIZE_OF(sw); if (r2_offset != 0) { EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), 0); @@ -971,10 +813,6 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R0, 0); break; case 2: -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (fast_call) - break; -#endif offset -= SSIZE_OF(sw); EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R1, 0); break; @@ -983,7 +821,7 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, break; case 4: offset -= SSIZE_OF(sw); - EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), compiler->scratches_offset); + EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw)); EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0); break; } @@ -993,15 +831,7 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, types >>= SLJIT_ARG_SHIFT; } - BINARY_IMM32(ADD, compiler->local_size, SLJIT_SP, 0); - FAIL_IF(emit_stack_frame_release(compiler)); - -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (args_size < prev_args_size) - BINARY_IMM32(ADD, prev_args_size - args_size, SLJIT_SP, 0); -#endif - - return SLJIT_SUCCESS; + return emit_stack_frame_release(compiler, 0); } stack_size = args_size + SSIZE_OF(sw); @@ -1014,16 +844,10 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, if (word_arg_count >= 3) stack_size += SSIZE_OF(sw); - prev_args_size = 0; -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (!(compiler->options & SLJIT_ENTER_CDECL)) - prev_args_size = compiler->args_size; -#endif - - prev_stack_size = prev_args_size + SSIZE_OF(sw) + saved_regs_size; + prev_stack_size = SSIZE_OF(sw) + saved_regs_size; min_size = prev_stack_size + compiler->local_size; - word_arg4_offset = compiler->scratches_offset; + word_arg4_offset = 2 * SSIZE_OF(sw); if (stack_size > min_size) { BINARY_IMM32(SUB, stack_size - min_size, SLJIT_SP, 0); @@ -1050,75 +874,30 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, } /* Restore saved registers. */ - offset = stack_size - prev_args_size - 2 * SSIZE_OF(sw); + offset = stack_size - 2 * SSIZE_OF(sw); EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), offset); if (compiler->saveds > 2 || compiler->scratches > 9) { offset -= SSIZE_OF(sw); EMIT_MOV(compiler, SLJIT_S2, 0, SLJIT_MEM1(SLJIT_SP), offset); } - if (compiler->saveds > 1 || compiler->scratches > 10) { + if ((compiler->saveds > 1 && kept_saveds_count <= 1) || compiler->scratches > 10) { offset -= SSIZE_OF(sw); EMIT_MOV(compiler, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_SP), offset); } - if (compiler->saveds > 0 || compiler->scratches > 11) { + if ((compiler->saveds > 0 && kept_saveds_count == 0) || compiler->scratches > 11) { offset -= SSIZE_OF(sw); EMIT_MOV(compiler, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_SP), offset); } /* Copy fourth argument and return address. */ -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (fast_call) { - offset = stack_size; - *extra_space = 0; - - if (word_arg_count >= 4 && prev_args_size == 0) { - offset -= SSIZE_OF(sw); - inst = emit_x86_instruction(compiler, 1, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), offset); - FAIL_IF(!inst); - *inst = XCHG_r_rm; - - SLJIT_ASSERT(args_size != prev_args_size); - } else { - if (word_arg_count >= 4) { - offset -= SSIZE_OF(sw); - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0); - } - - if (args_size != prev_args_size) - EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), stack_size - prev_args_size - SSIZE_OF(sw)); - } - - if (args_size != prev_args_size) - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size - args_size - SSIZE_OF(sw), SLJIT_R2, 0); - } else { -#endif /* SLJIT_X86_32_FASTCALL */ - offset = stack_size - SSIZE_OF(sw); - *extra_space = args_size; + offset = stack_size - SSIZE_OF(sw); + *extra_space = args_size; - if (word_arg_count >= 4 && prev_args_size == SSIZE_OF(sw)) { - offset -= SSIZE_OF(sw); - inst = emit_x86_instruction(compiler, 1, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), offset); - FAIL_IF(!inst); - *inst = XCHG_r_rm; - - SLJIT_ASSERT(prev_args_size > 0); - } else { - if (word_arg_count >= 4) { - offset -= SSIZE_OF(sw); - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0); - } - - if (prev_args_size > 0) - EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), stack_size - prev_args_size - SSIZE_OF(sw)); - } - - /* Copy return address. */ - if (prev_args_size > 0) - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size - SSIZE_OF(sw), SLJIT_R2, 0); -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (word_arg_count >= 4) { + offset -= SSIZE_OF(sw); + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0); } -#endif /* SLJIT_X86_32_FASTCALL */ while (types != 0) { switch (types & SLJIT_ARG_MASK) { @@ -1135,12 +914,6 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, default: switch (word_arg_count) { case 1: -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (fast_call) { - EMIT_MOV(compiler, SLJIT_R2, 0, r2_offset != 0 ? SLJIT_MEM1(SLJIT_SP) : SLJIT_R0, 0); - break; - } -#endif offset -= SSIZE_OF(sw); if (r2_offset != 0) { EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), 0); @@ -1149,10 +922,6 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R0, 0); break; case 2: -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (fast_call) - break; -#endif offset -= SSIZE_OF(sw); EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R1, 0); break; @@ -1168,12 +937,6 @@ static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler, types >>= SLJIT_ARG_SHIFT; } -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - /* Skip return address. */ - if (fast_call) - offset -= SSIZE_OF(sw); -#endif - SLJIT_ASSERT(offset >= 0); if (offset == 0) @@ -1198,6 +961,38 @@ static sljit_s32 emit_tail_call_end(struct sljit_compiler *compiler, sljit_s32 e return SLJIT_SUCCESS; } +static sljit_s32 tail_call_reg_arg_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types) +{ + sljit_s32 word_arg_count = 0; + sljit_s32 kept_saveds_count, offset; + + arg_types >>= SLJIT_ARG_SHIFT; + + while (arg_types) { + if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) + word_arg_count++; + + arg_types >>= SLJIT_ARG_SHIFT; + } + + if (word_arg_count < 4) + return SLJIT_SUCCESS; + + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw)); + + kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options); + offset = compiler->local_size + 3 * SSIZE_OF(sw); + + if ((compiler->saveds > 0 && kept_saveds_count == 0) || compiler->scratches > 11) + offset += SSIZE_OF(sw); + if ((compiler->saveds > 1 && kept_saveds_count <= 1) || compiler->scratches > 10) + offset += SSIZE_OF(sw); + if ((compiler->saveds > 2 && kept_saveds_count <= 2) || compiler->scratches > 9) + offset += SSIZE_OF(sw); + + return emit_mov(compiler, SLJIT_MEM1(SLJIT_SP), offset, TMP_REG1, 0); +} + SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 arg_types) { @@ -1209,18 +1004,21 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); if (type & SLJIT_CALL_RETURN) { + if ((type & 0xff) == SLJIT_CALL_REG_ARG) { + PTR_FAIL_IF(tail_call_reg_arg_with_args(compiler, arg_types)); + PTR_FAIL_IF(emit_stack_frame_release(compiler, 0)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_jump(compiler, SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP)); + } + stack_size = type; PTR_FAIL_IF(tail_call_with_args(compiler, &stack_size, arg_types, SLJIT_IMM, 0)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); - if (stack_size == 0) { - type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); - return sljit_emit_jump(compiler, type); - } + if (stack_size == 0) + return sljit_emit_jump(compiler, SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP)); jump = sljit_emit_jump(compiler, type); PTR_FAIL_IF(jump == NULL); @@ -1229,32 +1027,15 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile return jump; } -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if ((type & 0xff) == SLJIT_CALL) { - stack_size = c_fast_call_get_stack_size(arg_types, &word_arg_count); - PTR_FAIL_IF(c_fast_call_with_args(compiler, arg_types, stack_size, word_arg_count, 0)); - -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - - jump = sljit_emit_jump(compiler, type); - PTR_FAIL_IF(jump == NULL); - - PTR_FAIL_IF(post_call_with_args(compiler, arg_types, 0)); - return jump; + if ((type & 0xff) == SLJIT_CALL_REG_ARG) { + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_jump(compiler, type); } -#endif - - stack_size = cdecl_call_get_stack_size(compiler, arg_types, &word_arg_count); - PTR_FAIL_IF(cdecl_call_with_args(compiler, arg_types, stack_size, word_arg_count)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + stack_size = call_get_stack_size(arg_types, &word_arg_count); + PTR_FAIL_IF(call_with_args(compiler, arg_types, stack_size, word_arg_count, 0)); + SLJIT_SKIP_CHECKS(compiler); jump = sljit_emit_jump(compiler, type); PTR_FAIL_IF(jump == NULL); @@ -1268,14 +1049,29 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi { sljit_sw stack_size = 0; sljit_s32 word_arg_count; -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - sljit_s32 swap_args; -#endif CHECK_ERROR(); CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); if (type & SLJIT_CALL_RETURN) { + if ((type & 0xff) == SLJIT_CALL_REG_ARG) { + FAIL_IF(tail_call_reg_arg_with_args(compiler, arg_types)); + + if ((src & SLJIT_MEM) || (src > SLJIT_R2 && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) { + ADJUST_LOCAL_OFFSET(src, srcw); + CHECK_EXTRA_REGS(src, srcw, (void)0); + + EMIT_MOV(compiler, TMP_REG1, 0, src, srcw); + src = TMP_REG1; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, 0)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); + } + stack_size = type; FAIL_IF(tail_call_with_args(compiler, &stack_size, arg_types, src, srcw)); @@ -1284,10 +1080,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi srcw = 0; } -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); if (stack_size == 0) return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); @@ -1296,57 +1089,57 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi return emit_tail_call_end(compiler, stack_size); } -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - SLJIT_ASSERT(reg_map[SLJIT_R0] == 0 && reg_map[SLJIT_R2] == 1 && SLJIT_R0 == 1 && SLJIT_R2 == 3); + if ((type & 0xff) == SLJIT_CALL_REG_ARG) { + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, type, src, srcw); + } - if ((type & 0xff) == SLJIT_CALL) { - stack_size = c_fast_call_get_stack_size(arg_types, &word_arg_count); - swap_args = 0; + ADJUST_LOCAL_OFFSET(src, srcw); + CHECK_EXTRA_REGS(src, srcw, (void)0); - if (word_arg_count > 0) { - if ((src & REG_MASK) == SLJIT_R2 || OFFS_REG(src) == SLJIT_R2) { - swap_args = 1; - if (((src & REG_MASK) | 0x2) == SLJIT_R2) - src ^= 0x2; - if ((OFFS_REG(src) | 0x2) == SLJIT_R2) - src ^= TO_OFFS_REG(0x2); - } - } + if (src & SLJIT_MEM) { + EMIT_MOV(compiler, TMP_REG1, 0, src, srcw); + src = TMP_REG1; + srcw = 0; + } - FAIL_IF(c_fast_call_with_args(compiler, arg_types, stack_size, word_arg_count, swap_args)); + stack_size = call_get_stack_size(arg_types, &word_arg_count); + FAIL_IF(call_with_args(compiler, arg_types, stack_size, word_arg_count, src == TMP_REG1)); - compiler->scratches_offset += stack_size; - compiler->locals_offset += stack_size; + if (stack_size > 0 && src == SLJIT_MEM1(SLJIT_SP)) + srcw += stack_size; -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); + SLJIT_SKIP_CHECKS(compiler); + FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); + + return post_call_with_args(compiler, arg_types, stack_size); +} - compiler->scratches_offset -= stack_size; - compiler->locals_offset -= stack_size; +static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) +{ + sljit_u8* inst; - return post_call_with_args(compiler, arg_types, 0); - } -#endif + if (compiler->options & SLJIT_ENTER_REG_ARG) { + if (src == SLJIT_FR0) + return SLJIT_SUCCESS; - stack_size = cdecl_call_get_stack_size(compiler, arg_types, &word_arg_count); - FAIL_IF(cdecl_call_with_args(compiler, arg_types, stack_size, word_arg_count)); + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw); + } - compiler->scratches_offset += stack_size; - compiler->locals_offset += stack_size; + if (FAST_IS_REG(src)) { + FAIL_IF(emit_sse2_store(compiler, op & SLJIT_32, SLJIT_MEM1(SLJIT_SP), 0, src)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); + src = SLJIT_MEM1(SLJIT_SP); + srcw = 0; + } else { + ADJUST_LOCAL_OFFSET(src, srcw); + } - compiler->scratches_offset -= stack_size; - compiler->locals_offset -= stack_size; + inst = emit_x86_instruction(compiler, 1 | EX86_SSE2_OP1, 0, 0, src, srcw); + *inst = (op & SLJIT_32) ? FLDS : FLDL; - return post_call_with_args(compiler, arg_types, stack_size); + return SLJIT_SUCCESS; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) @@ -1404,6 +1197,88 @@ static sljit_s32 emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src return SLJIT_SUCCESS; } +/* --------------------------------------------------------------------- */ +/* Other operations */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_u8* inst; + sljit_s32 i, next, reg_idx, offset; + sljit_u8 regs[2]; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + + if (!(reg & REG_PAIR_MASK)) + return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw); + + ADJUST_LOCAL_OFFSET(mem, memw); + + regs[0] = U8(REG_PAIR_FIRST(reg)); + regs[1] = U8(REG_PAIR_SECOND(reg)); + + next = SSIZE_OF(sw); + + if (!(type & SLJIT_MEM_STORE) && (regs[0] == (mem & REG_MASK) || regs[0] == OFFS_REG(mem))) { + if (regs[1] == (mem & REG_MASK) || regs[1] == OFFS_REG(mem)) { + /* None of them are virtual register so TMP_REG1 will not be used. */ + EMIT_MOV(compiler, TMP_REG1, 0, OFFS_REG(mem), 0); + + if (regs[1] == OFFS_REG(mem)) + next = -SSIZE_OF(sw); + + mem = (mem & ~OFFS_REG_MASK) | TO_OFFS_REG(TMP_REG1); + } else { + next = -SSIZE_OF(sw); + + if (!(mem & OFFS_REG_MASK)) + memw += SSIZE_OF(sw); + } + } + + for (i = 0; i < 2; i++) { + reg_idx = next > 0 ? i : (i ^ 0x1); + reg = regs[reg_idx]; + + offset = -1; + + if (reg >= SLJIT_R3 && reg <= SLJIT_S3) { + offset = (2 * SSIZE_OF(sw)) + ((reg) - SLJIT_R3) * SSIZE_OF(sw); + reg = TMP_REG1; + + if (type & SLJIT_MEM_STORE) + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), offset); + } + + if ((mem & OFFS_REG_MASK) && (reg_idx == 1)) { + inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(1 + 4)); + FAIL_IF(!inst); + + INC_SIZE(4); + + inst[0] = (type & SLJIT_MEM_STORE) ? MOV_rm_r : MOV_r_rm; + inst[1] = 0x44 | U8(reg_map[reg] << 3); + inst[2] = U8(memw << 6) | U8(reg_map[OFFS_REG(mem)] << 3) | reg_map[mem & REG_MASK]; + inst[3] = sizeof(sljit_sw); + } else if (type & SLJIT_MEM_STORE) { + EMIT_MOV(compiler, mem, memw, reg, 0); + } else { + EMIT_MOV(compiler, reg, 0, mem, memw); + } + + if (!(mem & OFFS_REG_MASK)) + memw += next; + + if (!(type & SLJIT_MEM_STORE) && offset != -1) + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, TMP_REG1, 0); + } + + return SLJIT_SUCCESS; +} + static sljit_s32 skip_frames_before_return(struct sljit_compiler *compiler) { sljit_sw size; diff --git a/thirdparty/pcre2/src/sljit/sljitNativeX86_64.c b/thirdparty/pcre2/src/sljit/sljitNativeX86_64.c index f37df6e1bf..4e938ffcf3 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativeX86_64.c +++ b/thirdparty/pcre2/src/sljit/sljitNativeX86_64.c @@ -101,34 +101,38 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw /* Calculate size of b. */ inst_size += 1; /* mod r/m byte. */ if (b & SLJIT_MEM) { - if (!(b & OFFS_REG_MASK)) { - if (NOT_HALFWORD(immb)) { - PTR_FAIL_IF(emit_load_imm64(compiler, TMP_REG2, immb)); - immb = 0; - if (b & REG_MASK) - b |= TO_OFFS_REG(TMP_REG2); - else - b |= TMP_REG2; - } - else if (reg_lmap[b & REG_MASK] == 4) - b |= TO_OFFS_REG(SLJIT_SP); + if (!(b & OFFS_REG_MASK) && NOT_HALFWORD(immb)) { + PTR_FAIL_IF(emit_load_imm64(compiler, TMP_REG2, immb)); + immb = 0; + if (b & REG_MASK) + b |= TO_OFFS_REG(TMP_REG2); + else + b |= TMP_REG2; } if (!(b & REG_MASK)) inst_size += 1 + sizeof(sljit_s32); /* SIB byte required to avoid RIP based addressing. */ else { - if (reg_map[b & REG_MASK] >= 8) - rex |= REX_B; - - if (immb != 0 && (!(b & OFFS_REG_MASK) || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP))) { + if (immb != 0 && !(b & OFFS_REG_MASK)) { /* Immediate operand. */ if (immb <= 127 && immb >= -128) inst_size += sizeof(sljit_s8); else inst_size += sizeof(sljit_s32); } - else if (reg_lmap[b & REG_MASK] == 5) - inst_size += sizeof(sljit_s8); + else if (reg_lmap[b & REG_MASK] == 5) { + /* Swap registers if possible. */ + if ((b & OFFS_REG_MASK) && (immb & 0x3) == 0 && reg_lmap[OFFS_REG(b)] != 5) + b = SLJIT_MEM | OFFS_REG(b) | TO_OFFS_REG(b & REG_MASK); + else + inst_size += sizeof(sljit_s8); + } + + if (reg_map[b & REG_MASK] >= 8) + rex |= REX_B; + + if (reg_lmap[b & REG_MASK] == 4 && !(b & OFFS_REG_MASK)) + b |= TO_OFFS_REG(SLJIT_SP); if (b & OFFS_REG_MASK) { inst_size += 1; /* SIB byte. */ @@ -153,9 +157,9 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw inst_size += 4; } else if (flags & EX86_SHIFT_INS) { - imma &= compiler->mode32 ? 0x1f : 0x3f; + SLJIT_ASSERT(imma <= (compiler->mode32 ? 0x1f : 0x3f)); if (imma != 1) { - inst_size ++; + inst_size++; flags |= EX86_BYTE_ARG; } } else if (flags & EX86_BYTE_ARG) @@ -223,7 +227,7 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw } else if (b & REG_MASK) { reg_lmap_b = reg_lmap[b & REG_MASK]; - if (!(b & OFFS_REG_MASK) || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP) || reg_lmap_b == 5) { + if (!(b & OFFS_REG_MASK) || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP)) { if (immb != 0 || reg_lmap_b == 5) { if (immb <= 127 && immb >= -128) *buf_ptr |= 0x40; @@ -248,8 +252,14 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw } } else { + if (reg_lmap_b == 5) + *buf_ptr |= 0x40; + *buf_ptr++ |= 0x04; *buf_ptr++ = U8(reg_lmap_b | (reg_lmap[OFFS_REG(b)] << 3) | (immb << 6)); + + if (reg_lmap_b == 5) + *buf_ptr++ = 0; } } else { @@ -366,7 +376,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi { sljit_uw size; sljit_s32 word_arg_count = 0; - sljit_s32 saved_arg_count = 0; + sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options); sljit_s32 saved_regs_size, tmp, i; #ifdef _WIN64 sljit_s32 saved_float_regs_size; @@ -379,16 +389,19 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); + if (options & SLJIT_ENTER_REG_ARG) + arg_types = 0; + /* Emit ENDBR64 at function entry if needed. */ FAIL_IF(emit_endbranch(compiler)); compiler->mode32 = 0; /* Including the return address saved by the call instruction. */ - saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); + saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1); tmp = SLJIT_S0 - saveds; - for (i = SLJIT_S0; i > tmp; i--) { + for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) { size = reg_map[i] >= 8 ? 2 : 1; inst = (sljit_u8*)ensure_buf(compiler, 1 + size); FAIL_IF(!inst); @@ -561,15 +574,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp #endif /* _WIN64 */ /* Including the return address saved by the call instruction. */ - saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); + saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1); compiler->local_size = ((local_size + saved_regs_size + 0xf) & ~0xf) - saved_regs_size; return SLJIT_SUCCESS; } -static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) +static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to) { sljit_uw size; - sljit_s32 i, tmp; + sljit_s32 local_size, i, tmp; sljit_u8 *inst; #ifdef _WIN64 sljit_s32 saved_float_regs_offset; @@ -598,30 +611,21 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) *inst = MOVAPS_x_xm; saved_float_regs_offset += 16; } + + compiler->mode32 = 0; } #endif /* _WIN64 */ - if (compiler->local_size > 0) { - if (compiler->local_size <= 127) { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 4); - FAIL_IF(!inst); - INC_SIZE(4); - *inst++ = REX_W; - *inst++ = GROUP_BINARY_83; - *inst++ = MOD_REG | ADD | 4; - *inst = U8(compiler->local_size); - } - else { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 7); - FAIL_IF(!inst); - INC_SIZE(7); - *inst++ = REX_W; - *inst++ = GROUP_BINARY_81; - *inst++ = MOD_REG | ADD | 4; - sljit_unaligned_store_s32(inst, compiler->local_size); - } + local_size = compiler->local_size; + + if (is_return_to && compiler->scratches < SLJIT_FIRST_SAVED_REG && (compiler->saveds == SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { + local_size += SSIZE_OF(sw); + is_return_to = 0; } + if (local_size > 0) + BINARY_IMM32(ADD, local_size, SLJIT_SP, 0); + tmp = compiler->scratches; for (i = SLJIT_FIRST_SAVED_REG; i <= tmp; i++) { size = reg_map[i] >= 8 ? 2 : 1; @@ -633,8 +637,8 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) POP_REG(reg_lmap[i]); } - tmp = compiler->saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - compiler->saveds) : SLJIT_FIRST_SAVED_REG; - for (i = tmp; i <= SLJIT_S0; i++) { + tmp = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); + for (i = SLJIT_S0 + 1 - compiler->saveds; i <= tmp; i++) { size = reg_map[i] >= 8 ? 2 : 1; inst = (sljit_u8*)ensure_buf(compiler, 1 + size); FAIL_IF(!inst); @@ -644,6 +648,9 @@ static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler) POP_REG(reg_lmap[i]); } + if (is_return_to) + BINARY_IMM32(ADD, sizeof(sljit_sw), SLJIT_SP, 0); + return SLJIT_SUCCESS; } @@ -654,7 +661,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler CHECK_ERROR(); CHECK(check_sljit_emit_return_void(compiler)); - FAIL_IF(emit_stack_frame_release(compiler)); + compiler->mode32 = 0; + + FAIL_IF(emit_stack_frame_release(compiler, 0)); inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); FAIL_IF(!inst); @@ -663,6 +672,28 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler return SLJIT_SUCCESS; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_return_to(compiler, src, srcw)); + + compiler->mode32 = 0; + + if ((src & SLJIT_MEM) || (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) { + ADJUST_LOCAL_OFFSET(src, srcw); + + EMIT_MOV(compiler, TMP_REG2, 0, src, srcw); + src = TMP_REG2; + srcw = 0; + } + + FAIL_IF(emit_stack_frame_release(compiler, 1)); + + SLJIT_SKIP_CHECKS(compiler); + return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); +} + /* --------------------------------------------------------------------- */ /* Call / return instructions */ /* --------------------------------------------------------------------- */ @@ -786,17 +817,15 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compile compiler->mode32 = 0; - PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL)); + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL)); if (type & SLJIT_CALL_RETURN) { - PTR_FAIL_IF(emit_stack_frame_release(compiler)); + PTR_FAIL_IF(emit_stack_frame_release(compiler, 0)); type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP); } -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_jump(compiler, type); } @@ -816,22 +845,21 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compi } if (type & SLJIT_CALL_RETURN) { - if (src >= SLJIT_FIRST_SAVED_REG && src <= SLJIT_S0) { + if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { EMIT_MOV(compiler, TMP_REG2, 0, src, srcw); src = TMP_REG2; } - FAIL_IF(emit_stack_frame_release(compiler)); - type = SLJIT_JUMP; + FAIL_IF(emit_stack_frame_release(compiler, 0)); } - FAIL_IF(call_with_args(compiler, arg_types, &src)); + if ((type & 0xff) != SLJIT_CALL_REG_ARG) + FAIL_IF(call_with_args(compiler, arg_types, &src)); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + if (type & SLJIT_CALL_RETURN) + type = SLJIT_JUMP; + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_ijump(compiler, type, src, srcw); } @@ -907,9 +935,89 @@ static sljit_s32 emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src } /* --------------------------------------------------------------------- */ -/* Extend input */ +/* Other operations */ /* --------------------------------------------------------------------- */ +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_u8* inst; + sljit_s32 i, next, reg_idx; + sljit_u8 regs[2]; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + + if (!(reg & REG_PAIR_MASK)) + return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw); + + ADJUST_LOCAL_OFFSET(mem, memw); + + compiler->mode32 = 0; + + if ((mem & REG_MASK) == 0) { + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, memw); + + mem = SLJIT_MEM1(TMP_REG1); + memw = 0; + } else if (!(mem & OFFS_REG_MASK) && ((memw < HALFWORD_MIN) || (memw > HALFWORD_MAX - SSIZE_OF(sw)))) { + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, memw); + + mem = SLJIT_MEM2(mem & REG_MASK, TMP_REG1); + memw = 0; + } + + regs[0] = U8(REG_PAIR_FIRST(reg)); + regs[1] = U8(REG_PAIR_SECOND(reg)); + + next = SSIZE_OF(sw); + + if (!(type & SLJIT_MEM_STORE) && (regs[0] == (mem & REG_MASK) || regs[0] == OFFS_REG(mem))) { + if (regs[1] == (mem & REG_MASK) || regs[1] == OFFS_REG(mem)) { + /* Base and offset cannot be TMP_REG1. */ + EMIT_MOV(compiler, TMP_REG1, 0, OFFS_REG(mem), 0); + + if (regs[1] == OFFS_REG(mem)) + next = -SSIZE_OF(sw); + + mem = (mem & ~OFFS_REG_MASK) | TO_OFFS_REG(TMP_REG1); + } else { + next = -SSIZE_OF(sw); + + if (!(mem & OFFS_REG_MASK)) + memw += SSIZE_OF(sw); + } + } + + for (i = 0; i < 2; i++) { + reg_idx = next > 0 ? i : (i ^ 0x1); + reg = regs[reg_idx]; + + if ((mem & OFFS_REG_MASK) && (reg_idx == 1)) { + inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(1 + 5)); + FAIL_IF(!inst); + + INC_SIZE(5); + + inst[0] = U8(REX_W | ((reg_map[reg] >= 8) ? REX_R : 0) | ((reg_map[mem & REG_MASK] >= 8) ? REX_B : 0) | ((reg_map[OFFS_REG(mem)] >= 8) ? REX_X : 0)); + inst[1] = (type & SLJIT_MEM_STORE) ? MOV_rm_r : MOV_r_rm; + inst[2] = 0x44 | U8(reg_lmap[reg] << 3); + inst[3] = U8(memw << 6) | U8(reg_lmap[OFFS_REG(mem)] << 3) | reg_lmap[mem & REG_MASK]; + inst[4] = sizeof(sljit_sw); + } else if (type & SLJIT_MEM_STORE) { + EMIT_MOV(compiler, mem, memw, reg, 0); + } else { + EMIT_MOV(compiler, reg, 0, mem, memw); + } + + if (!(mem & OFFS_REG_MASK)) + memw += next; + } + + return SLJIT_SUCCESS; +} + static sljit_s32 emit_mov_int(struct sljit_compiler *compiler, sljit_s32 sign, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) diff --git a/thirdparty/pcre2/src/sljit/sljitNativeX86_common.c b/thirdparty/pcre2/src/sljit/sljitNativeX86_common.c index c7dd9be8fd..651942be80 100644 --- a/thirdparty/pcre2/src/sljit/sljitNativeX86_common.c +++ b/thirdparty/pcre2/src/sljit/sljitNativeX86_common.c @@ -26,11 +26,7 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) { -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - return "x86" SLJIT_CPUINFO " ABI:fastcall"; -#else return "x86" SLJIT_CPUINFO; -#endif } /* @@ -78,10 +74,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 3] = { #define CHECK_EXTRA_REGS(p, w, do) \ if (p >= SLJIT_R3 && p <= SLJIT_S3) { \ - if (p <= compiler->scratches) \ - w = compiler->scratches_offset + ((p) - SLJIT_R3) * SSIZE_OF(sw); \ - else \ - w = compiler->locals_offset + ((p) - SLJIT_S2) * SSIZE_OF(sw); \ + w = (2 * SSIZE_OF(sw)) + ((p) - SLJIT_R3) * SSIZE_OF(sw); \ p = SLJIT_MEM1(SLJIT_SP); \ do; \ } @@ -181,6 +174,7 @@ static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = { #define AND_rm_r 0x21 #define ANDPD_x_xm 0x54 #define BSR_r_rm (/* GROUP_0F */ 0xbd) +#define BSF_r_rm (/* GROUP_0F */ 0xbc) #define CALL_i32 0xe8 #define CALL_rm (/* GROUP_FF */ 2 << 3) #define CDQ 0x99 @@ -194,6 +188,8 @@ static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = { #define CVTTSD2SI_r_xm 0x2c #define DIV (/* GROUP_F7 */ 6 << 3) #define DIVSD_x_xm 0x5e +#define FLDS 0xd9 +#define FLDL 0xdd #define FSTPS 0xd9 #define FSTPD 0xdd #define INT3 0xcc @@ -209,6 +205,7 @@ static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = { #define JMP_rm (/* GROUP_FF */ 4 << 3) #define LEA_r_m 0x8d #define LOOP_i8 0xe2 +#define LZCNT_r_rm (/* GROUP_F3 */ /* GROUP_0F */ 0xbd) #define MOV_r_rm 0x8b #define MOV_r_i32 0xb8 #define MOV_rm_r 0x89 @@ -242,6 +239,8 @@ static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = { #define PUSH_r 0x50 #define PUSH_rm (/* GROUP_FF */ 6 << 3) #define PUSHF 0x9c +#define ROL (/* SHIFT */ 0 << 3) +#define ROR (/* SHIFT */ 1 << 3) #define RET_near 0xc3 #define RET_i16 0xc2 #define SBB (/* BINARY */ 3 << 3) @@ -250,6 +249,8 @@ static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = { #define SBB_rm_r 0x19 #define SAR (/* SHIFT */ 7 << 3) #define SHL (/* SHIFT */ 4 << 3) +#define SHLD (/* GROUP_0F */ 0xa5) +#define SHRD (/* GROUP_0F */ 0xad) #define SHR (/* SHIFT */ 5 << 3) #define SUB (/* BINARY */ 5 << 3) #define SUB_EAX_i32 0x2d @@ -258,6 +259,7 @@ static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = { #define SUBSD_x_xm 0x5c #define TEST_EAX_i32 0xa9 #define TEST_rm_r 0x85 +#define TZCNT_r_rm (/* GROUP_F3 */ /* GROUP_0F */ 0xbc) #define UCOMISD_x_xm 0x2e #define UNPCKLPD_x_xm 0x14 #define XCHG_EAX_r 0x90 @@ -269,6 +271,7 @@ static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = { #define XORPD_x_xm 0x57 #define GROUP_0F 0x0f +#define GROUP_F3 0xf3 #define GROUP_F7 0xf7 #define GROUP_FF 0xff #define GROUP_BINARY_81 0x81 @@ -290,10 +293,15 @@ static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = { /* Multithreading does not affect these static variables, since they store built-in CPU features. Therefore they can be overwritten by different threads if they detect the CPU features in the same time. */ +#define CPU_FEATURE_DETECTED 0x001 #if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2) -static sljit_s32 cpu_has_sse2 = -1; +#define CPU_FEATURE_SSE2 0x002 #endif -static sljit_s32 cpu_has_cmov = -1; +#define CPU_FEATURE_LZCNT 0x004 +#define CPU_FEATURE_TZCNT 0x008 +#define CPU_FEATURE_CMOV 0x010 + +static sljit_u32 cpu_feature_list = 0; #ifdef _WIN32_WCE #include <cmnintrin.h> @@ -326,18 +334,65 @@ static SLJIT_INLINE void sljit_unaligned_store_sw(void *addr, sljit_sw value) static void get_cpu_features(void) { - sljit_u32 features; + sljit_u32 feature_list = CPU_FEATURE_DETECTED; + sljit_u32 value; #if defined(_MSC_VER) && _MSC_VER >= 1400 int CPUInfo[4]; + + __cpuid(CPUInfo, 0); + if (CPUInfo[0] >= 7) { + __cpuidex(CPUInfo, 7, 0); + if (CPUInfo[1] & 0x8) + feature_list |= CPU_FEATURE_TZCNT; + } + + __cpuid(CPUInfo, (int)0x80000001); + if (CPUInfo[2] & 0x20) + feature_list |= CPU_FEATURE_LZCNT; + __cpuid(CPUInfo, 1); - features = (sljit_u32)CPUInfo[3]; + value = (sljit_u32)CPUInfo[3]; #elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_C) /* AT&T syntax. */ __asm__ ( + "movl $0x0, %%eax\n" + "lzcnt %%eax, %%eax\n" + "setnz %%al\n" + "movl %%eax, %0\n" + : "=g" (value) + : +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + : "eax" +#else + : "rax" +#endif + ); + + if (value & 0x1) + feature_list |= CPU_FEATURE_LZCNT; + + __asm__ ( + "movl $0x0, %%eax\n" + "tzcnt %%eax, %%eax\n" + "setnz %%al\n" + "movl %%eax, %0\n" + : "=g" (value) + : +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + : "eax" +#else + : "rax" +#endif + ); + + if (value & 0x1) + feature_list |= CPU_FEATURE_TZCNT; + + __asm__ ( "movl $0x1, %%eax\n" #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) /* On x86-32, there is no red zone, so this @@ -349,7 +404,7 @@ static void get_cpu_features(void) "pop %%ebx\n" #endif "movl %%edx, %0\n" - : "=g" (features) + : "=g" (value) : #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) : "%eax", "%ecx", "%edx" @@ -362,46 +417,82 @@ static void get_cpu_features(void) /* Intel syntax. */ __asm { + mov eax, 0 + lzcnt eax, eax + setnz al + mov value, eax + } + + if (value & 0x1) + feature_list |= CPU_FEATURE_LZCNT; + + __asm { + mov eax, 0 + tzcnt eax, eax + setnz al + mov value, eax + } + + if (value & 0x1) + feature_list |= CPU_FEATURE_TZCNT; + + __asm { mov eax, 1 cpuid - mov features, edx + mov value, edx } #endif /* _MSC_VER && _MSC_VER >= 1400 */ #if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2) - cpu_has_sse2 = (features >> 26) & 0x1; + if (value & 0x4000000) + feature_list |= CPU_FEATURE_SSE2; #endif - cpu_has_cmov = (features >> 15) & 0x1; + if (value & 0x8000) + feature_list |= CPU_FEATURE_CMOV; + + cpu_feature_list = feature_list; } static sljit_u8 get_jump_code(sljit_uw type) { switch (type) { case SLJIT_EQUAL: - case SLJIT_EQUAL_F64: + case SLJIT_F_EQUAL: + case SLJIT_UNORDERED_OR_EQUAL: + case SLJIT_ORDERED_EQUAL: /* Not supported. */ return 0x84 /* je */; case SLJIT_NOT_EQUAL: - case SLJIT_NOT_EQUAL_F64: + case SLJIT_F_NOT_EQUAL: + case SLJIT_ORDERED_NOT_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: /* Not supported. */ return 0x85 /* jne */; case SLJIT_LESS: case SLJIT_CARRY: - case SLJIT_LESS_F64: + case SLJIT_F_LESS: + case SLJIT_UNORDERED_OR_LESS: + case SLJIT_UNORDERED_OR_GREATER: return 0x82 /* jc */; case SLJIT_GREATER_EQUAL: case SLJIT_NOT_CARRY: - case SLJIT_GREATER_EQUAL_F64: + case SLJIT_F_GREATER_EQUAL: + case SLJIT_ORDERED_GREATER_EQUAL: + case SLJIT_ORDERED_LESS_EQUAL: return 0x83 /* jae */; case SLJIT_GREATER: - case SLJIT_GREATER_F64: + case SLJIT_F_GREATER: + case SLJIT_ORDERED_LESS: + case SLJIT_ORDERED_GREATER: return 0x87 /* jnbe */; case SLJIT_LESS_EQUAL: - case SLJIT_LESS_EQUAL_F64: + case SLJIT_F_LESS_EQUAL: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + case SLJIT_UNORDERED_OR_LESS_EQUAL: return 0x86 /* jbe */; case SLJIT_SIG_LESS: @@ -422,10 +513,10 @@ static sljit_u8 get_jump_code(sljit_uw type) case SLJIT_NOT_OVERFLOW: return 0x81 /* jno */; - case SLJIT_UNORDERED_F64: + case SLJIT_UNORDERED: return 0x8a /* jp */; - case SLJIT_ORDERED_F64: + case SLJIT_ORDERED: return 0x8b /* jpo */; } return 0; @@ -449,13 +540,13 @@ static sljit_u8* generate_near_jump_code(struct sljit_jump *jump, sljit_u8 *code else label_addr = jump->u.target - (sljit_uw)executable_offset; - short_jump = (sljit_sw)(label_addr - (jump->addr + 2)) >= -128 && (sljit_sw)(label_addr - (jump->addr + 2)) <= 127; - #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) if ((sljit_sw)(label_addr - (jump->addr + 1)) > HALFWORD_MAX || (sljit_sw)(label_addr - (jump->addr + 1)) < HALFWORD_MIN) return generate_far_jump_code(jump, code_ptr); #endif + short_jump = (sljit_sw)(label_addr - (jump->addr + 2)) >= -128 && (sljit_sw)(label_addr - (jump->addr + 2)) <= 127; + if (type == SLJIT_JUMP) { if (short_jump) *code_ptr++ = JMP_i8; @@ -581,32 +672,33 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil jump = compiler->jumps; while (jump) { - jump_addr = jump->addr + (sljit_uw)executable_offset; + if (jump->flags & (PATCH_MB | PATCH_MW)) { + if (jump->flags & JUMP_LABEL) + jump_addr = jump->u.label->addr; + else + jump_addr = jump->u.target; - if (jump->flags & PATCH_MB) { - SLJIT_ASSERT((sljit_sw)(jump->u.label->addr - (jump_addr + sizeof(sljit_s8))) >= -128 && (sljit_sw)(jump->u.label->addr - (jump_addr + sizeof(sljit_s8))) <= 127); - *(sljit_u8*)jump->addr = U8(jump->u.label->addr - (jump_addr + sizeof(sljit_s8))); - } else if (jump->flags & PATCH_MW) { - if (jump->flags & JUMP_LABEL) { -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)(jump->u.label->addr - (jump_addr + sizeof(sljit_sw)))); -#else - SLJIT_ASSERT((sljit_sw)(jump->u.label->addr - (jump_addr + sizeof(sljit_s32))) >= HALFWORD_MIN && (sljit_sw)(jump->u.label->addr - (jump_addr + sizeof(sljit_s32))) <= HALFWORD_MAX); - sljit_unaligned_store_s32((void*)jump->addr, (sljit_s32)(jump->u.label->addr - (jump_addr + sizeof(sljit_s32)))); -#endif - } - else { + jump_addr -= jump->addr + (sljit_uw)executable_offset; + + if (jump->flags & PATCH_MB) { + jump_addr -= sizeof(sljit_s8); + SLJIT_ASSERT((sljit_sw)jump_addr >= -128 && (sljit_sw)jump_addr <= 127); + *(sljit_u8*)jump->addr = U8(jump_addr); + } else { + jump_addr -= sizeof(sljit_s32); #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)(jump->u.target - (jump_addr + sizeof(sljit_sw)))); + sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)jump_addr); #else - SLJIT_ASSERT((sljit_sw)(jump->u.target - (jump_addr + sizeof(sljit_s32))) >= HALFWORD_MIN && (sljit_sw)(jump->u.target - (jump_addr + sizeof(sljit_s32))) <= HALFWORD_MAX); - sljit_unaligned_store_s32((void*)jump->addr, (sljit_s32)(jump->u.target - (jump_addr + sizeof(sljit_s32)))); + SLJIT_ASSERT((sljit_sw)jump_addr >= HALFWORD_MIN && (sljit_sw)jump_addr <= HALFWORD_MAX); + sljit_unaligned_store_s32((void*)jump->addr, (sljit_s32)jump_addr); #endif } } #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - else if (jump->flags & PATCH_MD) - sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)jump->u.label->addr); + else if (jump->flags & PATCH_MD) { + SLJIT_ASSERT(jump->flags & JUMP_LABEL); + sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)jump->u.label->addr); + } #endif jump = jump->next; @@ -647,9 +739,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) #ifdef SLJIT_IS_FPU_AVAILABLE return SLJIT_IS_FPU_AVAILABLE; #elif (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2) - if (cpu_has_sse2 == -1) + if (cpu_feature_list == 0) get_cpu_features(); - return cpu_has_sse2; + return (cpu_feature_list & CPU_FEATURE_SSE2) != 0; #else /* SLJIT_DETECT_SSE2 */ return 1; #endif /* SLJIT_DETECT_SSE2 */ @@ -657,31 +749,57 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) case SLJIT_HAS_VIRTUAL_REGISTERS: return 1; -#endif +#endif /* SLJIT_CONFIG_X86_32 */ case SLJIT_HAS_CLZ: + if (cpu_feature_list == 0) + get_cpu_features(); + + return (cpu_feature_list & CPU_FEATURE_LZCNT) ? 1 : 2; + + case SLJIT_HAS_CTZ: + if (cpu_feature_list == 0) + get_cpu_features(); + + return (cpu_feature_list & CPU_FEATURE_TZCNT) ? 1 : 2; + case SLJIT_HAS_CMOV: - if (cpu_has_cmov == -1) + if (cpu_feature_list == 0) get_cpu_features(); - return cpu_has_cmov; + return (cpu_feature_list & CPU_FEATURE_CMOV) != 0; + case SLJIT_HAS_ROT: case SLJIT_HAS_PREFETCH: return 1; case SLJIT_HAS_SSE2: #if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2) - if (cpu_has_sse2 == -1) + if (cpu_feature_list == 0) get_cpu_features(); - return cpu_has_sse2; -#else + return (cpu_feature_list & CPU_FEATURE_SSE2) != 0; +#else /* !SLJIT_DETECT_SSE2 */ return 1; -#endif +#endif /* SLJIT_DETECT_SSE2 */ default: return 0; } } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type) +{ + if (type < SLJIT_UNORDERED || type > SLJIT_ORDERED_LESS_EQUAL) + return 0; + + switch (type) { + case SLJIT_ORDERED_EQUAL: + case SLJIT_UNORDERED_OR_NOT_EQUAL: + return 0; + } + + return 1; +} + /* --------------------------------------------------------------------- */ /* Operators */ /* --------------------------------------------------------------------- */ @@ -1385,47 +1503,75 @@ static sljit_s32 emit_not_with_flags(struct sljit_compiler *compiler, #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) static const sljit_sw emit_clz_arg = 32 + 31; +static const sljit_sw emit_ctz_arg = 32; #endif -static sljit_s32 emit_clz(struct sljit_compiler *compiler, sljit_s32 op_flags, +static sljit_s32 emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 is_clz, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) { sljit_u8* inst; sljit_s32 dst_r; + sljit_sw max; - SLJIT_UNUSED_ARG(op_flags); - - if (cpu_has_cmov == -1) + if (cpu_feature_list == 0) get_cpu_features(); dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; + if (is_clz ? (cpu_feature_list & CPU_FEATURE_LZCNT) : (cpu_feature_list & CPU_FEATURE_TZCNT)) { + /* Group prefix added separately. */ + inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!inst); + INC_SIZE(1); + *inst++ = GROUP_F3; + + inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw); + FAIL_IF(!inst); + *inst++ = GROUP_0F; + *inst = is_clz ? LZCNT_r_rm : TZCNT_r_rm; + + if (dst & SLJIT_MEM) + EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0); + return SLJIT_SUCCESS; + } + inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw); FAIL_IF(!inst); *inst++ = GROUP_0F; - *inst = BSR_r_rm; + *inst = is_clz ? BSR_r_rm : BSF_r_rm; #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - if (cpu_has_cmov) { + max = is_clz ? (32 + 31) : 32; + + if (cpu_feature_list & CPU_FEATURE_CMOV) { if (dst_r != TMP_REG1) { - EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, 32 + 31); + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, max); inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG1, 0); } else - inst = emit_x86_instruction(compiler, 2, dst_r, 0, SLJIT_MEM0(), (sljit_sw)&emit_clz_arg); + inst = emit_x86_instruction(compiler, 2, dst_r, 0, SLJIT_MEM0(), is_clz ? (sljit_sw)&emit_clz_arg : (sljit_sw)&emit_ctz_arg); FAIL_IF(!inst); *inst++ = GROUP_0F; *inst = CMOVE_r_rm; } else - FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, 32 + 31)); + FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, max)); - inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 31, dst_r, 0); + if (is_clz) { + inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 31, dst_r, 0); + FAIL_IF(!inst); + *(inst + 1) |= XOR; + } #else - if (cpu_has_cmov) { - EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, !(op_flags & SLJIT_32) ? (64 + 63) : (32 + 31)); + if (is_clz) + max = compiler->mode32 ? (32 + 31) : (64 + 63); + else + max = compiler->mode32 ? 32 : 64; + + if (cpu_feature_list & CPU_FEATURE_CMOV) { + EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, max); inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0); FAIL_IF(!inst); @@ -1433,14 +1579,15 @@ static sljit_s32 emit_clz(struct sljit_compiler *compiler, sljit_s32 op_flags, *inst = CMOVE_r_rm; } else - FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, !(op_flags & SLJIT_32) ? (64 + 63) : (32 + 31))); + FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, max)); - inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, !(op_flags & SLJIT_32) ? 63 : 31, dst_r, 0); + if (is_clz) { + inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, max >> 1, dst_r, 0); + FAIL_IF(!inst); + *(inst + 1) |= XOR; + } #endif - FAIL_IF(!inst); - *(inst + 1) |= XOR; - if (dst & SLJIT_MEM) EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0); return SLJIT_SUCCESS; @@ -1578,7 +1725,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile return emit_unary(compiler, NOT_rm, dst, dstw, src, srcw); case SLJIT_CLZ: - return emit_clz(compiler, op_flags, dst, dstw, src, srcw); + case SLJIT_CTZ: + return emit_clz_ctz(compiler, (op == SLJIT_CLZ), dst, dstw, src, srcw); } return SLJIT_SUCCESS; @@ -2116,6 +2264,9 @@ static sljit_s32 emit_shift(struct sljit_compiler *compiler, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + sljit_s32 mode32; +#endif sljit_u8* inst; if ((src2 & SLJIT_IMM) || (src2 == SLJIT_PREF_SHIFT_REG)) { @@ -2155,40 +2306,61 @@ static sljit_s32 emit_shift(struct sljit_compiler *compiler, inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); FAIL_IF(!inst); *inst |= mode; - EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); + return emit_mov(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); } - else if (FAST_IS_REG(dst) && dst != src2 && dst != TMP_REG1 && !ADDRESSING_DEPENDS_ON(src2, dst)) { + + if (FAST_IS_REG(dst) && dst != src2 && dst != TMP_REG1 && !ADDRESSING_DEPENDS_ON(src2, dst)) { if (src1 != dst) EMIT_MOV(compiler, dst, 0, src1, src1w); +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + mode32 = compiler->mode32; + compiler->mode32 = 0; +#endif EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_PREF_SHIFT_REG, 0); +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = mode32; +#endif EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, dst, 0); FAIL_IF(!inst); *inst |= mode; +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 0; +#endif EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = mode32; +#endif + return SLJIT_SUCCESS; } - else { - /* This case is complex since ecx itself may be used for - addressing, and this case must be supported as well. */ - EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w); + + /* This case is complex since ecx itself may be used for + addressing, and this case must be supported as well. */ + EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w); #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), 0, SLJIT_PREF_SHIFT_REG, 0); - EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); - inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); - FAIL_IF(!inst); - *inst |= mode; - EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, SLJIT_MEM1(SLJIT_SP), 0); + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), 0, SLJIT_PREF_SHIFT_REG, 0); +#else /* !SLJIT_CONFIG_X86_32 */ + mode32 = compiler->mode32; + compiler->mode32 = 0; + EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_PREF_SHIFT_REG, 0); + compiler->mode32 = mode32; +#endif /* SLJIT_CONFIG_X86_32 */ + + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); + inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); + FAIL_IF(!inst); + *inst |= mode; + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, SLJIT_MEM1(SLJIT_SP), 0); #else - EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_PREF_SHIFT_REG, 0); - EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); - inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); - FAIL_IF(!inst); - *inst |= mode; - EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG2, 0); -#endif - if (dst != TMP_REG1) - return emit_mov(compiler, dst, dstw, TMP_REG1, 0); - } + compiler->mode32 = 0; + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG2, 0); + compiler->mode32 = mode32; +#endif /* SLJIT_CONFIG_X86_32 */ + + if (dst != TMP_REG1) + return emit_mov(compiler, dst, dstw, TMP_REG1, 0); return SLJIT_SUCCESS; } @@ -2202,12 +2374,13 @@ static sljit_s32 emit_shift_with_flags(struct sljit_compiler *compiler, /* The CPU does not set flags if the shift count is 0. */ if (src2 & SLJIT_IMM) { #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - if ((src2w & 0x3f) != 0 || (compiler->mode32 && (src2w & 0x1f) != 0)) - return emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w); -#else - if ((src2w & 0x1f) != 0) + src2w &= compiler->mode32 ? 0x1f : 0x3f; +#else /* !SLJIT_CONFIG_X86_64 */ + src2w &= 0x1f; +#endif /* SLJIT_CONFIG_X86_64 */ + if (src2w != 0) return emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w); -#endif + if (!set_flags) return emit_mov(compiler, dst, dstw, src1, src1w); /* OR dst, src, 0 */ @@ -2289,14 +2462,23 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile return emit_cum_binary(compiler, BINARY_OPCODE(XOR), dst, dstw, src1, src1w, src2, src2w); case SLJIT_SHL: + case SLJIT_MSHL: return emit_shift_with_flags(compiler, SHL, HAS_FLAGS(op), dst, dstw, src1, src1w, src2, src2w); case SLJIT_LSHR: + case SLJIT_MLSHR: return emit_shift_with_flags(compiler, SHR, HAS_FLAGS(op), dst, dstw, src1, src1w, src2, src2w); case SLJIT_ASHR: + case SLJIT_MASHR: return emit_shift_with_flags(compiler, SAR, HAS_FLAGS(op), dst, dstw, src1, src1w, src2, src2w); + case SLJIT_ROTL: + return emit_shift_with_flags(compiler, ROL, 0, + dst, dstw, src1, src1w, src2, src2w); + case SLJIT_ROTR: + return emit_shift_with_flags(compiler, ROR, 0, + dst, dstw, src1, src1w, src2, src2w); } return SLJIT_SUCCESS; @@ -2312,10 +2494,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compil CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w)); if (opcode != SLJIT_SUB && opcode != SLJIT_AND) { -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_op2(compiler, op, TMP_REG1, 0, src1, src1w, src2, src2w); } @@ -2334,6 +2513,122 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compil return emit_test_binary(compiler, src1, src1w, src2, src2w); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src_dst, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +{ + sljit_s32 restore_ecx = 0; + sljit_s32 is_rotate, is_left; + sljit_u8* inst; + sljit_sw dstw = 0; +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + sljit_s32 tmp2 = SLJIT_MEM1(SLJIT_SP); +#else /* !SLJIT_CONFIG_X86_32 */ + sljit_s32 tmp2 = TMP_REG2; +#endif /* SLJIT_CONFIG_X86_32 */ + + CHECK_ERROR(); + CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w)); + ADJUST_LOCAL_OFFSET(src1, src1w); + ADJUST_LOCAL_OFFSET(src2, src2w); + + CHECK_EXTRA_REGS(src1, src1w, (void)0); + CHECK_EXTRA_REGS(src2, src2w, (void)0); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = op & SLJIT_32; +#endif + + if (src2 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + src2w &= 0x1f; +#else /* !SLJIT_CONFIG_X86_32 */ + src2w &= (op & SLJIT_32) ? 0x1f : 0x3f; +#endif /* SLJIT_CONFIG_X86_32 */ + + if (src2w == 0) + return SLJIT_SUCCESS; + } + + is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL); + + is_rotate = (src_dst == src1); + CHECK_EXTRA_REGS(src_dst, dstw, (void)0); + + if (is_rotate) + return emit_shift(compiler, is_left ? ROL : ROR, src_dst, dstw, src1, src1w, src2, src2w); + + if ((src2 & SLJIT_IMM) || src2 == SLJIT_PREF_SHIFT_REG) { + if (!FAST_IS_REG(src1)) { + EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w); + src1 = TMP_REG1; + } + } else if (FAST_IS_REG(src1)) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 0; +#endif + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_PREF_SHIFT_REG, 0); +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = op & SLJIT_32; +#endif + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); + + if (src1 == SLJIT_PREF_SHIFT_REG) + src1 = TMP_REG1; + + if (src_dst == SLJIT_PREF_SHIFT_REG) + src_dst = TMP_REG1; + + restore_ecx = 1; + } else { + EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w); +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 0; +#endif + EMIT_MOV(compiler, tmp2, 0, SLJIT_PREF_SHIFT_REG, 0); +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = op & SLJIT_32; +#endif + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); + + src1 = TMP_REG1; + + if (src_dst == SLJIT_PREF_SHIFT_REG) { + src_dst = tmp2; + SLJIT_ASSERT(dstw == 0); + } + + restore_ecx = 2; + } + + inst = emit_x86_instruction(compiler, 2, src1, 0, src_dst, dstw); + FAIL_IF(!inst); + inst[0] = GROUP_0F; + + if (src2 & SLJIT_IMM) { + inst[1] = U8((is_left ? SHLD : SHRD) - 1); + + /* Immedate argument is added separately. */ + inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!inst); + INC_SIZE(1); + *inst = U8(src2w); + } else + inst[1] = U8(is_left ? SHLD : SHRD); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 0; +#endif + + if (restore_ecx == 1) + return emit_mov(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); + if (restore_ecx == 2) + return emit_mov(compiler, SLJIT_PREF_SHIFT_REG, 0, tmp2, 0); + + return SLJIT_SUCCESS; +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { @@ -2516,6 +2811,19 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { + switch (GET_FLAG_TYPE(op)) { + case SLJIT_ORDERED_LESS: + case SLJIT_UNORDERED_OR_GREATER_EQUAL: + case SLJIT_UNORDERED_OR_GREATER: + case SLJIT_ORDERED_LESS_EQUAL: + if (!FAST_IS_REG(src2)) { + FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src2, src2w)); + src2 = TMP_FREG; + } + + return emit_sse2_logic(compiler, UCOMISD_x_xm, !(op & SLJIT_32), src2, src1, src1w); + } + if (!FAST_IS_REG(src1)) { FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src1, src1w)); src1 = TMP_FREG; @@ -2769,7 +3077,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co ADJUST_LOCAL_OFFSET(dst, dstw); CHECK_EXTRA_REGS(dst, dstw, (void)0); - type &= 0xff; /* setcc = jcc + 0x10. */ cond_set = U8(get_jump_code((sljit_uw)type) + 0x10); @@ -2813,10 +3120,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co return emit_mov(compiler, dst, dstw, TMP_REG1, 0); } -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_op2(compiler, op, dst_save, dstw_save, dst_save, dstw_save, TMP_REG1, 0); #else @@ -2839,10 +3143,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co } /* Low byte is not accessible. */ - if (cpu_has_cmov == -1) + if (cpu_feature_list == 0) get_cpu_features(); - if (cpu_has_cmov) { + if (cpu_feature_list & CPU_FEATURE_CMOV) { EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, 1); /* a xor reg, reg operation would overwrite the flags. */ EMIT_MOV(compiler, dst, 0, SLJIT_IMM, 0); @@ -2927,10 +3231,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co if (GET_OPCODE(op) < SLJIT_ADD) return emit_mov(compiler, dst, dstw, TMP_REG1, 0); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif + SLJIT_SKIP_CHECKS(compiler); return sljit_emit_op2(compiler, op, dst_save, dstw_save, dst_save, dstw_save, TMP_REG1, 0); #endif /* SLJIT_CONFIG_X86_64 */ } @@ -2945,7 +3246,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - dst_reg &= ~SLJIT_32; + type &= ~SLJIT_32; if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV) || (dst_reg >= SLJIT_R3 && dst_reg <= SLJIT_S3)) return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw); @@ -2958,8 +3259,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil CHECK_EXTRA_REGS(src, srcw, (void)0); #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - compiler->mode32 = dst_reg & SLJIT_32; - dst_reg &= ~SLJIT_32; + compiler->mode32 = type & SLJIT_32; + type &= ~SLJIT_32; #endif if (SLJIT_UNLIKELY(src & SLJIT_IMM)) { @@ -2971,7 +3272,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compil inst = emit_x86_instruction(compiler, 2, dst_reg, 0, src, srcw); FAIL_IF(!inst); *inst++ = GROUP_0F; - *inst = U8(get_jump_code(type & 0xff) - 0x40); + *inst = U8(get_jump_code((sljit_uw)type) - 0x40); return SLJIT_SUCCESS; } diff --git a/thirdparty/pcre2/src/sljit/sljitWXExecAllocator.c b/thirdparty/pcre2/src/sljit/sljitWXExecAllocator.c index 72d5b8dd2b..6893813155 100644 --- a/thirdparty/pcre2/src/sljit/sljitWXExecAllocator.c +++ b/thirdparty/pcre2/src/sljit/sljitWXExecAllocator.c @@ -59,38 +59,15 @@ #include <sys/mman.h> #ifdef __NetBSD__ -#if defined(PROT_MPROTECT) -#define check_se_protected(ptr, size) (0) #define SLJIT_PROT_WX PROT_MPROTECT(PROT_EXEC) -#else /* !PROT_MPROTECT */ -#ifdef _NETBSD_SOURCE -#include <sys/param.h> -#else /* !_NETBSD_SOURCE */ -typedef unsigned int u_int; -#define devmajor_t sljit_s32 -#endif /* _NETBSD_SOURCE */ -#include <sys/sysctl.h> -#include <unistd.h> - -#define check_se_protected(ptr, size) netbsd_se_protected() - -static SLJIT_INLINE int netbsd_se_protected(void) -{ - int mib[3]; - int paxflags; - size_t len = sizeof(paxflags); - - mib[0] = CTL_PROC; - mib[1] = getpid(); - mib[2] = PROC_PID_PAXFLAGS; - - if (SLJIT_UNLIKELY(sysctl(mib, 3, &paxflags, &len, NULL, 0) < 0)) - return -1; - - return (paxflags & CTL_PROC_PAXFLAGS_MPROTECT) ? -1 : 0; -} -#endif /* PROT_MPROTECT */ +#define check_se_protected(ptr, size) (0) #else /* POSIX */ +#if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED) +#include <pthread.h> +#define SLJIT_SE_LOCK() pthread_mutex_lock(&se_lock) +#define SLJIT_SE_UNLOCK() pthread_mutex_unlock(&se_lock) +#endif /* !SLJIT_SINGLE_THREADED */ + #define check_se_protected(ptr, size) generic_se_protected(ptr, size) static SLJIT_INLINE int generic_se_protected(void *ptr, sljit_uw size) @@ -102,22 +79,20 @@ static SLJIT_INLINE int generic_se_protected(void *ptr, sljit_uw size) } #endif /* NetBSD */ -#if defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED +#ifndef SLJIT_SE_LOCK #define SLJIT_SE_LOCK() +#endif +#ifndef SLJIT_SE_UNLOCK #define SLJIT_SE_UNLOCK() -#else /* !SLJIT_SINGLE_THREADED */ -#include <pthread.h> -#define SLJIT_SE_LOCK() pthread_mutex_lock(&se_lock) -#define SLJIT_SE_UNLOCK() pthread_mutex_unlock(&se_lock) -#endif /* SLJIT_SINGLE_THREADED */ - +#endif #ifndef SLJIT_PROT_WX #define SLJIT_PROT_WX 0 -#endif /* !SLJIT_PROT_WX */ +#endif SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size) { -#if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED) +#if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED) \ + && !defined(__NetBSD__) static pthread_mutex_t se_lock = PTHREAD_MUTEX_INITIALIZER; #endif static int se_protected = !SLJIT_PROT_WX; |