diff --git a/gui/Cargo.toml b/gui/Cargo.toml index 276e2f1f73..cbbb971ba1 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -7,6 +7,7 @@ members = [ "lib/core/msdf-sys", "lib/data", "lib/eval-tt", + "lib/logger", "lib/optics", "lib/prelude", "lib/code-builder", @@ -15,16 +16,16 @@ members = [ ] [profile.dev] -opt-level = 1 +opt-level = 3 lto = false debug = true [profile.release] opt-level = 3 lto = true -# TODO: consider if we need it here. See discussion here: https://github.com/luna/basegl/pull/105 -debug = true +debug = false [profile.bench] opt-level = 3 lto = true +debug = false diff --git a/gui/LICENSE b/gui/LICENSE index 1e9ffa7076..0ad25db4bd 100644 --- a/gui/LICENSE +++ b/gui/LICENSE @@ -1,21 +1,661 @@ -The MIT License + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 -Copyright (c) 2019 Luna Team, Inc. http://luna-lang.org + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + Preamble -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/gui/README.md b/gui/README.md index 90938f640b..624b861968 100644 --- a/gui/README.md +++ b/gui/README.md @@ -61,6 +61,13 @@ please use the `npm run prod-server` command. **Please remember to disable the cache in your browser during development!** +### Minimizing the WASM binary size. +After building the project you can use the `scripts/minimize_wasm.sh` to optimize +the binary and compress it by using `gzip`. After the script is complete, the +final size is printed to stdout. Please note that in order to run the script, the +[Binaryen](https://github.com/WebAssembly/binaryen) toolkit has to be installed +on your system. + ### Running tests The sources use both unit tests and web test, which are run in a browser and produce visual results. To run them, use the `scripts/test.sh` script and follow diff --git a/gui/examples/webpack.common.js b/gui/examples/webpack.common.js index 35d90843cd..f718c32b50 100644 --- a/gui/examples/webpack.common.js +++ b/gui/examples/webpack.common.js @@ -25,6 +25,6 @@ module.exports = { }, performance: { hints: 'error', - maxAssetSize: 3.8 * mb, + maxAssetSize: 4.0 * mb, }, }; diff --git a/gui/lib/core/Cargo.toml b/gui/lib/core/Cargo.toml index 0bc772a292..b239c6ce48 100644 --- a/gui/lib/core/Cargo.toml +++ b/gui/lib/core/Cargo.toml @@ -8,35 +8,37 @@ edition = "2018" crate-type = ["rlib", "cdylib"] [features] -default = ["no_unboxed_callbacks"] +default = ["statistics", "no_unboxed_callbacks"] +statistics = [] no_unboxed_callbacks = [] [dependencies] -data = { version = "0.1.0" , path = "../data" } -basegl-prelude = { version = "0.1.0" , path = "../prelude" } -eval-tt = { version = "0.1.0" , path = "../eval-tt" } -optics = { version = "0.1.0" , path = "../optics" } -basegl-system-web = { version = "0.1.0" , path = "../system/web" } basegl-core-embedded-fonts = { version = "0.1.0" , path = "embedded-fonts" } basegl-core-msdf-sys = { version = "0.1.0" , path = "msdf-sys" } -shapely = { version = "0.1.0" , path = "../shapely/impl" } +basegl-prelude = { version = "0.1.0" , path = "../prelude" } +basegl-system-web = { version = "0.1.0" , path = "../system/web" } code-builder = { version = "0.1.0" , path = "../code-builder" } -wasm-bindgen = { version = "^0.2" , features = ["nightly"] } -js-sys = { version = "0.3.28" } -failure = { version = "0.1.5" } -derive_more = { version = "0.15.0" } -shrinkwraprs = { version = "0.3.0" } -itertools = { version = "0.8" } -nalgebra = { version = "0.19.0" } +data = { version = "0.1.0" , path = "../data" } +eval-tt = { version = "0.1.0" , path = "../eval-tt" } +logger = { version = "0.1.0" , path = "../logger" } +optics = { version = "0.1.0" , path = "../optics" } +shapely = { version = "0.1.0" , path = "../shapely/impl" } + bit_field = { version = "0.10.0" } -paste = { version = "0.1.6" } -enum_dispatch = { version = "0.2.0" } -typenum = { version = "1.11.2" } -rustc-hash = { version = "1.0.1" } console_error_panic_hook = { version = "0.1.6" } -num_enum = { version = "0.4.2" } -smallvec = { version = "1.0.0" } +enum_dispatch = { version = "0.2.0" } +failure = { version = "0.1.5" } Inflector = { version = "0.11.4" } +itertools = { version = "0.8" } +js-sys = { version = "0.3.28" } +nalgebra = { version = "0.19.0" } +num_enum = { version = "0.4.2" } +paste = { version = "0.1.6" } +rustc-hash = { version = "1.0.1" } +shrinkwraprs = { version = "0.3.0" } +smallvec = { version = "1.0.0" } +typenum = { version = "1.11.2" } +wasm-bindgen = { version = "^0.2" , features = ["nightly"] } [dependencies.web-sys] version = "0.3.4" @@ -50,7 +52,10 @@ features = [ 'HtmlCanvasElement', 'HtmlCollection', 'HtmlElement', + 'HtmlImageElement', + 'Location', 'Node', + 'Url', 'WebGlBuffer', 'WebGlProgram', 'WebGlRenderingContext', diff --git a/gui/lib/core/src/animation/physics/inertia.rs b/gui/lib/core/src/animation/physics/inertia.rs index 075fe0d862..eda86ca913 100644 --- a/gui/lib/core/src/animation/physics/inertia.rs +++ b/gui/lib/core/src/animation/physics/inertia.rs @@ -8,7 +8,6 @@ use crate::prelude::*; use crate::animation::animator::Animator; use crate::animation::animator::fixed_step::IntervalCounter; -use crate::math::utils::linear_interpolation; use crate::animation::position::HasPosition; use crate::system::web::animation_frame_loop::AnimationFrameLoop; @@ -20,6 +19,7 @@ use nalgebra::zero; // ==================== // === PhysicsForce === // ==================== + /// A trait for implementing 3 dimensional forces. pub trait PhysicsForce { fn force(&self, kinematics:&KinematicsProperties) -> Vector3; @@ -291,3 +291,19 @@ fn simulate(properties:&mut PhysicsProperties, delta_ms:f32) -> Vector3 { }); properties.kinematics().position() } + + + +// ============= +// === Utils === +// ============= + +use nalgebra::clamp; +use std::ops::Mul; +use std::ops::Add; + +pub fn linear_interpolation(a:T, b:T, t:f32) -> T + where T : Mul + Add { + let t = clamp(t, 0.0, 1.0); + a * (1.0 - t) + b * t +} diff --git a/gui/lib/core/src/control.rs b/gui/lib/core/src/control.rs index a889d1c843..3f40a50218 100644 --- a/gui/lib/core/src/control.rs +++ b/gui/lib/core/src/control.rs @@ -1,6 +1,4 @@ -#![allow(missing_docs)] - //! Root module for all control abstractions, like event loops or event systems. pub mod callback; -pub mod mouse_manager; +pub mod io; diff --git a/gui/lib/core/src/control/callback.rs b/gui/lib/core/src/control/callback.rs index 28307085a9..feb4d1408b 100644 --- a/gui/lib/core/src/control/callback.rs +++ b/gui/lib/core/src/control/callback.rs @@ -8,6 +8,18 @@ use crate::prelude::*; // === Callback === // ================ +/// Immutable callback type. +pub trait CallbackFn = Fn() + 'static; + +/// Immutable callback object. +pub type Callback = Box; + +/// Callback object smart constructor. +#[allow(non_snake_case)] +pub fn Callback(f:F) -> Callback { + Box::new(f) +} + /// Callback accepted by the `CallbackRegistry`. pub trait CallbackMut = FnMut() + 'static; diff --git a/gui/lib/core/src/control/io.rs b/gui/lib/core/src/control/io.rs new file mode 100644 index 0000000000..fbe048208a --- /dev/null +++ b/gui/lib/core/src/control/io.rs @@ -0,0 +1,3 @@ +//! Root module for all Input / Output events and devices. + +pub mod mouse; diff --git a/gui/lib/core/src/control/mouse_manager.rs b/gui/lib/core/src/control/io/mouse.rs similarity index 99% rename from gui/lib/core/src/control/mouse_manager.rs rename to gui/lib/core/src/control/io/mouse.rs index db1e3b2fd1..62097b6505 100644 --- a/gui/lib/core/src/control/mouse_manager.rs +++ b/gui/lib/core/src/control/io/mouse.rs @@ -1,4 +1,6 @@ -use crate::display::render::css3d::DOMContainer; +#![allow(missing_docs)] + +use crate::system::web::dom::DOMContainer; use crate::system::web::dyn_into; use crate::system::web::Result; use crate::system::web::Error; @@ -359,4 +361,4 @@ where T : FnMut(WheelEvent) + 'static { }, Err(_) => Err(Error::FailedToAddEventListener) } -} \ No newline at end of file +} diff --git a/gui/lib/core/src/data.rs b/gui/lib/core/src/data.rs index a82a1b4286..627f56a8af 100644 --- a/gui/lib/core/src/data.rs +++ b/gui/lib/core/src/data.rs @@ -4,3 +4,5 @@ pub mod container; pub mod dirty; pub mod function; pub mod seq; + +pub use data::opt_vec::OptVec; diff --git a/gui/lib/core/src/data/dirty.rs b/gui/lib/core/src/data/dirty.rs index 3593f5b61e..4a6026ee29 100644 --- a/gui/lib/core/src/data/dirty.rs +++ b/gui/lib/core/src/data/dirty.rs @@ -9,8 +9,6 @@ use crate::prelude::*; use crate::data::function::callback::*; -use crate::system::web::group; -use crate::system::web::Logger; use rustc_hash::FxHashSet; use std::hash::Hash; use std::mem; @@ -69,18 +67,18 @@ use traits::*; /// logging and callback utilities to the underlying data. Moreover, it /// implements public API for working with dirty flags. #[derive(Derivative)] -#[derivative(Debug(bound = "T:Debug"))] -pub struct DirtyFlag { +#[derivative(Debug(bound="T:Debug"))] +pub struct DirtyFlag { pub data : T, - on_set : Callback, + on_set : Function, logger : Logger, } // === Basics === -impl DirtyFlag { - pub fn new(logger: Logger, on_set:Callback) -> Self { +impl DirtyFlag { + pub fn new(logger: Logger, on_set: Function) -> Self { let data = default(); Self {data,on_set,logger} } @@ -93,36 +91,36 @@ impl DirtyFlag { // === Arguments === -impl -HasArg for DirtyFlag { +impl +HasArg for DirtyFlag { type Arg = Arg; } // === Global Operations === -impl -HasCheckAll for DirtyFlag { +impl +HasCheckAll for DirtyFlag { fn check_all(&self) -> bool { self.data.check_all() } } -impl -HasUnsetAll for DirtyFlag { +impl +HasUnsetAll for DirtyFlag { fn unset_all(&mut self) { self.data.unset_all() } } // === Check === -impl -HasCheck0 for DirtyFlag { +impl +HasCheck0 for DirtyFlag { fn check(&self) -> bool { self.data.check() } } -impl -HasCheck1 for DirtyFlag { +impl +HasCheck1 for DirtyFlag { fn check(&self, arg: &Self::Arg) -> bool { self.data.check(arg) } @@ -131,8 +129,8 @@ HasCheck1 for DirtyFlag { // === Set === -impl -HasSet0 for DirtyFlag { +impl +HasSet0 for DirtyFlag { fn set(&mut self) { let is_set = self.data.check_all(); if !is_set { @@ -144,14 +142,14 @@ HasSet0 for DirtyFlag { } } -impl -HasSet1 for DirtyFlag { +impl +HasSet1 for DirtyFlag { fn set(&mut self, arg: Self::Arg) { let first_set = !self.check_all(); let is_set = self.data.check(&arg); if !is_set { self.data.set(arg); - group!(self.logger, format!("Setting to {}.", self.data), { + group!(self.logger, "Setting to {self.data}.", { if first_set { self.on_set.call() } }) } @@ -161,19 +159,19 @@ HasSet1 for DirtyFlag { // === Unset === -impl -HasUnset0 for DirtyFlag { +impl +HasUnset0 for DirtyFlag { fn unset(&mut self) { - self.logger.info("Unsetting."); + info!(self.logger, "Unsetting."); self.data.unset() } } -impl -HasUnset1 for DirtyFlag +impl +HasUnset1 for DirtyFlag where Arg:Display { fn unset(&mut self, arg: &Self::Arg) { - self.logger.info(|| format!("Unsetting {}.", arg)); + info!(self.logger, "Unsetting {arg}."); self.data.unset(arg) } } @@ -189,19 +187,19 @@ HasUnset1 for DirtyFlag /// A version of `DirtyFlag` which uses internal mutability pattern. It is meant to expose the same /// API but without requiring `self` reference to be mutable. #[derive(Derivative)] -#[derivative(Debug(bound = "T:Debug"))] -#[derivative(Clone(bound = ""))] -pub struct SharedDirtyFlag { - rc: Rc>> +#[derivative(Debug(bound="T:Debug"))] +#[derivative(Clone(bound=""))] +pub struct SharedDirtyFlag { + rc: Rc>> } // === API === -impl -SharedDirtyFlag { - pub fn new(logger: Logger, on_set: OnSet) -> Self { - let callback = Callback(on_set); +impl +SharedDirtyFlag { + pub fn new(logger:Logger, on_set:OnMut) -> Self { + let callback = Function(on_set); let rc = Rc::new(RefCell::new(DirtyFlag::new(logger,callback))); Self { rc } } @@ -211,23 +209,23 @@ SharedDirtyFlag { } } -impl -SharedDirtyFlag { +impl +SharedDirtyFlag { pub fn clone_ref(&self) -> Self { self.clone() } } -impl -SharedDirtyFlag { - pub fn set_callback(&self, on_set:OnSet) { - self.rc.borrow_mut().on_set = Callback(on_set); +impl +SharedDirtyFlag { + pub fn set_callback(&self, on_set:OnMut) { + self.rc.borrow_mut().on_set = Function(on_set); } } -impl -From>>> for SharedDirtyFlag { - fn from(rc: Rc>>) -> Self { +impl +From>>> for SharedDirtyFlag { + fn from(rc: Rc>>) -> Self { Self {rc} } } @@ -235,22 +233,22 @@ From>>> for SharedDirtyFlag { // === Arg === -impl HasArg for SharedDirtyFlag { +impl HasArg for SharedDirtyFlag { type Arg = Arg; } // === Global Operations === -impl -SharedHasUnsetAll for SharedDirtyFlag { +impl +SharedHasUnsetAll for SharedDirtyFlag { fn unset_all(&self) { self.rc.borrow_mut().unset_all() } } -impl -HasCheckAll for SharedDirtyFlag { +impl +HasCheckAll for SharedDirtyFlag { fn check_all(&self) -> bool { self.rc.borrow().check_all() } @@ -258,39 +256,39 @@ HasCheckAll for SharedDirtyFlag { // === Check === -impl -HasCheck0 for SharedDirtyFlag { +impl +HasCheck0 for SharedDirtyFlag { fn check (&self) -> bool { self.rc.borrow().check() } } -impl -HasCheck1 for SharedDirtyFlag { +impl +HasCheck1 for SharedDirtyFlag { fn check (&self, arg:&Arg) -> bool { self.rc.borrow().check(arg) } } // === Set === -impl -SharedHasSet0 for SharedDirtyFlag { +impl +SharedHasSet0 for SharedDirtyFlag { fn set (&self) { self.rc.borrow_mut().set() } } -impl -SharedHasSet1 for SharedDirtyFlag { +impl +SharedHasSet1 for SharedDirtyFlag { fn set (&self, arg: Arg) { self.rc.borrow_mut().set(arg) } } // === Unset === -impl -SharedHasUnset0 for SharedDirtyFlag { +impl +SharedHasUnset0 for SharedDirtyFlag { fn unset(&self) { self.rc.borrow_mut().unset() } } -impl -SharedHasUnset1 for SharedDirtyFlag where Arg:Display { +impl +SharedHasUnset1 for SharedDirtyFlag where Arg:Display { fn unset(&self, arg:&Self::Arg) { self.rc.borrow_mut().unset(arg) } @@ -309,9 +307,9 @@ SharedHasUnset1 for SharedDirtyFlag where Arg:Display { /// The on / off dirty flag. If you need a simple dirty / clean switch, this one /// is the right choice. -pub type Bool = DirtyFlag ; -pub type SharedBool = SharedDirtyFlag ; -pub trait BoolCtx = where OnSet: Callback0; +pub type Bool = DirtyFlag ; +pub type SharedBool = SharedDirtyFlag ; +pub trait BoolCtx = where OnMut:Function0; #[derive(Debug,Display,Default)] pub struct BoolData { is_dirty: bool } @@ -330,9 +328,9 @@ impl HasUnset0 for BoolData { fn unset (&mut self) { self.is_dirty /// Dirty flag which keeps information about a range of dirty items. It does not track items /// separately, nor you are allowed to keep multiple ranges in it. Just a single value range. -pub type Range = DirtyFlag ,OnSet>; -pub type SharedRange = SharedDirtyFlag ,OnSet>; -pub trait RangeCtx = where OnSet: Callback0; +pub type Range = DirtyFlag ,OnMut>; +pub type SharedRange = SharedDirtyFlag ,OnMut>; +pub trait RangeCtx = where OnMut:Function0; pub trait RangeIx = PartialOrd + Copy + Debug; #[derive(Debug,Default)] @@ -384,9 +382,9 @@ impl Display for RangeData { /// counterpart. Please note that it uses `FxHashSet` under the hood, so there /// are no guarantees regarding attack-proof hashing algorithm here. -pub type Set = DirtyFlag ,OnSet>; -pub type SharedSet = SharedDirtyFlag ,OnSet>; -pub trait SetCtx = where OnSet: Callback0; +pub type Set = DirtyFlag ,OnMut>; +pub type SharedSet = SharedDirtyFlag ,OnMut>; +pub trait SetCtx = where OnMut:Function0; pub trait SetItem = Eq + Hash + Debug; #[derive(Derivative,Shrinkwrap)] @@ -442,16 +440,16 @@ use bit_field::BitField as BF; /// items must be a plain enumerator implementing `Into`. The data is /// stored as an efficient `BitField` under the hood. -pub type Enum = DirtyFlag ,OnSet>; -pub type SharedEnum = SharedDirtyFlag ,OnSet>; -pub trait EnumCtx = where OnSet: Callback0; +pub type Enum = DirtyFlag ,OnMut>; +pub type SharedEnum = SharedDirtyFlag ,OnMut>; +pub trait EnumCtx = where OnMut:Function0; pub trait EnumBase = Default + PartialEq + Copy + BF; pub trait EnumElem = Copy+Into; /// Dirty flag which keeps dirty indexes in a `BitField` under the hood. -pub type BitField = Enum ; -pub type SharedBitField = SharedEnum ; +pub type BitField = Enum ; +pub type SharedBitField = SharedEnum ; #[derive(Derivative)] #[derivative(Debug(bound="Prim:Debug"))] diff --git a/gui/lib/core/src/data/function.rs b/gui/lib/core/src/data/function.rs index dbfc39d2b2..2ce54d9ad7 100644 --- a/gui/lib/core/src/data/function.rs +++ b/gui/lib/core/src/data/function.rs @@ -3,6 +3,4 @@ #[warn(missing_docs)] pub mod callback; #[warn(missing_docs)] -pub mod closure; -#[warn(missing_docs)] -pub mod nop; +pub mod procedure; diff --git a/gui/lib/core/src/data/function/callback.rs b/gui/lib/core/src/data/function/callback.rs index 1e8a6954be..f80c141a3e 100644 --- a/gui/lib/core/src/data/function/callback.rs +++ b/gui/lib/core/src/data/function/callback.rs @@ -4,48 +4,17 @@ use crate::prelude::*; use std::fmt; -// ================================================================================================= -// ================================================================================================= -// ================================================================================================= - -// TODO -// -// We should refactor the whole file as soon as this gets resolved: -// https://github.com/rust-lang/rust/issues/65918 -// -// Then, we will be able to use unboxed closures (see the closure.rs file) and there would not be -// a need anymore for the closures to be in `WithPhantom,P>` type. It dereferences -// to the first type param, however, `Rc` does not implement `Fn` trait (as its -// superclasses could not be implemented). We are using `Rc` to be able to clone the closure. -// We could use `Box` instead but cloning boxed dyn closures is hard. Using unboxed closures will -// solve all of these. -// -// After the error is solved we could define a NOP type which implements the Fn* traits and use it -// instead of `()` when necessary. Then we would be able to use `Fn(...)` whenever we use -// `CallbackN(...)`. - -// ================================================================================================= -// ================================================================================================= -// ================================================================================================= - - -// ===================== -// === Callback Type === -// ===================== +// ============= +// === Types === +// ============= pub type NoCallback = (); #[derive(Shrinkwrap)] #[shrinkwrap(mutable)] -pub struct Callback(pub Func); +pub struct Function(pub Func); -//impl Default for Callback { -// fn default() -> Self { -// Callback(NOP::nop()) -// } -//} - -impl Debug for Callback { +impl Debug for Function { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Callback") } @@ -53,38 +22,22 @@ impl Debug for Callback { -// ========================== -// === Callback Interface === -// ========================== +// ================= +// === Instances === +// ================= -pub trait Callback0: 'static { +pub trait Function0 { fn call(&mut self); } -pub trait Callback1 { +pub trait Function1 { fn call(&mut self, arg1:Arg1); } -// -//pub trait Callback2 { -// fn call(&mut self, arg1:Arg1, arg2:Arg2); -//} -// -//pub trait Callback3 { -// fn call(&mut self, arg1:Arg1, arg2:Arg2, arg3:Arg3); -//} -// -//pub trait Callback4 { -// fn call(&mut self, arg1:Arg1, arg2:Arg2, arg3:Arg3, arg4:Arg4); -//} -// -//pub trait Callback5 { -// fn call(&mut self, arg1:Arg1, arg2:Arg2, arg3:Arg3, arg4:Arg4, arg5:Arg5); -//} // === Unit Implementations === -impl Callback0 for Option { +impl Function0 for Option { fn call(&mut self) { self.iter_mut().for_each(|t| { t.call() @@ -92,81 +45,25 @@ impl Callback0 for Option { } } -impl Callback0 for () { +impl Function0 for () { fn call(&mut self) {} } -impl Callback1 for () { +impl Function1 for () { fn call(&mut self, _arg1:Arg1) {} } -//impl Callback2 for () { -// fn call(&mut self, _arg1:Arg1, _arg2:Arg2) {} -//} -// -//impl Callback3 for () { -// fn call(&mut self, _arg1:Arg1, _arg2:Arg2, _arg3:Arg3) {} -//} -// -//impl Callback4 for () { -// fn call(&mut self, _arg1:Arg1, _arg2:Arg2, _arg3:Arg3, _arg4:Arg4) {} -//} -// -//impl Callback5 for () { -// fn call(&mut self, _arg1:Arg1, _arg2:Arg2, _arg3:Arg3, _arg4:Arg4, _arg5:Arg5) {} -//} - // === FnMut Implementations === -// FIXME: How to make it more generic? -impl Callback0 for WithPhantom T>, P> { - fn call(&mut self) { - (self)(); - } -} - -// FIXME: How to make it more generic? -impl Callback1 for WithPhantom T>, P> { - fn call(&mut self, arg1:Arg1) { - (self)(arg1); - } -} - -impl T + 'static, T> Callback0 for F { +impl T, T> Function0 for F { fn call(&mut self) { self(); } } -impl T, T> Callback1 for F { +impl T, T> Function1 for F { fn call(&mut self, arg1:Arg1) { self(arg1); } } - -//impl T, T> Callback2 for F { -// fn call(&mut self, arg1:Arg1, arg2:Arg2) { -// self(arg1, arg2); -// } -//} -// -//impl T, T> Callback3 for F { -// fn call(&mut self, arg1:Arg1, arg2:Arg2, arg3:Arg3) { -// self(arg1, arg2, arg3); -// } -//} -// -//impl T, T> -// Callback4 for F { -// fn call(&mut self, arg1:Arg1, arg2:Arg2, arg3:Arg3, arg4:Arg4) { -// self(arg1, arg2, arg3, arg4); -// } -//} -// -//impl T, T> -// Callback5 for F { -// fn call(&mut self, arg1:Arg1, arg2:Arg2, arg3:Arg3, arg4:Arg4, arg5:Arg5) { -// self(arg1, arg2, arg3, arg4, arg5); -// } -//} diff --git a/gui/lib/core/src/data/function/nop.rs b/gui/lib/core/src/data/function/nop.rs deleted file mode 100644 index 07b2385276..0000000000 --- a/gui/lib/core/src/data/function/nop.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![allow(missing_docs)] - -// =========== -// === NOP === -// =========== - -pub trait NOP { fn nop() -> Self; } - -impl NOP for () { fn nop() -> Self { } } -impl NOP for fn() { fn nop() -> Self { || {} } } -impl NOP for fn(T1) { fn nop() -> Self { |_| {} } } -impl NOP for fn(T1,T2) { fn nop() -> Self { |_, _| {} } } -impl NOP for fn(T1,T2,T3) { fn nop() -> Self { |_, _, _| {} } } -impl NOP for fn(T1,T2,T3,T4) { fn nop() -> Self { |_, _, _, _| {} } } -impl NOP for fn(T1,T2,T3,T4,T5) { fn nop() -> Self { |_, _, _, _, _| {} } } diff --git a/gui/lib/core/src/data/function/closure.rs b/gui/lib/core/src/data/function/procedure.rs similarity index 51% rename from gui/lib/core/src/data/function/closure.rs rename to gui/lib/core/src/data/function/procedure.rs index 92307a47ab..f04ce9fe0e 100644 --- a/gui/lib/core/src/data/function/closure.rs +++ b/gui/lib/core/src/data/function/procedure.rs @@ -72,100 +72,28 @@ macro_rules! closure { $body:tt ) => { paste::item! { #[cfg(not(feature = "no_unboxed_callbacks"))] + /// Closure type. pub type $type<$($param),*> = impl Fn($($larg_type),*) + Clone; #[cfg(not(feature = "no_unboxed_callbacks"))] + /// Closure constructor. pub fn $name<$($param:$param_type),*> ($($arg:$arg_type),*) -> $type<$($param),*> { move |$($larg),*| $body } #[cfg(feature = "no_unboxed_callbacks")] + /// Closure type. pub type $type<$($param),*> = - WithPhantom, $($param),*>; + Box; #[cfg(feature = "no_unboxed_callbacks")] + /// Closure constructor. pub fn $name<$($param:$param_type),*> ($($arg:$arg_type),*) - -> WithPhantom, $($param),*> { - WithPhantom::new(Rc::new(move |$($larg),*| $body)) + -> Box { + Box::new(move |$($larg),*| $body) } }}; } - - - -// =============== -// === Promote === -// =============== - -/// Promotion of closures is a complex topic. Consider the following code: -/// -/// ```compile_fail -/// pub type Buffer = Observable, BufferOnSet>; -/// -/// closure! { -/// fn buffer_on_set (dirty: ResizeDirty) -> -/// BufferOnSet { || dirty.set() } -/// } -/// ``` -/// -/// It defines an unboxed closure with type `BufferOnSet`, where `C` is -/// potentially another closure which is called when the buffer was set for the -/// first time (this is how the `dirty` flag behaves). -/// -/// In another file we've got: -/// -/// ```compile_fail -/// closure! { -/// fn attribute_on_set (dirty:AttributeDirty, ix: usize) -> -/// AttributeOnSet { || dirty.set(ix) } -/// } -/// ``` -/// -/// And we would like to promote the `Buffer` type: -/// -/// ```compile_fail -/// pub type Buffer = file1::Buffer>; -/// ``` -/// -/// This macro automates such promotion. See its usages to learn more. -#[macro_export] -macro_rules! promote { - - // === Final expansion, closure names provided in double braces. === - - ([[$($closure:ident),*]] $module:ident [$name:ident<$($param:ident),*>]) =>{ - pub type $name<$($param),*> = - $module::$name <$($param),*,$($closure),*>; - }; - ([[$($closure:ident),*]] $module:ident [$name:ident]) => { - pub type $name = $module::$name <$($closure),*>; - }; - - // === Intermediate expansion. === - - ([$($closure:ident),*] $module:ident [$name:ident<$($param:ident),*>]) => { - pub type $name<$($param),*,Callback> = - $module::$name <$($param),*,$($closure),*>; - }; - ([$($closure:ident),*] $module:ident [$name:ident]) => { - pub type $name = - $module::$name <$($closure),*>; - }; - - // === Mapped promotion === - - ($gens:tt $module:ident [$($targets:tt)*]) => { - eval_tt::eval!{ promote_all($gens,$module,split_comma([$($targets)*])) } - }; -} - -#[macro_export] -/// Promote all provided types to the current scope. -macro_rules! promote_all { - ([$gens:tt] [$module:ident] [$($target:tt)*]) => { - $(promote!{$gens $module $target})* - }; -} diff --git a/gui/lib/core/src/data/seq/observable.rs b/gui/lib/core/src/data/seq/observable.rs index ecb0644ae4..667a87a31e 100644 --- a/gui/lib/core/src/data/seq/observable.rs +++ b/gui/lib/core/src/data/seq/observable.rs @@ -1,7 +1,9 @@ #![allow(missing_docs)] use crate::prelude::*; -use crate::data::function::callback::{Callback0,Callback1}; +use crate::data::function::callback::Function0; +use crate::data::function::callback::Function1; + // ================== @@ -12,7 +14,7 @@ use crate::data::function::callback::{Callback0,Callback1}; /// structure changes. #[derive(Shrinkwrap)] #[derive(Derivative)] -#[derivative(Debug(bound="T:Debug"))] +#[derivative(Clone,Debug(bound="T:Debug"))] pub struct Observable { #[shrinkwrap(main_field)] pub data: T, @@ -39,7 +41,7 @@ Index for Observable { } } -impl,OnMut:Callback1,OnResize,Ix:Copy> +impl, OnMut: Function1 ,OnResize, Ix:Copy> IndexMut for Observable { #[inline] fn index_mut(&mut self, index:Ix) -> &mut Self::Output { @@ -48,7 +50,7 @@ IndexMut for Observable { } } -impl ,S,OnMut,OnResize:Callback0> +impl ,S,OnMut,OnResize:Function0> Extend for Observable { #[inline] fn extend>(&mut self, iter:I) { diff --git a/gui/lib/core/src/debug.rs b/gui/lib/core/src/debug.rs index e1c33e9621..e3ea513d16 100644 --- a/gui/lib/core/src/debug.rs +++ b/gui/lib/core/src/debug.rs @@ -2,3 +2,6 @@ pub mod monitor; pub mod stats; + +pub use monitor::*; +pub use stats::*; diff --git a/gui/lib/core/src/debug/stats.rs b/gui/lib/core/src/debug/stats.rs index cab2d75797..b78e7e2f1c 100644 --- a/gui/lib/core/src/debug/stats.rs +++ b/gui/lib/core/src/debug/stats.rs @@ -44,7 +44,6 @@ macro_rules! gen_stats { } impl Stats { $( - /// Field getter. pub fn $field(&self) -> $field_type { self.rc.borrow().$field @@ -98,3 +97,14 @@ impl StatsData { self.data_upload_size = 0; } } + +/// Keeps the body if the `statistics` compilation flag was enabled. +#[macro_export] +macro_rules! if_compiled_with_stats { + ($($tok:tt)*) => { + #[cfg(feature = "statistics")] + {$($tok)*} + #[cfg(not(feature = "statistics"))] + {} + }; +} diff --git a/gui/lib/core/src/display.rs b/gui/lib/core/src/display.rs index 35bc3e30f8..ec9743f9bb 100644 --- a/gui/lib/core/src/display.rs +++ b/gui/lib/core/src/display.rs @@ -5,6 +5,5 @@ pub mod camera; pub mod shape; pub mod symbol; pub mod object; -pub mod render; pub mod world; -pub mod navigation; \ No newline at end of file +pub mod navigation; diff --git a/gui/lib/core/src/display/camera.rs b/gui/lib/core/src/display/camera.rs index e4f19dc9df..35d2b5a02d 100644 --- a/gui/lib/core/src/display/camera.rs +++ b/gui/lib/core/src/display/camera.rs @@ -3,4 +3,4 @@ #[warn(missing_docs)] pub mod camera2d; -pub use camera2d::Camera2D; +pub use camera2d::Camera2d; diff --git a/gui/lib/core/src/display/camera/camera2d.rs b/gui/lib/core/src/display/camera/camera2d.rs index 23eecba5ff..a51d467b6e 100644 --- a/gui/lib/core/src/display/camera/camera2d.rs +++ b/gui/lib/core/src/display/camera/camera2d.rs @@ -8,7 +8,8 @@ use crate::prelude::*; use crate::data::dirty; use crate::display::object::DisplayObjectData; use nalgebra::{Vector3, Matrix4, Perspective3}; -use basegl_system_web::Logger; +use crate::system::gpu::data::uniform::Uniform; +use crate::system::gpu::data::uniform::UniformScope; use crate::data::dirty::traits::*; @@ -104,15 +105,16 @@ impl Default for Clipping { // ==================== -// === Camera2DData === +// === Camera2dData === // ==================== -/// Internal `Camera2D` representation. Please see `Camera2D` for full documentation. +/// Internal `Camera2d` representation. Please see `Camera2d` for full documentation. #[derive(Clone,Debug)] -pub struct Camera2DData { +pub struct Camera2dData { pub transform : DisplayObjectData, screen : Screen, zoom : f32, + zoom_uniform : Uniform, native_z : f32, alignment : Alignment, projection : Projection, @@ -127,8 +129,8 @@ pub struct Camera2DData { type ProjectionDirty = dirty::SharedBool<()>; type TransformDirty2 = dirty::SharedBool<()>; -impl Camera2DData { - pub fn new(logger: Logger) -> Self { +impl Camera2dData { + pub fn new(logger:Logger, globals:&UniformScope) -> Self { let screen = default(); let projection = default(); let clipping = default(); @@ -142,10 +144,11 @@ impl Camera2DData { let transform_dirty = TransformDirty2::new(logger.sub("transform_dirty"),()); let transform_dirty_copy = transform_dirty.clone(); let transform = DisplayObjectData::new(logger); + let zoom_uniform = globals.add_or_panic("zoom",1.0); transform.set_on_updated(move |_| { transform_dirty_copy.set(); }); transform.mod_position(|p| p.z = 1.0); projection_dirty.set(); - Self {transform,screen,projection,clipping,alignment,zoom,native_z,view_matrix + Self {transform,screen,projection,clipping,alignment,zoom,zoom_uniform,native_z,view_matrix ,projection_matrix,view_projection_matrix,projection_dirty,transform_dirty} } @@ -187,6 +190,7 @@ impl Camera2DData { } if changed { self.view_projection_matrix = self.projection_matrix * self.view_matrix; + self.zoom_uniform.set(self.zoom); } changed } @@ -195,7 +199,7 @@ impl Camera2DData { // === Getters === -impl Camera2DData { +impl Camera2dData { pub fn zoom(&self) -> f32 { self.zoom } @@ -208,7 +212,7 @@ impl Camera2DData { // === Setters === -impl Camera2DData { +impl Camera2dData { pub fn projection_mut(&mut self) -> &mut Projection { self.projection_dirty.set(); &mut self.projection @@ -240,7 +244,7 @@ impl Camera2DData { // === Transform Setters === -impl Camera2DData { +impl Camera2dData { pub fn mod_position)>(&mut self, f:F) { self.mod_position_keep_zoom(f); self.zoom = self.native_z / self.transform.position().z; @@ -254,7 +258,7 @@ impl Camera2DData { // === Private Transform Setters === -impl Camera2DData { +impl Camera2dData { fn mod_position_keep_zoom)>(&mut self, f:F) { self.transform.mod_position(f) } @@ -263,7 +267,7 @@ impl Camera2DData { // ================ -// === Camera2D === +// === Camera2d === // ================ /// Camera definition for 2D objects. @@ -282,14 +286,14 @@ impl Camera2DData { /// corner, you will get a view which behaves like a window in window-based GUIs. When scaling /// the window, the left-bottom corner will stay in place. #[derive(Clone,Debug)] -pub struct Camera2D { - rc: Rc> +pub struct Camera2d { + rc: Rc> } -impl Camera2D { +impl Camera2d { /// Creates new Camera instance. - pub fn new(logger:Logger) -> Self { - let data = Camera2DData::new(logger); + pub fn new(logger:Logger, globals:&UniformScope) -> Self { + let data = Camera2dData::new(logger,globals); let rc = Rc::new(RefCell::new(data)); Self {rc} } @@ -298,7 +302,7 @@ impl Camera2D { // === Modifiers === -impl Camera2D { +impl Camera2d { /// Sets screen dimensions. pub fn set_screen(&self, width:f32, height:f32) { self.rc.borrow_mut().set_screen(width,height) @@ -313,7 +317,7 @@ impl Camera2D { // === Getters === -impl Camera2D { +impl Camera2d { pub fn zoom(&self) -> f32 { self.rc.borrow().zoom() } @@ -326,7 +330,7 @@ impl Camera2D { // === Setters === -impl Camera2D { +impl Camera2d { pub fn mod_position)>(&self, f:F) { self.rc.borrow_mut().mod_position(f) } diff --git a/gui/lib/core/src/display/navigation/navigator.rs b/gui/lib/core/src/display/navigation/navigator.rs index df249bf222..a4ab98edc4 100644 --- a/gui/lib/core/src/display/navigation/navigator.rs +++ b/gui/lib/core/src/display/navigation/navigator.rs @@ -6,9 +6,9 @@ use events::NavigatorEvents; use events::ZoomEvent; use events::PanEvent; use crate::system::web::Result; -use crate::display::render::css3d::Camera; -use crate::display::render::css3d::CameraType; -use crate::display::render::css3d::DOMContainer; +use crate::system::web::dom::Camera; +use crate::system::web::dom::CameraType; +use crate::system::web::dom::DOMContainer; use crate::animation::position::HasPosition; use crate::animation::physics::inertia::PhysicsSimulator; use crate::animation::physics::inertia::SpringProperties; diff --git a/gui/lib/core/src/display/navigation/navigator/events.rs b/gui/lib/core/src/display/navigation/navigator/events.rs index 8da30de9f2..03bcafd873 100644 --- a/gui/lib/core/src/display/navigation/navigator/events.rs +++ b/gui/lib/core/src/display/navigation/navigator/events.rs @@ -1,18 +1,20 @@ -use crate::control::mouse_manager::MouseManager; -use crate::control::mouse_manager::MouseClickEvent; -use crate::control::mouse_manager::MouseWheelEvent; -use crate::control::mouse_manager::MousePositionEvent; -use crate::control::mouse_manager::MouseButton; -use crate::control::mouse_manager::WheelEventListener; -use crate::control::mouse_manager::MouseEventListener; +use crate::control::io::mouse::MouseManager; +use crate::control::io::mouse::MouseClickEvent; +use crate::control::io::mouse::MouseWheelEvent; +use crate::control::io::mouse::MousePositionEvent; +use crate::control::io::mouse::MouseButton; +use crate::control::io::mouse::WheelEventListener; +use crate::control::io::mouse::MouseEventListener; use crate::system::web::Result; -use crate::display::render::css3d::DOMContainer; +use crate::system::web::dom::DOMContainer; use nalgebra::Vector2; use std::rc::Rc; use std::cell::RefCell; use nalgebra::zero; + + // ================= // === ZoomEvent === // ================= diff --git a/gui/lib/core/src/display/object.rs b/gui/lib/core/src/display/object.rs index dda7114212..e0de8034da 100644 --- a/gui/lib/core/src/display/object.rs +++ b/gui/lib/core/src/display/object.rs @@ -9,9 +9,7 @@ use crate::closure; use crate::data::dirty; use crate::data::dirty::traits::*; use data::opt_vec::OptVec; -use crate::system::web::group; -use basegl_system_web::Logger; use nalgebra::Vector3; use nalgebra::Matrix4; use transform::CachedTransform; @@ -158,7 +156,7 @@ impl DisplayObjectDataMut { Some(_) => "Update with new parent origin.", None => "Update with old parent origin." }; - group!(self.logger, msg, { + group!(self.logger, "{msg}", { let origin_changed = self.transform.update(new_origin); let origin = &self.transform.matrix; if origin_changed { @@ -212,10 +210,11 @@ impl DisplayObjectDataMut { fn set_parent_bind(&mut self, bind:ParentBind) { self.logger.info("Adding new parent bind."); - let dirty = bind.parent.rc.borrow().child_dirty.clone_ref(); - let on_change = fn_on_change(dirty, bind.index); - self.transform.dirty.set_callback(Some(on_change.clone())); - self.child_dirty.set_callback(Some(on_change)); + let dirty = bind.parent.rc.borrow().child_dirty.clone_ref(); + let index = bind.index; + let on_mut = move || {dirty.set(index)}; + self.transform.dirty.set_callback(Some(Box::new(on_mut.clone()))); + self.child_dirty.set_callback(Some(Box::new(on_mut))); self.new_parent_dirty.set(); self.wrapped.set_parent_bind(bind); } diff --git a/gui/lib/core/src/display/object/transform.rs b/gui/lib/core/src/display/object/transform.rs index d015e1d6a3..1066146e79 100644 --- a/gui/lib/core/src/display/object/transform.rs +++ b/gui/lib/core/src/display/object/transform.rs @@ -4,8 +4,7 @@ use crate::prelude::*; use crate::data::dirty; use crate::data::function::callback::*; -use crate::system::web::group; -use basegl_system_web::Logger; + use nalgebra::Matrix4; use nalgebra::Vector3; use nalgebra::Vector4; @@ -21,7 +20,7 @@ use crate::data::dirty::traits::*; /// Defines the order in which particular axis coordinates are processed. Used for example to define /// the rotation order in `DisplayObject`. #[derive(Clone,Debug)] -pub enum AxisOrder { XYZ, XZY, YXZ, YZX, ZXY, ZYX } +pub enum AxisOrder {XYZ,XZY,YXZ,YZX,ZXY,ZYX} impl Default for AxisOrder { fn default() -> Self { Self::XYZ } @@ -231,7 +230,7 @@ impl CachedTransform { // === Setters === -impl CachedTransform { +impl CachedTransform { pub fn position_mut(&mut self) -> &mut Vector3 { self.dirty.set(); &mut self.transform.position diff --git a/gui/lib/core/src/display/render.rs b/gui/lib/core/src/display/render.rs index bb801bb7f2..b2e09b8056 100644 --- a/gui/lib/core/src/display/render.rs +++ b/gui/lib/core/src/display/render.rs @@ -2,5 +2,3 @@ #[warn(missing_docs)] pub mod css3d; -#[warn(missing_docs)] -pub mod webgl; diff --git a/gui/lib/core/src/display/shape/primitive/def/sdf.rs b/gui/lib/core/src/display/shape/primitive/def/sdf.rs index 172d4d42b6..22fea7ae91 100644 --- a/gui/lib/core/src/display/shape/primitive/def/sdf.rs +++ b/gui/lib/core/src/display/shape/primitive/def/sdf.rs @@ -13,7 +13,9 @@ use crate::display::shape::primitive::def::class::ShapeRef; use crate::display::shape::primitive::shader::canvas::Canvas; use crate::display::shape::primitive::shader::canvas::CanvasShape; use crate::display::shape::primitive::shader::data::ShaderData; -use crate::system::gpu::data::GpuData; +use crate::system::gpu::shader::glsl::Glsl; + +use crate::system::gpu::shader::glsl::traits::*; @@ -63,14 +65,14 @@ pub trait SdfShape { /// /// #[derive(Debug,Clone)] /// pub struct Circle { -/// pub glsl_name : String, -/// pub radius : String, +/// pub glsl_name : Glsl, +/// pub radius : Glsl, /// } /// /// impl Circle { /// pub fn new>(radius:radius) -> Self { -/// let glsl_name = "circle".to_string(); -/// let radius = radius.to_glsl(); +/// let glsl_name = "circle".into(); +/// let radius = radius.into(); /// Self {glsl_name,radius} /// } /// } @@ -97,7 +99,7 @@ pub trait SdfShape { /// let body = "return bound_sdf(length(position)-radius, bounding_box(radius,radius));"; /// let args = vec![ /// "vec2 position".to_string(), -/// format!("{} {}", <$f32 as GpuData>::gpu_type_name(), "radius") +/// format!("{} {}", <$f32 as BufferItem>::gpu_type_name(), "radius") /// ].join(", "); /// format!("sdf {} ({}) {{ {} }}",self.glsl_name,args,body) /// } @@ -156,7 +158,7 @@ macro_rules! _define_sdf_shape_immutable_part { let name = stringify!($name).to_snake_case(); let body = stringify!($body); let args = vec!["vec2 position".to_string(), $( - format!("{} {}", <$field_type as GpuData>::glsl_type_name(), stringify!($field)) + format!("{} {}", <$field_type>::glsl_prim_type(), stringify!($field)) ),*].join(", "); iformat!("BoundSdf {name} ({args}) {body}") } @@ -172,16 +174,16 @@ macro_rules! _define_sdf_shape_mutable_part { #[allow(missing_docs)] #[derive(Debug,Clone)] pub struct $name { - pub glsl_name : String, - $(pub $field : String),* + pub glsl_name : Glsl, + $(pub $field : Glsl),* } impl $name { /// Constructor. #[allow(clippy::new_without_default)] pub fn new <$($field:ShaderData<$field_type>),*> ( $($field : $field),* ) -> Self { - let glsl_name = stringify!($name).to_snake_case(); - $(let $field = $field.to_glsl();)* + let glsl_name = stringify!($name).to_snake_case().into(); + $(let $field = $field.into();)* Self {glsl_name,$($field),*} } } diff --git a/gui/lib/core/src/display/shape/primitive/def/transform.rs b/gui/lib/core/src/display/shape/primitive/def/transform.rs index aad92ebd4a..4426b62725 100644 --- a/gui/lib/core/src/display/shape/primitive/def/transform.rs +++ b/gui/lib/core/src/display/shape/primitive/def/transform.rs @@ -9,6 +9,7 @@ use crate::display::shape::primitive::def::class::ShapeRef; use crate::display::shape::primitive::shader::canvas::Canvas; use crate::display::shape::primitive::shader::canvas::CanvasShape; use crate::display::shape::primitive::shader::data::ShaderData; +use crate::system::gpu::shader::glsl::Glsl; @@ -32,15 +33,15 @@ use crate::display::shape::primitive::shader::data::ShaderData; /// /// pub struct Translate { /// pub child : child, -/// pub x : String, -/// pub y : String, +/// pub x : Glsl, +/// pub y : Glsl, /// } /// /// impl Translate { /// pub fn new,y:ShaderData>(child:&child,x:x,y:y) -> Self { /// let child = child.clone(); -/// let x = x.to_glsl(); -/// let y = y.to_glsl(); +/// let x = x.into(); +/// let y = y.into(); /// Self {child,x,y} /// } /// } @@ -80,7 +81,7 @@ macro_rules! _define_compound_shape_data { #[allow(missing_docs)] pub struct $name<$($shape_field),*> { $(pub $shape_field : $shape_field),*, - $(pub $field : String ),* + $(pub $field : Glsl),* } impl<$($shape_field:Shape),*> $name<$($shape_field),*> { @@ -88,7 +89,7 @@ macro_rules! _define_compound_shape_data { pub fn new<$($field:ShaderData<$field_type>),*> ($($shape_field:&$shape_field),*,$($field:$field),*) -> Self { $(let $shape_field = $shape_field.clone();)* - $(let $field = $field.to_glsl();)* + $(let $field = $field.into();)* Self {$($shape_field),*,$($field),*} } } diff --git a/gui/lib/core/src/display/shape/primitive/shader/canvas.rs b/gui/lib/core/src/display/shape/primitive/shader/canvas.rs index a03035de51..54a2db5a23 100644 --- a/gui/lib/core/src/display/shape/primitive/shader/canvas.rs +++ b/gui/lib/core/src/display/shape/primitive/shader/canvas.rs @@ -2,6 +2,7 @@ use crate::prelude::*; use crate::display::shape::primitive::shader::data::ShaderData; +use crate::system::gpu::shader::glsl::Glsl; @@ -189,13 +190,13 @@ impl Canvas { } /// Translate the current canvas origin. - pub fn translate,Y: ShaderData> + pub fn translate, Y:ShaderData> (&mut self, num:usize, s1:CanvasShape, x:X, y:Y) -> CanvasShape { self.if_not_defined(num, |this| { - let x = x.to_glsl(); - let y = y.to_glsl(); - let trans = iformat!("position = translate(position,vec2({x},{y}));"); - let expr = iformat!("return {s1.getter()};"); + let x:Glsl = x.into(); + let y:Glsl = y.into(); + let trans = iformat!("position = translate(position,vec2({x},{y}));"); + let expr = iformat!("return {s1.getter()};"); this.add_current_function_code_line(trans); let mut shape = this.new_shape_from_expr(num,&expr); shape.add_ids(&s1.ids); diff --git a/gui/lib/core/src/display/shape/primitive/shader/data.rs b/gui/lib/core/src/display/shape/primitive/shader/data.rs index c384f5e740..e11837a672 100644 --- a/gui/lib/core/src/display/shape/primitive/shader/data.rs +++ b/gui/lib/core/src/display/shape/primitive/shader/data.rs @@ -1,9 +1,8 @@ //! This module defines an abstraction for all types which can be used as GLSL code values. -use crate::prelude::*; - -use crate::system::gpu::data::GpuData; -use crate::system::gpu::data::Empty; +use crate::system::gpu::data::BufferItem; +use crate::system::gpu::data::GpuDefault; +use crate::system::gpu::shader::glsl::Glsl; @@ -17,39 +16,34 @@ use crate::system::gpu::data::Empty; /// allows for dirty injection of GLSL code easily. For example, when moving a shape, you can write /// `s1.translate("a","b")`, where `a` and `b` refer to variables defined in the GLSL shader. Such /// operation is not checked during compilation, so be careful when using it, please. - -pub trait ShaderData { +pub trait ShaderData: Into { /// Checks if the value is zero. fn is_zero (&self) -> bool; - - /// Converts the value to GLSL code. - fn to_glsl (&self) -> String; } // === Instances === +impl ShaderData for Glsl { + fn is_zero (&self) -> bool { self.str == "0" || self.str == "0.0" } +} + +impl ShaderData for &Glsl { + fn is_zero (&self) -> bool { (*self).str == "0" || (*self).str == "0.0" } +} + impl ShaderData for String { - fn is_zero (&self) -> bool { self == "0" || self == "0.0" } - fn to_glsl (&self) -> String { self.into() } + fn is_zero (&self) -> bool { self == "0" || self == "0.0" } } impl ShaderData for &String { - fn is_zero (&self) -> bool { *self == "0" || *self == "0.0" } - fn to_glsl (&self) -> String { (*self).into() } -} - -impl ShaderData for str { - fn is_zero (&self) -> bool { self == "0" || self == "0.0" } - fn to_glsl (&self) -> String { self.into() } + fn is_zero (&self) -> bool { *self == "0" || *self == "0.0" } } impl ShaderData for &str { - fn is_zero (&self) -> bool { *self == "0" || *self == "0.0" } - fn to_glsl (&self) -> String { (*self).into() } + fn is_zero (&self) -> bool { *self == "0" || *self == "0.0" } } -impl ShaderData for T { - fn is_zero (&self) -> bool { :: is_empty(self) } - fn to_glsl (&self) -> String { :: to_glsl(self) } +impl> ShaderData for T { + fn is_zero (&self) -> bool { :: is_gpu_default(self) } } diff --git a/gui/lib/core/src/display/shape/primitive/system.rs b/gui/lib/core/src/display/shape/primitive/system.rs index 67eaa4f723..4f417e3b12 100644 --- a/gui/lib/core/src/display/shape/primitive/system.rs +++ b/gui/lib/core/src/display/shape/primitive/system.rs @@ -3,7 +3,7 @@ use crate::prelude::*; -use crate::display::symbol::geometry::sprite::SpriteSystem; +use crate::display::symbol::geometry::SpriteSystem; use crate::display::world::World; use crate::display::symbol::material::Material; use crate::display::shape::primitive::shader; diff --git a/gui/lib/core/src/display/shape/text.rs b/gui/lib/core/src/display/shape/text.rs index e4f084ad99..3460487b8a 100644 --- a/gui/lib/core/src/display/shape/text.rs +++ b/gui/lib/core/src/display/shape/text.rs @@ -16,7 +16,7 @@ pub mod program; use crate::prelude::*; use crate::display::world::Workspace; -use crate::display::render::webgl::Context; +use crate::system::gpu::shader::Context; use crate::display::shape::text::buffer::TextComponentBuffers; use crate::display::shape::text::content::TextComponentContent; use crate::display::shape::text::cursor::Cursors; diff --git a/gui/lib/core/src/display/shape/text/buffer.rs b/gui/lib/core/src/display/shape/text/buffer.rs index 2c35b29ffa..e951409e0a 100644 --- a/gui/lib/core/src/display/shape/text/buffer.rs +++ b/gui/lib/core/src/display/shape/text/buffer.rs @@ -9,9 +9,9 @@ pub mod line; use crate::prelude::*; -use crate::display::render::webgl::Context; -use crate::display::render::webgl::set_buffer_data; -use crate::display::render::webgl::set_buffer_subdata; +use crate::system::gpu::shader::Context; +use crate::system::gpu::shader::set_buffer_data; +use crate::system::gpu::shader::set_buffer_subdata; use crate::display::shape::text::buffer::glyph_square::BASE_LAYOUT_SIZE; use crate::display::shape::text::buffer::glyph_square::GlyphAttributeBuilder; use crate::display::shape::text::buffer::glyph_square::GlyphVertexPositionBuilder; diff --git a/gui/lib/core/src/display/shape/text/cursor.rs b/gui/lib/core/src/display/shape/text/cursor.rs index 71f4dae7e1..1d60a3ae25 100644 --- a/gui/lib/core/src/display/shape/text/cursor.rs +++ b/gui/lib/core/src/display/shape/text/cursor.rs @@ -2,8 +2,8 @@ use crate::prelude::*; -use crate::display::render::webgl::Context; -use crate::display::render::webgl::set_buffer_data; +use crate::system::gpu::Context; +use crate::system::gpu::shader::set_buffer_data; use crate::display::shape::text::content::TextLocation; use crate::display::shape::text::content::TextComponentContent; use crate::display::shape::text::content::line::LineRef; diff --git a/gui/lib/core/src/display/shape/text/program.rs b/gui/lib/core/src/display/shape/text/program.rs index ae61010c62..6212719b60 100644 --- a/gui/lib/core/src/display/shape/text/program.rs +++ b/gui/lib/core/src/display/shape/text/program.rs @@ -2,10 +2,10 @@ use crate::prelude::*; -use crate::display::render::webgl::Context; -use crate::display::render::webgl::compile_shader; -use crate::display::render::webgl::link_program; -use crate::display::render::webgl::Program; +use crate::system::gpu::shader::Context; +use crate::system::gpu::shader::compile_shader; +use crate::system::gpu::shader::link_program; +use crate::system::gpu::shader::Program; use crate::display::shape::text::font::FontRenderInfo; use crate::display::shape::text::msdf::MsdfTexture; use crate::display::shape::text::TextComponentProperties; diff --git a/gui/lib/core/src/display/symbol.rs b/gui/lib/core/src/display/symbol.rs index 7971d0e7da..4536c668ab 100644 --- a/gui/lib/core/src/display/symbol.rs +++ b/gui/lib/core/src/display/symbol.rs @@ -9,28 +9,28 @@ pub mod registry; #[warn(missing_docs)] pub mod shader; +pub mod types { + use super::*; + pub use geometry::types::*; +} +pub use types::*; + + use crate::prelude::*; -use crate::closure; use crate::data::dirty::traits::*; use crate::data::dirty; -use crate::data::function::callback::*; use crate::debug::stats::Stats; -use crate::display::render::webgl::Context; -use crate::display::render::webgl; -use crate::system::gpu::buffer::IsBuffer; +use crate::system::gpu::shader::Context; +use crate::system::gpu::data::buffer::IsBuffer; use crate::system::gpu::data::uniform::AnyUniform; -use crate::system::gpu::data::uniform::AnyUniformOps; -use crate::system::gpu::data::uniform::UniformScope; +use crate::system::gpu::data::uniform::AnyTextureUniform; +use crate::system::gpu::data::uniform::AnyPrimUniform; +use crate::system::gpu::data::uniform::AnyPrimUniformOps; use crate::display::symbol::geometry::primitive::mesh; -use crate::promote; -use crate::promote_all; -use crate::promote_mesh_types; -use crate::promote_shader_types; -use crate::system::web::group; -use crate::system::web::Logger; -use eval_tt::*; +use shader::Shader; + use web_sys::WebGlVertexArrayObject; use web_sys::WebGlProgram; use web_sys::WebGlUniformLocation; @@ -38,16 +38,33 @@ use web_sys::WebGlUniformLocation; + /// Binds input variable definition in shader to both its location and an uniform declaration. #[derive(Clone,Debug)] pub struct UniformBinding { name : String, location : WebGlUniformLocation, - uniform : AnyUniform, + uniform : AnyPrimUniform, } impl UniformBinding { - pub fn new(name:Name, location:WebGlUniformLocation, uniform:AnyUniform) -> Self { + pub fn new(name:Name, location:WebGlUniformLocation, uniform:AnyPrimUniform) -> Self { + let name = name.into(); + Self {name,location,uniform} + } +} + + + +#[derive(Clone,Debug)] +pub struct TextureBinding { + name : String, + location : WebGlUniformLocation, + uniform : AnyTextureUniform, +} + +impl TextureBinding { + pub fn new(name:Name, location:WebGlUniformLocation, uniform:AnyTextureUniform) -> Self { let name = name.into(); Self {name,location,uniform} } @@ -119,73 +136,64 @@ impl Drop for VertexArrayObject { /// Symbol is a surface with attached `Shader`. #[derive(Derivative)] #[derivative(Debug(bound=""))] -pub struct Symbol { - pub surface : Mesh , - pub shader : Shader , - pub surface_dirty : GeometryDirty , - pub shader_dirty : ShaderDirty , - pub logger : Logger, +pub struct Symbol { + pub surface : Mesh, + pub shader : Shader, + pub surface_dirty : GeometryDirty, + pub shader_dirty : ShaderDirty, + symbol_scope : UniformScope, + global_scope : UniformScope, context : Context, + logger : Logger, vao : Option, uniforms : Vec, + textures : Vec, stats : Stats, } // === Types === -pub type GeometryDirty = dirty::SharedBool; -pub type ShaderDirty = dirty::SharedBool; -promote_mesh_types! { [OnSurfaceMut] mesh } -promote_shader_types! { [OnSurfaceMut] shader } +#[derive(Copy,Clone,Debug,PartialEq)] +pub enum ScopeType { + Mesh(mesh::ScopeType), Symbol, Global +} -#[macro_export] -/// Promote relevant types to parent scope. See `promote!` macro for more information. -macro_rules! promote_symbol_types { ($($args:tt)*) => { - crate::promote_mesh_types! {$($args)*} - crate::promote_shader_types! {$($args)*} - promote! {$($args)* [Symbol]} -};} +pub type GeometryDirty = dirty::SharedBool>; +pub type ShaderDirty = dirty::SharedBool>; -// === Callbacks === - -closure! { -fn surface_on_mut(dirty:GeometryDirty) -> OnSurfaceMut { - || dirty.set() -}} - -closure! { -fn shader_on_mut(dirty:ShaderDirty) -> OnShaderMut { - || dirty.set() -}} - // === Implementation === -impl Symbol { +impl Symbol { /// Create new instance with the provided on-dirty callback. - pub fn new - (global:&UniformScope, logger:Logger, stats:&Stats, ctx:&Context, on_dirty:OnMut) -> Self { + pub fn new + (global_scope:&UniformScope, logger:Logger, stats:&Stats, context:&Context, on_mut:OnMut) -> Self { stats.inc_symbol_count(); let init_logger = logger.clone(); group!(init_logger, "Initializing.", { - let context = ctx.clone(); - let on_dirty2 = on_dirty.clone(); + let on_mut2 = on_mut.clone(); let surface_logger = logger.sub("surface"); let shader_logger = logger.sub("shader"); let geo_dirt_logger = logger.sub("surface_dirty"); let mat_dirt_logger = logger.sub("shader_dirty"); - let surface_dirty = GeometryDirty::new(geo_dirt_logger,on_dirty2); - let shader_dirty = ShaderDirty::new(mat_dirt_logger,on_dirty); - let geo_on_change = surface_on_mut(surface_dirty.clone_ref()); - let mat_on_change = shader_on_mut(shader_dirty.clone_ref()); - let shader = Shader::new(shader_logger,&stats,ctx,mat_on_change); - let surface = Mesh::new(global,surface_logger,&stats,ctx,geo_on_change); + let surface_dirty = GeometryDirty::new(geo_dirt_logger,Box::new(on_mut2)); + let shader_dirty = ShaderDirty::new(mat_dirt_logger,Box::new(on_mut)); + let surface_dirty2 = surface_dirty.clone_ref(); + let shader_dirty2 = shader_dirty.clone_ref(); + let surface_on_mut = Box::new(move || { surface_dirty2.set() }); + let shader_on_mut = Box::new(move || { shader_dirty2.set() }); + let shader = Shader::new(shader_logger,&stats,context,shader_on_mut); + let surface = Mesh::new(surface_logger,&stats,context,surface_on_mut); + let symbol_scope = UniformScope::new(logger.sub("uniform_scope"),context); + let global_scope = global_scope.clone(); let vao = default(); let uniforms = default(); + let textures = default(); let stats = stats.clone_ref(); - Self{surface,shader,surface_dirty,shader_dirty,logger,context,vao,uniforms,stats} + let context = context.clone(); + Self{surface,shader,surface_dirty,shader_dirty,symbol_scope,global_scope,logger,context,vao,uniforms,textures,stats} }) } @@ -199,7 +207,7 @@ impl Symbol { if self.shader_dirty.check() { let var_bindings = self.discover_variable_bindings(); self.shader.update(&var_bindings); - self.init_vao(&var_bindings); + self.init_variable_bindings(&var_bindings); self.shader_dirty.unset(); } }) @@ -207,53 +215,69 @@ impl Symbol { /// Creates a new VertexArrayObject, discovers all variable bindings from shader to geometry, /// and initializes the VAO with the bindings. - fn init_vao(&mut self, var_bindings:&[shader::VarBinding]) { - self.vao = Some(VertexArrayObject::new(&self.context)); - let mut uniforms: Vec = default(); - self.with_program(|program|{ + fn init_variable_bindings(&mut self, var_bindings:&[shader::VarBinding]) { + self.vao = Some(VertexArrayObject::new(&self.context)); + self.uniforms = default(); + self.textures = default(); + self.with_program_mut(|this,program|{ for binding in var_bindings { if let Some(scope_type) = binding.scope.as_ref() { - let opt_scope = self.surface.var_scope(*scope_type); - match opt_scope { - None => { - let name = &binding.name; - let uni_name = shader::builder::mk_uniform_name(name); - let location = self.context.get_uniform_location(program,&uni_name); - match location { - None => self.logger.warning(|| format!("The uniform '{}' is not used in this shader. It is recommended to remove it from the material definition.", name)), - Some(location) => { - let uniform = self.surface.scopes.global.get(name).unwrap(); - let binding = UniformBinding::new(name,location,uniform); - uniforms.push(binding); - } - } - - }, - Some(scope) => { - let vtx_name = shader::builder::mk_vertex_name(&binding.name); - let location = self.context.get_attrib_location(program, &vtx_name); - if location < 0 { - self.logger.error(|| format!("Attribute '{}' not found.",vtx_name)); - } else { - let location = location as u32; - let buffer = &scope.buffer(&binding.name).unwrap(); - let is_instanced = scope_type == &mesh::ScopeType::Instance; - buffer.bind(webgl::Context::ARRAY_BUFFER); - buffer.vertex_attrib_pointer(location, is_instanced); - } - } + match scope_type { + ScopeType::Mesh(s) => this.init_attribute_binding(program,binding,*s), + _ => this.init_uniform_binding(program,binding), } } } }); - self.uniforms = uniforms; + } + + + fn init_attribute_binding(&mut self, program:&WebGlProgram, binding:&shader::VarBinding, mesh_scope_type:mesh::ScopeType) { + let vtx_name = shader::builder::mk_vertex_name(&binding.name); + let scope = self.surface.scope_by_type(mesh_scope_type); + let location = self.context.get_attrib_location(program, &vtx_name); + if location < 0 { + self.logger.error(|| format!("Attribute '{}' not found.",vtx_name)); + } else { + let location = location as u32; + let buffer = &scope.buffer(&binding.name).unwrap(); + let is_instanced = mesh_scope_type == mesh::ScopeType::Instance; + buffer.bind(Context::ARRAY_BUFFER); + buffer.vertex_attrib_pointer(location, is_instanced); + } + } + + fn init_uniform_binding(&mut self, program:&WebGlProgram, binding:&shader::VarBinding) { + let name = &binding.name; + let uni_name = shader::builder::mk_uniform_name(name); + let opt_location = self.context.get_uniform_location(program,&uni_name); + opt_location.map(|location|{ + let uniform = self.global_scope.get(name).unwrap_or_else(||{ + panic!("Internal error. Variable {} not found in program.",name) + }); + match uniform { + AnyUniform::Prim(uniform) => + self.uniforms.push(UniformBinding::new(name,location,uniform)), + AnyUniform::Texture(uniform) => + self.textures.push(TextureBinding::new(name,location,uniform)), + } + }); + } + + pub fn lookup_variable(&self, name:S) -> Option { + let name = name.as_ref(); + self.surface.lookup_variable(name).map(ScopeType::Mesh).or_else(|| { + if self.symbol_scope.contains(name) { Some(ScopeType::Symbol) } + else if self.global_scope.contains(name) { Some(ScopeType::Global) } + else { None } + }) } /// For each variable from the shader definition, looks up its position in geometry scopes. pub fn discover_variable_bindings(&self) -> Vec { let var_decls = self.shader.collect_variables(); var_decls.into_iter().map(|(var_name,var_decl)| { - let target = self.surface.lookup_variable(&var_name); + let target = self.lookup_variable(&var_name); if target.is_none() { let msg = || format!("Unable to bind variable '{}' to geometry buffer.", var_name); self.logger.warning(msg); @@ -268,13 +292,29 @@ impl Symbol { let program = self.shader.program().as_ref().unwrap(); // FIXME self.context.use_program(Some(&program)); let vao = self.vao.as_ref().unwrap(); // FIXME - let out = vao.with(|| { - f(program) - }); + let out = vao.with(||{ f(program) }); self.context.use_program(None); out } + /// Runs the provided function in a context of active program and active VAO. After the function + /// is executed, both program and VAO are bound to None. + pub fn with_program_mut T,T>(&mut self, f:F) -> T { + let this:&mut Self = self; + let program = this.shader.program().as_ref().unwrap().clone(); // FIXME + this.context.use_program(Some(&program)); + let out = this.with_vao_mut(|this|{ f(this,&program) }); + self.context.use_program(None); + out + } + + pub fn with_vao_mut T,T>(&mut self, f:F) -> T { + self.vao.as_ref().unwrap().bind(); + let out = f(self); + self.vao.as_ref().unwrap().unbind(); + out + } + pub fn render(&self) { group!(self.logger, "Rendering.", { self.with_program(|_|{ @@ -282,10 +322,10 @@ impl Symbol { binding.uniform.upload(&self.context,&binding.location); } - let mode = webgl::Context::TRIANGLE_STRIP; + let mode = Context::TRIANGLE_STRIP; let first = 0; - let count = self.surface.scopes.point.size() as i32; - let instance_count = self.surface.scopes.instance.size() as i32; + let count = self.surface.point.size() as i32; + let instance_count = self.surface.instance.size() as i32; self.stats.inc_draw_call_count(); self.context.draw_arrays_instanced(mode,first,count,instance_count); @@ -295,7 +335,7 @@ impl Symbol { } } -impl Drop for Symbol { +impl Drop for Symbol { fn drop(&mut self) { self.stats.dec_symbol_count(); } diff --git a/gui/lib/core/src/display/symbol/geometry.rs b/gui/lib/core/src/display/symbol/geometry.rs index 82ebfe74ad..77c2fb50f3 100644 --- a/gui/lib/core/src/display/symbol/geometry.rs +++ b/gui/lib/core/src/display/symbol/geometry.rs @@ -3,4 +3,16 @@ pub mod compound; pub mod primitive; -pub mod sprite; + + +// ================= +// === Reexports === +// ================= + +/// Common types. +pub mod types { + use super::*; + pub use primitive::types::*; + pub use compound::types::*; +} +pub use types::*; diff --git a/gui/lib/core/src/display/symbol/geometry/compound.rs b/gui/lib/core/src/display/symbol/geometry/compound.rs index a54b72d839..67f4cadff3 100644 --- a/gui/lib/core/src/display/symbol/geometry/compound.rs +++ b/gui/lib/core/src/display/symbol/geometry/compound.rs @@ -1 +1,16 @@ -#![allow(missing_docs)] +//! Root module for compound geometries. Compound geometries are defined by using primitive +//! geometries and behave like smart constructors for commonly used shapes. + +pub mod sprite; + + + +// =============== +// === Exports === +// =============== + +/// Common types. +pub mod types { + use super::*; + pub use sprite::*; +} diff --git a/gui/lib/core/src/display/symbol/geometry/compound/.gitkeep b/gui/lib/core/src/display/symbol/geometry/compound/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gui/lib/core/src/display/symbol/geometry/sprite.rs b/gui/lib/core/src/display/symbol/geometry/compound/sprite.rs similarity index 86% rename from gui/lib/core/src/display/symbol/geometry/sprite.rs rename to gui/lib/core/src/display/symbol/geometry/compound/sprite.rs index 4080126486..103e7f2bb4 100644 --- a/gui/lib/core/src/display/symbol/geometry/sprite.rs +++ b/gui/lib/core/src/display/symbol/geometry/compound/sprite.rs @@ -4,12 +4,12 @@ use crate::prelude::*; -use crate::display::object::*; -use crate::display::symbol::geometry::primitive::mesh::InstanceId; use crate::display::symbol::material::Material; +use crate::system::gpu::data::AttributeInstanceIndex; + +use crate::display::object::*; use crate::display::world::*; -use basegl_system_web::Logger; use nalgebra::Vector2; use nalgebra::Vector3; use nalgebra::Matrix4; @@ -29,7 +29,7 @@ pub struct SymbolRef { impl SymbolRef { /// Constructor. - pub fn new(world: World, symbol_id:SymbolId) -> Self { + pub fn new(world:World, symbol_id:SymbolId) -> Self { Self {world,symbol_id} } } @@ -44,12 +44,12 @@ impl SymbolRef { #[derive(Clone,Debug)] pub struct SpriteRef { symbol_ref : SymbolRef, - instance_id : InstanceId, + instance_id : AttributeInstanceIndex, } impl SpriteRef { /// Constructor. - pub fn new(symbol_ref:SymbolRef, instance_id:InstanceId) -> Self { + pub fn new(symbol_ref:SymbolRef, instance_id:AttributeInstanceIndex) -> Self { Self {symbol_ref,instance_id} } } @@ -132,16 +132,17 @@ struct SpriteData { impl SpriteData { pub fn new - (sprite_ref:SpriteRef, _transform:Attribute>, bbox:Attribute>) -> Self { + ( sprite_ref:SpriteRef + , _transform:Attribute> + , bbox:Attribute> + ) -> Self { let logger = Logger::new(format!("Sprite{}",sprite_ref.instance_id)); let display_object = DisplayObjectData::new(logger); let transform_cp = _transform.clone(); display_object.set_on_updated(move |t| { transform_cp.set(t.matrix().clone()); }); - sprite_ref.symbol_ref.world.mod_stats(|stats| stats.inc_sprite_count()); - Self {sprite_ref,display_object,_transform,bbox} } } @@ -181,11 +182,11 @@ impl Drop for SpriteData { /// system is a very efficient way to display geometry. Sprites are rendered as instances of the /// same mesh. Each sprite can be controlled by the instance and global attributes. pub struct SpriteSystem { - display_object : DisplayObjectData, - symbol_ref : SymbolRef, - transform : Buffer>, - _uv : Buffer>, - bbox : Buffer>, + display_object : DisplayObjectData, + symbol_ref : SymbolRef, + transform : Buffer>, + _uv : Buffer>, + bbox : Buffer>, } impl SpriteSystem { @@ -213,10 +214,10 @@ impl SpriteSystem { let p3_index = mesh.scopes.point.add_instance(); let p4_index = mesh.scopes.point.add_instance(); - uv.get(p1_index).set(Vector2::new(0.0, 0.0)); - uv.get(p2_index).set(Vector2::new(0.0, 1.0)); - uv.get(p3_index).set(Vector2::new(1.0, 0.0)); - uv.get(p4_index).set(Vector2::new(1.0, 1.0)); + uv.at(p1_index).set(Vector2::new(0.0, 0.0)); + uv.at(p2_index).set(Vector2::new(0.0, 1.0)); + uv.at(p3_index).set(Vector2::new(1.0, 0.0)); + uv.at(p4_index).set(Vector2::new(1.0, 1.0)); world_data.stats.inc_sprite_system_count(); @@ -232,8 +233,8 @@ impl SpriteSystem { let symbol = &mut world_data.workspace[self.symbol_ref.symbol_id]; symbol.surface.instance.add_instance() }; - let transform = self.transform.get(instance_id); - let bbox = self.bbox.get(instance_id); + let transform = self.transform.at(instance_id); + let bbox = self.bbox.at(instance_id); let sprite_ref = SpriteRef::new(self.symbol_ref.clone(),instance_id); bbox.set(Vector2::new(1.0,1.0)); let sprite = Sprite::new(sprite_ref,transform,bbox); @@ -243,11 +244,11 @@ impl SpriteSystem { fn geometry_material() -> Material { let mut material = Material::new(); - material.add_input ("bounds" , Vector2::::zeros()); - material.add_input ("uv" , Vector2::::zeros()); - material.add_input ("transform" , Matrix4::::identity()); - material.add_input ("view_projection" , Matrix4::::identity()); - material.add_output ("local" , Vector3::::zeros()); + material.add_input_def :: > ("bounds"); + material.add_input_def :: > ("uv"); + material.add_input_def :: > ("transform"); + material.add_input_def :: > ("view_projection"); + material.add_output_def :: > ("local"); material.set_main(" mat4 model_view_projection = input_view_projection * input_transform; input_local = vec3((input_uv - 0.5) * input_bounds, 0.0); diff --git a/gui/lib/core/src/display/symbol/geometry/primitive.rs b/gui/lib/core/src/display/symbol/geometry/primitive.rs index 75a8cda1a5..8d611ad177 100644 --- a/gui/lib/core/src/display/symbol/geometry/primitive.rs +++ b/gui/lib/core/src/display/symbol/geometry/primitive.rs @@ -1,4 +1,16 @@ -#![allow(missing_docs)] +//! Root module for primitive geometry definitions. A primitive geometry could not be constructed +//! from other types. -#[warn(missing_docs)] pub mod mesh; + + +// =============== +// === Exports === +// =============== + +/// Common types. +pub mod types { + use super::*; + pub use mesh::types::*; +} +pub use types::*; diff --git a/gui/lib/core/src/display/symbol/geometry/primitive/mesh.rs b/gui/lib/core/src/display/symbol/geometry/primitive/mesh.rs index 93c02cdadb..badef5498d 100644 --- a/gui/lib/core/src/display/symbol/geometry/primitive/mesh.rs +++ b/gui/lib/core/src/display/symbol/geometry/primitive/mesh.rs @@ -1,26 +1,31 @@ -#![allow(missing_docs)] +//! This module defines a [polygon mesh](https://en.wikipedia.org/wiki/Polygon_mesh). -pub use crate::system::gpu::data; -pub use crate::system::gpu::data::Uniform; -pub use crate::system::gpu::data::UniformScope; +use crate::prelude::*; use crate::closure; +use crate::control::callback::CallbackFn; use crate::data::dirty::traits::*; use crate::data::dirty; -use crate::data::function::callback::*; use crate::debug::stats::Stats; -use crate::display::render::webgl::Context; -use crate::prelude::*; -use crate::promote; -use crate::promote_all; -use crate::promote_scope_types; -use crate::system::web::group; -use crate::system::web::Logger; -use eval_tt::*; +use crate::system::gpu::shader::Context; + use num_enum::IntoPrimitive; +// =============== +// === Exports === +// =============== + +/// Common data types. +pub mod types { + pub use crate::system::gpu::types::*; + pub use super::Mesh; +} +pub use types::*; + + + // ============ // === Mesh === // ============ @@ -29,26 +34,11 @@ use num_enum::IntoPrimitive; /// A polygon mesh is a collection of vertices, edges and faces that defines the shape of a /// polyhedral object. Mesh describes the shape of the display element. It consist of several -/// scopes containing sets of variables. +/// scopes containing sets of variables. See the documentation of `Scopes` to learn more. /// -/// - Point Scope -/// A point is simply a point in space. Points are often assigned with such variables as -/// 'position' or 'color'. +/// Please note, that there are other, higher-level scopes defined by other structures, including: /// -/// - Vertex Scope -/// A vertex is a reference to a point. Primitives use vertices to reference points. For -/// example, the corners of a polygon, the center of a sphere, or a control vertex of a spline -/// curve. Primitives can share points, while vertices are unique to a primitive. -/// -/// - Primitive Scope -/// Primitives refer to a unit of geometry, lower-level than an object but above points. There -/// are several different types of primitives, including polygon faces or Bezier/NURBS surfaces. -/// -/// - Instance Scope -/// Instances are virtual copies of the same geometry. They share point, vertex, and primitive -/// variables. -/// -/// - Object Scope +/// - Symbol Scope /// Object refers to the whole geometry with all of its instances. /// /// - Global Scope @@ -59,40 +49,45 @@ use num_enum::IntoPrimitive; /// name was defined in various scopes, it gets resolved to the var defined in the most specific /// scope. For example, if var 'color' was defined in both 'instance' and 'point' scope, the 'point' /// definition overlapps the other one. -#[derive(Shrinkwrap)] +#[derive(Debug,Shrinkwrap)] #[shrinkwrap(mutable)] -#[derive(Derivative)] -#[derivative(Debug(bound=""))] -pub struct Mesh { +pub struct Mesh { + /// Scope list. #[shrinkwrap(main_field)] - pub scopes : Scopes , - pub scopes_dirty : ScopesDirty , - pub logger : Logger, - context : Context, - stats : Stats, + pub scopes : Scopes, + scopes_dirty : ScopesDirty, + logger : Logger, + context : Context, + stats : Stats, } -#[derive(Derivative)] -#[derivative(Debug(bound=""))] -pub struct Scopes { - pub point : AttributeScope , - pub vertex : AttributeScope , - pub primitive : AttributeScope , - pub instance : AttributeScope , - pub object : UniformScope, - pub global : UniformScope, +/// Container for all scopes owned by a mesh. +#[derive(Debug)] +pub struct Scopes { + /// Point Scope. A point is simply a point in space. Points are often assigned with such + /// variables as 'position' or 'color'. + pub point: AttributeScope, + + /// Vertex Scope. A vertex is a reference to a point. Primitives use vertices to reference + /// points. For example, the corners of a polygon, the center of a sphere, or a control vertex + /// of a spline curve. Primitives can share points, while vertices are unique to a primitive. + pub vertex: AttributeScope, + + /// Primitive Scope. Primitives refer to a unit of geometry, lower-level than an object but + /// above points. There are several different types of primitives, including polygon faces or + /// Bezier/NURBS surfaces. + pub primitive: AttributeScope, + + /// Instance Scope. Instances are virtual copies of the same geometry. They share point, vertex, + /// and primitive variables. + pub instance: AttributeScope, } -pub type PointId = usize; -pub type VertexId = usize; -pub type PrimitiveId = usize; -pub type InstanceId = usize; - -#[derive(Copy,Clone,Debug,IntoPrimitive,PartialEq)] +/// A singleton for each of scope types. +#[derive(Copy,Clone,Debug,Display,IntoPrimitive,PartialEq)] +#[allow(missing_docs)] #[repr(u8)] -pub enum ScopeType { - Point, Vertex, Primitive, Instance, Object, Global -} +pub enum ScopeType {Point,Vertex,Primitive,Instance} impl From for usize { fn from(t: ScopeType) -> Self { @@ -100,67 +95,47 @@ impl From for usize { } } -impl Display for ScopeType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f,"{:?}",self) - } -} - // === Types === -pub type ScopesDirty = dirty::SharedEnum; -promote_scope_types!{ [ScopeOnChange] data } - -#[macro_export] -/// Promote relevant types to parent scope. See `promote!` macro for more information. -macro_rules! promote_mesh_types { ($($args:tt)*) => { - crate::promote_scope_types! { $($args)* } - promote! {$($args)* [Mesh,Scopes]} -};} - - -// === Callbacks === +/// Dirty flag remembering which scopes were mutated. +pub type ScopesDirty = dirty::SharedEnum>; closure! { -fn scope_on_change(dirty:ScopesDirty, item:ScopeType) -> ScopeOnChange { +fn scope_on_change(dirty:ScopesDirty, item:ScopeType) -> ScopeOnChange { || dirty.set(item) }} // === Implementation === -macro_rules! update_scopes { ($self:ident . {$($name:ident),*} {$($uname:ident),*}) => {$( - if $self.scopes_dirty.check(&ScopeType::$uname) { - $self.scopes.$name.update() - } -)*}} - -impl Mesh { +macro_rules! update_scopes { + ($self:ident . {$($name:ident),*} {$($uname:ident),*}) => {$( + if $self.scopes_dirty.check(&ScopeType::$uname) { + $self.scopes.$name.update() + } + )*} +} +impl Mesh { /// Creates new mesh with attached dirty callback. - pub fn new - (global:&UniformScope, logger:Logger, stats:&Stats, context:&Context, on_mut:OnMut) -> Self { + pub fn new + (logger:Logger, stats:&Stats, context:&Context,on_mut:OnMut) -> Self { stats.inc_mesh_count(); let stats = stats.clone(); let scopes_logger = logger.sub("scopes_dirty"); - let scopes_dirty = ScopesDirty::new(scopes_logger,on_mut); + let scopes_dirty = ScopesDirty::new(scopes_logger,Box::new(on_mut)); let context = context.clone(); let scopes = group!(logger, "Initializing.", { - macro_rules! new_scope { ($cls:ident { $($name:ident),* } { $($uname:ident),* } ) => {$( + macro_rules! new_scope { ({ $($name:ident),* } { $($uname:ident),* } ) => {$( let sub_logger = logger.sub(stringify!($name)); let status_mod = ScopeType::$uname; let scs_dirty = scopes_dirty.clone_ref(); - let callback = scope_on_change(scs_dirty, status_mod); - let $name = $cls::new(sub_logger,&stats,&context,callback); + let callback = move || {scs_dirty.set(status_mod)}; + let $name = AttributeScope::new(sub_logger,&stats,&context,callback); )*}} - new_scope!(AttributeScope {point,vertex,primitive,instance}{Point,Vertex,Primitive,Instance}); - - let object_scope_logger = logger.sub("object"); - let object = UniformScope::new(object_scope_logger); - let global = global.clone(); - - Scopes {point,vertex,primitive,instance,object,global} + new_scope! ({point,vertex,primitive,instance}{Point,Vertex,Primitive,Instance}); + Scopes {point,vertex,primitive,instance} }); Self {context,scopes,scopes_dirty,logger,stats} } @@ -169,9 +144,9 @@ impl Mesh { pub fn update(&mut self) { group!(self.logger, "Updating.", { if self.scopes_dirty.check_all() { - update_scopes!(self.{point,vertex,primitive,instance} - {Point,Vertex,Primitive,Instance}); -// update_scopes!(self.{object,global}{Object,Global}); + update_scopes!{ + self.{point,vertex,primitive,instance}{Point,Vertex,Primitive,Instance} + } self.scopes_dirty.unset_all() } }) @@ -185,24 +160,21 @@ impl Mesh { else if self.scopes.vertex . contains(name) { Some(ScopeType::Vertex) } else if self.scopes.primitive . contains(name) { Some(ScopeType::Primitive) } else if self.scopes.instance . contains(name) { Some(ScopeType::Instance) } - else if self.scopes.object . contains(name) { Some(ScopeType::Object) } - else if self.scopes.global . contains(name) { Some(ScopeType::Global) } else {None} } /// Gets reference to scope based on the scope type. - pub fn var_scope(&self, scope_type:ScopeType) -> Option<&AttributeScope> { + pub fn scope_by_type(&self, scope_type:ScopeType) -> &AttributeScope { match scope_type { - ScopeType::Point => Some(&self.scopes.point), - ScopeType::Vertex => Some(&self.scopes.vertex), - ScopeType::Primitive => Some(&self.scopes.primitive), - ScopeType::Instance => Some(&self.scopes.instance), - _ => None + ScopeType::Point => &self.scopes.point, + ScopeType::Vertex => &self.scopes.vertex, + ScopeType::Primitive => &self.scopes.primitive, + ScopeType::Instance => &self.scopes.instance, } } } -impl Drop for Mesh { +impl Drop for Mesh { fn drop(&mut self) { self.stats.dec_mesh_count(); } diff --git a/gui/lib/core/src/display/symbol/material.rs b/gui/lib/core/src/display/symbol/material.rs index 78072779d2..5b18307a29 100644 --- a/gui/lib/core/src/display/symbol/material.rs +++ b/gui/lib/core/src/display/symbol/material.rs @@ -2,9 +2,8 @@ use crate::prelude::*; -use crate::system::gpu::data::GpuData; -use crate::display::render::webgl::glsl; use crate::display::symbol::shader::builder::CodeTemplete; +use crate::system::gpu::types::*; @@ -31,6 +30,13 @@ impl VarDecl { } } +impl + Into> +From for VarDecl { + fn from(t:T) -> Self { + Self::new(::glsl_prim_type(), t.glsl().into()) + } +} + // ================ @@ -48,6 +54,9 @@ pub struct Material { outputs : BTreeMap, } +/// Bounds for the material inputs. +pub trait Input = Into + GpuDefault; + impl Material { /// Constructor. pub fn new() -> Self { @@ -55,17 +64,23 @@ impl Material { } /// Adds a new input variable. - pub fn add_input(&mut self, name:Name, t:T) { - self.inputs.insert(name.into(),Self::make_var_decl(t)); + pub fn add_input(&mut self, name:&str, t:T) { + self.inputs.insert(name.into(),t.into()); } /// Adds a new output variable. - pub fn add_output(&mut self, name:Name, t:T) { - self.outputs.insert(name.into(),Self::make_var_decl(t)); + pub fn add_output(&mut self, name:&str, t:T) { + self.outputs.insert(name.into(),t.into()); } - fn make_var_decl(t:T) -> VarDecl { - VarDecl::new(::glsl_type(), t.to_glsl()) + /// Adds a new input variable. + pub fn add_input_def(&mut self, name:&str) { + self.inputs.insert(name.into(),::gpu_default().into()); + } + + /// Adds a new output variable. + pub fn add_output_def(&mut self, name:&str) { + self.outputs.insert(name.into(),::gpu_default().into()); } } diff --git a/gui/lib/core/src/display/symbol/registry.rs b/gui/lib/core/src/display/symbol/registry.rs index 9a0f1d33c8..9a38e86941 100644 --- a/gui/lib/core/src/display/symbol/registry.rs +++ b/gui/lib/core/src/display/symbol/registry.rs @@ -5,23 +5,18 @@ use crate::prelude::*; use crate::closure; use crate::data::dirty::traits::*; use crate::data::dirty; -use crate::data::function::callback::*; use crate::debug::stats::Stats; -use crate::display::camera::Camera2D; -use crate::display::render::webgl::Context; -use crate::display::symbol; -use crate::promote; -use crate::promote_all; -use crate::promote_symbol_types; +use crate::display::camera::Camera2d; +use crate::display::symbol::Symbol; use crate::system::gpu::data::uniform::Uniform; use crate::system::gpu::data::uniform::UniformScope; -use crate::system::web::group; -use crate::system::web::Logger; +use crate::system::gpu::shader::Context; + use data::opt_vec::OptVec; -use eval_tt::*; use nalgebra::Matrix4; + // ====================== // === SymbolRegistry === // ====================== @@ -29,14 +24,12 @@ use nalgebra::Matrix4; // === Definition === /// Registry for all the created symbols. -#[derive(Derivative)] -#[derivative(Debug(bound=""))] -pub struct SymbolRegistry { - pub symbols : OptVec>, - pub symbol_dirty : SymbolDirty, +#[derive(Debug)] +pub struct SymbolRegistry { + pub symbols : OptVec, + pub symbol_dirty : SymbolDirty, pub logger : Logger, pub view_projection : Uniform>, - pub zoom : Uniform, variables : UniformScope, context : Context, stats : Stats, @@ -45,42 +38,33 @@ pub struct SymbolRegistry { // === Types === -pub type SymbolId = usize; -pub type SymbolDirty = dirty::SharedSet; -promote_symbol_types!{ [OnSymbolChange] symbol } - -#[macro_export] -/// Promote relevant types to parent scope. See `promote!` macro for more information. -macro_rules! promote_symbol_registry_types { ($($args:tt)*) => { - crate::promote_symbol_types! { $($args)* } - promote! { $($args)* [SymbolRegistry] } -};} +pub type SymbolId = usize; +pub type SymbolDirty = dirty::SharedSet>; // === Callbacks === closure! { -fn mesh_on_change (dirty:SymbolDirty, ix:SymbolId) -> OnSymbolChange { +fn mesh_on_change(dirty:SymbolDirty, ix:SymbolId) -> OnSymbolChange { || dirty.set(ix) }} // === Implementation === -impl SymbolRegistry { +impl SymbolRegistry { /// Create new instance with the provided on-dirty callback. - pub fn new(variables:&UniformScope, stats:&Stats, context:&Context, logger:Logger, on_mut:OnDirty) -> Self { + pub fn new(variables:&UniformScope, stats:&Stats, context:&Context, logger:Logger, on_mut:OnMut) -> Self { logger.info("Initializing."); let symbol_logger = logger.sub("symbol_dirty"); - let symbol_dirty = SymbolDirty::new(symbol_logger, on_mut); + let symbol_dirty = SymbolDirty::new(symbol_logger,Box::new(on_mut)); let symbols = default(); let variables = variables.clone(); let view_projection = variables.add_or_panic("view_projection", Matrix4::::identity()); - let zoom = variables.add_or_panic("zoom" , 1.0); let context = context.clone(); let stats = stats.clone_ref(); - Self {symbols,symbol_dirty,logger,view_projection,zoom,variables,context,stats} + Self {symbols,symbol_dirty,logger,view_projection,variables,context,stats} } /// Creates a new `Symbol` instance. @@ -91,7 +75,7 @@ impl SymbolRegistry { let context = &self.context; let stats = &self.stats; self.symbols.insert_with_ix(|ix| { - let on_mut = mesh_on_change(symbol_dirty, ix); + let on_mut = move || {symbol_dirty.set(ix)}; let logger = logger.sub(format!("symbol{}",ix)); Symbol::new(variables,logger,stats,context,on_mut) }) @@ -107,11 +91,10 @@ impl SymbolRegistry { }) } - pub fn render(&self, camera:&Camera2D) { + pub fn render(&self, camera:&Camera2d) { let changed = camera.update(); if changed { self.view_projection.set(camera.view_projection_matrix()); - self.zoom.set(camera.zoom()); } group!(self.logger, "Rendering.", { for symbol in &self.symbols { @@ -121,14 +104,14 @@ impl SymbolRegistry { } } -impl Index for SymbolRegistry { - type Output = Symbol; +impl Index for SymbolRegistry { + type Output = Symbol; fn index(&self, ix:usize) -> &Self::Output { self.symbols.index(ix) } } -impl IndexMut for SymbolRegistry { +impl IndexMut for SymbolRegistry { fn index_mut(&mut self, ix:usize) -> &mut Self::Output { self.symbols.index_mut(ix) } diff --git a/gui/lib/core/src/display/symbol/shader.rs b/gui/lib/core/src/display/symbol/shader.rs index aa4e02ad30..88a2ae7b2f 100644 --- a/gui/lib/core/src/display/symbol/shader.rs +++ b/gui/lib/core/src/display/symbol/shader.rs @@ -7,17 +7,14 @@ use crate::prelude::*; use crate::data::dirty::traits::*; use crate::data::dirty; -use crate::data::function::callback::*; use crate::debug::stats::Stats; -use crate::display::render::webgl::Context; -use crate::display::render::webgl::glsl; -use crate::display::render::webgl; use crate::display::symbol::material::Material; use crate::display::symbol::material::VarDecl; +use crate::display::symbol::ScopeType; use crate::display::symbol::shader; -use crate::system::web::group; -use crate::system::web::Logger; -use crate::display::symbol::geometry::primitive::mesh::ScopeType; +use crate::system::gpu::shader::*; +use crate::system::gpu::shader::Context; +use crate::control::callback::CallbackFn; use web_sys::WebGlProgram; @@ -52,43 +49,37 @@ impl VarBinding { /// Shader keeps track of a shader and related WebGL Program. #[derive(Derivative)] #[derivative(Debug(bound=""))] -pub struct Shader { +pub struct Shader { geometry_material : Material, - material : Material, + surface_material : Material, program : Option, - pub dirty : Dirty , - pub logger : Logger, + dirty : Dirty, + logger : Logger, context : Context, stats : Stats, } // === Types === -pub type Dirty = dirty::SharedBool; - -#[macro_export] -/// Promote relevant types to parent scope. See `promote!` macro for more information. -macro_rules! promote_shader_types { ($($args:tt)*) => { - promote! {$($args)* [Shader]} -};} +pub type Dirty = dirty::SharedBool>; // === Implementation === -impl Shader { +impl Shader { /// Creates new shader with attached callback. - pub fn new(logger:Logger, stats:&Stats, context:&Context, on_mut:OnMut) -> Self { + pub fn new(logger:Logger, stats:&Stats, context:&Context, on_mut:OnMut) -> Self { stats.inc_shader_count(); let geometry_material = default(); - let material = default(); + let surface_material = default(); let program = default(); let dirty_logger = logger.sub("dirty"); - let dirty = Dirty::new(dirty_logger,on_mut); + let dirty = Dirty::new(dirty_logger,Box::new(on_mut)); let context = context.clone(); let stats = stats.clone_ref(); dirty.set(); - Self {geometry_material,material,program,dirty,logger,context,stats} + Self {geometry_material,surface_material,program,dirty,logger,context,stats} } // TODO: this is very work-in-progress function. It should be refactored in the next PR. @@ -108,10 +99,9 @@ impl Shader { match binding.scope { None => todo!(), Some(scope_type) => match scope_type { - ScopeType::Instance => shader_cfg.add_attribute (name,tp), - ScopeType::Point => shader_cfg.add_attribute (name,tp), - ScopeType::Global => shader_cfg.add_uniform (name,tp), - _ => todo!() + ScopeType::Symbol => shader_cfg.add_uniform (name,tp), + ScopeType::Global => shader_cfg.add_uniform (name,tp), + _ => shader_cfg.add_attribute (name,tp), } } } @@ -123,18 +113,18 @@ impl Shader { shader_cfg.add_output("color", glsl::PrimType::Vec4); let vertex_code = self.geometry_material.code().clone(); - let fragment_code = self.material.code().clone(); + let fragment_code = self.surface_material.code().clone(); shader_builder.compute(&shader_cfg,vertex_code,fragment_code); let shader = shader_builder.build(); - let vert_shader = webgl::compile_vertex_shader (&self.context,&shader.vertex); - let frag_shader = webgl::compile_fragment_shader(&self.context,&shader.fragment); + let vert_shader = compile_vertex_shader (&self.context,&shader.vertex); + let frag_shader = compile_fragment_shader(&self.context,&shader.fragment); if let Err(ref err) = frag_shader { self.logger.error(|| format!("{}", err)) } let vert_shader = vert_shader.unwrap(); let frag_shader = frag_shader.unwrap(); - let program = webgl::link_program(&self.context,&vert_shader,&frag_shader); + let program = link_program(&self.context,&vert_shader,&frag_shader); let program = program.unwrap(); self.program = Some(program); @@ -146,12 +136,12 @@ impl Shader { /// Traverses the shader definition and collects all attribute names. pub fn collect_variables(&self) -> BTreeMap { let geometry_material_inputs = self.geometry_material.inputs().clone(); - let surface_material_inputs = self.material.inputs().clone(); + let surface_material_inputs = self.surface_material.inputs().clone(); geometry_material_inputs.into_iter().chain(surface_material_inputs).collect() } } -impl Drop for Shader { +impl Drop for Shader { fn drop(&mut self) { self.stats.dec_shader_count(); } @@ -160,7 +150,7 @@ impl Drop for Shader { // === Getters === -impl Shader { +impl Shader { pub fn program(&self) -> &Option { &self.program } @@ -169,14 +159,14 @@ impl Shader { // === Setters === -impl Shader { +impl Shader { pub fn set_geometry_material>(&mut self, material:M) { self.geometry_material = material.into(); self.dirty.set(); } pub fn set_material>(&mut self, material:M) { - self.material = material.into(); + self.surface_material = material.into(); self.dirty.set(); } } diff --git a/gui/lib/core/src/display/symbol/shader/builder.rs b/gui/lib/core/src/display/symbol/shader/builder.rs index 48a522b7a7..f00bd0849c 100644 --- a/gui/lib/core/src/display/symbol/shader/builder.rs +++ b/gui/lib/core/src/display/symbol/shader/builder.rs @@ -3,7 +3,7 @@ use crate::prelude::*; use crate::data::container::Add; -use crate::display::render::webgl::glsl; +use crate::system::gpu::shader::glsl; use code_builder::HasCodeRepr; use std::collections::BTreeMap; diff --git a/gui/lib/core/src/display/world.rs b/gui/lib/core/src/display/world.rs index 989a05a7fc..092846e928 100644 --- a/gui/lib/core/src/display/world.rs +++ b/gui/lib/core/src/display/world.rs @@ -7,34 +7,28 @@ pub mod scene; #[warn(missing_docs)] pub mod workspace; -use crate::prelude::*; - +pub use crate::display::symbol::types::*; +pub use crate::display::world::workspace::Workspace; pub use crate::data::container::*; pub use crate::display::world::workspace::SymbolId; +use crate::prelude::*; + use crate::closure; use crate::control::callback::CallbackHandle; -use crate::data::dirty; use crate::data::dirty::traits::*; -use crate::debug::stats::Stats; -use crate::promote_all; -use crate::promote_workspace_types; -use crate::promote; -use crate::system::web; -use crate::system::web::group; -use crate::system::web::Logger; -use crate::display::shape::text::font::Fonts; -use crate::debug::monitor; +use crate::data::dirty; use crate::debug::monitor::Monitor; use crate::debug::monitor::Panel; -use crate::system::gpu::data::uniform::UniformScope; -use crate::system::gpu::data::uniform::Uniform; +use crate::debug::monitor; +use crate::debug::stats::Stats; +use crate::display::shape::text::font::Fonts; + +use crate::system::web; +use event_loop::EventLoop; use wasm_bindgen::prelude::Closure; use wasm_bindgen::{JsCast, JsValue}; - use web_sys::{Performance,KeyboardEvent}; -use event_loop::EventLoop; -use eval_tt::*; @@ -186,7 +180,6 @@ pub struct WorldData { pub workspace_dirty : WorkspaceDirty, pub logger : Logger, pub event_loop : EventLoop, - pub variables : UniformScope, pub performance : Performance, pub start_time : f32, pub time : Uniform, @@ -202,7 +195,6 @@ pub struct WorldData { pub type WorkspaceID = usize; pub type WorkspaceDirty = dirty::SharedBool; -promote_workspace_types!{ [[WorkspaceOnChange]] workspace } // === Callbacks === @@ -238,7 +230,8 @@ impl WorldData { if key == "0" { world_copy.borrow_mut().display_mode.set(0) } else if key == "1" { world_copy.borrow_mut().display_mode.set(1) } })); - web::document().unwrap().add_event_listener_with_callback("keydown",c.as_ref().unchecked_ref()).unwrap(); + web::document().unwrap().add_event_listener_with_callback + ("keydown",c.as_ref().unchecked_ref()).unwrap(); c.forget(); // ----------------------------------------------------------------------------------------- @@ -253,11 +246,12 @@ impl WorldData { let workspace_logger = logger.sub("workspace"); let workspace_dirty_logger = logger.sub("workspace_dirty"); let workspace_dirty = WorkspaceDirty::new(workspace_dirty_logger,()); - let on_change = workspace_on_change(workspace_dirty.clone_ref()); - let variables = UniformScope::new(logger.sub("global_variables")); + let workspace_dirty2 = workspace_dirty.clone(); + let on_change = move || {workspace_dirty2.set()}; + let workspace = Workspace::new(dom,workspace_logger,&stats,on_change).unwrap(); // fixme unwrap + let variables = &workspace.variables; let time = variables.add_or_panic("time",0.0); let display_mode = variables.add_or_panic("display_mode",0); - let workspace = Workspace::new(dom,&variables,workspace_logger,&stats,on_change).unwrap(); // fixme unwrap let fonts = Fonts::new(); let event_loop = EventLoop::new(); let update_handle = default(); @@ -268,7 +262,8 @@ impl WorldData { let stats_monitor_cp_2 = stats_monitor.clone(); event_loop.set_on_loop_started (move || { stats_monitor_cp_1.begin(); }); event_loop.set_on_loop_finished (move || { stats_monitor_cp_2.end(); }); - Self {workspace,workspace_dirty,logger,event_loop,variables,performance,start_time,time,display_mode,fonts,update_handle,stats,stats_monitor} + Self {workspace,workspace_dirty,logger,event_loop,performance,start_time,time,display_mode + ,fonts,update_handle,stats,stats_monitor} } pub fn run(&mut self) { @@ -282,11 +277,10 @@ impl WorldData { //TODO[WD]: Re-think when should we check the condition (uniform update): // if self.workspace_dirty.check_all() { group!(self.logger, "Updating.", { - // FIXME render only needed workspaces. - self.workspace_dirty.unset_all(); - let fonts = &mut self.fonts; - self.workspace.update(fonts); - }); + self.workspace_dirty.unset_all(); + let fonts = &mut self.fonts; + self.workspace.update(fonts); + }); } /// Dispose the world object, cancel all handlers and events. @@ -300,5 +294,3 @@ impl Drop for WorldData { self.logger.info("Dropping."); } } - - diff --git a/gui/lib/core/src/display/world/scene.rs b/gui/lib/core/src/display/world/scene.rs index 47e710690e..002077d7e9 100644 --- a/gui/lib/core/src/display/world/scene.rs +++ b/gui/lib/core/src/display/world/scene.rs @@ -2,9 +2,10 @@ use crate::prelude::*; -use crate::display::camera::Camera2D; +use crate::display::camera::Camera2d; use crate::display::object::DisplayObjectData; -use basegl_system_web::Logger; +use crate::system::gpu::data::uniform::UniformScope; + // ============= @@ -15,17 +16,16 @@ use basegl_system_web::Logger; #[derivative(Debug(bound=""))] pub struct Scene { pub root : DisplayObjectData, - pub camera : Camera2D + pub camera : Camera2d } // === Implementation === impl Scene { - pub fn new(logger:Logger) -> Self { + pub fn new(logger:Logger, globals:&UniformScope) -> Self { let root = DisplayObjectData::new(logger.sub("root")); - let camera = Camera2D::new(logger.sub("camera")); + let camera = Camera2d::new(logger.sub("camera"),globals); Self {root,camera} } } - diff --git a/gui/lib/core/src/display/world/workspace.rs b/gui/lib/core/src/display/world/workspace.rs index 594fb68027..3b247d2592 100644 --- a/gui/lib/core/src/display/world/workspace.rs +++ b/gui/lib/core/src/display/world/workspace.rs @@ -7,25 +7,18 @@ pub use crate::display::symbol::registry::SymbolId; use crate::closure; use crate::data::dirty::traits::*; use crate::data::dirty; -use crate::data::function::callback::*; use crate::debug::stats::Stats; -use crate::display::render::webgl; +use crate::system::gpu::shader::Context; use crate::display::shape::text::font::Fonts; use crate::display::shape::text; use crate::display::world::scene::Scene; -use crate::display::symbol::registry; -use crate::promote; -use crate::promote_all; -use crate::promote_symbol_registry_types; -use crate::system::web::fmt; -use crate::system::web::group; -use crate::system::web::Logger; +use crate::display::symbol::Symbol; +use crate::display::symbol::registry::SymbolRegistry; + use crate::system::web::resize_observer::ResizeObserver; use crate::system::web; use crate::system::gpu::data::uniform::UniformScope; - -use eval_tt::*; use wasm_bindgen::prelude::Closure; @@ -115,16 +108,17 @@ impl ShapeData { #[derive(Derivative)] #[derivative(Debug(bound=""))] -pub struct Workspace { +pub struct Workspace { pub canvas : web_sys::HtmlCanvasElement, - pub context : webgl::Context, - pub symbols : SymbolRegistry, - pub symbols_dirty : SymbolRegistryDirty, + pub context : Context, + pub symbols : SymbolRegistry, + pub symbols_dirty : SymbolRegistryDirty, pub scene : Scene, pub shape : Shape, - pub shape_dirty : ShapeDirty, + pub shape_dirty : ShapeDirty, pub logger : Logger, pub listeners : Listeners, + pub variables : UniformScope, // TODO[AO] this is a very temporary solution. Need to develop some general component handling. pub text_components : Vec, } @@ -132,22 +126,14 @@ pub struct Workspace { // === Types === -pub type ShapeDirty = dirty::SharedBool; -pub type SymbolRegistryDirty = dirty::SharedBool; -promote_symbol_registry_types!{ [OnSymbolRegistryChange] registry } - -#[macro_export] -/// Promote relevant types to parent scope. See `promote!` macro for more information. -macro_rules! promote_workspace_types { ($($args:tt)*) => { - crate::promote_symbol_registry_types! { $($args)* } - promote! { $($args)* [Workspace] } -};} +pub type ShapeDirty = dirty::SharedBool>; +pub type SymbolRegistryDirty = dirty::SharedBool>; // === Callbacks === closure! { -fn symbols_on_change (dirty:SymbolRegistryDirty) -> OnSymbolRegistryChange { +fn symbols_on_change(dirty:SymbolRegistryDirty) -> OnSymbolRegistryChange { || dirty.set() }} @@ -159,44 +145,43 @@ pub struct Listeners { resize: ResizeObserver, } -impl Workspace { +impl Workspace { /// Create new instance with the provided on-dirty callback. - pub fn new - (dom:Dom, variables:&UniformScope, logger:Logger, stats:&Stats, on_dirty:OnMut) -> Result { + pub fn new + (dom:Dom, logger:Logger, stats:&Stats, on_mut:OnMut) -> Result { logger.trace("Initializing."); let dom = dom.as_ref(); let canvas = web::get_canvas(dom)?; let context = web::get_webgl2_context(&canvas)?; let sub_logger = logger.sub("shape_dirty"); - let shape_dirty = ShapeDirty::new(sub_logger,on_dirty.clone()); + let shape_dirty = ShapeDirty::new(sub_logger,Box::new(on_mut.clone())); let sub_logger = logger.sub("symbols_dirty"); - let dirty_flag = SymbolRegistryDirty::new(sub_logger, on_dirty); + let dirty_flag = SymbolRegistryDirty::new(sub_logger,Box::new(on_mut)); let on_change = symbols_on_change(dirty_flag.clone_ref()); let sub_logger = logger.sub("symbols"); - let symbols = SymbolRegistry::new(variables,&stats,&context,sub_logger,on_change); + let variables = UniformScope::new(logger.sub("global_variables"),&context); + let symbols = SymbolRegistry::new(&variables,&stats,&context,sub_logger,on_change); let shape = Shape::default(); let listeners = Self::init_listeners(&logger,&canvas,&shape,&shape_dirty); let symbols_dirty = dirty_flag; - let scene = Scene::new(logger.sub("scene")); + let scene = Scene::new(logger.sub("scene"),&variables); let text_components = default(); variables.add("pixel_ratio", shape.pixel_ratio()); // FIXME: use correct blending function and rething premultiplying the alpha. - context.enable(webgl::Context::BLEND); - // context.blend_func(webgl::Context::ONE, webgl::Context::ONE_MINUS_SRC_ALPHA); - context.blend_func(webgl::Context::SRC_ALPHA, webgl::Context::ONE); - - let this = Self {canvas,context,symbols,scene,symbols_dirty - ,shape,shape_dirty,logger,listeners,text_components}; - + context.enable(Context::BLEND); + // context.blend_func(Context::ONE, Context::ONE_MINUS_SRC_ALPHA); + context.blend_func(Context::SRC_ALPHA, Context::ONE); + let this = Self {canvas,context,symbols,scene,symbols_dirty,shape,shape_dirty,logger + ,listeners,variables,text_components}; Ok(this) } /// Initialize all listeners and attach them to DOM elements. fn init_listeners - (logger:&Logger, canvas:&web_sys::HtmlCanvasElement, shape:&Shape, dirty:&ShapeDirty) + (logger:&Logger, canvas:&web_sys::HtmlCanvasElement, shape:&Shape, dirty:&ShapeDirty) -> Listeners { let logger = logger.clone(); let shape = shape.clone(); @@ -225,7 +210,7 @@ impl Workspace { self.logger.group(fmt!("Resized to {}px x {}px.", screen.width, screen.height), || { self.canvas.set_attribute("width", &canvas.width.to_string()).unwrap(); self.canvas.set_attribute("height", &canvas.height.to_string()).unwrap(); - self.context.viewport(0, 0, canvas.width as i32, canvas.height as i32); + self.context.viewport(0,0,canvas.width as i32, canvas.height as i32); }); } @@ -245,7 +230,7 @@ impl Workspace { self.logger.info("Clearing the scene."); self.context.clear_color(0.0, 0.0, 0.0, 1.0); - self.context.clear(webgl::Context::COLOR_BUFFER_BIT); + self.context.clear(Context::COLOR_BUFFER_BIT); self.logger.info("Rendering meshes."); self.symbols.render(&self.scene.camera); if !self.text_components.is_empty() { @@ -258,14 +243,14 @@ impl Workspace { } } -impl Index for Workspace { - type Output = Symbol; +impl Index for Workspace { + type Output = Symbol; fn index(&self, ix: usize) -> &Self::Output { self.symbols.index(ix) } } -impl IndexMut for Workspace { +impl IndexMut for Workspace { fn index_mut(&mut self, ix: usize) -> &mut Self::Output { self.symbols.index_mut(ix) } diff --git a/gui/lib/core/src/examples/camera_navigation.rs b/gui/lib/core/src/examples/camera_navigation.rs index 82ff50fa29..d3d4cca106 100644 --- a/gui/lib/core/src/examples/camera_navigation.rs +++ b/gui/lib/core/src/examples/camera_navigation.rs @@ -2,10 +2,10 @@ use wasm_bindgen::prelude::*; -use crate::display::render::css3d::Scene; -use crate::display::render::css3d::Camera; -use crate::display::render::css3d::html::HTMLObject; -use crate::display::render::css3d::html::HTMLRenderer; +use crate::system::web::dom::Scene; +use crate::system::web::dom::Camera; +use crate::system::web::dom::html::HTMLObject; +use crate::system::web::dom::html::HTMLRenderer; use crate::system::web::StyleSetter; use crate::display::navigation::navigator::Navigator; diff --git a/gui/lib/core/src/examples/shapes.rs b/gui/lib/core/src/examples/shapes.rs index efe9d9c706..c3c470a7da 100644 --- a/gui/lib/core/src/examples/shapes.rs +++ b/gui/lib/core/src/examples/shapes.rs @@ -1,7 +1,7 @@ #![allow(missing_docs)] use crate::display::object::DisplayObjectOps; -use crate::display::symbol::geometry::sprite::Sprite; +use crate::display::symbol::geometry::Sprite; use crate::display::shape::primitive::system::ShapeSystem; use crate::display::world::*; use crate::system::web::set_stdout; @@ -24,9 +24,6 @@ pub fn run_example_shapes() { } fn init(world: &World) { - - - let s1 = Circle("25.0 + 20.0*sin(input_time/1000.0)"); let s2 = s1.translate(25.0,0.0); let s3 = &s1 + &s2; diff --git a/gui/lib/core/src/examples/sprite_system.rs b/gui/lib/core/src/examples/sprite_system.rs index 19d036c05c..09f1548413 100644 --- a/gui/lib/core/src/examples/sprite_system.rs +++ b/gui/lib/core/src/examples/sprite_system.rs @@ -1,8 +1,8 @@ #![allow(missing_docs)] use crate::display::object::DisplayObjectOps; -use crate::display::symbol::geometry::sprite::Sprite; -use crate::display::symbol::geometry::sprite::SpriteSystem; +use crate::display::symbol::geometry::Sprite; +use crate::display::symbol::geometry::SpriteSystem; use crate::display::world::*; use crate::prelude::*; use crate::system::web::set_stdout; diff --git a/gui/lib/core/src/lib.rs b/gui/lib/core/src/lib.rs index 3507c1239a..0a6e26815d 100644 --- a/gui/lib/core/src/lib.rs +++ b/gui/lib/core/src/lib.rs @@ -2,7 +2,7 @@ //! component library. It is able to display millions of shapes 60 frames per second in a web //! browser on a modern laptop hardware. This is the main entry point to the library. -#![cfg_attr(test, allow(dead_code))] +#![allow(dead_code)] #![warn(missing_docs)] #![warn(trivial_casts)] @@ -20,6 +20,7 @@ #![feature(unboxed_closures)] #![feature(weak_into_raw)] #![feature(drain_filter)] +#![recursion_limit="256"] // To be removed after this gets resolved: https://github.com/rust-lang/cargo/issues/5034 #![allow(clippy::option_map_unit_fn)] @@ -49,6 +50,10 @@ pub mod debug; pub mod display; pub mod examples; pub mod system; -pub mod math; -pub use basegl_prelude as prelude; \ No newline at end of file +/// Prelude - commonly used utilities. +pub mod prelude { + pub use basegl_prelude::*; + pub use logger::*; + pub use shapely::newtype_copy; +} diff --git a/gui/lib/core/src/math.rs b/gui/lib/core/src/math.rs deleted file mode 100644 index d8be50c517..0000000000 --- a/gui/lib/core/src/math.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![allow(missing_docs)] - -pub mod utils; \ No newline at end of file diff --git a/gui/lib/core/src/math/utils.rs b/gui/lib/core/src/math/utils.rs deleted file mode 100644 index 635f4c96cb..0000000000 --- a/gui/lib/core/src/math/utils.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![allow(missing_docs)] - -use nalgebra::clamp; -use std::ops::Mul; -use std::ops::Add; - -pub fn linear_interpolation(a:T, b:T, t:f32) -> T - where T : Mul + Add { - let t = clamp(t, 0.0, 1.0); - a * (1.0 - t) + b * t -} diff --git a/gui/lib/core/src/system.rs b/gui/lib/core/src/system.rs index de7e5c3f0c..ea8bd79769 100644 --- a/gui/lib/core/src/system.rs +++ b/gui/lib/core/src/system.rs @@ -2,5 +2,4 @@ //! including the native system, JS world, GPU runtime, etc. pub mod gpu; - -pub use basegl_system_web as web; +pub mod web; diff --git a/gui/lib/core/src/system/gpu.rs b/gui/lib/core/src/system/gpu.rs index 9975162558..514315aa4a 100644 --- a/gui/lib/core/src/system/gpu.rs +++ b/gui/lib/core/src/system/gpu.rs @@ -1,4 +1,17 @@ -//! GPU-specific implementations. +//! GPU-specific types and related implementations. -pub mod buffer; pub mod data; +pub mod shader; + + +/// Common types. +pub mod types { + use web_sys::WebGl2RenderingContext; + + pub use super::data::types::*; + pub use super::shader::types::*; + + /// Alias for WebGl2RenderingContext. + pub type Context = WebGl2RenderingContext; +} +pub use types::*; diff --git a/gui/lib/core/src/system/gpu/buffer.rs b/gui/lib/core/src/system/gpu/buffer.rs deleted file mode 100644 index eeb7d96e00..0000000000 --- a/gui/lib/core/src/system/gpu/buffer.rs +++ /dev/null @@ -1,465 +0,0 @@ -#![allow(missing_docs)] - -use crate::prelude::*; - -use crate::closure; -use crate::data::dirty::traits::*; -use crate::data::dirty; -use crate::data::function::callback::*; -use crate::data::seq::observable::Observable; -use crate::debug::stats::Stats; -use crate::display::render::webgl::Context; -use crate::system::gpu::data::attribute::class::Attribute; -use crate::system::gpu::data::GpuData; -use crate::system::gpu::data::Item; -use crate::system::web::fmt; -use crate::system::web::group; -use crate::system::web::Logger; -use nalgebra::Matrix4; -use nalgebra::Vector2; -use nalgebra::Vector3; -use nalgebra::Vector4; -use std::iter::Extend; -use std::ops::RangeInclusive; -use web_sys::WebGlBuffer; - - - -// ================== -// === BufferData === -// ================== - -// === Definition === - -/// Please refer to the 'Buffer management pipeline' doc to learn more about -/// attributes, scopes, geometries, meshes, scenes, and other relevant concepts. -/// -/// Buffers are values stored in geometry. Under the hood they are stored in -/// vectors and are synchronised with GPU buffers on demand. -#[derive(Derivative,Shrinkwrap)] -#[shrinkwrap(mutable)] -#[derivative(Debug(bound="T:Debug"))] -pub struct BufferData { - #[shrinkwrap(main_field)] - pub buffer : Data , - pub buffer_dirty : BufferDirty , - pub resize_dirty : ResizeDirty , - pub logger : Logger, - pub gl_buffer : WebGlBuffer, - context : Context, - stats : Stats, - gpu_mem_usage : u32, -} - - -// === Types === - -pub type ObservableVec = Observable,OnMut,OnResize>; -pub type Data = ObservableVec,DataOnResize>; - -#[macro_export] -/// Promote relevant types to parent scope. See `promote!` macro for more information. -macro_rules! promote_buffer_types { ($callbacks:tt $module:ident) => { - promote! { $callbacks $module [BufferData,Buffer,AnyBuffer] } -};} - - -// === Callbacks === - -pub type BufferDirty = dirty::SharedRange; -pub type ResizeDirty = dirty::SharedBool; - -closure! { -fn buffer_on_resize (dirty:ResizeDirty) -> DataOnResize { - || dirty.set() -}} - -closure! { -fn buffer_on_mut (dirty:BufferDirty) -> DataOnSet { - |ix: usize| dirty.set(ix) -}} - - -// === Instances === - -impl -BufferData { - - /// Creates a new empty buffer. - pub fn new - (logger:Logger, stats:&Stats, context:&Context, on_mut:OnMut, on_resize:OnResize) -> Self { - stats.inc_buffer_count(); - logger.info(fmt!("Creating new {} buffer.", T::type_display())); - let stats = stats.clone_ref(); - let set_logger = logger.sub("buffer_dirty"); - let resize_logger = logger.sub("resize_dirty"); - let buffer_dirty = BufferDirty::new(set_logger,on_mut); - let resize_dirty = ResizeDirty::new(resize_logger,on_resize); - let buff_on_resize = buffer_on_resize(resize_dirty.clone_ref()); - let buff_on_mut = buffer_on_mut(buffer_dirty.clone_ref()); - let buffer = Data::new(buff_on_mut, buff_on_resize); - let context = context.clone(); - let gl_buffer = create_gl_buffer(&context); - let gpu_mem_usage = default(); - Self {buffer,buffer_dirty,resize_dirty,logger,gl_buffer,context,stats,gpu_mem_usage} - } -} - -impl -BufferData { - - /// View the data as slice of primitive elements. - pub fn as_prim_slice(&self) -> &[Item] { - ::convert_prim_buffer(&self.buffer.data) - } - - /// View the data as slice of elements. - pub fn as_slice(&self) -> &[T] { - &self.buffer.data - } - - /// Check dirty flags and update the state accordingly. - pub fn update(&mut self) { - group!(self.logger, "Updating.", { - self.context.bind_buffer(Context::ARRAY_BUFFER, Some(&self.gl_buffer)); - if self.resize_dirty.check() { - self.upload_data(&None); - } else if self.buffer_dirty.check_all() { - let range = &self.buffer_dirty.take().range; - self.upload_data(range); - } - self.buffer_dirty.unset_all(); - self.resize_dirty.unset(); - }) - } - - /// Uploads the provided data to the GPU buffer. - fn upload_data(&mut self, opt_range:&Option>) { - // Note that `js_buffer_view` is somewhat dangerous (hence the `unsafe`!). This is creating - // a raw view into our module's `WebAssembly.Memory` buffer, but if we allocate more pages - // for ourself (aka do a memory allocation in Rust) it'll cause the buffer to change, - // causing the resulting js array to be invalid. - // - // As a result, after `js_buffer_view` we have to be very careful not to do any memory - // allocations before it's dropped. - - self.logger.info("Setting buffer data."); - self.stats.inc_data_upload_count(); - - let data = self.as_slice(); - let item_byte_size = ::gpu_item_byte_size() as u32; - let item_count = ::item_count() as u32; - - match opt_range { - None => unsafe { - let js_array = data.js_buffer_view(); - self.context.buffer_data_with_array_buffer_view - (Context::ARRAY_BUFFER, &js_array, Context::STATIC_DRAW); - - self.stats.mod_gpu_memory_usage(|s| s - self.gpu_mem_usage); - self.gpu_mem_usage = self.len() as u32 * item_count * item_byte_size; - self.stats.mod_gpu_memory_usage(|s| s + self.gpu_mem_usage); - self.stats.mod_data_upload_size(|s| s + self.gpu_mem_usage); - } - Some(range) => { - let start = *range.start() as u32; - let end = *range.end() as u32; - let start_item = start * item_count; - let length = (end - start + 1) * item_count; - let dst_byte_offset = (item_byte_size * item_count * start) as i32; - unsafe { - let js_array = data.js_buffer_view(); - self.context.buffer_sub_data_with_i32_and_array_buffer_view_and_src_offset_and_length - (Context::ARRAY_BUFFER,dst_byte_offset,&js_array,start_item,length) - } - self.stats.mod_data_upload_size(|s| s + length * item_byte_size); - } - } - } - - /// Binds the buffer currently bound to gl.ARRAY_BUFFER to a generic vertex attribute of the - /// current vertex buffer object and specifies its layout. Please note that this function is - /// more complex that a raw call to `WebGLRenderingContext.vertexAttribPointer`, as it correctly - /// handles complex data types like `mat4`. See the following links to learn more: - /// https://developer.mozilla.org/docs/Web/API/WebGLRenderingContext/vertexAttribPointer - /// https://stackoverflow.com/questions/38853096/webgl-how-to-bind-values-to-a-mat4-attribute - pub fn vertex_attrib_pointer(&self, loc:u32, instanced:bool) { - let item_byte_size = ::gpu_item_byte_size() as i32; - let item_type = ::glsl_item_type_code(); - let rows = ::rows() as i32; - let cols = ::cols() as i32; - let col_byte_size = item_byte_size * rows; - let stride = col_byte_size * cols; - let normalize = false; - for col in 0..cols { - let lloc = loc + col as u32; - let off = col * col_byte_size; - self.context.enable_vertex_attrib_array(lloc); - self.context.vertex_attrib_pointer_with_i32(lloc,rows,item_type,normalize,stride,off); - if instanced { - self.context.vertex_attrib_divisor(lloc, 1); - } - } - } -} - -impl -BufferData { - /// Returns the number of elements in the buffer. - pub fn len(&self) -> usize { - self.buffer.len() - } - - /// Checks if the buffer is empty. - pub fn is_empty(&self) -> bool { - self.buffer.is_empty() - } - - /// Binds the underlying WebGLBuffer to a given target. - /// https://developer.mozilla.org/docs/Web/API/WebGLRenderingContext/bindBuffer - pub fn bind(&self, target:u32) { - self.context.bind_buffer(target, Some(&self.gl_buffer)); - } -} - -pub trait AddElementCtx = where - T: GpuData + Clone, - OnResize: Callback0; - -impl -BufferData where Self: AddElementCtx { - /// Adds a single new element initialized to default value. - pub fn add_element(&mut self) { - self.add_elements(1); - } - - /// Adds multiple new elements initialized to default values. - pub fn add_elements(&mut self, elem_count: usize) { - self.extend(iter::repeat(T::empty()).take(elem_count)); - } -} - -impl -Index for BufferData { - type Output = T; - fn index(&self, index: usize) -> &Self::Output { - self.buffer.index(index) - } -} - -impl -IndexMut for BufferData { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - self.buffer.index_mut(index) - } -} - -impl Drop for BufferData { - fn drop(&mut self) { - self.context.delete_buffer(Some(&self.gl_buffer)); - self.stats.mod_gpu_memory_usage(|s| s - self.gpu_mem_usage); - self.stats.dec_buffer_count(); - } -} - - -// === Utils === - -fn create_gl_buffer(context:&Context) -> WebGlBuffer { - let buffer = context.create_buffer(); - buffer.ok_or("failed to create buffer").unwrap() -} - - - -// ============== -// === Buffer === -// ============== - -/// Shared view for `Buffer`. -#[derive(Derivative)] -#[derivative(Debug(bound="T:Debug"))] -#[derivative(Clone(bound=""))] -pub struct Buffer { - pub rc: Rc>> -} - -impl -Buffer { - /// Creates a new empty buffer. - pub fn new - (logger:Logger, stats:&Stats, context:&Context, on_mut:OnMut, on_resize:OnResize) -> Self { - let data = BufferData::new(logger,stats,context,on_mut,on_resize); - let rc = Rc::new(RefCell::new(data)); - Self {rc} - } -} - -impl -Buffer { - /// Check dirty flags and update the state accordingly. - pub fn update(&self) { - self.rc.borrow_mut().update() - } - - /// binds the buffer currently bound to gl.ARRAY_BUFFER to a generic vertex - /// attribute of the current vertex buffer object and specifies its layout. - /// https://developer.mozilla.org/docs/Web/API/WebGLRenderingContext/vertexAttribPointer - pub fn vertex_attrib_pointer(&self, index:u32, instanced:bool) { - self.rc.borrow().vertex_attrib_pointer(index,instanced) - } -} - -impl -Buffer { - // FIXME: Rethink if buffer should know about Attribute. - /// Get the variable by given index. - pub fn get(&self, index:usize) -> Attribute { - Attribute::new(index, self.clone()) - } - - /// Returns the number of elements in the buffer. - pub fn len(&self) -> usize { - self.rc.borrow().len() - } - - /// Checks if the buffer is empty. - pub fn is_empty(&self) -> bool { - self.rc.borrow().is_empty() - } - - /// Binds the underlying WebGLBuffer to a given target. - /// https://developer.mozilla.org/docs/Web/API/WebGLRenderingContext/bindBuffer - pub fn bind(&self, target:u32) { - self.rc.borrow().bind(target) - } -} - -impl -Buffer where (): AddElementCtx { - /// Adds a single new element initialized to default value. - pub fn add_element(&self){ - self.rc.borrow_mut().add_element() - } -} - -impl -From>>> for Buffer { - fn from(rc: Rc>>) -> Self { - Self {rc} - } -} - - - -// ======================== -// === TO BE REFACTORED === -// ======================== - -// TODO The following code should be refactored to use the new macro `eval-tt` -// TODO engine. Some utils, like `cartesian` macro should also be refactored -// TODO out. - -macro_rules! cartesian_impl { - ($out:tt [] $b:tt $init_b:tt, $f:ident) => { - $f!{ $out } - }; - ($out:tt [$a:ident, $($at:tt)*] [] $init_b:tt, $f:ident) => { - cartesian_impl!{ $out [$($at)*] $init_b $init_b, $f } - }; - ([$($out:tt)*] [$a:ident, $($at:tt)*] [$b:ident, $($bt:tt)*] $init_b:tt - ,$f:ident) => { - cartesian_impl!{ - [$($out)* ($a, $b),] [$a, $($at)*] [$($bt)*] $init_b, $f - } - }; -} - -macro_rules! cartesian { - ([$($a:tt)*], [$($b:tt)*], $f:ident) => { - cartesian_impl!{ [] [$($a)*,] [$($b)*,] [$($b)*,], $f } - }; -} - - - -// ================= -// === AnyBuffer === -// ================= - -use enum_dispatch::*; - -// === Macros === - -#[derive(Debug)] -pub struct BadVariant; - -macro_rules! mk_any_buffer_impl { -([$(($base:ident, $param:ident)),*,]) => { paste::item! { - - /// An enum with a variant per possible buffer type (i32, f32, Vector, - /// and many, many more). It provides a faster alternative to dyn trait one: - /// `Buffer`. - #[enum_dispatch(IsBuffer)] - #[derive(Derivative)] - #[derivative(Debug(bound=""))] - pub enum AnyBuffer { - $( [] - (Buffer<$base<$param>, OnMut, OnResize>), - )* - } - - $( // ====================================================================== - - impl<'t, T, S> - TryFrom<&'t AnyBuffer> - for &'t Buffer<$base<$param>, T, S> { - type Error = BadVariant; - fn try_from(v: &'t AnyBuffer) - -> Result <&'t Buffer<$base<$param>, T, S>, Self::Error> { - match v { - AnyBuffer::[](a) => Ok(a), - _ => Err(BadVariant) - } - } - } - - impl<'t, T, S> - TryFrom<&'t mut AnyBuffer> - for &'t mut Buffer<$base<$param>, T, S> { - type Error = BadVariant; - fn try_from(v: &'t mut AnyBuffer) - -> Result <&'t mut Buffer<$base<$param>, T, S>, Self::Error> { - match v { - AnyBuffer::[](a) => Ok(a), - _ => Err(BadVariant) - } - } - } - - )* // ====================================================================== -} -}} - -macro_rules! mk_any_buffer { - ($bases:tt, $params:tt) => { - cartesian!($bases, $params, mk_any_buffer_impl); - } -} - - -// === Definition === - -type Identity = T; -mk_any_buffer!([Identity,Vector2,Vector3,Vector4,Matrix4], [f32]); - -/// Collection of all methods common to every buffer variant. -#[enum_dispatch] -pub trait IsBuffer { - fn add_element(&self); - fn len(&self) -> usize; - fn is_empty(&self) -> bool; - fn update(&self); - fn bind(&self, target:u32); - fn vertex_attrib_pointer(&self, index:u32, instanced:bool); -} diff --git a/gui/lib/core/src/system/gpu/data.rs b/gui/lib/core/src/system/gpu/data.rs index 8c747b8ae5..a597f1a9f8 100644 --- a/gui/lib/core/src/system/gpu/data.rs +++ b/gui/lib/core/src/system/gpu/data.rs @@ -6,15 +6,38 @@ //! https://www.khronos.org/opengl/wiki/Type_Qualifier_(GLSL) pub mod attribute; -pub mod class; +pub mod buffer; +pub mod default; +pub mod gl_enum; +pub mod prim; +pub mod sized; +pub mod texture; pub mod uniform; - // ================= // === Reexports === // ================= pub use attribute::*; -pub use class::*; +pub use buffer::item::*; +pub use default::*; pub use uniform::*; + +/// Common types. +pub mod types { + use super::*; + pub use attribute::Attribute; + pub use attribute::AttributeScope; + pub use buffer::AnyBuffer; + pub use buffer::Buffer; + pub use buffer::IsBuffer; + pub use buffer::BufferItem; + pub use default::GpuDefault; + pub use gl_enum::GlEnum; + pub use gl_enum::traits::*; + pub use prim::*; + pub use uniform::Uniform; + pub use uniform::UniformScope; +} +pub use types::*; diff --git a/gui/lib/core/src/system/gpu/data/attribute.rs b/gui/lib/core/src/system/gpu/data/attribute.rs index 0fddb96cdc..50928cdfe0 100644 --- a/gui/lib/core/src/system/gpu/data/attribute.rs +++ b/gui/lib/core/src/system/gpu/data/attribute.rs @@ -1,146 +1,136 @@ -#![allow(missing_docs)] - -#[warn(missing_docs)] -pub mod class; +//! This module defines attributes and related utilities. use crate::prelude::*; use crate::closure; -use crate::data::dirty::traits::*; +use crate::control::callback::CallbackFn; use crate::data::dirty; -use crate::data::function::callback::*; -use crate::debug::stats::Stats; -use crate::display::render::webgl::Context; -use crate::system::gpu::buffer::IsBuffer; -use crate::system::gpu::data::GpuData; -use crate::system::gpu::buffer; -use crate::promote; -use crate::promote_all; -use crate::promote_buffer_types; -use crate::system::web::group; -use crate::system::web::Logger; -use data::opt_vec::OptVec; -use eval_tt::*; +use crate::data::OptVec; +use crate::debug::Stats; +use crate::system::gpu::Context; + +use crate::data::dirty::traits::*; +use crate::system::gpu::types::*; -// ============= -// === Scope === -// ============= - -// === Definition === +// ====================== +// === AttributeScope === +// ====================== /// Scope defines a view for geometry structure. For example, there is point /// scope or instance scope. Scope contains buffer of data for each item it /// describes. -#[derive(Derivative)] -#[derivative(Debug(bound=""))] -pub struct AttributeScope { - pub buffers : OptVec>, - pub buffer_dirty : BufferDirty, - pub shape_dirty : ShapeDirty, - pub name_map : HashMap, - pub logger : Logger, - free_ids : Vec, - size : usize, - context : Context, - stats : Stats, +#[derive(Debug)] +pub struct AttributeScope { + buffers : OptVec, + buffer_dirty : BufferDirty, + shape_dirty : ShapeDirty, + buffer_name_map : HashMap, + logger : Logger, + free_ids : Vec, + size : usize, + context : Context, + stats : Stats, } // === Types === -pub type InstanceId = usize; -pub type BufferIndex = usize; -pub type BufferName = String; -pub type BufferDirty = dirty::SharedBitField; -pub type ShapeDirty = dirty::SharedBool; -pub type Attribute = class::Attribute,BufferOnResize>; -promote_buffer_types! {[BufferOnSet,BufferOnResize] buffer} +newtype_copy! { + /// Index of the attribute instance. + AttributeInstanceIndex(usize); -#[macro_export] -/// Promote relevant types to parent scope. See `promote!` macro for more information. -macro_rules! promote_scope_types { ($callbacks:tt $module:ident) => { - crate::promote_buffer_types! { $callbacks $module } - promote! { $callbacks $module [Attribute,AttributeScope] } -};} + /// Index of the attribute instance. + BufferIndex(usize); +} + +/// Dirty flag collecting information which buffers were mutated. +pub type BufferDirty = dirty::SharedBitField>; + +/// Dirty flag indicating that the shape of the attribute (all buffers) was changed. +pub type ShapeDirty = dirty::SharedBool>; // === Callbacks === closure! { -fn buffer_on_set (dirty:BufferDirty, ix:usize) -> BufferOnSet { +fn buffer_on_set(dirty:BufferDirty, ix:usize) -> BufferOnSet { || dirty.set(ix) }} closure! { -fn buffer_on_resize (dirty:ShapeDirty) -> BufferOnResize { +fn buffer_on_resize(dirty:ShapeDirty) -> BufferOnResize { || dirty.set() }} // === Implementation === -impl AttributeScope { +impl AttributeScope { /// Create a new scope with the provided dirty callback. - pub fn new(logger:Logger, stats:&Stats, context:&Context, on_mut:OnMut) -> Self { - logger.info("Initializing."); - let stats = stats.clone_ref(); - let buffer_logger = logger.sub("buffer_dirty"); - let shape_logger = logger.sub("shape_dirty"); - let buffer_dirty = BufferDirty::new(buffer_logger,on_mut.clone()); - let shape_dirty = ShapeDirty::new(shape_logger,on_mut); - let buffers = default(); - let name_map = default(); - let free_ids = default(); - let size = default(); - let context = context.clone(); - Self {context,buffers,buffer_dirty,shape_dirty,name_map,logger,free_ids,size,stats} + pub fn new + (logger:Logger, stats:&Stats, context:&Context, on_mut:OnMut) -> Self { + info!(logger,"Initializing.",{ + let stats = stats.clone_ref(); + let buffer_logger = logger.sub("buffer_dirty"); + let shape_logger = logger.sub("shape_dirty"); + let buffer_dirty = BufferDirty::new(buffer_logger,Box::new(on_mut.clone())); + let shape_dirty = ShapeDirty::new(shape_logger,Box::new(on_mut)); + let buffers = default(); + let buffer_name_map = default(); + let free_ids = default(); + let size = default(); + let context = context.clone(); + Self {context,buffers,buffer_dirty,shape_dirty,buffer_name_map,logger,free_ids,size + ,stats} + }) } } -impl AttributeScope { +impl AttributeScope { /// Adds a new named buffer to the scope. - pub fn add_buffer(&mut self, name:Name) -> Buffer - where AnyBuffer: From> { + pub fn add_buffer(&mut self, name:Name) -> Buffer + where AnyBuffer: From> { let name = name.as_ref().to_string(); let buffer_dirty = self.buffer_dirty.clone(); let shape_dirty = self.shape_dirty.clone(); let ix = self.buffers.reserve_ix(); - group!(self.logger, "Adding buffer '{}' at index {}.", name, ix, { + group!(self.logger, "Adding buffer '{name}' at index {ix}.", { let on_set = buffer_on_set(buffer_dirty, ix); let on_resize = buffer_on_resize(shape_dirty); let logger = self.logger.sub(&name); let context = &self.context; let buffer = Buffer::new(logger,&self.stats,context,on_set,on_resize); let buffer_ref = buffer.clone(); - self.buffers.set(ix, AnyBuffer::from(buffer)); - self.name_map.insert(name, ix); + self.buffers.set(ix,AnyBuffer::from(buffer)); + self.buffer_name_map.insert(name,ix.into()); self.shape_dirty.set(); buffer_ref }) } /// Lookups buffer by a given name. - pub fn buffer(&self, name:&str) -> Option<&AnyBuffer> { - self.name_map.get(name).map(|i| &self.buffers[*i]) + pub fn buffer(&self, name:&str) -> Option<&AnyBuffer> { + self.buffer_name_map.get(name).map(|i| &self.buffers[(*i).into()]) } /// Checks if a buffer with the given name was created in this scope. pub fn contains(&self, name:S) -> bool { - self.name_map.contains_key(name.as_ref()) + self.buffer_name_map.contains_key(name.as_ref()) } /// Adds a new instance to every buffer in the scope. - pub fn add_instance(&mut self) -> InstanceId { - group!(self.logger, "Adding {} instance(s).", 1, { + pub fn add_instance(&mut self) -> AttributeInstanceIndex { + let instance_count = 1; + group!(self.logger, "Adding {instance_count} instance(s).", { match self.free_ids.pop() { Some(ix) => ix, None => { let ix = self.size; - self.size += 1; + self.size += instance_count; self.buffers.iter_mut().for_each(|t| t.add_element()); - ix + ix.into() } } }) @@ -149,8 +139,8 @@ impl AttributeScope { /// Disposes instance for reuse in the future. Please note that the disposed data still /// exists in the buffer and will be used when rendering. It is yours responsibility to hide /// id, fo example by degenerating vertices. - pub fn dispose(&mut self, id:InstanceId) { - group!(self.logger, "Disposing instance {}.", id, { + pub fn dispose(&mut self, id:AttributeInstanceIndex) { + group!(self.logger, "Disposing instance {id}.", { self.free_ids.push(id); }) } @@ -179,3 +169,44 @@ impl AttributeScope { self.size } } + + + +// ================= +// === Attribute === +// ================= + +/// View for a particular buffer. Allows reading and writing buffer data +/// via the internal mutability pattern. It is implemented as a view on +/// a selected `Buffer` element under the hood. +#[derive(Clone,Debug,Derivative)] +pub struct Attribute { + index : AttributeInstanceIndex, + buffer : Buffer +} + +impl Attribute { + /// Creates a new variable as an indexed view over provided buffer. + pub fn new(index:AttributeInstanceIndex, buffer:Buffer) -> Self { + Self {index,buffer} + } +} + +impl Attribute { + /// Gets a copy of the data this attribute points to. + pub fn get(&self) -> T { + self.buffer.get(self.index.into()) + } + + /// Sets the data this attribute points to. + pub fn set(&self, value:T) { + self.buffer.set(self.index.into(),value); + } + + /// Modifies the data this attribute points to. + pub fn modify(&self, f:F) { + let mut value = self.get(); + f(&mut value); + self.set(value); + } +} diff --git a/gui/lib/core/src/system/gpu/data/attribute/class.rs b/gui/lib/core/src/system/gpu/data/attribute/class.rs deleted file mode 100644 index a8dfef2ec9..0000000000 --- a/gui/lib/core/src/system/gpu/data/attribute/class.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![allow(missing_docs)] - -use crate::prelude::*; - -use crate::data::function::callback::Callback0; -use crate::system::gpu::buffer::Buffer; - - -// ================= -// === Attribute === -// ================= - -/// View for a particular buffer. Allows reading and writing buffer data -/// via the internal mutability pattern. It is implemented as a view on -/// a selected `Buffer` element under the hood. -#[derive(Clone,Derivative)] -#[derivative(Debug(bound="T:Debug"))] -pub struct Attribute { - index : usize, - buffer : Buffer -} - -impl Attribute { - /// Creates a new variable as an indexed view over provided buffer. - pub fn new(index:usize, buffer: Buffer) -> Self { - Self {index, buffer} - } -} - -impl Attribute { - /// Gets immutable reference to the underlying data. - pub fn get(&self) -> T { - *self.buffer.rc.borrow().index(self.index) - } - - /// Sets the variable to a new value. - pub fn set(&self, value:T) { - *self.buffer.rc.borrow_mut().index_mut(self.index) = value; - } - - /// Modifies the underlying data by using the provided function. - pub fn modify(&self, f:F) { - let mut value = self.get(); - f(&mut value); - self.set(value); - } -} diff --git a/gui/lib/core/src/system/gpu/data/buffer.rs b/gui/lib/core/src/system/gpu/data/buffer.rs new file mode 100644 index 0000000000..0513306df9 --- /dev/null +++ b/gui/lib/core/src/system/gpu/data/buffer.rs @@ -0,0 +1,380 @@ +//! This module implements utilities for managing WebGL buffers. + +pub mod usage; +pub mod item; + +use crate::prelude::*; + +use crate::closure; +use crate::control::callback::Callback; +use crate::control::callback::CallbackFn; +use crate::data::dirty; +use crate::data::seq::observable::Observable; +use crate::debug::stats::Stats; +use crate::system::gpu::shader::Context; +use crate::system::gpu::data::buffer::usage::BufferUsage; +use crate::system::gpu::data::attribute::Attribute; +use crate::system::gpu::data::buffer::item::JsBufferView; + + +use crate::system::gpu::data::prim::*; +use crate::data::dirty::traits::*; +use crate::system::gpu::data::gl_enum::traits::*; + + +use nalgebra::Matrix4; +use nalgebra::Vector2; +use nalgebra::Vector3; +use nalgebra::Vector4; +use shapely::shared; +use std::iter::Extend; +use std::ops::RangeInclusive; +use web_sys::WebGlBuffer; + + +pub use crate::system::gpu::data::BufferItem; + + + + +// ============= +// === Types === +// ============= + +/// A vector which fires events whenever it is modified or resized. +pub type ObservableVec = Observable,OnMut,OnResize>; + +/// Dirty flag keeping track of the range of modified elements. +pub type MutDirty = dirty::SharedRange; + +/// Dirty flag keeping track of whether the buffer was resized. +pub type ResizeDirty = dirty::SharedBool; + +closure! { +fn on_resize_fn(dirty:ResizeDirty) -> OnResize { + || dirty.set() +}} + +closure! { +fn on_mut_fn(dirty:MutDirty) -> OnMut { + |ix: usize| dirty.set(ix) +}} + + + +// ============== +// === Buffer === +// ============== + +shared! {Buffer +/// CPU-counterpart of WebGL buffers. The buffer data is synchronised with GPU on demand, usually +/// in the update stage before drawing the frame. +#[derive(Debug)] +pub struct BufferData { + buffer : ObservableVec, + mut_dirty : MutDirty, + resize_dirty : ResizeDirty, + gl_buffer : WebGlBuffer, + usage : BufferUsage, + context : Context, + stats : Stats, + gpu_mem_usage : u32, + logger : Logger, +} + +impl { + /// Constructor. + pub fn new + (logger:Logger, stats:&Stats, context:&Context, on_mut:OnMut, on_resize:OnResize) -> Self { + info!(logger,"Creating new {T::type_display()} buffer.",{ + stats.inc_buffer_count(); + let mut_dirty = MutDirty::new(logger.sub("mut_dirty"),Callback(on_mut)); + let resize_dirty = ResizeDirty::new(logger.sub("resize_dirty"),Callback(on_resize)); + let on_resize_fn = on_resize_fn(resize_dirty.clone_ref()); + let on_mut_fn = on_mut_fn(mut_dirty.clone_ref()); + let buffer = ObservableVec::new(on_mut_fn,on_resize_fn); + let gl_buffer = create_gl_buffer(&context); + let usage = default(); + let context = context.clone(); + let stats = stats.clone_ref(); + let gpu_mem_usage = default(); + Self {buffer,mut_dirty,resize_dirty,logger,gl_buffer,usage,context,stats,gpu_mem_usage} + }) + } + + /// Returns the number of elements in the buffer. + pub fn len(&self) -> usize { + self.buffer.len() + } + + /// Checks if the buffer is empty. + pub fn is_empty(&self) -> bool { + self.buffer.is_empty() + } + + /// Reads the usage pattern of the buffer. + pub fn usage(&self) -> BufferUsage { + self.usage + } + + /// Sets the usage pattern of the buffer. + pub fn set_usage(&mut self, usage:BufferUsage) { + self.usage = usage; + self.resize_dirty.set(); + } + + /// Gets a copy of the data by its index. + pub fn get(&self, index:usize) -> T { + *self.buffer.index(index) + } + + /// Sets data value at the given index. + pub fn set(&mut self, index:usize, value:T) { + *self.buffer.index_mut(index) = value; + } + + /// Adds a single new element initialized to default value. + pub fn add_element(&mut self) { + self.add_elements(1); + } + + /// Adds multiple new elements initialized to default values. + pub fn add_elements(&mut self, elem_count:usize) { + self.extend(iter::repeat(T::gpu_default()).take(elem_count)); + } + + /// Check dirty flags and update the state accordingly. + pub fn update(&mut self) { + info!(self.logger, "Updating.", { + self.context.bind_buffer(Context::ARRAY_BUFFER,Some(&self.gl_buffer)); + if self.resize_dirty.check() { + self.upload_data(&None); + } else if self.mut_dirty.check_all() { + self.upload_data(&self.mut_dirty.take().range); + } else { + internal_warning!(self.logger,"Update requested but it was not needed.") + } + self.mut_dirty.unset_all(); + self.resize_dirty.unset(); + }) + } + + /// Binds the underlying WebGLBuffer to a given target. + /// https://developer.mozilla.org/docs/Web/API/WebGLRenderingContext/bindBuffer + pub fn bind(&self, target:u32) { + self.context.bind_buffer(target,Some(&self.gl_buffer)); + } + + /// Binds the buffer currently bound to gl.ARRAY_BUFFER to a generic vertex attribute of the + /// current vertex buffer object and specifies its layout. Please note that this function is + /// more complex that a raw call to `WebGLRenderingContext.vertexAttribPointer`, as it correctly + /// handles complex data types like `mat4`. See the following links to learn more: + /// https://developer.mozilla.org/docs/Web/API/WebGLRenderingContext/vertexAttribPointer + /// https://stackoverflow.com/questions/38853096/webgl-how-to-bind-values-to-a-mat4-attribute + pub fn vertex_attrib_pointer(&self, loc:u32, instanced:bool) { + let item_byte_size = T::item_gpu_byte_size() as i32; + let item_type = T::item_gl_enum().into(); + let rows = T::rows() as i32; + let cols = T::cols() as i32; + let col_byte_size = item_byte_size * rows; + let stride = col_byte_size * cols; + let normalize = false; + for col in 0..cols { + let lloc = loc + col as u32; + let off = col * col_byte_size; + self.context.enable_vertex_attrib_array(lloc); + self.context.vertex_attrib_pointer_with_i32(lloc,rows,item_type,normalize,stride,off); + if instanced { + let instance_count = 1; + self.context.vertex_attrib_divisor(lloc,instance_count); + } + } + } +}} + + +// === Private API === + +impl BufferData { + /// View the data as slice of primitive elements. + pub fn as_prim_slice(&self) -> &[item::Item] { + ::slice_to_items(&self.buffer.data) + } + + /// View the data as slice of elements. + pub fn as_slice(&self) -> &[T] { + &self.buffer.data + } +} + + +// === Data Upload === + +// Note [Safety] +// ============= +// Usage of `js_buffer_view` is somewhat dangerous. It is creating a raw view into the module's +// `WebAssembly.Memory` buffer, but if we allocate more pages for ourself (aka do a memory +// allocation in Rust) it'll cause the buffer to change, causing the resulting js array to be +// invalid. + +impl BufferData { + /// Uploads the provided data range to the GPU buffer. In case the local buffer was resized, + /// it will be re-created on the GPU. + fn upload_data(&mut self, opt_range:&Option>) { + info!(self.logger,"Uploading buffer data.",{ + self.stats.inc_data_upload_count(); + match opt_range { + None => self.replace_gpu_buffer(), + Some(range) => self.update_gpu_sub_buffer(range) + } + }); + } + + /// Replaces the whole GPU buffer by the local data. + fn replace_gpu_buffer(&mut self) { + let data = self.as_slice(); + let gl_enum = self.usage.into_gl_enum().into(); + unsafe { // Note [Safety] + let js_array = data.js_buffer_view(); + self.context.buffer_data_with_array_buffer_view + (Context::ARRAY_BUFFER,&js_array,gl_enum); + } + crate::if_compiled_with_stats! { + let item_byte_size = T::item_gpu_byte_size() as u32; + let item_count = T::item_count() as u32; + let new_gpu_mem_usage = self.len() as u32 * item_count * item_byte_size; + self.stats.mod_gpu_memory_usage(|s| s - self.gpu_mem_usage); + self.stats.mod_gpu_memory_usage(|s| s + new_gpu_mem_usage); + self.stats.mod_data_upload_size(|s| s + new_gpu_mem_usage); + self.gpu_mem_usage = new_gpu_mem_usage; + } + } + + /// Updates the GPU sub-buffer data by the provided index range. + fn update_gpu_sub_buffer(&mut self, range:&RangeInclusive) { + let data = self.as_slice(); + let item_byte_size = T::item_gpu_byte_size() as u32; + let item_count = T::item_count() as u32; + let start = *range.start() as u32; + let end = *range.end() as u32; + let start_item = start * item_count; + let length = (end - start + 1) * item_count; + let dst_byte_offset = (item_byte_size * item_count * start) as i32; + unsafe { // Note [Safety] + let js_array = data.js_buffer_view(); + self.context.buffer_sub_data_with_i32_and_array_buffer_view_and_src_offset_and_length + (Context::ARRAY_BUFFER,dst_byte_offset,&js_array,start_item,length) + } + self.stats.mod_data_upload_size(|s| s + length * item_byte_size); + } +} + + +// === Smart Accessors === + +impl Buffer { + /// Get the attribute pointing to a given buffer index. + pub fn at(&self, index:AttributeInstanceIndex) -> Attribute { + Attribute::new(index,self.clone_ref()) + } +} + + +// === Instances === + +impl Deref for BufferData { + type Target = ObservableVec; + fn deref(&self) -> &Self::Target { + &self.buffer + } +} + +impl DerefMut for BufferData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buffer + } +} + +impl Drop for BufferData { + fn drop(&mut self) { + self.context.delete_buffer(Some(&self.gl_buffer)); + self.stats.mod_gpu_memory_usage(|s| s - self.gpu_mem_usage); + self.stats.dec_buffer_count(); + } +} + + +// === Utils === + +fn create_gl_buffer(context:&Context) -> WebGlBuffer { + let buffer = context.create_buffer(); + buffer.ok_or("Failed to create WebGL buffer.").unwrap() +} + + + +// ================= +// === AnyBuffer === +// ================= + +use enum_dispatch::*; +use crate::system::gpu::data::AttributeInstanceIndex; + +// === Macros === + +/// Variant mismatch error type. +#[derive(Debug)] +pub struct BadVariant; + +macro_rules! define_any_buffer { +([] [$([$base:ident $param:ident])*]) => { paste::item! { + + /// An enum with a variant per possible buffer type (i32, f32, Vector, + /// and many, many more). It provides a faster alternative to dyn trait one: + /// `Buffer`. + #[enum_dispatch(IsBuffer)] + #[derive(Debug)] + #[allow(missing_docs)] + pub enum AnyBuffer { + $([](Buffer<$base<$param>>)),* + } + + $( + impl<'t> TryFrom<&'t AnyBuffer> for &'t Buffer<$base<$param>> { + type Error = BadVariant; + fn try_from(t:&'t AnyBuffer) -> Result <&'t Buffer<$base<$param>>,Self::Error> { + match t { + AnyBuffer::[](a) => Ok(a), + _ => Err(BadVariant) + } + } + } + + impl<'t> TryFrom<&'t mut AnyBuffer> for &'t mut Buffer<$base<$param>> { + type Error = BadVariant; + fn try_from(t:&'t mut AnyBuffer) -> Result <&'t mut Buffer<$base<$param>>,Self::Error> { + match t { + AnyBuffer::[](a) => Ok(a), + _ => Err(BadVariant) + } + } + } + )* +}}} + + +// === Definition === + +crate::with_all_prim_types!([[define_any_buffer] []]); + +/// Collection of all methods common to every buffer variant. +#[enum_dispatch] +#[allow(missing_docs)] +pub trait IsBuffer { + fn add_element(&self); + fn len(&self) -> usize; + fn is_empty(&self) -> bool; + fn update(&self); + fn bind(&self, target:u32); + fn vertex_attrib_pointer(&self, index:u32, instanced:bool); +} diff --git a/gui/lib/core/src/system/gpu/data/buffer/item.rs b/gui/lib/core/src/system/gpu/data/buffer/item.rs new file mode 100644 index 0000000000..a262a3d2da --- /dev/null +++ b/gui/lib/core/src/system/gpu/data/buffer/item.rs @@ -0,0 +1,266 @@ +//! This module defines abstraction for items in buffers stored on GPU. + +use crate::prelude::*; + +use crate::system::gpu::shader::glsl::Glsl; +use crate::system::gpu::shader::glsl; +use crate::system::gpu::data::gl_enum::GlEnum; +use crate::system::gpu::data::sized::GpuKnownSize; +use crate::system::gpu::data::GpuDefault; + +use crate::system::gpu::data::gl_enum::traits::*; + +use nalgebra::*; + + + +// ============= +// === Types === +// ============= + +/// Common Matrix bounds used as super-bounds for many helpers in this module. +pub trait MatrixCtx = where + T:Scalar, R:DimName, C:DimName, + DefaultAllocator: nalgebra::allocator::Allocator, + >::Buffer:Copy; + + + +// ================== +// === BufferItem === +// ================== + +// === Definition === + +pub trait JsBufferViewArr = Sized where [Self]:JsBufferView; + +/// Super bounds of the `BufferItem::Item` type; +pub trait ItemBounds = BufferItem + PhantomInto; + +/// Super bounds of the `BufferItem` trait. +pub trait BufferItemBounds = + Copy + GpuDefault + JsBufferViewArr + PhantomInto + Into + GpuKnownSize; + +/// Class for buffer items, like `f32` or `Vector`. +/// +/// WebGL buffers contain primitive values only, so for example, two `Vector3` are represented +/// as six `f32` values. This trait defines fast conversions (views) for the underlying flat data +/// storage. +pub trait BufferItem: BufferItemBounds { + + // === Types === + + /// The primitive type which this type is build of. In case of the most primitive types, like + /// `f32` this type may be set to itself. + type Item: ItemBounds; + + /// The number of rows of the type encoded as 2d matrix. + type Rows: DimName; + + /// The number of columns of the type encoded as 2d matrix. + type Cols: DimName; + + + // === Size === + + /// Returns the number of rows of the type encoded as 2d matrix. + fn rows() -> usize { + ::dim() + } + + /// Returns the number of columns of the type encoded as 2d matrix. + fn cols() -> usize { + ::dim() + } + + /// Count of primitives of the item. For example, `Vector3` contains + /// three primitives (`f32` values). + fn item_count() -> usize { + Self::rows() * Self::cols() + } + + + // === Conversions === + + /// Conversion from a slice of items to a buffer slice. + fn slice_from_items(buffer: &[Self::Item]) -> &[Self]; + + /// Conversion from a mutable slice of items to a mutable buffer slice. + fn slice_from_items_mut(buffer: &mut [Self::Item]) -> &mut [Self]; + + /// Converts from a buffer slice to a slice of items. + fn slice_to_items(buffer: &[Self]) -> &[Self::Item]; + + /// Converts from a mutable buffer slice to a mutable slice of items. + fn slice_to_items_mut(buffer: &mut [Self]) -> &mut [Self::Item]; + + + // === Temporary Helpers === + + // TODO: Remove when it gets resolved: https://github.com/rust-lang/rust/issues/68210 + /// Returns the WebGL enum code representing the item type, like Context::FLOAT. + fn item_gl_enum() -> GlEnum { + Self::Item::gl_enum() + } + + // TODO: Remove when it gets resolved: https://github.com/rust-lang/rust/issues/68210 + /// Returns the size in bytes in GPU memory of the primitive type of this type. + fn item_gpu_byte_size() -> usize { + Self::Item::gpu_byte_size() + } +} + + +// === Type Families === + +/// Item accessor. +pub type Item = ::Item; + +/// Rows accessor. +pub type Rows = ::Rows; + +/// Cols accessor. +pub type Cols = ::Cols; + + +// === Instances === + +impl BufferItem for bool { + type Item = Self; + type Rows = U1; + type Cols = U1; + + fn slice_from_items (buffer: & [Self::Item]) -> & [Self] { buffer } + fn slice_from_items_mut (buffer: &mut [Self::Item]) -> &mut [Self] { buffer } + fn slice_to_items (buffer: & [Self]) -> & [Self::Item] { buffer } + fn slice_to_items_mut (buffer: &mut [Self]) -> &mut [Self::Item] { buffer } +} + +impl BufferItem for i32 { + type Item = Self; + type Rows = U1; + type Cols = U1; + + fn slice_from_items (buffer: & [Self::Item]) -> & [Self] { buffer } + fn slice_from_items_mut (buffer: &mut [Self::Item]) -> &mut [Self] { buffer } + fn slice_to_items (buffer: & [Self]) -> & [Self::Item] { buffer } + fn slice_to_items_mut (buffer: &mut [Self]) -> &mut [Self::Item] { buffer } +} + +impl BufferItem for f32 { + type Item = Self; + type Rows = U1; + type Cols = U1; + + fn slice_from_items (buffer: & [Self::Item]) -> & [Self] { buffer } + fn slice_from_items_mut (buffer: &mut [Self::Item]) -> &mut [Self] { buffer } + fn slice_to_items (buffer: & [Self]) -> & [Self::Item] { buffer } + fn slice_to_items_mut (buffer: &mut [Self]) -> &mut [Self::Item] { buffer } +} + + +impl,R,C> BufferItem for MatrixMN + where T:ItemBounds, Self:MatrixCtx, + Self:GpuDefault + PhantomInto + GpuKnownSize { + type Item = T; + type Rows = R; + type Cols = C; + + fn slice_from_items(buffer: &[Self::Item]) -> &[Self] { + // This code casts slice to matrix. This is safe because `MatrixMN` + // uses `nalgebra::Owned` allocator, which resolves to array defined as + // `#[repr(C)]` under the hood. + let len = buffer.len() / Self::item_count(); + unsafe { std::slice::from_raw_parts(buffer.as_ptr().cast(), len) } + } + + fn slice_from_items_mut(buffer: &mut [Self::Item]) -> &mut [Self] { + // This code casts slice to matrix. This is safe because `MatrixMN` + // uses `nalgebra::Owned` allocator, which resolves to array defined as + // `#[repr(C)]` under the hood. + let len = buffer.len() / Self::item_count(); + unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr().cast(), len) } + } + + fn slice_to_items(buffer: &[Self]) -> &[Self::Item] { + // This code casts slice to matrix. This is safe because `MatrixMN` + // uses `nalgebra::Owned` allocator, which resolves to array defined as + // `#[repr(C)]` under the hood. + let len = buffer.len() * Self::item_count(); + unsafe { std::slice::from_raw_parts(buffer.as_ptr().cast(), len) } + } + + fn slice_to_items_mut(buffer: &mut [Self]) -> &mut [Self::Item] { + // This code casts slice to matrix. This is safe because `MatrixMN` + // uses `nalgebra::Owned` allocator, which resolves to array defined as + // `#[repr(C)]` under the hood. + let len = buffer.len() * Self::item_count(); + unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr().cast(), len) } + } +} + + + +// ==================== +// === JsBufferView === +// ==================== + +/// Extension method for viewing into wasm's linear memory. +pub trait JsBufferView { + /// Creates a JS typed array which is a view into wasm's linear memory at the slice specified. + /// + /// This function returns a new typed array which is a view into wasm's memory. This view does + /// not copy the underlying data. + /// + /// # Safety + /// + /// Views into WebAssembly memory are only valid so long as the backing buffer isn't resized in + /// JS. Once this function is called any future calls to `Box::new` (or malloc of any form) may + /// cause the returned value here to be invalidated. Use with caution! + /// + /// Additionally the returned object can be safely mutated but the input slice isn't guaranteed + /// to be mutable. + /// + /// Finally, the returned object is disconnected from the input slice's lifetime, so there's no + /// guarantee that the data is read at the right time. + unsafe fn js_buffer_view(&self) -> js_sys::Object; +} + + +// === Instances === + +impl JsBufferView for [bool] { + unsafe fn js_buffer_view(&self) -> js_sys::Object { + let i32arr = self.iter().cloned().map(|t| if t {1} else {0}).collect::>(); + js_sys::Int32Array::view(&i32arr).into() + } +} + +impl JsBufferView for [i32] { + unsafe fn js_buffer_view(&self) -> js_sys::Object { + js_sys::Int32Array::view(self).into() + } +} + +impl JsBufferView for [f32] { + unsafe fn js_buffer_view(&self) -> js_sys::Object { + js_sys::Float32Array::view(self).into() + } +} + +impl,R,C> JsBufferView for [MatrixMN] + where Self : MatrixCtx, + T : ItemBounds, + MatrixMN : BufferItem, + [Item>] : JsBufferView { + unsafe fn js_buffer_view(&self) -> js_sys::Object { + as BufferItem>::slice_to_items(self).js_buffer_view() + } +} + +impl,R,C> JsBufferView for MatrixMN + where Self:MatrixCtx, T:ItemBounds { + unsafe fn js_buffer_view(&self) -> js_sys::Object { + self.as_slice().js_buffer_view() + } +} diff --git a/gui/lib/core/src/system/gpu/data/buffer/usage.rs b/gui/lib/core/src/system/gpu/data/buffer/usage.rs new file mode 100644 index 0000000000..dc72eabc6a --- /dev/null +++ b/gui/lib/core/src/system/gpu/data/buffer/usage.rs @@ -0,0 +1,60 @@ +//! `BufferUsage` specifies the intended usage pattern of the data store for optimization purposes. + +use crate::prelude::*; + +use crate::system::gpu::shader::Context; +use crate::system::gpu::data::gl_enum::GlEnum; + + + +// =================== +// === BufferUsage === +// =================== + +crate::define_singleton_enum_gl! { + /// Specifies the intended usage pattern of the data store for optimization purposes. + BufferUsage { + + /// The contents are intended to be specified once by the application, and used many times + /// as the source for WebGL drawing and image specification commands. + Static = Context::STATIC_DRAW, + + /// Default. The contents are intended to be respecified repeatedly by the application, and + /// used many times as the source for WebGL drawing and image specification commands. + Dynamic = Context::DYNAMIC_DRAW, + + /// The contents are intended to be specified once by the application, and used at most a + /// few times as the source for WebGL drawing and image specification commands. + Stream = Context::STREAM_DRAW, + + /// The contents are intended to be specified once by reading data from WebGL, and queried + /// many times by the application. + StaticRead = Context::STATIC_READ, + + /// The contents are intended to be respecified repeatedly by reading data from WebGL, and + /// queried many times by the application. + DynamicRead = Context::DYNAMIC_READ, + + /// The contents are intended to be specified once by reading data from WebGL, and queried + /// at most a few times by the application + StreamRead = Context::STREAM_READ, + + /// The contents are intended to be specified once by reading data from WebGL, and used many + /// times as the source for WebGL drawing and image specification commands. + StaticCopy = Context::STATIC_COPY, + + /// The contents are intended to be respecified repeatedly by reading data from WebGL, and + /// used many times as the source for WebGL drawing and image specification commands. + DynamicCopy = Context::DYNAMIC_COPY, + + /// The contents are intended to be specified once by reading data from WebGL, and used at + /// most a few times as the source for WebGL drawing and image specification commands. + StreamCopy = Context::STREAM_COPY, + } +} + +impl Default for BufferUsage { + fn default() -> Self { + BufferUsage::Dynamic + } +} diff --git a/gui/lib/core/src/system/gpu/data/class.rs b/gui/lib/core/src/system/gpu/data/class.rs deleted file mode 100644 index bad6cf661b..0000000000 --- a/gui/lib/core/src/system/gpu/data/class.rs +++ /dev/null @@ -1,345 +0,0 @@ -#![allow(missing_docs)] - -use crate::display::render::webgl::Context; -use crate::display::render::webgl::glsl; - -use nalgebra::*; -use web_sys::WebGlUniformLocation; -use code_builder::HasCodeRepr; - - -// ============= -// === Types === -// ============= - -pub trait MatrixCtx = where - T:Scalar, R:DimName, C:DimName, - DefaultAllocator: nalgebra::allocator::Allocator, - >::Buffer:Copy; - - - -// ==================== -// === JSBufferView === -// ==================== - -pub trait JSBufferView { - /// Creates a JS typed array which is a view into wasm's linear memory at the slice specified. - /// - /// This function returns a new typed array which is a view into wasm's memory. This view does - /// not copy the underlying data. - /// - /// # Safety - /// - /// Views into WebAssembly memory are only valid so long as the backing buffer isn't resized in - /// JS. Once this function is called any future calls to `Box::new` (or malloc of any form) may - /// cause the returned value here to be invalidated. Use with caution! - /// - /// Additionally the returned object can be safely mutated but the input slice isn't guaranteed - /// to be mutable. - /// - /// Finally, the returned object is disconnected from the input slice's lifetime, so there's no - /// guarantee that the data is read at the right time. - unsafe fn js_buffer_view(&self) -> js_sys::Object; -} - - - -// ============= -// === Empty === -// ============= - -/// Trait for types which have empty value. -pub trait Empty { - fn empty() -> Self; - fn is_empty(&self) -> bool where Self:Sized+PartialEq { - *self == Self::empty() - } -} - -impl Empty for i32 { fn empty() -> Self { 0 } } -impl Empty for f32 { fn empty() -> Self { 0.0 } } -impl Empty for Vector2 { fn empty() -> Self { Self::new(0.0,0.0) } } -impl Empty for Vector3 { fn empty() -> Self { Self::new(0.0,0.0,0.0) } } -impl Empty for Vector4 { fn empty() -> Self { Self::new(0.0,0.0,0.0,1.0) } } -impl Empty for Matrix4 { fn empty() -> Self { Self::identity() } } - - - -// ================= -// === IsUniform === -// ================= - -pub type UniformLocation = WebGlUniformLocation; - -pub trait ContextUniformOps { - fn set_uniform(&self, location:&UniformLocation, value:&T); -} - -impl ContextUniformOps for Context { - fn set_uniform(&self, location:&UniformLocation, value:&i32) { - self.uniform1i(Some(location),*value); - } -} - -impl ContextUniformOps for Context { - fn set_uniform(&self, location:&UniformLocation, value:&f32) { - self.uniform1f(Some(location),*value); - } -} - -impl ContextUniformOps> for Context { - fn set_uniform(&self, location:&UniformLocation, value:&Vector2) { - self.uniform_matrix2fv_with_f32_array(Some(location),false,value.data.as_slice()); - } -} - -impl ContextUniformOps> for Context { - fn set_uniform(&self, location:&UniformLocation, value:&Vector3) { - self.uniform_matrix3fv_with_f32_array(Some(location),false,value.data.as_slice()); - } -} - -impl ContextUniformOps> for Context { - fn set_uniform(&self, location:&UniformLocation, value:&Vector4) { - self.uniform_matrix4fv_with_f32_array(Some(location),false,value.data.as_slice()); - } -} - -impl ContextUniformOps> for Context { - fn set_uniform(&self, location:&UniformLocation, value:&Matrix4) { - self.uniform_matrix4fv_with_f32_array(Some(location),false,value.data.as_slice()); - } -} - - - -// =============== -// === GpuData === -// =============== - -// === Definition === - -pub trait JSBufferViewArr = Sized where [Self]:JSBufferView; - -/// Class for buffer items, like `f32` or `Vector`. It defines utils -/// for mapping the item to WebGL buffer and vice versa. -pub trait GpuData : Copy + Empty + JSBufferViewArr { - - // === Types === - - /// The primitive type which this type is build of. In case of the most primitive types, like - /// `f32` this type may be set to itself. - type Item: GpuData; - - /// The number of rows of the type encoded as 2d matrix. - type Rows: DimName; - - /// The number of columns of the type encoded as 2d matrix. - type Cols: DimName; - - - // === Size === - - /// Returns the number of rows of the type encoded as 2d matrix. - fn rows() -> usize { - ::dim() - } - - /// Returns the number of columns of the type encoded as 2d matrix. - fn cols() -> usize { - ::dim() - } - - /// Count of primitives of the item. For example, `Vector3` contains - /// three primitives (`f32` values). - fn item_count() -> usize { - Self::rows() * Self::cols() - } - - /// Returns the size in bytes in GPU memory of the type. - fn gpu_byte_size() -> usize { - Self::gpu_item_byte_size() * Self::item_count() - } - - /// Returns the size in bytes in GPU memory of the primitive type of this type. - fn gpu_item_byte_size() -> usize { - Self::Item::gpu_byte_size() - } - - - // === Conversions === - - /// Conversion from slice of a buffer to the item. Buffers contain primitive - /// values only, so two `Vector3` are represented there as six `f32` - /// values. This allows us to view the buffers using desired types. - fn from_buffer(buffer: &[Self::Item]) -> &[Self]; - - /// Mutable conversion from slice of a buffer to the item. See the docs for - /// `from_buffer` to learn more. - fn from_buffer_mut(buffer: &mut [Self::Item]) -> &mut [Self]; - - // TODO: simplify when this gets resolved: https://github.com/rustsim/nalgebra/issues/687 - fn convert_prim_buffer(buffer: &[Self]) -> &[Self::Item]; - fn convert_prim_buffer_mut(buffer: &mut [Self]) -> &mut [Self::Item]; - - - // === GLSL === - - /// Returns the WebGL enum code representing the item type, like Context::FLOAT. - fn glsl_item_type_code() -> u32 { - Self::Item::glsl_item_type_code() - } - - /// Returns the GLSL type as GLSL AST item. - fn glsl_type() -> glsl::PrimType; - - /// Returns the GLSL type name, like `"float"` for `f32`. - fn glsl_type_name() -> String { - Self::glsl_type().to_code() - } - - /// Converts the data to GLSL value. - fn to_glsl(&self) -> String; -} - - -// === Type Families === - -pub type Item = ::Item; -pub type Rows = ::Rows; -pub type Cols = ::Cols; - - -// === Instances === - -impl GpuData for i32 { - type Item = Self; - type Rows = U1; - type Cols = U1; - - fn gpu_byte_size () -> usize { 4 } - fn from_buffer (buffer: & [Self::Item]) -> & [Self] { buffer } - fn from_buffer_mut (buffer: &mut [Self::Item]) -> &mut [Self] { buffer } - fn convert_prim_buffer (buffer: & [Self]) -> & [Self::Item] { buffer } - fn convert_prim_buffer_mut (buffer: &mut [Self]) -> &mut [Self::Item] { buffer } - fn glsl_item_type_code () -> u32 { Context::INT } - fn glsl_type () -> glsl::PrimType { glsl::PrimType::Int } - fn to_glsl (&self) -> String { self.to_string() } -} - -impl JSBufferView for [i32] { - unsafe fn js_buffer_view(&self) -> js_sys::Object { - js_sys::Int32Array::view(self).into() - } -} - -impl GpuData for f32 { - type Item = Self; - type Rows = U1; - type Cols = U1; - - fn gpu_byte_size () -> usize { 4 } - fn from_buffer (buffer: & [Self::Item]) -> & [Self] { buffer } - fn from_buffer_mut (buffer: &mut [Self::Item]) -> &mut [Self] { buffer } - fn convert_prim_buffer (buffer: & [Self]) -> & [Self::Item] { buffer } - fn convert_prim_buffer_mut (buffer: &mut [Self]) -> &mut [Self::Item] { buffer } - fn glsl_item_type_code () -> u32 { Context::FLOAT } - fn glsl_type () -> glsl::PrimType { glsl::PrimType::Float } - fn to_glsl (&self) -> String { - let is_int = self.fract() == 0.0; - if is_int { format!("{}.0" , self) } - else { format!("{}" , self) } - } -} - -impl JSBufferView for [f32] { - unsafe fn js_buffer_view(&self) -> js_sys::Object { - js_sys::Float32Array::view(self).into() - } -} - -impl,R,C> GpuData for MatrixMN - where T:Default, Self:MatrixCtx, Self:Empty { - type Item = T; - type Rows = R; - type Cols = C; - - fn from_buffer(buffer: &[Self::Item]) -> &[Self] { - // This code casts slice to matrix. This is safe because `MatrixMN` - // uses `nalgebra::Owned` allocator, which resolves to array defined as - // `#[repr(C)]` under the hood. - unsafe { - let len = buffer.len() / Self::item_count(); - std::slice::from_raw_parts(buffer.as_ptr().cast(), len) - } - } - - fn from_buffer_mut(buffer: &mut [Self::Item]) -> &mut [Self] { - // This code casts slice to matrix. This is safe because `MatrixMN` - // uses `nalgebra::Owned` allocator, which resolves to array defined as - // `#[repr(C)]` under the hood. - unsafe { - let len = buffer.len() / Self::item_count(); - std::slice::from_raw_parts_mut(buffer.as_mut_ptr().cast(), len) - } - } - - fn convert_prim_buffer(buffer: &[Self]) -> &[Self::Item] { - // This code casts slice to matrix. This is safe because `MatrixMN` - // uses `nalgebra::Owned` allocator, which resolves to array defined as - // `#[repr(C)]` under the hood. - let len = buffer.len() * Self::item_count(); - unsafe { std::slice::from_raw_parts(buffer.as_ptr().cast(), len) } - } - - fn convert_prim_buffer_mut(buffer: &mut [Self]) -> &mut [Self::Item] { - // This code casts slice to matrix. This is safe because `MatrixMN` - // uses `nalgebra::Owned` allocator, which resolves to array defined as - // `#[repr(C)]` under the hood. - unsafe { - let len = buffer.len() * Self::item_count(); - std::slice::from_raw_parts_mut(buffer.as_mut_ptr().cast(), len) - } - } - - fn glsl_type() -> glsl::PrimType { - let cols = ::cols(); - let rows = ::rows(); - match (cols,rows) { - (1,2) => glsl::PrimType::Vec2, - (1,3) => glsl::PrimType::Vec3, - (1,4) => glsl::PrimType::Vec4, - (2,2) => glsl::PrimType::Mat2, - (2,3) => glsl::PrimType::Mat2x3, - (2,4) => glsl::PrimType::Mat2x4, - (3,2) => glsl::PrimType::Mat3x2, - (3,3) => glsl::PrimType::Mat3, - (3,4) => glsl::PrimType::Mat3x4, - (4,4) => glsl::PrimType::Mat4, - _ => panic!("Unsupported GLSL matrix shape {}x{}",cols,rows) - } - } - - fn to_glsl(&self) -> String { - let vals:Vec = self.as_slice().iter().cloned().map(|t|format!("{:?}",t)).collect(); - format!("{}({})",Self::glsl_type_name(),vals.join(",")) - } -} - -impl,R,C> JSBufferView for [MatrixMN] -where Self : MatrixCtx, - T : Default, - MatrixMN : GpuData, - [Item>] : JSBufferView { - unsafe fn js_buffer_view(&self) -> js_sys::Object { - as GpuData>::convert_prim_buffer(self).js_buffer_view() - } -} - -impl,R,C> JSBufferView for MatrixMN -where Self:MatrixCtx { - unsafe fn js_buffer_view(&self) -> js_sys::Object { - self.as_slice().js_buffer_view() - } -} diff --git a/gui/lib/core/src/system/gpu/data/default.rs b/gui/lib/core/src/system/gpu/data/default.rs new file mode 100644 index 0000000000..998bc0a9d6 --- /dev/null +++ b/gui/lib/core/src/system/gpu/data/default.rs @@ -0,0 +1,57 @@ +//! Defines abstraction for data types that have a default value when used as GPU values. + +use nalgebra::*; + + + +// ================== +// === GpuDefault === +// ================== + +/// Trait for types which have a default value when used as GPU values. +pub trait GpuDefault { + /// Default value for this type. + fn gpu_default() -> Self; + + /// Checks if the current value is the same as the default one. + fn is_gpu_default(&self) -> bool where Self:Sized+PartialEq { + *self == Self::gpu_default() + } +} + + +// === Instances === + +macro_rules! define_gpu_defaults { + ($($ty:ty = $val:expr),* $(,)?) => {$( + impl GpuDefault for $ty { fn gpu_default() -> Self { $val } } + )*} +} + +define_gpu_defaults! { + i32 = 0, + f32 = 0.0, + bool = false, + + Vector2 = Vector2::new(0.0,0.0), + Vector3 = Vector3::new(0.0,0.0,0.0), + Vector4 = Vector4::new(0.0,0.0,0.0,1.0), + + Vector2 = Vector2::new(0,0), + Vector3 = Vector3::new(0,0,0), + Vector4 = Vector4::new(0,0,0,1), + + Vector2 = Vector2::new(false,false), + Vector3 = Vector3::new(false,false,false), + Vector4 = Vector4::new(false,false,false,false), + + Matrix2 = Matrix2::identity(), + Matrix3 = Matrix3::identity(), + Matrix4 = Matrix4::identity(), + Matrix2x3 = Matrix2x3::identity(), + Matrix2x4 = Matrix2x4::identity(), + Matrix3x2 = Matrix3x2::identity(), + Matrix3x4 = Matrix3x4::identity(), + Matrix4x2 = Matrix4x2::identity(), + Matrix4x3 = Matrix4x3::identity(), +} diff --git a/gui/lib/core/src/system/gpu/data/gl_enum.rs b/gui/lib/core/src/system/gpu/data/gl_enum.rs new file mode 100644 index 0000000000..0ea5f6adc0 --- /dev/null +++ b/gui/lib/core/src/system/gpu/data/gl_enum.rs @@ -0,0 +1,163 @@ +//! This module defines a wrapper for WebGL enums and associated utils. + +use crate::prelude::*; +use crate::system::gpu::shader::Context; +use crate::system::gpu::data::prim::*; + + + +// ============== +// === GlEnum === +// ============== + +/// The newtype for WebGL enums. +#[derive(Copy,Clone,Debug,Default,Display)] +pub struct GlEnum(pub u32); + +impl From for u32 { + fn from(t:GlEnum) -> u32 { + t.0 + } +} + + + +// ================== +// === Extensions === +// ================== + +/// Extension methods. +pub mod traits { + use super::*; + + /// Methods for every object which implements `Into`. + pub trait IntoGlEnum { + /// Converts the current value to `GlEnum`. + fn into_gl_enum(&self) -> GlEnum; + } + + impl IntoGlEnum for T where for<'a> &'a T:Into { + fn into_gl_enum(&self) -> GlEnum { + self.into() + } + } + + /// Methods for every object which implements `PhantomInto`. + pub trait PhantomIntoGlEnum { + /// Converts the current value to `GlEnum`. + fn gl_enum() -> GlEnum; + } + + impl PhantomIntoGlEnum for T where T:PhantomInto { + fn gl_enum() -> GlEnum { + T::phantom_into::() + } + } +} + + + +// ============== +// === Macros === +// ============== + +/// Combination of `define_singletons` and `define_gl_enum_conversions`. +#[macro_export] +macro_rules! define_singletons_gl { + ( $( $(#$meta:tt)* $name:ident = $expr:expr ),* $(,)? ) => { + shapely::define_singletons!{ $( $(#$meta)* $name),* } + $crate::define_gl_enum_conversions!{ $( $(#$meta)* $name = $expr ),* } + } +} + + +/// Defines conversions `From<$type>` and `From>` for every provided type. +#[macro_export] +macro_rules! define_gl_enum_conversions { + ( $( $(#$meta:tt)* $type:ty = $expr:expr ),* $(,)? ) => { + $( + impl From<$type> for GlEnum { + fn from(_:$type) -> Self { + GlEnum($expr) + } + } + + impl From> for GlEnum { + fn from(_:PhantomData<$type>) -> Self { + GlEnum($expr) + } + } + )* + } +} + +/// Combination of `define_singletons_gl` and `define_singleton_enum_gl_from`. +#[macro_export] +macro_rules! define_singleton_enum_gl { + ( + $(#$meta:tt)* + $name:ident { + $( $(#$field_meta:tt)* $field:ident = $expr:expr),* $(,)? + } + ) => { + $crate :: define_singletons_gl! { $($(#$field_meta)* $field = $expr),* } + $crate :: define_singleton_enum_gl_from! { $(#$meta)* $name {$($(#$field_meta)* $field),*}} + } +} + + +/// Defines associated enum type for the provided variants, just like `define_singleton_enum_from`. +/// It also defines conversions `From<$singleton>` and `From>` the enum +/// type. +#[macro_export] +macro_rules! define_singleton_enum_gl_from { + ( + $(#$meta:tt)* + $name:ident { + $( $(#$field_meta:tt)* $field:ident),* $(,)? + } + ) => { + shapely::define_singleton_enum_from! { $(#$meta)* $name {$($(#$field_meta)* $field),*}} + + impl From<&$name> for GlEnum { + fn from(t:&$name) -> Self { + match t { + $($name::$field => $field.into()),* + } + } + } + + impl From<$name> for GlEnum { + fn from(t:$name) -> Self { + match t { + $($name::$field => $field.into()),* + } + } + } + } +} + + +// ================================ +// === Primitive Type Instances === +// ================================ + +define_gl_enum_conversions! { + bool = Context::BOOL, + u8 = Context::UNSIGNED_BYTE, + u16 = Context::UNSIGNED_SHORT, + u32 = Context::UNSIGNED_INT, + i8 = Context::BYTE, + i16 = Context::SHORT, + i32 = Context::INT, + f16 = Context::HALF_FLOAT, + f32 = Context::FLOAT, + f32_u24_u8_REV = Context::FLOAT_32_UNSIGNED_INT_24_8_REV, + u16_4_4_4_4 = Context::UNSIGNED_SHORT_4_4_4_4, + u16_5_5_5_1 = Context::UNSIGNED_SHORT_5_5_5_1, + u16_5_6_5 = Context::UNSIGNED_SHORT_5_6_5, + u32_f10_f11_f11_REV = Context::UNSIGNED_INT_10F_11F_11F_REV, + u32_24_8 = Context::UNSIGNED_INT_24_8, + u32_2_10_10_10_REV = Context::UNSIGNED_INT_2_10_10_10_REV, + u32_5_9_9_9_REV = Context::UNSIGNED_INT_5_9_9_9_REV, +} diff --git a/gui/lib/core/src/system/gpu/data/prim.rs b/gui/lib/core/src/system/gpu/data/prim.rs new file mode 100644 index 0000000000..c582213358 --- /dev/null +++ b/gui/lib/core/src/system/gpu/data/prim.rs @@ -0,0 +1,68 @@ +//! This module exports primitive data and associated utils. + + + +// ================= +// === Reexports === +// ================= + +pub use nalgebra::Vector2; +pub use nalgebra::Vector3; +pub use nalgebra::Vector4; +pub use nalgebra::Matrix4; +pub use nalgebra::Matrix2; +pub use nalgebra::Matrix3; +pub use nalgebra::Matrix2x3; +pub use nalgebra::Matrix2x4; +pub use nalgebra::Matrix3x2; +pub use nalgebra::Matrix3x4; +pub use nalgebra::Matrix4x2; +pub use nalgebra::Matrix4x3; + + + +// ============= +// === Types === +// ============= + +/// `Identity` resolves to `A`. +pub type Identity = T; + +macro_rules! gen_unsupported_types { + ( $($name:ident),* $(,)? ) => {$( + #[derive(Copy,Clone,Debug)] + pub struct $name {} + )*} +} + +/// Types which are used in WebGL but are not (yet) bound to Rust types. +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +pub mod unsupported_types { + gen_unsupported_types! + { f16, f32_u24_u8_REV, u16_4_4_4_4, u16_5_5_5_1, u16_5_6_5, u32_f10_f11_f11_REV, u32_24_8 + , u32_2_10_10_10_REV, u32_5_9_9_9_REV + } +} +pub use unsupported_types::*; + + + +// ============== +// === Macros === +// ============== + +/// Evaluates the argument macro with a list of pairs `[container item]` for all container and for +/// all primitive types supported on GPU. One of the container type is `Identity` which just +/// resolves to it's argument. +#[macro_export] +macro_rules! with_all_prim_types { + ([[$f:path] $args:tt]) => { + $f! { $args + [[Identity i32] [Identity f32] [Identity bool] [Vector2 f32] [Vector3 f32] [Vector4 f32] + [Vector2 i32] [Vector3 i32] [Vector4 i32] [Vector2 bool] [Vector3 bool] [Vector4 bool] + [Matrix2 f32] [Matrix3 f32] [Matrix4 f32] [Matrix2x3 f32] [Matrix2x4 f32] + [Matrix3x2 f32] [Matrix3x4 f32] [Matrix4x2 f32] [Matrix4x3 f32]] + } + } +} diff --git a/gui/lib/core/src/system/gpu/data/sized.rs b/gui/lib/core/src/system/gpu/data/sized.rs new file mode 100644 index 0000000000..59c4876848 --- /dev/null +++ b/gui/lib/core/src/system/gpu/data/sized.rs @@ -0,0 +1,46 @@ +//! This module implements type-level utils for checking the size of values for a given type. + +use nalgebra::*; +use crate::system::gpu::data::buffer::item::MatrixCtx; + + + +// ==================== +// === GpuKnownSize === +// ==================== + +/// Extension methods. +pub mod traits { + use super::*; + + /// Type-level computation of byte size for types stored on GPU. + pub trait GpuKnownSize { + /// Byte size as type-level uint. + type GpuByteSize: DimName; + + /// Byte size of the type. + fn gpu_byte_size() -> usize { + ::dim() + } + } +} +pub use traits::*; + +/// A nicer way to query type-level byte size for types stored on GPU. +pub type GpuByteSize = ::GpuByteSize; + + +// === Instances === + +impl GpuKnownSize for bool { type GpuByteSize = U4; } +impl GpuKnownSize for i32 { type GpuByteSize = U4; } +impl GpuKnownSize for f32 { type GpuByteSize = U4; } + +type Mul = >::Output; +impl GpuKnownSize for MatrixMN + where Self:MatrixCtx, + R:DimMul, + Mul:DimName+DimMul>, + Mul,GpuByteSize>:DimName { + type GpuByteSize = Mul,GpuByteSize>; +} diff --git a/gui/lib/core/src/system/gpu/data/texture.rs b/gui/lib/core/src/system/gpu/data/texture.rs new file mode 100644 index 0000000000..2bf4b20853 --- /dev/null +++ b/gui/lib/core/src/system/gpu/data/texture.rs @@ -0,0 +1,583 @@ +//! This module implements GPU-based texture support. Proper texture handling is a complex topic. +//! Follow the link to learn more about many assumptions this module was built upon: +//! https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D + +use crate::prelude::*; + +use crate::system::gpu::types::*; +use crate::system::web; +use nalgebra::*; +use wasm_bindgen::JsCast; +use wasm_bindgen::prelude::Closure; +use web_sys::HtmlImageElement; +use web_sys::Url; +use web_sys::WebGlTexture; + + + +// ============= +// === Value === +// ============= + +/// Defines relation between types and values, like between `True` and `true`. +pub trait Value { + + /// The value-level counterpart of this type-value. + type Type; + + /// The value of this type-value. + fn value() -> Self::Type; +} + + + +// ======================= +// === Type-level Bool === +// ======================= + +/// Type level `true` value. +pub struct True {} + +/// Type level `false` value. +pub struct False {} + +impl Value for True { + type Type = bool; + fn value() -> Self::Type { + true + } +} + +impl Value for False { + type Type = bool; + fn value() -> Self::Type { + false + } +} + + + +// ================ +// === GL Types === +// ================ + +crate::define_singletons_gl! { + Alpha = Context::ALPHA, + Depth24Stencil8 = Context::DEPTH24_STENCIL8, + Depth32fStencil8 = Context::DEPTH32F_STENCIL8, + DepthComponent = Context::DEPTH_COMPONENT, + DepthComponent16 = Context::DEPTH_COMPONENT16, + DepthComponent24 = Context::DEPTH_COMPONENT24, + DepthComponent32f = Context::DEPTH_COMPONENT32F, + DepthStencil = Context::DEPTH_STENCIL, + Luminance = Context::LUMINANCE, + LuminanceAlpha = Context::LUMINANCE_ALPHA, + R11fG11fB10f = Context::R11F_G11F_B10F, + R16f = Context::R16F, + R16i = Context::R16I, + R16ui = Context::R16UI, + R32f = Context::R32F, + R32i = Context::R32I, + R32ui = Context::R32UI, + R8 = Context::R8, + R8i = Context::R8I, + R8SNorm = Context::R8_SNORM, + R8ui = Context::R8UI, + Red = Context::RED, + RedInteger = Context::RED_INTEGER, + Rg = Context::RG, + Rg16f = Context::RG16F, + Rg16i = Context::RG16I, + Rg16ui = Context::RG16UI, + Rg32f = Context::RG32F, + Rg32i = Context::RG32I, + Rg32ui = Context::RG32UI, + Rg8 = Context::RG8, + Rg8i = Context::RG8I, + Rg8SNorm = Context::RG8_SNORM, + Rg8ui = Context::RG8UI, + Rgb = Context::RGB, + Rgb10A2 = Context::RGB10_A2, + Rgb10A2ui = Context::RGB10_A2UI, + Rgb16f = Context::RGB16F, + Rgb16i = Context::RGB16I, + Rgb16ui = Context::RGB16UI, + Rgb32f = Context::RGB32F, + Rgb32i = Context::RGB32I, + Rgb32ui = Context::RGB32UI, + Rgb565 = Context::RGB565, + Rgb5A1 = Context::RGB5_A1, + Rgb8 = Context::RGB8, + Rgb8i = Context::RGB8I, + Rgb8SNorm = Context::RGB8_SNORM, + Rgb8ui = Context::RGB8UI, + Rgb9E5 = Context::RGB9_E5, + Rgba = Context::RGBA, + Rgba16f = Context::RGBA16F, + Rgba16i = Context::RGBA16I, + Rgba16ui = Context::RGBA16UI, + Rgba32f = Context::RGBA32F, + Rgba32i = Context::RGBA32I, + Rgba32ui = Context::RGBA32UI, + Rgba4 = Context::RGBA4, + Rgba8 = Context::RGBA8, + Rgba8i = Context::RGBA8I, + Rgba8SNorm = Context::RGBA8_SNORM, + Rgba8ui = Context::RGBA8UI, + RgbaInteger = Context::RGBA_INTEGER, + RgbInteger = Context::RGB_INTEGER, + RgInteger = Context::RG_INTEGER, + SRgb8 = Context::SRGB8, + SRgb8Alpha8 = Context::SRGB8_ALPHA8, +} + + + +// ============== +// === Format === +// ============== + +/// Trait for every format of a texture. +pub trait Format = Default + Into; + + + +// ================= +// === AnyFormat === +// ================= + +/// Texture formats. A `GlEnum` specifying the format of the texel data. Follow the link to learn +/// more: https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D +pub mod format { + use super::*; + crate::define_singleton_enum_gl_from! { + AnyFormat + { Alpha, DepthComponent, DepthStencil, Luminance, LuminanceAlpha, Red, RedInteger, Rg + , Rgb, Rgba, RgbaInteger, RgbInteger, RgInteger, + } + } +} +pub use format::*; + + + + +// ========================= +// === AnyInternalFormat === +// ========================= + +/// A GLenum specifying the color components in the texture. Follow the link to learn more: +/// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D +pub mod internal_format { + use super::*; + crate::define_singleton_enum_gl_from! { + AnyInternalFormat + { Alpha, Luminance, LuminanceAlpha, Rgb, Rgba, R8, R8SNorm, R16f, R32f, R8ui, R8i + , R16ui, R16i, R32ui, R32i, Rg8, Rg8SNorm, Rg16f, Rg32f, Rg8ui, Rg8i, Rg16ui, Rg16i + , Rg32ui, Rg32i, Rgb8, SRgb8, Rgb565, Rgb8SNorm, R11fG11fB10f, Rgb9E5, Rgb16f, Rgb32f + , Rgb8ui, Rgb8i, Rgb16ui, Rgb16i, Rgb32ui, Rgb32i, Rgba8, SRgb8Alpha8, Rgba8SNorm + , Rgb5A1, Rgba4, Rgb10A2, Rgba16f, Rgba32f, Rgba8ui, Rgba8i, Rgb10A2ui, Rgba16ui + , Rgba16i, Rgba32i, Rgba32ui, DepthComponent16, DepthComponent24, DepthComponent32f + , Depth24Stencil8, Depth32fStencil8 + } + } +} +pub use internal_format::*; + + + +// ====================== +// === InternalFormat === +// ====================== + +/// Provides information about the size of a texture element for a given `InternalFormat`. +pub trait TextureElement { + /// The size in bytes of a single element of the texture. + type ByteSize: DimName; +} + +/// Provides information about the suitable format and checks if the texture is color renderable +/// and filterable for a given `InternalFormat`. +pub trait InternalFormat : Default + Into +'static { + /// The `Format` associated with this `InternalFormat`. Please note that `InternalFormat` + /// dictates which `Format` to use, but this relation is asymmetrical. + type Format: Format; + + /// Checks if the texture format can be rendered as color. + type ColorRenderable: Value; + + /// Checks it he texture can be filtered. + type Filterable: Value; + + /// Checks if the texture format can be rendered as color. + fn color_renderable() -> bool { + ::value() + } + + /// Checks it he texture can be filtered. + fn filterable() -> bool { + ::value() + } +} + + +/// Generates `TextureElement` and `InternalFormat` instances. Please note that the relation +/// between internal format, format, and possible client texel types is very strict and you are +/// not allowed to choose them arbitrary. Follow the link to learn more about possible relations and +/// how the values were composed below: +/// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D +#[macro_export] +macro_rules! generate_internal_format_instances { + ([] $( $internal_format:ident $format:ident $color_renderable:tt $filterable:tt $elem_descs:tt + )*) => { + $( + $crate::generate_internal_format_instances_item! + { $internal_format $format $color_renderable $filterable $elem_descs } + )* + } +} + +/// See docs of `generate_internal_format_instances`. +#[macro_export] +macro_rules! generate_internal_format_instances_item { + ( $internal_format:ident $format:ident $color_renderable:tt $filterable:tt + [$($possible_types:ident : $bytes_per_element:ident),*] + ) => { + $(impl TextureElement<$possible_types> for $internal_format { + type ByteSize = $bytes_per_element; + })* + + impl InternalFormat for $internal_format { + type Format = $format; + type ColorRenderable = $color_renderable; + type Filterable = $filterable; + } + } +} + +/// Runs the provided macro with all texture format relations. In order to learn more about the +/// possible relations, refer to the source code and to the guide: +/// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D +#[macro_export] +macro_rules! with_texture_format_relations { ($f:ident $args:tt) => { $crate::$f! { $args +// INTERNAL_FORMAT FORMAT COL FILT [POSSIBLE_TYPE:BYTES_PER_TEXTURE_ELEM] + Alpha Alpha True True [u8:U1,f16:U2,f32:U4] + Luminance Luminance True True [u8:U1,f16:U2,f32:U4] + LuminanceAlpha LuminanceAlpha True True [u8:U2,f16:U4,f32:U8] + Rgb Rgb True True [u8:U3,f16:U6,f32:U12,u16_5_6_5:U2] + Rgba Rgba True True [u8:U4,f16:U8,f32:U16,u16_4_4_4_4:U2,u16_5_5_5_1:U2] + R8 Red True True [u8:U1] + R8SNorm Red False True [i8:U1] + R16f Red False True [f32:U4,f16:U2] + R32f Red False False [f32:U4] + R8ui RedInteger True False [u8:U1] + R8i RedInteger True False [i8:U1] + R16ui RedInteger True False [u16:U2] + R16i RedInteger True False [i16:U2] + R32ui RedInteger True False [u32:U4] + R32i RedInteger True False [i32:U4] + Rg8 Rg True True [u8:U2] + Rg8SNorm Rg False True [i8:U2] + Rg16f Rg False True [f32:U8,f16:U4] + Rg32f Rg False False [f32:U8] + Rg8ui RgInteger True False [u8:U2] + Rg8i RgInteger True False [i8:U2] + Rg16ui RgInteger True False [u16:U4] + Rg16i RgInteger True False [i16:U4] + Rg32ui RgInteger True False [u32:U8] + Rg32i RgInteger True False [i32:U8] + Rgb8 Rgb True True [u8:U3] + SRgb8 Rgb False True [u8:U3] + Rgb565 Rgb True True [u8:U3,u16_5_6_5:U2] + Rgb8SNorm Rgb False True [i8:U3] + R11fG11fB10f Rgb False True [f32:U12,f16:U6,u32_f10_f11_f11_REV:U4] + Rgb9E5 Rgb False True [f32:U12,f16:U6,u32_5_9_9_9_REV:U4] + Rgb16f Rgb False True [f32:U12,f16:U6] + Rgb32f Rgb False False [f32:U12] + Rgb8ui RgbInteger False False [u8:U3] + Rgb8i RgbInteger False False [i8:U3] + Rgb16ui RgbInteger False False [u16:U6] + Rgb16i RgbInteger False False [i16:U6] + Rgb32ui RgbInteger False False [u32:U12] + Rgb32i RgbInteger False False [i32:U12] + Rgba8 Rgba True True [u8:U4] + SRgb8Alpha8 Rgba True True [u8:U4] + Rgba8SNorm Rgba False True [i8:U4] + Rgb5A1 Rgba True True [u8:U4,u16_5_5_5_1:U2,u32_2_10_10_10_REV:U4] + Rgba4 Rgba True True [u8:U4,u16_4_4_4_4:U2] + Rgb10A2 Rgba True True [u32_2_10_10_10_REV:U4] + Rgba16f Rgba False True [f32:U16,f16:U8] + Rgba32f Rgba False False [f32:U16] + Rgba8ui RgbaInteger True False [u8:U4] + Rgba8i RgbaInteger True False [i8:U4] + Rgb10A2ui RgbaInteger True False [u32_2_10_10_10_REV:U4] + Rgba16ui RgbaInteger True False [u16:U8] + Rgba16i RgbaInteger True False [i16:U8] + Rgba32i RgbaInteger True False [i32:U16] + Rgba32ui RgbaInteger True False [u32:U16] + DepthComponent16 DepthComponent True False [u16:U2,u32:U4] + DepthComponent24 DepthComponent True False [u32:U4] + DepthComponent32f DepthComponent True False [f32:U4] + Depth24Stencil8 DepthStencil True False [u32_24_8:U4] + Depth32fStencil8 DepthStencil True False [f32_u24_u8_REV:U4] +}}} + +with_texture_format_relations!(generate_internal_format_instances []); + + + +// ===================== +// === TextureSource === +// ===================== + +/// Source of the texture. Please note that the texture will be loaded asynchronously on demand. +#[derive(Clone,Debug)] +pub enum TextureSource { + /// URL the texture should be loaded from. This source implies asynchronous loading. + Url(String) +} + +impl From for TextureSource { + fn from(s:S) -> Self { + Self::Url(s.into()) + } +} + + + +// =============== +// === Texture === +// =============== + +/// Texture representation. +#[derive(Derivative)] +#[derivative(Clone(bound=""))] +#[derivative(Debug(bound=""))] +pub struct Texture { + source : TextureSource, + phantom : PhantomData2, +} + +/// Bounds for every texture item type. +pub trait TextureItemType = PhantomInto + 'static; + +impl Texture { + /// Constructor. + pub fn new>(source:S) -> Self { + let source = source.into(); + let phantom = PhantomData; + Self {source,phantom} + } + + /// Internal format instance of this texture. Please note, that this value could be computed + /// without taking self reference, however it was defined in such way for convenient usage. + pub fn internal_format(&self) -> AnyInternalFormat { + ::default().into() + } + + /// Format instance of this texture. Please note, that this value could be computed + /// without taking self reference, however it was defined in such way for convenient usage. + pub fn format(&self) -> AnyFormat { + ::default().into() + } + + /// Internal format of this texture as `GlEnum`. Please note, that this value could be computed + /// without taking self reference, however it was defined in such way for convenient usage. + pub fn gl_internal_format(&self) -> i32 { + let GlEnum(u) = self.internal_format().into_gl_enum(); + u as i32 + } + + /// Format of this texture as `GlEnum`. Please note, that this value could be computed + /// without taking self reference, however it was defined in such way for convenient usage. + pub fn gl_format(&self) -> GlEnum { + self.format().into_gl_enum() + } + + /// Element type of this texture as `GlEnum`. Please note, that this value could be computed + /// without taking self reference, however it was defined in such way for convenient usage. + pub fn gl_elem_type(&self) -> u32 { + ::gl_enum().into() + } +} + +impl Texture { + /// Smart constructor. + #[allow(non_snake_case)] + pub fn Rgba>(source:S) -> Self { + let source = source.into(); + let phantom = PhantomData; + Self {source,phantom} + } +} + + + +// ==================== +// === BoundTexture === +// ==================== + +/// Texture bound to GL context. +#[derive(Debug,Derivative)] +#[derivative(Clone(bound=""))] +pub struct BoundTexture { + rc: Rc>> +} + +/// Texture bound to GL context. +#[derive(Debug)] +pub struct BoundTextureData { + texture : Texture, + gl_texture : WebGlTexture, + context : Context, +} + +impl BoundTextureData { + /// Constructor. + pub fn new(texture:Texture,context:&Context) -> Self { + let gl_texture = context.create_texture().unwrap(); + let context = context.clone(); + Self {texture,gl_texture,context} + } +} + +impl BoundTextureData { + /// Initializes default texture value. It is useful when the texture data needs to be downloaded + /// asynchronously. This method creates a mock 1px x 1px texture and uses it as a mock texture + /// until the download is complete. + pub fn init_mock(&self) { + let texture = &self.texture; + let target = Context::TEXTURE_2D; + let level = 0; + let internal_format = texture.gl_internal_format(); + let format = texture.gl_format().into(); + let elem_type = texture.gl_elem_type(); + let width = 1; + let height = 1; + let border = 0; + let color = vec![0,0,255,255]; + self.context.bind_texture(Context::TEXTURE_2D,Some(&self.gl_texture)); + self.context.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array + (target,level,internal_format,width,height,border,format,elem_type,Some(&color)).unwrap(); + } +} + +impl BoundTexture { + /// Constructor. + pub fn new(texture:Texture, context:&Context) -> Self { + let data = BoundTextureData::new(texture,context); + let rc = Rc::new(RefCell::new(data)); + let out = Self {rc}; + out.init_mock(); + out.reload(); + out + } + + /// Initializes default texture value. It is useful when the texture data needs to be downloaded + /// asynchronously. This method creates a mock 1px x 1px texture and uses it as a mock texture + /// until the download is complete. + pub fn init_mock(&self) { + self.rc.borrow().init_mock() + } + + /// Loads or re-loads the texture data from the provided source. If the source involves + /// downloading the data, this action will be performed asynchronously. + pub fn reload(&self) { + let data = self.rc.borrow(); + match &data.texture.source { + TextureSource::Url(url) => { + let image = HtmlImageElement::new().unwrap(); + let no_callback = >>::None; + let callback_ref = Rc::new(RefCell::new(no_callback)); + let image_ref = Rc::new(RefCell::new(image)); + let this = self.clone(); + let callback_ref2 = callback_ref.clone(); + let image_ref_opt = image_ref.clone(); + let callback: Closure = Closure::once(move || { + let _keep_alive = callback_ref2; + let data = this.rc.borrow(); + let texture = &data.texture; + let image = image_ref_opt.borrow(); + let target = Context::TEXTURE_2D; + let level = 0; + let internal_format = texture.gl_internal_format(); + let format = texture.gl_format().into(); + let elem_type = texture.gl_elem_type(); + data.context.bind_texture(target,Some(&data.gl_texture)); + data.context.tex_image_2d_with_u32_and_u32_and_html_image_element + (target,level,internal_format,format,elem_type,&image).unwrap(); + }); + let js_callback = callback.as_ref().unchecked_ref(); + let image = image_ref.borrow(); + request_cors_if_not_same_origin(&image,&url); + image.set_src(url); + image.add_event_listener_with_callback("load",js_callback).unwrap(); + *callback_ref.borrow_mut() = Some(callback); + } + } + } +} + + +// === Utils === + +/// CORS = Cross Origin Resource Sharing. It's a way for the webpage to ask the image server for +/// permission to use the image. To do this we set the crossOrigin attribute to something and then +/// when the browser tries to get the image from the server, if it's not the same domain, the browser +/// will ask for CORS permission. The string we set `cross_origin` to is sent to the server. +/// The server can look at that string and decide whether or not to give you permission. Most +/// servers that support CORS don't look at the string, they just give permission to everyone. +/// +/// **Note** +/// Why don't want to just always see the permission because asking for permission takes 2 HTTP +/// requests, so it's slower than not asking. If we know we're on the same domain or we know we +/// won't use the image for anything except img tags and or canvas2d then we don't want to set +/// crossDomain because it will make things slower. +fn request_cors_if_not_same_origin(img:&HtmlImageElement, url_str:&str) { + let url = Url::new(url_str).unwrap(); + let origin = web::window().location().origin().unwrap(); + if url.origin() != origin { + img.set_cross_origin(Some("")); + } +} + + + +// ====================== +// === Meta Iterators === +// ====================== + +/// See docs of `with_all_texture_types`. +#[macro_export] +macro_rules! with_all_texture_types_cartesians { + ($f:ident [$($out:tt)*]) => { + $f! { $($out)* } + }; + ($f:ident $out:tt [$a:tt []] $($in:tt)*) => { + $crate::with_all_texture_types_cartesians! {$f $out $($in)*} + }; + ($f:ident [$($out:tt)*] [$a:tt [$b:tt $($bs:tt)*]] $($in:tt)*) => { + $crate::with_all_texture_types_cartesians! {$f [$($out)* [$a $b]] [$a [$($bs)*]] $($in)* } + }; +} + +/// See docs of `with_all_texture_types`. +#[macro_export] +macro_rules! with_all_texture_types_impl { + ( [$f:ident] + $( $internal_format:ident $format:ident $color_renderable:tt $filterable:tt + [$($possible_types:ident : $bytes_per_element:ident),*] + )*) => { + $crate::with_all_texture_types_cartesians! + { $f [] $([$internal_format [$($possible_types)*]])* } + } +} + +/// Runs the argument macro providing it with list of all possible texture types: +/// `arg! { [Alpha u8] [Alpha f16] [Alpha f32] [Luminance u8] ... }` +#[macro_export] +macro_rules! with_all_texture_types { + ($f:ident) => { + $crate::with_texture_format_relations! { with_all_texture_types_impl [$f] } + } +} diff --git a/gui/lib/core/src/system/gpu/data/uniform.rs b/gui/lib/core/src/system/gpu/data/uniform.rs index c66b60f333..59019c2789 100644 --- a/gui/lib/core/src/system/gpu/data/uniform.rs +++ b/gui/lib/core/src/system/gpu/data/uniform.rs @@ -1,28 +1,63 @@ #![allow(missing_docs)] +pub mod upload; + use crate::prelude::*; use enum_dispatch::*; -use nalgebra::Matrix4; -use nalgebra::Vector3; use shapely::shared; +use upload::UniformUpload; use web_sys::WebGlUniformLocation; -use crate::display::render::webgl::Context; -use crate::system::gpu::data::ContextUniformOps; -use crate::system::gpu::data::GpuData; -use crate::system::web::Logger; +use crate::system::gpu::shader::Context; +use crate::system::gpu::data::texture::*; +use crate::system::gpu::data::prim::*; -// ============= -// === Types === -// ============= +// ==================== +// === UniformValue === +// ==================== -/// A set of constraints that every uniform has to met. -pub trait UniformValue = GpuData where - AnyUniform : From>, - Context : ContextUniformOps; +/// Describes every value which can be kept inside an Uniform. +pub trait UniformValue = UniformUpload; + +/// Some values need to be initialized before they can be used as uniforms. Textures, for example, +/// need to allocate memory on GPU and if used with remote source, need to download images. +/// For primitive types, like numbers or matrices, the binding operation does nothing. +pub trait IntoUniformValue = IntoUniformValueImpl where + Uniform>: Into; + +/// Internal helper for `IntoUniformValue`. +pub trait IntoUniformValueImpl { + type Result; + fn into_uniform_value(self, context:&Context) -> Self::Result; +} + +/// Result of the binding operation. +pub type AsUniformValue = ::Result; + + +// === Instances === + +macro_rules! define_identity_uniform_value_impl { + ( [] [$([$t1:ident $t2:ident])*] ) => {$( + impl IntoUniformValueImpl for $t1<$t2> { + type Result = $t1<$t2>; + fn into_uniform_value(self, _context:&Context) -> Self::Result { + self + } + } + )*} +} +crate::with_all_prim_types!([[define_identity_uniform_value_impl][]]); + +impl IntoUniformValueImpl for Texture { + type Result = BoundTexture; + fn into_uniform_value(self, context:&Context) -> Self::Result { + BoundTexture::new(self,context) + } +} @@ -33,17 +68,19 @@ pub trait UniformValue = GpuData where shared! { UniformScope /// A scope containing set of uniform values. -#[derive(Clone,Debug)] +#[derive(Debug)] pub struct UniformScopeData { - map : HashMap, - logger : Logger, + map : HashMap, + logger : Logger, + context : Context, } impl { /// Constructor. - pub fn new(logger: Logger) -> Self { - let map = default(); - Self {map,logger} + pub fn new(logger:Logger, context:&Context) -> Self { + let map = default(); + let context = context.clone(); + Self {map,logger,context} } /// Look up uniform by name. @@ -57,14 +94,18 @@ impl { } /// Add a new uniform with a given name and initial value. Returns `None` if the name is in use. - pub fn add - (&mut self, name:Name, value:Value) -> Option> { + /// Please note that the value will be bound to the context before it becomes the uniform. + /// Refer to the docs of `IntoUniformValue` to learn more. + pub fn add + (&mut self, name:Name, value:Value) -> Option>> { self.add_or_else(name,value,Some,|_|None) } /// Add a new uniform with a given name and initial value. Panics if the name is in use. - pub fn add_or_panic - (&mut self, name:Name, value:Value) -> Uniform { + /// Please note that the value will be bound to the context before it becomes the uniform. + /// Refer to the docs of `IntoUniformValue` to learn more. + pub fn add_or_panic + (&mut self, name:Name, value:Value) -> Uniform> { self.add_or_else(name,value,|t|{t},|name| { panic!("Trying to override uniform '{}'.", name.as_ref()) }) @@ -75,10 +116,13 @@ impl UniformScopeData { /// Adds a new uniform with a given name and initial value. In case the name was already in use, /// it fires the `fail` function. Otherwise, it fires the `ok` function on the newly created /// uniform. - pub fn add_or_else)->T, Fail:Fn(Name)->T, T> - (&mut self, name:Name, value:Value, ok:Ok, fail:Fail) -> T { + pub fn add_or_else + (&mut self, name:Name, value:Value, ok:Ok, fail:Fail) -> T + where Ok : Fn(Uniform>)->T, + Fail : Fn(Name)->T { if self.map.contains_key(name.as_ref()) { fail(name) } else { - let uniform = Uniform::new(value); + let bound_value = value.into_uniform_value(&self.context); + let uniform = Uniform::new(bound_value); let any_uniform = uniform.clone().into(); self.map.insert(name.into(),any_uniform); ok(uniform) @@ -88,20 +132,20 @@ impl UniformScopeData { -// =================== -// === UniformData === -// =================== +// =============== +// === Uniform === +// =============== shared! { Uniform /// An uniform value. -#[derive(Clone,Debug)] +#[derive(Debug)] pub struct UniformData { value: Value, dirty: bool, } -impl { +impl { /// Constructor. pub fn new(value:Value) -> Self { let dirty = false; @@ -134,12 +178,91 @@ impl { pub fn unset_dirty(&mut self) { self.dirty = false; } +}} +impl UniformData { /// Uploads the uniform data to the provided location of the currently bound shader program. pub fn upload(&self, context:&Context, location:&WebGlUniformLocation) { - context.set_uniform(location,&self.value); + self.value.upload_uniform(context,location); } -}} +} + +impl Uniform { + /// Uploads the uniform data to the provided location of the currently bound shader program. + pub fn upload(&self, context:&Context, location:&WebGlUniformLocation) { + self.rc.borrow().upload(context,location) + } +} + + + +// ====================== +// === AnyPrimUniform === +// ====================== + +macro_rules! define_any_prim_uniform { + ( [] [$([$t1:ident $t2:ident])*] ) => { paste::item! { + /// Existentially typed uniform value. + #[allow(non_camel_case_types)] + #[enum_dispatch(AnyPrimUniformOps)] + #[derive(Clone,Debug)] + pub enum AnyPrimUniform { + $([](Uniform<$t1<$t2>>)),* + } + }} +} +crate::with_all_prim_types!([[define_any_prim_uniform][]]); + +/// Set of operations exposed by the `AnyPrimUniform` value. +#[enum_dispatch] +pub trait AnyPrimUniformOps { + fn upload(&self, context:&Context, location:&WebGlUniformLocation); +} + + + +// ========================= +// === AnyTextureUniform === +// ========================= + +macro_rules! gen_any_texture_uniform { + ( $([$internal_format:tt $type:tt])* ) => { paste::item! { + #[allow(missing_docs)] + #[allow(non_camel_case_types)] + #[enum_dispatch(AnyTextureUniformOps)] + #[derive(Clone,Debug)] + pub enum AnyTextureUniform { + $( [< $internal_format _ $type >] (Uniform>) ),* + } + }} +} + +macro_rules! gen_prim_conversions { + ( [] [$([$t1:ident $t2:ident])*] ) => {$( + impl From>> for AnyUniform { + fn from(t:Uniform<$t1<$t2>>) -> Self { + Self::Prim(t.into()) + } + } + )*} +} + +macro_rules! gen_texture_conversions { + ( $([$internal_format:tt $type:tt])* ) => {$( + impl From>> for AnyUniform { + fn from(t:Uniform>) -> Self { + Self::Texture(t.into()) + } + } + )*} +} + +crate::with_all_texture_types!(gen_any_texture_uniform); + + +#[enum_dispatch] +pub trait AnyTextureUniformOps { +} @@ -147,19 +270,11 @@ impl { // === AnyUniform === // ================== -/// Existentially typed uniform value. -#[allow(non_camel_case_types)] -#[enum_dispatch(AnyUniformOps)] #[derive(Clone,Debug)] pub enum AnyUniform { - Variant_i32 (Uniform), - Variant_f32 (Uniform), - Variant_Vector3_of_f32(Uniform>), - Variant_Matrix4_of_f32(Uniform>) + Prim(AnyPrimUniform), + Texture(AnyTextureUniform) } -/// Set of operations exposed by the `AnyUniform` value. -#[enum_dispatch] -pub trait AnyUniformOps { - fn upload(&self, context:&Context, location:&WebGlUniformLocation); -} +crate::with_all_prim_types!([[gen_prim_conversions][]]); +crate::with_all_texture_types!(gen_texture_conversions); diff --git a/gui/lib/core/src/system/gpu/data/uniform/upload.rs b/gui/lib/core/src/system/gpu/data/uniform/upload.rs new file mode 100644 index 0000000000..7eaad7ee61 --- /dev/null +++ b/gui/lib/core/src/system/gpu/data/uniform/upload.rs @@ -0,0 +1,155 @@ +//! This module defines abstraction for uniform uploading. WebGL defines a lot of functions for +//! uploading particular data shapes. Fortunately, Rust is strongly typed, so we can establish a +//! single abstraction for data uploading. + +use web_sys::WebGlUniformLocation; + +use crate::system::gpu::Context; +use crate::system::gpu::data::prim::*; + + + +// ===================== +// === UniformUpload === +// ===================== + +/// Abstraction for uploading uniforms to GPU based on their types. +pub trait UniformUpload { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation); +} + +impl UniformUpload for bool { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + let value = if *self {1} else {0}; + context.uniform1i(Some(location),value); + } +} + +impl UniformUpload for i32 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform1i(Some(location),*self); + } +} + +impl UniformUpload for f32 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform1f(Some(location),*self); + } +} + +impl UniformUpload for Vector2 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform2fv_with_f32_array(Some(location),self.data.as_slice()); + } +} + +impl UniformUpload for Vector3 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform3fv_with_f32_array(Some(location),self.data.as_slice()); + } +} + +impl UniformUpload for Vector4 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform4fv_with_f32_array(Some(location),self.data.as_slice()); + } +} + +impl UniformUpload for Vector2 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform2iv_with_i32_array(Some(location),self.data.as_slice()); + } +} + +impl UniformUpload for Vector3 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform3iv_with_i32_array(Some(location),self.data.as_slice()); + } +} + +impl UniformUpload for Vector4 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform4iv_with_i32_array(Some(location),self.data.as_slice()); + } +} + +impl UniformUpload for Vector2 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + let v:Vec = self.data.as_slice().iter().cloned().map(|t| if t {1} else {0}).collect(); + context.uniform2iv_with_i32_array(Some(location),&v); + } +} + +impl UniformUpload for Vector3 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + let v:Vec = self.data.as_slice().iter().cloned().map(|t| if t {1} else {0}).collect(); + context.uniform3iv_with_i32_array(Some(location),&v); + } +} + +impl UniformUpload for Vector4 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + let v:Vec = self.data.as_slice().iter().cloned().map(|t| if t {1} else {0}).collect(); + context.uniform4iv_with_i32_array(Some(location),&v); + } +} + +impl UniformUpload for Matrix2 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform_matrix2fv_with_f32_array(Some(location),false,self.data.as_slice()); + } +} + +impl UniformUpload for Matrix3 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform_matrix3fv_with_f32_array(Some(location),false,self.data.as_slice()); + } +} + +impl UniformUpload for Matrix4 { + fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) { + context.uniform_matrix4fv_with_f32_array(Some(location),false,self.data.as_slice()); + } +} + +impl UniformUpload for Matrix2x3 { + fn upload_uniform(&self, _context:&Context, _location:&WebGlUniformLocation) { + todo!() // FIXME: https://github.com/rustwasm/wasm-bindgen/issues/1956 + // context.uniform_matrix2x3fv_with_f32_array(Some(location),false,self.data.as_slice()); + } +} + +impl UniformUpload for Matrix2x4 { + fn upload_uniform(&self, _context:&Context, _location:&WebGlUniformLocation) { + todo!() // FIXME: https://github.com/rustwasm/wasm-bindgen/issues/1956 + // context.uniform_matrix2x4fv_with_f32_array(Some(location),false,self.data.as_slice()); + } +} + +impl UniformUpload for Matrix3x2 { + fn upload_uniform(&self, _context:&Context, _location:&WebGlUniformLocation) { + todo!() // FIXME: https://github.com/rustwasm/wasm-bindgen/issues/1956 + // context.uniform_matrix3x2fv_with_f32_array(Some(location),false,self.data.as_slice()); + } +} + +impl UniformUpload for Matrix3x4 { + fn upload_uniform(&self, _context:&Context, _location:&WebGlUniformLocation) { + todo!() // FIXME: https://github.com/rustwasm/wasm-bindgen/issues/1956 + // context.uniform_matrix3x4fv_with_f32_array(Some(location),false,self.data.as_slice()); + } +} + +impl UniformUpload for Matrix4x2 { + fn upload_uniform(&self, _context:&Context, _location:&WebGlUniformLocation) { + todo!() // FIXME: https://github.com/rustwasm/wasm-bindgen/issues/1956 + // context.uniform_matrix4x2fv_with_f32_array(Some(location),false,self.data.as_slice()); + } +} + +impl UniformUpload for Matrix4x3 { + fn upload_uniform(&self, _context:&Context, _location:&WebGlUniformLocation) { + todo!() // FIXME: https://github.com/rustwasm/wasm-bindgen/issues/1956 + // context.uniform_matrix4x3fv_with_f32_array(Some(location),false,self.data.as_slice()); + } +} diff --git a/gui/lib/core/src/display/render/webgl.rs b/gui/lib/core/src/system/gpu/shader.rs similarity index 96% rename from gui/lib/core/src/display/render/webgl.rs rename to gui/lib/core/src/system/gpu/shader.rs index a545560073..18beae2903 100644 --- a/gui/lib/core/src/display/render/webgl.rs +++ b/gui/lib/core/src/system/gpu/shader.rs @@ -13,6 +13,20 @@ use web_sys::WebGlShader; +// =============== +// === Exports === +// =============== + +/// Common types. +pub mod types { + pub use super::glsl; + pub use glsl::Glsl; + pub use glsl::traits::*; +} +pub use types::*; + + + // ============= // === Types === // ============= diff --git a/gui/lib/core/src/display/render/webgl/glsl.rs b/gui/lib/core/src/system/gpu/shader/glsl.rs similarity index 74% rename from gui/lib/core/src/display/render/webgl/glsl.rs rename to gui/lib/core/src/system/gpu/shader/glsl.rs index 53364f88ee..1e550ddaf1 100644 --- a/gui/lib/core/src/display/render/webgl/glsl.rs +++ b/gui/lib/core/src/system/gpu/shader/glsl.rs @@ -6,8 +6,102 @@ use crate::prelude::*; use crate::data::container::Add; + use code_builder::{CodeBuilder, HasCodeRepr}; +use nalgebra::*; use shapely::derive_clone_plus; +use crate::system::gpu::data::buffer::item::MatrixCtx; + + + +// ================================================================================================= +// === Glsl ======================================================================================== +// ================================================================================================= + +/// A GLSL code representation. +#[derive(Clone,Debug,Shrinkwrap)] +#[shrinkwrap(mutable)] +pub struct Glsl { + /// Raw, textual code representation. + pub str: String, +} + +impl Display for Glsl { + fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.str,f) + } +} + + +// === Conversions from Glsl === + +impl From for String { + fn from(t:Glsl) -> Self { + t.str + } +} + +impl From<&Glsl> for String { + fn from(t:&Glsl) -> Self { + t.str.clone() + } +} + + +// === Conversions to Glsl === + +impl From<&Glsl> for Glsl { + fn from(t:&Glsl) -> Self { + t.clone() + } +} + +impl From for Glsl { + fn from(t:String) -> Self { + Self {str:t} + } +} + +impl From<&String> for Glsl { + fn from(t:&String) -> Self { + Self {str:t.into()} + } +} + +impl From<&str> for Glsl { + fn from(t:&str) -> Self { + Self {str:(*t).into()} + } +} + +impl From for Glsl { + fn from(t:bool) -> Self { + t.to_string().into() + } +} + +impl From for Glsl { + fn from(t:i32) -> Self { + t.to_string().into() + } +} + +impl From for Glsl { + fn from(t:f32) -> Self { + let is_int = t.fract() == 0.0; + if is_int { iformat!("{t}.0").into() } + else { iformat!("{t}").into() } + } +} + +impl From> for Glsl +where Self:MatrixCtx, PhantomData>:Into { + fn from(t:MatrixMN) -> Self { + let type_name = PrimType::phantom_from::>().to_code(); + let vals:Vec = t.as_slice().iter().cloned().map(|t|format!("{:?}",t)).collect(); + format!("{}({})",type_name,vals.join(",")).into() + } +} @@ -341,14 +435,14 @@ pub enum PrimType { Mat4x2, Mat4x3, Mat4x4, Vec2, Vec3, Vec4, IVec2, IVec3, IVec4, BVec2, BVec3, BVec4, UInt, UVec2, UVec3, UVec4, - Sampler2D, Sampler3D, SamplerCube, - Sampler2DShadow, SamplerCubeShadow, - Sampler2DArray, - Sampler2DArrayShadow, - ISampler2D, ISampler3D, ISamplerCube, - ISampler2DArray, - USampler2D, USampler3D, USamplerCube, - USampler2DArray, + Sampler2d, Sampler3d, SamplerCube, + Sampler2dShadow, SamplerCubeShadow, + Sampler2dArray, + Sampler2dArrayShadow, + ISampler2d, ISampler3d, ISamplerCube, + ISampler2dArray, + USampler2d, USampler3d, USamplerCube, + USampler2dArray, Struct(Identifier), } @@ -384,26 +478,38 @@ impl HasCodeRepr for PrimType { Self::UVec2 => builder.add("uvec2"), Self::UVec3 => builder.add("uvec3"), Self::UVec4 => builder.add("uvec4"), - Self::Sampler2D => builder.add("sampler2d"), - Self::Sampler3D => builder.add("sampler3d"), + Self::Sampler2d => builder.add("sampler2d"), + Self::Sampler3d => builder.add("sampler3d"), Self::SamplerCube => builder.add("samplerCube"), - Self::Sampler2DShadow => builder.add("sampler2DShadow"), + Self::Sampler2dShadow => builder.add("sampler2DShadow"), Self::SamplerCubeShadow => builder.add("samplerCubeShadow"), - Self::Sampler2DArray => builder.add("sampler2DArray"), - Self::Sampler2DArrayShadow => builder.add("sampler2DArrayShadow"), - Self::ISampler2D => builder.add("isampler2D"), - Self::ISampler3D => builder.add("isampler3D"), + Self::Sampler2dArray => builder.add("sampler2DArray"), + Self::Sampler2dArrayShadow => builder.add("sampler2DArrayShadow"), + Self::ISampler2d => builder.add("isampler2D"), + Self::ISampler3d => builder.add("isampler3D"), Self::ISamplerCube => builder.add("isamplerCube"), - Self::ISampler2DArray => builder.add("isampler2DArray"), - Self::USampler2D => builder.add("usampler2D"), - Self::USampler3D => builder.add("usampler3D"), + Self::ISampler2dArray => builder.add("isampler2DArray"), + Self::USampler2d => builder.add("usampler2D"), + Self::USampler3d => builder.add("usampler3D"), Self::USamplerCube => builder.add("usamplerCube"), - Self::USampler2DArray => builder.add("usampler2DArray"), + Self::USampler2dArray => builder.add("usampler2DArray"), Self::Struct(ident) => builder.add(ident), }; } } +impl From for String { + fn from(t:PrimType) -> Self { + t.to_code() + } +} + +impl Display for PrimType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f,"{}",self.to_code()) + } +} + // ================= @@ -446,6 +552,7 @@ pub struct LinkageStorage { #[derive(Clone,Debug)] pub enum InterpolationStorage {Smooth, Flat} + // === Printers === impl HasCodeRepr for Layout { @@ -633,3 +740,85 @@ impl HasCodeRepr for Module { builder.add(&self.main); } } + + +// ============================ +// === PrimType Conversions === +// ============================ + +macro_rules! define_glsl_prim_type_conversions { + ($($ty:ty => $name:ident),* $(,)?) => {$( + impl From> for PrimType { + fn from(_:PhantomData<$ty>) -> Self { + Self::$name + } + } + + impl From> for Type { + fn from(_:PhantomData<$ty>) -> Self { + PrimType::$name.into() + } + } + )*} +} + +define_glsl_prim_type_conversions! { + bool => Bool, + i32 => Int, + f32 => Float, + + Vector2 => Vec2, + Vector3 => Vec3, + Vector4 => Vec4, + + Vector2 => IVec2, + Vector3 => IVec3, + Vector4 => IVec4, + + Vector2 => BVec2, + Vector3 => BVec3, + Vector4 => BVec4, + + Matrix2 => Mat2, + Matrix3 => Mat3, + Matrix4 => Mat4, + + Matrix2x3 => Mat2x3, + Matrix2x4 => Mat2x4, + Matrix3x2 => Mat3x2, + Matrix3x4 => Mat3x4, + Matrix4x2 => Mat4x2, + Matrix4x3 => Mat4x3, +} + + +// === Smart accessors === + +/// Extension methods. +pub mod traits { + use super::*; + + /// Extension methods for every type which could be converted to `PrimType`. + pub trait PhantomIntoPrimType: Sized + PhantomInto { + /// `PrimType` representation of the current type. + fn glsl_prim_type() -> PrimType { + Self::phantom_into() + } + } + impl> PhantomIntoPrimType for T {} + + pub trait IntoGlsl<'a> where Self:'a, &'a Self:Into { + fn glsl(&'a self) -> Glsl { + self.into() + } + } + impl<'a,T> IntoGlsl<'a> for T where T:'a, &'a T:Into {} + + pub trait IntoGlsl2 where Self:Into { + fn glsl(self) -> Glsl { + self.into() + } + } + impl IntoGlsl2 for T where T:Into {} +} +pub use traits::*; diff --git a/gui/lib/core/src/system/web.rs b/gui/lib/core/src/system/web.rs new file mode 100644 index 0000000000..a26511244e --- /dev/null +++ b/gui/lib/core/src/system/web.rs @@ -0,0 +1,5 @@ +//! Root module for web-based functionalities. + +pub mod dom; + +pub use basegl_system_web::*; diff --git a/gui/lib/core/src/display/render/css3d.rs b/gui/lib/core/src/system/web/dom.rs similarity index 100% rename from gui/lib/core/src/display/render/css3d.rs rename to gui/lib/core/src/system/web/dom.rs diff --git a/gui/lib/core/src/display/render/css3d/camera.rs b/gui/lib/core/src/system/web/dom/camera.rs similarity index 98% rename from gui/lib/core/src/display/render/css3d/camera.rs rename to gui/lib/core/src/system/web/dom/camera.rs index e8d8d37c9a..cfb9e7d972 100644 --- a/gui/lib/core/src/display/render/css3d/camera.rs +++ b/gui/lib/core/src/system/web/dom/camera.rs @@ -2,7 +2,7 @@ use crate::prelude::*; -use crate::display::render::css3d::Object; +use crate::system::web::dom::Object; use nalgebra::base::Matrix4; use nalgebra::geometry::Perspective3; diff --git a/gui/lib/core/src/display/render/css3d/dom_container.rs b/gui/lib/core/src/system/web/dom/dom_container.rs similarity index 100% rename from gui/lib/core/src/display/render/css3d/dom_container.rs rename to gui/lib/core/src/system/web/dom/dom_container.rs diff --git a/gui/lib/core/src/display/render/css3d/graphics_renderer.rs b/gui/lib/core/src/system/web/dom/graphics_renderer.rs similarity index 100% rename from gui/lib/core/src/display/render/css3d/graphics_renderer.rs rename to gui/lib/core/src/system/web/dom/graphics_renderer.rs diff --git a/gui/lib/core/src/display/render/css3d/html.rs b/gui/lib/core/src/system/web/dom/html.rs similarity index 100% rename from gui/lib/core/src/display/render/css3d/html.rs rename to gui/lib/core/src/system/web/dom/html.rs diff --git a/gui/lib/core/src/display/render/css3d/html/html_object.rs b/gui/lib/core/src/system/web/dom/html/html_object.rs similarity index 98% rename from gui/lib/core/src/display/render/css3d/html/html_object.rs rename to gui/lib/core/src/system/web/dom/html/html_object.rs index 1d6a59dbce..130177b76e 100644 --- a/gui/lib/core/src/display/render/css3d/html/html_object.rs +++ b/gui/lib/core/src/system/web/dom/html/html_object.rs @@ -2,7 +2,7 @@ use crate::prelude::*; -use crate::display::render::css3d::Object; +use crate::system::web::dom::Object; use crate::system::web::create_element; use crate::system::web::dyn_into; use crate::system::web::Result; diff --git a/gui/lib/core/src/display/render/css3d/html/html_renderer.rs b/gui/lib/core/src/system/web/dom/html/html_renderer.rs similarity index 95% rename from gui/lib/core/src/display/render/css3d/html/html_renderer.rs rename to gui/lib/core/src/system/web/dom/html/html_renderer.rs index 8b4b451b23..71820d0bf5 100644 --- a/gui/lib/core/src/display/render/css3d/html/html_renderer.rs +++ b/gui/lib/core/src/system/web/dom/html/html_renderer.rs @@ -2,12 +2,12 @@ use crate::prelude::*; -use crate::display::render::css3d::GraphicsRenderer; -use crate::display::render::css3d::Scene; -use crate::display::render::css3d::Camera; -use crate::display::render::css3d::CameraType; -use crate::display::render::css3d::html::HTMLObject; -use crate::system::gpu::data::JSBufferView; +use crate::system::web::dom::GraphicsRenderer; +use crate::system::web::dom::Scene; +use crate::system::web::dom::Camera; +use crate::system::web::dom::CameraType; +use crate::system::web::dom::html::HTMLObject; +use crate::system::gpu::data::JsBufferView; use crate::system::web::Result; use crate::system::web::create_element; use crate::system::web::dyn_into; @@ -29,7 +29,7 @@ use web_sys::HtmlElement; mod js { use super::*; - #[wasm_bindgen(module = "/src/display/render/css3d/html/snippets.js")] + #[wasm_bindgen(module = "/src/system/web/dom/html/snippets.js")] extern "C" { pub fn set_object_transform(dom:&JsValue, matrix_array:&Object); pub fn setup_perspective(dom: &JsValue, znear: &JsValue); diff --git a/gui/lib/core/src/display/render/css3d/html/snippets.js b/gui/lib/core/src/system/web/dom/html/snippets.js similarity index 100% rename from gui/lib/core/src/display/render/css3d/html/snippets.js rename to gui/lib/core/src/system/web/dom/html/snippets.js diff --git a/gui/lib/core/src/display/render/css3d/htmlscene.rs b/gui/lib/core/src/system/web/dom/htmlscene.rs similarity index 100% rename from gui/lib/core/src/display/render/css3d/htmlscene.rs rename to gui/lib/core/src/system/web/dom/htmlscene.rs diff --git a/gui/lib/core/src/display/render/css3d/object.rs b/gui/lib/core/src/system/web/dom/object.rs similarity index 98% rename from gui/lib/core/src/display/render/css3d/object.rs rename to gui/lib/core/src/system/web/dom/object.rs index e81e31b15b..817e69ad76 100644 --- a/gui/lib/core/src/display/render/css3d/object.rs +++ b/gui/lib/core/src/system/web/dom/object.rs @@ -2,7 +2,7 @@ use crate::prelude::*; -use crate::display::render::css3d::Transform; +use crate::system::web::dom::Transform; use crate::animation::position::HasPosition; use nalgebra::UnitQuaternion; diff --git a/gui/lib/core/src/display/render/css3d/scene.rs b/gui/lib/core/src/system/web/dom/scene.rs similarity index 100% rename from gui/lib/core/src/display/render/css3d/scene.rs rename to gui/lib/core/src/system/web/dom/scene.rs diff --git a/gui/lib/core/src/display/render/css3d/transform.rs b/gui/lib/core/src/system/web/dom/transform.rs similarity index 100% rename from gui/lib/core/src/display/render/css3d/transform.rs rename to gui/lib/core/src/system/web/dom/transform.rs diff --git a/gui/lib/core/tests/html_renderer.rs b/gui/lib/core/tests/html_renderer.rs index b873b6a218..37ec36a26e 100644 --- a/gui/lib/core/tests/html_renderer.rs +++ b/gui/lib/core/tests/html_renderer.rs @@ -18,10 +18,10 @@ extern "C" { #[cfg(test)] mod tests { - use basegl::display::render::css3d::Scene; - use basegl::display::render::css3d::Camera; - use basegl::display::render::css3d::html::HTMLObject; - use basegl::display::render::css3d::html::HTMLRenderer; + use basegl::system::web::dom::Scene; + use basegl::system::web::dom::Camera; + use basegl::system::web::dom::html::HTMLObject; + use basegl::system::web::dom::html::HTMLRenderer; use basegl::system::web::StyleSetter; use basegl::system::web::get_performance; use basegl::animation::position::HasPosition; diff --git a/gui/lib/core/tests/navigation.rs b/gui/lib/core/tests/navigation.rs index 6ab7dfe987..3a663b7cc8 100644 --- a/gui/lib/core/tests/navigation.rs +++ b/gui/lib/core/tests/navigation.rs @@ -7,10 +7,10 @@ web_configure!(run_in_browser); #[cfg(test)] mod tests { - use basegl::display::render::css3d::Scene; - use basegl::display::render::css3d::Camera; - use basegl::display::render::css3d::html::HTMLObject; - use basegl::display::render::css3d::html::HTMLRenderer; + use basegl::system::web::dom::Scene; + use basegl::system::web::dom::Camera; + use basegl::system::web::dom::html::HTMLObject; + use basegl::system::web::dom::html::HTMLRenderer; use basegl::system::web::StyleSetter; use basegl::display::navigation::navigator::Navigator; use basegl::animation::position::HasPosition; diff --git a/gui/lib/core/tests/physics_simulator.rs b/gui/lib/core/tests/physics_simulator.rs index 190d06dcb5..cf201e7596 100644 --- a/gui/lib/core/tests/physics_simulator.rs +++ b/gui/lib/core/tests/physics_simulator.rs @@ -13,10 +13,10 @@ mod tests { use basegl::animation::physics::inertia::PhysicsSimulator; use basegl::animation::physics::inertia::PhysicsProperties; use basegl::animation::animator::fixed_step::FixedStepAnimator; - use basegl::display::render::css3d::html::HTMLRenderer; - use basegl::display::render::css3d::html::HTMLObject; - use basegl::display::render::css3d::Scene; - use basegl::display::render::css3d::Camera; + use basegl::system::web::dom::html::HTMLRenderer; + use basegl::system::web::dom::html::HTMLObject; + use basegl::system::web::dom::Scene; + use basegl::system::web::dom::Camera; use basegl::animation::position::HasPosition; use web_test::*; use nalgebra::{zero, Vector3}; diff --git a/gui/lib/logger/Cargo.toml b/gui/lib/logger/Cargo.toml new file mode 100644 index 0000000000..98d08bc319 --- /dev/null +++ b/gui/lib/logger/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "logger" +version = "0.1.0" +authors = ["Enso Team "] +edition = "2018" + +[lib] + +[features] +default = [] + +[dependencies] +wasm-bindgen = { version = "^0.2", features = ["nightly"] } + +[dependencies.web-sys] +version = "0.3.4" +features = ['console'] diff --git a/gui/lib/logger/src/lib.rs b/gui/lib/logger/src/lib.rs new file mode 100644 index 0000000000..420dd36891 --- /dev/null +++ b/gui/lib/logger/src/lib.rs @@ -0,0 +1,190 @@ +#![feature(trait_alias)] +#![feature(set_stdio)] + +use std::fmt::Debug; +use wasm_bindgen::JsValue; + +#[cfg(target_arch = "wasm32")] +use web_sys::console; + + + +// ============== +// === LogMsg === +// ============== + +pub trait LogMsg { + fn with_log_msg T, T>(&self, f:F) -> T; +} + +impl LogMsg for &str { + fn with_log_msg T, T>(&self, f:F) -> T { + f(self) + } +} + +impl S, S: AsRef> LogMsg for F { + fn with_log_msg T, T>(&self, f:G) -> T { + f(self().as_ref()) + } +} + + +// ============== +// === Logger === +// ============== + +#[derive(Clone,Debug,Default)] +pub struct Logger { + pub path: String, +} + +#[allow(dead_code)] +impl Logger { + pub fn new>(path:T) -> Self { + let path = path.as_ref().to_string(); + Self { path } + } + + pub fn sub>(&self, path: T) -> Self { + Self::new(format!("{}.{}", self.path, path.as_ref())) + } + + pub fn group T>(&self, msg: M, f: F) -> T { + self.group_begin(msg); + let out = f(); + self.group_end(); + out + } + + fn format(&self, msg: M) -> JsValue { + msg.with_log_msg(|s| format!("[{}] {}", self.path, s)).into() + } +} + +#[cfg(target_arch = "wasm32")] +impl Logger { + pub fn trace(&self, _msg: M) { +// console::debug_1(&self.format(msg)); + } + + pub fn info(&self, _msg: M) { +// console::group_1(&self.format(msg)); +// console::group_end(); + } + + pub fn warning(&self, msg: M) { + console::warn_1(&self.format(msg)); + } + + pub fn error(&self, msg: M) { + console::error_1(&self.format(msg)); + } + + pub fn group_begin(&self, _msg: M) { +// console::group_1(&self.format(msg)); + } + + pub fn group_end(&self) { +// console::group_end(); + } +} + +// FIXME: Add the non-wasm impl +#[cfg(not(target_arch = "wasm32"))] +impl Logger { + pub fn trace(&self, _msg: M) {} + pub fn info(&self, _msg: M) {} + pub fn warning(&self, _msg: M) {} + pub fn error(&self, _msg: M) {} + pub fn group_begin(&self, _msg: M) {} + pub fn group_end(&self) {} +} + + +// ==================== +// === Logger Utils === +// ==================== + +#[macro_export] +macro_rules! fmt { + ($($arg:tt)*) => (||(format!($($arg)*))) +} + +#[macro_export] +macro_rules! group { + ($logger:expr, $message:tt, {$($body:tt)*}) => {{ + let __logger = $logger.clone(); + __logger.group_begin(|| iformat!{$message}); + let out = {$($body)*}; + __logger.group_end(); + out + }}; +} + +#[macro_export] +macro_rules! log_template { + ($method:ident $logger:expr, $message:tt $($rest:tt)*) => { + $crate::log_template_impl! {$method $logger, iformat!($message) $($rest)*} + }; +} + + +#[macro_export] +macro_rules! log_template_impl { + ($method:ident $logger:expr, $expr:expr) => {{ + $logger.$method(|| $expr); + }}; + ($method:ident $logger:expr, $expr:expr, $body:tt) => {{ + let __logger = $logger.clone(); + __logger.group_begin(|| $expr); + let out = $body; + __logger.group_end(); + out + }}; +} + +#[macro_export] +macro_rules! with_internal_bug_message { ($f:ident $($args:tt)*) => { $crate::$f! { +"This is a bug. We will be thankful if you report it and provide us with as much information as \ +possible at https://github.com/luna/enso/issues. Thank you!" +$($args)* +}};} + +#[macro_export] +macro_rules! log_internal_bug_template { + ($($toks:tt)*) => { + $crate::with_internal_bug_message! { log_internal_bug_template_impl $($toks)* } + }; +} + +#[macro_export] +macro_rules! log_internal_bug_template_impl { + ($note:tt $method:ident $logger:expr, $message:tt $($rest:tt)*) => { + $crate::log_template_impl! {$method $logger, + format!("Internal Error. {}\n\n{}",iformat!($message),$note) $($rest)* + } + }; +} + + +#[macro_export] +macro_rules! info { + ($($toks:tt)*) => { + $crate::log_template! {info $($toks)*} + }; +} + +#[macro_export] +macro_rules! warning { + ($($toks:tt)*) => { + $crate::log_template! {warning $($toks)*} + }; +} + +#[macro_export] +macro_rules! internal_warning { + ($($toks:tt)*) => { + $crate::log_internal_bug_template! {warning $($toks)*} + }; +} diff --git a/gui/lib/prelude/Cargo.toml b/gui/lib/prelude/Cargo.toml index e74c85f1cd..59bf718c8b 100644 --- a/gui/lib/prelude/Cargo.toml +++ b/gui/lib/prelude/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" [dependencies] boolinator = "2.4.0" derivative = "1.0.3" -derive_more = "0.15.0" +derive_more = "0.99.2" failure = "0.1.5" itertools = "0.8" num = "0.2.0" diff --git a/gui/lib/prelude/src/lib.rs b/gui/lib/prelude/src/lib.rs index 13659989d2..8e8b3cb4ce 100644 --- a/gui/lib/prelude/src/lib.rs +++ b/gui/lib/prelude/src/lib.rs @@ -56,7 +56,7 @@ use nalgebra::Scalar; pub trait Str = Into + AsRef; /// Alias for `Default::default()`. -pub fn default() -> T { +pub fn default() -> T { Default::default() } @@ -150,6 +150,60 @@ impl WithPhantom { +// ========================== +// === PhantomConversions === +// ========================== + +/// A utility for easy driving of type-level computations from value level. Often we've got some +/// type level relations, like a few singleton types, and for each such type we've got an associated +/// value. For example, we can define types `Int` and `Float` and associate with them +/// `WebGlContext::Int` and `WebGlContext::Float` constants encoded as `GlEnum`. In order to convert +/// `Int` or `Float` to the `GlEnum` we do not need the instance of the types, only the information +/// what type it was. So we can define: +/// +/// ```compile_fail +/// impl From> for u32 { +/// from(_:PhantomData>) { +/// GlEnum(WebGlContext::Int) +/// } +/// } +/// ``` +/// +/// And use it like: +/// +/// ```compile_fail +/// let val = GlEnum::from(PhantomData::) +/// ``` +/// +/// Using this utility we can always write the following code instead: +/// +/// ```compile_fail +/// let val = GlEnum::phantom_from::() +/// ``` +pub trait PhantomConversions: Sized { + fn phantom_into

() -> P where Self:PhantomInto

{ + PhantomData::.into() + } + fn phantom_from>() -> Self { + PhantomData::

.into() + } +} +impl PhantomConversions for T {} + +/// Like `Into` but for phantom types. +pub trait PhantomInto = where PhantomData: Into; + + +/// Provides method `to`, which is just like `into` but allows fo superfish syntax. +pub trait ToImpl: Sized { + fn to

(self) -> P where Self:Into

{ + self.into() + } +} +impl ToImpl for T {} + + + // ===================== // === Rc Extensions === // ===================== @@ -308,4 +362,4 @@ impl TypeDisplay for Matrix { ///// Please see the `drop_lifetime` docs. //pub unsafe fn drop_lifetime_mut<'a,'b,T>(t: &'a mut T) -> &'b mut T { // std::mem::transmute(t) -//} \ No newline at end of file +//} diff --git a/gui/lib/shapely/impl/Cargo.toml b/gui/lib/shapely/impl/Cargo.toml index 9fa0f38f73..c6b4e1c092 100644 --- a/gui/lib/shapely/impl/Cargo.toml +++ b/gui/lib/shapely/impl/Cargo.toml @@ -12,4 +12,3 @@ default = [] [dependencies] shapely-macros = { version = "0.1.0" , path = "../macros" } basegl-prelude = { version = "0.1.0" , path = "../../prelude" } - diff --git a/gui/lib/shapely/impl/src/cartesian.rs b/gui/lib/shapely/impl/src/cartesian.rs new file mode 100644 index 0000000000..0a7686fe4e --- /dev/null +++ b/gui/lib/shapely/impl/src/cartesian.rs @@ -0,0 +1,45 @@ +/// Computes a cartesian product of the provided input. +/// +/// For the following expression: +/// ```compile_fail +/// cartesian!(f [g] [a b c] [x y z]); +/// ``` +/// +/// It expands to: +/// ```compile_fail +/// f! { [g] [ [a x] [a y] [a z] [b x] [b y] [b z] [c x] [c y] [c z] ] } +/// ``` +/// +/// If you provide underscore as second argument, it is skipped in the ouput macro: +/// +/// ```compile_fail +/// cartesian!(f _ [a b c] [x y z]); +/// ``` +/// +/// Expands to: +/// ```compile_fail +/// f! { [ [a x] [a y] [a z] [b x] [b y] [b z] [c x] [c y] [c z] ] } +/// ``` +#[macro_export] +macro_rules! cartesian { + ($f:tt [$($a:tt)*] [$($b:tt)*]) => { + $crate::_cartesian_impl!{ $f [] [$($a)*] [$($b)*] [$($b)*] } + }; +} + +/// Internal helper for `cartesian` macro. +#[macro_export] +macro_rules! _cartesian_impl { + ([[$f:path]] $out:tt [] $b:tt $init_b:tt) => { + $f!{ $out } + }; + ([[$f:path] $args:tt] $out:tt [] $b:tt $init_b:tt) => { + $f!{ $args $out } + }; + ($f:tt $out:tt [$a:ident $($at:tt)*] [] $init_b:tt) => { + $crate::_cartesian_impl!{ $f $out [$($at)*] $init_b $init_b } + }; + ($f:tt [$($out:tt)*] [$a:ident $($at:tt)*] [$b:ident $($bt:tt)*] $init_b:tt) => { + $crate::_cartesian_impl!{ $f [$($out)* [$a $b]] [$a $($at)*] [$($bt)*] $init_b } + }; +} diff --git a/gui/lib/shapely/impl/src/lib.rs b/gui/lib/shapely/impl/src/lib.rs index d902a2e33d..21c58bedc3 100644 --- a/gui/lib/shapely/impl/src/lib.rs +++ b/gui/lib/shapely/impl/src/lib.rs @@ -10,6 +10,8 @@ #![feature(overlapping_marker_traits)] pub mod shared; +pub mod singleton; +pub mod cartesian; pub use shapely_macros::*; @@ -19,6 +21,54 @@ use std::pin::Pin; use basegl_prelude::*; +/// Generates a newtype wrapper for the provided types. It also generates a lot of impls, +/// including Copy, Clone, Debug, Default, Display, From, Into, Deref, and DerefMut. +/// +/// For the following input: +/// ```compile_fail +/// newtype_copy! { +/// AttributeIndex(usize) +/// } +/// ``` +/// +/// The following code is generated: +/// ```compile_fail +/// #[derive(Copy, Clone, Debug, Default, Display, From, Into)] +/// pub struct AttributeIndex(usize); +/// impl Deref for AttributeIndex { +/// type Target = usize; +/// fn deref(&self) -> &Self::Target { +/// &self.0 +/// } +/// } +/// impl DerefMut for AttributeIndex { +/// fn deref_mut(&mut self) -> &mut Self::Target { +/// &mut self.0 +/// } +/// } +/// ``` +#[macro_export] +macro_rules! newtype_copy { + ($( $(#$meta:tt)* $name:ident($type:ty); )*) => {$( + $(#$meta)* + #[derive(Copy,Clone,Debug,Default,Display,From,Into)] + pub struct $name($type); + + impl Deref for $name { + type Target = $type; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for $name { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + )*} +} + #[macro_export] macro_rules! derive_clone_plus { ($name:ident) => { diff --git a/gui/lib/shapely/impl/src/shared.rs b/gui/lib/shapely/impl/src/shared.rs index 85b4a7b91d..d9fa8e43ac 100644 --- a/gui/lib/shapely/impl/src/shared.rs +++ b/gui/lib/shapely/impl/src/shared.rs @@ -114,14 +114,17 @@ macro_rules! shared_bracket_impl { ([impl [$($impl_params:tt)*] $name:ident $name_mut:ident $([$($params:tt)*])?] [ $( $(#[$($meta:tt)*])* - pub fn $fn_name:ident $([$($fn_params:tt)*])? ($($fn_args:tt)*) $(-> $fn_type:ty)? { + pub fn $fn_name:ident + $([$($fn_params:tt)*])? ($($fn_args:tt)*) $(-> $fn_type:ty)? { $($fn_body:tt)* - })* + } + )* ]) => { impl <$($impl_params)*> $name_mut $(<$($params)*>)? { $( $(#[$($meta)*])* - pub fn $fn_name $(<$($fn_params)*>)* ($($fn_args)*) $(-> $fn_type)? {$($fn_body)*} + pub fn $fn_name $(<$($fn_params)*>)* + ($($fn_args)*) $(-> $fn_type)? {$($fn_body)*} )* } @@ -164,7 +167,7 @@ macro_rules! shared_bracket_normalized { ( [$name:ident] [ $(#[$($meta:tt)*])* pub struct $name_mut:ident $params:tt { - $($field:ident : $field_type:ty),* $(,)? + $($(#[$($field_meta:tt)*])* $field:ident : $field_type:ty),* $(,)? } $(impl $([$($impl_params:tt)*])? {$($impl_body:tt)*})* @@ -172,7 +175,7 @@ macro_rules! shared_bracket_normalized { $crate::shared_struct! { $(#[$($meta)*])* pub struct $name $name_mut $params { - $($field : $field_type),* + $($(#[$($field_meta)*])* $field : $field_type),* } } @@ -187,14 +190,28 @@ macro_rules! shared_struct { ( $(#[$($meta:tt)*])* pub struct $name:ident $name_mut:ident [$($params:tt)*] { - $($field:ident : $field_type:ty),* $(,)? + $($(#[$($field_meta:tt)*])* $field:ident : $field_type:ty),* $(,)? } ) => { $(#[$($meta)*])* pub struct $name <$($params)*> { rc: Rc>> } $(#[$($meta)*])* - pub struct $name_mut <$($params)*> { $($field : $field_type),* } + pub struct $name_mut <$($params)*> { $($(#[$($field_meta)*])* $field : $field_type),* } + + impl<$($params)*> Clone for $name <$($params)*> { + fn clone(&self) -> Self { + let rc = self.rc.clone(); + Self {rc} + } + } + + impl<$($params)*> $name <$($params)*> { + /// Cheap clone of the structure. Implemented as the `Rc::clone` under the hood. + pub fn clone_ref(&self) -> Self { + self.clone() + } + } }; } diff --git a/gui/lib/shapely/impl/src/singleton.rs b/gui/lib/shapely/impl/src/singleton.rs new file mode 100644 index 0000000000..965c2e2f53 --- /dev/null +++ b/gui/lib/shapely/impl/src/singleton.rs @@ -0,0 +1,143 @@ +//! This module defines helpers for defining singletons and associated enum types. A singleton is +//! a type with one possible value. It is used mainly for a type level programming purposes. + +/// Defines singleton types. For the following input: +/// ```compile_fail +/// define_singletons!{ +/// /// A Foo! +/// Foo, +/// /// A Bar! +/// Bar, +/// } +/// ``` +/// +/// It expands to: +/// +/// ``` +/// #[allow(missing_docs)] +/// #[derive(Copy, Clone, Debug)] +/// #[doc = r###"A Foo!"###] +/// pub struct Foo; +/// impl Default for Foo { +/// fn default() -> Self { +/// Self +/// } +/// } +/// #[allow(missing_docs)] +/// #[derive(Copy, Clone, Debug)] +/// #[doc = r###"A Bar!"###] +/// pub struct Bar; +/// impl Default for Bar { +/// fn default() -> Self { +/// Self +/// } +/// } +/// +/// ``` +#[macro_export] +macro_rules! define_singletons { + ( $( $(#$meta:tt)* $name:ident ),* $(,)? ) => {$( + #[allow(missing_docs)] + #[derive(Copy,Clone,Debug)] + $(#$meta)* + pub struct $name; + + impl Default for $name { + fn default() -> Self { + Self + } + } + )*} +} + +/// Defines an associated enum type for predefined singletons. +/// +/// For the following input: +/// ```compile_fail +/// define_singleton_enum!{ +/// MyEnum { +/// /// A Foo! +/// Foo, +/// /// A Bar! +/// Bar, +/// } +/// } +/// ``` +/// +/// It expands to: +/// +/// ```compile_fail +/// #[allow(missing_docs)] +/// #[derive(Copy, Clone, Debug)] +/// pub enum MyEnum { +/// #[doc = r###"A Foo!"###] +/// Foo, +/// #[doc = r###"A Bar!"###] +/// Bar, +/// } +/// impl From for MyEnum { +/// fn from(_: Foo) -> Self { +/// Self::Foo +/// } +/// } +/// impl From> for MyEnum { +/// fn from(_: PhantomData) -> Self { +/// Self::Foo +/// } +/// } +/// impl From for MyEnum { +/// fn from(_: Bar) -> Self { +/// Self::Bar +/// } +/// } +/// impl From> for MyEnum { +/// fn from(_: PhantomData) -> Self { +/// Self::Bar +/// } +/// } +/// ``` +#[macro_export] +macro_rules! define_singleton_enum_from { + ( + $(#$meta:tt)* + $name:ident { + $( $(#$field_meta:tt)* $field:ident ),* $(,)? + } + ) => { + #[allow(missing_docs)] + #[derive(Copy,Clone,Debug)] + $(#$meta)* + pub enum $name { + $( $(#$field_meta)* $field ),* + } + + $( + impl From<$field> for $name { + fn from(_:$field) -> Self { + Self::$field + } + } + + impl From> for $name { + fn from(_:PhantomData<$field>) -> Self { + Self::$field + } + } + )* + } +} + +/// Defines singletons and an associated enum type. +/// It expands to the same as `define_singletons` and `define_singleton_enum_from`. +#[macro_export] +macro_rules! define_singleton_enum { + ( + $(#$meta:tt)* + $name:ident { + $( $(#$field_meta:tt)* $field:ident ),* $(,)? + } + ) => { + $crate::define_singletons! { $($(#$field_meta)* $field),* } + $crate::define_singleton_enum_from! { $(#$meta)* $name {$($(#$field_meta)* $field),*}} + } +} diff --git a/gui/lib/system/web/src/lib.rs b/gui/lib/system/web/src/lib.rs index 818afbb321..e4a39c3903 100644 --- a/gui/lib/system/web/src/lib.rs +++ b/gui/lib/system/web/src/lib.rs @@ -10,7 +10,6 @@ use basegl_prelude::*; use wasm_bindgen::prelude::Closure; use wasm_bindgen::JsCast; -use wasm_bindgen::JsValue; use web_sys::HtmlCanvasElement; use web_sys::WebGl2RenderingContext; use web_sys::Performance; @@ -58,139 +57,6 @@ impl Error { } -// =================== -// === JS Bindings === -// =================== - -#[macro_export] -macro_rules! console_log { - ($($t:tt)*) => ($crate::console::log_1(&format_args!($($t)*) - .to_string().into())) -} - - -// ============== -// === LogMsg === -// ============== - -pub trait LogMsg { - fn with_log_msg T, T>(&self, f:F) -> T; -} - -impl LogMsg for &str { - fn with_log_msg T, T>(&self, f:F) -> T { - f(self) - } -} - -impl S, S: AsRef> LogMsg for F { - fn with_log_msg T, T>(&self, f:G) -> T { - f(self().as_ref()) - } -} - - -// ============== -// === Logger === -// ============== - -#[derive(Clone,Debug,Default)] -pub struct Logger { - pub path: String, -} - -#[allow(dead_code)] -impl Logger { - pub fn new>(path:T) -> Self { - let path = path.as_ref().to_string(); - Self { path } - } - - pub fn sub>(&self, path: T) -> Self { - Self::new(format!("{}.{}", self.path, path.as_ref())) - } - - pub fn group T>(&self, msg: M, f: F) -> T { - self.group_begin(msg); - let out = f(); - self.group_end(); - out - } - - fn format(&self, msg: M) -> JsValue { - msg.with_log_msg(|s| format!("[{}] {}", self.path, s)).into() - } -} - -#[cfg(target_arch = "wasm32")] -impl Logger { - pub fn trace(&self, _msg: M) { -// console::debug_1(&self.format(msg)); - } - - pub fn info(&self, _msg: M) { -// console::group_1(&self.format(msg)); -// console::group_end(); - } - - pub fn warning(&self, msg: M) { - console::warn_1(&self.format(msg)); - } - - pub fn error(&self, msg: M) { - console::error_1(&self.format(msg)); - } - - pub fn group_begin(&self, _msg: M) { -// console::group_1(&self.format(msg)); - } - - pub fn group_end(&self) { -// console::group_end(); - } -} - -// FIXME: Add the non-wasm impl -#[cfg(not(target_arch = "wasm32"))] -impl Logger { - pub fn trace(&self, _msg: M) {} - pub fn info(&self, _msg: M) {} - pub fn warning(&self, _msg: M) {} - pub fn error(&self, _msg: M) {} - pub fn group_begin(&self, _msg: M) {} - pub fn group_end(&self) {} -} - - -// ==================== -// === Logger Utils === -// ==================== - -#[macro_export] -macro_rules! fmt { - ($($arg:tt)*) => (||(format!($($arg)*))) -} - -#[macro_export] -macro_rules! group { - ($logger:expr, $message:expr, $body:tt) => {{ - $logger.group_begin(|| $message); - let out = $body; - $logger.group_end(); - out - }}; - ($logger:expr, $str:expr, $a1:expr, $body:tt) => {{ - group!($logger, format!($str,$a1), $body) - }}; - ($logger:expr, $str:expr, $a1:expr, $a2:expr, $body:tt) => {{ - group!($logger, format!($str,$a1,$a2), $body) - }}; - ($logger:expr, $str:expr, $a1:expr, $a2:expr, $a3:expr, $body:tt) => {{ - group!($logger, format!($str,$a1,$a2,$a3), $body) - }}; -} - - // ============= // === Utils === // ============= @@ -225,17 +91,22 @@ where T : wasm_bindgen::JsCast + Debug, obj.dyn_into().map_err(|_| Error::type_mismatch(&expected, &got)) } -pub fn window() -> Result { +pub fn window() -> web_sys::Window { + web_sys::window().unwrap_or_else(|| panic!("Cannot access window object.")) +} + + +pub fn try_window() -> Result { web_sys::window().ok_or_else(|| Error::missing("window")) } pub fn device_pixel_ratio() -> Result { - let win = window()?; + let win = try_window()?; Ok(win.device_pixel_ratio()) } pub fn document() -> Result { - window()?.document().ok_or_else(|| Error::missing("document")) + try_window()?.document().ok_or_else(|| Error::missing("document")) } pub fn get_element_by_id(id:&str) -> Result { @@ -266,17 +137,17 @@ pub fn get_webgl2_context } pub fn request_animation_frame(f:&Closure) -> Result { - let req = window()?.request_animation_frame(f.as_ref().unchecked_ref()); + let req = try_window()?.request_animation_frame(f.as_ref().unchecked_ref()); req.map_err(|_| Error::missing("requestAnimationFrame")) } pub fn cancel_animation_frame(id:i32) -> Result<()> { - let req = window()?.cancel_animation_frame(id); + let req = try_window()?.cancel_animation_frame(id); req.map_err(|_| Error::missing("cancel_animation_frame")) } pub fn get_performance() -> Result { - window()?.performance().ok_or_else(|| Error::missing("performance")) + try_window()?.performance().ok_or_else(|| Error::missing("performance")) } diff --git a/gui/script/minimize_wasm.sh b/gui/script/minimize_wasm.sh new file mode 100755 index 0000000000..31dfa2f86a --- /dev/null +++ b/gui/script/minimize_wasm.sh @@ -0,0 +1,6 @@ +#!/bin/bash +cd target/web + +wasm-opt -O3 -o basegl_bg_opt.wasm basegl_bg.wasm +gzip --best --force basegl_bg_opt.wasm +du -h basegl_bg_opt.wasm.gz | awk '{ print $1 }'