From 0e5053b5a8908467a52c7a1990721817868c01be Mon Sep 17 00:00:00 2001 From: Mossfet Date: Fri, 17 Mar 2023 23:00:47 +0000 Subject: [PATCH] Initial commit --- LICENSE | 674 ++++++++++++++++++++++ Makefile | 16 + README.md | 3 + VulkanTest | Bin 0 -> 90264 bytes main.cpp | 1348 +++++++++++++++++++++++++++++++++++++++++++ shaders/compile.sh | 4 + shaders/frag.spv | Bin 0 -> 2588 bytes shaders/shader.frag | 29 + shaders/shader.vert | 11 + shaders/vert.spv | Bin 0 -> 1080 bytes 10 files changed, 2085 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100755 VulkanTest create mode 100644 main.cpp create mode 100755 shaders/compile.sh create mode 100644 shaders/frag.spv create mode 100644 shaders/shader.frag create mode 100644 shaders/shader.vert create mode 100644 shaders/vert.spv diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..55fe9be --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + 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. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is 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. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + 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. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + 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 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. Use with the GNU Affero General Public License. + + 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 Affero 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 special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 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 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 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. + + Metaballs-Vulkan + Copyright (C) 2022 Mossfet + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) 2022 Mossfet + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + 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 GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e86eced --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +CFLAGS = -std=c++17 -O2 +LDFLAGS = -lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi + +VulkanTest: main.cpp + g++ $(CFLAGS) -o VulkanTest main.cpp $(LDFLAGS) + +Metaballs: main.cpp + g++ $(CFLAGS) -DNDEBUG -o Metaballs main.cpp $(LDFLAGS) + +.PHONY: test clean + +test: VulkanTest + ./VulkanTest + +clean: + rm -f VulkanTest diff --git a/README.md b/README.md new file mode 100644 index 0000000..7c022c7 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +This is a metaballs project I worked on for fun, with the Vulkan library. It was tested for a pretty weak Chromebook, and is totally broken on most other systems. I might go back to fix it, but I'm looking to do new projects. + +The code here was based on the code (and knowledge) from the [Vulkan Tutorial](https://vulkan-tutorial.com) website, the code of which has kindly been licensed under the CC0. This project is licensed under the GPLv3 or later. diff --git a/VulkanTest b/VulkanTest new file mode 100755 index 0000000000000000000000000000000000000000..6bc2357bff00639170841c89c41445391a74bc55 GIT binary patch literal 90264 zcmeEv3wTu3wf_VXiHe?ppsAvccC3k_CO~YWq6s8&Mkbi@GCq@#Oh_azlL-U_2_#X* z+skPozy{)yjQuQjLLc$w-MSM}U5Z`BvN_^up|KD%zb7oE;)7$obe&4^}=h4YN zYwfl7UVE*z*WP=beGW_WeCH2|iyP{)uA#1TU2(YY%vXqP3%_eNvFgfpWx5V?WxGyx z{mgX`Q1SR>tE>akT|-ocohDjzY>?%Wt(Nys$5?cBYPZgmPk{D%O&tO zn}2X+%il$d3kOFy<4v{Pp}$|mI-RW9XF)dM?iK3L)PL#=u(J}EAi zEBH>~3l3A|?6l63r=8mJB#-=MTJqdK%~sV#y1jq7Cac{3X|7eyPT2;p8Sezj{+;#; z>~gQDdNTJ*2{{FP0v5Ssk)dC2g1MjH=2xXHGbys_e9? z%9_Te)0#5RJnhU0r#IA|K2BI*u{8W>Q&XnRat&#AbrNTnk8{NdY^m8b#DeX7(vHH9 zVrTicSNQI~>Du&@Px!EHeOk_W?H|8Vwv{yO2g{I#7jgXA7t8+|KhjV>D|*}|X|AF~ z;_k%nclb@pd+CF?Os`oprBp6cO~A9{WKOZ872bk?q`3diJceY*S3F?DO|+kD4p zUw!dB$Opa78}4$A93Pf-)9t?j<4E|q+j0dCFV#r~6jQ*~JK@3*UpJK@2V~o4O z=oiPp->$h_Ck|o{yMZ69o_Av47suf9>=<(UWehv<#i%DZY;b2{0A}Gy)=e?Uy33B?_-R+dt;32j2Qgvh{4ay82SpwsQ>RV#%XR0{+GwV-yOq# zYGd^G&KUjrO$>P^A2zt%UJ(O-Pz*lD$MA1|jv==>G3q%thF!fEgP%1q`jr|(k3})` z^_v*{?1)jnKL(#4$Dsc-20tIikk8Z@e&CuIdY>D^|BQ+;-an3EC+Ehnw|z1C`%;W{ z?~5UaPo3g2uMc|rW}F|JOlo+PW-lfaYSZN*EHE&4Lp3GvNV{KakIZ?)RppPypL zhxC0`yEgrA(J$gVta_TQdJ2I*(FNrfC;m4}S%67Smisn|=))X}aO6mhe zMXn<6%>1G;rtU>#dF~c7?zrLi}Kc}(0 z++RPVa*=;>NmbRHlG1s0x&5*Qo$GH1)YmRlRSL%9t)jNEstny~Q2Z&Zj^-3mNoByn z9hjQ#zpAma-e2aefskrS{dr9Re@#PWZH?l8roXq{1RYs&mh{xTrIa6!ewhRV{Cs$Bp4%2L0SIKQO2vT9*LeQlk;K2WJz5PjL58my$H z#q(?Ht4jh6Q}yYpy~(w83nz&x09#U?QBeY;$*(PI6s|0+LoF;S4Nmui)%t>xhG_pM z*H%}T)RYy})>eTmucpjGL>fV#Y(CM2(wwrgdLeAA_ScJ8+0Tet2K3V6#9Li5*FU?` zzX0WON=2X3{7nHttB&PVRn?ZFvV4DaZT&)!P4_qW1N(DgcXvTaU1>#0WsM~i=^65( z+VzzztZfXSngjG;4IIosdkVmHuh66%#IA(7K_?}f#wEimu3p%Ip2>#Lk12Ig<@wg zeMN*|F*=}LTU7(NkDAk9RyV_sE>+al%a9Rm%^0Y{NpN^I`)eBvN65rfECB=W7+dJs z`St#&;ixRf7`6Ifn=t6bwjy$|`!k?<_QxtHgi+8kputVg8bnHIwt6DQD867u7y^o_J_XTMMtReXSgM4kop@Dw%HNIt;EEp1su z@Q`Yh7F{xJM&L}koGO2Adft3j(IqniIL9-|D1yu6IkDt}3R9&*MNd21W^3vgO0x@6{TASYO* z%P}qbOR5L0Lba2L7K%Xa&mAX>&Ge#ee^aTyE`V7NG-Xu<^Q#r@nWf;jrgnVM>~ZPi z8Wv*BP+e4tZp|BCQ@cQwp82mdUYXJ0_s^S)UQ8&jLr2roi;Ak}NJokSd8*Cy@%5N4 zYOAqMq2tdg$}gJh56myYsshWI(x#H4xs4_DWkn^*(W4MVn^iP@4us|R7gg3^eGw=^ z6-D*_IVDw`)8`a`$;=2z4b&tOn2}aAeFj!V6o2}7CBgE_DlSC^6(Dqgb%peM+SD0= zjMBiuIzN&^yGn1d zT9#fqx=>kDTJPsTjL|dIZba1+xD2xl@t^FgO^trS;wUZ{CxwIl{=BlG2y2a+h(^S$ z(dNwA)6!w(rA5~$}4LsMMo%nrak25l$4>`hQL4@s`q11VWlV*5Ruko85qJ3 zR-&?QC~^*ve^P=IL`2XuJ0WBCLddKd#>ZuOQ&SOEYYnwEC79e77R|?FG-ZYZx@&1c z4CS9NP>~Kh4d*~NV2y@Bjd?oRQfjrRy)%nurWNJQIJ2l=>WuM4%6MzX*EI%|T{}%4 zJ<3-$k2bibw%Fg_RBSVvQJYa-)!0xGA^c|?R-`bk!08mIW@&c%R4C);VsYu|>Ej|p zsj_D7V4a>(LRU%yoDryMpmpuRv&gWjmLu+PQF!(dk?&FXwFJa2vB~7GT{`gvB8iD3$=>sxbT)`CZli>gwA0 zMB^ktskL9P#umhdfm~W$XVF9aO<3H48oL)}VI@I`6in(Cf)LYb>Aa%Sig`ulC6!gK zx_WGW%U!6K`*ofv7*Ak+W7WKp8iMHr3)9nif2Q0wm1mwgzdD-7u~$!UoMcxsK0 zrKg@60i}pkF}D<*h*VIjD2n{pCYON2IhYQ>WqrU^1QocZOrBgc{`5506rXp} zPl(*5M{mbRZ^xaUp(qLqQEtNN>FEQ&EZ5}1!kkIoqV&_ppPn@!D?QVZHy~%+z&g?g zltYXKCn|7yA|8KvH^lmja~E>QzX`ZM2tU&CKO0gdiMOc_lJY}@dYFZeb0tRZSOZHP z?7-M<9ujHKZduiPsB1V*NjY)kN3}?96=yaFDo3?IPT881-+^>Pr8H|ukeU>A9O6*K zu$+>IU5}j-t8(nWtarY|!dxylq+0N^g^0Wz?)p7&yy76?Rx6(|tWlMfh^QUv`iq6T z<)&XD(wOL4W5Ev#Jj}Jxf*X&nM1*Xp>si3ODAmiYJi;aWZbO9eFxRUVF1R!m@Niey zf)_sj0Qfn?^`!+@2>rpXcmy$la=q}tJVbC0avfp8S4no^Op6gb{>ngs_^S0chU z#B~l}UiNt`Amv&;O{Hm8oX^HLPgVHL{qV&Wy}lnl(>}MgA3nQ4)l;}1zQDp4?}tya z@Yz;LLCX!Kgp0ta|pxcWT6_vqeWKQu({h zfj`ZG?{?t%jKscnIq*RniL}Roztw>^9r)WE_&x`Il>^`5;D5OT-)zYfMk42-?5qw4 z{&zMKsoRRzlKyT7-i|{P{~HJ1juR8#;lSH*XySkCz}sWM;1Pw7Tq_)SJj#h&?GC&>CYZU(fwx?Pl<9Eb zozGeBb>NSP)b4VvcHnuw&%V|;@S|-c(oP5dNC$qK18+aeV0O0y{}TuOE(iW-2foLF zKgNMK9eDeUEwlR^_+uUP`yBY=9C+6VWv#T4;~n@!2j1<#CpqvZIPl31{D}^{+kyY7 z1E1o+dmQ*Q2mT}nKGT8!nFF8gz@O~E>kfR117G04r#kS34*V$&e6a(6ssmr)!1Gy} zebqVer`t%RO%D7S4t%o%pXR`?aNyG&_;v?=oCCkgfgkU{cR2799Qb=3_zVYrwF7S* z#g^PP4*bs@^qmfTrUSptfzNW_yB+wm9Qa)h{MinCj{|>>18+L;6CL zo&)b18MXgx2R_k(&vD?B9Qa8Ne6j;S*@1UE@VO3riUXhLz^6IzQylnA2VQsJvmJP^ z1Ft*q7dY?*4!pG_lH5WE-shk%cHr|J_zDN!T0%)~odbWNgTBdux0Xbb+w8#q!a={n zfuHWcw>$7N9QaiZ{7eVF!+}>PuvO;04*YBf{b~pPA_snr17GOCcRKJFJMh~a_)8r4 zZU_ES2Y#0Wf0+Z{)V@z*ji%wGMop17GLBH#zWEIq=O6e7ys|!hvsa;M*PefCInEfp2u+I~@4=4*b0i z`~nAlwFBSez^`%O7dr5r4*Vhqewzb-wFBSnz%O>-cRBD&9QYmw{u&3~bl{gd@O=(^ zvje}+fnVmpyR3Z&*FP-|e4+#2>cA&C@NEu!vIBpu1Mha=uXEs29QYLue3}D)y#t@= zz#9&HwgZ2I1Ft*qAqT#|f&ZlgU+BQ!=)e~{@a+zKg#&++17GLBuXNy>9Qd0Z_+|(G zR}TD-w?9hYM+y8WfgdICqXd4Gz>gC6Q38WY;8X3mul1G>5_Kct`RDL>psh17q`zBl z*_60Z3iM~Z33UHSZ{x3ad^YX~%g!p?(~s+xK7}^2qzLQ7Wj6;3^|7j1YSw_5W?94Ur+c@ z!f668Cp?_6Ti`{68Bz`>30zP3aKbKuuOzGy?)x5yljaakBHSbJC4@&1?iP4D;gN(p z1-^jr5rkI@Jc%$v!{H8r&mzo_aJXIIafFW~+$``ZgpVRzC-8}c84?Z`3w$(ThJwQd z0*@rj5O6qK;6n&A^czkScnD#Je8X;mzkU|*v4oQZ{)8|?ykVEX?-OQdH{ACf`~N0k zH{l+EUnP73;ckI<5@rZD+$r!5!apUvTHpwlJF^nvjx7M@Tr8;1YS;fEMd36iwK`aI7#4o!lx5<34A5t zGYI#6EBYs#Mz}}dO9-bE?iP4D;cjXZLFhix`Vu6n)%n)g~K;V&t85#{|3w#J+hD5_@0uLd~P-xgK@Yl}( zo=7-J;7ap~cM;YJ7YlqlVK3nVfmaf~fN-|J*At#fI8ET? zgnfkF0xu$*PdG{7dcxBPy9B4c{f?iBa}!ZQf3 z7I+e2h8V*g0-r^gp~Y~!z~czdCfqFWDTEnH4A%*KB4LIQ!^Hw0O_-s>aDl)h2{U9E z&KCF(!VDFL(*zzum?6TjTi~ys27Ec;B!NF6%#dK%CGh)%843*deI@!QTuit};8zKk z5bhRuCt-#H!<_=}AY4j#wZIz)ml5s|cr9T+;dX%^BwS9oS>XE!&m~+Z@LhyC0fmbN zzMXI-;R1nI626jfw!qgDo<}%M;N^s?2)hMdM7Wx8lEC$ZYY4jpzLIb);l6#Mf5LTy zdj!6O@KuDn1)fg0o^YqY7Z7eByjtK%gc*7ZcL;nIVTRno?E;S@JfCp0z^4#qh%HBod^BN()WQV+IkHD`IUPicE;GKk*6Ydmv2jLdNs|DUbxRr2+z-tM&5pEaw zLBiJ(ZWj1H!q*Y56ZkH|D+m`0d^_PF;R1nI626{rw!qgDHVCH)yqxe2gxvx!A{-)| z1h`;*B5uw9M%VSw=;MZANfgS^HX#Xa)k6u#Zs6T8^K(p%dW#vS2fu5~F^?iO@(ovi zL6RPtI2Tpw!7b)`3?OfC6Y#o`ZF-IS5D1LyG(D?3FmBx;tSGp*zgt_^=L^1PmVF^Cm`{Ke za~SG)GLBs;#9!@^yve=0*04nH^SUuHOzw;f>RC_yUT^CUB$#>Nlw4d+Zj6cVDw4fU zk`pbGvp`aQx^_HO=yK)EEZ6pS<^=aWb||1(dhnCn;FcNsvJd#;V3}^jpOgZ;9&Gij z03y9p4?W>Y!JU?x>nYHKZ|PfeJ*05)rdZvydRCEnTw7Z`9oDUmdk+fo;ypi1!b=;G z>uJ(Ppnd^RmT79MhoWZ6;}lH!0Go6vR+?$0x|NEkkk7R&>6)x`1=3ITKS4TMT0O07 z)jSHrE&W5v>pLvmbVGHnOg~liw()R%*(UZsXkL1G&Se|T+u%rOAHnYMR;($?qvC7N znUynhmL59o=^gl{5)`V3j{RyI>TLO-$sxU5PeppCFI479@`b8Ant835#l$njEzN$37bB!x*J`%$PnRzkF4wBMsDvaljK!uN{0LP% z7tgy_X?SI-A`q@c|6RV&NuO+&`d&pvFgQKPqJNR1dr=0BnjBwRO2Sv5SkDPk-=4)`O!nK#qZX91`FHG_}z`Aujl;HX=zIQHWo$TN_c4 zrj4jWfrQT>bhDO&$Vf_Sc3;8Fy!6iW?Wn1Q%eT>Qb-7%hMN0tx z*Dc^LlyD!DtbKve=<%~4;@0or{vPf4X7+WXm4bem&}S@S0)G+g)4M)8Pw(0{RFB)D zKmUDT6e{?wRY78ZPq}t{ZnQl4Yo6%eiYgn&&(d2aUI0pn=bgY|@H2;_&i>JvK$)dr zc1yxJAdI_wW0c>Gj={qHC+iE(1+B$j&P+Y^6L0V{c%JVteqO-9Ng6P465(@#qda=c z*V=+fx^W(E;u_PnM-EysOgF}ObYls)xjb=8{P7sbxt_$Rawb7JNiu*nTueWy8F1~l9)B?_^z-C0$$Rh2o+s%GSX z^3<(M`V$pzd$7+TZ1yvh@F6C9sJ|6@f~LsU&tc(Pn(6!E$bh3nJLnI7oC+Nlz6w(h z;jh+OJ;FSBxy+1v)+M5xx^ zb65(~zHZz<)SC7K3pld}eyXi&oXpwdfsa^jt2yjrq!4U+=cTZH)lXF0EEVOs)MOtT z&HcT-0_a4$GWqvDV#y`p;|*-E$<*Wa7z^Aj&&HYOL2u!!U@hk7s7mN^F3OoLbIuHO zCq)k~hTCfE*IMt4V`2DoGys2<>q&%Kv{rtmMuvsYs7uao`$B6?2PWUR1^`MO1i)() zCF^akX|1av4WChpoVNpIUZVNS?2nVE2(SAsuZ zaBsd*owP0mB<5q3hJh>)HooFBu2gtE_=A>Ei~XYDatSPEIE>A~J` zBkELgBp-^1tE4L|qRrmm2jPoB1m^ABeluHgUki-)2KR(dLN3RHG0ScP(;WFdnp9+u zE};W<5Mp?6rMwhmQ5*Y)aP80Q)K(+So&Oo!RpW_d=%9=U$6iMDEV-UHqmD^DZM`8)N88P zz&vVGYyG__(N867t*-#cHv-fYEF(uOqlRh=oKO9lMGy=$Mb+xYB23p8VD&De+adH~1M9BlNmfmX~CM~}mSm_JC6Mh?7@)}FQ@su3Fk>bNsn$9v{Y03ao;wE$e_!=SGcJMv-Z8VkXE z4!VA^)G$&v0HEs}*vV!g8%H~d!j9>+c(JVU6Rc2m12d**42m)?u`2;!Rg!2PA(fot zQFSC(bzn@7@q~ZPDh8UXj&%@>U?5*3qI|`B%&rd=TS=`&j z&(Mtr+Qm}7@fm;Ma^IKrPT)9OZ;i0~tJBhV>MffS&0{fGy;&bL!V5pt$ug2e!RCX5*orD5h6$1{#$5T>LP9F#)K4ek11W?PRWunvD+1v`y>apl-#y_ml!zgNlyN~ zf3@}V(6(qZr;F4fj4#Z)rOz0up^P<%c5ou$)OrpX!HVdR^qp#7gNGAFgBuMcVI6j- zbQiZ@m^TZ@S0|bmfF1hD@J+z9=UgQFQ_OkZ;CDGQbAnI%f?czG!DnXpf*Y|-T_wJ6 zX_{lrXm3@yvz)011D--X6!2uj`4^|}#4eSw8yC`SZVyP5%Rgmi7kU6E+csVVF&-We zPVP+~HoVOsG*5hw!i)C+pW+LCPqt8rI{|sQ@S)g{bsq}e?h3E zH@^3)-Y;3%Fj^dp;{j9}6@^;gSn&uhuel-k6!eTnfY*U427)_-cPa7st?`~yfZ%(h!l&9bZFC+~>8VI3X-CHQAaYX$L;;n? zT`zaKPa!l%1?xJTd>yfM1Pjd}BK=!CL7V;sd-*&>;R8{HfZ#$#Y{=&+mMu90CKVu- z+KSDeO7Z{cVe#$4tJw!F86gHx>>5&VuN$}0?_KH2Swp_A#NK}Rw)E}hNYPuy3yzd?J(4gRJ#EGM=vqj`^uEPKz}}tsy{4a< zFbR2|>e?yYpK4c(zI-77?PA1R9%3*`4;lARq`^;ja4PeoJu=1;|rC8pW z_rJjM)QU#<_t-+P1o={s{~>%lLrz0R@^R-kq|b4)Ood3<_s zx5E+;K}@GJ_JKwQYJs^473X3F=JN%sJSnowx(Q;R8mg*x>0R#(rTghaSHwYV%}!!T zZ*Yqq%1+isVAF>oo#<`(E>3H$M=@_``00Aqw!kUvpXO+_hr>2Nr6VFdJ4p`~K=ECT z?~4546jD@So$Yd!mgEb50(Q-D;K-L1@FZ%j6Hz7INAZ?i&rczv&-2pX2Kyc|k^Wj9 zLz7tN_5Kv{&>jIvOHW+R7n|Y&@vWVWSF~)zCVq3Wo>k>Z49xZ#7rD7MOBQDk+$}AB zk>`p-eZ{OVSIg_gug6@D-oiwRF&W>76`4m0kb$_~yP^Xw=LzuU0-Yxp9Vp0MQRbOR z_a=O-$n{(XLzxLPO2ur7T?1F;0T1TGFU<|;A7xO?%~b_?LIm{G;SxV?EQftigN{ka zT2Fw_MDm%J{zdp1Sx7e%bV_a?Y)0bD$;b$=0g=cJLW8JKUgQ?mv5Gc7Boo1ny$Sp? zdL0Xpx1R}mk9jUvl{J{!4RF*h#t5l|y)uytetoTw;hYtc)>EqG?0h?^-;ehzg-=Gc|GL}jGdp`$+>j9Z7Xdb`^Fs1RUX zji--h>26xyW=>@(V2}Q#TU_M<^7}P-h$+9Ei}c|0`r2onR+l5#lk79*Vuyc*FWBQt zH6?zlJ+MJvJ6>Hw^{j4eC1Dx)S?|bX?+s!Z@{O+Lbz}Ju*9|w6t*zYROMPF*ZolnM z>=KtrLr^3yy+8d!%rJ5IdGU-QcQjmAj@uV}!(gJStA>31a!BPY~uTFyek{yG*-N&#RA}_-|KDd!YbBuV2;Rt7JUs?+LgLmBP zOZ~`fh5UOT#r`a~!x#L*2aQaEKv8?(uHIengWmPF-hDv`mz(6^D35s>imA=6U^aW> zwuUD_{1N%eJ{dKlUbFA-B#tyW0%gNFAm)6fuZzmDPuHew@rBN5_IABDwD(46xPKlr zyzF(xM;R$OkK=C*O7hYoAsl8ahK zysTYX%U`I3tlir3X{gZ`nsBI|^*3#~39R=?yT5uU{$R{KTFaTFfmvliFg`w0H1ZR@ zahtrst=_oJUMzu8%;ih%(p!4t0zdPH5FpS)qyA1T*N+g8FNkgWtFN&*V@MQP+ji|n zE~D~k3f!Yi4aI-y3o@vl2YLj41}aqFie(d+4qbquf_9Drdwq5O;;|MEVmq}Z?JXML2fo&S*+ld~g^5*VD|eK&>Ru(SVze!u?P53b+a(8Ld--|y)}2kLhoY$=9*TPmR6-d+D``dtW%{6FaTXmt1o z9>0&i@&oDjIPt&(#;*ZeilN`T%c0*x$;*G#`2Dpg=->9wXTAJ`>-UwHejxpxOUFNO z{Qe30i=p3*Wzg@b$F z~!wYUX^ zGe>r4H;us*{Mm*DDoST^{i?U@OVqBF9d^sUB<;G7q=BpPcnncF(|lG1kWWBWKI1em z4!81%&U-%O7N7}k#j*F?E}>N|6`L76B*OMeg8K|q$o5k=;#06$@&t6OTv3X=5-#JP znc9dOJk8Q-e2NqyfT9IIB@!GLGjLVs3#zD``59Ht$1#V?;~05D%6-c^w#6vhWD+2R z%f);&E_WH?E|jigOJ=Q;LzD1SuuE%Az|vR`wota=udo`n&kf;x&^i%S=y3^Hq;4^b zaUe-IMCjpXL252Vn<~JY@f@;38HX2-hJ-`4N-PygWhJsC~>Ao~3!N8CXDegpl*(C-m=3&Fhl zssA+n9*iFSAI9%}JAQEePC*kth<^V(h4$H10i=y&|Z(C=@y|4aI{p3g|+6$dD8 zEXF*d8@Dks?wHXr?ebwQr(iYp%DFU5z4H-Y+JK4Z`M@wec$+G0G)Wyd*D-OFxr*@q zWo}l)M!PDrV!txST4l8BUcfBKfHC4^2ji9#`rEQb;E8NEX5LZ2at5AYrS3!pb7sWE zji=05g0NrekOWUN#D&doAx?(Qz5o&Og=aZ3V&%VX<-e@*g+D9*BbI+1!-?e|VSaRe z@4eZchw(%uGcuoRZ8*COx=p$jqi(mvr?t9e9>1A#2#>`K*o#%x)fUHz6{pe$hDGgq?D*;; z6<<9WZS2v5`y%V1{o6@>Sr0A#Il`_>_7Akm^NUEF^$FEo##!G%ob?@V*V{wGhXJY9 zgA!-`TH>q>(>Kz~i{k)fMOxp0io<83gv864i8$-Kh_ik(6Mmy~GfEsd?|W37^&YV{ zJ|CifFLdhn_zc$QLbl;|5xO;2&tYJ)+)tF{9Jb&VT(A z?I%vM|zfhAo~fU2#=T+xE+&3t0x&6;wwfoanc43w}NlXRKRNx zX!3>b_cWsozT2|`sadiKdc;!>H#*?PftTPOU+`gfOzHVMv+ zvxY=-F`HL;y790usvBynM>l^t-VRX=;URe)XSb~98aevp0R&|^!qXbFrsFu!nDL)n1T*IBy@@-t>`#hkQ znbYz;j(qEaU+g&tLP`r2#37l0qj6&(uS7gST>cy6r+1d;Xf@B2Yc(4llXqxv29CQ( zVqXUKHNL=gkF zpW&87z;fEi4c_CSdqK{(R*tj(<}(-_)bj~wjW#p}9mTR}Nm}}Lyvt$U*~#HHs}PUw z3ee&h_APardK}oNEx!>h!5X#YYBixsvRt{8Q=x$1)<#Sz7H7bPeM}ROEGms>Py9$pd|%nJWfbZYhgcH4CZaB?AqlY8$TK;5Ox*Kg#48D7Jw8dtIy{*uoNG0GaqpS4D7{d^ z`PXq+$st%o*>JK4?k>fy&a7Kc$>8unzj+eranyV5;ZpCHW*KDyQ^jyu#2V#widr;` zqsO~AJVKUfPz89>JoEAXex4{C3Vtc=7fFM4D+c$90E($|W0RA}q&8xx)+(ol@jUuB zBK+atYN#G+!ioKcsRfcqIBg^As7|^3Y}erwpra=0$fC_6Ekl!7YUW*liD4gDGKW&m zF#FTR_W5TjPkL2ZeZ=^A1O2f~YG!;A_n$B-^z&lOlx?d%f%foofdyg1Tvvp?f?% zq7la%+L<6=H?hsQN2ts@xp2Wj!)$rG4VDezng_$Z>G01|Pu6<@yv2bTl8WTZHQ!KU zq0ygv*~dstcM>??s@SM1pn1` zlBQTV+{U9OUbyW+as+R&Dnmk46Y89+ZYc%Q0&6d(a7z^16){^WSCq!&D)zA%w>-4H z&6tei$*rDEAZNPT!gu3kuqq-Rwlcbr(ke1(EiBF6b7e#GPt!(W-Pr2MBy1$yI~A23 zaf2+O@xJ%lEE zK#&%_?ZE9(3Y(2v&T^mfVbCqOVTVQm$@CtilCjG|5$5uwd>u?!T z3_=yTAZ4HKWeSmWk`(Z2f#r#sz5YgJT<~iAq2Z^I0<{SV>&I<(^mY@9^8&UVw@0g6 zTJmw~w$)|}ZLPq)bm21Gu_IBL~m$PZ$5ZsvU{p zjt#|A9Od&+h?~SGWZl4La9=z&0*(7ZMkf-QX@$k5^`3=t6UdYAK+v1u7I7o8!fB0i zNo0yF>coEJi1cofGC#%2XRk%%z-8jTT&v!M7JE=^3`lYN8E%omTk-)n-%tJ83m_dh z^NWY*u^O*pOw0S*`}{%HGWwcjltRWCc+;-F}WjjDuCmAKU^@mrMOTwLD!l`1$!75ww$NK@CVf=8%=msthNB2B%|{7}Z# z(GrEK#2d}R+$mOxanjUYb1hrIo+sIC$Af#`C7IXq4paIx(1fd?0r}(r8GDI;@TO$+ zZm+3BFugctxYs}rc~gG2jV!*rm4 zETQ@88@@k)ZxgoSuW}DBB4r;TBo0wJU#2})y2(oKL3)7^|0Z7QQ$v~_jANCPtueU* z*iPKV;Rhs3VbW*zy2#s2*=bpl1ZR48@ufbDhw$CR@M8O3OUJ#TtI ztYP7Ulr1v1DeQ%{*IkNAvA>-47?mL> zalBixvFh)a*Fu(kz@a_BaZ_7dM_BsC%A*q)t~a*np(@W<-t9DQ>p*=Zdcw;5JMlfb zaUEATy0H`+jjE^cdr=Mog+_Jr)u<%%7V>T=pNTb!akJE8v=pM0HyGB9;obUH?9w5? zKmMt|f8wZd{!LnI4MqoD&i9})9+D#1Ji^#%cV-`B%T`5Kr%4}SRl*0_HGlEfXm(3B zYCufJIH%hTV(8h$1P>ixnbD1C$XN3R1cQtgwj^W8xS0%c*aq;T7sD~e)tS-~2aUZ( z<%qCd4eXu{nipQg*r1J2&Oyh5Hr8U(*Y2tinxYE zZZja<@s_sDTUHB@BUeC_p*T=Rx{gt~TEO8O_~f9@JQ)48N8cGJf#HpvXYB-Rx&rFK zLN0oh%}(MN7CbZm6obRGFkB^4G7Lkl!aKf}HvqDLNNoh(CO1=H@uIN6sENHO5;&~+ z@m6TgrM0%<@0Jz;XzA;|w58k98Hcy{f+A^-cUqrgye|ic@lGSfCuCs9QZa@(;+$%z zyzoaEDr3(kYcjZUh%rX*SO82Y@kl4b1096#I>s6?W*A9Xu(8adN*6o8&D(} zT*?B;{0ppBl`2!E-Z?AM+|GMZ>c#>(ne2LJ*#5?6%yUaH9*ZPKJD)(S91-_}))(BTrcGBX6?oE&iAlzZoDf}!;&Kgmfq3U z*@P9K=}X;*`j?as%*C>7A>7Di91fSVAB}hGhCmD`wroPMZ(s;4+l4408N+In%RRgW zWq$GpS@S;}X=U;4KBEC|bzPmr7b)xH$(?j3=Sd2tSDrWe_{wX(F<)qW`Np&qU)D1| zJOB)*yl|(^U>Jw zd^EN$H;C0!)1$uNH>fMA_d#nteGy;mjl9*Xb9@FD^KJV3W#s~o3bsEQ;Hfh~g!g;Z z(gvb&nFpaw;|?i6qk8XPZvQ6yop}fXQf5u&NU`811l#yzZ!7bpsbaka4|tP(aed4w z<|ot+#W!m9AzHcq_f#1IRn%@B*gzl>+`MZ3N;2@utDg0O)_O$(Yr0GYwX!LE6iCK9 zzC;$7S$skY$HDajUggKDS8(C29~4kH`OjWrHcS0V-#V#}U0v1{4^&rpD8GFe zx|RS-rg&SvM-cinb^%_kg81Gs(MPsKX0kkQQsf5Kl*!O}A?K6D=-@s|)=2mx-lg{$ z8Mh~d2vj)rVXR^CU|5Ww>}$aa~kxF zsS+P(!(!MMDu&6W>LDF<;ejB|TDaw#MoUqUOMvEkDEN=CF~&rY;4(WfA^IR(P7jc) z)~a#Zfqo&z%dmDfD%|*D5(Y2kIee2t+QkQolGMx!4|)mS2*;<3QoP0$?tHkB;0Kl` z35=9I92CCSJQ}THupJNI!c}N9U(U?hw!ee<1IEJLs5}`$1oiLny&-S#b=p~Sb}L@T ze4F~gh94i}!+^IQ2CTnZsy+!{l3Syy{t9PymmDi+W5iYP2=6bOP&T*?{%=JMYk{h8 zqH3nc9l+B^b{0y5WK}6z>jqj3 zs>@{P*^PD>i%k-)@tEYWgOH2IBe3%haLN#`HbZWJ&tf}Ai^-S~hb zOk=ZrY0B=fdCGrV-)$lb#{$q>)Oso|KkK{32XteshtY7d>$9Wb?4Lx#Kcq=5+bp98 zFWjdDYVB@^Pl01BZ(k2eu(w-lyM$$+JQ&yC;3>^FJJ^)6@wo7Bt@yt0?1yC(1n}}A z9MyN`YiPk|RHXQfV%XSsUhVu3!=K92&1Te$8m;9`%M)s}Xt!(>qkln~Tat_!&U_SW z-{^;@@OeA=;FIIKQ$Ir^>#|WDpKJVteZp}9yePUDV_j?GGAg~Z^);>SHrzEop*Dem zGThxk9s-xNb_Qm*z808H1IVV)!r?*RlkpinD1VF$EBIV0v(9`>w1J2eDswDx-r+VH zQ7K>@Q7OFzHNx&#FAT3(Yx~33U=J{w$WH0K(^^+}ISjL)@b@91S2*)wq`wq8?Db9 zT=p&Ue8FvA<%SUo39S$#O5GCN)@2S2c73YeF$qK}+F;=u%MIm_7+#NmW3>Tm*eWJwURiAW|Q z6iSFPJQAi6-Uq92c<8n(tbwBj<9k4a{b<`3}}H`rvuMgfgaAFk`QI3GD;%S-dy1F-&`8x(O*>`RVnky0)-nDC4%% zt-3av6I5MKUMY1QBz5gE50|>O_Fl;f@zI!#;p?pme+v5wqWscx2jIfSk zYcf9p=UjRnf;!AbPF=PQ=zjqqzBBNg60wZeE)?ipXYTlos>b>{-=8Rva@rhNcr;Q+ zJnHD(5CK0&6r*=JH&=&I2JfOb^3SD^fxoe9KrRCh_#T=!_)7GgWn?LhZw?&zeF%OL z;&fQE{Y40zzvD+CG<*?aJH7}3lhjsr$p;~}Yi)N?A{<|D;3oYwep751z63EMF+cUQ z3q$9)^ZD5_(`)AoK@%shhq?`E?GO8pNUrR5V*Hhtw*?d7g;3(Qy|Z(T65# zr@(Y?x?SX*{zXK{%RWdHKZ1u19GfzRgL?+-RXBpN4u?}uG7G>N{F5T%tis9iAxwkx z<(M)Yi+p^IWRF?Q-dc-%wO{dtn&rWiOf6VlwI^YQ8nDdw1vfA2B8^(@ICgNbI($7HUo62Am`6vl%_mR| zC#7bT=E*0ZVa-*1&YTd2m8g|ETPKBi`L{IW%dp$QG6Emo!g&X5nnwSs3Y2gX5};=k zO;6p$9(3@Q5n1dDIUhwP(S$0Nz?@t-EemGVqB#YFfg_!z?-uVryT^wGct5ua?R1(8@Z7ewl2z=CK;<2pUm(#Lsd zZI||KC;!*6Uee`LH)_Q8AFrR#jWbX5Tk8e|`gGqvsoMv8V75b9S6pZ|(yd7t{ zac)rl0WyU7U`x}C!+Q^l`V){DGu#NlCTBgbYm+7?xG=z-r)CV}$btOj=y=;&tn0tZnymmoF%HiP4lorwF-h zQ0K67J;UMQQh-b3r{TtGt&8CF;l@^@E*_jL1IVouU#}{}U?}8ZpeH2(Jt&UXKOrBX zgOhrgT!j#1r>BO+DhfpHFb5FA+QZYEL4B@=p}Wj8Y2(2y@HIKwBi|xC0+|NScbhdg z0goLe&Rw=_j`wAK7dVd#0ddkFnSC66_$LOidF+7+=4iKW@@2iR-O`zpxT*2?#w2$Z z>sz_0_x7j&>C7v@#u#iM&rAQ%m({Da^4y0WQeGEd&Qj;R$>HSkc06dfMY-eP9&gLm zlpJluMZ18!!_x!C0Tr2au6+Q!!72NEh<#?73#2<65vUC;Feba918mFg5h5NP;p7M3 zPbZI-G479=qgWAP3jM4Y`)$Dr@P>3yhL(>pgQrra}R?`hQ z>qIVQq&5)ip@%)q1YsR(m|`1YjBY^=I>ou1mzvP-kmx9iuO`Y!C?^r3d+3^RO^WP4 zZjnGE&dXuiAhKw`$jpK*%ggH)7FgVP-jR`C&C^Q&7JEfG{AP}xAe z3lxtj7V~(`2@Itqn=7%4fv3y|T7B5v>{%)2ZyNW|&*2SrtP?Ol07;E<3`M5P#?vrW za;P~X9cmqAiwdWI;j-dA^6_}{%$x0L>4O_lT`1$p6DcoDOPkG0h%rXLbvZDhjIoz8 zW3M?u{Po7(ozI-^6B)@BCL3}IJ-1=YY4yzK*^Y%X)ltk9ig%s3f z`y=iz`W*{ihHD1+3PyF{E8hN$RVFO)P`tHCv<+(GGl&~p{_0qAX@OYghZ4%LP$U8E z6zb{$ovdR(L=>}V%;s>8V6Kz5568S3nkx%!QuBl)j-$Q-hu_~#&E5+FudyHzPa^Os zX>aI4w|O4a$gJVWVwCX4_o%{t^GocEnbzX1Hm`AEVZL!)GpfY5GYe3<>44(~3l0JM zd=Fz9Zqh##!;c`lnz%|ImQ$o zRG|#;Kh2lL@X>i5R7tk5xMigz&_lybfGHMe-T)<&KX$wEP`gi43gQ3kgDQH~W^K6- zUV&{%U*MAQq6=Mn9Zynppk8n2#(UX59{h6YMr&5>0fV?+LI4J-rM7ySfhX@Ab^}kW zAYPYn1*!O4m6JswJ$fDQFoRhqE$HJZlT9ej>gWfVvMr`P7R+cY>Fxo{S)dFl?WP@x zeV~OR;RQ4$Nk|Xk{4Q=#q5vJ{!Im^BvyqHX3EH$$_6(@|ktC_x9wcsc z%VETZdE|tBv(3o-Glncx{ZXQ_@MoT#A8d!4^411N_u5U+jfL-_VK?&`8Hg4n? z&BJmuRT(&r(X2%&<)<#dIU06ZbqAV|kqy4nz?av!j>bZ+B&Z2AxrEiqnOrK5U1SPU zvSN2YouheJ&LN}sJ;*uvpPL-IHyq;bdw7`bc(AukSEgx0TPKl#yRJS#)Q0;8!eMKH<_pui0P zsWmfKSj3Z1Ru2zCgf0`+PNWK*e^s^vt%&F{Pr=tcPu?+a4tG>NXKrX3%-iWX)|D z-K^i-izdCsqm+V(+WN=T%!{CMK~Q*DK1^8=g}K5w`Fi&1B>gfd8c$DClrfxo(iwul zGTO2SOiJ&XM`LofD;uEoF0=xzf6W=+*7|9VmNQKa&_J!5U=>>LL9%ud{+Lf--ev8U z)JIQ?uzT88lt)juZ_Y~cc!TSM&$x;qw{cnw&G2AlKnng;SJtq5!2rCjT>o# zco!6hHPq)ZuC>Z&*WDll*RfKuqP0o5!w6s3jMUtXt-05@jm2$;1Lcxf@_nHT5>-RPFaWeM{(N@AS!AUOd)+iP*lP)5yNb8ffuzZ z7kK7S;mthiVG*Pb8gQ2_q#x_4&r>XncC~zkuWy}2bjw%x`c^aQ#3W`9Gi9o~X_U76 zw#Tx?n{q=hl){Wu3pg6Jcs^aQ!OQ zuoCz7`r1C9`TNf$znS^B<35saozL`!ur9^Zs;WPNPS&Xx7KX19LY8sKx4FEb^&(u* zy(l6VS;GRzJ$Fl}EPNN3i}Ek>$Lq}0b_P-y8D(tpsW-QQz zXBU>y%D>^weL3s3>1ZRQtGgdjtfR2$HfKXQDqc0b1;aX&u>|kPSYfwC%Q?_T55+&h zj11p2X6!L%!cUlIU}Rzo6h0XZtK!d)IFxZ@wD{S;^sZOA52)N7cp1s6{8;AxPUYUD zavvNW$?ac;+!mGFpmOhzmVE=cqJ2d$T@f4+ZMu^LIWP}Jkgf<$kJj*CBydLvk`%$g zQ3kFdK|d6vYS;@9y;~6_xSRyLK_IU|hxhXCeg1iee|q@mHU4>-f1bx5%I;pagAHH- z{Swa18RzA!-LpFJ*Ep7+A6k*jbxLr9ZLiDTgSMLW;8%Dt7VGrym%R&qm+A*Ui>Q!cj(W*ua8e^c|)qhOIhu#0>NAa<@Pmh@EXlc5TDmb(zz^bUiPLciAPl% zP|`cTolV^v7f4*g*7WoZ-e9|w*xKBTg1iQNkshw#I%dcr;EA;u(Asr3fe0r>@T5K- z-=Mph*%C>9|Ipx0JzbjWdUM#aH=AdV{cQx+mc5CSoqKKg{gJq9I)$!^uc>u%I{Q>B z?83h-k`M>=X=fzt9OZH~WaE4K*7=tWEj@AK+8}|0H?S1`t&qab-c6Ek&Y<#eMjYIP zuRyOY{qWyYy&pB%_2eL)IeIOK@DFE(>c6r-siB{||27>6JSYm`DHVvh%j=YW%9kzN z^x(vsOE}90cQYcqp^XgSF*oV6_Nv!Hr4U~WeH(^IshN@zG7eeq`PqNlW!7qppAX@y&~ zS>4*~9jx~>4FHXZxX9q%qk(X3D6@`A6*W7g$FSG_$(PI2$JKNU|^hXT!|M^ zGN$7FIucz7$AUSw^#RBS=&=?$Q=!}LB^q@WLKx=FXgd9i^>OYYF7pr9^!LlhGS|r? zx6tULQF6T-s|E9Z@l`lTHyO@nt9jTGu!C2mQ2Qio4a|JW-edYdgs0}YRP!6u#YT?k zR+Q$em{DL!`1e=vn#rH923A7m_$ODxHzJs1jiaqYsn+o2GO}{XC;q|YiB~5{Fl!h} zg(o44QatW!c}9RrHqs1!ynd9xj}rJ%0zXRNM+y8WfgdICqXd4G!2d@Qh{J=5IVEN8 z+PXkxZB0p)yQH+#-_YQiUs6?BRzjS+s$`+R{w%j*dj7o0_5PB8Ki5B}aqg@@WmQ8y z$o(~Q{q=c;GhMFv^K$(Sf%@8oF$l^_Dy#fu?m(@(RO)fhX)G`I*PmdEB${7dSySe2 zXsisB%&GFbtNqor^$XpBg>`vc_HSFRiUFtE`#p zF0HMuE(vq5*+JN1OhCta_XU+GQ25Rdeu6hW=9jGX&aaWf#6*W{Y z@~5P_RPI@473CM9%Po$q^{)W=}$~v~y z;14vYo<`{`xi&1UDXpllt*Kn3v{*amO7Pdw2ee#QS?8~+ ztnt&_Y8wLw>d&fYZhc8zMP+G&+b%FjU6s`(bN%l5mHq{g&H*jeL$pv(T}i`WxgwdU zO+-tVlwQ?XS?_l@L`3A4cJ`NlL*tz4%7DA9zGT55#uKftuHKJv6Zl{2lW{PmU{-^B zL1m!AJ-e}L9xSV&v97MRJ|ZTJ;fBi6B8)tk_E~7R2Dgo+GJebJYpdNgja60d$_6)$ zR>rn9;G+CUJ0;N(hbGar{rx}#M$lD_7~=l2u^2i5sjy@|Mm2}&2`=3a({|6SuPmvV zTjh66t99qqVBGm#v!@opK@^qIZxl7saU7@_42VEUO)085Ft4h1Ze=O_Q<;B$rBJ92 zMES>9a5vT=9}U0>SWYRX9W8)+sa=5X)|ZzITAyWa14moLI-~9GZ z^(l(|`w>voJpt+4Dn@H9uJ9 zvtQDD9)hmxtsm~~%f=Jty`9?k5qN#dyqaly5y8C>XxYyDA=KA*`S z?wi$*Gv*Li+%ZGXjt3z-V^;L{^Gx5atNQ!nQTNdL{{C&KcMa09h^Qsvna~Z4bp;TS zLb_spf4>{_D;D(kcO%Vg>hDiRxfMvukZwa-%=(Z9keWzWAWdA@-`{~W8R^4Fb)@m= zSQAoyVeMX|W0CGcnvHY~j`CF@U9lK1B_VA>?CuWKe=lNoCel@ie4Ud37?Il*NLS%G zWhc@^JT@5xL8KspmWi~G5808fKw6A+4blM83Iu<*Azg*^b)=bi(3%2aCgb^f2hx2A zpCv;HyKp+K9qAez8%)G$msL1w){M02*Zuwbklu^qz&@OmTJhWd{!K_L{tJBJERy~^ z^b_eSyg~CB(k8roS%vt01z(v&s*{XIx`J=x#yIUIa<_4gMd zZQ9u1e-F|%&!Rn~DG2@L;t-YoQh)y{q$bj*nZAs6VMNKVfnTJiiFTNVQ4Z;v_xtyCKe;*MO)()w`jU6t|=+*nBl_`+hLHzbE${E| zqk$ftbpGMV7ifnrNNjeUcl6n(jQ5-ftk^Q1_PdHf z+yLzyo>XvnqLjY_Aj_j`axp%>8b6jlff)Vpq?Z5J-nT%iWXWX)#dWJB0E1f0z#i~jFc-I_>1f@sD!MFgz+RzE`y1`rG#H=|;d4(zv&kRpbpHF_|f%umJw+M1v zM>xd1%Nq?X+DUSpGFF8uc9;<6+LBPk8Yxbdw3c97sG>a&fuK}RNDrI|UUkfCr?-vD zWEGXk6%djtjA$@hE}*Nx?>2<{G1~ufKuW_-FDRyb%@oe}Zm$j~>nO4IToYR@YjEK9 zT~Kz5bv=&Z9o5-~phI4P+>9&Gz`*YYFRCreKT7gPo~Hb5B>&4o&zZiZ9v7j37s+T6 zziv#$n@FEgp?L1}uFTfAHKDy8U#TbbkY{D+fTs&^6)@h`{3_TCoj@|W77b((mJ43v zy1{vErMzB-(7rDtF}a!>MKD46qILi0pbP2L9ODrv6q&D8l&`2mz3L!V|Kyx6)KVeJ z@hgR1{SEZ$sgxEJ&JM57gCIp~Q9?!Z9=}@9{0i$OOW-F*xRt;e(8Kw-tAH!d!%>=P zb?GAD_QG$RhcvII`h~LCNo6}_baMUjHlu<8gHl%&tUY+84MXCSaX*T9Zp({@%I8Jk z_5t^0%7>K8x@>vRXm2s^J&vW_5)>H(rxQ<$(6`W*bFsm#?aI2-5sG<#;K-{vxYel@ zc06+MnP-3S!~}g|>N{V=_XqEEygLzZ6Z+?ZdS{S{FL(@qKU>y+L(# zCD?SsUV10`p@kLfP#iTY~i` zEC%YI$9!8*UB`)j+g3sIqJtpzA!y~E9$)QXZ*>!p6mN_eF5w5!(QJFKF8Q)A4xnj z;Q0h>{GI5xjo)gXPVI;7iNl8P0y4tdW>|D9bzlAjcp4Zdd=f>P%)lu znSImKmx8(RaV=}ukj}PqM`*jx_qypWmzCi82GaM?|DK+v>km}=X0;9e){$GMO5b+W z>z^E$o_-g}xb+0mcK`;JbN!O#Ml!z%=H%UvO;3LoekE_@cL&+u-cCrnLrS|nxGA)# zQ+kI$_5{L|9h{zilfp=yAZ;pXv%@mqVWRGNJ5b8NWNoUpAQ_|;9eJqEU>h)~a)=Ua zFz9FxULLA|Et*Bha+ySVd=>FkklpuL{Kan5(PT|EPy+n>Um0`g*P z);uxxn5C?LSmIcFWc@9LC=O%1{O1?&%@fH5t{lHNfcw>p)6-88LXIK5+hF2$1Tn@y zGZanuZA2Zp>80uEt4Pl-Q)$`dMSI*yw)K=DW!e!sy4H8pu4rXBR)EJ<7`X1mSo|&c zRXIZPJA4qg_xmVEjC2v->Y$*>nI{lmrgwU}7Io72AsQRxxzoFb#uK2Y@#|jGClFP6 zs~-Evc+Nyv1M;f5dV2an^52E9pxt3i`F0}hO~9Rt_Z$1boAR<-@y^!+E2)004c+1K z{fhE}u^GlOs_^AlgW?zjk82S}JH?TqIPUQ5FsF>2zFpu-$9#L1Y(9v@|ITYZ;S-NYEOs;XDbt=mA~Byg%zcLV zmVucA#NB-57d*R4#6KAOe4i~5-z+xq@OZHa|0~63!GCKB`5!1TkCccfrT>)@MDwcj zel`d%*xY`(r(=)D{GKVE_Ly(_#UW1wM}ytvt$N%Pf8$&Hl3zUIGmrW`#|#trub3t< z511z2e$tO!f#3I=Kl6(p`w4*rN{6Ifz6RX;%vVF=c8_@^gkh5TN=Q85eX;l_A#rSu z`N&+cbFK;h-E&Pe_3umnD|5{sg~T7E|8pVpg^;)>L^RKaE>;E7_8he5_e#v0g5r6~ z$!|-{&j-b>pm}>x+)epG%*#IE`4p=6uTAr?Uu^dSx!Z640%<1iOMdg0e(?r*;ihlz z^=yoyfFATrdG?fvM||cV%EUJe^WHM?s$srf=J`~S`9PU?EA))<-7@jB`GorIeDlv` z;!|bjwF|_ZWhR2XP-cEbFp}cYRJ!h z=8eVTn}&I9vG}oR{#~)SKR_>k9WXytEVdWnHi2&!o1d3uiI@LU{63knit`cn|3I1n z@wdj^-k$`-!vXWPfOsKb!v9KO&g%iOy9hLhGP>*@RFU5;GCy;=_{8Z@>3dH%Z(A&W zaJu=W#o{-oo8Mk6K6!?D*J5$+8RmBvi)YR-zp_}oc80lsvH0|2^S;I6>BWlC{I!7Z zH~-!cA2;te#N);SDYsKR{x>}4JzlgM&qu|-EhAZ;?wPY>NsHKk0P*@3kl_vV0KJ>` zoKm+70=ds?-e-t^@|j;X2x+{Fw8}|$QUWI>a8d#%C2&##Cna!F0w*PKQUWI>a8d#% zC2&##?h@F$R=wN4O2Mo7S%+wts|MgrH0cT#V_3>iVNd@5|0aR*Lxnlgb5iQ#knFUK zSVWVlaQgi++jEWWxz+Z(-uC@fvT=jV!`O6g*L z=HT+iMnX04dz#xMNAteq-h69%l;8nO)sVyNjC5nz&q@xo(fag z(qTGDI-38H9iznDCQM z$NwiRb1+JhVpptsUaGLu{H)V;l%F3uSH1ruKWo0s>#qjHSZ~=ARNtg9?F@Su4ltZx zIK}W5hI<(9XLyj|A%;g79%JZl7Sb(bxRhZ%!*+%}3CN&@Cd_W4E-G(Kf|RA>lwB)>|r>-aDw3! z!&?~cVYr{+L57DI9$|Qlp?@XE&u}TjdWP)`dl(KdoM1S`@D_%98183ykl`VQM;IPs z=*KEXzPqu^lmD3YceJ*?ue_pT?S}Kp!;z|RRW-l40MIM_Me_f^8uenS0yF-pzw*j2 zBo)1#K)rv<$n^|`6c@?xmzvr4P3k>Pb&*c@qt4v%?H;JG<0>0|VUr36gmfDpD|}{j z7Mu$oR=y;ADXD@C{v+go=*sXEB3*YD;n(5!0sMTPqQ@XLpEpqM126(> z=&2~Ml7PqmIDkoqzjU}gk z8~)Nc#?OpmS^_Tm2|^l&jnMb-Ac|h6r&rXIQFIkzF#c6N6^T=dk$vOmhVMyGB44eb zEuMn^9561aAtWWSj*?;+f2Np=jQi=S)cmRh#y0@W@6g|wGQ+_#uq^fOp9HagA7b-A zWJci^e){y@|DBSL;=wD7%K>Cmv;ZZenkzhn^b#WQeM%=?ljEU3F9{%U7A5vx!qK1i zLBiD#9o-+|p+7Hu_8eG8IQme_Lx0|%;xG8$z?BBxq)*ZOKO~|*aH?*_i@<-*`!0aj ze+ym*{#bS=G4MZ64}YG25n?S8#xIE<{dp^aKlK;*J2PN-ifHk>h!)%eeve{V^lRcD z@F<=|*U?jvCq#v06orVg*t6gq;#%}Dy(>}g771eoczNj0`x*QNw&1OCJK^Zh`!&Mt zBiy$LM}J-teBgtGdxUWG=Y5uN=i$$IfpGNaeT8tJBf3`!M}OWo3HM3L>NMf#&s&T~ z4;7tvKKZ^#zO%?j-_v@Rk?&FRg~|6K`7R_MiPak=ABn?D1>m`ZeD5dUKJvxM_XPQd z$wzh6yOn%L$@e$pGw|pA6#h(N6?m7d*TGxy-Uysc1?2w|{DCF&ZiXLJUni;$pZYcU z=a&Y4YQB!=Ilsc+zm!Ptm7qQx^g_~eo&xrj;HmJ6;?Luckh0Rv6U7vs-pJ>GD5ip< z;^F!qoQJTbftRTsV09Hy{%i5$K!Fpr`PG^-Aj%E_9Wba}7oYC`5MIpnA_-zHox|gq zOA_!xZFnz*XF+*UOXwbA`3tZtY75DaH~e1)Wl^+jrI_Dt3mELEqEdPDDdY1 z;CxCu8MX9qHh!cQGsNH&{LKO4QUuFsPE9gJBvfT8L9(%c1e=CRPdmkP>O5a)ap*mv z`6ehl^F(k-Q748u-W7s??_3cKUJ~@-V;N(q#Fz_(FZ90Pg~7%k5%}lkJvLzA6PORg zMHqn<@f(u}=}oc6(>dRRFeOXCX*n>#`5s>=7?j+W6w#|WGSHHMW)hvsp*(1n!F_ed6qhd7+V>66i-?qs9j_w!Fv*KZK#=){)AA)g1M_|`H=)$3C=!q7 zPa&vYIC_$Zv#Hh!8q#__XM5*)sboF!PZWD~N9t?vtn-ytwY>Y1HRrzj;;?T{X3*;a zpKCx&%u^J|iFq;A!$JJE;&&Z>RH<*mZy$b-;`cOuGUAdp_&h(O$0PXjy+MC;z>ral zKOdPu=2`gj6J3D%tfB~f6#IJt9b@-U>?&K-CWQQpbEp#)NHs!whX)0qs>E>#a?aV_s|ZX}SBKs!Vw zf1<5Sq*5cJcrlht;NLOI%uu=#JFpW&7bmgbGub;-nV3w(MS3*8rMJI7WhK+2c@NlI zIwmY@9L8Nu#L9?ta-`lG7#hXa*?~N|@ew8p*Avkowk4O7J(%N)i@RmmbR0X$tz-7$QN*KT1ySX?!G|+%$qy2ndb*)mxPLa6M{#F29W4~_WTZ}(W45eRB|#*Fp-6PrbJ3w3v+}Nqh*Uc9AuFCno$B3` zw)&EJZyP|^+n2O5y_<5uaj<}@E^B2?;Y{S-_fncjY{KeXht2S4N7i^c(JyAIk9aDR zg!XWJF*3VanGr^4T~zpHInrN={q~8fxL7`(PD$+~x2NansKOwWp0Uw%a&kF@k)|gN z6GH>}i5(vS4K(7!aww$n965H{XO`nfkmyMb%D9sw@uBhlgd!JI;OVMCBpf2jK&@p) za!=IW=@C&9lBLYvLUXvz<5ZkGXgQ;3NRjqto*NiC>UiU(kH6Wj=ZxPUCNs^kqV?S7^Lj`oAUVPZihxyQa)R zr~G~o^stOY!wWe&@;xW{oQ8D=opSa5C5eX(&+8L9)Ng@52XZK7IiJrjXn-d1T(4*x zL+*3H>g4)iI)B}|VmtR3Thcfc8cxeLAz_)?7i z?k1x<&m~>xzv_bjt_%L3UGPVNC;7x4RgubJGQ6ijP)_oEFYuE7enn4vJjl_t7Ebgg z6kIv-b-gWLgP-B1_m#%f;vm1Dfvryqxe5iiX-48sar>R#x%l*#q{@8`jTTFlS zYl>d(9fFtkM>(bcLoWDFyWoF@;yV@T8KYqk90T!H7y5^QKSz-k{6FtPe?-zFzg(Z^ zaVQfDm`>?m1U$*dUT-gQq3@A+@V`tkuI1|Zzg*})4m|O{+SV@iF}~?q#fSFTkvkO= z0YtxjQa#iDM{=}5#VP$8T<{|<_}g6Y54hkD0)LA43}K(|f8K@uRp2T8M>)THRr1Aq z(D6H^zs?2U=7PV<1^+eRDc-$bP!ZBTI&v?$(EkQ_YF~G-e%mgoaZ3%_t5Z7LfG7TU zd`uD2K1*`rz%PM(?%?)|<|Ex_nEtuAdZsCPa>0;_w~6DeP-w9Xc&B)qUGN_Tp3-ym z4T=HneI&P+@q4-cFHvamqzj+dUGS%(;W(vd8R5P2gfR(RTfM(*n_e4f{QSe`mxzjvWuQ0koi_qyO4T=16xPvv`?t$h1j=u3r@JDxI{ims};7JMnLH!EbWGXI${RfTwtGyiLU?_u#@i2XmWvHN&0bf)Bgk zi_tGrKc#0ImompF=t*ys&+$kM7mxTT@KO#m{Rk)bodf=(3;(BG@bk)?)4#+8Uk|*D z_evS9Fu5Il+=c!o7yNfz@K3woUjkmrpY^=1pK}%{{B~Qttz`Uxj7q201B06WK863R z0^;w1cPhumKswQ1>Vofg!KYpD*8#r-_LcsQOxt630#9X_`K$Vrx|~z z^q)_7FFLCx77T+h@D%USA%)QL90i{GYrCENVHf@{xZnfGpsZITh5XxrcS`3v7yM2a z{Fhzu`&{r(x!_+0p3=GZBPyNp+y!_SVQA-+&MhwZZ7%pvyWsb@;2(Cu|H1`db_Ut) zGu=wyozg$-g1;4bl0)eh#Y@+RAG*;0l`#Ws4`FX*Qn9A-Y4pp{O`f42+$gD&`|n9trZ#fMh5rF)mNe5zgW zQ5XD17yK~rBnP|QewPdVqb~TDUGT3m|GhmbJMz3Uc;{mo*QwkuaKT^bg1-`YSr0cV z{;zO7ybt(N*e!N__z=_E$B8chPyPEC_wU;kI}s{(PG`ggf0+w@qYFOig5T|eKj4D@ z5%44jd;j};7y8rCaXZPs3V7lZ+a#pZ^*QN6|4|qGZ7%qGT<}iGqYtC^_f1eBfF&F&YcRTYr7kEm~)pw}~>HHIN-7fSaF8GfzpVFIEcsgf>+}*&J zOJCvNqb_`2WIokNMX$?c!MV=qzW{hyjy8F&a-sjA3;x^8r=0n~43qA^T<90R$2mQ9 zz*Bn4zoEjv$$ai|p?};3{~s=Re1_mypU(u|sh@2Gp3-wiTqOk6OuF|2U(Wt~FzCYn z!!GnU1Ahwi&jB7k>gnctfhRq4l=X~`>*tIw=lN=wH`1BQobx5}7O!$EakOfT#U481 zyDC10#o;U0c4byrR(x_Y91chF525aidHi_aX$Ih-c-Mp%p_91IFguF0IQPeR4+dHl+iU<>-)emk?7pKxhvCX zb@t;F?PO+K=jLcvrgjJiKa;N)f6)d+1TN`BG={{~=z!J;mca4eh3#LErD#_j5}%hA zJZDoQ{k@$8W=|1O-^x=bubSxIBcMMJkf_Eb$U76JlSnVIB>h;&0 z)T%hi5^J343~ntzL%=!mxK#j$p`)4(TXB41K*eSCk6N3CM*Dh)tbWvBEZ~n%3Vf?E zHk81z=~R!MC@dVnp0s*XsorfCYIJIwkVny5{o}*K+Ylu02_HrzaY;ZPsb#gVZ(b9% zqHEhM3&i;J#fq+Eto^wzO{CXw_8JQo@k<8hlQJ^Ylj&R%WXz5nM`v zW$@17)x$NB=<4D6NG<+r8duPN7=Mwv`W3YeHTB^-JcJ2T8)>XTBv7 zBMtSn)kH$r+UiJTMNRdJI${}akbmJwZB0$2t_G?t42^@&h^!QDmWWQ^HVkeKxGzIr z+R%kN8^jpS>CX(PY*>-1NLAf&UL+FzRa1 zF?5BYh}$AEPNJz%ank)3IBMT9-a0gG^dbd<)0v7`#2l9|5CimYE0SrPmY*20QPagJ zbaTNK=nFgh@nw*ekYDbobk=gd@d+8!@~~qf+QKtfY8|fYR*XLLp_u3^v29)Y6n`A) zujJ7H!AeoIBKrlxL^9l4cTH-1B$|xH;(5Fi!?=fIG=+T37BXB-s!Cl*0D*5B8raH- zyF%W-fY9BYE;YlJWGb=3Bo}_;Giy3Xc8Xt^GLuOTCtw+*MpLcfC~8GM->OJhQP!&~ zTk>m7c*TtCTRJb3^(i{Bw$ZZim7uhRph-tYQ^V*0v>DSG16fxM)x5EF-Ii){ZMY+( z6PJ-p;u9q@qIxZPNsR@a8Xs(eers;&w8B+2pl(*TkE~5hW;*G16Dm@D7f-9Y$wn5w zVgqG?`+KfQsC==4L7f;%j1-_$<#W2=K?)3)?!<@3974D^E0VKLR|j9!6632~($a9` zoWeLDI|0R@zo5*|uu52AG^Fmj)O8*0B-{*SK+U;2uQ|Kd;Rdv;{&qCc==xP?^>5c` zjZ&R(=N?|6t^w)7tvl<~O*>K$T-lYDlGPvTUse4LxcnyulLy8Fc$4X?yI+*jrp%zK zpSH>g*+xgk(bYk6g{mLIO$_4>l)}=o`qQHpZXzOuBei@Bnw_mLlCpNF?9dTtnf26w z@uetgQ_31b&norj`c+*Ks>GztP#EnA^ap8~Kf**OqzaY_I;(_awM{Ekgxuyjj%3r5<>}e zezr?sl-4P^vL9-H3oZzZsdZ&WKHncN0(BhhRiGKPNYeY zGsAUza8y(%G-@j+psK1Z6t7lQs%Lh-28Qa+Xx+fjczV#Tol&LptOI<1sCs6XqAir` z>`e_fbIGv_RV^*Lf=Zl$7&1t{Qu@@rWSbV8z3_wzT6N>sr;IVP%lf z4vj`%pW}m5e3EX_pex%5YuG&11s7}V*&-W@)vYZNSyY7=e?^C9*EWsRCR?CE)3QRt z3@X$GIXiJ%ECpKx!$CQWoM{n}J%r)6loWN&V8vEczaIYO4LP-9?~-?Ut&vxGK?!Ua zfi0j+Fj7MCv2EG>hikKyT6Xk}S#6JStQ=KECiNssRreMc(Bd92rBkG>uoAiwa_Q)q zwRSX)kHM}=Y#2#Gn+&URif)cJ%EIYJzfYoth0LA1t}1K}!b+`+)7P<(N&|**Ta%bw zh?3niL&)3}wf2r-ssybRBIZtcxSzDF&WG;rGKtBgO*=~y^#sh%T4_&`^-8lCov`87 z!N8;s`EAYSe=Zq!g%ydYR%h?kWffeLtpHNGFtX6+j1J=)c2ZJgHBmz;R1sC-2FR5f z71y2IXvyBnNKn}hqw_E6zA?9QLK8IMFa{B@+mQZEWGWz>Dtz4>(4gYY^vyptm)Pjg zP`0z<>O8Y!IT7RDR*-o$bT+fcS$F7`#8w9x7ei^XvTf6G(e+fHRIfLx2Oiy+xT>@4 zW`}j<$kyS4G)`=3p$ioYN@{kk6>d~lDi#F6D0W59mI9R3y%~)9hhh`MdA(^>qiVP| z`<*nGEnQp=y1-;Xbth88u!%F=`fcOFFs2}4>N-9sHL|UBKbjjRYK2kX19h61bSH>~ z(K&bN;n4}``TVMV+$qgSt(;-V@a2&fHUi)}K5V&2qq36RP;+#d&NiaOxW_IxrGBNE z)`P1P!3(Uitqx5Y?VrZhC(?tv>&q^6aiLO`TvW10L^I>ZoaVf;y9;S>8lP}Z-^K_* zjZ8Mk-T@Ku6`FE3CeNtWwMI^-=69adyT`X!@xd+F(32cebq*sdbnw(0tMuqf5~CWW z`ZcB7j#~|^?lrA?_KxIUZ&} z;e;Cv1lA%iG{pg%XCh2P)^rl{-k33zo5)a$uoRG|wN>9)Xq^^};n`+#v##>AG7lZ| zdd66|x@U{37RRfI6I-NB+$Ha3r5fOF=gd_3PF>XO*1J@Hs{PA&XOA)LY_qB&Y>>NB zsWfoqFmCC@TyN(9W~OUVVt*Zz-dIIg-yNnkg#5C}n`%|VFuXj@>~1Y7Qn^&;Fcin6 zZGlplwG6|J=&K;J)#}n%RT6oF|8~0mxKB*(0FTlS)X9TBzexL8t%X1}jxB z9@J8oKEr^dzFx^eH6>Q_5V!2m@}>y$sdPolEur+etid=GFITyO%F8_?D}3%h$U0C} zg@^riD_$9gGkPIqZRE97o+Gi9Z6R+t0LYrr$7{vI3GCsYD4eNRH4W;+aV3B!&e;-j znk>w(oCVpYDTKlWut;N8h%;9AY1!5GA;4@4`L65#r~({Gp4@c7&zLBXoxoo2$EPm*`EAo{7kr70j-X?UT=nTmFo8KYL5ARhm@~C|IW^>oT|@eN&+Vg@B}) zkXsv^O-D4+)$&r3#Iv&-re*ds1dJl+BZO|cj6ElIZi+KnK`dN{>BwAPc)Yr5Z5Y`K zb!wqOV-2DSXR=`do~NcAM`>-ExC9nBp7`=ki>s65281~J2# zKcXiKFS`UUznIFcCvMJ-_j8k~GuKGve1Lt4dWI?`wZ>X|aCCgAAC)MbSATM%P-~2~ zsocW0!ZGHryX#qIImv2m!kHyJS59+l`0k)^up_Iq{;sB=Leq>yZ5->iwivs+(R}}c zSvI-!T(}-=;|#l`u-ls*VQ1bYi zN}P$JsIcN&NoQVE5&0kvk`Yz?+eQ$Df|-jbn{OF3aNUJJKYQhx*`1@z8$aLy>}|Od!5Z zi6i_dqQYO5j9Y4|bV>8xk{Eb#rTbQ*GaN+?}6{zOl-ccswj_%-|;UQ-j*(dhHu8#uhC(_HkkhX0KhBtIQq z?@Mpe@!RBAt{E{bL13!8I=nt-B}Rxf54+w_;mZp`=i0AEF9R9z()ril`QOdq_bN7; zT$E>h%5wqU6G^oVufyB-=4f^r)^T`SaB+a+KWclS<*w0fF5&l@Du(M!O{t)cS7T!? z;p_N(gWGHYY>$G@j zXhLS9A2qxV9_?om33_ + +#define GLM_FORCE_RADIANS +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const uint32_t WIDTH = 800; +const uint32_t HEIGHT = 600; +const int MAX_FRAMES_IN_FLIGHT = 2; + +const std::vector validationLayers = { + "VK_LAYER_KHRONOS_validation", + "VK_LAYER_LUNARG_monitor" +}; + +const std::vector deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME +}; + +#ifdef NDEBUG +const bool enableValidationLayers = false; +#else +const bool enableValidationLayers = true; +#endif + +VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { + auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != nullptr) { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) { + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != nullptr) { + func(instance, debugMessenger, pAllocator); + } +} + +struct QueueFamilyIndices { + std::optional graphicsFamily; + std::optional presentFamily; + + bool isComplete() { + return graphicsFamily.has_value() && presentFamily.has_value(); + } +}; + +struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; +}; + +struct Vertex { + glm::vec2 pos; + glm::vec3 color; + + static VkVertexInputBindingDescription getBindingDescription() { + VkVertexInputBindingDescription bindingDescription{}; + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + return bindingDescription; + } + + static std::array getAttributeDescriptions() { + std::array attributeDescriptions{}; + attributeDescriptions[0].binding = 0; + attributeDescriptions[0].location = 0; + attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[0].offset = offsetof(Vertex, pos); + + attributeDescriptions[1].binding = 0; + attributeDescriptions[1].location = 1; + attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; + attributeDescriptions[1].offset = offsetof(Vertex, color); + + + return attributeDescriptions; + } +}; + +struct UniformBufferObject { + //alignas(16) glm::vec4 ball1; + //alignas(16) glm: vec4 ball2; + glm::vec4 balls[8]; +}; + +/*glm::vec4 ballsArr[12] { + glm::vec4(0.0f, 0.0f, 8.0f, 0.0f), + glm::vec4(-0.5f, -0.5f, 8.0f, 0.0f), + glm::vec4(0.5f, -0.1f, 8.0f, 0.0f), + glm::vec4(-0.5f, 0.5f, 10.0f, 0.0f) +};*/ + +const std::vector vertices = { + {{-1.0f, -1.0f}, {1.0f, 0.0f, 0.0f}}, + {{1.0f, -1.0f}, {0.0f, 1.0f, 0.0f}}, + {{1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}}, + {{-1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}}, +}; + +const std::vector indices = { + 0, 1, 2, 2, 3, 0 +}; + +class HelloTriangleApplication { + public: + void run(int argc, char* argv[]) { + initArgs(argc, argv); + initBalls(); + initWindow(); + initVulkan(); + mainLoop(); + cleanup(); + } + + private: + int scenario; + + GLFWwindow* window; + + VkInstance instance; + VkDebugUtilsMessengerEXT debugMessenger; + VkSurfaceKHR surface; + + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + VkDevice device; + + VkQueue graphicsQueue; + VkQueue presentQueue; + + VkSwapchainKHR swapChain; + std::vector swapChainImages; + VkFormat swapChainImageFormat; + VkExtent2D swapChainExtent; + std::vector swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; + + std::vector uniformBuffers; + std::vector uniformBuffersMemory; + + VkDescriptorPool descriptorPool; + std::vector descriptorSets; + + std::vector commandBuffers; + + std::vector imageAvailableSemaphores; + std::vector renderFinishedSemaphores; + std::vector inFlightFences; + uint32_t currentFrame = 0; + + glm::vec4 ballsArr[8]; + std::pair speedArr[12]; + + bool framebufferResized = false; + + void initWindow() { + glfwInit(); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); + glfwSetWindowUserPointer(window, this); + glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); + } + + static void framebufferResizeCallback(GLFWwindow* window, int width, int height) { + auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); + app->framebufferResized = true; + } + + void initVulkan() { + createInstance(); + setupDebugMessenger(); + createSurface(); + pickPhysicalDevice(); + createLogicalDevice(); + createSwapChain(); + createImageViews(); + createRenderPass(); + createDescriptorSetLayout(); + createGraphicsPipeline(); + createFramebuffers(); + createCommandPool(); + createVertexBuffer(); + createIndexBuffer(); + createUniformBuffers(); + createDescriptorPool(); + createDescriptorSets(); + createCommandBuffers(); + createSyncObjects(); + } + + int initArgs(int argc, char* argv[]) { + if (argc == 1) { + return 0; + } + if (strcmp(argv[1], "negative") == 0) { + scenario = 1; + return 0; + } else if (strcmp(argv[1], "default") == 0) { + return 0; + } else { + printf("Scenario not found: %s", argv[1]); + return 1; + } + } + + + void initBalls() { + int arrayLen = sizeof(ballsArr) / sizeof(ballsArr[0]); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution pos_dist(-0.5, 0.5); + std::uniform_real_distribution speed_dist(0.0f, 0.0001f); + std::uniform_real_distribution size_dist; + if (scenario == 0) { + size_dist = std::uniform_real_distribution(4.0f, 12.0f); + } else if (scenario == 1) { + size_dist = std::uniform_real_distribution(-10.0f, 10.0f); + } + + for (int i = 0; i < arrayLen; i++) { + ballsArr[i] = glm::vec4(pos_dist(gen), pos_dist(gen), size_dist(gen), 0.0f); + speedArr[i] = std::make_pair(copysign(speed_dist(gen), -ballsArr[i].x), copysign(speed_dist(gen), -ballsArr[i].y)); + } + + //ballsArr[11] = glm::vec4(0.0f, 0.0f, -32.0f, 0.0f); + //speedArr[11] = std::make_pair(0.0f, 0.0f); + } + + void mainLoop() { + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + drawFrame(); + } + + vkDeviceWaitIdle(device); + } + + void cleanupSwapChain() { + for (auto framebuffer : swapChainFramebuffers) { + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void cleanup() { + cleanupSwapChain(); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroyBuffer(device, uniformBuffers[i], nullptr); + vkFreeMemory(device, uniformBuffersMemory[i], nullptr); + } + + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + + vkDestroyBuffer(device, indexBuffer, nullptr); + vkFreeMemory(device, indexBufferMemory, nullptr); + + vkDestroyBuffer(device, vertexBuffer, nullptr); + vkFreeMemory(device, vertexBufferMemory, nullptr); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + vkDestroyFence(device, inFlightFences[i], nullptr); + } + + vkDestroyCommandPool(device, commandPool, nullptr); + + vkDestroyDevice(device, nullptr); + + if (enableValidationLayers) { + DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); + } + + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + + glfwDestroyWindow(window); + + glfwTerminate(); + } + + void recreateSwapChain() { + int width = 0, height = 0; + glfwGetFramebufferSize(window, &width, &height); + while (width == 0 || height == 0) { + glfwGetFramebufferSize(window, &width, &height); + glfwWaitEvents(); + } + + vkDeviceWaitIdle(device); + cleanupSwapChain(); + + createSwapChain(); + createImageViews(); + createRenderPass(); + createGraphicsPipeline(); + createFramebuffers(); + } + + void createInstance() { + if (enableValidationLayers && !checkValidationLayerSupport()) { + throw std::runtime_error("validation layers requested, but not available!"); + } + + VkApplicationInfo appInfo{}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "Hello Triangle"; + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.pEngineName = "No Engine"; + appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + + auto extensions = getRequiredExtensions(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + + VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; + if (enableValidationLayers) { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + + populateDebugMessengerCreateInfo(debugCreateInfo); + createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo; + } else { + createInfo.enabledLayerCount = 0; + + createInfo.pNext = nullptr; + } + + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { + throw std::runtime_error("failed to create instance!"); + } + } + + void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) { + createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + createInfo.pfnUserCallback = debugCallback; + } + + void setupDebugMessenger() { + if (!enableValidationLayers) return; + + VkDebugUtilsMessengerCreateInfoEXT createInfo; + populateDebugMessengerCreateInfo(createInfo); + + if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) { + throw std::runtime_error("failed to set up debug messenger!"); + } + } + + void createSurface() { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { + throw std::runtime_error("failed to create window surface!"); + } + } + + void pickPhysicalDevice() { + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + + if (deviceCount == 0) { + throw std::runtime_error("failed to find GPUs with Vulkan support!"); + } + + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + + for (const auto& device : devices) { + if (isDeviceSuitable(device)) { + physicalDevice = device; + break; + } + } + + if (physicalDevice == VK_NULL_HANDLE) { + throw std::runtime_error("failed to find a suitable GPU!"); + } + } + + void createLogicalDevice() { + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + + std::vector queueCreateInfos; + std::set uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()}; + + float queuePriority = 1.0f; + for (uint32_t queueFamily : uniqueQueueFamilies) { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkPhysicalDeviceFeatures deviceFeatures{}; + + VkDeviceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + + createInfo.pEnabledFeatures = &deviceFeatures; + + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + + if (enableValidationLayers) { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + } else { + createInfo.enabledLayerCount = 0; + } + + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { + throw std::runtime_error("failed to create logical device!"); + } + + vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); + vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); + } + + void createSwapChain() { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = surface; + + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; + + if (indices.graphicsFamily != indices.presentFamily) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + + createInfo.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { + throw std::runtime_error("failed to create swap chain!"); + } + + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); + + swapChainImageFormat = surfaceFormat.format; + swapChainExtent = extent; + } + + void createImageViews() { + swapChainImageViews.resize(swapChainImages.size()); + + for (size_t i = 0; i < swapChainImages.size(); i++) { + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = swapChainImages[i]; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapChainImageFormat; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create image views!"); + } + } + } + + void createRenderPass() { + VkAttachmentDescription colorAttachment{}; + colorAttachment.format = swapChainImageFormat; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef{}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkSubpassDependency dependency{}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { + throw std::runtime_error("failed to create render pass!"); + } + } + + void createDescriptorSetLayout() { + VkDescriptorSetLayoutBinding uboLayoutBinding{}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + uboLayoutBinding.pImmutableSamplers = nullptr; // Optional + + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &uboLayoutBinding; + + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("failed to create descriptor set layout!"); + } + } + + void createGraphicsPipeline() { + auto vertShaderCode = readFile("shaders/vert.spv"); + auto fragShaderCode = readFile("shaders/frag.spv"); + + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + auto bindingDescription = Vertex::getBindingDescription(); + auto attributeDescriptions = Vertex::getAttributeDescriptions(); + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float) swapChainExtent.width; + viewport.height = (float) swapChainExtent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = swapChainExtent; + + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; // Optional + rasterizer.depthBiasClamp = 0.0f; // Optional + rasterizer.depthBiasSlopeFactor = 0.0f; // Optional + + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; // Optional + multisampling.pSampleMask = nullptr; // Optional + multisampling.alphaToCoverageEnable = VK_FALSE; // Optional + multisampling.alphaToOneEnable = VK_FALSE; // Optional + + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional + + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; // Optional + colorBlending.blendConstants[1] = 0.0f; // Optional + colorBlending.blendConstants[2] = 0.0f; // Optional + colorBlending.blendConstants[3] = 0.0f; // Optional + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; + pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional + pipelineLayoutInfo.pPushConstantRanges = nullptr; // Optional + + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("failed to create pipeline layout!"); + } + + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = nullptr; // Optional + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = nullptr; // Optional + pipelineInfo.layout = pipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional + pipelineInfo.basePipelineIndex = -1; // Optional + + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { + throw std::runtime_error("failed to create graphics pipeline!"); + } + + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); + } + + void createFramebuffers() { + swapChainFramebuffers.resize(swapChainImageViews.size()); + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + VkImageView attachments[] = { + swapChainImageViews[i] + }; + + VkFramebufferCreateInfo framebufferInfo{}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = swapChainExtent.width; + framebufferInfo.height = swapChainExtent.height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create framebuffer!"); + } + } + } + + void createCommandPool() { + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); + + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create graphics command pool!"); + } + } + + void createVertexBuffer() { + VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); + + void* data; + vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data); + memcpy(data, vertices.data(), (size_t) bufferSize); + vkUnmapMemory(device, stagingBufferMemory); + + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); + + copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); + } + + void createIndexBuffer() { + VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); + + void* data; + vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data); + memcpy(data, indices.data(), (size_t) bufferSize); + vkUnmapMemory(device, stagingBufferMemory); + + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); + + copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); + } + + void createUniformBuffers() { + VkDeviceSize bufferSize = sizeof(UniformBufferObject); + + uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT); + uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]); + } + } + + void createDescriptorPool() { + VkDescriptorPoolSize poolSize{}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &poolSize; + poolInfo.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); + + if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create descriptor pool!"); + } + } + + void createDescriptorSets() { + std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); + allocInfo.pSetLayouts = layouts.data(); + + descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); + if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate descriptor sets!"); + } + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = uniformBuffers[i]; + bufferInfo.offset = 0; + bufferInfo.range = sizeof(UniformBufferObject); + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSets[i]; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferInfo; + descriptorWrite.pImageInfo = nullptr; // Optional + descriptorWrite.pTexelBufferView = nullptr; // Optional + + vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr); + } + } + + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = size; + bufferInfo.usage = usage; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { + throw std::runtime_error("failed to create buffer!"); + } + + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(device, buffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); + + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate buffer memory!"); + } + vkBindBufferMemory(device, buffer, bufferMemory, 0); + } + + void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = commandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + VkBufferCopy copyRegion{}; + copyRegion.srcOffset = 0; // Optional + copyRegion.dstOffset = 0; // Optional + copyRegion.size = size; + vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(graphicsQueue); + + vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); + } + + uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) { + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); + + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + + throw std::runtime_error("failed to find suitable memory type!"); + } + + void createCommandBuffers() { + commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (uint32_t) commandBuffers.size(); + + if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate command buffers!"); + } + } + + void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; // Optional + beginInfo.pInheritanceInfo = nullptr; // Optional + + if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { + throw std::runtime_error("failed to begin recording command buffer!"); + } + + VkRenderPassBeginInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex]; + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent = swapChainExtent; + + VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + + VkBuffer vertexBuffers[] = {vertexBuffer}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + + vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT16); + + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[currentFrame], 0, nullptr); + vkCmdDrawIndexed(commandBuffer, static_cast(indices.size()), 1, 0, 0, 0); + + vkCmdEndRenderPass(commandBuffer); + + if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { + throw std::runtime_error("failed to record command buffer!"); + } + } + + void createSyncObjects() { + imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + + VkSemaphoreCreateInfo semaphoreInfo{}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS || + vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create synchronization objects for a frame!"); + } + } + } + + void updateUniformBuffer(uint32_t currentImage) { + static auto startTime = std::chrono::high_resolution_clock::now(); + + auto currentTime = std::chrono::high_resolution_clock::now(); + float time = std::chrono::duration(currentTime - startTime).count(); + + UniformBufferObject ubo{}; + + int arrayLen = sizeof(ballsArr) / sizeof(ballsArr[0]); + + //ballsArr[0] = ballsArr[0] + glm::vec4(0.001f, 0.0f, 0.0f, 0.0f); + //ballsArr[1] = ballsArr[1] + glm::vec4(0.005f, 0.005f, 0.0f, 0.0f); + //ballsArr[2] = ballsArr[2] + glm::vec4(-0.003f, 0.0f, 0.00001f, 0.0f); + //ballsArr[3] = ballsArr[3] + glm::vec4(0.003f, -0.003f, 0.0f, 0.0f); + + for (int i = 0; i < arrayLen; i++) { + ballsArr[i] = ballsArr[i] + glm::vec4(speedArr[i].first, speedArr[i].second, 0.0f, 0.0f) * time; + } + + //ubo.balls = ballsVec; + memcpy(ubo.balls, &ballsArr, sizeof(ballsArr)); + /* + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 10.0f); + ubo.proj[1][1] *= -1; + */ + + + void* data; + vkMapMemory(device, uniformBuffersMemory[currentImage], 0, sizeof(ubo), 0, &data); + memcpy(data, &ubo, sizeof(ubo)); + vkUnmapMemory(device, uniformBuffersMemory[currentImage]); + } + + void drawFrame() { + vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + + uint32_t imageIndex; + VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + return; + } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + throw std::runtime_error("failed to acquire swap chain image!"); + } + + updateUniformBuffer(currentFrame); + + vkResetFences(device, 1, &inFlightFences[currentFrame]); + + vkResetCommandBuffer(commandBuffers[currentFrame], 0); + recordCommandBuffer(commandBuffers[currentFrame], imageIndex); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; + + VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) { + throw std::runtime_error("failed to submit draw command buffer!"); + } + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = {swapChain}; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + presentInfo.pImageIndices = &imageIndex; + + result = vkQueuePresentKHR(presentQueue, &presentInfo); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != VK_SUCCESS) { + throw std::runtime_error("failed to present swap chain image!"); + } + + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + } + + + VkShaderModule createShaderModule(const std::vector& code) { + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw std::runtime_error("failed to create shader module!"); + } + + return shaderModule; + } + + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { + for (const auto& availableFormat : availableFormats) { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return availableFormat; + } + } + + return availableFormats[0]; + } + + VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes) { + for (const auto& availablePresentMode : availablePresentModes) { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + return availablePresentMode; + } + } + + return VK_PRESENT_MODE_FIFO_KHR; + } + + VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } else { + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; + + actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + + return actualExtent; + } + } + + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + + if (formatCount != 0) { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); + + if (presentModeCount != 0) { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + } + + return details; + } + + bool isDeviceSuitable(VkPhysicalDevice device) { + QueueFamilyIndices indices = findQueueFamilies(device); + + bool extensionsSupported = checkDeviceExtensionSupport(device); + + bool swapChainAdequate = false; + if (extensionsSupported) { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); + swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + } + + return indices.isComplete() && extensionsSupported && swapChainAdequate; + } + + bool checkDeviceExtensionSupport(VkPhysicalDevice device) { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + + for (const auto& extension : availableExtensions) { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); + } + + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { + QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + int i = 0; + for (const auto& queueFamily : queueFamilies) { + if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + } + + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + + if (presentSupport) { + indices.presentFamily = i; + } + + if (indices.isComplete()) { + break; + } + + i++; + } + + return indices; + } + + std::vector getRequiredExtensions() { + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions; + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); + + if (enableValidationLayers) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + + return extensions; + } + + bool checkValidationLayerSupport() { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + for (const char* layerName : validationLayers) { + bool layerFound = false; + + for (const auto& layerProperties : availableLayers) { + if (strcmp(layerName, layerProperties.layerName) == 0) { + layerFound = true; + break; + } + } + + if (!layerFound) { + return false; + } + } + + return true; + } + + static std::vector readFile(const std::string& filename) { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + + if (!file.is_open()) { + throw std::runtime_error("failed to open file!"); + } + + size_t fileSize = (size_t) file.tellg(); + std::vector buffer(fileSize); + + file.seekg(0); + file.read(buffer.data(), fileSize); + + file.close(); + return buffer; + } + + static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { + std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; + + return VK_FALSE; + } +}; + +int main(int argc, char* argv[]) { + HelloTriangleApplication app; + + try { + app.run(argc, argv); + } catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/shaders/compile.sh b/shaders/compile.sh new file mode 100755 index 0000000..1dd05ac --- /dev/null +++ b/shaders/compile.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +glslc shader.vert -o vert.spv +glslc shader.frag -o frag.spv diff --git a/shaders/frag.spv b/shaders/frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..d148d85ff4ac54da8e7e9c08c1d67897500be138 GIT binary patch literal 2588 zcmZ9N{Zo`x6vr>ZA_}IaVlPM_S)@f;s1+$7>Y{C7nZ4J#EDNnIEVC@m_)|7bGyOaL z-c-}i_3!jHQ`6`3@Eqo;>m1JayxeoX_uRXWy?ZA1rqq-6rvqs{?X1IT51y0;Q-7w* zmD`o6t?tUy^!c-9Jd%2|L~{h(f< z_VuMj;+wTj&3n+k`5(KK&9uGUU1+!39YR?zXS{Qc9cP(+kK*e`4k3$Vc^22ij-r2W z=JPJhm;1@x*p5A3u)WVgwB-=?5pLMJl@9_uh0HR(QO7Hr-+Y$UB*wKo1jA z>wN_FOAz(6eH;2BzY0$2;qE!>SnU7NcW@7E5A&_}1zLXk<)`i2(szM-ciz4kmwW!e zQ$m~TjQ!mO7vF`yxv-tZV!<|kGq*2O&ArZ0>^|@NPwHPn+eiHyeP+>fh|Bmq+PL}> zac6%8vBr0>bN1JA-vatCah*Qlo5HzS3Byy0k*C&xof}51f5|8@w?|9 zWA4%aCgRz?f{XTBxzGGKw?5A@hn9~zdM<(O`H=md?QKM#?<8iWuQ)5u75#66?f*^Q z|6Q;?-&OS27x{O<=Kqk-?tQR6-`($=`$M$LIU4%_sUh+fN1ac=)(QL5|Jk4A_ARFF z-0vdpG0&LqR(`(cyNKtrw|(pvb(g{R`99~n0@hbYt=ga)w zJ_qXyeoe4G->6dLXH0%kS6$>Q@4xvKy@L2V>c{r2xCW7n@Ekx7<=Eeb{};;9Z@wJ8 z`{TKPo{9O*djj!0>2JlD-^4i2lVIcX^gDqbMXW!9M9x#WKkAQT+c)Z;1V?>i#k~{Q z#^YT-4IV@E$6I>_tp619b$;$=!S*#a#5tVBb4U*&U*nTV@IC{U_h}?}KM&R)yk7tp zc~4;nZ)3&1FJc=H-e= 0.8) { + outColor = vec4(intensity, intensity, intensity, 1.0); + } else if (intensity <= -0.8) { + outColor = vec4(-intensity, 0, 0, 1.0); + } +} diff --git a/shaders/shader.vert b/shaders/shader.vert new file mode 100644 index 0000000..9f27f54 --- /dev/null +++ b/shaders/shader.vert @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec3 inColor; + +layout(location = 0) out vec3 fragColor; + +void main() { + gl_Position = vec4(inPosition, 0.0, 1.0); + fragColor = inColor; +} diff --git a/shaders/vert.spv b/shaders/vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..88ef6b4b73b291a4e8d203bbd9dab65c30607721 GIT binary patch literal 1080 zcmYk4Uuzmc6vZd%Mx)Wx7-O5JsUeSr(1)fJN*h8_whw}qKwlTpZ5Y_BvTjP-r+l`2 zs=gF@ezQB`$o1ZP?!Pm4wpux>8?$Aarfp75ak{1=#F&=Zb~+s0j?P#4{QTX;TQWMP z>JrJ>Gds?%>HmIeQ=GPBU-C-wT2j@oBK@R-nkI##C>)Q&(U)kpTt=(>cM?a5P2*^8 zvv`)rG~+IbCUN#X&hq$|T%iSyO>D8Uxm~0}>n@)= zINYZBt^K3P)D$r|D|VgO<&9nC({vWEV{CxfLz1jxf$quOIe~ucz^3bw4r3=jvgzIR zB3WcEujY1S(*pUbEbhVa+GE9=@LI-r436Z%$79%F>?c$No4im6oK7SheE4koyca$* zda9!CR6Xd4eJE>NdMFvFULTM1nbnuykRD1nZ%Uspgnq~Jo6^*SoydZl??DV1DsNZX z-{WKHvs*CcbX;E1mpS-*5)QncNizq|^y^B)0i1sL>;jB>at|aN@AsYecuD;W+02G3 z{a;E`14jI~w880*&u)F(@6^Zf*){Xu>&@ZG4ms5P6%t5)5(?*@1is&dGWW2=q}JG= v#OOH@Q@B2tkV}mFai=Tw;66tZcFNv9YbrkZ%!KDq0!L!p1^=HIe~|nKrgK*= literal 0 HcmV?d00001