diff options
146 files changed, 2491 insertions, 2486 deletions
@@ -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/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/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/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/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/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..bffa395770 100644 --- a/doc/classes/AStarGrid2D.xml +++ b/doc/classes/AStarGrid2D.xml @@ -103,6 +103,7 @@ <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="update"> @@ -134,20 +135,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 +162,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/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/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/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/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/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/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/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index e222894647..f587ababfe 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> 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/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..637e502d49 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1097,6 +1097,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 +1439,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 +1548,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/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..be18b008be 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; } @@ -1217,26 +1226,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 +1246,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 +1270,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 +1373,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 +1404,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 +1430,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 +1458,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); @@ -2416,8 +2415,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 +2426,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 +2580,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 +2593,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 +2602,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; 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/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..e7eeacf576 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); } @@ -3044,10 +3050,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 +3140,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 +3148,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); } @@ -3291,10 +3207,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 +3383,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 +3395,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); } @@ -3636,10 +3459,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 +3515,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 +3523,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,7 +3560,7 @@ 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); // Set sampler state here as the same texture can be used in multiple places with different flags 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/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 45f544c02d..f9348311a4 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 @@ -4524,14 +4523,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 +4553,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 +4585,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 +4688,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 +4794,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 +4895,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 +4965,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 +4995,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 +5071,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 +5148,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 +5159,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 +5469,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 +5521,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 +5566,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 +5728,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 +6380,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 +6495,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 +7140,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 +7756,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/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index deca638f3b..5cb7016b35 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -751,7 +751,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) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 4c3c9973cb..02cfd3e873 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2754,11 +2754,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 +2779,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; 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/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 730aa3bd61..1e406e6d17 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(); 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/project_manager.cpp b/editor/project_manager.cpp index a1b2aa3089..1b169076c6 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -446,7 +446,7 @@ 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("• ") + TTR("Supports desktop, mobile + web platforms.") + String::utf8("\n• ") + TTR("Least advanced 3D graphics.") + 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).") + 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/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 91f31174dd..258f1a80f7 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1288,15 +1288,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); } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 1149749ef7..30ac1f29b1 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; + } + + if (member.get_datatype().is_set()) { return; } - p_class->resolved_interface = true; + + 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,6 +899,8 @@ 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); + 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; @@ -803,6 +922,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 +933,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 +961,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 +971,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); @@ -878,12 +1002,22 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } 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 +1028,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 +1187,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 +1274,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 +1298,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 +1383,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 +1401,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 +1511,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; } @@ -2745,7 +2998,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 +3021,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 +3117,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; @@ -2887,14 +3144,8 @@ 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()); - break; default: break; // Type already set. } @@ -2907,33 +3158,28 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod get_class_node_current_scope_classes(parser->current_class, &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,7 +3385,7 @@ 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()); } @@ -3153,7 +3399,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident 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()); } @@ -3347,53 +3593,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_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 +3875,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 +3883,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()); @@ -3752,7 +3988,6 @@ 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()) { @@ -3774,12 +4009,21 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va error_type.kind = GDScriptParser::DataType::VARIANT; return error_type; } - ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + ref->raise_status(GDScriptParserRef::INHERITANCE_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) { + if (!found->has_member(E)) { + return GDScriptParser::DataType(); + } + + if (found->get_member(E).type != GDScriptParser::ClassNode::Member::CLASS) { + return GDScriptParser::DataType(); + } + + resolve_class_member(found, E, p_source); + found = found->get_member(E).m_class; } @@ -3787,8 +4031,9 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va 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_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 2a98b856ce..4740b9b5a9 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -157,6 +157,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(); 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/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/features/out_of_order.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd new file mode 100644 index 0000000000..069e54c528 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd @@ -0,0 +1,50 @@ +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, + EV2 = EV3 + 1 +} + +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/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/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/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 512643867b..046973d193 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -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..2cee360f42 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -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/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/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/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/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/tile_map.cpp b/scene/2d/tile_map.cpp index 3ae7a0b34d..842b614032 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1996,7 +1996,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; 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 b5b37bf837..0f214ecd97 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 { @@ -134,7 +135,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()); } 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/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index f26e05518e..a0f2096009 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: @@ -3298,7 +3330,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 +3340,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 +3821,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 +3844,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 +3901,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())); + } } } } @@ -5287,7 +5331,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..7aa6e6fa2a 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; @@ -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/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 933d6fb7ad..e17947c27b 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) { 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 b0a63bb7fa..be9c0dc725 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -829,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); @@ -1546,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); @@ -1649,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/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/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/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..c60fa91664 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(); 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/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 36d26ad783..9a8b917802 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); 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/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 f860bab15e..a55cccdc6b 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; 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..200bc461fd 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -2001,10 +2001,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 +2212,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 +2220,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/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/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/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index 6703d8e7d0..23ee9c3842 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) { diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h index a96fc847e1..d7d8303a06 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h @@ -54,16 +54,20 @@ 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 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 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_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() {} diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 51aa81745b..8b1ff0a72a 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -1534,9 +1534,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 +1599,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 +1607,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..ef3299ba1e 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() {} 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/shader_language.h b/servers/rendering/shader_language.h index 2534d1f252..9c3cc9c5cd 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -720,6 +720,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 { 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/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); } } } |