Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit edca1f8

Browse files
authored
Merge pull request #155 from MacGamerFR/French-update
French update
2 parents ce7d8f8 + f1ed16a commit edca1f8

11 files changed

Lines changed: 218 additions & 90 deletions

File tree

fr/00_Introduction.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ rien laisser au hasard, et il n'y a aucune structure, aucun environnement créé
1616
recréer à partir de rien. Le travail du driver graphique sera ainsi considérablement réduit, ce qui implique un plus
1717
grand travail de votre part pour assurer un comportement correct.
1818

19-
Le message véhiculé ici est que Vulkan n'est pas fait pour tout le monde. Cet API est conçu pour les programmeurs
19+
Le message véhiculé ici est que Vulkan n'est pas fait pour tout le monde. Cette API est conçue pour les programmeurs
2020
concernés par la programmation avec GPU de haute performance, et qui sont prêts à y travailler sérieusement. Si vous
2121
êtes intéressées dans le développement de jeux vidéos, et moins dans les graphismes eux-mêmes, vous devriez plutôt
2222
continuer d'utiliser OpenGL et DirectX, qui ne seront pas dépréciés en faveur de Vulkan avant un certain temps. Une
@@ -31,7 +31,7 @@ Cela étant dit, présentons quelques prérequis pour ce tutoriel:
3131
[AMD](https://www.amd.com/en/technologies/vulkan),
3232
[Intel](https://software.intel.com/en-us/blogs/2017/02/10/intel-announces-that-we-are-moving-from-beta-support-to-full-official-support-for))
3333
* De l'expérience avec le C++ (familiarité avec RAII, listes d'initialisation, et autres fonctionnalités modernes)
34-
* Un compilateur compatible C++11 (Visual Studio 2013+, GCC 4.8+)
34+
* Un compilateur avec un support décent des fonctionnalités du C++17 (Visual Studio 2017+, GCC 7+ ou Clang 5+)
3535
* Un minimum d'expérience dans le domaine de la programmation graphique
3636

3737
Ce tutoriel ne considérera pas comme acquis les concepts d'OpenGL et de Direct3D, mais il requiert que vous connaissiez

fr/02_Environnement_de_développement.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ carte graphique supporte Vulkan. Allez dans le dossier d'installation du SDK, ou
2828
Si vous recevez un message d'erreur assurez-vous que votre driver est à jour, inclut Vulkan et que votre carte graphique
2929
est supportée. Référez-vous au [chapitre introductif](!fr/Introduction) pour les liens vers les principaux constructeurs.
3030

31-
Il y a un autre programme dans ce dossier qui vous sera utile : "glslangValidator.exe". Nous en aurons besoin pour la
32-
compilation des shaders. Il transforme un code compréhensible facilement et semblable au C (le
33-
[GLSL](https://en.wikipedia.org/wiki/OpenGL_Shading_Language)) en byte code.
31+
Il y a d'autres programmes dans ce dossier qui vous seront utiles : "glslangValidator.exe" et "glslc.exe". Nous en aurons besoin pour la
32+
compilation des shaders. Ils transforment un code compréhensible facilement et semblable au C (le
33+
[GLSL](https://en.wikipedia.org/wiki/OpenGL_Shading_Language)) en bytecode.
3434
Nous couvrirons cela dans le chapitre des [modules shader](!fr/Dessiner_un_triangle/Pipeline_graphique_basique/Modules_shaders).
3535
Le dossier "Bin" contient aussi les fichiers binaires du loader Vulkan et des validation layers. Le dossier "Lib" en
3636
contient les librairies.
@@ -414,9 +414,9 @@ clean:
414414
Vous pouvez désormais utiliser ce dossier comme exemple pour vos futurs projets Vulkan. Faites-en une copie, changez le
415415
nom du projet et tout sera prêt!
416416

417-
Avant d'avancer, regardons le SDK plus en détail. Il y a un autre programme dans ce dossier qui vous sera utile :
418-
"glslangValidator". Nous nous en servirons pour compiler les shaders. Il transforme un code lisible par l'homme
419-
écrit en [GLSL](https://en.wikipedia.org/wiki/OpenGL_Shading_Language) en byte code. Nous couvrirons cela dans le
417+
Avant d'avancer, regardons le SDK plus en détail. Il y a d'autres programmes dans ce dossier qui vous seront utiles :
418+
"glslangValidator" et "glslc". Nous nous en servirons pour compiler les shaders. Ils transforment un code lisible par l'homme
419+
écrit en [GLSL](https://en.wikipedia.org/wiki/OpenGL_Shading_Language) en bytecode. Nous couvrirons cela dans le
420420
chapitre [modeules shader](!fr/Dessiner_un_triangle/Pipeline_graphique_basique/Modules_shaders).
421421

422422

fr/03_Dessiner_un_triangle/00_Mise_en_place/03_Physical_devices_et_queue_families.md

Lines changed: 99 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -186,29 +186,83 @@ traite que les commandes de calcul et qu'une autre ne supporte que les commandes
186186
187187
Nous devons analyser quelles queue families existent sur le système et lesquelles correspondent aux commandes que nous
188188
souhaitons utiliser. Nous allons donc créer la fonction `findQueueFamilies` dans laquelle nous chercherons les
189-
commandes nous intéressant. Nous allons commencer par ne chercher qu'une queue supportant les commandes graphiques, mais
190-
nous étendrons cela plus tard dans le tutoriel.
189+
commandes nous intéressant.
191190
192-
Cette fonction retournera les indices des queue families satisfaisant les propriétés que nous désirons. La meilleure
193-
manière de faire cela est d'utiliser une structure dans laquelle les valeurs seront contenues dans des `std::optional`.
194-
De cette manière il sera facile de voir quelles queues ont été trouvées.
191+
Nous allons chercher une queue qui supporte les commandes graphiques, la fonction pourrait ressembler à ça:
195192
196193
```c++
197-
struct QueueFamilyIndices {
198-
std::optional<uint32_t> graphicsFamily;
194+
uint32_t findQueueFamilies(VkPhysicalDevice device) {
195+
// Code servant à trouver la famille de queue "graphique"
196+
}
197+
```
199198

200-
bool isComplete() {
201-
return graphicsFamily.has_value();
202-
}
199+
Mais dans un des prochains chapitres, nous allons avoir besoin d'une autre famille de queues, il est donc plus intéressant
200+
de s'y préparer dès maintenant en empactant plusieurs indices dans une structure:
201+
202+
```c++
203+
struct QueueFamilyIndices {
204+
uint32_t graphicsFamily;
203205
};
206+
207+
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
208+
QueueFamilyIndices indices;
209+
// Code pour trouver les indices de familles à ajouter à la structure
210+
return indices
211+
}
212+
```
213+
214+
Que se passe-t-il si une famille n'est pas disponible ? On pourrait lancer une exception dans `findQueueFamilies`,
215+
mais cette fonction n'est pas vraiment le bon endroit pour prendre des decisions concernant le choix du bon Device.
216+
Par exemple, on pourrait *préférer* des Devices avec une queue de transfert dédiée, sans toutefois le requérir.
217+
Par conséquent nous avons besoin d'indiquer si une certaine famille de queues à été trouvé.
218+
219+
Ce n'est pas très pratique d'utiliser une valeur magique pour indiquer la non-existence d'une famille, comme n'importe
220+
quelle valeur de `uint32_t` peut théoriquement être une valeur valide d'index de famille, incluant `0`.
221+
Heureusement, le C++17 introduit un type qui permet la distinction entre le cas où la valeur existe et celui
222+
où elle n'existe pas:
223+
224+
```c++
225+
#include <optional>
226+
227+
...
228+
229+
std::optional<uint32_t> graphicsFamily;
230+
231+
std::cout << std::boolalpha << graphicsFamily.has_value() << std::endl; // faux
232+
233+
graphicsFamily = 0;
234+
235+
std::cout << std::boolalpha << graphicsFamily.has_value() << std::endl; // vrai
204236
```
205237

206-
Il est nécessaire d'inclure `optional`. Nous pouvons dès maintenant implémenter `findQueueFamilies` :
238+
`std::optional` est un wrapper qui ne contient aucune valeur tant que vous ne lui en assignez pas une.
239+
Vous pouvez, quelque soit le moment, lui demander si il contient une valeur ou non en appelant sa fonction membre
240+
`has_value()`. On peut donc changer le code comme suit:
207241

208242
```c++
243+
#include <optional>
244+
245+
...
246+
247+
struct QueueFamilyIndices {
248+
std::optional<uint32_t> graphicsFamily;
249+
};
250+
209251
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
210252
QueueFamilyIndices indices;
211253

254+
// Assigne l'index aux familles qui ont pu être trouvées
255+
256+
return indices;
257+
}
258+
```
259+
260+
On peut maintenant commencer à implémenter `findQueueFamilies`:
261+
262+
```c++
263+
QueueFamilyIndices findQueueFamily(VkPhysicalDevice) {
264+
QueueFamilyIndices indices;
265+
212266
...
213267
214268
return indices;
@@ -237,10 +291,6 @@ for (const auto& queueFamily : queueFamilies) {
237291
indices.graphicsFamily = i;
238292
}
239293
240-
if (indices.isComplete()) {
241-
break;
242-
}
243-
244294
i++;
245295
}
246296
```
@@ -249,13 +299,47 @@ Nous pouvons maintenant utiliser cette fonction dans `isDeviceSuitable` pour s'a
249299
recevoir les commandes que nous voulons lui envoyer :
250300

251301
```c++
302+
bool isDeviceSuitable(VkPhysicalDevice device) {
303+
QueueFamilyIndices indices = findQueueFamilies(device);
304+
305+
return indices.graphicsFamily.has_value();
306+
}
307+
```
308+
309+
Pour que ce soit plus pratique, nous allons aussi ajouté une fonction générique à la structure:
310+
311+
```c++
312+
struct QueueFamilyIndices {
313+
std::optional<uint32_t> graphicsFamily;
314+
315+
bool isComplete() {
316+
return graphicsFamily.has_value();
317+
}
318+
};
319+
320+
...
321+
252322
bool isDeviceSuitable(VkPhysicalDevice device) {
253323
QueueFamilyIndices indices = findQueueFamilies(device);
254324
255325
return indices.isComplete();
256326
}
257327
```
258328

329+
On peut également utiliser ceci pour sortitr plus tôt de `findQueueFamilies`:
330+
331+
```c++
332+
for (const auto& queueFamily : queueFamilies) {
333+
...
334+
335+
if (indices.isComplete()) {
336+
break;
337+
}
338+
339+
i++;
340+
}
341+
```
342+
259343
Bien, c'est tout ce dont nous aurons besoin pour choisir le bon physical device! La prochaine étape est de [créer un
260344
logical device](!fr/Dessiner_un_triangle/Mise_en_place/Logical_device_et_queues) pour créer une interface avec la carte.
261345

fr/03_Dessiner_un_triangle/02_Pipeline_graphique_basique/01_Modules_shaders.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ compilateurs étaient très laxistes par rapport à la spécification qui leur
1111
complexe, il pouvait être accepté par l'un et pas par l'autre, ou pire s'éxécuter différemment. Avec le format de
1212
plus bas niveau qu'est SPIR-V, ces problèmes seront normalement éliminés.
1313

14-
Cela ne veut cependant pas dire que nous devrons écrire ces bytecodes à la main. Khronos fournit lui-même un
14+
Cela ne veut cependant pas dire que nous devrons écrire ces bytecodes à la main. Khronos fournit même un
1515
compilateur transformant GLSL en SPIR-V. Ce compilateur standard vérifiera que votre code correspond à la spécification.
16-
Le compilateur est fourni avec le SDK et s'appelle `glslangValidator`, vous n'aurez donc rien de plus à télécharger.
16+
Vous pouvez également l'inclure comme une bibliothèque pour produire du SPIR-V au runtime, mais nous ne ferons pas cela dans ce tutoriel.
17+
Le compilateur est fourni avec le SDK et s'appelle `glslangValidator`, mais nous allons utiliser un autre compilateur
18+
nommé `glslc`, écrit par Google. L'avantage de ce dernier est qu'il utilise le même format d'options que GCC ou Clang,
19+
et inclu quelques fonctionnalités supplémentaires comme les *includes*. Les deux compilateurs sont fournis dans le SDK,
20+
vous n'avez donc rien de plus à télécharger.
1721

1822
GLSL est un language possédant une syntaxe proche du C. Les programmes y ont une fonction `main` invoquée pour chaque
1923
objet à traiter. Plutôt que d'utiliser des paramètres et des valeurs de retour, GLSL utilise des variables globales
@@ -201,40 +205,37 @@ void main() {
201205
}
202206
```
203207

204-
Nous allons maintenant compiler ces shaders en bytecode SPIR-V à l'aide du programme `glslangValidator`.
208+
Nous allons maintenant compiler ces shaders en bytecode SPIR-V à l'aide du programme `glslc`.
205209

206210
**Windows**
207211

208212
Créez un fichier `compile.bat` et copiez ceci dedans :
209213

210214
```bash
211-
C:/VulkanSDK/x.x.x.x/Bin32/glslangValidator.exe -V shader.vert
212-
C:/VulkanSDK/x.x.x.x/Bin32/glslangValidator.exe -V shader.frag
215+
C:/VulkanSDK/x.x.x.x/Bin32/glslc.exe shader.vert -o vert.spv
216+
C:/VulkanSDK/x.x.x.x/Bin32/glslc.exe shader.frag -o frag.spv
213217
pause
214218
```
215219

216-
Corrigez le chemin vers `glslangValidator.exe` pour que le .bat pointe effectivement là où le vôtre se trouve.
220+
Corrigez le chemin vers `glslc.exe` pour que le .bat pointe effectivement là où le vôtre se trouve.
217221
Double-cliquez pour lancer ce script.
218222

219223
**Linux**
220224

221225
Créez un fichier `compile.sh` et copiez ceci dedans :
222226

223227
```bash
224-
/home/user/VulkanSDK/x.x.x.x/x86_64/bin/glslangValidator -V shader.vert
225-
/home/user/VulkanSDK/x.x.x.x/x86_64/bin/glslangValidator -V shader.frag
228+
/home/user/VulkanSDK/x.x.x.x/x86_64/bin/glslc shader.vert -o vert.spv
229+
/home/user/VulkanSDK/x.x.x.x/x86_64/bin/glslc shader.frag -o frag.spv
226230
```
227231

228-
Corrigez le chemin menant au `glslangValidator` pour qu'il pointe là où il est. Rendez le script exécutable avec la
232+
Corrigez le chemin menant au `glslc` pour qu'il pointe là où il est. Rendez le script exécutable avec la
229233
commande `chmod +x compile.sh` et lancez-le.
230234

231235
**Fin des instructions spécifiques**
232236

233-
Ces deux commmandes invoquent le programme avec l'argument `-V`. Celui-ci indique au programme de compiler les sources
234-
GLSL en bytecode SPIR-V, sinon le programme se contenterait de vérifier que les sources correspondent au standard.
235-
Une fois le script exécuté vous vous retrouverez avec deux nouveaux fichiers : `vert.spv` et `frag.spv`. Les noms
236-
sont automatiquement dérivés du type de shader, mais vous pouvez les renommer si vous le souhaitez. Vous aurez
237-
peut-être un message parlant de fonctionnalités manquantes mais vous pouvez l'ignorer sans problème.
237+
Ces deux commandes instruisent le compilateur de lire le code GLSL source contenu dans un fichier et d'écrire
238+
le bytecode SPIR-V dans un fichier grâce à l'option `-o` (output).
238239

239240
Si votre shader contient une erreur de syntaxe le compilateur vous indiquera le problème et la ligne à laquelle il
240241
apparait. Essayez de retirer un point-virgule et voyez l'efficacité du debogueur. Essayez également de voir les

fr/03_Dessiner_un_triangle/03_Effectuer_le_rendu/01_Command_buffers.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ command buffer.
155155
for (size_t i = 0; i < commandBuffers.size(); i++) {
156156
VkCommandBufferBeginInfo beginInfo = {};
157157
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
158-
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
158+
beginInfo.flags = 0; // Optionnel
159159
beginInfo.pInheritanceInfo = nullptr; // Optionel
160160

161161
if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
@@ -173,9 +173,10 @@ unique render pass
173173
* `VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT` : le command buffer peut être ré-envoyé à la queue alors qu'il y est
174174
déjà et/ou est en cours d'exécution
175175

176-
Nous devons utiliser la dernière valeur pour le cas où nous préparons l'affichage de la frame suivante alors que la
177-
frame actuelle n'est pas finie. Le paramètre `pInheritanceInfo` n'a de sens que pour les command buffers secondaires. Il
178-
indique l'état à hériter de l'appel par le command buffer primaire.
176+
Nous n'avons pas besoin de ces flags ici.
177+
178+
Le paramètre `pInheritanceInfo` n'a de sens que pour les command buffers secondaires.
179+
Il indique l'état à hériter de l'appel par le command buffer primaire.
179180

180181
Si un command buffer est déjà prêt un appel à `vkBeginCommandBuffer` le regénèrera implicitement. Il n'est pas possible
181182
d'enregistrer un command buffer en plusieurs fois.

fr/03_Dessiner_un_triangle/03_Effectuer_le_rendu/02_Rendu_et_présentation.md

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ Les deux premiers champs permettent de fournir l'indice de la subpasse d'origine
211211
particulière `VK_SUBPASS_EXTERNAL` réfère à la subpass implicite soit avant soit après la render pass, selon que
212212
cette valeur est indiquée dans respectivement `srcSubpass` ou `dstSubpass`. L'indice `0` correspond à notre
213213
seule et unique subpasse. La valeur fournie à `dstSubpass` doit toujours être supérieure à `srcSubpass` car sinon une
214-
boucle infinie peut apparaître.
214+
boucle infinie peut apparaître (sauf si une des subpasse est `VK_SUBPASS_EXTERNAL`).
215215

216216
```c++
217217
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
@@ -539,8 +539,75 @@ void createSyncObjects() {
539539
}
540540
```
541541

542-
Le programme devrait maintenant fonctionner normalement et la perte de mémoire ne devrait plus exister! Et nous avons
543-
maintenant implémenté tout ce qu'il faut pour s'assurer que nous n'avons jamais plus de deux frames en vol!
542+
La fuite de mémoire n'est plus, mais le programme ne fonctionne pas encore correctement. Si `MAX_FRAMES_IN_FLIGHT` est
543+
plus grand que le nombre d'images de la swapchain ou que `vkAcquireNextImageKHR` ne retourne pas les images dans l'ordre,
544+
alors il est possible que nous lancions le rendu dans une image qui est déjà *en vol*. Pour éviter ça, nous devons pour
545+
chaque image de la swapchain si une frame en vol est en train d'utiliser celle-ci. Cette correspondance permettra de suivre
546+
les images en vol par leur fences respective, de cette façon nous aurons immédiatement un objet de synchronisation à attendre
547+
avant qu'une nouvelle frame puisse utiliser cette image.
548+
549+
Tout d'abord, ajoutez une nouvelle liste nommée `imagesInFlight`:
550+
551+
```c++
552+
std::vector<VkFence> inFlightFences;
553+
std::vector<VkFence> imagesInFlight;
554+
size_t currentFrame = 0;
555+
```
556+
557+
Préparez-la dans `CreateSyncObjects`:
558+
559+
```c++
560+
void createSyncObjects() {
561+
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
562+
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
563+
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
564+
imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE);
565+
566+
...
567+
}
568+
```
569+
570+
Initiallement aucune frame n'utilise d'image, donc on peut explicitement l'initialiser à *pas de fence*. Maintenant, nous allons modifier
571+
`drawFrame` pour attendre la fin de n'importe quelle frame qui serait en train d'utiliser l'image qu'on nous assigné pour la nouvelle frame.
572+
573+
```c++
574+
void drawFrame() {
575+
...
576+
577+
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
578+
579+
// Vérifier si une frame précédente est en train d'utiliser cette image (il y a une fence à attendre)
580+
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
581+
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
582+
}
583+
// Marque l'image comme étant à nouveau utilisée par cette frame
584+
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
585+
586+
...
587+
}
588+
```
589+
590+
Parce que nous avons maintenant plus d'appels à `vkWaitForFences`, les apples à `vkResetFences` doivent être **déplacés**. Le mieux reste
591+
de simplement l'appeler juste avant d'utiliser la fence:
592+
593+
```c++
594+
void drawFrame() {
595+
...
596+
597+
vkResetFences(device, 1, &inFlightFences[currentFrame]);
598+
599+
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
600+
throw std::runtime_error("échec de l'envoi d'un command buffer!");
601+
}
602+
603+
...
604+
}
605+
```
606+
607+
Nous avons implémenté tout ce qui est nécessaire à la synchronisation pour certifier qu'il n'y a pas plus de deux frames de travail
608+
dans la queue et que ces frames n'utilise pas accidentellement la même image. Notez qu'il est tout à fait normal pour d'autre parties du code,
609+
comme le nettoyage final, de se reposer sur des mécanismes de synchronisation plus durs comme `vkDeviceWaitIdle`. Vous devriez décider
610+
de la bonne approche à utiliser en vous basant sur vos besoins de performances.
544611

545612
Pour en apprendre plus sur la synchronisation rendez vous sur
546613
[ces exemples complets](https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples#swapchain-image-acquire-and-present)

0 commit comments

Comments
 (0)