├── .gitignore ├── LICENSE.md ├── LICENSE.txt ├── README.md ├── SUMMARY.md ├── docs ├── .gitignore ├── LICENSE.md ├── LICENSE.txt ├── docs │ ├── .gitignore │ ├── LICENSE.md │ ├── LICENSE.txt │ ├── gitbook │ │ ├── fonts │ │ │ └── fontawesome │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ ├── gitbook-plugin-fontsettings │ │ │ ├── fontsettings.js │ │ │ └── website.css │ │ ├── gitbook-plugin-highlight │ │ │ ├── ebook.css │ │ │ └── website.css │ │ ├── gitbook-plugin-lunr │ │ │ ├── lunr.min.js │ │ │ └── search-lunr.js │ │ ├── gitbook-plugin-search │ │ │ ├── lunr.min.js │ │ │ ├── search-engine.js │ │ │ ├── search.css │ │ │ └── search.js │ │ ├── gitbook-plugin-sharing │ │ │ └── buttons.js │ │ ├── gitbook.js │ │ ├── images │ │ │ ├── apple-touch-icon-precomposed-152.png │ │ │ └── favicon.ico │ │ ├── style.css │ │ └── theme.js │ ├── index.html │ ├── search_index.json │ ├── tools.html │ └── 画个三角形吧 │ │ ├── 1.0.1.png │ │ ├── 1.0创建窗口.html │ │ ├── 1.0创建窗口.md │ │ ├── 1.1.2.png │ │ ├── 1.10处理输入事件.html │ │ ├── 1.10处理输入事件.md │ │ ├── 1.11写一个clearcolor.html │ │ ├── 1.11写一个clearcolor.md │ │ ├── 1.12渲染过程.html │ │ ├── 1.12渲染过程.md │ │ ├── 1.13把图形管线写完.html │ │ ├── 1.13把图形管线写完.md │ │ ├── 1.14帧缓冲.html │ │ ├── 1.14帧缓冲.md │ │ ├── 1.15命令池.html │ │ ├── 1.15命令池.md │ │ ├── 1.16劳资要的三角形.html │ │ ├── 1.16劳资要的三角形.md │ │ ├── 1.17小结.html │ │ ├── 1.17小结.md │ │ ├── 1.1创建实例.html │ │ ├── 1.1创建实例.md │ │ ├── 1.2配置log.html │ │ ├── 1.2配置log.md │ │ ├── 1.3物理逻辑设备.html │ │ ├── 1.3物理逻辑设备.md │ │ ├── 1.4.1.png │ │ ├── 1.4创建交换链.html │ │ ├── 1.4创建交换链.md │ │ ├── 1.5.1.png │ │ ├── 1.5设置同步.html │ │ ├── 1.5设置同步.md │ │ ├── 1.6创建renderpass.html │ │ ├── 1.6创建renderpass.md │ │ ├── 1.7创建图片视图和帧缓冲.html │ │ ├── 1.7创建图片视图和帧缓冲.md │ │ ├── 1.8创建命令池和命令缓冲.html │ │ ├── 1.8创建命令池和命令缓冲.md │ │ ├── 1.9写一个drop.html │ │ ├── 1.9写一个drop.md │ │ ├── README.md │ │ └── index.html ├── gitbook │ ├── fonts │ │ └── fontawesome │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ ├── gitbook-plugin-fontsettings │ │ ├── fontsettings.js │ │ └── website.css │ ├── gitbook-plugin-highlight │ │ ├── ebook.css │ │ └── website.css │ ├── gitbook-plugin-lunr │ │ ├── lunr.min.js │ │ └── search-lunr.js │ ├── gitbook-plugin-search │ │ ├── lunr.min.js │ │ ├── search-engine.js │ │ ├── search.css │ │ └── search.js │ ├── gitbook-plugin-sharing │ │ └── buttons.js │ ├── gitbook.js │ ├── images │ │ ├── apple-touch-icon-precomposed-152.png │ │ └── favicon.ico │ ├── style.css │ └── theme.js ├── index.html ├── search_index.json ├── tools.html └── 画个三角形吧 │ ├── 1.0.1.png │ ├── 1.0创建窗口.html │ ├── 1.0创建窗口.md │ ├── 1.1.2.png │ ├── 1.10处理输入事件.html │ ├── 1.10处理输入事件.md │ ├── 1.11.1.png │ ├── 1.11写一个clearcolor.html │ ├── 1.11写一个clearcolor.md │ ├── 1.12渲染过程.html │ ├── 1.12渲染过程.md │ ├── 1.13把图形管线写完.html │ ├── 1.13把图形管线写完.md │ ├── 1.14帧缓冲.html │ ├── 1.14帧缓冲.md │ ├── 1.15命令池.html │ ├── 1.15命令池.md │ ├── 1.16劳资要的三角形.html │ ├── 1.16劳资要的三角形.md │ ├── 1.17小结.html │ ├── 1.17小结.md │ ├── 1.1创建实例.html │ ├── 1.1创建实例.md │ ├── 1.2配置log.html │ ├── 1.2配置log.md │ ├── 1.3物理逻辑设备.html │ ├── 1.3物理逻辑设备.md │ ├── 1.4.1.png │ ├── 1.4创建交换链.html │ ├── 1.4创建交换链.md │ ├── 1.5.1.png │ ├── 1.5设置同步.html │ ├── 1.5设置同步.md │ ├── 1.6创建renderpass.html │ ├── 1.6创建renderpass.md │ ├── 1.7创建图片视图和帧缓冲.html │ ├── 1.7创建图片视图和帧缓冲.md │ ├── 1.8创建命令池和命令缓冲.html │ ├── 1.8创建命令池和命令缓冲.md │ ├── 1.9写一个drop.html │ ├── 1.9写一个drop.md │ ├── README.md │ └── index.html ├── tools.md └── 画个三角形吧 ├── 1.0.1.png ├── 1.0创建窗口.md ├── 1.1.2.png ├── 1.10处理输入事件.md ├── 1.11.1.png ├── 1.11写一个clearcolor.md ├── 1.12渲染过程.md ├── 1.13把图形管线写完.md ├── 1.14帧缓冲.md ├── 1.15命令池.md ├── 1.16劳资要的三角形.md ├── 1.17小结.md ├── 1.1创建实例.md ├── 1.2配置log.md ├── 1.3物理逻辑设备.md ├── 1.4.1.png ├── 1.4创建交换链.md ├── 1.5.1.png ├── 1.5设置同步.md ├── 1.6创建renderpass.md ├── 1.7创建图片视图和帧缓冲.md ├── 1.8创建命令池和命令缓冲.md ├── 1.9写一个drop.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | _book -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # 🍋柠檬浣熊的向现实低头开源协议 (LXXSDT-License) 2 | 3 | 4 | > 版权© 2019 LemonHX(lemonhx@lemonhx.tech) 5 | > 6 | > 注意下,LXXSDT-License是此协议的官方名称,禁止修改 7 | 8 | 特此授予任何阅读过此篇协议的人源代码的阅读权限 9 | 若没有阅读此协议, 任何关于此开源项目的行为均是禁止的. 10 | 11 | 被此协议授权的文档及软件都必须遵守当前地区的法律法规, 12 | 因此,此协议位于法规之下. 13 | 14 | 任何使用此协议授权的软件和文档都可以通过附加子协议来严格的规范其他用户的使用权限, 15 | 所以,所有的权限如果同子协议有冲突那么将按照子协议定义的权限来解释. 16 | 17 | 子协议的命名规范是SUBLICENSE.txt/SUBLICENSE.md 18 | 不符合命名规范的任何子协议不适用于上述条款. 19 | 20 | 此协议授权,任何用户均可无限制的阅读,复制,分发本软件的副本. 21 | 22 | 对于内容的修改,合并,再发布以及出售须符合以下条款: 23 | 24 | 1. 声明修改内容与原作者无关 25 | 2. 再发布方或出售方依据企业规模适当的向原作者提供经济资助 26 | 帮原作者解决一部分日常花销,或直接聘用原作者. 27 | 提供经济的建议的方法有: 28 | - 给作者买最喜欢的苹果2kg. 29 | - 给作者买一杯星巴克咖啡. 30 | 3. 在此,私认为用户理应为新加的代码提供必要的文档但这并不是必须的 31 | 不过若想和原项目进行合并或修改原项目请务必写好齐全的文档. 32 | 33 | 4. 对于个人用户和开源社区用户,欲想使用此协议授权下的项目用作非盈利项目, 34 | 须先对作者给予肯定(星),之后可以采用下述行为: 35 | - 通过各种方式,可以是不正式的向作者发送一封感谢信,并与Ta交朋友. 36 | - 报告错误或问题,帮助作者修改. 37 | - 索要作者QQ号码,并给作者开一年XX会员. 38 | 39 | 在此协议授权下,这个项目的贡献者拥有对此协议的署名权, 40 | 并享有此协议的全部特权但不可删除或修改原协议. 41 | 42 | --- 43 | 44 | ### ENGLISH VERSION 45 | 46 | --- 47 | 48 | # 🍋LemonHX's Bow To Life(XXSDT) License (LXXSDT-License) 49 | 50 | 51 | > Copyright © 2019 by LemonHX(lemonhx@lemonhx.tech) 52 | > 53 | > Note that LXXSDT-License is the official name of this agreement 54 | and is forbidden to be modified. 55 | 56 | It is hereby granted permission to read any source code to whom has read this agreement. 57 | If you do not read this agreement, 58 | any action on this open-source project is prohibited. 59 | 60 | Documents and software authorized by this agreement must comply with current local laws and regulations. 61 | Therefore, this agreement is under the regulations. 62 | 63 | Any software and documentation licensed under this agreement could use alternative sublicense to strictly regulate the access rights of other users. 64 | Therefore, all permissions will be interpreted according to the permissions defined by the sublicense if they conflict with the sublicense. 65 | 66 | The naming convention for sublicense is SUBLICENSE.txt/SUBLICENSE.md 67 | Any sublicense that does not conform to the naming convention does not apply to the above terms. 68 | 69 | This agreement authorizes that any user can 70 | read, copy, and distribute copies of the software without restriction. 71 | 72 | Modifications, mergers, redistributions, and sales of content are subject to the following terms: 73 | 1. The changes are not related to the original author. 74 | 75 | 2. The re-publisher or seller shall provide financial assistance to the original author according to the size of the enterprise. 76 | Help the original author to solve some of the daily expenses, or directly hire the original author. 77 | Ways to provide economic advice are: 78 | - Buy apple 2kg for the author. 79 | - Buy a cup of Starbucks coffee for the author. 80 | 81 | 3. privately believe that the user should provide the necessary documentation for the newly added code, but this is not required. 82 | However, if you want to merge with the original project or modify the original project, please be sure to write a complete document. 83 | 84 | 4. For individual users and open source community users, if you want to use the projects authorized by this agreement for non-profit projects, 85 | The author must be affirmed (star) first, after which the following behavior can be used: 86 | - In a variety of ways, you can send a thank you letter to the author and make friends with Ta. 87 | - Report bugs or issues to help the author modify. 88 | - Ask the author's QQ number and give the author a year of XX membership. 89 | 90 | Under the authority of this agreement, the contributors to this project have the right to authorize this agreement. 91 | And enjoy all the privileges of this agreement but can not delete or modify the original agreement. 92 | 93 | --- 94 | 95 | ### 经典几大行/Classic Lines 96 | 97 | --- 98 | 99 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 100 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 101 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 102 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 103 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 104 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 105 | 106 | 107 | 108 | --- 109 | 110 | ### 署名人/Signer 111 | 112 | --- 113 | - **🍋LemonHX(lemonhx@lemonhx.tech)** -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 🍋柠檬浣熊的向现实低头开源协议 (LXXSDT-License) 2 | 3 | 4 | -- 版权© 2019 LemonHX(lemonhx@lemonhx.tech) 5 | -- 注意下,LXXSDT-License是此协议的官方名称,禁止修改 6 | 7 | 特此授予任何阅读过此篇协议的人源代码的阅读权限 8 | 若没有阅读此协议, 任何关于此开源项目的行为均是禁止的. 9 | 10 | 被此协议授权的文档及软件都必须遵守当前地区的法律法规, 11 | 因此,此协议位于法规之下. 12 | 13 | 任何使用此协议授权的软件和文档都可以通过附加子协议来严格的规范其他用户的使用权限, 14 | 所以,所有的权限如果同子协议有冲突那么将按照子协议定义的权限来解释. 15 | 16 | 子协议的命名规范是SUBLICENSE.txt/SUBLICENSE.md 17 | 不符合命名规范的任何子协议不适用于上述条款. 18 | 19 | 此协议授权,任何用户均可无限制的阅读,复制,分发本软件的副本. 20 | 21 | 对于内容的修改,合并,再发布以及出售须符合以下条款: 22 | 23 | 1. 声明修改内容与原作者无关 24 | 2. 再发布方或出售方依据企业规模适当的向原作者提供经济资助 25 | 帮原作者解决一部分日常花销,或直接聘用原作者. 26 | 提供经济的建议的方法有: 27 | - 给作者买最喜欢的苹果2kg. 28 | - 给作者买一杯星巴克咖啡. 29 | 3. 在此,私认为用户理应为新加的代码提供必要的文档但这并不是必须的 30 | 不过若想和原项目进行合并或修改原项目请务必写好齐全的文档. 31 | 32 | 4. 对于个人用户和开源社区用户,欲想使用此协议授权下的项目用作非盈利项目, 33 | 须先对作者给予肯定(星),之后可以采用下述行为: 34 | - 通过各种方式,可以是不正式的向作者发送一封感谢信,并与Ta交朋友. 35 | - 报告错误或问题,帮助作者修改. 36 | - 索要作者XX号码,并给作者开一年XX会员. 37 | 38 | 在此协议授权下,这个项目的贡献者拥有对此协议的署名权, 39 | 并享有此协议的全部特权但不可删除或修改原协议. 40 | 41 | =================== 42 | ENGLISH VERSION 43 | =================== 44 | 45 | 🍋LemonHX's Bow To Life(XXSDT) License (LXXSDT-License) 46 | 47 | 48 | -- Copyright © 2019 by LemonHX(lemonhx@lemonhx.tech) 49 | -- Note that LXXSDT-License is the official name of this agreement 50 | and is forbidden to be modified. 51 | 52 | It is hereby granted permission to read any source code to whom has read this agreement. 53 | If you do not read this agreement, 54 | any action on this open-source project is prohibited. 55 | 56 | Documents and software authorized by this agreement must comply with current local laws and regulations. 57 | Therefore, this agreement is under the regulations. 58 | 59 | Any software and documentation licensed under this agreement could use alternative sublicense to strictly regulate the access rights of other users. 60 | Therefore, all permissions will be interpreted according to the permissions defined by the sublicense if they conflict with the sublicense. 61 | 62 | The naming convention for sublicense is SUBLICENSE.txt/SUBLICENSE.md 63 | Any sublicense that does not conform to the naming convention does not apply to the above terms. 64 | 65 | This agreement authorizes that any user can 66 | read, copy, and distribute copies of the software without restriction. 67 | 68 | Modifications, mergers, redistributions, and sales of content are subject to the following terms: 69 | 1. The changes are not related to the original author. 70 | 71 | 2. The re-publisher or seller shall provide financial assistance to the original author according to the size of the enterprise. 72 | Help the original author to solve some of the daily expenses, or directly hire the original author. 73 | Ways to provide economic advice are: 74 | - Buy apple 2kg for the author. 75 | - Buy a cup of Starbucks coffee for the author. 76 | 77 | 3. privately believe that the user should provide the necessary documentation for the newly added code, but this is not required. 78 | However, if you want to merge with the original project or modify the original project, please be sure to write a complete document. 79 | 80 | 4. For individual users and open source community users, if you want to use the projects authorized by this agreement for non-profit projects, 81 | The author must be affirmed (star) first, after which the following behavior can be used: 82 | - In a variety of ways, you can send a thank you letter to the author and make friends with Ta. 83 | - Report bugs or issues to help the author modify. 84 | - Ask the author's XX number and give the author a year of XX membership. 85 | 86 | Under the authority of this agreement, the contributors to this project have the right to authorize this agreement. 87 | And enjoy all the privileges of this agreement but can not delete or modify the original agreement. 88 | 89 | ========== 经典几大行/Classic Lines =========== 90 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 91 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 92 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 93 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 94 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 95 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 96 | 97 | 98 | 99 | =================================== 100 | =========== 署名人/Signer ========== 101 | =================================== 102 | 🍋LemonHX(lemonhx@lemonhx.tech) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 前言 2 | ## 预警: 本教程适用于对自己的Rust水平有一定自信的学生 3 | 对不起,久等了,整个暑假都在摸鱼啥都没更新 4 | 但是我终于把这个上传上去了 -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [前言](README.md) 4 | * [工具安装](tools.md) 5 | * [1.画个三角形吧](画个三角形吧/README.md) 6 | * [1.0 创建窗口](画个三角形吧/1.0创建窗口.md) 7 | * [1.1 创建实例](画个三角形吧/1.1创建实例.md) 8 | * [1.1.1 配置log](画个三角形吧/1.2配置log.md) 9 | * [1.1.2 选择物理设备和逻辑设备](画个三角形吧/1.3物理逻辑设备.md) 10 | * [1.1.3 交换链](画个三角形吧/1.4创建交换链.md) 11 | * [1.1.4 设置同步](画个三角形吧/1.5设置同步.md) 12 | * [1.1.5 renderpass](画个三角形吧/1.6创建renderpass.md) 13 | * [1.1.6 图片视图和帧缓冲](画个三角形吧/1.7创建图片视图和帧缓冲.md) 14 | * [1.1.7 命令池和命令缓冲](画个三角形吧/1.8创建命令池和命令缓冲.md) 15 | * [1.1.8 实现DROP](画个三角形吧/1.9写一个drop.md) 16 | * [1.1.9 处理输入事件](画个三角形吧/1.10处理输入事件.md) 17 | * [1.1.10 高端清屏](画个三角形吧/1.11写一个clearcolor.md) 18 | * [1.1.11 渲染过程](画个三角形吧/1.12渲染过程.md) 19 | * [1.1.12 把图形管线写完](画个三角形吧/1.13把图形管线写完.md) 20 | * [1.1.13 帧缓冲](画个三角形吧/1.14帧缓冲.md) 21 | * [1.1.14 命令池](画个三角形吧/1.15命令池.md) 22 | * [1.2 劳资要的三角形](画个三角形吧/1.16劳资要的三角形.md) 23 | * [1.3 小结](画个三角形吧/1.17小结.md) 24 | 25 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _book -------------------------------------------------------------------------------- /docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | # 🍋柠檬浣熊的向现实低头开源协议 (LXXSDT-License) 2 | 3 | 4 | > 版权© 2019 LemonHX(lemonhx@lemonhx.tech) 5 | > 6 | > 注意下,LXXSDT-License是此协议的官方名称,禁止修改 7 | 8 | 特此授予任何阅读过此篇协议的人源代码的阅读权限 9 | 若没有阅读此协议, 任何关于此开源项目的行为均是禁止的. 10 | 11 | 被此协议授权的文档及软件都必须遵守当前地区的法律法规, 12 | 因此,此协议位于法规之下. 13 | 14 | 任何使用此协议授权的软件和文档都可以通过附加子协议来严格的规范其他用户的使用权限, 15 | 所以,所有的权限如果同子协议有冲突那么将按照子协议定义的权限来解释. 16 | 17 | 子协议的命名规范是SUBLICENSE.txt/SUBLICENSE.md 18 | 不符合命名规范的任何子协议不适用于上述条款. 19 | 20 | 此协议授权,任何用户均可无限制的阅读,复制,分发本软件的副本. 21 | 22 | 对于内容的修改,合并,再发布以及出售须符合以下条款: 23 | 24 | 1. 声明修改内容与原作者无关 25 | 2. 再发布方或出售方依据企业规模适当的向原作者提供经济资助 26 | 帮原作者解决一部分日常花销,或直接聘用原作者. 27 | 提供经济的建议的方法有: 28 | - 给作者买最喜欢的苹果2kg. 29 | - 给作者买一杯星巴克咖啡. 30 | 3. 在此,私认为用户理应为新加的代码提供必要的文档但这并不是必须的 31 | 不过若想和原项目进行合并或修改原项目请务必写好齐全的文档. 32 | 33 | 4. 对于个人用户和开源社区用户,欲想使用此协议授权下的项目用作非盈利项目, 34 | 须先对作者给予肯定(星),之后可以采用下述行为: 35 | - 通过各种方式,可以是不正式的向作者发送一封感谢信,并与Ta交朋友. 36 | - 报告错误或问题,帮助作者修改. 37 | - 索要作者QQ号码,并给作者开一年XX会员. 38 | 39 | 在此协议授权下,这个项目的贡献者拥有对此协议的署名权, 40 | 并享有此协议的全部特权但不可删除或修改原协议. 41 | 42 | --- 43 | 44 | ### ENGLISH VERSION 45 | 46 | --- 47 | 48 | # 🍋LemonHX's Bow To Life(XXSDT) License (LXXSDT-License) 49 | 50 | 51 | > Copyright © 2019 by LemonHX(lemonhx@lemonhx.tech) 52 | > 53 | > Note that LXXSDT-License is the official name of this agreement 54 | and is forbidden to be modified. 55 | 56 | It is hereby granted permission to read any source code to whom has read this agreement. 57 | If you do not read this agreement, 58 | any action on this open-source project is prohibited. 59 | 60 | Documents and software authorized by this agreement must comply with current local laws and regulations. 61 | Therefore, this agreement is under the regulations. 62 | 63 | Any software and documentation licensed under this agreement could use alternative sublicense to strictly regulate the access rights of other users. 64 | Therefore, all permissions will be interpreted according to the permissions defined by the sublicense if they conflict with the sublicense. 65 | 66 | The naming convention for sublicense is SUBLICENSE.txt/SUBLICENSE.md 67 | Any sublicense that does not conform to the naming convention does not apply to the above terms. 68 | 69 | This agreement authorizes that any user can 70 | read, copy, and distribute copies of the software without restriction. 71 | 72 | Modifications, mergers, redistributions, and sales of content are subject to the following terms: 73 | 1. The changes are not related to the original author. 74 | 75 | 2. The re-publisher or seller shall provide financial assistance to the original author according to the size of the enterprise. 76 | Help the original author to solve some of the daily expenses, or directly hire the original author. 77 | Ways to provide economic advice are: 78 | - Buy apple 2kg for the author. 79 | - Buy a cup of Starbucks coffee for the author. 80 | 81 | 3. privately believe that the user should provide the necessary documentation for the newly added code, but this is not required. 82 | However, if you want to merge with the original project or modify the original project, please be sure to write a complete document. 83 | 84 | 4. For individual users and open source community users, if you want to use the projects authorized by this agreement for non-profit projects, 85 | The author must be affirmed (star) first, after which the following behavior can be used: 86 | - In a variety of ways, you can send a thank you letter to the author and make friends with Ta. 87 | - Report bugs or issues to help the author modify. 88 | - Ask the author's QQ number and give the author a year of XX membership. 89 | 90 | Under the authority of this agreement, the contributors to this project have the right to authorize this agreement. 91 | And enjoy all the privileges of this agreement but can not delete or modify the original agreement. 92 | 93 | --- 94 | 95 | ### 经典几大行/Classic Lines 96 | 97 | --- 98 | 99 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 100 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 101 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 102 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 103 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 104 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 105 | 106 | 107 | 108 | --- 109 | 110 | ### 署名人/Signer 111 | 112 | --- 113 | - **🍋LemonHX(lemonhx@lemonhx.tech)** -------------------------------------------------------------------------------- /docs/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 🍋柠檬浣熊的向现实低头开源协议 (LXXSDT-License) 2 | 3 | 4 | -- 版权© 2019 LemonHX(lemonhx@lemonhx.tech) 5 | -- 注意下,LXXSDT-License是此协议的官方名称,禁止修改 6 | 7 | 特此授予任何阅读过此篇协议的人源代码的阅读权限 8 | 若没有阅读此协议, 任何关于此开源项目的行为均是禁止的. 9 | 10 | 被此协议授权的文档及软件都必须遵守当前地区的法律法规, 11 | 因此,此协议位于法规之下. 12 | 13 | 任何使用此协议授权的软件和文档都可以通过附加子协议来严格的规范其他用户的使用权限, 14 | 所以,所有的权限如果同子协议有冲突那么将按照子协议定义的权限来解释. 15 | 16 | 子协议的命名规范是SUBLICENSE.txt/SUBLICENSE.md 17 | 不符合命名规范的任何子协议不适用于上述条款. 18 | 19 | 此协议授权,任何用户均可无限制的阅读,复制,分发本软件的副本. 20 | 21 | 对于内容的修改,合并,再发布以及出售须符合以下条款: 22 | 23 | 1. 声明修改内容与原作者无关 24 | 2. 再发布方或出售方依据企业规模适当的向原作者提供经济资助 25 | 帮原作者解决一部分日常花销,或直接聘用原作者. 26 | 提供经济的建议的方法有: 27 | - 给作者买最喜欢的苹果2kg. 28 | - 给作者买一杯星巴克咖啡. 29 | 3. 在此,私认为用户理应为新加的代码提供必要的文档但这并不是必须的 30 | 不过若想和原项目进行合并或修改原项目请务必写好齐全的文档. 31 | 32 | 4. 对于个人用户和开源社区用户,欲想使用此协议授权下的项目用作非盈利项目, 33 | 须先对作者给予肯定(星),之后可以采用下述行为: 34 | - 通过各种方式,可以是不正式的向作者发送一封感谢信,并与Ta交朋友. 35 | - 报告错误或问题,帮助作者修改. 36 | - 索要作者XX号码,并给作者开一年XX会员. 37 | 38 | 在此协议授权下,这个项目的贡献者拥有对此协议的署名权, 39 | 并享有此协议的全部特权但不可删除或修改原协议. 40 | 41 | =================== 42 | ENGLISH VERSION 43 | =================== 44 | 45 | 🍋LemonHX's Bow To Life(XXSDT) License (LXXSDT-License) 46 | 47 | 48 | -- Copyright © 2019 by LemonHX(lemonhx@lemonhx.tech) 49 | -- Note that LXXSDT-License is the official name of this agreement 50 | and is forbidden to be modified. 51 | 52 | It is hereby granted permission to read any source code to whom has read this agreement. 53 | If you do not read this agreement, 54 | any action on this open-source project is prohibited. 55 | 56 | Documents and software authorized by this agreement must comply with current local laws and regulations. 57 | Therefore, this agreement is under the regulations. 58 | 59 | Any software and documentation licensed under this agreement could use alternative sublicense to strictly regulate the access rights of other users. 60 | Therefore, all permissions will be interpreted according to the permissions defined by the sublicense if they conflict with the sublicense. 61 | 62 | The naming convention for sublicense is SUBLICENSE.txt/SUBLICENSE.md 63 | Any sublicense that does not conform to the naming convention does not apply to the above terms. 64 | 65 | This agreement authorizes that any user can 66 | read, copy, and distribute copies of the software without restriction. 67 | 68 | Modifications, mergers, redistributions, and sales of content are subject to the following terms: 69 | 1. The changes are not related to the original author. 70 | 71 | 2. The re-publisher or seller shall provide financial assistance to the original author according to the size of the enterprise. 72 | Help the original author to solve some of the daily expenses, or directly hire the original author. 73 | Ways to provide economic advice are: 74 | - Buy apple 2kg for the author. 75 | - Buy a cup of Starbucks coffee for the author. 76 | 77 | 3. privately believe that the user should provide the necessary documentation for the newly added code, but this is not required. 78 | However, if you want to merge with the original project or modify the original project, please be sure to write a complete document. 79 | 80 | 4. For individual users and open source community users, if you want to use the projects authorized by this agreement for non-profit projects, 81 | The author must be affirmed (star) first, after which the following behavior can be used: 82 | - In a variety of ways, you can send a thank you letter to the author and make friends with Ta. 83 | - Report bugs or issues to help the author modify. 84 | - Ask the author's XX number and give the author a year of XX membership. 85 | 86 | Under the authority of this agreement, the contributors to this project have the right to authorize this agreement. 87 | And enjoy all the privileges of this agreement but can not delete or modify the original agreement. 88 | 89 | ========== 经典几大行/Classic Lines =========== 90 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 91 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 92 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 93 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 94 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 95 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 96 | 97 | 98 | 99 | =================================== 100 | =========== 署名人/Signer ========== 101 | =================================== 102 | 🍋LemonHX(lemonhx@lemonhx.tech) -------------------------------------------------------------------------------- /docs/docs/.gitignore: -------------------------------------------------------------------------------- 1 | _book -------------------------------------------------------------------------------- /docs/docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | # 🍋柠檬浣熊的向现实低头开源协议 (LXXSDT-License) 2 | 3 | 4 | > 版权© 2019 LemonHX(lemonhx@lemonhx.tech) 5 | > 6 | > 注意下,LXXSDT-License是此协议的官方名称,禁止修改 7 | 8 | 特此授予任何阅读过此篇协议的人源代码的阅读权限 9 | 若没有阅读此协议, 任何关于此开源项目的行为均是禁止的. 10 | 11 | 被此协议授权的文档及软件都必须遵守当前地区的法律法规, 12 | 因此,此协议位于法规之下. 13 | 14 | 任何使用此协议授权的软件和文档都可以通过附加子协议来严格的规范其他用户的使用权限, 15 | 所以,所有的权限如果同子协议有冲突那么将按照子协议定义的权限来解释. 16 | 17 | 子协议的命名规范是SUBLICENSE.txt/SUBLICENSE.md 18 | 不符合命名规范的任何子协议不适用于上述条款. 19 | 20 | 此协议授权,任何用户均可无限制的阅读,复制,分发本软件的副本. 21 | 22 | 对于内容的修改,合并,再发布以及出售须符合以下条款: 23 | 24 | 1. 声明修改内容与原作者无关 25 | 2. 再发布方或出售方依据企业规模适当的向原作者提供经济资助 26 | 帮原作者解决一部分日常花销,或直接聘用原作者. 27 | 提供经济的建议的方法有: 28 | - 给作者买最喜欢的苹果2kg. 29 | - 给作者买一杯星巴克咖啡. 30 | 3. 在此,私认为用户理应为新加的代码提供必要的文档但这并不是必须的 31 | 不过若想和原项目进行合并或修改原项目请务必写好齐全的文档. 32 | 33 | 4. 对于个人用户和开源社区用户,欲想使用此协议授权下的项目用作非盈利项目, 34 | 须先对作者给予肯定(星),之后可以采用下述行为: 35 | - 通过各种方式,可以是不正式的向作者发送一封感谢信,并与Ta交朋友. 36 | - 报告错误或问题,帮助作者修改. 37 | - 索要作者QQ号码,并给作者开一年XX会员. 38 | 39 | 在此协议授权下,这个项目的贡献者拥有对此协议的署名权, 40 | 并享有此协议的全部特权但不可删除或修改原协议. 41 | 42 | --- 43 | 44 | ### ENGLISH VERSION 45 | 46 | --- 47 | 48 | # 🍋LemonHX's Bow To Life(XXSDT) License (LXXSDT-License) 49 | 50 | 51 | > Copyright © 2019 by LemonHX(lemonhx@lemonhx.tech) 52 | > 53 | > Note that LXXSDT-License is the official name of this agreement 54 | and is forbidden to be modified. 55 | 56 | It is hereby granted permission to read any source code to whom has read this agreement. 57 | If you do not read this agreement, 58 | any action on this open-source project is prohibited. 59 | 60 | Documents and software authorized by this agreement must comply with current local laws and regulations. 61 | Therefore, this agreement is under the regulations. 62 | 63 | Any software and documentation licensed under this agreement could use alternative sublicense to strictly regulate the access rights of other users. 64 | Therefore, all permissions will be interpreted according to the permissions defined by the sublicense if they conflict with the sublicense. 65 | 66 | The naming convention for sublicense is SUBLICENSE.txt/SUBLICENSE.md 67 | Any sublicense that does not conform to the naming convention does not apply to the above terms. 68 | 69 | This agreement authorizes that any user can 70 | read, copy, and distribute copies of the software without restriction. 71 | 72 | Modifications, mergers, redistributions, and sales of content are subject to the following terms: 73 | 1. The changes are not related to the original author. 74 | 75 | 2. The re-publisher or seller shall provide financial assistance to the original author according to the size of the enterprise. 76 | Help the original author to solve some of the daily expenses, or directly hire the original author. 77 | Ways to provide economic advice are: 78 | - Buy apple 2kg for the author. 79 | - Buy a cup of Starbucks coffee for the author. 80 | 81 | 3. privately believe that the user should provide the necessary documentation for the newly added code, but this is not required. 82 | However, if you want to merge with the original project or modify the original project, please be sure to write a complete document. 83 | 84 | 4. For individual users and open source community users, if you want to use the projects authorized by this agreement for non-profit projects, 85 | The author must be affirmed (star) first, after which the following behavior can be used: 86 | - In a variety of ways, you can send a thank you letter to the author and make friends with Ta. 87 | - Report bugs or issues to help the author modify. 88 | - Ask the author's QQ number and give the author a year of XX membership. 89 | 90 | Under the authority of this agreement, the contributors to this project have the right to authorize this agreement. 91 | And enjoy all the privileges of this agreement but can not delete or modify the original agreement. 92 | 93 | --- 94 | 95 | ### 经典几大行/Classic Lines 96 | 97 | --- 98 | 99 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 100 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 101 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 102 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 103 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 104 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 105 | 106 | 107 | 108 | --- 109 | 110 | ### 署名人/Signer 111 | 112 | --- 113 | - **🍋LemonHX(lemonhx@lemonhx.tech)** -------------------------------------------------------------------------------- /docs/docs/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 🍋柠檬浣熊的向现实低头开源协议 (LXXSDT-License) 2 | 3 | 4 | -- 版权© 2019 LemonHX(lemonhx@lemonhx.tech) 5 | -- 注意下,LXXSDT-License是此协议的官方名称,禁止修改 6 | 7 | 特此授予任何阅读过此篇协议的人源代码的阅读权限 8 | 若没有阅读此协议, 任何关于此开源项目的行为均是禁止的. 9 | 10 | 被此协议授权的文档及软件都必须遵守当前地区的法律法规, 11 | 因此,此协议位于法规之下. 12 | 13 | 任何使用此协议授权的软件和文档都可以通过附加子协议来严格的规范其他用户的使用权限, 14 | 所以,所有的权限如果同子协议有冲突那么将按照子协议定义的权限来解释. 15 | 16 | 子协议的命名规范是SUBLICENSE.txt/SUBLICENSE.md 17 | 不符合命名规范的任何子协议不适用于上述条款. 18 | 19 | 此协议授权,任何用户均可无限制的阅读,复制,分发本软件的副本. 20 | 21 | 对于内容的修改,合并,再发布以及出售须符合以下条款: 22 | 23 | 1. 声明修改内容与原作者无关 24 | 2. 再发布方或出售方依据企业规模适当的向原作者提供经济资助 25 | 帮原作者解决一部分日常花销,或直接聘用原作者. 26 | 提供经济的建议的方法有: 27 | - 给作者买最喜欢的苹果2kg. 28 | - 给作者买一杯星巴克咖啡. 29 | 3. 在此,私认为用户理应为新加的代码提供必要的文档但这并不是必须的 30 | 不过若想和原项目进行合并或修改原项目请务必写好齐全的文档. 31 | 32 | 4. 对于个人用户和开源社区用户,欲想使用此协议授权下的项目用作非盈利项目, 33 | 须先对作者给予肯定(星),之后可以采用下述行为: 34 | - 通过各种方式,可以是不正式的向作者发送一封感谢信,并与Ta交朋友. 35 | - 报告错误或问题,帮助作者修改. 36 | - 索要作者XX号码,并给作者开一年XX会员. 37 | 38 | 在此协议授权下,这个项目的贡献者拥有对此协议的署名权, 39 | 并享有此协议的全部特权但不可删除或修改原协议. 40 | 41 | =================== 42 | ENGLISH VERSION 43 | =================== 44 | 45 | 🍋LemonHX's Bow To Life(XXSDT) License (LXXSDT-License) 46 | 47 | 48 | -- Copyright © 2019 by LemonHX(lemonhx@lemonhx.tech) 49 | -- Note that LXXSDT-License is the official name of this agreement 50 | and is forbidden to be modified. 51 | 52 | It is hereby granted permission to read any source code to whom has read this agreement. 53 | If you do not read this agreement, 54 | any action on this open-source project is prohibited. 55 | 56 | Documents and software authorized by this agreement must comply with current local laws and regulations. 57 | Therefore, this agreement is under the regulations. 58 | 59 | Any software and documentation licensed under this agreement could use alternative sublicense to strictly regulate the access rights of other users. 60 | Therefore, all permissions will be interpreted according to the permissions defined by the sublicense if they conflict with the sublicense. 61 | 62 | The naming convention for sublicense is SUBLICENSE.txt/SUBLICENSE.md 63 | Any sublicense that does not conform to the naming convention does not apply to the above terms. 64 | 65 | This agreement authorizes that any user can 66 | read, copy, and distribute copies of the software without restriction. 67 | 68 | Modifications, mergers, redistributions, and sales of content are subject to the following terms: 69 | 1. The changes are not related to the original author. 70 | 71 | 2. The re-publisher or seller shall provide financial assistance to the original author according to the size of the enterprise. 72 | Help the original author to solve some of the daily expenses, or directly hire the original author. 73 | Ways to provide economic advice are: 74 | - Buy apple 2kg for the author. 75 | - Buy a cup of Starbucks coffee for the author. 76 | 77 | 3. privately believe that the user should provide the necessary documentation for the newly added code, but this is not required. 78 | However, if you want to merge with the original project or modify the original project, please be sure to write a complete document. 79 | 80 | 4. For individual users and open source community users, if you want to use the projects authorized by this agreement for non-profit projects, 81 | The author must be affirmed (star) first, after which the following behavior can be used: 82 | - In a variety of ways, you can send a thank you letter to the author and make friends with Ta. 83 | - Report bugs or issues to help the author modify. 84 | - Ask the author's XX number and give the author a year of XX membership. 85 | 86 | Under the authority of this agreement, the contributors to this project have the right to authorize this agreement. 87 | And enjoy all the privileges of this agreement but can not delete or modify the original agreement. 88 | 89 | ========== 经典几大行/Classic Lines =========== 90 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 91 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 92 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 93 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 94 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 95 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 96 | 97 | 98 | 99 | =================================== 100 | =========== 署名人/Signer ========== 101 | =================================== 102 | 🍋LemonHX(lemonhx@lemonhx.tech) -------------------------------------------------------------------------------- /docs/docs/gitbook/fonts/fontawesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/gitbook/fonts/fontawesome/FontAwesome.otf -------------------------------------------------------------------------------- /docs/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/docs/gitbook/gitbook-plugin-fontsettings/fontsettings.js: -------------------------------------------------------------------------------- 1 | require(['gitbook', 'jquery'], function(gitbook, $) { 2 | // Configuration 3 | var MAX_SIZE = 4, 4 | MIN_SIZE = 0, 5 | BUTTON_ID; 6 | 7 | // Current fontsettings state 8 | var fontState; 9 | 10 | // Default themes 11 | var THEMES = [ 12 | { 13 | config: 'white', 14 | text: 'White', 15 | id: 0 16 | }, 17 | { 18 | config: 'sepia', 19 | text: 'Sepia', 20 | id: 1 21 | }, 22 | { 23 | config: 'night', 24 | text: 'Night', 25 | id: 2 26 | } 27 | ]; 28 | 29 | // Default font families 30 | var FAMILIES = [ 31 | { 32 | config: 'serif', 33 | text: 'Serif', 34 | id: 0 35 | }, 36 | { 37 | config: 'sans', 38 | text: 'Sans', 39 | id: 1 40 | } 41 | ]; 42 | 43 | // Return configured themes 44 | function getThemes() { 45 | return THEMES; 46 | } 47 | 48 | // Modify configured themes 49 | function setThemes(themes) { 50 | THEMES = themes; 51 | updateButtons(); 52 | } 53 | 54 | // Return configured font families 55 | function getFamilies() { 56 | return FAMILIES; 57 | } 58 | 59 | // Modify configured font families 60 | function setFamilies(families) { 61 | FAMILIES = families; 62 | updateButtons(); 63 | } 64 | 65 | // Save current font settings 66 | function saveFontSettings() { 67 | gitbook.storage.set('fontState', fontState); 68 | update(); 69 | } 70 | 71 | // Increase font size 72 | function enlargeFontSize(e) { 73 | e.preventDefault(); 74 | if (fontState.size >= MAX_SIZE) return; 75 | 76 | fontState.size++; 77 | saveFontSettings(); 78 | } 79 | 80 | // Decrease font size 81 | function reduceFontSize(e) { 82 | e.preventDefault(); 83 | if (fontState.size <= MIN_SIZE) return; 84 | 85 | fontState.size--; 86 | saveFontSettings(); 87 | } 88 | 89 | // Change font family 90 | function changeFontFamily(configName, e) { 91 | if (e && e instanceof Event) { 92 | e.preventDefault(); 93 | } 94 | 95 | var familyId = getFontFamilyId(configName); 96 | fontState.family = familyId; 97 | saveFontSettings(); 98 | } 99 | 100 | // Change type of color theme 101 | function changeColorTheme(configName, e) { 102 | if (e && e instanceof Event) { 103 | e.preventDefault(); 104 | } 105 | 106 | var $book = gitbook.state.$book; 107 | 108 | // Remove currently applied color theme 109 | if (fontState.theme !== 0) 110 | $book.removeClass('color-theme-'+fontState.theme); 111 | 112 | // Set new color theme 113 | var themeId = getThemeId(configName); 114 | fontState.theme = themeId; 115 | if (fontState.theme !== 0) 116 | $book.addClass('color-theme-'+fontState.theme); 117 | 118 | saveFontSettings(); 119 | } 120 | 121 | // Return the correct id for a font-family config key 122 | // Default to first font-family 123 | function getFontFamilyId(configName) { 124 | // Search for plugin configured font family 125 | var configFamily = $.grep(FAMILIES, function(family) { 126 | return family.config == configName; 127 | })[0]; 128 | // Fallback to default font family 129 | return (!!configFamily)? configFamily.id : 0; 130 | } 131 | 132 | // Return the correct id for a theme config key 133 | // Default to first theme 134 | function getThemeId(configName) { 135 | // Search for plugin configured theme 136 | var configTheme = $.grep(THEMES, function(theme) { 137 | return theme.config == configName; 138 | })[0]; 139 | // Fallback to default theme 140 | return (!!configTheme)? configTheme.id : 0; 141 | } 142 | 143 | function update() { 144 | var $book = gitbook.state.$book; 145 | 146 | $('.font-settings .font-family-list li').removeClass('active'); 147 | $('.font-settings .font-family-list li:nth-child('+(fontState.family+1)+')').addClass('active'); 148 | 149 | $book[0].className = $book[0].className.replace(/\bfont-\S+/g, ''); 150 | $book.addClass('font-size-'+fontState.size); 151 | $book.addClass('font-family-'+fontState.family); 152 | 153 | if(fontState.theme !== 0) { 154 | $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, ''); 155 | $book.addClass('color-theme-'+fontState.theme); 156 | } 157 | } 158 | 159 | function init(config) { 160 | // Search for plugin configured font family 161 | var configFamily = getFontFamilyId(config.family), 162 | configTheme = getThemeId(config.theme); 163 | 164 | // Instantiate font state object 165 | fontState = gitbook.storage.get('fontState', { 166 | size: config.size || 2, 167 | family: configFamily, 168 | theme: configTheme 169 | }); 170 | 171 | update(); 172 | } 173 | 174 | function updateButtons() { 175 | // Remove existing fontsettings buttons 176 | if (!!BUTTON_ID) { 177 | gitbook.toolbar.removeButton(BUTTON_ID); 178 | } 179 | 180 | // Create buttons in toolbar 181 | BUTTON_ID = gitbook.toolbar.createButton({ 182 | icon: 'fa fa-font', 183 | label: 'Font Settings', 184 | className: 'font-settings', 185 | dropdown: [ 186 | [ 187 | { 188 | text: 'A', 189 | className: 'font-reduce', 190 | onClick: reduceFontSize 191 | }, 192 | { 193 | text: 'A', 194 | className: 'font-enlarge', 195 | onClick: enlargeFontSize 196 | } 197 | ], 198 | $.map(FAMILIES, function(family) { 199 | family.onClick = function(e) { 200 | return changeFontFamily(family.config, e); 201 | }; 202 | 203 | return family; 204 | }), 205 | $.map(THEMES, function(theme) { 206 | theme.onClick = function(e) { 207 | return changeColorTheme(theme.config, e); 208 | }; 209 | 210 | return theme; 211 | }) 212 | ] 213 | }); 214 | } 215 | 216 | // Init configuration at start 217 | gitbook.events.bind('start', function(e, config) { 218 | var opts = config.fontsettings; 219 | 220 | // Generate buttons at start 221 | updateButtons(); 222 | 223 | // Init current settings 224 | init(opts); 225 | }); 226 | 227 | // Expose API 228 | gitbook.fontsettings = { 229 | enlargeFontSize: enlargeFontSize, 230 | reduceFontSize: reduceFontSize, 231 | setTheme: changeColorTheme, 232 | setFamily: changeFontFamily, 233 | getThemes: getThemes, 234 | setThemes: setThemes, 235 | getFamilies: getFamilies, 236 | setFamilies: setFamilies 237 | }; 238 | }); 239 | 240 | 241 | -------------------------------------------------------------------------------- /docs/docs/gitbook/gitbook-plugin-fontsettings/website.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Theme 1 3 | */ 4 | .color-theme-1 .dropdown-menu { 5 | background-color: #111111; 6 | border-color: #7e888b; 7 | } 8 | .color-theme-1 .dropdown-menu .dropdown-caret .caret-inner { 9 | border-bottom: 9px solid #111111; 10 | } 11 | .color-theme-1 .dropdown-menu .buttons { 12 | border-color: #7e888b; 13 | } 14 | .color-theme-1 .dropdown-menu .button { 15 | color: #afa790; 16 | } 17 | .color-theme-1 .dropdown-menu .button:hover { 18 | color: #73553c; 19 | } 20 | /* 21 | * Theme 2 22 | */ 23 | .color-theme-2 .dropdown-menu { 24 | background-color: #2d3143; 25 | border-color: #272a3a; 26 | } 27 | .color-theme-2 .dropdown-menu .dropdown-caret .caret-inner { 28 | border-bottom: 9px solid #2d3143; 29 | } 30 | .color-theme-2 .dropdown-menu .buttons { 31 | border-color: #272a3a; 32 | } 33 | .color-theme-2 .dropdown-menu .button { 34 | color: #62677f; 35 | } 36 | .color-theme-2 .dropdown-menu .button:hover { 37 | color: #f4f4f5; 38 | } 39 | .book .book-header .font-settings .font-enlarge { 40 | line-height: 30px; 41 | font-size: 1.4em; 42 | } 43 | .book .book-header .font-settings .font-reduce { 44 | line-height: 30px; 45 | font-size: 1em; 46 | } 47 | .book.color-theme-1 .book-body { 48 | color: #704214; 49 | background: #f3eacb; 50 | } 51 | .book.color-theme-1 .book-body .page-wrapper .page-inner section { 52 | background: #f3eacb; 53 | } 54 | .book.color-theme-2 .book-body { 55 | color: #bdcadb; 56 | background: #1c1f2b; 57 | } 58 | .book.color-theme-2 .book-body .page-wrapper .page-inner section { 59 | background: #1c1f2b; 60 | } 61 | .book.font-size-0 .book-body .page-inner section { 62 | font-size: 1.2rem; 63 | } 64 | .book.font-size-1 .book-body .page-inner section { 65 | font-size: 1.4rem; 66 | } 67 | .book.font-size-2 .book-body .page-inner section { 68 | font-size: 1.6rem; 69 | } 70 | .book.font-size-3 .book-body .page-inner section { 71 | font-size: 2.2rem; 72 | } 73 | .book.font-size-4 .book-body .page-inner section { 74 | font-size: 4rem; 75 | } 76 | .book.font-family-0 { 77 | font-family: Georgia, serif; 78 | } 79 | .book.font-family-1 { 80 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 81 | } 82 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal { 83 | color: #704214; 84 | } 85 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a { 86 | color: inherit; 87 | } 88 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, 89 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2, 90 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h3, 91 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h4, 92 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h5, 93 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { 94 | color: inherit; 95 | } 96 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, 97 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2 { 98 | border-color: inherit; 99 | } 100 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { 101 | color: inherit; 102 | } 103 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal hr { 104 | background-color: inherit; 105 | } 106 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal blockquote { 107 | border-color: inherit; 108 | } 109 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, 110 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { 111 | background: #fdf6e3; 112 | color: #657b83; 113 | border-color: #f8df9c; 114 | } 115 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal .highlight { 116 | background-color: inherit; 117 | } 118 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table th, 119 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table td { 120 | border-color: #f5d06c; 121 | } 122 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr { 123 | color: inherit; 124 | background-color: #fdf6e3; 125 | border-color: #444444; 126 | } 127 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { 128 | background-color: #fbeecb; 129 | } 130 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal { 131 | color: #bdcadb; 132 | } 133 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal a { 134 | color: #3eb1d0; 135 | } 136 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, 137 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2, 138 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3, 139 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4, 140 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5, 141 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { 142 | color: #fffffa; 143 | } 144 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, 145 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2 { 146 | border-color: #373b4e; 147 | } 148 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { 149 | color: #373b4e; 150 | } 151 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal hr { 152 | background-color: #373b4e; 153 | } 154 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal blockquote { 155 | border-color: #373b4e; 156 | } 157 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, 158 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { 159 | color: #9dbed8; 160 | background: #2d3143; 161 | border-color: #2d3143; 162 | } 163 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal .highlight { 164 | background-color: #282a39; 165 | } 166 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table th, 167 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table td { 168 | border-color: #3b3f54; 169 | } 170 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr { 171 | color: #b6c2d2; 172 | background-color: #2d3143; 173 | border-color: #3b3f54; 174 | } 175 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { 176 | background-color: #35394b; 177 | } 178 | .book.color-theme-1 .book-header { 179 | color: #afa790; 180 | background: transparent; 181 | } 182 | .book.color-theme-1 .book-header .btn { 183 | color: #afa790; 184 | } 185 | .book.color-theme-1 .book-header .btn:hover { 186 | color: #73553c; 187 | background: none; 188 | } 189 | .book.color-theme-1 .book-header h1 { 190 | color: #704214; 191 | } 192 | .book.color-theme-2 .book-header { 193 | color: #7e888b; 194 | background: transparent; 195 | } 196 | .book.color-theme-2 .book-header .btn { 197 | color: #3b3f54; 198 | } 199 | .book.color-theme-2 .book-header .btn:hover { 200 | color: #fffff5; 201 | background: none; 202 | } 203 | .book.color-theme-2 .book-header h1 { 204 | color: #bdcadb; 205 | } 206 | .book.color-theme-1 .book-body .navigation { 207 | color: #afa790; 208 | } 209 | .book.color-theme-1 .book-body .navigation:hover { 210 | color: #73553c; 211 | } 212 | .book.color-theme-2 .book-body .navigation { 213 | color: #383f52; 214 | } 215 | .book.color-theme-2 .book-body .navigation:hover { 216 | color: #fffff5; 217 | } 218 | /* 219 | * Theme 1 220 | */ 221 | .book.color-theme-1 .book-summary { 222 | color: #afa790; 223 | background: #111111; 224 | border-right: 1px solid rgba(0, 0, 0, 0.07); 225 | } 226 | .book.color-theme-1 .book-summary .book-search { 227 | background: transparent; 228 | } 229 | .book.color-theme-1 .book-summary .book-search input, 230 | .book.color-theme-1 .book-summary .book-search input:focus { 231 | border: 1px solid transparent; 232 | } 233 | .book.color-theme-1 .book-summary ul.summary li.divider { 234 | background: #7e888b; 235 | box-shadow: none; 236 | } 237 | .book.color-theme-1 .book-summary ul.summary li i.fa-check { 238 | color: #33cc33; 239 | } 240 | .book.color-theme-1 .book-summary ul.summary li.done > a { 241 | color: #877f6a; 242 | } 243 | .book.color-theme-1 .book-summary ul.summary li a, 244 | .book.color-theme-1 .book-summary ul.summary li span { 245 | color: #877f6a; 246 | background: transparent; 247 | font-weight: normal; 248 | } 249 | .book.color-theme-1 .book-summary ul.summary li.active > a, 250 | .book.color-theme-1 .book-summary ul.summary li a:hover { 251 | color: #704214; 252 | background: transparent; 253 | font-weight: normal; 254 | } 255 | /* 256 | * Theme 2 257 | */ 258 | .book.color-theme-2 .book-summary { 259 | color: #bcc1d2; 260 | background: #2d3143; 261 | border-right: none; 262 | } 263 | .book.color-theme-2 .book-summary .book-search { 264 | background: transparent; 265 | } 266 | .book.color-theme-2 .book-summary .book-search input, 267 | .book.color-theme-2 .book-summary .book-search input:focus { 268 | border: 1px solid transparent; 269 | } 270 | .book.color-theme-2 .book-summary ul.summary li.divider { 271 | background: #272a3a; 272 | box-shadow: none; 273 | } 274 | .book.color-theme-2 .book-summary ul.summary li i.fa-check { 275 | color: #33cc33; 276 | } 277 | .book.color-theme-2 .book-summary ul.summary li.done > a { 278 | color: #62687f; 279 | } 280 | .book.color-theme-2 .book-summary ul.summary li a, 281 | .book.color-theme-2 .book-summary ul.summary li span { 282 | color: #c1c6d7; 283 | background: transparent; 284 | font-weight: 600; 285 | } 286 | .book.color-theme-2 .book-summary ul.summary li.active > a, 287 | .book.color-theme-2 .book-summary ul.summary li a:hover { 288 | color: #f4f4f5; 289 | background: #252737; 290 | font-weight: 600; 291 | } 292 | -------------------------------------------------------------------------------- /docs/docs/gitbook/gitbook-plugin-highlight/ebook.css: -------------------------------------------------------------------------------- 1 | pre, 2 | code { 3 | /* http://jmblog.github.io/color-themes-for-highlightjs */ 4 | /* Tomorrow Comment */ 5 | /* Tomorrow Red */ 6 | /* Tomorrow Orange */ 7 | /* Tomorrow Yellow */ 8 | /* Tomorrow Green */ 9 | /* Tomorrow Aqua */ 10 | /* Tomorrow Blue */ 11 | /* Tomorrow Purple */ 12 | } 13 | pre .hljs-comment, 14 | code .hljs-comment, 15 | pre .hljs-title, 16 | code .hljs-title { 17 | color: #8e908c; 18 | } 19 | pre .hljs-variable, 20 | code .hljs-variable, 21 | pre .hljs-attribute, 22 | code .hljs-attribute, 23 | pre .hljs-tag, 24 | code .hljs-tag, 25 | pre .hljs-regexp, 26 | code .hljs-regexp, 27 | pre .hljs-deletion, 28 | code .hljs-deletion, 29 | pre .ruby .hljs-constant, 30 | code .ruby .hljs-constant, 31 | pre .xml .hljs-tag .hljs-title, 32 | code .xml .hljs-tag .hljs-title, 33 | pre .xml .hljs-pi, 34 | code .xml .hljs-pi, 35 | pre .xml .hljs-doctype, 36 | code .xml .hljs-doctype, 37 | pre .html .hljs-doctype, 38 | code .html .hljs-doctype, 39 | pre .css .hljs-id, 40 | code .css .hljs-id, 41 | pre .css .hljs-class, 42 | code .css .hljs-class, 43 | pre .css .hljs-pseudo, 44 | code .css .hljs-pseudo { 45 | color: #c82829; 46 | } 47 | pre .hljs-number, 48 | code .hljs-number, 49 | pre .hljs-preprocessor, 50 | code .hljs-preprocessor, 51 | pre .hljs-pragma, 52 | code .hljs-pragma, 53 | pre .hljs-built_in, 54 | code .hljs-built_in, 55 | pre .hljs-literal, 56 | code .hljs-literal, 57 | pre .hljs-params, 58 | code .hljs-params, 59 | pre .hljs-constant, 60 | code .hljs-constant { 61 | color: #f5871f; 62 | } 63 | pre .ruby .hljs-class .hljs-title, 64 | code .ruby .hljs-class .hljs-title, 65 | pre .css .hljs-rules .hljs-attribute, 66 | code .css .hljs-rules .hljs-attribute { 67 | color: #eab700; 68 | } 69 | pre .hljs-string, 70 | code .hljs-string, 71 | pre .hljs-value, 72 | code .hljs-value, 73 | pre .hljs-inheritance, 74 | code .hljs-inheritance, 75 | pre .hljs-header, 76 | code .hljs-header, 77 | pre .hljs-addition, 78 | code .hljs-addition, 79 | pre .ruby .hljs-symbol, 80 | code .ruby .hljs-symbol, 81 | pre .xml .hljs-cdata, 82 | code .xml .hljs-cdata { 83 | color: #718c00; 84 | } 85 | pre .css .hljs-hexcolor, 86 | code .css .hljs-hexcolor { 87 | color: #3e999f; 88 | } 89 | pre .hljs-function, 90 | code .hljs-function, 91 | pre .python .hljs-decorator, 92 | code .python .hljs-decorator, 93 | pre .python .hljs-title, 94 | code .python .hljs-title, 95 | pre .ruby .hljs-function .hljs-title, 96 | code .ruby .hljs-function .hljs-title, 97 | pre .ruby .hljs-title .hljs-keyword, 98 | code .ruby .hljs-title .hljs-keyword, 99 | pre .perl .hljs-sub, 100 | code .perl .hljs-sub, 101 | pre .javascript .hljs-title, 102 | code .javascript .hljs-title, 103 | pre .coffeescript .hljs-title, 104 | code .coffeescript .hljs-title { 105 | color: #4271ae; 106 | } 107 | pre .hljs-keyword, 108 | code .hljs-keyword, 109 | pre .javascript .hljs-function, 110 | code .javascript .hljs-function { 111 | color: #8959a8; 112 | } 113 | pre .hljs, 114 | code .hljs { 115 | display: block; 116 | background: white; 117 | color: #4d4d4c; 118 | padding: 0.5em; 119 | } 120 | pre .coffeescript .javascript, 121 | code .coffeescript .javascript, 122 | pre .javascript .xml, 123 | code .javascript .xml, 124 | pre .tex .hljs-formula, 125 | code .tex .hljs-formula, 126 | pre .xml .javascript, 127 | code .xml .javascript, 128 | pre .xml .vbscript, 129 | code .xml .vbscript, 130 | pre .xml .css, 131 | code .xml .css, 132 | pre .xml .hljs-cdata, 133 | code .xml .hljs-cdata { 134 | opacity: 0.5; 135 | } 136 | -------------------------------------------------------------------------------- /docs/docs/gitbook/gitbook-plugin-lunr/lunr.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.12 3 | * Copyright (C) 2015 Oliver Nightingale 4 | * MIT Licensed 5 | * @license 6 | */ 7 | !function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.5.12",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(t){return arguments.length&&null!=t&&void 0!=t?Array.isArray(t)?t.map(function(t){return t.toLowerCase()}):t.toString().trim().toLowerCase().split(/[\s\-]+/):[]},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,o=0;n>o;o++){for(var r=t[o],s=0;i>s&&(r=this._stack[s](r,o,t),void 0!==r);s++);void 0!==r&&e.push(r)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(r===t)return o;t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o]}return r===t?o:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,o=e+Math.floor(i/2),r=this.elements[o];i>1;)t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o];return r>t?o:t>r?o+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,o=0,r=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>r-1||o>s-1)break;a[i]!==h[o]?a[i]h[o]&&o++:(n.add(a[i]),i++,o++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;return this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone(),i.add.apply(i,n.toArray()),i},t.SortedSet.prototype.toJSON=function(){return this.toArray()},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.Store,this.tokenStore=new t.TokenStore,this.corpusTokens=new t.SortedSet,this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var t=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,t)},t.Index.prototype.off=function(t,e){return this.eventEmitter.removeListener(t,e)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;return n._fields=e.fields,n._ref=e.ref,n.documentStore=t.Store.load(e.documentStore),n.tokenStore=t.TokenStore.load(e.tokenStore),n.corpusTokens=t.SortedSet.load(e.corpusTokens),n.pipeline=t.Pipeline.load(e.pipeline),n},t.Index.prototype.field=function(t,e){var e=e||{},n={name:t,boost:e.boost||1};return this._fields.push(n),this},t.Index.prototype.ref=function(t){return this._ref=t,this},t.Index.prototype.add=function(e,n){var i={},o=new t.SortedSet,r=e[this._ref],n=void 0===n?!0:n;this._fields.forEach(function(n){var r=this.pipeline.run(t.tokenizer(e[n.name]));i[n.name]=r,t.SortedSet.prototype.add.apply(o,r)},this),this.documentStore.set(r,o),t.SortedSet.prototype.add.apply(this.corpusTokens,o.toArray());for(var s=0;s0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(t.tokenizer(e)),i=new t.Vector,o=[],r=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*r,h=this,l=this.tokenStore.expand(e).reduce(function(n,o){var r=h.corpusTokens.indexOf(o),s=h.idf(o),l=1,u=new t.SortedSet;if(o!==e){var c=Math.max(3,o.length-e.length);l=1/Math.log(c)}return r>-1&&i.insert(r,a*s*l),Object.keys(h.tokenStore.get(o)).forEach(function(t){u.add(t)}),n.union(u)},new t.SortedSet);o.push(l)},this);var a=o.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,o=new t.Vector,r=0;i>r;r++){var s=n.elements[r],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);o.insert(this.corpusTokens.indexOf(s),a*h)}return o},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,a="^("+o+")?"+r+o+"("+r+")?$",h="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,u=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(l),p=/^(.+?)(ss|i)es$/,m=/^(.+?)([^s])s$/,v=/^(.+?)eed$/,y=/^(.+?)(ed|ing)$/,g=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),x=new RegExp("^"+o+i+"[^aeiouwxy]$"),k=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,_=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,O=/^(.+?)e$/,P=/ll$/,N=new RegExp("^"+o+i+"[^aeiouwxy]$"),T=function(n){var i,o,r,s,a,h,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,a=m,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=v,a=y,s.test(n)){var T=s.exec(n);s=u,s.test(T[1])&&(s=g,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,l=x,a.test(n)?n+="e":h.test(n)?(s=g,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=k,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+t[o])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+e[o])}if(s=_,a=F,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=O,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=N,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=P,a=c,s.test(n)&&a.test(n)&&(s=g,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==e?e:void 0},t.stopWordFilter.stopWords={a:"a",able:"able",about:"about",across:"across",after:"after",all:"all",almost:"almost",also:"also",am:"am",among:"among",an:"an",and:"and",any:"any",are:"are",as:"as",at:"at",be:"be",because:"because",been:"been",but:"but",by:"by",can:"can",cannot:"cannot",could:"could",dear:"dear",did:"did","do":"do",does:"does",either:"either","else":"else",ever:"ever",every:"every","for":"for",from:"from",get:"get",got:"got",had:"had",has:"has",have:"have",he:"he",her:"her",hers:"hers",him:"him",his:"his",how:"how",however:"however",i:"i","if":"if","in":"in",into:"into",is:"is",it:"it",its:"its",just:"just",least:"least",let:"let",like:"like",likely:"likely",may:"may",me:"me",might:"might",most:"most",must:"must",my:"my",neither:"neither",no:"no",nor:"nor",not:"not",of:"of",off:"off",often:"often",on:"on",only:"only",or:"or",other:"other",our:"our",own:"own",rather:"rather",said:"said",say:"say",says:"says",she:"she",should:"should",since:"since",so:"so",some:"some",than:"than",that:"that",the:"the",their:"their",them:"them",then:"then",there:"there",these:"these",they:"they","this":"this",tis:"tis",to:"to",too:"too",twas:"twas",us:"us",wants:"wants",was:"was",we:"we",were:"were",what:"what",when:"when",where:"where",which:"which","while":"while",who:"who",whom:"whom",why:"why",will:"will","with":"with",would:"would",yet:"yet",you:"you",your:"your"},t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){var e=t.replace(/^\W+/,"").replace(/\W+$/,"");return""===e?void 0:e},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t[0],o=t.slice(1);return i in n||(n[i]={docs:{}}),0===o.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(o,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n element for each result 48 | res.results.forEach(function(res) { 49 | var $li = $('
  • ', { 50 | 'class': 'search-results-item' 51 | }); 52 | 53 | var $title = $('

    '); 54 | 55 | var $link = $('', { 56 | 'href': gitbook.state.basePath + '/' + res.url, 57 | 'text': res.title 58 | }); 59 | 60 | var content = res.body.trim(); 61 | if (content.length > MAX_DESCRIPTION_SIZE) { 62 | content = content.slice(0, MAX_DESCRIPTION_SIZE).trim()+'...'; 63 | } 64 | var $content = $('

    ').html(content); 65 | 66 | $link.appendTo($title); 67 | $title.appendTo($li); 68 | $content.appendTo($li); 69 | $li.appendTo($searchList); 70 | }); 71 | } 72 | 73 | function launchSearch(q) { 74 | // Add class for loading 75 | $body.addClass('with-search'); 76 | $body.addClass('search-loading'); 77 | 78 | // Launch search query 79 | throttle(gitbook.search.query(q, 0, MAX_RESULTS) 80 | .then(function(results) { 81 | displayResults(results); 82 | }) 83 | .always(function() { 84 | $body.removeClass('search-loading'); 85 | }), 1000); 86 | } 87 | 88 | function closeSearch() { 89 | $body.removeClass('with-search'); 90 | $bookSearchResults.removeClass('open'); 91 | } 92 | 93 | function launchSearchFromQueryString() { 94 | var q = getParameterByName('q'); 95 | if (q && q.length > 0) { 96 | // Update search input 97 | $searchInput.val(q); 98 | 99 | // Launch search 100 | launchSearch(q); 101 | } 102 | } 103 | 104 | function bindSearch() { 105 | // Bind DOM 106 | $searchInput = $('#book-search-input input'); 107 | $bookSearchResults = $('#book-search-results'); 108 | $searchList = $bookSearchResults.find('.search-results-list'); 109 | $searchTitle = $bookSearchResults.find('.search-results-title'); 110 | $searchResultsCount = $searchTitle.find('.search-results-count'); 111 | $searchQuery = $searchTitle.find('.search-query'); 112 | 113 | // Launch query based on input content 114 | function handleUpdate() { 115 | var q = $searchInput.val(); 116 | 117 | if (q.length == 0) { 118 | closeSearch(); 119 | } 120 | else { 121 | launchSearch(q); 122 | } 123 | } 124 | 125 | // Detect true content change in search input 126 | // Workaround for IE < 9 127 | var propertyChangeUnbound = false; 128 | $searchInput.on('propertychange', function(e) { 129 | if (e.originalEvent.propertyName == 'value') { 130 | handleUpdate(); 131 | } 132 | }); 133 | 134 | // HTML5 (IE9 & others) 135 | $searchInput.on('input', function(e) { 136 | // Unbind propertychange event for IE9+ 137 | if (!propertyChangeUnbound) { 138 | $(this).unbind('propertychange'); 139 | propertyChangeUnbound = true; 140 | } 141 | 142 | handleUpdate(); 143 | }); 144 | 145 | // Push to history on blur 146 | $searchInput.on('blur', function(e) { 147 | // Update history state 148 | if (usePushState) { 149 | var uri = updateQueryString('q', $(this).val()); 150 | history.pushState({ path: uri }, null, uri); 151 | } 152 | }); 153 | } 154 | 155 | gitbook.events.on('page.change', function() { 156 | bindSearch(); 157 | closeSearch(); 158 | 159 | // Launch search based on query parameter 160 | if (gitbook.search.isInitialized()) { 161 | launchSearchFromQueryString(); 162 | } 163 | }); 164 | 165 | gitbook.events.on('search.ready', function() { 166 | bindSearch(); 167 | 168 | // Launch search from query param at start 169 | launchSearchFromQueryString(); 170 | }); 171 | 172 | function getParameterByName(name) { 173 | var url = window.location.href; 174 | name = name.replace(/[\[\]]/g, '\\$&'); 175 | var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)', 'i'), 176 | results = regex.exec(url); 177 | if (!results) return null; 178 | if (!results[2]) return ''; 179 | return decodeURIComponent(results[2].replace(/\+/g, ' ')); 180 | } 181 | 182 | function updateQueryString(key, value) { 183 | value = encodeURIComponent(value); 184 | 185 | var url = window.location.href; 186 | var re = new RegExp('([?&])' + key + '=.*?(&|#|$)(.*)', 'gi'), 187 | hash; 188 | 189 | if (re.test(url)) { 190 | if (typeof value !== 'undefined' && value !== null) 191 | return url.replace(re, '$1' + key + '=' + value + '$2$3'); 192 | else { 193 | hash = url.split('#'); 194 | url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, ''); 195 | if (typeof hash[1] !== 'undefined' && hash[1] !== null) 196 | url += '#' + hash[1]; 197 | return url; 198 | } 199 | } 200 | else { 201 | if (typeof value !== 'undefined' && value !== null) { 202 | var separator = url.indexOf('?') !== -1 ? '&' : '?'; 203 | hash = url.split('#'); 204 | url = hash[0] + separator + key + '=' + value; 205 | if (typeof hash[1] !== 'undefined' && hash[1] !== null) 206 | url += '#' + hash[1]; 207 | return url; 208 | } 209 | else 210 | return url; 211 | } 212 | } 213 | }); 214 | -------------------------------------------------------------------------------- /docs/docs/gitbook/gitbook-plugin-sharing/buttons.js: -------------------------------------------------------------------------------- 1 | require(['gitbook', 'jquery'], function(gitbook, $) { 2 | var SITES = { 3 | 'facebook': { 4 | 'label': 'Facebook', 5 | 'icon': 'fa fa-facebook', 6 | 'onClick': function(e) { 7 | e.preventDefault(); 8 | window.open('http://www.facebook.com/sharer/sharer.php?s=100&p[url]='+encodeURIComponent(location.href)); 9 | } 10 | }, 11 | 'twitter': { 12 | 'label': 'Twitter', 13 | 'icon': 'fa fa-twitter', 14 | 'onClick': function(e) { 15 | e.preventDefault(); 16 | window.open('http://twitter.com/home?status='+encodeURIComponent(document.title+' '+location.href)); 17 | } 18 | }, 19 | 'google': { 20 | 'label': 'Google+', 21 | 'icon': 'fa fa-google-plus', 22 | 'onClick': function(e) { 23 | e.preventDefault(); 24 | window.open('https://plus.google.com/share?url='+encodeURIComponent(location.href)); 25 | } 26 | }, 27 | 'weibo': { 28 | 'label': 'Weibo', 29 | 'icon': 'fa fa-weibo', 30 | 'onClick': function(e) { 31 | e.preventDefault(); 32 | window.open('http://service.weibo.com/share/share.php?content=utf-8&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)); 33 | } 34 | }, 35 | 'instapaper': { 36 | 'label': 'Instapaper', 37 | 'icon': 'fa fa-instapaper', 38 | 'onClick': function(e) { 39 | e.preventDefault(); 40 | window.open('http://www.instapaper.com/text?u='+encodeURIComponent(location.href)); 41 | } 42 | }, 43 | 'vk': { 44 | 'label': 'VK', 45 | 'icon': 'fa fa-vk', 46 | 'onClick': function(e) { 47 | e.preventDefault(); 48 | window.open('http://vkontakte.ru/share.php?url='+encodeURIComponent(location.href)); 49 | } 50 | } 51 | }; 52 | 53 | 54 | 55 | gitbook.events.bind('start', function(e, config) { 56 | var opts = config.sharing; 57 | 58 | // Create dropdown menu 59 | var menu = $.map(opts.all, function(id) { 60 | var site = SITES[id]; 61 | 62 | return { 63 | text: site.label, 64 | onClick: site.onClick 65 | }; 66 | }); 67 | 68 | // Create main button with dropdown 69 | if (menu.length > 0) { 70 | gitbook.toolbar.createButton({ 71 | icon: 'fa fa-share-alt', 72 | label: 'Share', 73 | position: 'right', 74 | dropdown: [menu] 75 | }); 76 | } 77 | 78 | // Direct actions to share 79 | $.each(SITES, function(sideId, site) { 80 | if (!opts[sideId]) return; 81 | 82 | gitbook.toolbar.createButton({ 83 | icon: site.icon, 84 | label: site.text, 85 | position: 'right', 86 | onClick: site.onClick 87 | }); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /docs/docs/gitbook/images/apple-touch-icon-precomposed-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/gitbook/images/apple-touch-icon-precomposed-152.png -------------------------------------------------------------------------------- /docs/docs/gitbook/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/gitbook/images/favicon.ico -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.0.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/画个三角形吧/1.0.1.png -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.0创建窗口.md: -------------------------------------------------------------------------------- 1 | # 开始冒险的第一步,整个窗口出来 2 | 3 | ## 初始化一个项目 4 | 首先我们使用 ```cargo init try``` 5 | 创建一个基础的Rust项目 6 | 7 | ``` 8 | try 9 | src 10 | cargo.toml 11 | .git 12 | ``` 13 | 大概是这个样子的 14 | 15 | 我们需要安装一些库. Rust是一个非常好的工具因为他提供了Cargo这么好用的减少心智负担的工具 16 | 我们打开 cargo.toml 17 | ```toml 18 | [package] 19 | #这个一般都给你写好了,不用操心 20 | name = "try" 21 | #项目名称 22 | version = "0.1.0" 23 | #项目版本 24 | authors = ["lemonhx "] 25 | #作者们 26 | edition = "2018" 27 | 28 | [dependencies] 29 | #依赖的库 30 | winit = "0.19" 31 | #我们核心的GFX硬件抽象库 32 | gfx-hal = "0.3.0" 33 | #一个玩过rust就知道的库 34 | arrayvec = "0.4" 35 | 36 | #vulkan作为gfx后端的配置 37 | [dependencies.gfx-backend-vulkan] 38 | version = "0.3" 39 | features = ["winit"] 40 | optional = true 41 | 42 | #metal作为gfx后端的配置 43 | [target.'cfg(target_os = "macos")'.dependencies.gfx-backend-metal] 44 | version = "0.3" 45 | features = ["winit"] 46 | optional = true 47 | 48 | #DX12作为gfx后端的配置 49 | [target.'cfg(windows)'.dependencies.gfx-backend-dx12] 50 | version = "0.3.0" 51 | features = ["winit"] 52 | optional = true 53 | 54 | #编译配置 55 | [build-dependencies] 56 | glsl-to-spirv = "0.1.7" 57 | 58 | #启用扩展,我们默认使用跨平台的Vulkan 59 | [features] 60 | default = ["gfx-backend-vulkan"] 61 | metal = ["gfx-backend-metal"] 62 | dx12 = ["gfx-backend-dx12"] 63 | vulkan = ["gfx-backend-vulkan"] 64 | ``` 65 | 抄好之后使用```cargo run```下载依赖并运行,不出问题的话就会有hello world窗口弹出 66 | ## 窗口结构 67 | 我们需要定义以下窗口结构体 68 | ```rust 69 | pub struct WindowState { 70 | pub evloop: EventsLoop, 71 | pub window: Window, 72 | } 73 | ``` 74 | 这是我定义的一个窗口结构体,里面装有一个事件循环和一个窗口 75 | 76 | 事件循环是什么可能很多人不知道我可以大概解释一下: 77 | 78 | 我自己在跑步(循环),跑着跑着被绊倒了(遇到事件),我爬起来(事件的处理方式),我继续跑(重新回到循环) 79 | 80 | 这个是一个简单的事件循环,同样,我还可以 81 | 82 | 跑步(循环),被车撞了(遇到事件),躺地上求救(事件处理),没人搭理死了(处理结果) 83 | 84 | 很简单明了,创建窗口需要绑定一个事件循环的原因是,你可能对着这个窗口按键,点击鼠标,拖拽,窗口会用你之前定义好的事件处理方式去处理这些事件. 85 | 86 | 然后如何初始化一个对应这个窗口的实例呢? 87 | ```rust 88 | impl WindowState { 89 | //为了避免使代码过度复杂,这里就直接不行报错了 90 | pub fn new>(title: T, size: (f64, f64)) -> Self{ 91 | let evloop = EventsLoop::new(); //创建事件循环 92 | let window = match WindowBuilder::new() //使用窗口创造器 93 | .with_title(title) //绑上标题 94 | .with_dimensions(LogicalSize::new(size.0, size.1)) //设置大小 95 | .build(&evloop) //尝试构造 这里的返回类型是一个Result所以我们再match一手 96 | { 97 | Ok(x) => x, 98 | Err(e) => {println!("创建窗口失败,原因:{:?}", e);panic!()} 99 | }; 100 | Self { 101 | evloop, 102 | window, 103 | } 104 | } 105 | } 106 | ``` 107 | 108 | ## 事件处理() 109 | 一个窗口对应了一个事件循环,有些时候事件循环就是一个```while```循环,但是这里我们的```winit```库给我们提供了一个基于control flow的一个事件循环,总体来讲就是我们不需要把事件的处理方法挤在一个```while```里面来写了,在这里我定义了一个事件处理函数 110 | 111 | ```rust 112 | use winit::{ControlFlow, Event, VirtualKeyCode, WindowEvent};// 使用 控制流 事件 按键代码 窗口事件 113 | // 这个函数收到一个事件,返回一个控制流(之后干什么) 114 | pub fn event_handler(event: Event) -> winit::ControlFlow { 115 | match event { 116 | // 处理键盘输入事件 117 | Event::WindowEvent { event, .. } => match event { 118 | WindowEvent::KeyboardInput { input, .. } => { 119 | if let Some(VirtualKeyCode::Escape) = input.virtual_keycode {//如果是ESC被按下来了 120 | ControlFlow::Break //退出事件循环 121 | } else { 122 | ControlFlow::Continue //要不啥也不干 123 | } 124 | } 125 | WindowEvent::CloseRequested => ControlFlow::Break, //如果窗口被关了,退出程序 126 | 127 | _ => ControlFlow::Continue, 128 | }, 129 | _ => ControlFlow::Continue, 130 | } 131 | } 132 | ``` 133 | ## 把上面的拼装到一起 134 | 最后开始写```app.rs``` 135 | 我们的app暂时仅仅只有一个窗口,所以我们创建一个结构体包含一个窗口实例就行了 136 | ```rust 137 | use super::super::window::window::*; 138 | use super::super::event_handler::event_handler::*; 139 | pub struct App { 140 | windowstate : WindowState 141 | } 142 | impl App { 143 | pub fn new() { 144 | let mut ws = WindowState::new("new_window", (1024f64, 768f64)); //设置窗口标题和大小 145 | ws.evloop.run_forever(event_handler); //开始运行事件循环 146 | } 147 | } 148 | ``` 149 | 150 | ## yes! 开始运行 151 | 最后,我们更改```main.rs``` 152 | ```rust 153 | pub mod app; 154 | pub mod window; 155 | pub mod event_handler; 156 | 157 | use app::app::*; 158 | 159 | fn main() { 160 | let _ = App::new(); 161 | } 162 | 163 | ``` 164 | 最后```cargo run```会有一个窗口弹出 165 | 166 | 我的大概是这个效果 167 | ![来自ubuntu + dde](./1.0.1.png) 168 | 169 | 通常情况下运行会得到一个白色或者黑色的窗口,甚至在某些情况下窗口里会出现你正在打开的程序的一部分的图像 170 | 这都是正常的现象 -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.1.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/画个三角形吧/1.1.2.png -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.10处理输入事件.md: -------------------------------------------------------------------------------- 1 | # 1.1.9 处理输入事件 2 | 3 | -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.11写一个clearcolor.md: -------------------------------------------------------------------------------- 1 | # 1.1.10 高端清屏 2 | 3 | -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.12渲染过程.md: -------------------------------------------------------------------------------- 1 | # 1.1.11 渲染过程 2 | 3 | -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.13把图形管线写完.md: -------------------------------------------------------------------------------- 1 | # 1.1.12 把图形管线写完 2 | 3 | -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.14帧缓冲.md: -------------------------------------------------------------------------------- 1 | # 1.1.13 帧缓冲 2 | 3 | -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.15命令池.md: -------------------------------------------------------------------------------- 1 | # 1.1.14 命令池 2 | 3 | -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.16劳资要的三角形.md: -------------------------------------------------------------------------------- 1 | # 1.2 劳资要的三角形 2 | 3 | -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.17小结.md: -------------------------------------------------------------------------------- 1 | # 1.3 小结 2 | 3 | -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.1创建实例.md: -------------------------------------------------------------------------------- 1 | # 先弄几个方便使用的结构 2 | 3 | ## 帧的概念 4 | 什么是一帧? 其实我们说的帧就是一幅静止的画面,无数个静止的画面连起来播放,通过我们的脑补帧来让我们看到连贯的动画,fps就是我们常用的衡量多少帧每秒的单位,一般情况下29fps是电视台用的帧率,60fps是一般的游戏的帧率(在我们玩游戏的时候神经高度集中,29会让我们觉得晕),144fps就是一些竞技类的游戏想要达到的帧率. 5 | 6 | 帧又是怎么诞生的呢? 一般情况下就是在电脑里渲染出3D场景然后利用投射投到2D的屏幕上,也就是fps就是一秒钟我们能渲染几次这个图像,```gfx-hal```使用的都是常见的光栅化的渲染(虽然正在准备加入```Nvidia Ray Tracing```支持),所谓的光栅化其实也是非常的简单,我们在电脑里获取了每一个物体的顶点的位置和顶点的连接之后,利用着色器(shader)来给顶点上色,然后在给顶点围起来的区域上色. 7 | 8 | 这里在补充一下,光线追踪指的是利用每一束计算机模拟的光打在物体上模拟出来的光路来上色的,上色的细节会比光栅化好的很多(因为一般涉及到那种带有反射啊,金属材质什么的光栅化需要很多的骚技巧来模拟) 9 | 10 | 11 | 看到这里你们大概有个概念了吧 下面是伪码 12 | ```rust 13 | Vec //一堆帧 14 | 15 | //渲染主程序 16 | loop{ 17 | //... 18 | render(hardware,data); 19 | } 20 | ``` 21 | 这里再补充一下,很多时候你们会听到帧缓冲这个概念,什么是帧缓冲的? 22 | 其实说白了就是提前渲染好的一堆帧用来当GPU忙的喘不过气来的时候来顶替一下,混淆一下你的大脑,因为毕竟每一帧的东西都不太一样,难免有些地方会慢一两秒.所以帧缓冲就是来改善体验,让用户觉得更流畅的. 23 | 24 | ## 如何控制上面的循环呢 25 | 在第一课里,你已经掌握了事件循环和窗口的创建,但是实际上这距离真正的图形学还有点远,我们还需要处理一些非常底层的,硬件的,和GPU沟通的硬件代码,虽然```gfx-hal```已经给我们提供了一个很好的核心,但在核心之上还需要写一些硬件代码,为了符合Rust的美学,我们需要用什么结构来包装起来 26 | > 吐槽啊,hkt什么时候出来,现在写的这一堆monad类似的东西用一个hkt能简化多少工作流程 27 | 所以这里要引入 28 | 29 | ```HardwareState```用来控制```GPU```的状态 和 ```LocalState```控制用户的输入输出之类的状态,来更好的用高级抽象控制GPU的执行,控制非纯操作的作用范围 30 | 31 | 上面的那个```render```函数就是用来渲染的 函数定义伪码 32 | ```rust 33 | pub fn render(hal:&mut HardwareState,data:&LocalState){ 34 | hal.draw(data.color) 35 | } 36 | ``` 37 | 38 | 这段是写给真正从事过这方面开发的人看的,有些时候render会出错,出错了要debug,那么这个```gfx```是怎么debug的呢?其实,```gfx```是一个胶水库,粘合了所有的底层API,所以如果你使用的是```DX12```后端那么你可以用你最喜欢的```Visual Studio```来调试可执行程序,如果你使用的是```Vulkan```后端那么你可以使用```LunarG Vulkan SDK``` 提供给你的一系列程序来调试 同时 ```gfx```会自动的报出```debug layer```的数据,```Metal``` 我没使用过,不清楚,如果你想让我写关于这方面的可以考虑资助我一台```Macbook```,你喜欢```OpenGL```??!!??!! 对不起,我们不认识,你可选择右上角```⌧```再见 39 | 40 | ## 设置后端 41 | ```rust 42 | #[cfg(feature = "dx12")] 43 | use gfx_backend_dx12 as back; 44 | #[cfg(feature = "vulkan")] 45 | use gfx_backend_vulkan as back; 46 | #[cfg(feature = "metal")] 47 | use gfx_backend_metal as back; 48 | #[cfg(not(any(feature = "vulkan", feature = "dx12",feature = "metal",)))] 49 | use gfx_backend_vulkan as back; 50 | ``` 51 | 这几行代码会先查看你的```cargo.toml```来确定你使用的是哪一个后端,然后再使用,和```C语言```里面的```#ifdef```差不多 52 | 然后在编译的时候需要指定 ```cargo run --features vulkan```之类的来确定到底用了那个后端 53 | ## 开始设计```HardwareState``` 54 | ```rust 55 | pub struct HardwareState { 56 | current_frame: usize, // 当前帧 57 | frames_in_flight: usize, // 当前传输的帧 58 | in_flight_fences: Vec<::Fence>, // 正在运行中的内存屏障 59 | render_finished_semaphores: Vec<::Semaphore>, // 渲染结束时发送的信号 60 | image_available_semaphores: Vec<::Semaphore>, // 可用信号 61 | command_buffers: Vec>, // 命令缓冲区域 62 | command_pool: ManuallyDrop>, // 命令池 63 | framebuffers: Vec<::Framebuffer>, // 帧缓冲 64 | image_views: Vec<(::ImageView)>, // 图像视图 65 | render_pass: ManuallyDrop<::RenderPass>, // 渲染过程 66 | render_area: Rect, // 渲染区域 67 | queue_group: QueueGroup, // 队列簇 68 | swapchain: ManuallyDrop<::Swapchain>, // 交换链 69 | device: ManuallyDrop, // 设备 70 | _adapter: Adapter, //用来抽象设备的转接器 71 | _surface: ::Surface, //你的屏幕 72 | _instance: ManuallyDrop, //示例 73 | } 74 | ``` 75 | 76 | 这个结构是我们用来管理硬件状态用的最基本的结构,我们会拆出几个章节来分别讲解怎么把这个结构创建出来 -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.2配置log.md: -------------------------------------------------------------------------------- 1 | # 配置log 2 | 3 | 因为本章节需要用到大量的调试信息,所以我们需要配置一个```log```来干两件事情 4 | 1. 接受底层API的log 5 | 2. 报出自己的log 6 | 在上一章已经提到了,只要是用的```vulkan```的后端的话是自动开启```validation layer```的 7 | 我们只需要将 8 | ```toml 9 | [dependencies] 10 | winit = "0.19.1" 11 | gfx-hal = "0.3.0" 12 | arrayvec = "0.4" 13 | log = "0.4.6" #新增 14 | simple_logger = "1.2" #新增 15 | ``` 16 | 加入到我们的```cargo.toml```里,然后呢在```main.rs```开启 17 | ```rust 18 | #[allow(unused_imports)] 19 | use log::{error, warn, info, debug, trace}; 20 | //... 21 | simple_logger::init().unwrap(); 22 | ``` 23 | 我们就可以看到成吨的log了 24 | 25 | 这个log组件非常的好用,他内置了几个宏 26 | ```error!```, ```warn!```, ```info!```, ```debug!``` , ```trace!``` 27 | 用法和```println!```一样的,这个会使得输出带着五颜六色的开头,能够使得你更好的读log 28 | 29 | 更主要的是这个库还可以让你在需要的时候去直接监听```stdout``` ```stderr```不过这篇教程主要侧重点不在这里有兴趣的可以去看看他的文档 -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.3物理逻辑设备.md: -------------------------------------------------------------------------------- 1 | # 物理设备和逻辑设备的创建 2 | 3 | ```rust 4 | #[cfg(feature = "dx12")] 5 | use gfx_backend_dx12 as back; 6 | #[cfg(feature = "vulkan")] 7 | use gfx_backend_vulkan as back; 8 | #[cfg(feature = "metal")] 9 | use gfx_backend_metal as back; 10 | #[cfg(not(any(feature = "vulkan", feature = "dx12",feature = "metal",)))] 11 | use gfx_backend_vulkan as back; 12 | ``` 13 | 这是我们已经有了的东西 14 | 选中了```vulkan```作为我们的后端,不过你要是喜欢```dx12```也可以 15 | 16 | ```rust 17 | // 创建一个实例 18 | let instance = back::Instance::create(title, 1); 19 | // 创建一个Surface(应该用绑定更好) 20 | let mut surface = instance.create_surface(window); 21 | // 使用一个adapter(转接器) 22 | let adapter = instance 23 | .enumerate_adapters() 24 | .into_iter() 25 | .find(|a| { 26 | a.queue_families 27 | .iter() 28 | .any(|qf| qf.supports_graphics() && surface.supports_queue_family(qf)) 29 | }) 30 | .ok_or("找不到任何可用的转接器")?; 31 | ``` 32 | 33 | `surface`指的就是你的屏幕 34 | `adapter`是我在`gfx-hal`看到的一个概念,用来抽象逻辑设备和队列簇的 35 | 有一个图可以帮助理解,虽然是我做`vulkan`教程用剩下的但是能够帮助理解实例,设备和队列簇的关系的 36 | ![队列簇](./1.1.2.png) 37 | 38 | ```rust 39 | // 使用一个设备和一个队列簇 40 | let (device, queue_group) = adapter 41 | .open_with::<_, Graphics>(1, |family| surface.supports_queue_family(family)) 42 | .unwrap(); 43 | ``` 44 | 这几行也很简单,首先使用adapter搜索有支持Graphics的显卡,如果这个显卡支持队列簇(是个民用显卡都会支持的)就返回这个显卡 45 | 46 | `devide`是一个逻辑设备,这里物理设备到逻辑设备的转换已经由`gfx-hal`写好了,我们不必操心这部分了(之前需要写一堆配置文件) 47 | 然后`queue group`就是我们的队列簇了 -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.4.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/画个三角形吧/1.4.1.png -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.4创建交换链.md: -------------------------------------------------------------------------------- 1 | # 创建个交换链出来 2 | 3 | 首先,各位有可能对交换链的概念没有理解或者理解的不是特别的透彻,简单说交换链就是一个公交车,公交车里的乘客就是一个帧,作用呢就是万一显卡没跟上,或者显卡太快了,让用户感觉画面没有什么实质性的变化的一个工具,交换链的发明使得用户体验上没有那么多的卡顿. 4 | 5 | ![交换链](./1.4.1.png) 6 | 7 | ```rust 8 | //swapchain 是我们的交换链 extent是交换链的大小 backbuffer 是一个缓冲 format是格式 frames_in_flight可以理解为正在传输中的帧吧 9 | let (swapchain, extent, backbuffer, format, frames_in_flight) = { 10 | let (caps, preferred_formats, present_modes) = 11 | surface.compatibility(&adapter.physical_device); 12 | info!("{:?}", caps); 13 | info!("首选格式: {:?}", preferred_formats); 14 | info!("当前模式: {:?}", present_modes); 15 | 16 | let present_mode = { 17 | use gfx_hal::window::PresentMode::*; 18 | [Mailbox, Fifo, Relaxed, Immediate] //这是几种GPU中常见的模式 Mailbox就是邮箱一样的,FIFO就是first in first out 感兴趣的可以百度 19 | .iter() 20 | .cloned() 21 | .find(|pm| present_modes.contains(pm)) 22 | .ok_or("没有设置任何当前模式")? 23 | }; 24 | 25 | let format = match preferred_formats { 26 | None => Format::Rgba8Srgb, 27 | Some(formats) => match formats 28 | .iter() 29 | .find(|format| format.base_format().1 == ChannelType::Srgb) 30 | .cloned() 31 | { 32 | Some(srgb_format) => srgb_format, 33 | None => formats.get(0).cloned().ok_or("没有任何首选格式")?, 34 | }, 35 | }; 36 | let extent = caps.extents.end().clone(); 37 | let image_count = if present_mode == PresentMode::Mailbox { 38 | (caps.image_count.end - 1).min(3) 39 | } else { 40 | (caps.image_count.end - 1).min(2) 41 | }; 42 | let image_usage = if caps.usage.contains(Usage::COLOR_ATTACHMENT) { //如果这个设备没有彩色支持的话,天啊!!! 43 | Usage::COLOR_ATTACHMENT 44 | } else { 45 | Err("天哪!都9086年了你居然还在用单色屏幕??!!")? 46 | }; 47 | let mut swapchain_config = 48 | SwapchainConfig::new(extent.width, extent.height, format, image_count); 49 | swapchain_config.present_mode = present_mode; 50 | swapchain_config.image_usage = image_usage; 51 | 52 | info!("{:?}", swapchain_config); 53 | // 创建交换链和一个(图像)缓冲区 54 | let (swapchain, backbuffer) = unsafe { 55 | device 56 | .create_swapchain(&mut surface, swapchain_config, None) 57 | .map_err(|_| "创建交换链失败")? 58 | }; 59 | 60 | (swapchain, extent, backbuffer, format, image_count as usize) 61 | }; 62 | ``` -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.5.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/docs/画个三角形吧/1.5.1.png -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.5设置同步.md: -------------------------------------------------------------------------------- 1 | # 设置同步 2 | 3 | 对于没学过操作系统的同学,可能不是特别清楚信号量和内存屏障大概是干什么用的 4 | ## 什么是信号量呢? 5 | 6 | 信号量是一个用来同步不同线程之间的操作的一个值,保持在0至指定最大值之间(这里我们用1就行,也就是说atom bool其实是我们的最好的选择) 7 | 当一个线程等待一个信号量的时候就-1,释放一次信号量的时候就+1,这里因为我们使用的一个bool所以就false true就行了. 8 | 信号量的计数值大于0(true),为signaled(有信号)状态;计数值等于0(false),为nonsignaled(Iphone没信号)状态. 9 | 给大家付个图片帮助大家理解 10 | ![信号量](./1.5.1.png) 11 | 12 | ## 那什么又是内存屏障呢? 13 | 14 | 其实这个东西的出现跟我们现在使用的垃圾CPU有关系 15 | 我们的垃圾CPU为了提升运行速度多半采取了乱序执行,而某些操作我们必须让他进行同步执行,设置内存屏障就保证了这个内存的访问是严格按照顺序执行的.要注意的一点是内存屏障的实现上在Windows系统上和类Unix系统上都是不太一样的,常见的垃圾x86会在linux下编译出这种傻逼的代码,给大家当笑话,缓解一下学习计算机图形学的枯燥 16 | 17 | 准备好了吗 18 | 19 | ```asm 20 | addl $0,0 (%esp) 21 | ``` 22 | 哈哈哈哈哈...你们觉得不好笑吗 23 | 24 | 25 | ```rust 26 | // image_available_semaphores 图片可用的信号量 render... 渲染结束的信号量 in... 正在传输的墙(内存屏障) 27 | // 这个实现虽然有点捞但是比较容易看懂 28 | let (image_available_semaphores, render_finished_semaphores, in_flight_fences) = { 29 | let mut image_available_semaphores: Vec<::Semaphore> = vec![]; 30 | let mut render_finished_semaphores: Vec<::Semaphore> = vec![]; 31 | let mut in_flight_fences: Vec<::Fence> = vec![]; 32 | for _ in 0..frames_in_flight { 33 | in_flight_fences 34 | .push(device.create_fence(true).map_err(|_| "创建内存屏障失败")?); 35 | image_available_semaphores 36 | .push(device.create_semaphore().map_err(|_| "创建信号量失败")?); 37 | render_finished_semaphores 38 | .push(device.create_semaphore().map_err(|_| "创建信号量失败")?); 39 | } 40 | ( 41 | image_available_semaphores, 42 | render_finished_semaphores, 43 | in_flight_fences, 44 | ) 45 | }; 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.6创建renderpass.md: -------------------------------------------------------------------------------- 1 | # 写一个renderpass吧 2 | 3 | 为什么我不翻译了?因为`renderpass`的术语特别的模糊,容易引起歧义,就这么说吧,现在我有一堆盒子,我把这堆盒子进行上色叫一个`pass`,每个`pass`就是一个绘制调用(其实很多时候进行一次渲染经过很多次绘制调用因为单次渲染大批量效率并不是特别的高) 4 | 5 | 这里随着延迟渲染技术,多`pass`渲染其实现在的立足和地位都受到了某种程度的打击,但是因为概念好理解然后遍布了所有的教科书所以还是讲一下比较好. 6 | 7 | ```rust 8 | // 创建一个 RenderPass 9 | let render_pass = { 10 | let color_attachment = Attachment { 11 | format: Some(format), // 之前还记的创建swapchain的时候的那个format吗 12 | samples: 1, 13 | ops: AttachmentOps { 14 | load: AttachmentLoadOp::Clear, 15 | store: AttachmentStoreOp::Store, 16 | }, 17 | stencil_ops: AttachmentOps::DONT_CARE, 18 | layouts: Layout::Undefined..Layout::Present, 19 | }; 20 | // sub pass 就是每一个render pass里的实际对attachments进行读写操作的组件. 21 | // 效果越复杂subpass越多 22 | let subpass = SubpassDesc { 23 | colors: &[(0, Layout::ColorAttachmentOptimal)], 24 | depth_stencil: None, 25 | inputs: &[], 26 | resolves: &[], 27 | preserves: &[], 28 | }; 29 | unsafe { 30 | device 31 | .create_render_pass(&[color_attachment], &[subpass], &[]) 32 | .map_err(|_| "创建render pass 失败")? 33 | } 34 | }; 35 | ``` -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.7创建图片视图和帧缓冲.md: -------------------------------------------------------------------------------- 1 | # 图片视图和帧缓冲 2 | 3 | 什么是图像视图? 就是一个对象在内存中连续的装了一个或一堆的图像结束. 4 | 什么是帧缓冲? 跟上面一样,也是装了一堆帧的一块内存. 5 | 6 | ```rust 7 | // 创建图像视图 8 | let image_views: Vec<_> = backbuffer 9 | .into_iter() 10 | .map(|image| unsafe { 11 | device 12 | .create_image_view( 13 | &image, 14 | ViewKind::D2, 15 | format, 16 | Swizzle::NO, 17 | SubresourceRange { 18 | aspects: Aspects::COLOR, 19 | levels: 0..1, 20 | layers: 0..1, 21 | }, 22 | ) 23 | .map_err(|_| "为图像创建图像视图失败") 24 | }) 25 | .collect::, &str>>()?; 26 | 27 | // 创建帧缓冲 28 | let framebuffers: Vec<::Framebuffer> = { 29 | image_views 30 | .iter() 31 | .map(|image_view| unsafe { 32 | device 33 | .create_framebuffer( 34 | &render_pass, 35 | vec![image_view], 36 | Extent { 37 | width: extent.width as u32, 38 | height: extent.height as u32, 39 | depth: 1, 40 | }, 41 | ) 42 | .map_err(|_| "创建帧缓冲失败") 43 | }) 44 | .collect::, &str>>()? 45 | }; 46 | ``` -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.8创建命令池和命令缓冲.md: -------------------------------------------------------------------------------- 1 | # 创建命令池和命令缓冲 2 | 3 | 在`Vulkan`图形的定义上命令池是一个不透明的对象,分摊了很多命令缓冲区的创建资源成本,但是却是**不同步**的,所以千万不能跨线程使用. 4 | 5 | 命令缓冲区是一个用来记录命令的对象,这些命令就像email一样显卡肯定一下子看不完所以用缓冲区暂时的储存一下随后给显卡看. 6 | 7 | `Vulkan`有两个缓冲区一个是主要的缓冲区直接提交到队列,一个是辅助缓冲区,提交到主缓冲区. 8 | 9 | ```rust 10 | // 创建命令池 11 | let mut command_pool = unsafe { 12 | device 13 | .create_command_pool_typed(&queue_group, CommandPoolCreateFlags::RESET_INDIVIDUAL) 14 | .map_err(|_| "创建命令池失败,错误可能来自底层接口")? 15 | }; 16 | 17 | // 创建命令缓冲 18 | let command_buffers: Vec<_> = framebuffers 19 | .iter() 20 | .map(|_| command_pool.acquire_command_buffer()) 21 | .collect(); 22 | ``` 23 | 24 | 写到这里,一个`HardwareState`差不多就完工了 25 | 但是先别着急,看完下面这一章再开始抄代码. -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/1.9写一个drop.md: -------------------------------------------------------------------------------- 1 | # 实现drop 2 | 3 | 其实这个完全是因为现在的`gfx-hal`写的差劲的原因,人家`Rust`的内存管理理论上都不需要用户来管的... 4 | 5 | ```rust 6 | impl core::ops::Drop for HardwareState { 7 | // 我发现了个bug在这个垃圾gfx-hal里面swapchain更换的特别频繁的时候,Rust自动drop跟不上,所以这里用的手动drop 8 | // 还有可爱的GFX开发组哟,能不能把修一修,这个unsafe看的我浑身难受 9 | fn drop(&mut self) { 10 | let _ = self.device.wait_idle(); 11 | unsafe { 12 | for fence in self.in_flight_fences.drain(..) { 13 | self.device.destroy_fence(fence) 14 | } 15 | for semaphore in self.render_finished_semaphores.drain(..) { 16 | self.device.destroy_semaphore(semaphore) 17 | } 18 | for semaphore in self.image_available_semaphores.drain(..) { 19 | self.device.destroy_semaphore(semaphore) 20 | } 21 | for framebuffer in self.framebuffers.drain(..) { 22 | self.device.destroy_framebuffer(framebuffer); 23 | } 24 | for image_view in self.image_views.drain(..) { 25 | self.device.destroy_image_view(image_view); 26 | } 27 | use core::ptr::read; 28 | self.device.destroy_command_pool( 29 | ManuallyDrop::into_inner(read(&self.command_pool)).into_raw(), 30 | ); 31 | self.device 32 | .destroy_render_pass(ManuallyDrop::into_inner(read(&self.render_pass))); 33 | self.device 34 | .destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain))); 35 | ManuallyDrop::drop(&mut self.device); 36 | ManuallyDrop::drop(&mut self._instance); 37 | } 38 | } 39 | } 40 | ``` -------------------------------------------------------------------------------- /docs/docs/画个三角形吧/README.md: -------------------------------------------------------------------------------- 1 | # 1.画个三角形吧 2 | 3 | 在这一章主要让大家迈出学习计算机图形学框架的第一步 4 | 也就是画个三角形出来.... 5 | 6 | 别小看这个三角形🔺,等你真正画出来就知道多 蛋碎 困难了,很多计算机图形爱好者都是被这个劝退的. -------------------------------------------------------------------------------- /docs/gitbook/fonts/fontawesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/gitbook/fonts/fontawesome/FontAwesome.otf -------------------------------------------------------------------------------- /docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/gitbook/gitbook-plugin-fontsettings/fontsettings.js: -------------------------------------------------------------------------------- 1 | require(['gitbook', 'jquery'], function(gitbook, $) { 2 | // Configuration 3 | var MAX_SIZE = 4, 4 | MIN_SIZE = 0, 5 | BUTTON_ID; 6 | 7 | // Current fontsettings state 8 | var fontState; 9 | 10 | // Default themes 11 | var THEMES = [ 12 | { 13 | config: 'white', 14 | text: 'White', 15 | id: 0 16 | }, 17 | { 18 | config: 'sepia', 19 | text: 'Sepia', 20 | id: 1 21 | }, 22 | { 23 | config: 'night', 24 | text: 'Night', 25 | id: 2 26 | } 27 | ]; 28 | 29 | // Default font families 30 | var FAMILIES = [ 31 | { 32 | config: 'serif', 33 | text: 'Serif', 34 | id: 0 35 | }, 36 | { 37 | config: 'sans', 38 | text: 'Sans', 39 | id: 1 40 | } 41 | ]; 42 | 43 | // Return configured themes 44 | function getThemes() { 45 | return THEMES; 46 | } 47 | 48 | // Modify configured themes 49 | function setThemes(themes) { 50 | THEMES = themes; 51 | updateButtons(); 52 | } 53 | 54 | // Return configured font families 55 | function getFamilies() { 56 | return FAMILIES; 57 | } 58 | 59 | // Modify configured font families 60 | function setFamilies(families) { 61 | FAMILIES = families; 62 | updateButtons(); 63 | } 64 | 65 | // Save current font settings 66 | function saveFontSettings() { 67 | gitbook.storage.set('fontState', fontState); 68 | update(); 69 | } 70 | 71 | // Increase font size 72 | function enlargeFontSize(e) { 73 | e.preventDefault(); 74 | if (fontState.size >= MAX_SIZE) return; 75 | 76 | fontState.size++; 77 | saveFontSettings(); 78 | } 79 | 80 | // Decrease font size 81 | function reduceFontSize(e) { 82 | e.preventDefault(); 83 | if (fontState.size <= MIN_SIZE) return; 84 | 85 | fontState.size--; 86 | saveFontSettings(); 87 | } 88 | 89 | // Change font family 90 | function changeFontFamily(configName, e) { 91 | if (e && e instanceof Event) { 92 | e.preventDefault(); 93 | } 94 | 95 | var familyId = getFontFamilyId(configName); 96 | fontState.family = familyId; 97 | saveFontSettings(); 98 | } 99 | 100 | // Change type of color theme 101 | function changeColorTheme(configName, e) { 102 | if (e && e instanceof Event) { 103 | e.preventDefault(); 104 | } 105 | 106 | var $book = gitbook.state.$book; 107 | 108 | // Remove currently applied color theme 109 | if (fontState.theme !== 0) 110 | $book.removeClass('color-theme-'+fontState.theme); 111 | 112 | // Set new color theme 113 | var themeId = getThemeId(configName); 114 | fontState.theme = themeId; 115 | if (fontState.theme !== 0) 116 | $book.addClass('color-theme-'+fontState.theme); 117 | 118 | saveFontSettings(); 119 | } 120 | 121 | // Return the correct id for a font-family config key 122 | // Default to first font-family 123 | function getFontFamilyId(configName) { 124 | // Search for plugin configured font family 125 | var configFamily = $.grep(FAMILIES, function(family) { 126 | return family.config == configName; 127 | })[0]; 128 | // Fallback to default font family 129 | return (!!configFamily)? configFamily.id : 0; 130 | } 131 | 132 | // Return the correct id for a theme config key 133 | // Default to first theme 134 | function getThemeId(configName) { 135 | // Search for plugin configured theme 136 | var configTheme = $.grep(THEMES, function(theme) { 137 | return theme.config == configName; 138 | })[0]; 139 | // Fallback to default theme 140 | return (!!configTheme)? configTheme.id : 0; 141 | } 142 | 143 | function update() { 144 | var $book = gitbook.state.$book; 145 | 146 | $('.font-settings .font-family-list li').removeClass('active'); 147 | $('.font-settings .font-family-list li:nth-child('+(fontState.family+1)+')').addClass('active'); 148 | 149 | $book[0].className = $book[0].className.replace(/\bfont-\S+/g, ''); 150 | $book.addClass('font-size-'+fontState.size); 151 | $book.addClass('font-family-'+fontState.family); 152 | 153 | if(fontState.theme !== 0) { 154 | $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, ''); 155 | $book.addClass('color-theme-'+fontState.theme); 156 | } 157 | } 158 | 159 | function init(config) { 160 | // Search for plugin configured font family 161 | var configFamily = getFontFamilyId(config.family), 162 | configTheme = getThemeId(config.theme); 163 | 164 | // Instantiate font state object 165 | fontState = gitbook.storage.get('fontState', { 166 | size: config.size || 2, 167 | family: configFamily, 168 | theme: configTheme 169 | }); 170 | 171 | update(); 172 | } 173 | 174 | function updateButtons() { 175 | // Remove existing fontsettings buttons 176 | if (!!BUTTON_ID) { 177 | gitbook.toolbar.removeButton(BUTTON_ID); 178 | } 179 | 180 | // Create buttons in toolbar 181 | BUTTON_ID = gitbook.toolbar.createButton({ 182 | icon: 'fa fa-font', 183 | label: 'Font Settings', 184 | className: 'font-settings', 185 | dropdown: [ 186 | [ 187 | { 188 | text: 'A', 189 | className: 'font-reduce', 190 | onClick: reduceFontSize 191 | }, 192 | { 193 | text: 'A', 194 | className: 'font-enlarge', 195 | onClick: enlargeFontSize 196 | } 197 | ], 198 | $.map(FAMILIES, function(family) { 199 | family.onClick = function(e) { 200 | return changeFontFamily(family.config, e); 201 | }; 202 | 203 | return family; 204 | }), 205 | $.map(THEMES, function(theme) { 206 | theme.onClick = function(e) { 207 | return changeColorTheme(theme.config, e); 208 | }; 209 | 210 | return theme; 211 | }) 212 | ] 213 | }); 214 | } 215 | 216 | // Init configuration at start 217 | gitbook.events.bind('start', function(e, config) { 218 | var opts = config.fontsettings; 219 | 220 | // Generate buttons at start 221 | updateButtons(); 222 | 223 | // Init current settings 224 | init(opts); 225 | }); 226 | 227 | // Expose API 228 | gitbook.fontsettings = { 229 | enlargeFontSize: enlargeFontSize, 230 | reduceFontSize: reduceFontSize, 231 | setTheme: changeColorTheme, 232 | setFamily: changeFontFamily, 233 | getThemes: getThemes, 234 | setThemes: setThemes, 235 | getFamilies: getFamilies, 236 | setFamilies: setFamilies 237 | }; 238 | }); 239 | 240 | 241 | -------------------------------------------------------------------------------- /docs/gitbook/gitbook-plugin-fontsettings/website.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Theme 1 3 | */ 4 | .color-theme-1 .dropdown-menu { 5 | background-color: #111111; 6 | border-color: #7e888b; 7 | } 8 | .color-theme-1 .dropdown-menu .dropdown-caret .caret-inner { 9 | border-bottom: 9px solid #111111; 10 | } 11 | .color-theme-1 .dropdown-menu .buttons { 12 | border-color: #7e888b; 13 | } 14 | .color-theme-1 .dropdown-menu .button { 15 | color: #afa790; 16 | } 17 | .color-theme-1 .dropdown-menu .button:hover { 18 | color: #73553c; 19 | } 20 | /* 21 | * Theme 2 22 | */ 23 | .color-theme-2 .dropdown-menu { 24 | background-color: #2d3143; 25 | border-color: #272a3a; 26 | } 27 | .color-theme-2 .dropdown-menu .dropdown-caret .caret-inner { 28 | border-bottom: 9px solid #2d3143; 29 | } 30 | .color-theme-2 .dropdown-menu .buttons { 31 | border-color: #272a3a; 32 | } 33 | .color-theme-2 .dropdown-menu .button { 34 | color: #62677f; 35 | } 36 | .color-theme-2 .dropdown-menu .button:hover { 37 | color: #f4f4f5; 38 | } 39 | .book .book-header .font-settings .font-enlarge { 40 | line-height: 30px; 41 | font-size: 1.4em; 42 | } 43 | .book .book-header .font-settings .font-reduce { 44 | line-height: 30px; 45 | font-size: 1em; 46 | } 47 | .book.color-theme-1 .book-body { 48 | color: #704214; 49 | background: #f3eacb; 50 | } 51 | .book.color-theme-1 .book-body .page-wrapper .page-inner section { 52 | background: #f3eacb; 53 | } 54 | .book.color-theme-2 .book-body { 55 | color: #bdcadb; 56 | background: #1c1f2b; 57 | } 58 | .book.color-theme-2 .book-body .page-wrapper .page-inner section { 59 | background: #1c1f2b; 60 | } 61 | .book.font-size-0 .book-body .page-inner section { 62 | font-size: 1.2rem; 63 | } 64 | .book.font-size-1 .book-body .page-inner section { 65 | font-size: 1.4rem; 66 | } 67 | .book.font-size-2 .book-body .page-inner section { 68 | font-size: 1.6rem; 69 | } 70 | .book.font-size-3 .book-body .page-inner section { 71 | font-size: 2.2rem; 72 | } 73 | .book.font-size-4 .book-body .page-inner section { 74 | font-size: 4rem; 75 | } 76 | .book.font-family-0 { 77 | font-family: Georgia, serif; 78 | } 79 | .book.font-family-1 { 80 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 81 | } 82 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal { 83 | color: #704214; 84 | } 85 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a { 86 | color: inherit; 87 | } 88 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, 89 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2, 90 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h3, 91 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h4, 92 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h5, 93 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { 94 | color: inherit; 95 | } 96 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, 97 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2 { 98 | border-color: inherit; 99 | } 100 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { 101 | color: inherit; 102 | } 103 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal hr { 104 | background-color: inherit; 105 | } 106 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal blockquote { 107 | border-color: inherit; 108 | } 109 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, 110 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { 111 | background: #fdf6e3; 112 | color: #657b83; 113 | border-color: #f8df9c; 114 | } 115 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal .highlight { 116 | background-color: inherit; 117 | } 118 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table th, 119 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table td { 120 | border-color: #f5d06c; 121 | } 122 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr { 123 | color: inherit; 124 | background-color: #fdf6e3; 125 | border-color: #444444; 126 | } 127 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { 128 | background-color: #fbeecb; 129 | } 130 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal { 131 | color: #bdcadb; 132 | } 133 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal a { 134 | color: #3eb1d0; 135 | } 136 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, 137 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2, 138 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3, 139 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4, 140 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5, 141 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { 142 | color: #fffffa; 143 | } 144 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, 145 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2 { 146 | border-color: #373b4e; 147 | } 148 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { 149 | color: #373b4e; 150 | } 151 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal hr { 152 | background-color: #373b4e; 153 | } 154 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal blockquote { 155 | border-color: #373b4e; 156 | } 157 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, 158 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { 159 | color: #9dbed8; 160 | background: #2d3143; 161 | border-color: #2d3143; 162 | } 163 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal .highlight { 164 | background-color: #282a39; 165 | } 166 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table th, 167 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table td { 168 | border-color: #3b3f54; 169 | } 170 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr { 171 | color: #b6c2d2; 172 | background-color: #2d3143; 173 | border-color: #3b3f54; 174 | } 175 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { 176 | background-color: #35394b; 177 | } 178 | .book.color-theme-1 .book-header { 179 | color: #afa790; 180 | background: transparent; 181 | } 182 | .book.color-theme-1 .book-header .btn { 183 | color: #afa790; 184 | } 185 | .book.color-theme-1 .book-header .btn:hover { 186 | color: #73553c; 187 | background: none; 188 | } 189 | .book.color-theme-1 .book-header h1 { 190 | color: #704214; 191 | } 192 | .book.color-theme-2 .book-header { 193 | color: #7e888b; 194 | background: transparent; 195 | } 196 | .book.color-theme-2 .book-header .btn { 197 | color: #3b3f54; 198 | } 199 | .book.color-theme-2 .book-header .btn:hover { 200 | color: #fffff5; 201 | background: none; 202 | } 203 | .book.color-theme-2 .book-header h1 { 204 | color: #bdcadb; 205 | } 206 | .book.color-theme-1 .book-body .navigation { 207 | color: #afa790; 208 | } 209 | .book.color-theme-1 .book-body .navigation:hover { 210 | color: #73553c; 211 | } 212 | .book.color-theme-2 .book-body .navigation { 213 | color: #383f52; 214 | } 215 | .book.color-theme-2 .book-body .navigation:hover { 216 | color: #fffff5; 217 | } 218 | /* 219 | * Theme 1 220 | */ 221 | .book.color-theme-1 .book-summary { 222 | color: #afa790; 223 | background: #111111; 224 | border-right: 1px solid rgba(0, 0, 0, 0.07); 225 | } 226 | .book.color-theme-1 .book-summary .book-search { 227 | background: transparent; 228 | } 229 | .book.color-theme-1 .book-summary .book-search input, 230 | .book.color-theme-1 .book-summary .book-search input:focus { 231 | border: 1px solid transparent; 232 | } 233 | .book.color-theme-1 .book-summary ul.summary li.divider { 234 | background: #7e888b; 235 | box-shadow: none; 236 | } 237 | .book.color-theme-1 .book-summary ul.summary li i.fa-check { 238 | color: #33cc33; 239 | } 240 | .book.color-theme-1 .book-summary ul.summary li.done > a { 241 | color: #877f6a; 242 | } 243 | .book.color-theme-1 .book-summary ul.summary li a, 244 | .book.color-theme-1 .book-summary ul.summary li span { 245 | color: #877f6a; 246 | background: transparent; 247 | font-weight: normal; 248 | } 249 | .book.color-theme-1 .book-summary ul.summary li.active > a, 250 | .book.color-theme-1 .book-summary ul.summary li a:hover { 251 | color: #704214; 252 | background: transparent; 253 | font-weight: normal; 254 | } 255 | /* 256 | * Theme 2 257 | */ 258 | .book.color-theme-2 .book-summary { 259 | color: #bcc1d2; 260 | background: #2d3143; 261 | border-right: none; 262 | } 263 | .book.color-theme-2 .book-summary .book-search { 264 | background: transparent; 265 | } 266 | .book.color-theme-2 .book-summary .book-search input, 267 | .book.color-theme-2 .book-summary .book-search input:focus { 268 | border: 1px solid transparent; 269 | } 270 | .book.color-theme-2 .book-summary ul.summary li.divider { 271 | background: #272a3a; 272 | box-shadow: none; 273 | } 274 | .book.color-theme-2 .book-summary ul.summary li i.fa-check { 275 | color: #33cc33; 276 | } 277 | .book.color-theme-2 .book-summary ul.summary li.done > a { 278 | color: #62687f; 279 | } 280 | .book.color-theme-2 .book-summary ul.summary li a, 281 | .book.color-theme-2 .book-summary ul.summary li span { 282 | color: #c1c6d7; 283 | background: transparent; 284 | font-weight: 600; 285 | } 286 | .book.color-theme-2 .book-summary ul.summary li.active > a, 287 | .book.color-theme-2 .book-summary ul.summary li a:hover { 288 | color: #f4f4f5; 289 | background: #252737; 290 | font-weight: 600; 291 | } 292 | -------------------------------------------------------------------------------- /docs/gitbook/gitbook-plugin-highlight/ebook.css: -------------------------------------------------------------------------------- 1 | pre, 2 | code { 3 | /* http://jmblog.github.io/color-themes-for-highlightjs */ 4 | /* Tomorrow Comment */ 5 | /* Tomorrow Red */ 6 | /* Tomorrow Orange */ 7 | /* Tomorrow Yellow */ 8 | /* Tomorrow Green */ 9 | /* Tomorrow Aqua */ 10 | /* Tomorrow Blue */ 11 | /* Tomorrow Purple */ 12 | } 13 | pre .hljs-comment, 14 | code .hljs-comment, 15 | pre .hljs-title, 16 | code .hljs-title { 17 | color: #8e908c; 18 | } 19 | pre .hljs-variable, 20 | code .hljs-variable, 21 | pre .hljs-attribute, 22 | code .hljs-attribute, 23 | pre .hljs-tag, 24 | code .hljs-tag, 25 | pre .hljs-regexp, 26 | code .hljs-regexp, 27 | pre .hljs-deletion, 28 | code .hljs-deletion, 29 | pre .ruby .hljs-constant, 30 | code .ruby .hljs-constant, 31 | pre .xml .hljs-tag .hljs-title, 32 | code .xml .hljs-tag .hljs-title, 33 | pre .xml .hljs-pi, 34 | code .xml .hljs-pi, 35 | pre .xml .hljs-doctype, 36 | code .xml .hljs-doctype, 37 | pre .html .hljs-doctype, 38 | code .html .hljs-doctype, 39 | pre .css .hljs-id, 40 | code .css .hljs-id, 41 | pre .css .hljs-class, 42 | code .css .hljs-class, 43 | pre .css .hljs-pseudo, 44 | code .css .hljs-pseudo { 45 | color: #c82829; 46 | } 47 | pre .hljs-number, 48 | code .hljs-number, 49 | pre .hljs-preprocessor, 50 | code .hljs-preprocessor, 51 | pre .hljs-pragma, 52 | code .hljs-pragma, 53 | pre .hljs-built_in, 54 | code .hljs-built_in, 55 | pre .hljs-literal, 56 | code .hljs-literal, 57 | pre .hljs-params, 58 | code .hljs-params, 59 | pre .hljs-constant, 60 | code .hljs-constant { 61 | color: #f5871f; 62 | } 63 | pre .ruby .hljs-class .hljs-title, 64 | code .ruby .hljs-class .hljs-title, 65 | pre .css .hljs-rules .hljs-attribute, 66 | code .css .hljs-rules .hljs-attribute { 67 | color: #eab700; 68 | } 69 | pre .hljs-string, 70 | code .hljs-string, 71 | pre .hljs-value, 72 | code .hljs-value, 73 | pre .hljs-inheritance, 74 | code .hljs-inheritance, 75 | pre .hljs-header, 76 | code .hljs-header, 77 | pre .hljs-addition, 78 | code .hljs-addition, 79 | pre .ruby .hljs-symbol, 80 | code .ruby .hljs-symbol, 81 | pre .xml .hljs-cdata, 82 | code .xml .hljs-cdata { 83 | color: #718c00; 84 | } 85 | pre .css .hljs-hexcolor, 86 | code .css .hljs-hexcolor { 87 | color: #3e999f; 88 | } 89 | pre .hljs-function, 90 | code .hljs-function, 91 | pre .python .hljs-decorator, 92 | code .python .hljs-decorator, 93 | pre .python .hljs-title, 94 | code .python .hljs-title, 95 | pre .ruby .hljs-function .hljs-title, 96 | code .ruby .hljs-function .hljs-title, 97 | pre .ruby .hljs-title .hljs-keyword, 98 | code .ruby .hljs-title .hljs-keyword, 99 | pre .perl .hljs-sub, 100 | code .perl .hljs-sub, 101 | pre .javascript .hljs-title, 102 | code .javascript .hljs-title, 103 | pre .coffeescript .hljs-title, 104 | code .coffeescript .hljs-title { 105 | color: #4271ae; 106 | } 107 | pre .hljs-keyword, 108 | code .hljs-keyword, 109 | pre .javascript .hljs-function, 110 | code .javascript .hljs-function { 111 | color: #8959a8; 112 | } 113 | pre .hljs, 114 | code .hljs { 115 | display: block; 116 | background: white; 117 | color: #4d4d4c; 118 | padding: 0.5em; 119 | } 120 | pre .coffeescript .javascript, 121 | code .coffeescript .javascript, 122 | pre .javascript .xml, 123 | code .javascript .xml, 124 | pre .tex .hljs-formula, 125 | code .tex .hljs-formula, 126 | pre .xml .javascript, 127 | code .xml .javascript, 128 | pre .xml .vbscript, 129 | code .xml .vbscript, 130 | pre .xml .css, 131 | code .xml .css, 132 | pre .xml .hljs-cdata, 133 | code .xml .hljs-cdata { 134 | opacity: 0.5; 135 | } 136 | -------------------------------------------------------------------------------- /docs/gitbook/gitbook-plugin-lunr/lunr.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.12 3 | * Copyright (C) 2015 Oliver Nightingale 4 | * MIT Licensed 5 | * @license 6 | */ 7 | !function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.5.12",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(t){return arguments.length&&null!=t&&void 0!=t?Array.isArray(t)?t.map(function(t){return t.toLowerCase()}):t.toString().trim().toLowerCase().split(/[\s\-]+/):[]},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,o=0;n>o;o++){for(var r=t[o],s=0;i>s&&(r=this._stack[s](r,o,t),void 0!==r);s++);void 0!==r&&e.push(r)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(r===t)return o;t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o]}return r===t?o:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,o=e+Math.floor(i/2),r=this.elements[o];i>1;)t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o];return r>t?o:t>r?o+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,o=0,r=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>r-1||o>s-1)break;a[i]!==h[o]?a[i]h[o]&&o++:(n.add(a[i]),i++,o++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;return this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone(),i.add.apply(i,n.toArray()),i},t.SortedSet.prototype.toJSON=function(){return this.toArray()},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.Store,this.tokenStore=new t.TokenStore,this.corpusTokens=new t.SortedSet,this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var t=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,t)},t.Index.prototype.off=function(t,e){return this.eventEmitter.removeListener(t,e)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;return n._fields=e.fields,n._ref=e.ref,n.documentStore=t.Store.load(e.documentStore),n.tokenStore=t.TokenStore.load(e.tokenStore),n.corpusTokens=t.SortedSet.load(e.corpusTokens),n.pipeline=t.Pipeline.load(e.pipeline),n},t.Index.prototype.field=function(t,e){var e=e||{},n={name:t,boost:e.boost||1};return this._fields.push(n),this},t.Index.prototype.ref=function(t){return this._ref=t,this},t.Index.prototype.add=function(e,n){var i={},o=new t.SortedSet,r=e[this._ref],n=void 0===n?!0:n;this._fields.forEach(function(n){var r=this.pipeline.run(t.tokenizer(e[n.name]));i[n.name]=r,t.SortedSet.prototype.add.apply(o,r)},this),this.documentStore.set(r,o),t.SortedSet.prototype.add.apply(this.corpusTokens,o.toArray());for(var s=0;s0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(t.tokenizer(e)),i=new t.Vector,o=[],r=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*r,h=this,l=this.tokenStore.expand(e).reduce(function(n,o){var r=h.corpusTokens.indexOf(o),s=h.idf(o),l=1,u=new t.SortedSet;if(o!==e){var c=Math.max(3,o.length-e.length);l=1/Math.log(c)}return r>-1&&i.insert(r,a*s*l),Object.keys(h.tokenStore.get(o)).forEach(function(t){u.add(t)}),n.union(u)},new t.SortedSet);o.push(l)},this);var a=o.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,o=new t.Vector,r=0;i>r;r++){var s=n.elements[r],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);o.insert(this.corpusTokens.indexOf(s),a*h)}return o},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,a="^("+o+")?"+r+o+"("+r+")?$",h="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,u=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(l),p=/^(.+?)(ss|i)es$/,m=/^(.+?)([^s])s$/,v=/^(.+?)eed$/,y=/^(.+?)(ed|ing)$/,g=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),x=new RegExp("^"+o+i+"[^aeiouwxy]$"),k=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,_=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,O=/^(.+?)e$/,P=/ll$/,N=new RegExp("^"+o+i+"[^aeiouwxy]$"),T=function(n){var i,o,r,s,a,h,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,a=m,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=v,a=y,s.test(n)){var T=s.exec(n);s=u,s.test(T[1])&&(s=g,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,l=x,a.test(n)?n+="e":h.test(n)?(s=g,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=k,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+t[o])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+e[o])}if(s=_,a=F,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=O,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=N,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=P,a=c,s.test(n)&&a.test(n)&&(s=g,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==e?e:void 0},t.stopWordFilter.stopWords={a:"a",able:"able",about:"about",across:"across",after:"after",all:"all",almost:"almost",also:"also",am:"am",among:"among",an:"an",and:"and",any:"any",are:"are",as:"as",at:"at",be:"be",because:"because",been:"been",but:"but",by:"by",can:"can",cannot:"cannot",could:"could",dear:"dear",did:"did","do":"do",does:"does",either:"either","else":"else",ever:"ever",every:"every","for":"for",from:"from",get:"get",got:"got",had:"had",has:"has",have:"have",he:"he",her:"her",hers:"hers",him:"him",his:"his",how:"how",however:"however",i:"i","if":"if","in":"in",into:"into",is:"is",it:"it",its:"its",just:"just",least:"least",let:"let",like:"like",likely:"likely",may:"may",me:"me",might:"might",most:"most",must:"must",my:"my",neither:"neither",no:"no",nor:"nor",not:"not",of:"of",off:"off",often:"often",on:"on",only:"only",or:"or",other:"other",our:"our",own:"own",rather:"rather",said:"said",say:"say",says:"says",she:"she",should:"should",since:"since",so:"so",some:"some",than:"than",that:"that",the:"the",their:"their",them:"them",then:"then",there:"there",these:"these",they:"they","this":"this",tis:"tis",to:"to",too:"too",twas:"twas",us:"us",wants:"wants",was:"was",we:"we",were:"were",what:"what",when:"when",where:"where",which:"which","while":"while",who:"who",whom:"whom",why:"why",will:"will","with":"with",would:"would",yet:"yet",you:"you",your:"your"},t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){var e=t.replace(/^\W+/,"").replace(/\W+$/,"");return""===e?void 0:e},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t[0],o=t.slice(1);return i in n||(n[i]={docs:{}}),0===o.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(o,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n element for each result 48 | res.results.forEach(function(res) { 49 | var $li = $('

  • ', { 50 | 'class': 'search-results-item' 51 | }); 52 | 53 | var $title = $('

    '); 54 | 55 | var $link = $('', { 56 | 'href': gitbook.state.basePath + '/' + res.url, 57 | 'text': res.title 58 | }); 59 | 60 | var content = res.body.trim(); 61 | if (content.length > MAX_DESCRIPTION_SIZE) { 62 | content = content.slice(0, MAX_DESCRIPTION_SIZE).trim()+'...'; 63 | } 64 | var $content = $('

    ').html(content); 65 | 66 | $link.appendTo($title); 67 | $title.appendTo($li); 68 | $content.appendTo($li); 69 | $li.appendTo($searchList); 70 | }); 71 | } 72 | 73 | function launchSearch(q) { 74 | // Add class for loading 75 | $body.addClass('with-search'); 76 | $body.addClass('search-loading'); 77 | 78 | // Launch search query 79 | throttle(gitbook.search.query(q, 0, MAX_RESULTS) 80 | .then(function(results) { 81 | displayResults(results); 82 | }) 83 | .always(function() { 84 | $body.removeClass('search-loading'); 85 | }), 1000); 86 | } 87 | 88 | function closeSearch() { 89 | $body.removeClass('with-search'); 90 | $bookSearchResults.removeClass('open'); 91 | } 92 | 93 | function launchSearchFromQueryString() { 94 | var q = getParameterByName('q'); 95 | if (q && q.length > 0) { 96 | // Update search input 97 | $searchInput.val(q); 98 | 99 | // Launch search 100 | launchSearch(q); 101 | } 102 | } 103 | 104 | function bindSearch() { 105 | // Bind DOM 106 | $searchInput = $('#book-search-input input'); 107 | $bookSearchResults = $('#book-search-results'); 108 | $searchList = $bookSearchResults.find('.search-results-list'); 109 | $searchTitle = $bookSearchResults.find('.search-results-title'); 110 | $searchResultsCount = $searchTitle.find('.search-results-count'); 111 | $searchQuery = $searchTitle.find('.search-query'); 112 | 113 | // Launch query based on input content 114 | function handleUpdate() { 115 | var q = $searchInput.val(); 116 | 117 | if (q.length == 0) { 118 | closeSearch(); 119 | } 120 | else { 121 | launchSearch(q); 122 | } 123 | } 124 | 125 | // Detect true content change in search input 126 | // Workaround for IE < 9 127 | var propertyChangeUnbound = false; 128 | $searchInput.on('propertychange', function(e) { 129 | if (e.originalEvent.propertyName == 'value') { 130 | handleUpdate(); 131 | } 132 | }); 133 | 134 | // HTML5 (IE9 & others) 135 | $searchInput.on('input', function(e) { 136 | // Unbind propertychange event for IE9+ 137 | if (!propertyChangeUnbound) { 138 | $(this).unbind('propertychange'); 139 | propertyChangeUnbound = true; 140 | } 141 | 142 | handleUpdate(); 143 | }); 144 | 145 | // Push to history on blur 146 | $searchInput.on('blur', function(e) { 147 | // Update history state 148 | if (usePushState) { 149 | var uri = updateQueryString('q', $(this).val()); 150 | history.pushState({ path: uri }, null, uri); 151 | } 152 | }); 153 | } 154 | 155 | gitbook.events.on('page.change', function() { 156 | bindSearch(); 157 | closeSearch(); 158 | 159 | // Launch search based on query parameter 160 | if (gitbook.search.isInitialized()) { 161 | launchSearchFromQueryString(); 162 | } 163 | }); 164 | 165 | gitbook.events.on('search.ready', function() { 166 | bindSearch(); 167 | 168 | // Launch search from query param at start 169 | launchSearchFromQueryString(); 170 | }); 171 | 172 | function getParameterByName(name) { 173 | var url = window.location.href; 174 | name = name.replace(/[\[\]]/g, '\\$&'); 175 | var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)', 'i'), 176 | results = regex.exec(url); 177 | if (!results) return null; 178 | if (!results[2]) return ''; 179 | return decodeURIComponent(results[2].replace(/\+/g, ' ')); 180 | } 181 | 182 | function updateQueryString(key, value) { 183 | value = encodeURIComponent(value); 184 | 185 | var url = window.location.href; 186 | var re = new RegExp('([?&])' + key + '=.*?(&|#|$)(.*)', 'gi'), 187 | hash; 188 | 189 | if (re.test(url)) { 190 | if (typeof value !== 'undefined' && value !== null) 191 | return url.replace(re, '$1' + key + '=' + value + '$2$3'); 192 | else { 193 | hash = url.split('#'); 194 | url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, ''); 195 | if (typeof hash[1] !== 'undefined' && hash[1] !== null) 196 | url += '#' + hash[1]; 197 | return url; 198 | } 199 | } 200 | else { 201 | if (typeof value !== 'undefined' && value !== null) { 202 | var separator = url.indexOf('?') !== -1 ? '&' : '?'; 203 | hash = url.split('#'); 204 | url = hash[0] + separator + key + '=' + value; 205 | if (typeof hash[1] !== 'undefined' && hash[1] !== null) 206 | url += '#' + hash[1]; 207 | return url; 208 | } 209 | else 210 | return url; 211 | } 212 | } 213 | }); 214 | -------------------------------------------------------------------------------- /docs/gitbook/gitbook-plugin-sharing/buttons.js: -------------------------------------------------------------------------------- 1 | require(['gitbook', 'jquery'], function(gitbook, $) { 2 | var SITES = { 3 | 'facebook': { 4 | 'label': 'Facebook', 5 | 'icon': 'fa fa-facebook', 6 | 'onClick': function(e) { 7 | e.preventDefault(); 8 | window.open('http://www.facebook.com/sharer/sharer.php?s=100&p[url]='+encodeURIComponent(location.href)); 9 | } 10 | }, 11 | 'twitter': { 12 | 'label': 'Twitter', 13 | 'icon': 'fa fa-twitter', 14 | 'onClick': function(e) { 15 | e.preventDefault(); 16 | window.open('http://twitter.com/home?status='+encodeURIComponent(document.title+' '+location.href)); 17 | } 18 | }, 19 | 'google': { 20 | 'label': 'Google+', 21 | 'icon': 'fa fa-google-plus', 22 | 'onClick': function(e) { 23 | e.preventDefault(); 24 | window.open('https://plus.google.com/share?url='+encodeURIComponent(location.href)); 25 | } 26 | }, 27 | 'weibo': { 28 | 'label': 'Weibo', 29 | 'icon': 'fa fa-weibo', 30 | 'onClick': function(e) { 31 | e.preventDefault(); 32 | window.open('http://service.weibo.com/share/share.php?content=utf-8&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)); 33 | } 34 | }, 35 | 'instapaper': { 36 | 'label': 'Instapaper', 37 | 'icon': 'fa fa-instapaper', 38 | 'onClick': function(e) { 39 | e.preventDefault(); 40 | window.open('http://www.instapaper.com/text?u='+encodeURIComponent(location.href)); 41 | } 42 | }, 43 | 'vk': { 44 | 'label': 'VK', 45 | 'icon': 'fa fa-vk', 46 | 'onClick': function(e) { 47 | e.preventDefault(); 48 | window.open('http://vkontakte.ru/share.php?url='+encodeURIComponent(location.href)); 49 | } 50 | } 51 | }; 52 | 53 | 54 | 55 | gitbook.events.bind('start', function(e, config) { 56 | var opts = config.sharing; 57 | 58 | // Create dropdown menu 59 | var menu = $.map(opts.all, function(id) { 60 | var site = SITES[id]; 61 | 62 | return { 63 | text: site.label, 64 | onClick: site.onClick 65 | }; 66 | }); 67 | 68 | // Create main button with dropdown 69 | if (menu.length > 0) { 70 | gitbook.toolbar.createButton({ 71 | icon: 'fa fa-share-alt', 72 | label: 'Share', 73 | position: 'right', 74 | dropdown: [menu] 75 | }); 76 | } 77 | 78 | // Direct actions to share 79 | $.each(SITES, function(sideId, site) { 80 | if (!opts[sideId]) return; 81 | 82 | gitbook.toolbar.createButton({ 83 | icon: site.icon, 84 | label: site.text, 85 | position: 'right', 86 | onClick: site.onClick 87 | }); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /docs/gitbook/images/apple-touch-icon-precomposed-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/gitbook/images/apple-touch-icon-precomposed-152.png -------------------------------------------------------------------------------- /docs/gitbook/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/gitbook/images/favicon.ico -------------------------------------------------------------------------------- /docs/画个三角形吧/1.0.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/画个三角形吧/1.0.1.png -------------------------------------------------------------------------------- /docs/画个三角形吧/1.0创建窗口.md: -------------------------------------------------------------------------------- 1 | # 开始冒险的第一步,整个窗口出来 2 | 3 | ## 初始化一个项目 4 | 首先我们使用 ```cargo init try``` 5 | 创建一个基础的Rust项目 6 | 7 | ``` 8 | try 9 | src 10 | cargo.toml 11 | .git 12 | ``` 13 | 大概是这个样子的 14 | 15 | 我们需要安装一些库. Rust是一个非常好的工具因为他提供了Cargo这么好用的减少心智负担的工具 16 | 我们打开 cargo.toml 17 | ```toml 18 | [package] 19 | #这个一般都给你写好了,不用操心 20 | name = "try" 21 | #项目名称 22 | version = "0.1.0" 23 | #项目版本 24 | authors = ["lemonhx "] 25 | #作者们 26 | edition = "2018" 27 | 28 | [dependencies] 29 | #依赖的库 30 | winit = "0.19" 31 | #我们核心的GFX硬件抽象库 32 | gfx-hal = "0.3.0" 33 | #一个玩过rust就知道的库 34 | arrayvec = "0.4" 35 | 36 | #vulkan作为gfx后端的配置 37 | [dependencies.gfx-backend-vulkan] 38 | version = "0.3" 39 | features = ["winit"] 40 | optional = true 41 | 42 | #metal作为gfx后端的配置 43 | [target.'cfg(target_os = "macos")'.dependencies.gfx-backend-metal] 44 | version = "0.3" 45 | features = ["winit"] 46 | optional = true 47 | 48 | #DX12作为gfx后端的配置 49 | [target.'cfg(windows)'.dependencies.gfx-backend-dx12] 50 | version = "0.3.0" 51 | features = ["winit"] 52 | optional = true 53 | 54 | #编译配置 55 | [build-dependencies] 56 | glsl-to-spirv = "0.1.7" 57 | 58 | #启用扩展,我们默认使用跨平台的Vulkan 59 | [features] 60 | default = ["gfx-backend-vulkan"] 61 | metal = ["gfx-backend-metal"] 62 | dx12 = ["gfx-backend-dx12"] 63 | vulkan = ["gfx-backend-vulkan"] 64 | ``` 65 | 抄好之后使用```cargo run```下载依赖并运行,不出问题的话就会有hello world窗口弹出 66 | ## 窗口结构 67 | 我们需要定义以下窗口结构体 68 | ```rust 69 | pub struct WindowState { 70 | pub evloop: EventsLoop, 71 | pub window: Window, 72 | } 73 | ``` 74 | 这是我定义的一个窗口结构体,里面装有一个事件循环和一个窗口 75 | 76 | 事件循环是什么可能很多人不知道我可以大概解释一下: 77 | 78 | 我自己在跑步(循环),跑着跑着被绊倒了(遇到事件),我爬起来(事件的处理方式),我继续跑(重新回到循环) 79 | 80 | 这个是一个简单的事件循环,同样,我还可以 81 | 82 | 跑步(循环),被车撞了(遇到事件),躺地上求救(事件处理),没人搭理死了(处理结果) 83 | 84 | 很简单明了,创建窗口需要绑定一个事件循环的原因是,你可能对着这个窗口按键,点击鼠标,拖拽,窗口会用你之前定义好的事件处理方式去处理这些事件. 85 | 86 | 然后如何初始化一个对应这个窗口的实例呢? 87 | ```rust 88 | impl WindowState { 89 | //为了避免使代码过度复杂,这里就直接不行报错了 90 | pub fn new>(title: T, size: (f64, f64)) -> Self{ 91 | let evloop = EventsLoop::new(); //创建事件循环 92 | let window = match WindowBuilder::new() //使用窗口创造器 93 | .with_title(title) //绑上标题 94 | .with_dimensions(LogicalSize::new(size.0, size.1)) //设置大小 95 | .build(&evloop) //尝试构造 这里的返回类型是一个Result所以我们再match一手 96 | { 97 | Ok(x) => x, 98 | Err(e) => {println!("创建窗口失败,原因:{:?}", e);panic!()} 99 | }; 100 | Self { 101 | evloop, 102 | window, 103 | } 104 | } 105 | } 106 | ``` 107 | 108 | ## 事件处理() 109 | 一个窗口对应了一个事件循环,有些时候事件循环就是一个```while```循环,但是这里我们的```winit```库给我们提供了一个基于control flow的一个事件循环,总体来讲就是我们不需要把事件的处理方法挤在一个```while```里面来写了,在这里我定义了一个事件处理函数 110 | 111 | ```rust 112 | use winit::{ControlFlow, Event, VirtualKeyCode, WindowEvent};// 使用 控制流 事件 按键代码 窗口事件 113 | // 这个函数收到一个事件,返回一个控制流(之后干什么) 114 | pub fn event_handler(event: Event) -> winit::ControlFlow { 115 | match event { 116 | // 处理键盘输入事件 117 | Event::WindowEvent { event, .. } => match event { 118 | WindowEvent::KeyboardInput { input, .. } => { 119 | if let Some(VirtualKeyCode::Escape) = input.virtual_keycode {//如果是ESC被按下来了 120 | ControlFlow::Break //退出事件循环 121 | } else { 122 | ControlFlow::Continue //要不啥也不干 123 | } 124 | } 125 | WindowEvent::CloseRequested => ControlFlow::Break, //如果窗口被关了,退出程序 126 | 127 | _ => ControlFlow::Continue, 128 | }, 129 | _ => ControlFlow::Continue, 130 | } 131 | } 132 | ``` 133 | ## 把上面的拼装到一起 134 | 最后开始写```app.rs``` 135 | 我们的app暂时仅仅只有一个窗口,所以我们创建一个结构体包含一个窗口实例就行了 136 | ```rust 137 | use super::super::window::window::*; 138 | use super::super::event_handler::event_handler::*; 139 | pub struct App { 140 | windowstate : WindowState 141 | } 142 | impl App { 143 | pub fn new() { 144 | let mut ws = WindowState::new("new_window", (1024f64, 768f64)); //设置窗口标题和大小 145 | ws.evloop.run_forever(event_handler); //开始运行事件循环 146 | } 147 | } 148 | ``` 149 | 150 | ## yes! 开始运行 151 | 最后,我们更改```main.rs``` 152 | ```rust 153 | pub mod app; 154 | pub mod window; 155 | pub mod event_handler; 156 | 157 | use app::app::*; 158 | 159 | fn main() { 160 | let _ = App::new(); 161 | } 162 | 163 | ``` 164 | 最后```cargo run```会有一个窗口弹出 165 | 166 | 我的大概是这个效果 167 | ![来自ubuntu + dde](./1.0.1.png) 168 | 169 | 通常情况下运行会得到一个白色或者黑色的窗口,甚至在某些情况下窗口里会出现你正在打开的程序的一部分的图像 170 | 这都是正常的现象 -------------------------------------------------------------------------------- /docs/画个三角形吧/1.1.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/画个三角形吧/1.1.2.png -------------------------------------------------------------------------------- /docs/画个三角形吧/1.10处理输入事件.md: -------------------------------------------------------------------------------- 1 | # 1.1.9 处理输入事件 2 | 3 | 这一章,我们讲一下如何处理输入事件. 4 | 5 | 我们可以把一个方形当成屏幕,鼠标就是方形上的一个点的位置. 6 | 那么我们就可以通过x轴和y轴的距离判断鼠标的位置. 7 | 8 | 所以我们开写,在`event_handler`目录下创建然后添加`local_state.rs` 9 | ```rust 10 | #[derive(Debug, Clone, Copy, Default)] 11 | pub struct LocalState { 12 | pub frame_width: f64, // 窗口宽 13 | pub frame_height: f64, // 窗口高 14 | pub mouse_x: f64, // 鼠标的x轴距离 15 | pub mouse_y: f64, // 鼠标的y轴距离 16 | } 17 | ``` 18 | 然后根据这个,我们写一个更新`State`的函数 19 | ```rust 20 | impl LocalState { 21 | pub fn update_from_input(&mut self, input: UserInput/*这个一会儿再讲*/) { 22 | if let Some(frame_size) = input.new_frame_size { 23 | //if let 其实就是个match的语法糖 24 | self.frame_width = frame_size.0; 25 | self.frame_height = frame_size.1; 26 | } 27 | if let Some(position) = input.new_mouse_position { 28 | self.mouse_x = position.0; 29 | self.mouse_y = position.1; 30 | } 31 | } 32 | } 33 | ``` 34 | 好的,我们已经写好了这个`State`那么如何获取到屏幕的输入呢? 35 | 36 | 我们需要去里用`winit`的`event_loop`去获取 37 | 所以我们在`event_handler`下新建一个`user_input.rs` 38 | ```rust 39 | pub struct UserInput { 40 | pub end_requested: bool, // 窗口被关了 41 | pub new_frame_size: Option<(f64, f64)>, // 更新的屏幕宽度 42 | pub new_mouse_position: Option<(f64, f64)>, // 更新的鼠标位置 43 | } 44 | ``` 45 | 然后用`event_loop`去获取这些参数 46 | ```rust 47 | impl UserInput { 48 | pub fn poll_events_loop(events_loop: &mut EventsLoop) -> Self { 49 | let mut output = UserInput::default(); 50 | events_loop.poll_events(|event| match event { 51 | Event::WindowEvent { 52 | event: WindowEvent::CloseRequested, 53 | // 如果pull下来窗口关闭行为 54 | .. 55 | } => output.end_requested = true, 56 | Event::WindowEvent { 57 | event: WindowEvent::Resized(logical), 58 | // 如果发现窗口大小改变了 59 | .. 60 | } => { 61 | output.new_frame_size = Some((logical.width, logical.height)); 62 | } 63 | Event::WindowEvent { 64 | event: WindowEvent::CursorMoved { position, .. }, 65 | // 发现鼠标的位置移动了 66 | .. 67 | } => { 68 | output.new_mouse_position = Some((position.x, position.y)); 69 | } 70 | _ => (), 71 | }); 72 | output 73 | } 74 | } 75 | ``` 76 | 好的,这样我们就成功的获取到了鼠标的位置,窗口的大小 -------------------------------------------------------------------------------- /docs/画个三角形吧/1.11.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/画个三角形吧/1.11.1.png -------------------------------------------------------------------------------- /docs/画个三角形吧/1.11写一个clearcolor.md: -------------------------------------------------------------------------------- 1 | # 1.1.10 高端清屏 2 | 3 | 我们既然已经获得了鼠标的位置, 4 | 那么我们可以尝试利用鼠标的位置去给屏幕换上不同的颜色 5 | 6 | ```rust 7 | // 根据鼠标的位置将屏幕染成指定的颜色 8 | pub fn clear_color_by_mouse( 9 | s: &mut HardwareState, 10 | color: [f32; 4], 11 | ) -> Result, &'static str> { 12 | // 设置信号量 13 | let image_available = &s.image_available_semaphores[s.current_frame]; 14 | let render_finished = &s.render_finished_semaphores[s.current_frame]; 15 | // 获取当前帧数 16 | s.current_frame = (s.current_frame + 1) % s.frames_in_flight; 17 | 18 | let (i_u32, i_usize) = unsafe { 19 | let (image_index, _) = s 20 | .swapchain 21 | //请求从swapchain里取出一个图片 22 | .acquire_image( 23 | core::u64::MAX, 24 | Some(image_available), 25 | Some(&s.in_flight_fences[s.current_frame]), 26 | ) 27 | .map_err(|_| "错误,不能从交换链中取出图片!")?; 28 | (image_index, image_index as usize) 29 | }; 30 | 31 | let flight_fence = &s.in_flight_fences[i_usize]; 32 | unsafe { 33 | s.device 34 | .wait_for_fence(flight_fence, core::u64::MAX) 35 | .map_err(|_| "错误,等待内存屏障失败!")?; 36 | s.device 37 | .reset_fence(flight_fence) 38 | .map_err(|_| "错误,重置内存屏障失败!")?; 39 | } 40 | 41 | unsafe { 42 | // 获取一个command buffer 43 | let buffer = &mut s.command_buffers[i_usize]; 44 | // 拿到Clear color 的值 45 | let clear_values = [ClearValue::Color(ClearColor::from(color))]; 46 | buffer.begin(false); 47 | buffer.begin_render_pass_inline( 48 | &s.render_pass, 49 | &s.framebuffers[i_usize], 50 | s.render_area, 51 | clear_values.iter(), 52 | ); 53 | buffer.finish(); 54 | } 55 | 56 | // 拿到command buffer 57 | let command_buffers = &s.command_buffers[i_usize..=i_usize]; 58 | // 等信号量各就各位 59 | let wait_semaphores: ArrayVec<[_; 1]> = 60 | [(image_available, PipelineStage::COLOR_ATTACHMENT_OUTPUT)].into(); 61 | let signal_semaphores: ArrayVec<[_; 1]> = [render_finished].into(); 62 | let present_wait_semaphores: ArrayVec<[_; 1]> = [render_finished].into(); 63 | // 开推 64 | let submission = Submission { 65 | command_buffers, 66 | wait_semaphores, 67 | signal_semaphores, 68 | }; 69 | 70 | // 扔到控制队列 71 | let the_command_queue = &mut s.queue_group.queues[0]; 72 | // tada! 完成了 73 | unsafe { 74 | the_command_queue.submit(submission, Some(flight_fence)); 75 | s.swapchain 76 | .present(the_command_queue, i_u32, present_wait_semaphores) 77 | .map_err(|_| "错误,添加到区块链失败!") 78 | } 79 | } 80 | 81 | ``` 82 | 然后我们在`app.rs`中去写一个根据鼠标位置变换rgb的函数 83 | ```rust 84 | fn change_rgb_by_state(hal_state: &mut HardwareState, local_state: &LocalState) { 85 | let b = (local_state.mouse_x / local_state.frame_width) as f32; 86 | let g = (local_state.mouse_y / local_state.frame_height) as f32; 87 | let r = (b + g) * 0.3; 88 | let a = 1.0; 89 | let _ = clear_color_by_mouse(hal_state, [r, g, b, a]); 90 | } 91 | ``` 92 | 最后我们再重写下`App` 93 | ```rust 94 | impl App { 95 | pub fn new() { 96 | let mut ws = WindowState::new(title, (1024f64, 768f64)); 97 | let mut hs = match HardwareState::new(&ws.window, title) { 98 | Ok(x) => x, 99 | Err(x) => panic!(), 100 | }; 101 | let (frame_width, frame_height) = ws 102 | .window 103 | .get_inner_size() 104 | .map(|logical| logical.into()) 105 | .unwrap_or((1.0, 1.0)); 106 | let mut ls = LocalState { 107 | frame_width, 108 | frame_height, 109 | mouse_x: 0.0, 110 | mouse_y: 0.0, 111 | }; 112 | let mut running = true; 113 | while running { 114 | let inputs = UserInput::poll_events_loop(&mut ws.evloop); 115 | if inputs.end_requested { 116 | running = false; 117 | } 118 | if inputs.new_frame_size.is_some() { 119 | drop(hs); 120 | hs = match HardwareState::new(&ws.window, title) { 121 | Ok(state) => state, 122 | Err(e) => panic!(e), 123 | }; 124 | } 125 | ls.update_from_input(inputs); 126 | change_rgb_by_state(&mut hs, &ls); 127 | } 128 | } 129 | } 130 | 131 | ``` 132 | 现在我们去运行一下,会发现屏幕的颜色跟随鼠标的位置而改变 133 | ![来自windows](./1.11.1.png) -------------------------------------------------------------------------------- /docs/画个三角形吧/1.12渲染过程.md: -------------------------------------------------------------------------------- 1 | # 1.1.11 渲染过程 2 | 3 | -------------------------------------------------------------------------------- /docs/画个三角形吧/1.13把图形管线写完.md: -------------------------------------------------------------------------------- 1 | # 1.1.12 把图形管线写完 2 | 3 | -------------------------------------------------------------------------------- /docs/画个三角形吧/1.14帧缓冲.md: -------------------------------------------------------------------------------- 1 | # 1.1.13 帧缓冲 2 | 3 | -------------------------------------------------------------------------------- /docs/画个三角形吧/1.15命令池.md: -------------------------------------------------------------------------------- 1 | # 1.1.14 命令池 2 | 3 | -------------------------------------------------------------------------------- /docs/画个三角形吧/1.16劳资要的三角形.md: -------------------------------------------------------------------------------- 1 | # 1.2 劳资要的三角形 2 | 3 | -------------------------------------------------------------------------------- /docs/画个三角形吧/1.17小结.md: -------------------------------------------------------------------------------- 1 | # 1.3 小结 2 | 3 | -------------------------------------------------------------------------------- /docs/画个三角形吧/1.1创建实例.md: -------------------------------------------------------------------------------- 1 | # 先弄几个方便使用的结构 2 | 3 | ## 帧的概念 4 | 什么是一帧? 其实我们说的帧就是一幅静止的画面,无数个静止的画面连起来播放,通过我们的脑补帧来让我们看到连贯的动画,fps就是我们常用的衡量多少帧每秒的单位,一般情况下29fps是电视台用的帧率,60fps是一般的游戏的帧率(在我们玩游戏的时候神经高度集中,29会让我们觉得晕),144fps就是一些竞技类的游戏想要达到的帧率. 5 | 6 | 帧又是怎么诞生的呢? 一般情况下就是在电脑里渲染出3D场景然后利用投射投到2D的屏幕上,也就是fps就是一秒钟我们能渲染几次这个图像,```gfx-hal```使用的都是常见的光栅化的渲染(虽然正在准备加入```Nvidia Ray Tracing```支持),所谓的光栅化其实也是非常的简单,我们在电脑里获取了每一个物体的顶点的位置和顶点的连接之后,利用着色器(shader)来给顶点上色,然后在给顶点围起来的区域上色. 7 | 8 | 这里在补充一下,光线追踪指的是利用每一束计算机模拟的光打在物体上模拟出来的光路来上色的,上色的细节会比光栅化好的很多(因为一般涉及到那种带有反射啊,金属材质什么的光栅化需要很多的骚技巧来模拟) 9 | 10 | 11 | 看到这里你们大概有个概念了吧 下面是伪码 12 | ```rust 13 | Vec //一堆帧 14 | 15 | //渲染主程序 16 | loop{ 17 | //... 18 | render(hardware,data); 19 | } 20 | ``` 21 | 这里再补充一下,很多时候你们会听到帧缓冲这个概念,什么是帧缓冲的? 22 | 其实说白了就是提前渲染好的一堆帧用来当GPU忙的喘不过气来的时候来顶替一下,混淆一下你的大脑,因为毕竟每一帧的东西都不太一样,难免有些地方会慢一两秒.所以帧缓冲就是来改善体验,让用户觉得更流畅的. 23 | 24 | ## 如何控制上面的循环呢 25 | 在第一课里,你已经掌握了事件循环和窗口的创建,但是实际上这距离真正的图形学还有点远,我们还需要处理一些非常底层的,硬件的,和GPU沟通的硬件代码,虽然```gfx-hal```已经给我们提供了一个很好的核心,但在核心之上还需要写一些硬件代码,为了符合Rust的美学,我们需要用什么结构来包装起来 26 | > 吐槽啊,hkt什么时候出来,现在写的这一堆monad类似的东西用一个hkt能简化多少工作流程 27 | 所以这里要引入 28 | 29 | ```HardwareState```用来控制```GPU```的状态 和 ```LocalState```控制用户的输入输出之类的状态,来更好的用高级抽象控制GPU的执行,控制非纯操作的作用范围 30 | 31 | 上面的那个```render```函数就是用来渲染的 函数定义伪码 32 | ```rust 33 | pub fn render(hal:&mut HardwareState,data:&LocalState){ 34 | hal.draw(data.color) 35 | } 36 | ``` 37 | 38 | 这段是写给真正从事过这方面开发的人看的,有些时候render会出错,出错了要debug,那么这个```gfx```是怎么debug的呢?其实,```gfx```是一个胶水库,粘合了所有的底层API,所以如果你使用的是```DX12```后端那么你可以用你最喜欢的```Visual Studio```来调试可执行程序,如果你使用的是```Vulkan```后端那么你可以使用```LunarG Vulkan SDK``` 提供给你的一系列程序来调试 同时 ```gfx```会自动的报出```debug layer```的数据,```Metal``` 我没使用过,不清楚,如果你想让我写关于这方面的可以考虑资助我一台```Macbook```,你喜欢```OpenGL```??!!??!! 对不起,我们不认识,你可选择右上角```⌧```再见 39 | 40 | ## 设置后端 41 | ```rust 42 | #[cfg(feature = "dx12")] 43 | use gfx_backend_dx12 as back; 44 | #[cfg(feature = "vulkan")] 45 | use gfx_backend_vulkan as back; 46 | #[cfg(feature = "metal")] 47 | use gfx_backend_metal as back; 48 | #[cfg(not(any(feature = "vulkan", feature = "dx12",feature = "metal",)))] 49 | use gfx_backend_vulkan as back; 50 | ``` 51 | 这几行代码会先查看你的```cargo.toml```来确定你使用的是哪一个后端,然后再使用,和```C语言```里面的```#ifdef```差不多 52 | 然后在编译的时候需要指定 ```cargo run --features vulkan```之类的来确定到底用了那个后端 53 | ## 开始设计```HardwareState``` 54 | ```rust 55 | pub struct HardwareState { 56 | current_frame: usize, // 当前帧 57 | frames_in_flight: usize, // 当前传输的帧 58 | in_flight_fences: Vec<::Fence>, // 正在运行中的内存屏障 59 | render_finished_semaphores: Vec<::Semaphore>, // 渲染结束时发送的信号 60 | image_available_semaphores: Vec<::Semaphore>, // 可用信号 61 | command_buffers: Vec>, // 命令缓冲区域 62 | command_pool: ManuallyDrop>, // 命令池 63 | framebuffers: Vec<::Framebuffer>, // 帧缓冲 64 | image_views: Vec<(::ImageView)>, // 图像视图 65 | render_pass: ManuallyDrop<::RenderPass>, // 渲染过程 66 | render_area: Rect, // 渲染区域 67 | queue_group: QueueGroup, // 队列簇 68 | swapchain: ManuallyDrop<::Swapchain>, // 交换链 69 | device: ManuallyDrop, // 设备 70 | _adapter: Adapter, //用来抽象设备的转接器 71 | _surface: ::Surface, //你的屏幕 72 | _instance: ManuallyDrop, //示例 73 | } 74 | ``` 75 | 76 | 这个结构是我们用来管理硬件状态用的最基本的结构,我们会拆出几个章节来分别讲解怎么把这个结构创建出来 -------------------------------------------------------------------------------- /docs/画个三角形吧/1.2配置log.md: -------------------------------------------------------------------------------- 1 | # 配置log 2 | 3 | 因为本章节需要用到大量的调试信息,所以我们需要配置一个```log```来干两件事情 4 | 1. 接受底层API的log 5 | 2. 报出自己的log 6 | 在上一章已经提到了,只要是用的```vulkan```的后端的话是自动开启```validation layer```的 7 | 我们只需要将 8 | ```toml 9 | [dependencies] 10 | winit = "0.19.1" 11 | gfx-hal = "0.3.0" 12 | arrayvec = "0.4" 13 | log = "0.4.6" #新增 14 | simple_logger = "1.2" #新增 15 | ``` 16 | 加入到我们的```cargo.toml```里,然后呢在```main.rs```开启 17 | ```rust 18 | #[allow(unused_imports)] 19 | use log::{error, warn, info, debug, trace}; 20 | //... 21 | simple_logger::init().unwrap(); 22 | ``` 23 | 我们就可以看到成吨的log了 24 | 25 | 这个log组件非常的好用,他内置了几个宏 26 | ```error!```, ```warn!```, ```info!```, ```debug!``` , ```trace!``` 27 | 用法和```println!```一样的,这个会使得输出带着五颜六色的开头,能够使得你更好的读log 28 | 29 | 更主要的是这个库还可以让你在需要的时候去直接监听```stdout``` ```stderr```不过这篇教程主要侧重点不在这里有兴趣的可以去看看他的文档 -------------------------------------------------------------------------------- /docs/画个三角形吧/1.3物理逻辑设备.md: -------------------------------------------------------------------------------- 1 | # 物理设备和逻辑设备的创建 2 | 3 | ```rust 4 | #[cfg(feature = "dx12")] 5 | use gfx_backend_dx12 as back; 6 | #[cfg(feature = "vulkan")] 7 | use gfx_backend_vulkan as back; 8 | #[cfg(feature = "metal")] 9 | use gfx_backend_metal as back; 10 | #[cfg(not(any(feature = "vulkan", feature = "dx12",feature = "metal",)))] 11 | use gfx_backend_vulkan as back; 12 | ``` 13 | 这是我们已经有了的东西 14 | 选中了```vulkan```作为我们的后端,不过你要是喜欢```dx12```也可以 15 | 16 | ```rust 17 | // 创建一个实例 18 | let instance = back::Instance::create(title, 1); 19 | // 创建一个Surface(应该用绑定更好) 20 | let mut surface = instance.create_surface(window); 21 | // 使用一个adapter(转接器) 22 | let adapter = instance 23 | .enumerate_adapters() 24 | .into_iter() 25 | .find(|a| { 26 | a.queue_families 27 | .iter() 28 | .any(|qf| qf.supports_graphics() && surface.supports_queue_family(qf)) 29 | }) 30 | .ok_or("找不到任何可用的转接器")?; 31 | ``` 32 | 33 | `surface`指的就是你的屏幕 34 | `adapter`是我在`gfx-hal`看到的一个概念,用来抽象逻辑设备和队列簇的 35 | 有一个图可以帮助理解,虽然是我做`vulkan`教程用剩下的但是能够帮助理解实例,设备和队列簇的关系的 36 | ![队列簇](./1.1.2.png) 37 | 38 | ```rust 39 | // 使用一个设备和一个队列簇 40 | let (device, queue_group) = adapter 41 | .open_with::<_, Graphics>(1, |family| surface.supports_queue_family(family)) 42 | .unwrap(); 43 | ``` 44 | 这几行也很简单,首先使用adapter搜索有支持Graphics的显卡,如果这个显卡支持队列簇(是个民用显卡都会支持的)就返回这个显卡 45 | 46 | `devide`是一个逻辑设备,这里物理设备到逻辑设备的转换已经由`gfx-hal`写好了,我们不必操心这部分了(之前需要写一堆配置文件) 47 | 然后`queue group`就是我们的队列簇了 -------------------------------------------------------------------------------- /docs/画个三角形吧/1.4.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/画个三角形吧/1.4.1.png -------------------------------------------------------------------------------- /docs/画个三角形吧/1.4创建交换链.md: -------------------------------------------------------------------------------- 1 | # 创建个交换链出来 2 | 3 | 首先,各位有可能对交换链的概念没有理解或者理解的不是特别的透彻,简单说交换链就是一个公交车,公交车里的乘客就是一个帧,作用呢就是万一显卡没跟上,或者显卡太快了,让用户感觉画面没有什么实质性的变化的一个工具,交换链的发明使得用户体验上没有那么多的卡顿. 4 | 5 | ![交换链](./1.4.1.png) 6 | 7 | ```rust 8 | //swapchain 是我们的交换链 extent是交换链的大小 backbuffer 是一个缓冲 format是格式 frames_in_flight可以理解为正在传输中的帧吧 9 | let (swapchain, extent, backbuffer, format, frames_in_flight) = { 10 | let (caps, preferred_formats, present_modes) = 11 | surface.compatibility(&adapter.physical_device); 12 | info!("{:?}", caps); 13 | info!("首选格式: {:?}", preferred_formats); 14 | info!("当前模式: {:?}", present_modes); 15 | 16 | let present_mode = { 17 | use gfx_hal::window::PresentMode::*; 18 | [Mailbox, Fifo, Relaxed, Immediate] //这是几种GPU中常见的模式 Mailbox就是邮箱一样的,FIFO就是first in first out 感兴趣的可以百度 19 | .iter() 20 | .cloned() 21 | .find(|pm| present_modes.contains(pm)) 22 | .ok_or("没有设置任何当前模式")? 23 | }; 24 | 25 | let format = match preferred_formats { 26 | None => Format::Rgba8Srgb, 27 | Some(formats) => match formats 28 | .iter() 29 | .find(|format| format.base_format().1 == ChannelType::Srgb) 30 | .cloned() 31 | { 32 | Some(srgb_format) => srgb_format, 33 | None => formats.get(0).cloned().ok_or("没有任何首选格式")?, 34 | }, 35 | }; 36 | let extent = caps.extents.end().clone(); 37 | let image_count = if present_mode == PresentMode::Mailbox { 38 | (caps.image_count.end - 1).min(3) 39 | } else { 40 | (caps.image_count.end - 1).min(2) 41 | }; 42 | let image_usage = if caps.usage.contains(Usage::COLOR_ATTACHMENT) { //如果这个设备没有彩色支持的话,天啊!!! 43 | Usage::COLOR_ATTACHMENT 44 | } else { 45 | Err("天哪!都9086年了你居然还在用单色屏幕??!!")? 46 | }; 47 | let mut swapchain_config = 48 | SwapchainConfig::new(extent.width, extent.height, format, image_count); 49 | swapchain_config.present_mode = present_mode; 50 | swapchain_config.image_usage = image_usage; 51 | 52 | info!("{:?}", swapchain_config); 53 | // 创建交换链和一个(图像)缓冲区 54 | let (swapchain, backbuffer) = unsafe { 55 | device 56 | .create_swapchain(&mut surface, swapchain_config, None) 57 | .map_err(|_| "创建交换链失败")? 58 | }; 59 | 60 | (swapchain, extent, backbuffer, format, image_count as usize) 61 | }; 62 | ``` -------------------------------------------------------------------------------- /docs/画个三角形吧/1.5.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/docs/画个三角形吧/1.5.1.png -------------------------------------------------------------------------------- /docs/画个三角形吧/1.5设置同步.md: -------------------------------------------------------------------------------- 1 | # 设置同步 2 | 3 | 对于没学过操作系统的同学,可能不是特别清楚信号量和内存屏障大概是干什么用的 4 | ## 什么是信号量呢? 5 | 6 | 信号量是一个用来同步不同线程之间的操作的一个值,保持在0至指定最大值之间(这里我们用1就行,也就是说atom bool其实是我们的最好的选择) 7 | 当一个线程等待一个信号量的时候就-1,释放一次信号量的时候就+1,这里因为我们使用的一个bool所以就false true就行了. 8 | 信号量的计数值大于0(true),为signaled(有信号)状态;计数值等于0(false),为nonsignaled(Iphone没信号)状态. 9 | 给大家付个图片帮助大家理解 10 | ![信号量](./1.5.1.png) 11 | 12 | ## 那什么又是内存屏障呢? 13 | 14 | 其实这个东西的出现跟我们现在使用的垃圾CPU有关系 15 | 我们的垃圾CPU为了提升运行速度多半采取了乱序执行,而某些操作我们必须让他进行同步执行,设置内存屏障就保证了这个内存的访问是严格按照顺序执行的.要注意的一点是内存屏障的实现上在Windows系统上和类Unix系统上都是不太一样的,常见的垃圾x86会在linux下编译出这种傻逼的代码,给大家当笑话,缓解一下学习计算机图形学的枯燥 16 | 17 | 准备好了吗 18 | 19 | ```asm 20 | addl $0,0 (%esp) 21 | ``` 22 | 哈哈哈哈哈...你们觉得不好笑吗 23 | 24 | 25 | ```rust 26 | // image_available_semaphores 图片可用的信号量 render... 渲染结束的信号量 in... 正在传输的墙(内存屏障) 27 | // 这个实现虽然有点捞但是比较容易看懂 28 | let (image_available_semaphores, render_finished_semaphores, in_flight_fences) = { 29 | let mut image_available_semaphores: Vec<::Semaphore> = vec![]; 30 | let mut render_finished_semaphores: Vec<::Semaphore> = vec![]; 31 | let mut in_flight_fences: Vec<::Fence> = vec![]; 32 | for _ in 0..frames_in_flight { 33 | in_flight_fences 34 | .push(device.create_fence(true).map_err(|_| "创建内存屏障失败")?); 35 | image_available_semaphores 36 | .push(device.create_semaphore().map_err(|_| "创建信号量失败")?); 37 | render_finished_semaphores 38 | .push(device.create_semaphore().map_err(|_| "创建信号量失败")?); 39 | } 40 | ( 41 | image_available_semaphores, 42 | render_finished_semaphores, 43 | in_flight_fences, 44 | ) 45 | }; 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /docs/画个三角形吧/1.6创建renderpass.md: -------------------------------------------------------------------------------- 1 | # 写一个renderpass吧 2 | 3 | 为什么我不翻译了?因为`renderpass`的术语特别的模糊,容易引起歧义,就这么说吧,现在我有一堆盒子,我把这堆盒子进行上色叫一个`pass`,每个`pass`就是一个绘制调用(其实很多时候进行一次渲染经过很多次绘制调用因为单次渲染大批量效率并不是特别的高) 4 | 5 | 这里随着延迟渲染技术,多`pass`渲染其实现在的立足和地位都受到了某种程度的打击,但是因为概念好理解然后遍布了所有的教科书所以还是讲一下比较好. 6 | 7 | ```rust 8 | // 创建一个 RenderPass 9 | let render_pass = { 10 | let color_attachment = Attachment { 11 | format: Some(format), // 之前还记的创建swapchain的时候的那个format吗 12 | samples: 1, 13 | ops: AttachmentOps { 14 | load: AttachmentLoadOp::Clear, 15 | store: AttachmentStoreOp::Store, 16 | }, 17 | stencil_ops: AttachmentOps::DONT_CARE, 18 | layouts: Layout::Undefined..Layout::Present, 19 | }; 20 | // sub pass 就是每一个render pass里的实际对attachments进行读写操作的组件. 21 | // 效果越复杂subpass越多 22 | let subpass = SubpassDesc { 23 | colors: &[(0, Layout::ColorAttachmentOptimal)], 24 | depth_stencil: None, 25 | inputs: &[], 26 | resolves: &[], 27 | preserves: &[], 28 | }; 29 | unsafe { 30 | device 31 | .create_render_pass(&[color_attachment], &[subpass], &[]) 32 | .map_err(|_| "创建render pass 失败")? 33 | } 34 | }; 35 | ``` -------------------------------------------------------------------------------- /docs/画个三角形吧/1.7创建图片视图和帧缓冲.md: -------------------------------------------------------------------------------- 1 | # 图片视图和帧缓冲 2 | 3 | 什么是图像视图? 就是一个对象在内存中连续的装了一个或一堆的图像结束. 4 | 什么是帧缓冲? 跟上面一样,也是装了一堆帧的一块内存. 5 | 6 | ```rust 7 | // 创建图像视图 8 | let image_views: Vec<_> = backbuffer 9 | .into_iter() 10 | .map(|image| unsafe { 11 | device 12 | .create_image_view( 13 | &image, 14 | ViewKind::D2, 15 | format, 16 | Swizzle::NO, 17 | SubresourceRange { 18 | aspects: Aspects::COLOR, 19 | levels: 0..1, 20 | layers: 0..1, 21 | }, 22 | ) 23 | .map_err(|_| "为图像创建图像视图失败") 24 | }) 25 | .collect::, &str>>()?; 26 | 27 | // 创建帧缓冲 28 | let framebuffers: Vec<::Framebuffer> = { 29 | image_views 30 | .iter() 31 | .map(|image_view| unsafe { 32 | device 33 | .create_framebuffer( 34 | &render_pass, 35 | vec![image_view], 36 | Extent { 37 | width: extent.width as u32, 38 | height: extent.height as u32, 39 | depth: 1, 40 | }, 41 | ) 42 | .map_err(|_| "创建帧缓冲失败") 43 | }) 44 | .collect::, &str>>()? 45 | }; 46 | ``` -------------------------------------------------------------------------------- /docs/画个三角形吧/1.8创建命令池和命令缓冲.md: -------------------------------------------------------------------------------- 1 | # 创建命令池和命令缓冲 2 | 3 | 在`Vulkan`图形的定义上命令池是一个不透明的对象,分摊了很多命令缓冲区的创建资源成本,但是却是**不同步**的,所以千万不能跨线程使用. 4 | 5 | 命令缓冲区是一个用来记录命令的对象,这些命令就像email一样显卡肯定一下子看不完所以用缓冲区暂时的储存一下随后给显卡看. 6 | 7 | `Vulkan`有两个缓冲区一个是主要的缓冲区直接提交到队列,一个是辅助缓冲区,提交到主缓冲区. 8 | 9 | ```rust 10 | // 创建命令池 11 | let mut command_pool = unsafe { 12 | device 13 | .create_command_pool_typed(&queue_group, CommandPoolCreateFlags::RESET_INDIVIDUAL) 14 | .map_err(|_| "创建命令池失败,错误可能来自底层接口")? 15 | }; 16 | 17 | // 创建命令缓冲 18 | let command_buffers: Vec<_> = framebuffers 19 | .iter() 20 | .map(|_| command_pool.acquire_command_buffer()) 21 | .collect(); 22 | ``` 23 | 24 | 写到这里,一个`HardwareState`差不多就完工了 25 | 但是先别着急,看完下面这一章再开始抄代码. -------------------------------------------------------------------------------- /docs/画个三角形吧/1.9写一个drop.md: -------------------------------------------------------------------------------- 1 | # 实现drop 2 | 3 | 其实这个完全是因为现在的`gfx-hal`写的差劲的原因,人家`Rust`的内存管理理论上都不需要用户来管的... 4 | 5 | ```rust 6 | impl core::ops::Drop for HardwareState { 7 | // 我发现了个bug在这个垃圾gfx-hal里面swapchain更换的特别频繁的时候,Rust自动drop跟不上,所以这里用的手动drop 8 | // 还有可爱的GFX开发组哟,能不能把修一修,这个unsafe看的我浑身难受 9 | fn drop(&mut self) { 10 | let _ = self.device.wait_idle(); 11 | unsafe { 12 | for fence in self.in_flight_fences.drain(..) { 13 | self.device.destroy_fence(fence) 14 | } 15 | for semaphore in self.render_finished_semaphores.drain(..) { 16 | self.device.destroy_semaphore(semaphore) 17 | } 18 | for semaphore in self.image_available_semaphores.drain(..) { 19 | self.device.destroy_semaphore(semaphore) 20 | } 21 | for framebuffer in self.framebuffers.drain(..) { 22 | self.device.destroy_framebuffer(framebuffer); 23 | } 24 | for image_view in self.image_views.drain(..) { 25 | self.device.destroy_image_view(image_view); 26 | } 27 | use core::ptr::read; 28 | self.device.destroy_command_pool( 29 | ManuallyDrop::into_inner(read(&self.command_pool)).into_raw(), 30 | ); 31 | self.device 32 | .destroy_render_pass(ManuallyDrop::into_inner(read(&self.render_pass))); 33 | self.device 34 | .destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain))); 35 | ManuallyDrop::drop(&mut self.device); 36 | ManuallyDrop::drop(&mut self._instance); 37 | } 38 | } 39 | } 40 | ``` -------------------------------------------------------------------------------- /docs/画个三角形吧/README.md: -------------------------------------------------------------------------------- 1 | # 1.画个三角形吧 2 | 3 | 在这一章主要让大家迈出学习计算机图形学框架的第一步 4 | 也就是画个三角形出来.... 5 | 6 | 别小看这个三角形🔺,等你真正画出来就知道多 蛋碎 困难了,很多计算机图形爱好者都是被这个劝退的. -------------------------------------------------------------------------------- /tools.md: -------------------------------------------------------------------------------- 1 | # 工具安装 2 | 3 | ## Linux用户 4 | * 下载`Vulkan SDK` [⭐点击这里](https://vulkan.lunarg.com/sdk/home) 5 | * 下载`Rust` [🚀点击这里](https://www.rust-lang.org/zh-CN/tools/install) 6 | * 下载一个IDE 推荐`Clion`或`VSCode` 7 | * 如果使用了`VSCode` 请安装 `Rust (rls)` 和 `Better TOML` 8 | 9 | ## Windows用户? 想用DX12? 10 | * 安装visual studio 2019 [🔥点击这里](https://visualstudio.microsoft.com/zh-hans/) 11 | * 点选所有跟`C++`有关的东西,打开游戏开发那里安装DX调试器 12 | * 然后安装`Rust` [🚀点击这里](https://www.rust-lang.org/zh-CN/tools/install) 13 | * 用`Vulkan`同`Linux` 14 | 15 | ## MAC用户? 请给我捐一台旧MAC设备 16 | 我真搞不懂你们怎么下载metal,也真不懂你们平时用什么 -------------------------------------------------------------------------------- /画个三角形吧/1.0.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/画个三角形吧/1.0.1.png -------------------------------------------------------------------------------- /画个三角形吧/1.0创建窗口.md: -------------------------------------------------------------------------------- 1 | # 开始冒险的第一步,整个窗口出来 2 | 3 | ## 初始化一个项目 4 | 首先我们使用 ```cargo init try``` 5 | 创建一个基础的Rust项目 6 | 7 | ``` 8 | try 9 | src 10 | cargo.toml 11 | .git 12 | ``` 13 | 大概是这个样子的 14 | 15 | 我们需要安装一些库. Rust是一个非常好的工具因为他提供了Cargo这么好用的减少心智负担的工具 16 | 我们打开 cargo.toml 17 | ```toml 18 | [package] 19 | #这个一般都给你写好了,不用操心 20 | name = "try" 21 | #项目名称 22 | version = "0.1.0" 23 | #项目版本 24 | authors = ["lemonhx "] 25 | #作者们 26 | edition = "2018" 27 | 28 | [dependencies] 29 | #依赖的库 30 | winit = "0.19" 31 | #我们核心的GFX硬件抽象库 32 | gfx-hal = "0.3.0" 33 | #一个玩过rust就知道的库 34 | arrayvec = "0.4" 35 | 36 | #vulkan作为gfx后端的配置 37 | [dependencies.gfx-backend-vulkan] 38 | version = "0.3" 39 | features = ["winit"] 40 | optional = true 41 | 42 | #metal作为gfx后端的配置 43 | [target.'cfg(target_os = "macos")'.dependencies.gfx-backend-metal] 44 | version = "0.3" 45 | features = ["winit"] 46 | optional = true 47 | 48 | #DX12作为gfx后端的配置 49 | [target.'cfg(windows)'.dependencies.gfx-backend-dx12] 50 | version = "0.3.0" 51 | features = ["winit"] 52 | optional = true 53 | 54 | #编译配置 55 | [build-dependencies] 56 | glsl-to-spirv = "0.1.7" 57 | 58 | #启用扩展,我们默认使用跨平台的Vulkan 59 | [features] 60 | default = ["gfx-backend-vulkan"] 61 | metal = ["gfx-backend-metal"] 62 | dx12 = ["gfx-backend-dx12"] 63 | vulkan = ["gfx-backend-vulkan"] 64 | ``` 65 | 抄好之后使用```cargo run```下载依赖并运行,不出问题的话就会有hello world窗口弹出 66 | ## 窗口结构 67 | 我们需要定义以下窗口结构体 68 | ```rust 69 | pub struct WindowState { 70 | pub evloop: EventsLoop, 71 | pub window: Window, 72 | } 73 | ``` 74 | 这是我定义的一个窗口结构体,里面装有一个事件循环和一个窗口 75 | 76 | 事件循环是什么可能很多人不知道我可以大概解释一下: 77 | 78 | 我自己在跑步(循环),跑着跑着被绊倒了(遇到事件),我爬起来(事件的处理方式),我继续跑(重新回到循环) 79 | 80 | 这个是一个简单的事件循环,同样,我还可以 81 | 82 | 跑步(循环),被车撞了(遇到事件),躺地上求救(事件处理),没人搭理死了(处理结果) 83 | 84 | 很简单明了,创建窗口需要绑定一个事件循环的原因是,你可能对着这个窗口按键,点击鼠标,拖拽,窗口会用你之前定义好的事件处理方式去处理这些事件. 85 | 86 | 然后如何初始化一个对应这个窗口的实例呢? 87 | ```rust 88 | impl WindowState { 89 | //为了避免使代码过度复杂,这里就直接不行报错了 90 | pub fn new>(title: T, size: (f64, f64)) -> Self{ 91 | let evloop = EventsLoop::new(); //创建事件循环 92 | let window = match WindowBuilder::new() //使用窗口创造器 93 | .with_title(title) //绑上标题 94 | .with_dimensions(LogicalSize::new(size.0, size.1)) //设置大小 95 | .build(&evloop) //尝试构造 这里的返回类型是一个Result所以我们再match一手 96 | { 97 | Ok(x) => x, 98 | Err(e) => {println!("创建窗口失败,原因:{:?}", e);panic!()} 99 | }; 100 | Self { 101 | evloop, 102 | window, 103 | } 104 | } 105 | } 106 | ``` 107 | 108 | ## 事件处理() 109 | 一个窗口对应了一个事件循环,有些时候事件循环就是一个```while```循环,但是这里我们的```winit```库给我们提供了一个基于control flow的一个事件循环,总体来讲就是我们不需要把事件的处理方法挤在一个```while```里面来写了,在这里我定义了一个事件处理函数 110 | 111 | ```rust 112 | use winit::{ControlFlow, Event, VirtualKeyCode, WindowEvent};// 使用 控制流 事件 按键代码 窗口事件 113 | // 这个函数收到一个事件,返回一个控制流(之后干什么) 114 | pub fn event_handler(event: Event) -> winit::ControlFlow { 115 | match event { 116 | // 处理键盘输入事件 117 | Event::WindowEvent { event, .. } => match event { 118 | WindowEvent::KeyboardInput { input, .. } => { 119 | if let Some(VirtualKeyCode::Escape) = input.virtual_keycode {//如果是ESC被按下来了 120 | ControlFlow::Break //退出事件循环 121 | } else { 122 | ControlFlow::Continue //要不啥也不干 123 | } 124 | } 125 | WindowEvent::CloseRequested => ControlFlow::Break, //如果窗口被关了,退出程序 126 | 127 | _ => ControlFlow::Continue, 128 | }, 129 | _ => ControlFlow::Continue, 130 | } 131 | } 132 | ``` 133 | ## 把上面的拼装到一起 134 | 最后开始写```app.rs``` 135 | 我们的app暂时仅仅只有一个窗口,所以我们创建一个结构体包含一个窗口实例就行了 136 | ```rust 137 | use super::super::window::window::*; 138 | use super::super::event_handler::event_handler::*; 139 | pub struct App { 140 | windowstate : WindowState 141 | } 142 | impl App { 143 | pub fn new() { 144 | let mut ws = WindowState::new("new_window", (1024f64, 768f64)); //设置窗口标题和大小 145 | ws.evloop.run_forever(event_handler); //开始运行事件循环 146 | } 147 | } 148 | ``` 149 | 150 | ## yes! 开始运行 151 | 最后,我们更改```main.rs``` 152 | ```rust 153 | pub mod app; 154 | pub mod window; 155 | pub mod event_handler; 156 | 157 | use app::app::*; 158 | 159 | fn main() { 160 | let _ = App::new(); 161 | } 162 | 163 | ``` 164 | 最后```cargo run```会有一个窗口弹出 165 | 166 | 我的大概是这个效果 167 | ![来自ubuntu + dde](./1.0.1.png) 168 | 169 | 通常情况下运行会得到一个白色或者黑色的窗口,甚至在某些情况下窗口里会出现你正在打开的程序的一部分的图像 170 | 这都是正常的现象 -------------------------------------------------------------------------------- /画个三角形吧/1.1.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/画个三角形吧/1.1.2.png -------------------------------------------------------------------------------- /画个三角形吧/1.10处理输入事件.md: -------------------------------------------------------------------------------- 1 | # 1.1.9 处理输入事件 2 | 3 | 这一章,我们讲一下如何处理输入事件. 4 | 5 | 我们可以把一个方形当成屏幕,鼠标就是方形上的一个点的位置. 6 | 那么我们就可以通过x轴和y轴的距离判断鼠标的位置. 7 | 8 | 所以我们开写,在`event_handler`目录下创建然后添加`local_state.rs` 9 | ```rust 10 | #[derive(Debug, Clone, Copy, Default)] 11 | pub struct LocalState { 12 | pub frame_width: f64, // 窗口宽 13 | pub frame_height: f64, // 窗口高 14 | pub mouse_x: f64, // 鼠标的x轴距离 15 | pub mouse_y: f64, // 鼠标的y轴距离 16 | } 17 | ``` 18 | 然后根据这个,我们写一个更新`State`的函数 19 | ```rust 20 | impl LocalState { 21 | pub fn update_from_input(&mut self, input: UserInput/*这个一会儿再讲*/) { 22 | if let Some(frame_size) = input.new_frame_size { 23 | //if let 其实就是个match的语法糖 24 | self.frame_width = frame_size.0; 25 | self.frame_height = frame_size.1; 26 | } 27 | if let Some(position) = input.new_mouse_position { 28 | self.mouse_x = position.0; 29 | self.mouse_y = position.1; 30 | } 31 | } 32 | } 33 | ``` 34 | 好的,我们已经写好了这个`State`那么如何获取到屏幕的输入呢? 35 | 36 | 我们需要去里用`winit`的`event_loop`去获取 37 | 所以我们在`event_handler`下新建一个`user_input.rs` 38 | ```rust 39 | pub struct UserInput { 40 | pub end_requested: bool, // 窗口被关了 41 | pub new_frame_size: Option<(f64, f64)>, // 更新的屏幕宽度 42 | pub new_mouse_position: Option<(f64, f64)>, // 更新的鼠标位置 43 | } 44 | ``` 45 | 然后用`event_loop`去获取这些参数 46 | ```rust 47 | impl UserInput { 48 | pub fn poll_events_loop(events_loop: &mut EventsLoop) -> Self { 49 | let mut output = UserInput::default(); 50 | events_loop.poll_events(|event| match event { 51 | Event::WindowEvent { 52 | event: WindowEvent::CloseRequested, 53 | // 如果pull下来窗口关闭行为 54 | .. 55 | } => output.end_requested = true, 56 | Event::WindowEvent { 57 | event: WindowEvent::Resized(logical), 58 | // 如果发现窗口大小改变了 59 | .. 60 | } => { 61 | output.new_frame_size = Some((logical.width, logical.height)); 62 | } 63 | Event::WindowEvent { 64 | event: WindowEvent::CursorMoved { position, .. }, 65 | // 发现鼠标的位置移动了 66 | .. 67 | } => { 68 | output.new_mouse_position = Some((position.x, position.y)); 69 | } 70 | _ => (), 71 | }); 72 | output 73 | } 74 | } 75 | ``` 76 | 好的,这样我们就成功的获取到了鼠标的位置,窗口的大小 -------------------------------------------------------------------------------- /画个三角形吧/1.11.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/画个三角形吧/1.11.1.png -------------------------------------------------------------------------------- /画个三角形吧/1.11写一个clearcolor.md: -------------------------------------------------------------------------------- 1 | # 1.1.10 高端清屏 2 | 3 | 我们既然已经获得了鼠标的位置, 4 | 那么我们可以尝试利用鼠标的位置去给屏幕换上不同的颜色 5 | 6 | ```rust 7 | // 根据鼠标的位置将屏幕染成指定的颜色 8 | pub fn clear_color_by_mouse( 9 | s: &mut HardwareState, 10 | color: [f32; 4], 11 | ) -> Result, &'static str> { 12 | // 设置信号量 13 | let image_available = &s.image_available_semaphores[s.current_frame]; 14 | let render_finished = &s.render_finished_semaphores[s.current_frame]; 15 | // 获取当前帧数 16 | s.current_frame = (s.current_frame + 1) % s.frames_in_flight; 17 | 18 | let (i_u32, i_usize) = unsafe { 19 | let (image_index, _) = s 20 | .swapchain 21 | //请求从swapchain里取出一个图片 22 | .acquire_image( 23 | core::u64::MAX, 24 | Some(image_available), 25 | Some(&s.in_flight_fences[s.current_frame]), 26 | ) 27 | .map_err(|_| "错误,不能从交换链中取出图片!")?; 28 | (image_index, image_index as usize) 29 | }; 30 | 31 | let flight_fence = &s.in_flight_fences[i_usize]; 32 | unsafe { 33 | s.device 34 | .wait_for_fence(flight_fence, core::u64::MAX) 35 | .map_err(|_| "错误,等待内存屏障失败!")?; 36 | s.device 37 | .reset_fence(flight_fence) 38 | .map_err(|_| "错误,重置内存屏障失败!")?; 39 | } 40 | 41 | unsafe { 42 | // 获取一个command buffer 43 | let buffer = &mut s.command_buffers[i_usize]; 44 | // 拿到Clear color 的值 45 | let clear_values = [ClearValue::Color(ClearColor::from(color))]; 46 | buffer.begin(false); 47 | buffer.begin_render_pass_inline( 48 | &s.render_pass, 49 | &s.framebuffers[i_usize], 50 | s.render_area, 51 | clear_values.iter(), 52 | ); 53 | buffer.finish(); 54 | } 55 | 56 | // 拿到command buffer 57 | let command_buffers = &s.command_buffers[i_usize..=i_usize]; 58 | // 等信号量各就各位 59 | let wait_semaphores: ArrayVec<[_; 1]> = 60 | [(image_available, PipelineStage::COLOR_ATTACHMENT_OUTPUT)].into(); 61 | let signal_semaphores: ArrayVec<[_; 1]> = [render_finished].into(); 62 | let present_wait_semaphores: ArrayVec<[_; 1]> = [render_finished].into(); 63 | // 开推 64 | let submission = Submission { 65 | command_buffers, 66 | wait_semaphores, 67 | signal_semaphores, 68 | }; 69 | 70 | // 扔到控制队列 71 | let the_command_queue = &mut s.queue_group.queues[0]; 72 | // tada! 完成了 73 | unsafe { 74 | the_command_queue.submit(submission, Some(flight_fence)); 75 | s.swapchain 76 | .present(the_command_queue, i_u32, present_wait_semaphores) 77 | .map_err(|_| "错误,添加到区块链失败!") 78 | } 79 | } 80 | 81 | ``` 82 | 然后我们在`app.rs`中去写一个根据鼠标位置变换rgb的函数 83 | ```rust 84 | fn change_rgb_by_state(hal_state: &mut HardwareState, local_state: &LocalState) { 85 | let b = (local_state.mouse_x / local_state.frame_width) as f32; 86 | let g = (local_state.mouse_y / local_state.frame_height) as f32; 87 | let r = (b + g) * 0.3; 88 | let a = 1.0; 89 | let _ = clear_color_by_mouse(hal_state, [r, g, b, a]); 90 | } 91 | ``` 92 | 最后我们再重写下`App` 93 | ```rust 94 | impl App { 95 | pub fn new() { 96 | let mut ws = WindowState::new(title, (1024f64, 768f64)); 97 | let mut hs = match HardwareState::new(&ws.window, title) { 98 | Ok(x) => x, 99 | Err(x) => panic!(), 100 | }; 101 | let (frame_width, frame_height) = ws 102 | .window 103 | .get_inner_size() 104 | .map(|logical| logical.into()) 105 | .unwrap_or((1.0, 1.0)); 106 | let mut ls = LocalState { 107 | frame_width, 108 | frame_height, 109 | mouse_x: 0.0, 110 | mouse_y: 0.0, 111 | }; 112 | let mut running = true; 113 | while running { 114 | let inputs = UserInput::poll_events_loop(&mut ws.evloop); 115 | if inputs.end_requested { 116 | running = false; 117 | } 118 | if inputs.new_frame_size.is_some() { 119 | drop(hs); 120 | hs = match HardwareState::new(&ws.window, title) { 121 | Ok(state) => state, 122 | Err(e) => panic!(e), 123 | }; 124 | } 125 | ls.update_from_input(inputs); 126 | change_rgb_by_state(&mut hs, &ls); 127 | } 128 | } 129 | } 130 | 131 | ``` 132 | 现在我们去运行一下,会发现屏幕的颜色跟随鼠标的位置而改变 133 | ![来自windows](./1.11.1.png) -------------------------------------------------------------------------------- /画个三角形吧/1.12渲染过程.md: -------------------------------------------------------------------------------- 1 | # 1.1.11 渲染过程 2 | 3 | -------------------------------------------------------------------------------- /画个三角形吧/1.13把图形管线写完.md: -------------------------------------------------------------------------------- 1 | # 1.1.12 把图形管线写完 2 | 3 | -------------------------------------------------------------------------------- /画个三角形吧/1.14帧缓冲.md: -------------------------------------------------------------------------------- 1 | # 1.1.13 帧缓冲 2 | 3 | -------------------------------------------------------------------------------- /画个三角形吧/1.15命令池.md: -------------------------------------------------------------------------------- 1 | # 1.1.14 命令池 2 | 3 | -------------------------------------------------------------------------------- /画个三角形吧/1.16劳资要的三角形.md: -------------------------------------------------------------------------------- 1 | # 1.2 劳资要的三角形 2 | 3 | -------------------------------------------------------------------------------- /画个三角形吧/1.17小结.md: -------------------------------------------------------------------------------- 1 | # 1.3 小结 2 | 3 | -------------------------------------------------------------------------------- /画个三角形吧/1.1创建实例.md: -------------------------------------------------------------------------------- 1 | # 先弄几个方便使用的结构 2 | 3 | ## 帧的概念 4 | 什么是一帧? 其实我们说的帧就是一幅静止的画面,无数个静止的画面连起来播放,通过我们的脑补帧来让我们看到连贯的动画,fps就是我们常用的衡量多少帧每秒的单位,一般情况下29fps是电视台用的帧率,60fps是一般的游戏的帧率(在我们玩游戏的时候神经高度集中,29会让我们觉得晕),144fps就是一些竞技类的游戏想要达到的帧率. 5 | 6 | 帧又是怎么诞生的呢? 一般情况下就是在电脑里渲染出3D场景然后利用投射投到2D的屏幕上,也就是fps就是一秒钟我们能渲染几次这个图像,```gfx-hal```使用的都是常见的光栅化的渲染(虽然正在准备加入```Nvidia Ray Tracing```支持),所谓的光栅化其实也是非常的简单,我们在电脑里获取了每一个物体的顶点的位置和顶点的连接之后,利用着色器(shader)来给顶点上色,然后在给顶点围起来的区域上色. 7 | 8 | 这里在补充一下,光线追踪指的是利用每一束计算机模拟的光打在物体上模拟出来的光路来上色的,上色的细节会比光栅化好的很多(因为一般涉及到那种带有反射啊,金属材质什么的光栅化需要很多的骚技巧来模拟) 9 | 10 | 11 | 看到这里你们大概有个概念了吧 下面是伪码 12 | ```rust 13 | Vec //一堆帧 14 | 15 | //渲染主程序 16 | loop{ 17 | //... 18 | render(hardware,data); 19 | } 20 | ``` 21 | 这里再补充一下,很多时候你们会听到帧缓冲这个概念,什么是帧缓冲的? 22 | 其实说白了就是提前渲染好的一堆帧用来当GPU忙的喘不过气来的时候来顶替一下,混淆一下你的大脑,因为毕竟每一帧的东西都不太一样,难免有些地方会慢一两秒.所以帧缓冲就是来改善体验,让用户觉得更流畅的. 23 | 24 | ## 如何控制上面的循环呢 25 | 在第一课里,你已经掌握了事件循环和窗口的创建,但是实际上这距离真正的图形学还有点远,我们还需要处理一些非常底层的,硬件的,和GPU沟通的硬件代码,虽然```gfx-hal```已经给我们提供了一个很好的核心,但在核心之上还需要写一些硬件代码,为了符合Rust的美学,我们需要用什么结构来包装起来 26 | > 吐槽啊,hkt什么时候出来,现在写的这一堆monad类似的东西用一个hkt能简化多少工作流程 27 | 所以这里要引入 28 | 29 | ```HardwareState```用来控制```GPU```的状态 和 ```LocalState```控制用户的输入输出之类的状态,来更好的用高级抽象控制GPU的执行,控制非纯操作的作用范围 30 | 31 | 上面的那个```render```函数就是用来渲染的 函数定义伪码 32 | ```rust 33 | pub fn render(hal:&mut HardwareState,data:&LocalState){ 34 | hal.draw(data.color) 35 | } 36 | ``` 37 | 38 | 这段是写给真正从事过这方面开发的人看的,有些时候render会出错,出错了要debug,那么这个```gfx```是怎么debug的呢?其实,```gfx```是一个胶水库,粘合了所有的底层API,所以如果你使用的是```DX12```后端那么你可以用你最喜欢的```Visual Studio```来调试可执行程序,如果你使用的是```Vulkan```后端那么你可以使用```LunarG Vulkan SDK``` 提供给你的一系列程序来调试 同时 ```gfx```会自动的报出```debug layer```的数据,```Metal``` 我没使用过,不清楚,如果你想让我写关于这方面的可以考虑资助我一台```Macbook```,你喜欢```OpenGL```??!!??!! 对不起,我们不认识,你可选择右上角```⌧```再见 39 | 40 | ## 设置后端 41 | ```rust 42 | #[cfg(feature = "dx12")] 43 | use gfx_backend_dx12 as back; 44 | #[cfg(feature = "vulkan")] 45 | use gfx_backend_vulkan as back; 46 | #[cfg(feature = "metal")] 47 | use gfx_backend_metal as back; 48 | #[cfg(not(any(feature = "vulkan", feature = "dx12",feature = "metal",)))] 49 | use gfx_backend_vulkan as back; 50 | ``` 51 | 这几行代码会先查看你的```cargo.toml```来确定你使用的是哪一个后端,然后再使用,和```C语言```里面的```#ifdef```差不多 52 | 然后在编译的时候需要指定 ```cargo run --features vulkan```之类的来确定到底用了那个后端 53 | ## 开始设计```HardwareState``` 54 | ```rust 55 | pub struct HardwareState { 56 | current_frame: usize, // 当前帧 57 | frames_in_flight: usize, // 当前传输的帧 58 | in_flight_fences: Vec<::Fence>, // 正在运行中的内存屏障 59 | render_finished_semaphores: Vec<::Semaphore>, // 渲染结束时发送的信号 60 | image_available_semaphores: Vec<::Semaphore>, // 可用信号 61 | command_buffers: Vec>, // 命令缓冲区域 62 | command_pool: ManuallyDrop>, // 命令池 63 | framebuffers: Vec<::Framebuffer>, // 帧缓冲 64 | image_views: Vec<(::ImageView)>, // 图像视图 65 | render_pass: ManuallyDrop<::RenderPass>, // 渲染过程 66 | render_area: Rect, // 渲染区域 67 | queue_group: QueueGroup, // 队列簇 68 | swapchain: ManuallyDrop<::Swapchain>, // 交换链 69 | device: ManuallyDrop, // 设备 70 | _adapter: Adapter, //用来抽象设备的转接器 71 | _surface: ::Surface, //你的屏幕 72 | _instance: ManuallyDrop, //示例 73 | } 74 | ``` 75 | 76 | 这个结构是我们用来管理硬件状态用的最基本的结构,我们会拆出几个章节来分别讲解怎么把这个结构创建出来 -------------------------------------------------------------------------------- /画个三角形吧/1.2配置log.md: -------------------------------------------------------------------------------- 1 | # 配置log 2 | 3 | 因为本章节需要用到大量的调试信息,所以我们需要配置一个```log```来干两件事情 4 | 1. 接受底层API的log 5 | 2. 报出自己的log 6 | 在上一章已经提到了,只要是用的```vulkan```的后端的话是自动开启```validation layer```的 7 | 我们只需要将 8 | ```toml 9 | [dependencies] 10 | winit = "0.19.1" 11 | gfx-hal = "0.3.0" 12 | arrayvec = "0.4" 13 | log = "0.4.6" #新增 14 | simple_logger = "1.2" #新增 15 | ``` 16 | 加入到我们的```cargo.toml```里,然后呢在```main.rs```开启 17 | ```rust 18 | #[allow(unused_imports)] 19 | use log::{error, warn, info, debug, trace}; 20 | //... 21 | simple_logger::init().unwrap(); 22 | ``` 23 | 我们就可以看到成吨的log了 24 | 25 | 这个log组件非常的好用,他内置了几个宏 26 | ```error!```, ```warn!```, ```info!```, ```debug!``` , ```trace!``` 27 | 用法和```println!```一样的,这个会使得输出带着五颜六色的开头,能够使得你更好的读log 28 | 29 | 更主要的是这个库还可以让你在需要的时候去直接监听```stdout``` ```stderr```不过这篇教程主要侧重点不在这里有兴趣的可以去看看他的文档 -------------------------------------------------------------------------------- /画个三角形吧/1.3物理逻辑设备.md: -------------------------------------------------------------------------------- 1 | # 物理设备和逻辑设备的创建 2 | 3 | ```rust 4 | #[cfg(feature = "dx12")] 5 | use gfx_backend_dx12 as back; 6 | #[cfg(feature = "vulkan")] 7 | use gfx_backend_vulkan as back; 8 | #[cfg(feature = "metal")] 9 | use gfx_backend_metal as back; 10 | #[cfg(not(any(feature = "vulkan", feature = "dx12",feature = "metal",)))] 11 | use gfx_backend_vulkan as back; 12 | ``` 13 | 这是我们已经有了的东西 14 | 选中了```vulkan```作为我们的后端,不过你要是喜欢```dx12```也可以 15 | 16 | ```rust 17 | // 创建一个实例 18 | let instance = back::Instance::create(title, 1); 19 | // 创建一个Surface(应该用绑定更好) 20 | let mut surface = instance.create_surface(window); 21 | // 使用一个adapter(转接器) 22 | let adapter = instance 23 | .enumerate_adapters() 24 | .into_iter() 25 | .find(|a| { 26 | a.queue_families 27 | .iter() 28 | .any(|qf| qf.supports_graphics() && surface.supports_queue_family(qf)) 29 | }) 30 | .ok_or("找不到任何可用的转接器")?; 31 | ``` 32 | 33 | `surface`指的就是你的屏幕 34 | `adapter`是我在`gfx-hal`看到的一个概念,用来抽象逻辑设备和队列簇的 35 | 有一个图可以帮助理解,虽然是我做`vulkan`教程用剩下的但是能够帮助理解实例,设备和队列簇的关系的 36 | ![队列簇](./1.1.2.png) 37 | 38 | ```rust 39 | // 使用一个设备和一个队列簇 40 | let (device, queue_group) = adapter 41 | .open_with::<_, Graphics>(1, |family| surface.supports_queue_family(family)) 42 | .unwrap(); 43 | ``` 44 | 这几行也很简单,首先使用adapter搜索有支持Graphics的显卡,如果这个显卡支持队列簇(是个民用显卡都会支持的)就返回这个显卡 45 | 46 | `devide`是一个逻辑设备,这里物理设备到逻辑设备的转换已经由`gfx-hal`写好了,我们不必操心这部分了(之前需要写一堆配置文件) 47 | 然后`queue group`就是我们的队列簇了 -------------------------------------------------------------------------------- /画个三角形吧/1.4.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/画个三角形吧/1.4.1.png -------------------------------------------------------------------------------- /画个三角形吧/1.4创建交换链.md: -------------------------------------------------------------------------------- 1 | # 创建个交换链出来 2 | 3 | 首先,各位有可能对交换链的概念没有理解或者理解的不是特别的透彻,简单说交换链就是一个公交车,公交车里的乘客就是一个帧,作用呢就是万一显卡没跟上,或者显卡太快了,让用户感觉画面没有什么实质性的变化的一个工具,交换链的发明使得用户体验上没有那么多的卡顿. 4 | 5 | ![交换链](./1.4.1.png) 6 | 7 | ```rust 8 | //swapchain 是我们的交换链 extent是交换链的大小 backbuffer 是一个缓冲 format是格式 frames_in_flight可以理解为正在传输中的帧吧 9 | let (swapchain, extent, backbuffer, format, frames_in_flight) = { 10 | let (caps, preferred_formats, present_modes) = 11 | surface.compatibility(&adapter.physical_device); 12 | info!("{:?}", caps); 13 | info!("首选格式: {:?}", preferred_formats); 14 | info!("当前模式: {:?}", present_modes); 15 | 16 | let present_mode = { 17 | use gfx_hal::window::PresentMode::*; 18 | [Mailbox, Fifo, Relaxed, Immediate] //这是几种GPU中常见的模式 Mailbox就是邮箱一样的,FIFO就是first in first out 感兴趣的可以百度 19 | .iter() 20 | .cloned() 21 | .find(|pm| present_modes.contains(pm)) 22 | .ok_or("没有设置任何当前模式")? 23 | }; 24 | 25 | let format = match preferred_formats { 26 | None => Format::Rgba8Srgb, 27 | Some(formats) => match formats 28 | .iter() 29 | .find(|format| format.base_format().1 == ChannelType::Srgb) 30 | .cloned() 31 | { 32 | Some(srgb_format) => srgb_format, 33 | None => formats.get(0).cloned().ok_or("没有任何首选格式")?, 34 | }, 35 | }; 36 | let extent = caps.extents.end().clone(); 37 | let image_count = if present_mode == PresentMode::Mailbox { 38 | (caps.image_count.end - 1).min(3) 39 | } else { 40 | (caps.image_count.end - 1).min(2) 41 | }; 42 | let image_usage = if caps.usage.contains(Usage::COLOR_ATTACHMENT) { //如果这个设备没有彩色支持的话,天啊!!! 43 | Usage::COLOR_ATTACHMENT 44 | } else { 45 | Err("天哪!都9086年了你居然还在用单色屏幕??!!")? 46 | }; 47 | let mut swapchain_config = 48 | SwapchainConfig::new(extent.width, extent.height, format, image_count); 49 | swapchain_config.present_mode = present_mode; 50 | swapchain_config.image_usage = image_usage; 51 | 52 | info!("{:?}", swapchain_config); 53 | // 创建交换链和一个(图像)缓冲区 54 | let (swapchain, backbuffer) = unsafe { 55 | device 56 | .create_swapchain(&mut surface, swapchain_config, None) 57 | .map_err(|_| "创建交换链失败")? 58 | }; 59 | 60 | (swapchain, extent, backbuffer, format, image_count as usize) 61 | }; 62 | ``` -------------------------------------------------------------------------------- /画个三角形吧/1.5.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustcc/When-Rust-Meets-Computer-Graphics/805243ac1e82e4e96723c3e009489bef8a7980f3/画个三角形吧/1.5.1.png -------------------------------------------------------------------------------- /画个三角形吧/1.5设置同步.md: -------------------------------------------------------------------------------- 1 | # 设置同步 2 | 3 | 对于没学过操作系统的同学,可能不是特别清楚信号量和内存屏障大概是干什么用的 4 | ## 什么是信号量呢? 5 | 6 | 信号量是一个用来同步不同线程之间的操作的一个值,保持在0至指定最大值之间(这里我们用1就行,也就是说atom bool其实是我们的最好的选择) 7 | 当一个线程等待一个信号量的时候就-1,释放一次信号量的时候就+1,这里因为我们使用的一个bool所以就false true就行了. 8 | 信号量的计数值大于0(true),为signaled(有信号)状态;计数值等于0(false),为nonsignaled(Iphone没信号)状态. 9 | 给大家付个图片帮助大家理解 10 | ![信号量](./1.5.1.png) 11 | 12 | ## 那什么又是内存屏障呢? 13 | 14 | 其实这个东西的出现跟我们现在使用的垃圾CPU有关系 15 | 我们的垃圾CPU为了提升运行速度多半采取了乱序执行,而某些操作我们必须让他进行同步执行,设置内存屏障就保证了这个内存的访问是严格按照顺序执行的.要注意的一点是内存屏障的实现上在Windows系统上和类Unix系统上都是不太一样的,常见的垃圾x86会在linux下编译出这种傻逼的代码,给大家当笑话,缓解一下学习计算机图形学的枯燥 16 | 17 | 准备好了吗 18 | 19 | ```asm 20 | addl $0,0 (%esp) 21 | ``` 22 | 哈哈哈哈哈...你们觉得不好笑吗 23 | 24 | 25 | ```rust 26 | // image_available_semaphores 图片可用的信号量 render... 渲染结束的信号量 in... 正在传输的墙(内存屏障) 27 | // 这个实现虽然有点捞但是比较容易看懂 28 | let (image_available_semaphores, render_finished_semaphores, in_flight_fences) = { 29 | let mut image_available_semaphores: Vec<::Semaphore> = vec![]; 30 | let mut render_finished_semaphores: Vec<::Semaphore> = vec![]; 31 | let mut in_flight_fences: Vec<::Fence> = vec![]; 32 | for _ in 0..frames_in_flight { 33 | in_flight_fences 34 | .push(device.create_fence(true).map_err(|_| "创建内存屏障失败")?); 35 | image_available_semaphores 36 | .push(device.create_semaphore().map_err(|_| "创建信号量失败")?); 37 | render_finished_semaphores 38 | .push(device.create_semaphore().map_err(|_| "创建信号量失败")?); 39 | } 40 | ( 41 | image_available_semaphores, 42 | render_finished_semaphores, 43 | in_flight_fences, 44 | ) 45 | }; 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /画个三角形吧/1.6创建renderpass.md: -------------------------------------------------------------------------------- 1 | # 写一个renderpass吧 2 | 3 | 为什么我不翻译了?因为`renderpass`的术语特别的模糊,容易引起歧义,就这么说吧,现在我有一堆盒子,我把这堆盒子进行上色叫一个`pass`,每个`pass`就是一个绘制调用(其实很多时候进行一次渲染经过很多次绘制调用因为单次渲染大批量效率并不是特别的高) 4 | 5 | 这里随着延迟渲染技术,多`pass`渲染其实现在的立足和地位都受到了某种程度的打击,但是因为概念好理解然后遍布了所有的教科书所以还是讲一下比较好. 6 | 7 | ```rust 8 | // 创建一个 RenderPass 9 | let render_pass = { 10 | let color_attachment = Attachment { 11 | format: Some(format), // 之前还记的创建swapchain的时候的那个format吗 12 | samples: 1, 13 | ops: AttachmentOps { 14 | load: AttachmentLoadOp::Clear, 15 | store: AttachmentStoreOp::Store, 16 | }, 17 | stencil_ops: AttachmentOps::DONT_CARE, 18 | layouts: Layout::Undefined..Layout::Present, 19 | }; 20 | // sub pass 就是每一个render pass里的实际对attachments进行读写操作的组件. 21 | // 效果越复杂subpass越多 22 | let subpass = SubpassDesc { 23 | colors: &[(0, Layout::ColorAttachmentOptimal)], 24 | depth_stencil: None, 25 | inputs: &[], 26 | resolves: &[], 27 | preserves: &[], 28 | }; 29 | unsafe { 30 | device 31 | .create_render_pass(&[color_attachment], &[subpass], &[]) 32 | .map_err(|_| "创建render pass 失败")? 33 | } 34 | }; 35 | ``` -------------------------------------------------------------------------------- /画个三角形吧/1.7创建图片视图和帧缓冲.md: -------------------------------------------------------------------------------- 1 | # 图片视图和帧缓冲 2 | 3 | 什么是图像视图? 就是一个对象在内存中连续的装了一个或一堆的图像结束. 4 | 什么是帧缓冲? 跟上面一样,也是装了一堆帧的一块内存. 5 | 6 | ```rust 7 | // 创建图像视图 8 | let image_views: Vec<_> = backbuffer 9 | .into_iter() 10 | .map(|image| unsafe { 11 | device 12 | .create_image_view( 13 | &image, 14 | ViewKind::D2, 15 | format, 16 | Swizzle::NO, 17 | SubresourceRange { 18 | aspects: Aspects::COLOR, 19 | levels: 0..1, 20 | layers: 0..1, 21 | }, 22 | ) 23 | .map_err(|_| "为图像创建图像视图失败") 24 | }) 25 | .collect::, &str>>()?; 26 | 27 | // 创建帧缓冲 28 | let framebuffers: Vec<::Framebuffer> = { 29 | image_views 30 | .iter() 31 | .map(|image_view| unsafe { 32 | device 33 | .create_framebuffer( 34 | &render_pass, 35 | vec![image_view], 36 | Extent { 37 | width: extent.width as u32, 38 | height: extent.height as u32, 39 | depth: 1, 40 | }, 41 | ) 42 | .map_err(|_| "创建帧缓冲失败") 43 | }) 44 | .collect::, &str>>()? 45 | }; 46 | ``` -------------------------------------------------------------------------------- /画个三角形吧/1.8创建命令池和命令缓冲.md: -------------------------------------------------------------------------------- 1 | # 创建命令池和命令缓冲 2 | 3 | 在`Vulkan`图形的定义上命令池是一个不透明的对象,分摊了很多命令缓冲区的创建资源成本,但是却是**不同步**的,所以千万不能跨线程使用. 4 | 5 | 命令缓冲区是一个用来记录命令的对象,这些命令就像email一样显卡肯定一下子看不完所以用缓冲区暂时的储存一下随后给显卡看. 6 | 7 | `Vulkan`有两个缓冲区一个是主要的缓冲区直接提交到队列,一个是辅助缓冲区,提交到主缓冲区. 8 | 9 | ```rust 10 | // 创建命令池 11 | let mut command_pool = unsafe { 12 | device 13 | .create_command_pool_typed(&queue_group, CommandPoolCreateFlags::RESET_INDIVIDUAL) 14 | .map_err(|_| "创建命令池失败,错误可能来自底层接口")? 15 | }; 16 | 17 | // 创建命令缓冲 18 | let command_buffers: Vec<_> = framebuffers 19 | .iter() 20 | .map(|_| command_pool.acquire_command_buffer()) 21 | .collect(); 22 | ``` 23 | 24 | 写到这里,一个`HardwareState`差不多就完工了 25 | 但是先别着急,看完下面这一章再开始抄代码. -------------------------------------------------------------------------------- /画个三角形吧/1.9写一个drop.md: -------------------------------------------------------------------------------- 1 | # 实现drop 2 | 3 | 其实这个完全是因为现在的`gfx-hal`写的差劲的原因,人家`Rust`的内存管理理论上都不需要用户来管的... 4 | 5 | ```rust 6 | impl core::ops::Drop for HardwareState { 7 | // 我发现了个bug在这个垃圾gfx-hal里面swapchain更换的特别频繁的时候,Rust自动drop跟不上,所以这里用的手动drop 8 | // 还有可爱的GFX开发组哟,能不能把修一修,这个unsafe看的我浑身难受 9 | fn drop(&mut self) { 10 | let _ = self.device.wait_idle(); 11 | unsafe { 12 | for fence in self.in_flight_fences.drain(..) { 13 | self.device.destroy_fence(fence) 14 | } 15 | for semaphore in self.render_finished_semaphores.drain(..) { 16 | self.device.destroy_semaphore(semaphore) 17 | } 18 | for semaphore in self.image_available_semaphores.drain(..) { 19 | self.device.destroy_semaphore(semaphore) 20 | } 21 | for framebuffer in self.framebuffers.drain(..) { 22 | self.device.destroy_framebuffer(framebuffer); 23 | } 24 | for image_view in self.image_views.drain(..) { 25 | self.device.destroy_image_view(image_view); 26 | } 27 | use core::ptr::read; 28 | self.device.destroy_command_pool( 29 | ManuallyDrop::into_inner(read(&self.command_pool)).into_raw(), 30 | ); 31 | self.device 32 | .destroy_render_pass(ManuallyDrop::into_inner(read(&self.render_pass))); 33 | self.device 34 | .destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain))); 35 | ManuallyDrop::drop(&mut self.device); 36 | ManuallyDrop::drop(&mut self._instance); 37 | } 38 | } 39 | } 40 | ``` -------------------------------------------------------------------------------- /画个三角形吧/README.md: -------------------------------------------------------------------------------- 1 | # 1.画个三角形吧 2 | 3 | 在这一章主要让大家迈出学习计算机图形学框架的第一步 4 | 也就是画个三角形出来.... 5 | 6 | 别小看这个三角形🔺,等你真正画出来就知道多 蛋碎 困难了,很多计算机图形爱好者都是被这个劝退的. --------------------------------------------------------------------------------