diff --git a/.gitignore b/.gitignore index ae5f6c81..410b95ad 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,221 @@ ads.txt build_ebook.log temp_ebook.md ebook/*.pdf -ebook/*.epub \ No newline at end of file +ebook/*.epub + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +# Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +# poetry.lock +# poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +# pdm.lock +# pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +# pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# Redis +*.rdb +*.aof +*.pid + +# RabbitMQ +mnesia/ +rabbitmq/ +rabbitmq-data/ + +# ActiveMQ +activemq-data/ + +# SageMath parsed files +*.sage.py + +# Environments +.env +.envrc +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +# .idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..24ee5b1b --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/LICENSE.CC-BY-SA-40 b/LICENSE.CC-BY-SA-40 new file mode 100644 index 00000000..7028425e --- /dev/null +++ b/LICENSE.CC-BY-SA-40 @@ -0,0 +1,427 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the "Licensor". The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSE.CC0 b/LICENSE.CC0 new file mode 100644 index 00000000..0e259d42 --- /dev/null +++ b/LICENSE.CC0 @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/README.md b/README.md index fcbb3775..b14b7f26 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ necessary. Now with the above done, we can generate the static files. Asuming the daux.io and VulkanTutorial directories are next to each other, go into the `daux.io` directory and run a command similar to: -`php generate -s ../VulkanTutorial -d ../VulkanTutorial/out`. +`daux generate -s ../VulkanTutorial -d ../VulkanTutorial/out`. `-s` tells it where to find the documentation, while `-d` tells it where to put the generated files. diff --git a/build_ebook.py b/build_ebook.py index 39e9a969..112483dc 100755 --- a/build_ebook.py +++ b/build_ebook.py @@ -11,6 +11,9 @@ from subprocess import CalledProcessError from re import Match import shutil +import argparse +import sys +from make_parser import make_parser logging.basicConfig( format="%(asctime)s %(levelname)-8s %(message)s", @@ -148,7 +151,7 @@ def compile_full_markdown( return markdown_file -def build_pdf(markdown_file: Path, pdf_file: Path) -> Path: +def build_pdf(markdown_file: Path, pdf_file: Path, args: argparse.Namespace) -> Path: """Build combined Markdown file into a PDF.""" try: @@ -157,12 +160,21 @@ def build_pdf(markdown_file: Path, pdf_file: Path) -> Path: raise RuntimeError(f"failed to build {pdf_file}: xelatex not installed") try: + keys_values = [(arg, getattr(args, arg)) for arg in vars(args)] + opts = [f"{key}={val}" for key, val in keys_values if val != ""] + pandoc_args = [x for i in opts for x in ("-V", i)] + subprocess.check_output( [ "pandoc", markdown_file.as_posix(), "-V", - "documentclass=report", + "documentclass=report" + ] + + + pandoc_args + + + [ "-t", "latex", "-s", @@ -202,8 +214,10 @@ def build_epub(markdown_file: Path, epub_file: Path) -> Path: return epub_file - def main() -> None: + parser = make_parser() + args = parser.parse_args(sys.argv[1:]) + """Build ebooks.""" with TemporaryDirectory() as raw_out_dir: out_dir = Path(raw_out_dir) @@ -223,11 +237,11 @@ def main() -> None: ) logging.info(f"{lang}: building pdf...") - pdf_file = build_pdf(markdown_file, out_dir / f"{lang}.pdf") + pdf_file = build_pdf(markdown_file, out_dir / f"{lang}.pdf", args) logging.info(f"{lang}: building epub...") epub_file = build_epub(markdown_file, out_dir / f"{lang}.epub") - + shutil.copy(pdf_file, f"ebook/vulkan_tutorial_{lang}.pdf") shutil.copy(epub_file, f"ebook/vulkan_tutorial_{lang}.epub") diff --git a/code/22_descriptor_layout.cpp b/code/22_descriptor_set_layout.cpp similarity index 99% rename from code/22_descriptor_layout.cpp rename to code/22_descriptor_set_layout.cpp index 123c2269..3d54aa6a 100644 --- a/code/22_descriptor_layout.cpp +++ b/code/22_descriptor_set_layout.cpp @@ -232,7 +232,7 @@ class HelloTriangleApplication { vkDestroyPipeline(device, graphicsPipeline, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyRenderPass(device, renderPass, nullptr); - + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { vkDestroyBuffer(device, uniformBuffers[i], nullptr); vkFreeMemory(device, uniformBuffersMemory[i], nullptr); @@ -593,7 +593,7 @@ class HelloTriangleApplication { viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportState.viewportCount = 1; viewportState.scissorCount = 1; - + VkPipelineRasterizationStateCreateInfo rasterizer{}; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.depthClampEnable = VK_FALSE; @@ -751,7 +751,7 @@ class HelloTriangleApplication { 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]); - + vkMapMemory(device, uniformBuffersMemory[i], 0, bufferSize, 0, &uniformBuffersMapped[i]); } } diff --git a/code/27_depth_buffering.cpp b/code/27_depth_buffering.cpp index 96015dd4..5e0452c2 100644 --- a/code/27_depth_buffering.cpp +++ b/code/27_depth_buffering.cpp @@ -561,8 +561,8 @@ class HelloTriangleApplication { VkSubpassDependency dependency{}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; - dependency.srcAccessMask = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; diff --git a/code/28_model_loading.cpp b/code/28_model_loading.cpp index 6f1b7b44..0097a311 100644 --- a/code/28_model_loading.cpp +++ b/code/28_model_loading.cpp @@ -568,8 +568,8 @@ class HelloTriangleApplication { VkSubpassDependency dependency{}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; - dependency.srcAccessMask = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; @@ -995,10 +995,10 @@ class HelloTriangleApplication { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; - std::string warn, err; + std::string err; - if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, MODEL_PATH.c_str())) { - throw std::runtime_error(warn + err); + if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, MODEL_PATH.c_str())) { + throw std::runtime_error(err); } std::unordered_map uniqueVertices{}; diff --git a/code/29_mipmapping.cpp b/code/29_mipmapping.cpp index c12692b4..874a8d82 100644 --- a/code/29_mipmapping.cpp +++ b/code/29_mipmapping.cpp @@ -569,8 +569,8 @@ class HelloTriangleApplication { VkSubpassDependency dependency{}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; - dependency.srcAccessMask = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; @@ -952,7 +952,7 @@ class HelloTriangleApplication { samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.minLod = 0.0f; - samplerInfo.maxLod = static_cast(mipLevels); + samplerInfo.maxLod = VK_LOD_CLAMP_NONE; samplerInfo.mipLodBias = 0.0f; if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { @@ -1089,10 +1089,10 @@ class HelloTriangleApplication { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; - std::string warn, err; + std::string err; - if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, MODEL_PATH.c_str())) { - throw std::runtime_error(warn + err); + if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, MODEL_PATH.c_str())) { + throw std::runtime_error(err); } std::unordered_map uniqueVertices{}; diff --git a/code/30_multisampling.cpp b/code/30_multisampling.cpp index d430108d..ab6cbc3f 100644 --- a/code/30_multisampling.cpp +++ b/code/30_multisampling.cpp @@ -596,8 +596,8 @@ class HelloTriangleApplication { VkSubpassDependency dependency{}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; - dependency.srcAccessMask = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; @@ -1002,7 +1002,7 @@ class HelloTriangleApplication { samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.minLod = 0.0f; - samplerInfo.maxLod = static_cast(mipLevels); + samplerInfo.maxLod = VK_LOD_CLAMP_NONE; samplerInfo.mipLodBias = 0.0f; if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { @@ -1139,10 +1139,10 @@ class HelloTriangleApplication { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; - std::string warn, err; + std::string err; - if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, MODEL_PATH.c_str())) { - throw std::runtime_error(warn + err); + if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, MODEL_PATH.c_str())) { + throw std::runtime_error(err); } std::unordered_map uniqueVertices{}; diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index e8c67b02..4c409f92 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -28,8 +28,15 @@ function (add_shaders_target TARGET) OUTPUT ${SHADERS_DIR} COMMAND ${CMAKE_COMMAND} -E make_directory ${SHADERS_DIR} ) + set (SHADERS ${SHADERS_DIR}/frag.spv ${SHADERS_DIR}/vert.spv) + # Some chapters may have compute shaders in addition to vertex and fragment shaders, + # so we conditionally check this and add them to the target + string(FIND "${SHADER_SOURCES}" "${CHAPTER_SHADER}.comp" COMPUTE_SHADER_INDEX) + if (${COMPUTE_SHADER_INDEX} GREATER -1) + set (SHADERS ${SHADERS} ${SHADERS_DIR}/comp.spv) + endif() add_custom_command ( - OUTPUT ${SHADERS_DIR}/frag.spv ${SHADERS_DIR}/vert.spv + OUTPUT ${SHADERS} COMMAND glslang::validator ARGS --target-env vulkan1.0 ${SHADER_SOURCES} --quiet WORKING_DIRECTORY ${SHADERS_DIR} @@ -37,7 +44,7 @@ function (add_shaders_target TARGET) COMMENT "Compiling Shaders" VERBATIM ) - add_custom_target (${TARGET} DEPENDS ${SHADERS_DIR}/frag.spv ${SHADERS_DIR}/vert.spv) + add_custom_target (${TARGET} DEPENDS ${SHADERS}) endfunction () function (add_chapter CHAPTER_NAME) @@ -52,7 +59,7 @@ function (add_chapter CHAPTER_NAME) if (DEFINED CHAPTER_SHADER) set (CHAPTER_SHADER_TARGET ${CHAPTER_NAME}_shader) - file (GLOB SHADER_SOURCES ${CHAPTER_SHADER}.frag ${CHAPTER_SHADER}.vert) + file (GLOB SHADER_SOURCES ${CHAPTER_SHADER}.frag ${CHAPTER_SHADER}.vert ${CHAPTER_SHADER}.comp) add_shaders_target (${CHAPTER_SHADER_TARGET} CHAPTER_NAME ${CHAPTER_NAME} SOURCES ${SHADER_SOURCES}) add_dependencies (${CHAPTER_NAME} ${CHAPTER_SHADER_TARGET}) endif () @@ -128,7 +135,7 @@ add_chapter (21_index_buffer SHADER 18_shader_vertexbuffer LIBS glm::glm) -add_chapter (22_descriptor_layout +add_chapter (22_descriptor_set_layout SHADER 22_shader_ubo LIBS glm::glm) diff --git a/config.json b/config.json index 35920b42..edb3a0b3 100644 --- a/config.json +++ b/config.json @@ -21,12 +21,13 @@ "Support the website": "https://www.paypal.me/AOvervoorde", "Vulkan Specification": "https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/", "LunarG Vulkan SDK": "https://lunarg.com/vulkan-sdk/", - "Vulkan Guide": "https://github.com/KhronosGroup/Vulkan-Guide", + "Vulkan Guide": "https://docs.vulkan.org/guide/latest/", "Vulkan Hardware Database": "https://vulkan.gpuinfo.org/", "Rust code": "https://github.com/bwasty/vulkan-tutorial-rs", "Java code": "https://github.com/Naitsirc98/Vulkan-Tutorial-Java", "Go code": "https://github.com/vkngwrapper/vulkan-tutorial", - "Visual Studio 2019 samples": "https://github.com/jjYBdx4IL/VulkanTutorial-VisualStudioProjectFiles" + "Visual Studio 2019 samples": "https://github.com/jjYBdx4IL/VulkanTutorial-VisualStudioProjectFiles", + "Chinese translation": "https://github.com/fangcun010/VulkanTutorialCN" } }, "ignore": { diff --git a/en/00_Introduction.md b/en/00_Introduction.md index b6c55caa..1d14c781 100644 --- a/en/00_Introduction.md +++ b/en/00_Introduction.md @@ -1,3 +1,12 @@ +>## Read before following this tutorial +> +>This tutorial was written shortly after Vulkan was initially released, back in +>2016. A lot has changed since then and this tutorial no longer reflects the best +>way to use Vulkan today. +> +>Instead of reading this website, I recommend to follow the guide or one of the +>tutorials linked here: [https://vulkan.org/learn](https://vulkan.org/learn) + ## About This tutorial will teach you the basics of using the [Vulkan](https://www.khronos.org/vulkan/) @@ -48,7 +57,7 @@ for a great introduction of computer graphics concepts. Some other great compute You can use C instead of C++ if you want, but you will have to use a different linear algebra library and you will be on your own in terms of code structuring. We will use C++ features like classes and RAII to organize logic and resource -lifetimes. There is also an [alternative version](https://github.com/bwasty/vulkan-tutorial-rs) of this tutorial available for Rust developers. +lifetimes. There are also two alternative versions of this tutorial available for Rust developers: [Vulkano based](https://github.com/bwasty/vulkan-tutorial-rs), [Vulkanalia based](https://kylemayes.github.io/vulkanalia). To make it easier to follow along for developers using other programming languages, and to get some experience with the base API we'll be using the original C API to work with Vulkan. If you are using C++, however, you may prefer using the newer [Vulkan-Hpp](https://github.com/KhronosGroup/Vulkan-Hpp) bindings that abstract some of the dirty work and help prevent certain classes of errors. @@ -125,3 +134,20 @@ still stuck after that, then feel free to ask for help in the comment section of the closest related chapter. Ready to dive into the future of high performance graphics APIs? [Let's go!](!en/Overview) + +## License + +Copyright (C) 2015-2023, Alexander Overvoorde + +The contents are licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/), +unless stated otherwise. By contributing, you agree to license +your contributions to the public under that same license. + +The code listings in the `code` directory in the source repository are licensed +under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/). +By contributing to that directory, you agree to license your contributions to +the public under that same public domain-like license. + +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. diff --git a/en/02_Development_environment.md b/en/02_Development_environment.md index 4f1da483..2d8b2535 100644 --- a/en/02_Development_environment.md +++ b/en/02_Development_environment.md @@ -204,7 +204,7 @@ The most important components you'll need for developing Vulkan applications on * `sudo apt install vulkan-tools` or `sudo dnf install vulkan-tools`: Command-line utilities, most importantly `vulkaninfo` and `vkcube`. Run these to confirm your machine supports Vulkan. * `sudo apt install libvulkan-dev` or `sudo dnf install vulkan-loader-devel` : Installs Vulkan loader. The loader looks up the functions in the driver at runtime, similarly to GLEW for OpenGL - if you're familiar with that. -* `sudo apt install vulkan-validationlayers-dev spirv-tools` or `sudo dnf install mesa-vulkan-devel vulkan-validation-layers-devel`: Installs the standard validation layers and required SPIR-V tools. These are crucial when debugging Vulkan applications, and we'll discuss them in the upcoming chapter. +* `sudo apt install vulkan-validationlayers spirv-tools` or `sudo dnf install mesa-vulkan-drivers vulkan-validation-layers-devel`: Installs the standard validation layers and required SPIR-V tools. These are crucial when debugging Vulkan applications, and we'll discuss them in the upcoming chapter. On Arch Linux, you can run `sudo pacman -S vulkan-devel` to install all the required tools above. @@ -219,6 +219,11 @@ include the Vulkan runtime and that your graphics card is supported. See the [introduction chapter](!en/Introduction) for links to drivers from the major vendors. +### X Window System and XFree86-VidModeExtension +It is possible that these libraries are not on the system, if not, you can install them using the following commands: +* `sudo apt install libxxf86vm-dev` or `dnf install libXxf86vm-devel`: Provides an interface to the XFree86-VidModeExtension. +* `sudo apt install libxi-dev` or `dnf install libXi-devel`: Provides an X Window System client interface to the XINPUT extension. + ### GLFW As mentioned before, Vulkan by itself is a platform agnostic API and does not @@ -241,7 +246,7 @@ sudo dnf install glfw-devel ``` or ```bash -sudo pacman -S glfw-wayland # glfw-x11 for X11 users +sudo pacman -S glfw ``` ### GLM @@ -348,7 +353,7 @@ LDFLAGS = -lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi The flag `-lglfw` is for GLFW, `-lvulkan` links with the Vulkan function loader and the remaining flags are low-level system libraries that GLFW needs. The remaining flags are dependencies of GLFW itself: the threading and window management. -It is possible that the `Xxf68vm` and `Xi` libraries are not yet installed on your system. You can find them in the following packages: +It is possible that the `Xxf86vm` and `Xi` libraries are not yet installed on your system. You can find them in the following packages: ```bash sudo apt install libxxf86vm-dev libxi-dev diff --git a/en/03_Drawing_a_triangle/00_Setup/00_Base_code.md b/en/03_Drawing_a_triangle/00_Setup/00_Base_code.md index df26c6ac..e5b0e0b3 100644 --- a/en/03_Drawing_a_triangle/00_Setup/00_Base_code.md +++ b/en/03_Drawing_a_triangle/00_Setup/00_Base_code.md @@ -145,8 +145,14 @@ disable it for now with another window hint call: glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); ``` -All that's left now is creating the actual window. Add a `GLFWwindow* window;` -private class member to store a reference to it and initialize the window with: +All that's left now is creating the actual window. Add a private class member to store a reference to it: + +```c++ +private: +GLFWwindow* window; +``` + +Initialize the window with ```c++ window = glfwCreateWindow(800, 600, "Vulkan", nullptr, nullptr); diff --git a/en/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.md b/en/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.md index ef1ceabd..f593b5a6 100644 --- a/en/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.md +++ b/en/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.md @@ -515,7 +515,7 @@ you'll get the best performance by enabling clipping. createInfo.oldSwapchain = VK_NULL_HANDLE; ``` -That leaves one last field, `oldSwapChain`. With Vulkan it's possible that your swap chain becomes invalid or unoptimized while your application is +That leaves one last field, `oldSwapchain`. With Vulkan it's possible that your swap chain becomes invalid or unoptimized while your application is running, for example because the window was resized. In that case the swap chain actually needs to be recreated from scratch and a reference to the old one must be specified in this field. This is a complex topic that we'll learn more about diff --git a/en/03_Drawing_a_triangle/02_Graphics_pipeline_basics/01_Shader_modules.md b/en/03_Drawing_a_triangle/02_Graphics_pipeline_basics/01_Shader_modules.md index 20aee6b5..ef12e836 100644 --- a/en/03_Drawing_a_triangle/02_Graphics_pipeline_basics/01_Shader_modules.md +++ b/en/03_Drawing_a_triangle/02_Graphics_pipeline_basics/01_Shader_modules.md @@ -45,7 +45,7 @@ two SPIR-V binaries and load them into the program. ## Vertex shader The vertex shader processes each incoming vertex. It takes its attributes, like -world position, color, normal and texture coordinates as input. The output is +model space position, color, normal and texture coordinates as input. The output is the final position in clip coordinates and the attributes that need to be passed on to the fragment shader, like color and texture coordinates. These values will then be interpolated over the fragments by the rasterizer to produce a smooth diff --git a/en/03_Drawing_a_triangle/02_Graphics_pipeline_basics/02_Fixed_functions.md b/en/03_Drawing_a_triangle/02_Graphics_pipeline_basics/02_Fixed_functions.md index a80f72ae..5b4bfdec 100644 --- a/en/03_Drawing_a_triangle/02_Graphics_pipeline_basics/02_Fixed_functions.md +++ b/en/03_Drawing_a_triangle/02_Graphics_pipeline_basics/02_Fixed_functions.md @@ -175,7 +175,7 @@ viewportState.scissorCount = 1; viewportState.pScissors = &scissor; ``` -Independent of how you set them, it's is possible to use multiple viewports and scissor rectangles on some graphics cards, so the structure members reference an array of them. Using multiple requires enabling a GPU feature (see logical device creation). +Independent of how you set them, it's possible to use multiple viewports and scissor rectangles on some graphics cards, so the structure members reference an array of them. Using multiple requires enabling a GPU feature (see logical device creation). ## Rasterizer diff --git a/en/03_Drawing_a_triangle/04_Swap_chain_recreation.md b/en/03_Drawing_a_triangle/04_Swap_chain_recreation.md index 5da07458..ce58528b 100644 --- a/en/03_Drawing_a_triangle/04_Swap_chain_recreation.md +++ b/en/03_Drawing_a_triangle/04_Swap_chain_recreation.md @@ -49,19 +49,19 @@ void recreateSwapChain() { } ``` -Note that we don't recreate the renderpass here for simplicity. In theory it can be possible for the swap chain image format to change during an applications' lifetime, e.g. when moving a window from an standard range to an high dynamic range monitor. This may require the application to recreate the renderpass to make sure the change between dynamic ranges is properly reflected. +Note that we don't recreate the renderpass here for simplicity. In theory it can be possible for the swap chain image format to change during an applications' lifetime, e.g. when moving a window from a standard range to a high dynamic range monitor. This may require the application to recreate the renderpass to make sure the change between dynamic ranges is properly reflected. We'll move the cleanup code of all objects that are recreated as part of a swap chain refresh from `cleanup` to `cleanupSwapChain`: ```c++ void cleanupSwapChain() { - for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { - vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + for (auto framebuffer : swapChainFramebuffers) { + vkDestroyFramebuffer(device, framebuffer, nullptr); } - for (size_t i = 0; i < swapChainImageViews.size(); i++) { - vkDestroyImageView(device, swapChainImageViews[i], nullptr); + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(device, imageView, nullptr); } vkDestroySwapchainKHR(device, swapChain, nullptr); diff --git a/en/05_Uniform_buffers/00_Descriptor_layout_and_buffer.md b/en/05_Uniform_buffers/00_Descriptor_set_layout_and_buffer.md similarity index 97% rename from en/05_Uniform_buffers/00_Descriptor_layout_and_buffer.md rename to en/05_Uniform_buffers/00_Descriptor_set_layout_and_buffer.md index b2c18ebb..2bdcc2dc 100644 --- a/en/05_Uniform_buffers/00_Descriptor_layout_and_buffer.md +++ b/en/05_Uniform_buffers/00_Descriptor_set_layout_and_buffer.md @@ -13,11 +13,11 @@ images. We're going to set up a buffer that contains the transformation matrices and have the vertex shader access them through a descriptor. Usage of descriptors consists of three parts: -* Specify a descriptor layout during pipeline creation +* Specify a descriptor set layout during pipeline creation * Allocate a descriptor set from a descriptor pool * Bind the descriptor set during rendering -The *descriptor layout* specifies the types of resources that are going to be +The *descriptor set layout* specifies the types of resources that are going to be accessed by the pipeline, just like a render pass specifies the types of attachments that will be accessed. A *descriptor set* specifies the actual buffer or image resources that will be bound to the descriptors, just like a @@ -86,7 +86,7 @@ void main() { Note that the order of the `uniform`, `in` and `out` declarations doesn't matter. The `binding` directive is similar to the `location` directive for -attributes. We're going to reference this binding in the descriptor layout. The +attributes. We're going to reference this binding in the descriptor set layout. The line with `gl_Position` is changed to use the transformations to compute the final position in clip coordinates. Unlike the 2D triangles, the last component of the clip coordinates may not be `1`, which will result in a division when @@ -208,7 +208,7 @@ layouts here, because a single one already includes all of the bindings. We'll get back to that in the next chapter, where we'll look into descriptor pools and descriptor sets. -The descriptor layout should stick around while we may create new graphics +The descriptor set layout should stick around while we may create new graphics pipelines i.e. until the program ends: ```c++ @@ -233,7 +233,7 @@ We should have multiple buffers, because multiple frames may be in flight at the time and we don't want to update the buffer in preparation of the next frame while a previous one is still reading from it! Thus, we need to have as many uniform buffers as we have frames in flight, and write to a uniform buffer that is not currently -being read by the GPU +being read by the GPU. To that end, add new class members for `uniformBuffers`, and `uniformBuffersMemory`: @@ -411,6 +411,6 @@ In the next chapter we'll look at descriptor sets, which will actually bind the `VkBuffer`s to the uniform buffer descriptors so that the shader can access this transformation data. -[C++ code](/code/22_descriptor_layout.cpp) / +[C++ code](/code/22_descriptor_set_layout.cpp) / [Vertex shader](/code/22_shader_ubo.vert) / [Fragment shader](/code/22_shader_ubo.frag) diff --git a/en/05_Uniform_buffers/01_Descriptor_pool_and_sets.md b/en/05_Uniform_buffers/01_Descriptor_pool_and_sets.md index 5e373f68..b204db24 100644 --- a/en/05_Uniform_buffers/01_Descriptor_pool_and_sets.md +++ b/en/05_Uniform_buffers/01_Descriptor_pool_and_sets.md @@ -1,6 +1,6 @@ ## Introduction -The descriptor layout from the previous chapter describes the type of +The descriptor set layout from the previous chapter describes the type of descriptors that can be bound. In this chapter we're going to create a descriptor set for each `VkBuffer` resource to bind it to the uniform buffer descriptor. @@ -95,7 +95,7 @@ void createDescriptorSets() { A descriptor set allocation is described with a `VkDescriptorSetAllocateInfo` struct. You need to specify the descriptor pool to allocate from, the number of -descriptor sets to allocate, and the descriptor layout to base them on: +descriptor sets to allocate, and the descriptor set layout to base them on: ```c++ std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); @@ -374,7 +374,7 @@ Don't forget to recompile your shader after removing the `foo` field. ## Multiple descriptor sets As some of the structures and function calls hinted at, it is actually possible -to bind multiple descriptor sets simultaneously. You need to specify a descriptor layout for +to bind multiple descriptor sets simultaneously. You need to specify a descriptor set layout for each descriptor set when creating the pipeline layout. Shaders can then reference specific descriptor sets like this: diff --git a/en/06_Texture_mapping/02_Combined_image_sampler.md b/en/06_Texture_mapping/02_Combined_image_sampler.md index b86853d6..0f1e5496 100644 --- a/en/06_Texture_mapping/02_Combined_image_sampler.md +++ b/en/06_Texture_mapping/02_Combined_image_sampler.md @@ -6,7 +6,7 @@ image sampler*. This descriptor makes it possible for shaders to access an image resource through a sampler object like the one we created in the previous chapter. -We'll start by modifying the descriptor layout, descriptor pool and descriptor +We'll start by modifying the descriptor set layout, descriptor pool and descriptor set to include such a combined image sampler descriptor. After that, we're going to add texture coordinates to `Vertex` and modify the fragment shader to read colors from the texture instead of just interpolating the vertex colors. @@ -74,7 +74,7 @@ type (`VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER`, etc.) as specified by the corresponding `descriptorCount` members for the creation of the descriptor pool. However, it remains best practise to do so, and in the future, `VK_LAYER_KHRONOS_validation` will warn about this type of problem if you enable -[Best Practice Validation](https://vulkan.lunarg.com/doc/view/1.2.189.0/linux/best_practices.html). +[Best Practice Validation](https://vulkan.lunarg.com/doc/view/1.4.304.0/linux/best_practices.html). The final step is to bind the actual image and sampler resources to the descriptors in the descriptor set. Go to the `createDescriptorSets` function. @@ -129,7 +129,7 @@ are now ready to be used by the shaders! ## Texture coordinates There is one important ingredient for texture mapping that is still missing, and -that's the actual coordinates for each vertex. The coordinates determine how the +that's the actual texture coordinates for each vertex. The texture coordinates determine how the image is actually mapped to the geometry. ```c++ diff --git a/en/07_Depth_buffering.md b/en/07_Depth_buffering.md index 0aed8eab..92040cb3 100644 --- a/en/07_Depth_buffering.md +++ b/en/07_Depth_buffering.md @@ -425,7 +425,8 @@ Next, update the `VkSubpassDependency` struct to refer to both attachments. ```c++ -dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; +dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; +dependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; ``` diff --git a/en/08_Loading_models.md b/en/08_Loading_models.md index 620e31bf..f3ae89c0 100644 --- a/en/08_Loading_models.md +++ b/en/08_Loading_models.md @@ -138,10 +138,10 @@ void loadModel() { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; - std::string warn, err; + std::string err; - if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, MODEL_PATH.c_str())) { - throw std::runtime_error(warn + err); + if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, MODEL_PATH.c_str())) { + throw std::runtime_error(err); } } ``` diff --git a/en/09_Generating_Mipmaps.md b/en/09_Generating_Mipmaps.md index 377ffa53..e4033136 100644 --- a/en/09_Generating_Mipmaps.md +++ b/en/09_Generating_Mipmaps.md @@ -317,13 +317,13 @@ void createTextureSampler() { ... samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.minLod = 0.0f; // Optional - samplerInfo.maxLod = static_cast(mipLevels); + samplerInfo.maxLod = VK_LOD_CLAMP_NONE; samplerInfo.mipLodBias = 0.0f; // Optional ... } ``` -To allow the full range of mip levels to be used, we set `minLod` to 0.0f, and `maxLod` to the number of mip levels. We have no reason to change the `lod` value , so we set `mipLodBias` to 0.0f. +To allow the full range of mip levels to be used, we set `minLod` to 0.0f, and `maxLod` to `VK_LOD_CLAMP_NONE`. This constant is equal to `1000.0f`, which means that all available mipmap levels in the texture will be sampled. We have no reason to change the `lod` value, so we set `mipLodBias` to 0.0f. Now run your program and you should see the following: diff --git a/en/10_Multisampling.md b/en/10_Multisampling.md index e85dba91..70b27b51 100644 --- a/en/10_Multisampling.md +++ b/en/10_Multisampling.md @@ -196,6 +196,14 @@ Set the `pResolveAttachments` subpass struct member to point to the newly create ... ``` +Since we're reusing the multisampled color image, it's necessary to update the `srcAccessMask` of the `VkSubpassDependency`. This update ensures that any write operations to the color attachment are completed before subsequent ones begin, thus preventing write-after-write hazards that can lead to unstable rendering results: + +```c++ + ... + dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + ... +``` + Now update render pass info struct with the new color attachment: ```c++ diff --git "a/fr/02_Environnement_de_d\303\251veloppement.md" "b/fr/02_Environnement_de_d\303\251veloppement.md" index 80796d81..1041fdbe 100644 --- "a/fr/02_Environnement_de_d\303\251veloppement.md" +++ "b/fr/02_Environnement_de_d\303\251veloppement.md" @@ -186,7 +186,7 @@ packages. Il vous faut un compilateur qui supporte C++17 (GCC 7+ ou Clang 5+). V Les composants les plus importants pour le développement d'applications Vulkan sous Linux sont le loader Vulkan, les validation layers et quelques utilitaires pour tester que votre machine est bien en état de faire fonctionner une application Vulkan: * `sudo apt install vulkan-tools` ou `sudo dnf install vulkan-tools`: Les utilitaires en ligne de commande, plus précisément `vulkaninfo` et `vkcube`. Lancez ceux-ci pour vérifier le bon fonctionnement de votre machine pour Vulkan. * `sudo apt install libvulkan-dev` ou `sudo dnf install vulkan-headers vulkan-loader-devel`: Installe le loader Vulkan. Il sert à aller chercher les fonctions auprès du driver de votre GPU au runtime, de la même façon que GLEW le fait pour OpenGL - si vous êtes familier avec ceci. -* `sudo apt install vulkan-validationlayers-dev` ou `sudo dnf install mesa-vulkan-devel vulkan-validation-layers-devel`: Installe les layers de validation standards. Ceux-ci sont cruciaux pour débugger vos applications Vulkan, et nous en reparlerons dans un prochain chapitre. +* `sudo apt install vulkan-validationlayers-dev` ou `sudo dnf install mesa-vulkan-drivers vulkan-validation-layers-devel`: Installe les layers de validation standards. Ceux-ci sont cruciaux pour débugger vos applications Vulkan, et nous en reparlerons dans un prochain chapitre. Si l'installation est un succès, vous devriez être prêt pour la partie Vulkan. N'oubliez pas de lancer `vkcube` et assurez-vous de voir la fenêtre suivante: diff --git a/fr/03_Dessiner_un_triangle/00_Mise_en_place/01_Instance.md b/fr/03_Dessiner_un_triangle/00_Mise_en_place/01_Instance.md index 496e840a..851c5aee 100644 --- a/fr/03_Dessiner_un_triangle/00_Mise_en_place/01_Instance.md +++ b/fr/03_Dessiner_un_triangle/00_Mise_en_place/01_Instance.md @@ -20,7 +20,7 @@ VkInstance instance; Pour créer l'instance, nous allons d'abord remplir une première structure avec des informations sur notre application. Ces données sont optionnelles, mais elles peuvent fournir des informations utiles au driver pour optimiser ou -dignostiquer les erreurs lors de l'exécution, par exemple en reconnaissant le nom d'un moteur graphique. Cette structure +diagnostiquer les erreurs lors de l'exécution, par exemple en reconnaissant le nom d'un moteur graphique. Cette structure s'appelle `VkApplicationInfo` : ```c++ diff --git "a/fr/03_Dessiner_un_triangle/01_Pr\303\251sentation/01_Swap_chain.md" "b/fr/03_Dessiner_un_triangle/01_Pr\303\251sentation/01_Swap_chain.md" index d12f1c76..539efe5b 100644 --- "a/fr/03_Dessiner_un_triangle/01_Pr\303\251sentation/01_Swap_chain.md" +++ "b/fr/03_Dessiner_un_triangle/01_Pr\303\251sentation/01_Swap_chain.md" @@ -458,7 +458,7 @@ informations, vous obtiendrez de meilleures performances en activant ce mode. createInfo.oldSwapchain = VK_NULL_HANDLE; ``` -Il nous reste un dernier champ, `oldSwapChain`. Il est possible avec Vulkan que la swap chain devienne +Il nous reste un dernier champ, `oldSwapchain`. Il est possible avec Vulkan que la swap chain devienne invalide ou mal adaptée pendant que votre application tourne, par exemple parce que la fenêtre a été redimensionnée. Dans ce cas la swap chain doit être intégralement recréée et une référence à l'ancienne swap chain doit être fournie. C'est un sujet compliqué que nous aborderons [dans un chapitre futur](!fr/Dessiner_un_triangle/Recréation_de_la_swap_chain). diff --git "a/fr/03_Dessiner_un_triangle/04_Recr\303\251ation_de_la_swap_chain.md" "b/fr/03_Dessiner_un_triangle/04_Recr\303\251ation_de_la_swap_chain.md" index 4f979076..175ae235 100644 --- "a/fr/03_Dessiner_un_triangle/04_Recr\303\251ation_de_la_swap_chain.md" +++ "b/fr/03_Dessiner_un_triangle/04_Recr\303\251ation_de_la_swap_chain.md" @@ -58,18 +58,12 @@ Nous allons déplacer le code de suppression depuis `cleanup` jusqu'à `cleanupS ```c++ void cleanupSwapChain() { - for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { - vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + for (auto framebuffer : swapChainFramebuffers) { + vkDestroyFramebuffer(device, framebuffer, nullptr); } - vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); - - vkDestroyPipeline(device, graphicsPipeline, nullptr); - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyRenderPass(device, renderPass, nullptr); - - for (size_t i = 0; i < swapChainImageViews.size(); i++) { - vkDestroyImageView(device, swapChainImageViews[i], nullptr); + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(device, imageView, nullptr); } vkDestroySwapchainKHR(device, swapChain, nullptr); @@ -82,6 +76,11 @@ Nous pouvons ensuite appeler cette nouvelle fonction depuis `cleanup` pour évit void cleanup() { cleanupSwapChain(); + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + + vkDestroyRenderPass(device, renderPass, nullptr); + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); diff --git "a/fr/08_Charger_des_mod\303\250les.md" "b/fr/08_Charger_des_mod\303\250les.md" index 13a9b35e..04cdfda1 100644 --- "a/fr/08_Charger_des_mod\303\250les.md" +++ "b/fr/08_Charger_des_mod\303\250les.md" @@ -120,10 +120,10 @@ void loadModel() { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; - std::string warn, err; + std::string err; - if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, MODEL_PATH.c_str())) { - throw std::runtime_error(warn + err); + if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, MODEL_PATH.c_str())) { + throw std::runtime_error(err); } } ``` diff --git "a/fr/09_G\303\251n\303\251rer_des_mipmaps.md" "b/fr/09_G\303\251n\303\251rer_des_mipmaps.md" index 33ffaa25..7d328479 100644 --- "a/fr/09_G\303\251n\303\251rer_des_mipmaps.md" +++ "b/fr/09_G\303\251n\303\251rer_des_mipmaps.md" @@ -375,14 +375,13 @@ void createTextureSampler() { ... samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.minLod = 0.0f; - samplerInfo.maxLod = static_cast(mipLevels); + samplerInfo.maxLod = VK_LOD_CLAMP_NONE; samplerInfo.mipLodBias = 0.0f; // Optionnel ... } ``` -Pour utiliser la totalité des niveaux de mipmaps, nous mettons `minLod` à `0.0f` et `maxLod` au nombre de niveaux de -mipmaps. Nous n'avons aucune raison d'altérer `lod` avec `mipLodBias`, alors nous pouvons le mettre à `0.0f`. +Pour utiliser la totalité des niveaux de mipmaps, nous mettons `minLod` à `0.0f` et `maxLod` à `VK_LOD_CLAMP_NONE`. Cette constante est égale à `1000.0f`, ce qui veut dire que la totalité des niveaux de mipmaps disponible dans la texture sera échantillonée. Nous n'avons aucune raison d'altérer `lod` avec `mipLodBias`, alors nous pouvons le mettre à `0.0f`. Lancez votre programme et vous devriez voir ceci : diff --git a/make_parser.py b/make_parser.py new file mode 100644 index 00000000..8d0c5b5b --- /dev/null +++ b/make_parser.py @@ -0,0 +1,37 @@ +import argparse + +def make_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Build the pdf and epub files of the Vulkan Tutorial." + ) + + parser.add_argument( + "--geometry:left", + type=str, + required=False, + default="2.5cm", + help="Specify left margin space as a string. Example: 2cm.", + ) + parser.add_argument( + "--geometry:right", + type=str, + required=False, + default="2.5cm", + help="Specify right margin space as a string. Example: 2cm.", + ) + parser.add_argument( + "--geometry:top", + type=str, + required=False, + default="2.5cm", + help="Specify top margin space as a string. Example: 2cm.", + ) + parser.add_argument( + "--geometry:bottom", + type=str, + required=False, + default="2.5cm", + help="Specify bottom margin space as a string. Example: 2cm.", + ) + + return parser diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..7749678c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "vulkantutorial" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = "~=3.13.0" +dependencies = [] diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..84d6b942 --- /dev/null +++ b/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 3 +requires-python = "==3.13.*" + +[[package]] +name = "vulkantutorial" +version = "0.1.0" +source = { virtual = "." }