Wikilivres frwikibooks https://fr.wikibooks.org/wiki/Accueil MediaWiki 1.46.0-wmf.24 first-letter Média Spécial Discussion Utilisateur Discussion utilisateur Wikilivres Discussion Wikilivres Fichier Discussion fichier MediaWiki Discussion MediaWiki Modèle Discussion modèle Aide Discussion aide Catégorie Discussion catégorie Transwiki Discussion Transwiki Wikijunior Discussion Wikijunior TimedText TimedText talk Module Discussion module Event Event talk Philosophie/Histoire de la philosophie 0 615 763988 761916 2026-04-19T07:41:43Z PandaMystique 119061 763988 wikitext text/x-wiki <!-- ═══════════════════════════════════════════════════════════════════ EN-TÊTE PRINCIPAL ═══════════════════════════════════════════════════════════════════ --> <div style="background: linear-gradient(180deg, #2c3e50, #1a252f); border-radius: 8px; padding: 25px 30px; margin-bottom: 20px; text-align: center;"> <div style="font-size: 1.5em; font-weight: 600; color: #ffffff; letter-spacing: 0.02em; margin-bottom: 6px;">Histoire de la philosophie</div> <div style="font-size: 0.95em; color: #a8b8c8; letter-spacing: 0.03em;">Commentaires, vocabulaires, exposés</div> </div> <!-- ═══════════════════════════════════════════════════════════════════ NAVIGATION PAR PÉRIODE ═══════════════════════════════════════════════════════════════════ --> <div style="background: #f8f9fa; border: 1px solid #e0e0e0; border-radius: 6px; padding: 14px 20px; margin-bottom: 25px; text-align: center; font-size: 0.9em;"> <span style="color: #666; margin-right: 8px;">Naviguer :</span> [[#Antique|<span style="color: #8a5a30;">● Antiquité</span>]] &nbsp;•&nbsp; [[#Moderne|<span style="color: #4a6a8a;">● Époque moderne</span>]] &nbsp;•&nbsp; [[#Contemporaine|<span style="color: #5a3d7a;">● Contemporaine</span>]] &nbsp;•&nbsp; [[#Bibliographie|<span style="color: #555;">● Bibliographie</span>]] </div> <!-- ═══════════════════════════════════════════════════════════════════ FRISE CHRONOLOGIQUE SIMPLIFIÉE ═══════════════════════════════════════════════════════════════════ --> <table style="width: 100%; border-collapse: collapse; margin-bottom: 30px;"> <tr> <td style="width: 25%; background: linear-gradient(180deg, #f5efe5, #ebe3d5); border: 1px solid #c9b896; border-radius: 6px 0 0 6px; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #8a6a40; text-transform: uppercase; letter-spacing: 0.08em;">Antiquité</div> <div style="font-size: 0.85em; color: #5a4a30; margin-top: 4px;">VIᵉ s. av. J.-C. → Vᵉ s.</div> </td> <td style="width: 25%; background: linear-gradient(180deg, #e8f0f5, #dae6ef); border: 1px solid #a0b8c8; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #4a6a85; text-transform: uppercase; letter-spacing: 0.08em;">Moyen Âge</div> <div style="font-size: 0.85em; color: #3a5065; margin-top: 4px;">Vᵉ → XVᵉ s.</div> </td> <td style="width: 25%; background: linear-gradient(180deg, #eef5f0, #e0ede5); border: 1px solid #7aa088; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #4a7a5a; text-transform: uppercase; letter-spacing: 0.08em;">Époque moderne</div> <div style="font-size: 0.85em; color: #3a5a45; margin-top: 4px;">XVIᵉ → XVIIIᵉ s.</div> </td> <td style="width: 25%; background: linear-gradient(180deg, #f0eaf5, #e4daf0); border: 1px solid #9080a8; border-radius: 0 6px 6px 0; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #6a5080; text-transform: uppercase; letter-spacing: 0.08em;">Contemporaine</div> <div style="font-size: 0.85em; color: #4a3860; margin-top: 4px;">XIXᵉ → XXIᵉ s.</div> </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : PHILOSOPHIE ANTIQUE ═══════════════════════════════════════════════════════════════════ --> <div id="Antique" style="margin: 30px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #c9a860;"> <span style="font-size: 1.15em; font-weight: 700; color: #6a5020; letter-spacing: 0.02em;">PHILOSOPHIE ANTIQUE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">VIᵉ siècle av. J.-C. — Vᵉ siècle</span> </div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- PRÉSOCRATIQUES --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #faf6ed, #f2ebdc); border:1px solid #d4c090; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a4a20; margin-bottom:8px;">🌅 [[Philosophie/Présocratiques|Présocratiques]]</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Les premiers penseurs grecs cherchent le principe (''archè'') de toutes choses.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #d4c090; padding-top:8px;"> → [[Philosophie/Thalès de Milet|Thalès de Milet]]<br/> → [[Philosophie/Anaximandre de Milet|Anaximandre]] </div> </td> <!-- PLATON --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #fdf8ed, #f6eedc); border:1px solid #d4b860; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a4800; margin-bottom:8px;">📜 [[Pour lire Platon|Platon]]</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Théorie des Idées, dialectique et fondation de l'Académie.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #d4b860; padding-top:8px;"> → [[Pour lire Platon/Vocabulaire|Vocabulaire platonicien]] </div> </td> <!-- STOÏCIENS --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f8f4e8, #f0e8d8); border:1px solid #c8b070; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a4820; margin-bottom:8px;">🏛️ Stoïcisme</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Vivre selon la nature, maîtriser ses passions, accepter le destin.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #c8b070; padding-top:8px;"> → [[Les Stoïciens : Épictète Le poignard à la main|Épictète]] </div> </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : PHILOSOPHIE MODERNE ═══════════════════════════════════════════════════════════════════ --> <div id="Moderne" style="margin: 35px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #4a90b8;"> <span style="font-size: 1.15em; font-weight: 700; color: #2a5a7a; letter-spacing: 0.02em;">PHILOSOPHIE MODERNE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">XVIᵉ — XVIIIᵉ siècle</span> </div> <!-- Sous-section : XVIIe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #4a6a85; margin: 20px 0 12px 0; padding-left: 10px; border-left: 3px solid #a0c0d8;">XVIIᵉ siècle — L'âge classique</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- DESCARTES --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #eef4f8, #e0ecf4); border:1px solid #6090b8; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#1a4a6a; margin-bottom:4px;">[[Dictionnaire de philosophie/René Descartes|René Descartes]]</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1596 — 1650</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Le ''cogito'', le doute méthodique et la fondation de la philosophie moderne.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #6090b8; padding-top:8px;"> → [[Méditations métaphysiques]] </div> </td> <!-- SPINOZA --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #edf5f2, #dfeee8); border:1px solid #5a9878; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#1a5038; margin-bottom:4px;">Baruch Spinoza</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1632 — 1677</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Substance unique, déterminisme et béatitude par la connaissance.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #5a9878; padding-top:8px;"> → [[Commentaire de l'Éthique]] </div> </td> <!-- PASCAL --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f5f0f8, #ebe4f2); border:1px solid #8878a8; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#3a2860; margin-bottom:4px;">Blaise Pascal</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1623 — 1662</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Misère et grandeur de l'homme, le pari, la condition humaine.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #8878a8; padding-top:8px;"> → [[Philosophie/Commentaire du passage à propos de l'Homme esclave du divertissement|Le divertissement]]<br/> → [[Philosophie/Commentaire du passage à propos des deux infinis|Les deux infinis]] </div> </td> </tr> </table> <!-- Sous-section : XVIIIe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #4a6a85; margin: 25px 0 12px 0; padding-left: 10px; border-left: 3px solid #a0c0d8;">XVIIIᵉ siècle — Les Lumières</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- HUME --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f0f5f8, #e2eef5); border:1px solid #5088b0; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#1a4a68; margin-bottom:4px;">David Hume</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1711 — 1776</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Empirisme radical, critique de la causalité et scepticisme modéré.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #5088b0; padding-top:8px;"> → [[Philosophie/Vocabulaire/David Hume|Vocabulaire]] </div> </td> <!-- KANT --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #eef2f8, #e0e8f2); border:1px solid #6080a8; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#2a3860; margin-bottom:4px;">Emmanuel Kant</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1724 — 1804</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Révolution copernicienne, critique de la raison et impératif catégorique.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #6080a8; padding-top:8px;"> → [[Philosophie/Vocabulaire/Kant|Vocabulaire]]<br/> → [[Commentaire de la Fondation de la métaphysique des mœurs|Fondation de la métaphysique des mœurs]] </div> </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : PHILOSOPHIE CONTEMPORAINE ═══════════════════════════════════════════════════════════════════ --> <div id="Contemporaine" style="margin: 35px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #9070b0;"> <span style="font-size: 1.15em; font-weight: 700; color: #5a3d7a; letter-spacing: 0.02em;">PHILOSOPHIE CONTEMPORAINE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">XIXᵉ — XXIᵉ siècle</span> </div> <!-- Sous-section : XIXe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #6a5080; margin: 20px 0 12px 0; padding-left: 10px; border-left: 3px solid #c0a8d0;">XIXᵉ siècle</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- NIETZSCHE --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f8f0f2, #f0e4e8); border:1px solid #a87080; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a2838; margin-bottom:4px;">[[Philosophie/Nietzsche|Friedrich Nietzsche]]</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1844 — 1900</div> <div style="font-size:0.88em; color:#555; line-height:1.6;">Critique de la morale, volonté de puissance, mort de Dieu et éternel retour.</div> </td> <!-- Placeholder pour futurs philosophes du XIXe --> <td style="width:50%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:16px 18px; color: #999; font-size: 0.9em; text-align: center;"> <div style="margin: 15px 0;">''À venir : Hegel, Marx, Kierkegaard…''</div> </td> </tr> </table> <!-- Sous-section : XXe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #6a5080; margin: 25px 0 12px 0; padding-left: 10px; border-left: 3px solid #c0a8d0;">XXᵉ siècle</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- HEIDEGGER --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f3eef8, #e8e0f2); border:1px solid #8068a0; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#3a2858; margin-bottom:4px;">Martin Heidegger</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1889 — 1976</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Question de l'être, ''Dasein'', temporalité et critique de la métaphysique.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #8068a0; padding-top:8px;"> → [[Être et Temps]] </div> </td> <!-- Placeholder pour futurs philosophes du XXe --> <td style="width:50%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:16px 18px; color: #999; font-size: 0.9em; text-align: center;"> <div style="margin: 15px 0;">''À venir : Sartre, Merleau-Ponty, Wittgenstein…''</div> </td> </tr> </table> <!-- Sous-section : Courants --> <div style="font-size: 0.9em; font-weight: 600; color: #6a5080; margin: 25px 0 12px 0; padding-left: 10px; border-left: 3px solid #c0a8d0;">Courants philosophiques</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- PHILOSOPHIE ANALYTIQUE --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f0f2f8, #e5e8f2); border:1px solid #7080a0; border-radius:8px; padding:14px 16px; overflow:hidden;"> <div style="font-weight:bold; font-size:1em; color:#2a3860; margin-bottom:6px;">🔬 [[Philosophie analytique]]</div> <div style="font-size:0.85em; color:#555; line-height:1.5; margin-bottom:10px;">Analyse logique du langage et clarification des concepts.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #8068a0; padding-top:8px;"> → [[Ignorance: A Case for Scepticism]] </div> </td> <!-- Placeholders pour futurs courants --> <td style="width:33%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:14px 16px; color: #999; font-size: 0.85em; text-align: center;"> ''À venir : Phénoménologie'' </td> <td style="width:33%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:14px 16px; color: #999; font-size: 0.85em; text-align: center;"> ''À venir : Existentialisme'' </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : BIBLIOGRAPHIE ═══════════════════════════════════════════════════════════════════ --> <div id="Bibliographie" style="margin: 35px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #888;"> <span style="font-size: 1.15em; font-weight: 700; color: #444; letter-spacing: 0.02em;">BIBLIOGRAPHIE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">Ouvrages de référence</span> </div> <div style="background: linear-gradient(180deg, #fafafa, #f5f5f5); border: 1px solid #e0e0e0; border-radius: 8px; padding: 18px 22px; font-size: 0.92em; line-height: 1.8; color: #444;"> <div style="margin-bottom: 8px;">📚 <strong>PRADEAU</strong>, Jean-François (dir.), ''Histoire de la philosophie'', Éditions du Seuil, 2009</div> <div style="margin-bottom: 8px;">📚 <strong>CHÂTELET</strong>, François, ''Histoire de la philosophie'', 8 tomes, Paris, Hachette-Pluriel, 1999-2000</div> <div style="margin-bottom: 8px;">📚 <strong>BRÉHIER</strong>, Émile, ''Histoire de la philosophie'', PUF, Quadrige, 2004, {{ISBN|2-13054-396-0}} — [http://classiques.uqac.ca/classiques/brehier_emile/brehier_emile.html Texte en ligne]</div> <div>📚 <strong>ZARADER</strong>, Jean-Pierre (dir.), ''Le vocabulaire des philosophes'', 5 tomes, Paris, Ellipses, 2002-2006</div> </div> <!-- ═══════════════════════════════════════════════════════════════════ FOOTER : RESSOURCES ═══════════════════════════════════════════════════════════════════ --> <div style="margin-top: 25px; background: linear-gradient(180deg, #f5f5f3, #eaeae8); border: 1px solid #d8d8d5; border-radius: 8px; padding: 16px 20px;"> <div style="font-size: 0.8em; font-weight: 700; color: #555; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 10px;">Voir aussi</div> <div style="font-size: 0.9em; color: #444; line-height: 1.7;"> → [[Philosophie|Portail Philosophie]] &nbsp;•&nbsp; → [[Dictionnaire de philosophie|Dictionnaire des concepts]] &nbsp;•&nbsp; → [[Manuel de terminale de philosophie|Manuel de Terminale]] &nbsp;•&nbsp; → [[w:Histoire de la philosophie|Wikipédia]] </div> </div> [[Catégorie:Discipline philosophique]] [[Catégorie:Histoire|Histoire de la philosophie]] [[Catégorie:Histoire de la philosophie|*]] niq802xodrho4w7f7wrp4fkf2fpfbbh 763989 763988 2026-04-19T07:43:02Z PandaMystique 119061 763989 wikitext text/x-wiki <!-- ═══════════════════════════════════════════════════════════════════ EN-TÊTE PRINCIPAL ═══════════════════════════════════════════════════════════════════ --> <div style="background: linear-gradient(180deg, #2c3e50, #1a252f); border-radius: 8px; padding: 25px 30px; margin-bottom: 20px; text-align: center;"> <div style="font-size: 1.5em; font-weight: 600; color: #ffffff; letter-spacing: 0.02em; margin-bottom: 6px;">Histoire de la philosophie</div> <div style="font-size: 0.95em; color: #a8b8c8; letter-spacing: 0.03em;">Commentaires, vocabulaires, exposés</div> </div> <!-- ═══════════════════════════════════════════════════════════════════ NAVIGATION PAR PÉRIODE ═══════════════════════════════════════════════════════════════════ --> <div style="background: #f8f9fa; border: 1px solid #e0e0e0; border-radius: 6px; padding: 14px 20px; margin-bottom: 25px; text-align: center; font-size: 0.9em;"> <span style="color: #666; margin-right: 8px;">Naviguer :</span> [[#Antique|<span style="color: #8a5a30;">● Antiquité</span>]] &nbsp;•&nbsp; [[#Moderne|<span style="color: #4a6a8a;">● Époque moderne</span>]] &nbsp;•&nbsp; [[#Contemporaine|<span style="color: #5a3d7a;">● Contemporaine</span>]] &nbsp;•&nbsp; [[#Bibliographie|<span style="color: #555;">● Bibliographie</span>]] </div> <!-- ═══════════════════════════════════════════════════════════════════ FRISE CHRONOLOGIQUE SIMPLIFIÉE ═══════════════════════════════════════════════════════════════════ --> <table style="width: 100%; border-collapse: collapse; margin-bottom: 30px;"> <tr> <td style="width: 25%; background: linear-gradient(180deg, #f5efe5, #ebe3d5); border: 1px solid #c9b896; border-radius: 6px 0 0 6px; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #8a6a40; text-transform: uppercase; letter-spacing: 0.08em;">Antiquité</div> <div style="font-size: 0.85em; color: #5a4a30; margin-top: 4px;">VIᵉ s. av. J.-C. → Vᵉ s.</div> </td> <td style="width: 25%; background: linear-gradient(180deg, #e8f0f5, #dae6ef); border: 1px solid #a0b8c8; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #4a6a85; text-transform: uppercase; letter-spacing: 0.08em;">Moyen Âge</div> <div style="font-size: 0.85em; color: #3a5065; margin-top: 4px;">Vᵉ → XVᵉ s.</div> </td> <td style="width: 25%; background: linear-gradient(180deg, #eef5f0, #e0ede5); border: 1px solid #7aa088; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #4a7a5a; text-transform: uppercase; letter-spacing: 0.08em;">Époque moderne</div> <div style="font-size: 0.85em; color: #3a5a45; margin-top: 4px;">XVIᵉ → XVIIIᵉ s.</div> </td> <td style="width: 25%; background: linear-gradient(180deg, #f0eaf5, #e4daf0); border: 1px solid #9080a8; border-radius: 0 6px 6px 0; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #6a5080; text-transform: uppercase; letter-spacing: 0.08em;">Contemporaine</div> <div style="font-size: 0.85em; color: #4a3860; margin-top: 4px;">XIXᵉ → XXIᵉ s.</div> </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : PHILOSOPHIE ANTIQUE ═══════════════════════════════════════════════════════════════════ --> <div id="Antique" style="margin: 30px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #c9a860;"> <span style="font-size: 1.15em; font-weight: 700; color: #6a5020; letter-spacing: 0.02em;">PHILOSOPHIE ANTIQUE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">VIᵉ siècle av. J.-C. — Vᵉ siècle</span> </div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- PRÉSOCRATIQUES --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #faf6ed, #f2ebdc); border:1px solid #d4c090; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a4a20; margin-bottom:8px;">🌅 [[Philosophie/Présocratiques|Présocratiques]]</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Les premiers penseurs grecs cherchent le principe (''archè'') de toutes choses.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #d4c090; padding-top:8px;"> → [[Philosophie/Thalès de Milet|Thalès de Milet]]<br/> → [[Philosophie/Anaximandre de Milet|Anaximandre]] </div> </td> <!-- PLATON --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #fdf8ed, #f6eedc); border:1px solid #d4b860; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a4800; margin-bottom:8px;">📜 [[Pour lire Platon|Platon]]</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Théorie des Idées, dialectique et fondation de l'Académie.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #d4b860; padding-top:8px;"> → [[Pour lire Platon/Guide des dialogues/Apologie de Socrate|Apologie de Socrate]]<br/> → [[Pour lire Platon/Vocabulaire|Vocabulaire platonicien]] </div> </td> <!-- STOÏCIENS --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f8f4e8, #f0e8d8); border:1px solid #c8b070; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a4820; margin-bottom:8px;">🏛️ Stoïcisme</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Vivre selon la nature, maîtriser ses passions, accepter le destin.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #c8b070; padding-top:8px;"> → [[Les Stoïciens : Épictète Le poignard à la main|Épictète]] </div> </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : PHILOSOPHIE MODERNE ═══════════════════════════════════════════════════════════════════ --> <div id="Moderne" style="margin: 35px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #4a90b8;"> <span style="font-size: 1.15em; font-weight: 700; color: #2a5a7a; letter-spacing: 0.02em;">PHILOSOPHIE MODERNE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">XVIᵉ — XVIIIᵉ siècle</span> </div> <!-- Sous-section : XVIIe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #4a6a85; margin: 20px 0 12px 0; padding-left: 10px; border-left: 3px solid #a0c0d8;">XVIIᵉ siècle — L'âge classique</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- DESCARTES --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #eef4f8, #e0ecf4); border:1px solid #6090b8; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#1a4a6a; margin-bottom:4px;">[[Dictionnaire de philosophie/René Descartes|René Descartes]]</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1596 — 1650</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Le ''cogito'', le doute méthodique et la fondation de la philosophie moderne.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #6090b8; padding-top:8px;"> → [[Méditations métaphysiques]] </div> </td> <!-- SPINOZA --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #edf5f2, #dfeee8); border:1px solid #5a9878; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#1a5038; margin-bottom:4px;">Baruch Spinoza</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1632 — 1677</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Substance unique, déterminisme et béatitude par la connaissance.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #5a9878; padding-top:8px;"> → [[Commentaire de l'Éthique]] </div> </td> <!-- PASCAL --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f5f0f8, #ebe4f2); border:1px solid #8878a8; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#3a2860; margin-bottom:4px;">Blaise Pascal</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1623 — 1662</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Misère et grandeur de l'homme, le pari, la condition humaine.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #8878a8; padding-top:8px;"> → [[Philosophie/Commentaire du passage à propos de l'Homme esclave du divertissement|Le divertissement]]<br/> → [[Philosophie/Commentaire du passage à propos des deux infinis|Les deux infinis]] </div> </td> </tr> </table> <!-- Sous-section : XVIIIe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #4a6a85; margin: 25px 0 12px 0; padding-left: 10px; border-left: 3px solid #a0c0d8;">XVIIIᵉ siècle — Les Lumières</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- HUME --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f0f5f8, #e2eef5); border:1px solid #5088b0; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#1a4a68; margin-bottom:4px;">David Hume</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1711 — 1776</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Empirisme radical, critique de la causalité et scepticisme modéré.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #5088b0; padding-top:8px;"> → [[Philosophie/Vocabulaire/David Hume|Vocabulaire]] </div> </td> <!-- KANT --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #eef2f8, #e0e8f2); border:1px solid #6080a8; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#2a3860; margin-bottom:4px;">Emmanuel Kant</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1724 — 1804</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Révolution copernicienne, critique de la raison et impératif catégorique.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #6080a8; padding-top:8px;"> → [[Philosophie/Vocabulaire/Kant|Vocabulaire]]<br/> → [[Commentaire de la Fondation de la métaphysique des mœurs|Fondation de la métaphysique des mœurs]] </div> </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : PHILOSOPHIE CONTEMPORAINE ═══════════════════════════════════════════════════════════════════ --> <div id="Contemporaine" style="margin: 35px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #9070b0;"> <span style="font-size: 1.15em; font-weight: 700; color: #5a3d7a; letter-spacing: 0.02em;">PHILOSOPHIE CONTEMPORAINE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">XIXᵉ — XXIᵉ siècle</span> </div> <!-- Sous-section : XIXe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #6a5080; margin: 20px 0 12px 0; padding-left: 10px; border-left: 3px solid #c0a8d0;">XIXᵉ siècle</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- NIETZSCHE --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f8f0f2, #f0e4e8); border:1px solid #a87080; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a2838; margin-bottom:4px;">[[Philosophie/Nietzsche|Friedrich Nietzsche]]</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1844 — 1900</div> <div style="font-size:0.88em; color:#555; line-height:1.6;">Critique de la morale, volonté de puissance, mort de Dieu et éternel retour.</div> </td> <!-- Placeholder pour futurs philosophes du XIXe --> <td style="width:50%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:16px 18px; color: #999; font-size: 0.9em; text-align: center;"> <div style="margin: 15px 0;">''À venir : Hegel, Marx, Kierkegaard…''</div> </td> </tr> </table> <!-- Sous-section : XXe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #6a5080; margin: 25px 0 12px 0; padding-left: 10px; border-left: 3px solid #c0a8d0;">XXᵉ siècle</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- HEIDEGGER --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f3eef8, #e8e0f2); border:1px solid #8068a0; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#3a2858; margin-bottom:4px;">Martin Heidegger</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1889 — 1976</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Question de l'être, ''Dasein'', temporalité et critique de la métaphysique.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #8068a0; padding-top:8px;"> → [[Être et Temps]] </div> </td> <!-- Placeholder pour futurs philosophes du XXe --> <td style="width:50%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:16px 18px; color: #999; font-size: 0.9em; text-align: center;"> <div style="margin: 15px 0;">''À venir : Sartre, Merleau-Ponty, Wittgenstein…''</div> </td> </tr> </table> <!-- Sous-section : Courants --> <div style="font-size: 0.9em; font-weight: 600; color: #6a5080; margin: 25px 0 12px 0; padding-left: 10px; border-left: 3px solid #c0a8d0;">Courants philosophiques</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- PHILOSOPHIE ANALYTIQUE --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f0f2f8, #e5e8f2); border:1px solid #7080a0; border-radius:8px; padding:14px 16px; overflow:hidden;"> <div style="font-weight:bold; font-size:1em; color:#2a3860; margin-bottom:6px;">🔬 [[Philosophie analytique]]</div> <div style="font-size:0.85em; color:#555; line-height:1.5; margin-bottom:10px;">Analyse logique du langage et clarification des concepts.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #8068a0; padding-top:8px;"> → [[Ignorance: A Case for Scepticism]] </div> </td> <!-- Placeholders pour futurs courants --> <td style="width:33%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:14px 16px; color: #999; font-size: 0.85em; text-align: center;"> ''À venir : Phénoménologie'' </td> <td style="width:33%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:14px 16px; color: #999; font-size: 0.85em; text-align: center;"> ''À venir : Existentialisme'' </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : BIBLIOGRAPHIE ═══════════════════════════════════════════════════════════════════ --> <div id="Bibliographie" style="margin: 35px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #888;"> <span style="font-size: 1.15em; font-weight: 700; color: #444; letter-spacing: 0.02em;">BIBLIOGRAPHIE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">Ouvrages de référence</span> </div> <div style="background: linear-gradient(180deg, #fafafa, #f5f5f5); border: 1px solid #e0e0e0; border-radius: 8px; padding: 18px 22px; font-size: 0.92em; line-height: 1.8; color: #444;"> <div style="margin-bottom: 8px;">📚 <strong>PRADEAU</strong>, Jean-François (dir.), ''Histoire de la philosophie'', Éditions du Seuil, 2009</div> <div style="margin-bottom: 8px;">📚 <strong>CHÂTELET</strong>, François, ''Histoire de la philosophie'', 8 tomes, Paris, Hachette-Pluriel, 1999-2000</div> <div style="margin-bottom: 8px;">📚 <strong>BRÉHIER</strong>, Émile, ''Histoire de la philosophie'', PUF, Quadrige, 2004, {{ISBN|2-13054-396-0}} — [http://classiques.uqac.ca/classiques/brehier_emile/brehier_emile.html Texte en ligne]</div> <div>📚 <strong>ZARADER</strong>, Jean-Pierre (dir.), ''Le vocabulaire des philosophes'', 5 tomes, Paris, Ellipses, 2002-2006</div> </div> <!-- ═══════════════════════════════════════════════════════════════════ FOOTER : RESSOURCES ═══════════════════════════════════════════════════════════════════ --> <div style="margin-top: 25px; background: linear-gradient(180deg, #f5f5f3, #eaeae8); border: 1px solid #d8d8d5; border-radius: 8px; padding: 16px 20px;"> <div style="font-size: 0.8em; font-weight: 700; color: #555; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 10px;">Voir aussi</div> <div style="font-size: 0.9em; color: #444; line-height: 1.7;"> → [[Philosophie|Portail Philosophie]] &nbsp;•&nbsp; → [[Dictionnaire de philosophie|Dictionnaire des concepts]] &nbsp;•&nbsp; → [[Manuel de terminale de philosophie|Manuel de Terminale]] &nbsp;•&nbsp; → [[w:Histoire de la philosophie|Wikipédia]] </div> </div> [[Catégorie:Discipline philosophique]] [[Catégorie:Histoire|Histoire de la philosophie]] [[Catégorie:Histoire de la philosophie|*]] 3delvw1fj7mo2ekdo4bnmnqpgv38h0m 763991 763989 2026-04-19T07:54:42Z PandaMystique 119061 763991 wikitext text/x-wiki <!-- ═══════════════════════════════════════════════════════════════════ EN-TÊTE PRINCIPAL ═══════════════════════════════════════════════════════════════════ --> <div style="background: linear-gradient(180deg, #2c3e50, #1a252f); border-radius: 8px; padding: 25px 30px; margin-bottom: 20px; text-align: center;"> <div style="font-size: 1.5em; font-weight: 600; color: #ffffff; letter-spacing: 0.02em; margin-bottom: 6px;">Histoire de la philosophie</div> <div style="font-size: 0.95em; color: #a8b8c8; letter-spacing: 0.03em;">Commentaires, vocabulaires, exposés</div> </div> <!-- ═══════════════════════════════════════════════════════════════════ NAVIGATION PAR PÉRIODE ═══════════════════════════════════════════════════════════════════ --> <div style="background: #f8f9fa; border: 1px solid #e0e0e0; border-radius: 6px; padding: 14px 20px; margin-bottom: 25px; text-align: center; font-size: 0.9em;"> <span style="color: #666; margin-right: 8px;">Naviguer :</span> [[#Antique|<span style="color: #8a5a30;">● Antiquité</span>]] &nbsp;•&nbsp; [[#Moderne|<span style="color: #4a6a8a;">● Époque moderne</span>]] &nbsp;•&nbsp; [[#Contemporaine|<span style="color: #5a3d7a;">● Contemporaine</span>]] &nbsp;•&nbsp; [[#Bibliographie|<span style="color: #555;">● Bibliographie</span>]] </div> <!-- ═══════════════════════════════════════════════════════════════════ FRISE CHRONOLOGIQUE SIMPLIFIÉE ═══════════════════════════════════════════════════════════════════ --> <table style="width: 100%; border-collapse: collapse; margin-bottom: 30px;"> <tr> <td style="width: 25%; background: linear-gradient(180deg, #f5efe5, #ebe3d5); border: 1px solid #c9b896; border-radius: 6px 0 0 6px; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #8a6a40; text-transform: uppercase; letter-spacing: 0.08em;">Antiquité</div> <div style="font-size: 0.85em; color: #5a4a30; margin-top: 4px;">VIᵉ s. av. J.-C. → Vᵉ s.</div> </td> <td style="width: 25%; background: linear-gradient(180deg, #e8f0f5, #dae6ef); border: 1px solid #a0b8c8; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #4a6a85; text-transform: uppercase; letter-spacing: 0.08em;">Moyen Âge</div> <div style="font-size: 0.85em; color: #3a5065; margin-top: 4px;">Vᵉ → XVᵉ s.</div> </td> <td style="width: 25%; background: linear-gradient(180deg, #eef5f0, #e0ede5); border: 1px solid #7aa088; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #4a7a5a; text-transform: uppercase; letter-spacing: 0.08em;">Époque moderne</div> <div style="font-size: 0.85em; color: #3a5a45; margin-top: 4px;">XVIᵉ → XVIIIᵉ s.</div> </td> <td style="width: 25%; background: linear-gradient(180deg, #f0eaf5, #e4daf0); border: 1px solid #9080a8; border-radius: 0 6px 6px 0; padding: 12px 15px; text-align: center;"> <div style="font-size: 0.75em; color: #6a5080; text-transform: uppercase; letter-spacing: 0.08em;">Contemporaine</div> <div style="font-size: 0.85em; color: #4a3860; margin-top: 4px;">XIXᵉ → XXIᵉ s.</div> </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : PHILOSOPHIE ANTIQUE ═══════════════════════════════════════════════════════════════════ --> <div id="Antique" style="margin: 30px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #c9a860;"> <span style="font-size: 1.15em; font-weight: 700; color: #6a5020; letter-spacing: 0.02em;">PHILOSOPHIE ANTIQUE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">VIᵉ siècle av. J.-C. — Vᵉ siècle</span> </div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- PRÉSOCRATIQUES --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #faf6ed, #f2ebdc); border:1px solid #d4c090; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a4a20; margin-bottom:8px;">🌅 [[Philosophie/Présocratiques|Présocratiques]]</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Les premiers penseurs grecs cherchent le principe (''archè'') de toutes choses.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #d4c090; padding-top:8px;"> → [[Philosophie/Thalès de Milet|Thalès de Milet]]<br/> → [[Philosophie/Anaximandre de Milet|Anaximandre]]<br/> → [[Dictionnaire de philosophie/Anaxagore|Anaxagore]] </div> </td> <!-- PLATON --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #fdf8ed, #f6eedc); border:1px solid #d4b860; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a4800; margin-bottom:8px;">📜 [[Pour lire Platon|Platon]]</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Théorie des Idées, dialectique et fondation de l'Académie.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #d4b860; padding-top:8px;"> → [[Pour lire Platon/Guide des dialogues/Apologie de Socrate|Apologie de Socrate]]<br/> → [[Pour lire Platon/Vocabulaire|Vocabulaire platonicien]] </div> </td> <!-- STOÏCIENS --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f8f4e8, #f0e8d8); border:1px solid #c8b070; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a4820; margin-bottom:8px;">🏛️ Stoïcisme</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Vivre selon la nature, maîtriser ses passions, accepter le destin.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #c8b070; padding-top:8px;"> → [[Les Stoïciens : Épictète Le poignard à la main|Épictète]] </div> </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : PHILOSOPHIE MODERNE ═══════════════════════════════════════════════════════════════════ --> <div id="Moderne" style="margin: 35px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #4a90b8;"> <span style="font-size: 1.15em; font-weight: 700; color: #2a5a7a; letter-spacing: 0.02em;">PHILOSOPHIE MODERNE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">XVIᵉ — XVIIIᵉ siècle</span> </div> <!-- Sous-section : XVIIe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #4a6a85; margin: 20px 0 12px 0; padding-left: 10px; border-left: 3px solid #a0c0d8;">XVIIᵉ siècle — L'âge classique</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- DESCARTES --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #eef4f8, #e0ecf4); border:1px solid #6090b8; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#1a4a6a; margin-bottom:4px;">[[Dictionnaire de philosophie/René Descartes|René Descartes]]</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1596 — 1650</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Le ''cogito'', le doute méthodique et la fondation de la philosophie moderne.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #6090b8; padding-top:8px;"> → [[Méditations métaphysiques]] </div> </td> <!-- SPINOZA --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #edf5f2, #dfeee8); border:1px solid #5a9878; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#1a5038; margin-bottom:4px;">Baruch Spinoza</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1632 — 1677</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Substance unique, déterminisme et béatitude par la connaissance.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #5a9878; padding-top:8px;"> → [[Commentaire de l'Éthique]] </div> </td> <!-- PASCAL --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f5f0f8, #ebe4f2); border:1px solid #8878a8; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#3a2860; margin-bottom:4px;">Blaise Pascal</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1623 — 1662</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Misère et grandeur de l'homme, le pari, la condition humaine.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #8878a8; padding-top:8px;"> → [[Philosophie/Commentaire du passage à propos de l'Homme esclave du divertissement|Le divertissement]]<br/> → [[Philosophie/Commentaire du passage à propos des deux infinis|Les deux infinis]] </div> </td> </tr> </table> <!-- Sous-section : XVIIIe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #4a6a85; margin: 25px 0 12px 0; padding-left: 10px; border-left: 3px solid #a0c0d8;">XVIIIᵉ siècle — Les Lumières</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- HUME --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f0f5f8, #e2eef5); border:1px solid #5088b0; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#1a4a68; margin-bottom:4px;">David Hume</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1711 — 1776</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Empirisme radical, critique de la causalité et scepticisme modéré.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #5088b0; padding-top:8px;"> → [[Philosophie/Vocabulaire/David Hume|Vocabulaire]] </div> </td> <!-- KANT --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #eef2f8, #e0e8f2); border:1px solid #6080a8; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#2a3860; margin-bottom:4px;">Emmanuel Kant</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1724 — 1804</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Révolution copernicienne, critique de la raison et impératif catégorique.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #6080a8; padding-top:8px;"> → [[Philosophie/Vocabulaire/Kant|Vocabulaire]]<br/> → [[Commentaire de la Fondation de la métaphysique des mœurs|Fondation de la métaphysique des mœurs]] </div> </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : PHILOSOPHIE CONTEMPORAINE ═══════════════════════════════════════════════════════════════════ --> <div id="Contemporaine" style="margin: 35px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #9070b0;"> <span style="font-size: 1.15em; font-weight: 700; color: #5a3d7a; letter-spacing: 0.02em;">PHILOSOPHIE CONTEMPORAINE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">XIXᵉ — XXIᵉ siècle</span> </div> <!-- Sous-section : XIXe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #6a5080; margin: 20px 0 12px 0; padding-left: 10px; border-left: 3px solid #c0a8d0;">XIXᵉ siècle</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- NIETZSCHE --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f8f0f2, #f0e4e8); border:1px solid #a87080; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#5a2838; margin-bottom:4px;">[[Philosophie/Nietzsche|Friedrich Nietzsche]]</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1844 — 1900</div> <div style="font-size:0.88em; color:#555; line-height:1.6;">Critique de la morale, volonté de puissance, mort de Dieu et éternel retour.</div> </td> <!-- Placeholder pour futurs philosophes du XIXe --> <td style="width:50%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:16px 18px; color: #999; font-size: 0.9em; text-align: center;"> <div style="margin: 15px 0;">''À venir : Hegel, Marx, Kierkegaard…''</div> </td> </tr> </table> <!-- Sous-section : XXe siècle --> <div style="font-size: 0.9em; font-weight: 600; color: #6a5080; margin: 25px 0 12px 0; padding-left: 10px; border-left: 3px solid #c0a8d0;">XXᵉ siècle</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- HEIDEGGER --> <td style="width:50%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f3eef8, #e8e0f2); border:1px solid #8068a0; border-radius:8px; padding:16px 18px; overflow:hidden;"> <div style="font-weight:bold; font-size:1.05em; color:#3a2858; margin-bottom:4px;">Martin Heidegger</div> <div style="font-size:0.8em; color:#888; margin-bottom:8px;">1889 — 1976</div> <div style="font-size:0.88em; color:#555; line-height:1.6; margin-bottom:10px;">Question de l'être, ''Dasein'', temporalité et critique de la métaphysique.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #8068a0; padding-top:8px;"> → [[Être et Temps]] </div> </td> <!-- Placeholder pour futurs philosophes du XXe --> <td style="width:50%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:16px 18px; color: #999; font-size: 0.9em; text-align: center;"> <div style="margin: 15px 0;">''À venir : Sartre, Merleau-Ponty, Wittgenstein…''</div> </td> </tr> </table> <!-- Sous-section : Courants --> <div style="font-size: 0.9em; font-weight: 600; color: #6a5080; margin: 25px 0 12px 0; padding-left: 10px; border-left: 3px solid #c0a8d0;">Courants philosophiques</div> <table style="width:100%; border-collapse:separate; border-spacing:12px;"> <tr> <!-- PHILOSOPHIE ANALYTIQUE --> <td style="width:33%; vertical-align:top; position:relative; background:linear-gradient(180deg, #f0f2f8, #e5e8f2); border:1px solid #7080a0; border-radius:8px; padding:14px 16px; overflow:hidden;"> <div style="font-weight:bold; font-size:1em; color:#2a3860; margin-bottom:6px;">🔬 [[Philosophie analytique]]</div> <div style="font-size:0.85em; color:#555; line-height:1.5; margin-bottom:10px;">Analyse logique du langage et clarification des concepts.</div> <div style="font-size:0.85em; color:#666; border-top:1px dashed #8068a0; padding-top:8px;"> → [[Ignorance: A Case for Scepticism]] </div> </td> <!-- Placeholders pour futurs courants --> <td style="width:33%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:14px 16px; color: #999; font-size: 0.85em; text-align: center;"> ''À venir : Phénoménologie'' </td> <td style="width:33%; vertical-align:top; background: #fafafa; border: 1px dashed #ccc; border-radius:8px; padding:14px 16px; color: #999; font-size: 0.85em; text-align: center;"> ''À venir : Existentialisme'' </td> </tr> </table> <!-- ═══════════════════════════════════════════════════════════════════ SECTION : BIBLIOGRAPHIE ═══════════════════════════════════════════════════════════════════ --> <div id="Bibliographie" style="margin: 35px 0 15px 0; padding-bottom: 6px; border-bottom: 2px solid #888;"> <span style="font-size: 1.15em; font-weight: 700; color: #444; letter-spacing: 0.02em;">BIBLIOGRAPHIE</span> <span style="font-size: 0.85em; color: #999; margin-left: 12px;">Ouvrages de référence</span> </div> <div style="background: linear-gradient(180deg, #fafafa, #f5f5f5); border: 1px solid #e0e0e0; border-radius: 8px; padding: 18px 22px; font-size: 0.92em; line-height: 1.8; color: #444;"> <div style="margin-bottom: 8px;">📚 <strong>PRADEAU</strong>, Jean-François (dir.), ''Histoire de la philosophie'', Éditions du Seuil, 2009</div> <div style="margin-bottom: 8px;">📚 <strong>CHÂTELET</strong>, François, ''Histoire de la philosophie'', 8 tomes, Paris, Hachette-Pluriel, 1999-2000</div> <div style="margin-bottom: 8px;">📚 <strong>BRÉHIER</strong>, Émile, ''Histoire de la philosophie'', PUF, Quadrige, 2004, {{ISBN|2-13054-396-0}} — [http://classiques.uqac.ca/classiques/brehier_emile/brehier_emile.html Texte en ligne]</div> <div>📚 <strong>ZARADER</strong>, Jean-Pierre (dir.), ''Le vocabulaire des philosophes'', 5 tomes, Paris, Ellipses, 2002-2006</div> </div> <!-- ═══════════════════════════════════════════════════════════════════ FOOTER : RESSOURCES ═══════════════════════════════════════════════════════════════════ --> <div style="margin-top: 25px; background: linear-gradient(180deg, #f5f5f3, #eaeae8); border: 1px solid #d8d8d5; border-radius: 8px; padding: 16px 20px;"> <div style="font-size: 0.8em; font-weight: 700; color: #555; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 10px;">Voir aussi</div> <div style="font-size: 0.9em; color: #444; line-height: 1.7;"> → [[Philosophie|Portail Philosophie]] &nbsp;•&nbsp; → [[Dictionnaire de philosophie|Dictionnaire des concepts]] &nbsp;•&nbsp; → [[Manuel de terminale de philosophie|Manuel de Terminale]] &nbsp;•&nbsp; → [[w:Histoire de la philosophie|Wikipédia]] </div> </div> [[Catégorie:Discipline philosophique]] [[Catégorie:Histoire|Histoire de la philosophie]] [[Catégorie:Histoire de la philosophie|*]] lcq3x3xul0hah2wqsrnmm4bqqpim6z7 Livre de cuisine/Potée lorraine 0 27546 763964 687306 2026-04-18T18:13:47Z DavidL 1746 763964 wikitext text/x-wiki {{livre de cuisine}} {{ébauche|cuisine|Lorraine}} La '''potée lorraine''' est un plat, appelé aussi potaye, elle réunit viandes et légumes pour les servir en un plat complet : soupe avec tranches de pain grillé, puis légumes surmontés de cochonnailles. == Ingrédients == Pour 4 personnes : * 1 jarret de {{i|porc}} demi-sel, * 2 {{i|saucisse}}s fumées, * 100 g de {{i|lardon}}s, * 250 g de {{i|haricot blanc|haricots blancs}}, * 4 {{i|carotte}}s, * 4 {{i|navet}}s, * 4 petits {{i|poireau}}x, * 6 {{i|pomme de terre|pommes de terre}}, * 1 petit {{i|chou frisé}}, * 1 {{i|'=oui|oignon}} piqué d'un {{i|clou de girofle}}, * 1 {{i|bouquet garni}}, * 2 cuillerées à soupe d'{{i|'=oui|huile}}, * sel et {{i|poivre}}. == Préparation == {| | Préparation : || 30 min |- | Cuisson : || 1h50 |- | Attente : || 12h |} # Trempez Les haricots dans l'eau froide (12 h). # Mettez-les avec le jarret dans un faitout avec l'oignon et le bouquet garni. # Couvrez d'eau, portez à ébullition, puis écumez. # Laissez cuire à l'eau frémissante 1h50. # Pelez les carottes et les navets. # Nettoyez les poireaux, liez-les. # Ajoutez ces légumes, les saucisses et les lardons au bout d'1 h de cuisson. # Blanchissez les feuilles de chou 2 min dans de l'eau bouillante salée. # Épluchez les pommes de terre. # Faites-les cuire 20 min. # Servez la potée chaude avec les pommes de terre et arrosez de bouillon. [[Catégorie:Cuisine lorraine]] [[es:Potée Lorraine]] 4c1jzswjzcfmbu7nvnee06anijezz30 Pour lire Platon 0 29731 763977 758711 2026-04-19T06:14:05Z PandaMystique 119061 763977 wikitext text/x-wiki == Introduction == {{version imprimable}} {{autres projets|commons=Category:Philosophy}} Ce livre propose de fournir au lecteur les connaissances nécessaires à la compréhension des œuvres de Platon. Il s'adresse aux débutants (par exemple, des élèves de terminale, mais également tous ceux qui souhaitent acquérir une culture générale ou découvrir ce philosophe sans avoir de culture philosophique particulière) et aux lecteurs intermédiaires ou avancés, c'est-à-dire à des lecteurs qui ont lu quelques dialogues parmi les moins difficiles et qui sont éventuellement familiers de quelques-unes des notions principales de Platon. Ce livre ne s'adresse pas aux spécialistes de Platon qui ne trouveront ici rien qu'ils ne connaissent déjà par la fréquentation assidue des dialogues et des commentateurs. == Table des matières == === '''[[/Premiers pas/]]''' === * [[Pour lire Platon/Platon et les mythes#Qui est Platon ?|1 Qui est Platon ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Platon a-t-il écrit ?|2 Pourquoi Platon a-t-il écrit ?]] * [[Pour lire Platon/Platon et les mythes#Qu'a-t-il écrit ?|3 Qu'a-t-il écrit ?]] * [[Pour lire Platon/Platon et les mythes#Dans quel ordre Platon a-t-il écrit ses dialogues ?|4 Dans quel ordre Platon a-t-il écrit ses dialogues ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?|5 Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?]] * [[Pour lire Platon/Platon et les mythes#Qui sont les personnages des dialogues de Platon ?|6 Qui sont les personnages des dialogues de Platon ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?|7 Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?]] * [[Pour lire Platon/Platon et les mythes#De quoi Platon parle-t-il dans ses dialogues ?|8 De quoi Platon parle-t-il dans ses dialogues ?]] * [[Pour lire Platon/Platon et les mythes#Les dialogues de Platon forment-ils un système philosophique ?|9 Les dialogues de Platon forment-ils un système philosophique ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi lire un auteur qui est mort il y a 24 siècles ?|10 Pourquoi lire un auteur qui est mort il y a 24 siècles ?]] === '''[[/Conseils pour la lecture|Conseils pour la lecture]]''' === *<small>Quelques conseils élémentaires avant de se lancer</small> * [[Pour lire Platon/Conseils pour la lecture#Quelles traductions choisir ?|1 Quelles traductions choisir ?]] * [[Pour lire Platon/Conseils pour la lecture#Faut-il apprendre le grec pour bien comprendre Platon ?|2 Faut-il apprendre le grec pour bien comprendre Platon ?]] * [[Pour lire Platon/Conseils pour la lecture#Par quels dialogues commencer ?|3 Par quels dialogues commencer ?]] * [[Pour lire Platon/Conseils pour la lecture#Comment tirer profit de la lecture des dialogues ?|4 Comment tirer profit de la lecture des dialogues ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels outils utiliser au cours de la lecture ?|5 Quels outils utiliser au cours de la lecture ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels livres lire sur Platon ?|6 Quels livres lire sur Platon ?]] === [[Pour lire Platon/Introduction par les dialogues|Introduction par les dialogues]] === === [[Pour lire Platon/Introduction par les mythes|Introduction par les mythes]] === === Lecture des dialogues === * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] [[Catégorie:Histoire de la philosophie]][[Catégorie:Philosophe]] {{AutoCat}} __NOTOC__ 00tfnukzsrr00newuvtxh5ihgzq2l3h 763979 763977 2026-04-19T06:16:22Z PandaMystique 119061 /* Introduction par les dialogues */ 763979 wikitext text/x-wiki == Introduction == {{version imprimable}} {{autres projets|commons=Category:Philosophy}} Ce livre propose de fournir au lecteur les connaissances nécessaires à la compréhension des œuvres de Platon. Il s'adresse aux débutants (par exemple, des élèves de terminale, mais également tous ceux qui souhaitent acquérir une culture générale ou découvrir ce philosophe sans avoir de culture philosophique particulière) et aux lecteurs intermédiaires ou avancés, c'est-à-dire à des lecteurs qui ont lu quelques dialogues parmi les moins difficiles et qui sont éventuellement familiers de quelques-unes des notions principales de Platon. Ce livre ne s'adresse pas aux spécialistes de Platon qui ne trouveront ici rien qu'ils ne connaissent déjà par la fréquentation assidue des dialogues et des commentateurs. == Table des matières == === '''[[/Premiers pas/]]''' === * [[Pour lire Platon/Platon et les mythes#Qui est Platon ?|1 Qui est Platon ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Platon a-t-il écrit ?|2 Pourquoi Platon a-t-il écrit ?]] * [[Pour lire Platon/Platon et les mythes#Qu'a-t-il écrit ?|3 Qu'a-t-il écrit ?]] * [[Pour lire Platon/Platon et les mythes#Dans quel ordre Platon a-t-il écrit ses dialogues ?|4 Dans quel ordre Platon a-t-il écrit ses dialogues ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?|5 Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?]] * [[Pour lire Platon/Platon et les mythes#Qui sont les personnages des dialogues de Platon ?|6 Qui sont les personnages des dialogues de Platon ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?|7 Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?]] * [[Pour lire Platon/Platon et les mythes#De quoi Platon parle-t-il dans ses dialogues ?|8 De quoi Platon parle-t-il dans ses dialogues ?]] * [[Pour lire Platon/Platon et les mythes#Les dialogues de Platon forment-ils un système philosophique ?|9 Les dialogues de Platon forment-ils un système philosophique ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi lire un auteur qui est mort il y a 24 siècles ?|10 Pourquoi lire un auteur qui est mort il y a 24 siècles ?]] === '''[[/Conseils pour la lecture|Conseils pour la lecture]]''' === *<small>Quelques conseils élémentaires avant de se lancer</small> * [[Pour lire Platon/Conseils pour la lecture#Quelles traductions choisir ?|1 Quelles traductions choisir ?]] * [[Pour lire Platon/Conseils pour la lecture#Faut-il apprendre le grec pour bien comprendre Platon ?|2 Faut-il apprendre le grec pour bien comprendre Platon ?]] * [[Pour lire Platon/Conseils pour la lecture#Par quels dialogues commencer ?|3 Par quels dialogues commencer ?]] * [[Pour lire Platon/Conseils pour la lecture#Comment tirer profit de la lecture des dialogues ?|4 Comment tirer profit de la lecture des dialogues ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels outils utiliser au cours de la lecture ?|5 Quels outils utiliser au cours de la lecture ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels livres lire sur Platon ?|6 Quels livres lire sur Platon ?]] === [[Pour lire Platon/Introduction par les dialogues|Introduction par les dialogues]] === * [[Pour lire Platon/Guide des dialogues/Introduction|Introduction]] === [[Pour lire Platon/Introduction par les mythes|Introduction par les mythes]] === === Lecture des dialogues === * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] [[Catégorie:Histoire de la philosophie]][[Catégorie:Philosophe]] {{AutoCat}} __NOTOC__ fzrhr5xopptrfw94frbbr4fpgz8ypnb 763980 763979 2026-04-19T06:19:52Z PandaMystique 119061 /* Introduction par les dialogues */ 763980 wikitext text/x-wiki == Introduction == {{version imprimable}} {{autres projets|commons=Category:Philosophy}} Ce livre propose de fournir au lecteur les connaissances nécessaires à la compréhension des œuvres de Platon. Il s'adresse aux débutants (par exemple, des élèves de terminale, mais également tous ceux qui souhaitent acquérir une culture générale ou découvrir ce philosophe sans avoir de culture philosophique particulière) et aux lecteurs intermédiaires ou avancés, c'est-à-dire à des lecteurs qui ont lu quelques dialogues parmi les moins difficiles et qui sont éventuellement familiers de quelques-unes des notions principales de Platon. Ce livre ne s'adresse pas aux spécialistes de Platon qui ne trouveront ici rien qu'ils ne connaissent déjà par la fréquentation assidue des dialogues et des commentateurs. == Table des matières == === '''[[/Premiers pas/]]''' === * [[Pour lire Platon/Platon et les mythes#Qui est Platon ?|1 Qui est Platon ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Platon a-t-il écrit ?|2 Pourquoi Platon a-t-il écrit ?]] * [[Pour lire Platon/Platon et les mythes#Qu'a-t-il écrit ?|3 Qu'a-t-il écrit ?]] * [[Pour lire Platon/Platon et les mythes#Dans quel ordre Platon a-t-il écrit ses dialogues ?|4 Dans quel ordre Platon a-t-il écrit ses dialogues ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?|5 Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?]] * [[Pour lire Platon/Platon et les mythes#Qui sont les personnages des dialogues de Platon ?|6 Qui sont les personnages des dialogues de Platon ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?|7 Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?]] * [[Pour lire Platon/Platon et les mythes#De quoi Platon parle-t-il dans ses dialogues ?|8 De quoi Platon parle-t-il dans ses dialogues ?]] * [[Pour lire Platon/Platon et les mythes#Les dialogues de Platon forment-ils un système philosophique ?|9 Les dialogues de Platon forment-ils un système philosophique ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi lire un auteur qui est mort il y a 24 siècles ?|10 Pourquoi lire un auteur qui est mort il y a 24 siècles ?]] === '''[[/Conseils pour la lecture|Conseils pour la lecture]]''' === *<small>Quelques conseils élémentaires avant de se lancer</small> * [[Pour lire Platon/Conseils pour la lecture#Quelles traductions choisir ?|1 Quelles traductions choisir ?]] * [[Pour lire Platon/Conseils pour la lecture#Faut-il apprendre le grec pour bien comprendre Platon ?|2 Faut-il apprendre le grec pour bien comprendre Platon ?]] * [[Pour lire Platon/Conseils pour la lecture#Par quels dialogues commencer ?|3 Par quels dialogues commencer ?]] * [[Pour lire Platon/Conseils pour la lecture#Comment tirer profit de la lecture des dialogues ?|4 Comment tirer profit de la lecture des dialogues ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels outils utiliser au cours de la lecture ?|5 Quels outils utiliser au cours de la lecture ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels livres lire sur Platon ?|6 Quels livres lire sur Platon ?]] === [[Pour lire Platon/Introduction par les dialogues|Introduction par les dialogues]] === * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] * [[Pour lire Platon/Guide des dialogues/Introduction|Introduction]] === [[Pour lire Platon/Introduction par les mythes|Introduction par les mythes]] === === Lecture des dialogues === * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] [[Catégorie:Histoire de la philosophie]][[Catégorie:Philosophe]] {{AutoCat}} __NOTOC__ nv2bp5hue24tl0atduv54mwnk1d7w5p 763981 763980 2026-04-19T06:21:27Z PandaMystique 119061 /* Lecture des dialogues */ 763981 wikitext text/x-wiki == Introduction == {{version imprimable}} {{autres projets|commons=Category:Philosophy}} Ce livre propose de fournir au lecteur les connaissances nécessaires à la compréhension des œuvres de Platon. Il s'adresse aux débutants (par exemple, des élèves de terminale, mais également tous ceux qui souhaitent acquérir une culture générale ou découvrir ce philosophe sans avoir de culture philosophique particulière) et aux lecteurs intermédiaires ou avancés, c'est-à-dire à des lecteurs qui ont lu quelques dialogues parmi les moins difficiles et qui sont éventuellement familiers de quelques-unes des notions principales de Platon. Ce livre ne s'adresse pas aux spécialistes de Platon qui ne trouveront ici rien qu'ils ne connaissent déjà par la fréquentation assidue des dialogues et des commentateurs. == Table des matières == === '''[[/Premiers pas/]]''' === * [[Pour lire Platon/Platon et les mythes#Qui est Platon ?|1 Qui est Platon ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Platon a-t-il écrit ?|2 Pourquoi Platon a-t-il écrit ?]] * [[Pour lire Platon/Platon et les mythes#Qu'a-t-il écrit ?|3 Qu'a-t-il écrit ?]] * [[Pour lire Platon/Platon et les mythes#Dans quel ordre Platon a-t-il écrit ses dialogues ?|4 Dans quel ordre Platon a-t-il écrit ses dialogues ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?|5 Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?]] * [[Pour lire Platon/Platon et les mythes#Qui sont les personnages des dialogues de Platon ?|6 Qui sont les personnages des dialogues de Platon ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?|7 Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?]] * [[Pour lire Platon/Platon et les mythes#De quoi Platon parle-t-il dans ses dialogues ?|8 De quoi Platon parle-t-il dans ses dialogues ?]] * [[Pour lire Platon/Platon et les mythes#Les dialogues de Platon forment-ils un système philosophique ?|9 Les dialogues de Platon forment-ils un système philosophique ?]] * [[Pour lire Platon/Platon et les mythes#Pourquoi lire un auteur qui est mort il y a 24 siècles ?|10 Pourquoi lire un auteur qui est mort il y a 24 siècles ?]] === '''[[/Conseils pour la lecture|Conseils pour la lecture]]''' === *<small>Quelques conseils élémentaires avant de se lancer</small> * [[Pour lire Platon/Conseils pour la lecture#Quelles traductions choisir ?|1 Quelles traductions choisir ?]] * [[Pour lire Platon/Conseils pour la lecture#Faut-il apprendre le grec pour bien comprendre Platon ?|2 Faut-il apprendre le grec pour bien comprendre Platon ?]] * [[Pour lire Platon/Conseils pour la lecture#Par quels dialogues commencer ?|3 Par quels dialogues commencer ?]] * [[Pour lire Platon/Conseils pour la lecture#Comment tirer profit de la lecture des dialogues ?|4 Comment tirer profit de la lecture des dialogues ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels outils utiliser au cours de la lecture ?|5 Quels outils utiliser au cours de la lecture ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels livres lire sur Platon ?|6 Quels livres lire sur Platon ?]] === [[Pour lire Platon/Introduction par les dialogues|Introduction par les dialogues]] === * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] * [[Pour lire Platon/Guide des dialogues/Introduction|Introduction]] === [[Pour lire Platon/Introduction par les mythes|Introduction par les mythes]] === === Lecture des dialogues === * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] === [[Pour lire Platon/Vocabulaire|Vocabulaire]] === [[Catégorie:Histoire de la philosophie]][[Catégorie:Philosophe]] {{AutoCat}} __NOTOC__ 6442f3szk3w9aqys1it9ql9odu4b7b1 763984 763981 2026-04-19T06:42:26Z PandaMystique 119061 /* /Premiers pas/ */ 763984 wikitext text/x-wiki == Introduction == {{version imprimable}} {{autres projets|commons=Category:Philosophy}} Ce livre propose de fournir au lecteur les connaissances nécessaires à la compréhension des œuvres de Platon. Il s'adresse aux débutants (par exemple, des élèves de terminale, mais également tous ceux qui souhaitent acquérir une culture générale ou découvrir ce philosophe sans avoir de culture philosophique particulière) et aux lecteurs intermédiaires ou avancés, c'est-à-dire à des lecteurs qui ont lu quelques dialogues parmi les moins difficiles et qui sont éventuellement familiers de quelques-unes des notions principales de Platon. Ce livre ne s'adresse pas aux spécialistes de Platon qui ne trouveront ici rien qu'ils ne connaissent déjà par la fréquentation assidue des dialogues et des commentateurs. == Table des matières == === '''[[/Premiers pas/]]''' === * [[Pour lire Platon/Premiers pas#Qui est Platon ?|1 Qui est Platon ?]] * [[Pour lire Platon/Premiers pas#Pourquoi Platon a-t-il écrit ?|2 Pourquoi Platon a-t-il écrit ?]] * [[Pour lire Platon/Premiers pas#Qu'a-t-il écrit ?|3 Qu'a-t-il écrit ?]] * [[Pour lire Platon/Premiers pas#Dans quel ordre Platon a-t-il écrit ses dialogues ?|4 Dans quel ordre Platon a-t-il écrit ses dialogues ?]] * [[Pour lire Platon/Premiers pas#Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?|5 Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?]] * [[Pour lire Platon/Premiers pas#Qui sont les personnages des dialogues de Platon ?|6 Qui sont les personnages des dialogues de Platon ?]] * [[Pour lire Platon/Premiers pas#Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?|7 Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?]] * [[Pour lire Platon/Premiers pas#De quoi Platon parle-t-il dans ses dialogues ?|8 De quoi Platon parle-t-il dans ses dialogues ?]] * [[Pour lire Platon/Premiers pas#Les dialogues de Platon forment-ils un système philosophique ?|9 Les dialogues de Platon forment-ils un système philosophique ?]] * [[Pour lire Platon/Premiers pas#Pourquoi lire un auteur qui est mort il y a 24 siècles ?|10 Pourquoi lire un auteur qui est mort il y a 24 siècles ?]] === '''[[/Conseils pour la lecture|Conseils pour la lecture]]''' === *<small>Quelques conseils élémentaires avant de se lancer</small> * [[Pour lire Platon/Conseils pour la lecture#Quelles traductions choisir ?|1 Quelles traductions choisir ?]] * [[Pour lire Platon/Conseils pour la lecture#Faut-il apprendre le grec pour bien comprendre Platon ?|2 Faut-il apprendre le grec pour bien comprendre Platon ?]] * [[Pour lire Platon/Conseils pour la lecture#Par quels dialogues commencer ?|3 Par quels dialogues commencer ?]] * [[Pour lire Platon/Conseils pour la lecture#Comment tirer profit de la lecture des dialogues ?|4 Comment tirer profit de la lecture des dialogues ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels outils utiliser au cours de la lecture ?|5 Quels outils utiliser au cours de la lecture ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels livres lire sur Platon ?|6 Quels livres lire sur Platon ?]] === [[Pour lire Platon/Introduction par les dialogues|Introduction par les dialogues]] === * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] * [[Pour lire Platon/Guide des dialogues/Introduction|Introduction]] === [[Pour lire Platon/Introduction par les mythes|Introduction par les mythes]] === === Lecture des dialogues === * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] === [[Pour lire Platon/Vocabulaire|Vocabulaire]] === [[Catégorie:Histoire de la philosophie]][[Catégorie:Philosophe]] {{AutoCat}} __NOTOC__ klua6x13kmfl0lcaikolzmcr4v029n6 763992 763984 2026-04-19T08:05:08Z PandaMystique 119061 763992 wikitext text/x-wiki == Introduction == {{version imprimable}} Ce livre propose de fournir au lecteur les connaissances nécessaires à la compréhension des œuvres de Platon. Il s'adresse aux débutants (par exemple, des élèves de terminale, mais également tous ceux qui souhaitent acquérir une culture générale ou découvrir ce philosophe sans avoir de culture philosophique particulière) et aux lecteurs intermédiaires ou avancés, c'est-à-dire à des lecteurs qui ont lu quelques dialogues parmi les moins difficiles et qui sont éventuellement familiers de quelques-unes des notions principales de Platon. Ce livre ne s'adresse pas aux spécialistes de Platon qui ne trouveront ici rien qu'ils ne connaissent déjà par la fréquentation assidue des dialogues et des commentateurs. == Table des matières == === '''[[/Premiers pas/]]''' === * [[Pour lire Platon/Premiers pas#Qui est Platon ?|1 Qui est Platon ?]] * [[Pour lire Platon/Premiers pas#Pourquoi Platon a-t-il écrit ?|2 Pourquoi Platon a-t-il écrit ?]] * [[Pour lire Platon/Premiers pas#Qu'a-t-il écrit ?|3 Qu'a-t-il écrit ?]] * [[Pour lire Platon/Premiers pas#Dans quel ordre Platon a-t-il écrit ses dialogues ?|4 Dans quel ordre Platon a-t-il écrit ses dialogues ?]] * [[Pour lire Platon/Premiers pas#Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?|5 Pourquoi Platon a-t-il écrit des dialogues et pas des traités ?]] * [[Pour lire Platon/Premiers pas#Qui sont les personnages des dialogues de Platon ?|6 Qui sont les personnages des dialogues de Platon ?]] * [[Pour lire Platon/Premiers pas#Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?|7 Pourquoi Socrate est-il le personnage principal des dialogues de Platon ?]] * [[Pour lire Platon/Premiers pas#De quoi Platon parle-t-il dans ses dialogues ?|8 De quoi Platon parle-t-il dans ses dialogues ?]] * [[Pour lire Platon/Premiers pas#Les dialogues de Platon forment-ils un système philosophique ?|9 Les dialogues de Platon forment-ils un système philosophique ?]] * [[Pour lire Platon/Premiers pas#Pourquoi lire un auteur qui est mort il y a 24 siècles ?|10 Pourquoi lire un auteur qui est mort il y a 24 siècles ?]] === '''[[/Conseils pour la lecture|Conseils pour la lecture]]''' === *<small>Quelques conseils élémentaires avant de se lancer</small> * [[Pour lire Platon/Conseils pour la lecture#Quelles traductions choisir ?|1 Quelles traductions choisir ?]] * [[Pour lire Platon/Conseils pour la lecture#Faut-il apprendre le grec pour bien comprendre Platon ?|2 Faut-il apprendre le grec pour bien comprendre Platon ?]] * [[Pour lire Platon/Conseils pour la lecture#Par quels dialogues commencer ?|3 Par quels dialogues commencer ?]] * [[Pour lire Platon/Conseils pour la lecture#Comment tirer profit de la lecture des dialogues ?|4 Comment tirer profit de la lecture des dialogues ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels outils utiliser au cours de la lecture ?|5 Quels outils utiliser au cours de la lecture ?]] * [[Pour lire Platon/Conseils pour la lecture#Quels livres lire sur Platon ?|6 Quels livres lire sur Platon ?]] === [[Pour lire Platon/Introduction par les dialogues|Introduction par les dialogues]] === * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] * [[Pour lire Platon/Guide des dialogues/Introduction|Introduction]] === [[Pour lire Platon/Introduction par les mythes|Introduction par les mythes]] === === Lecture des dialogues === * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] === [[Pour lire Platon/Vocabulaire|Vocabulaire]] === [[Catégorie:Histoire de la philosophie]][[Catégorie:Philosophe]] {{AutoCat}} __NOTOC__ 01fc0xrgytk667dh865t2sswt4xrzcu Pour lire Platon/Platon et les mythes 0 29734 763983 758710 2026-04-19T06:41:39Z PandaMystique 119061 Contenu remplacé par « {{suppression}} » 763983 wikitext text/x-wiki {{suppression}} 5wl0gm38rtf3k5sgq06d2rnqi61p54r Pour lire Platon/Introduction par les dialogues 0 29778 763974 679363 2026-04-19T06:10:39Z PandaMystique 119061 763974 wikitext text/x-wiki {{Remarque|Ce chapitre est actuellement en cours de rédaction et son contenu n'est pour le moment pas au point.}} Le premier chapitre nous a permis de faire connaissance avec Platon. Nous allons à présent faire connaissance avec sa pensée. Voici comment nous organiserons ce chapitre. Chaque section abordera un domaine à travers une série de questions. Pour chacune de ces questions, nous donnerons un exposé succinct du contexte, une réponse détaillée et parfois une discussion argumentée des thèses de Platon et de leur actualité. Enfin, chaque section proposera la lecture de quelques extraits des dialogues. Nous prenons le parti de faire une première présentation de la pensée de Platon d'une manière qui pourra paraître trop systématique et artificielle. Cette manière n'a cependant pour objectif que d'éviter au débutant d'être noyé sous la multiplicité des sujets traités dans les dialogues. Selon les cas, les réponses pourront être très détaillées, mais nous privilégierons toujours une rédaction utilisant des mots familiers, plutôt qu'un vocabulaire philosophique spécifique. En ce sens, nous ne rechercherons pas l'exactitude absolue, mais la compréhension des problèmes soulevés. Les discussions des thèses de Platon auront pour but d'élargir la réflexion. Comme pour le premier chapitre, la liste des questions proposées n'est pas close. {{Pour lire Platon}} == Le philosophe == === Qu'est-ce que l'amour de la sagesse ? === '''Lecture conseillée''' : ''Phèdre'', ''Le Banquet'' '''Contexte'''<br/> L'étymologie du mot ''philosophie'' est sans doute connue par un très grande nombre de personnes : la philosophie est l'amour de la sagesse ou du savoir. Mais, quoique connue, cette signification n'étonne peut-être pas autant qu'elle le devrait. La lecture de Platon va nous montrer de quelle manière cet amour peut nous paraître déroutant, voir nous choquer. Tout d'abord, être sage, ou connaître, semble se rapporter à un état intellectuel. Pour Platon, il s'agit de la contemplation des réalités vraies. Or, il ne va pas de soi que l'on puisse dire qu'une réalité qui à avoir avec nos facultés intellectuelles puisse susciter une affection, et encore moins une affection tel qu'un désir. On peut ressentir de la joie à trouver une vérité, mais ce sentiment n'est pas ce qui fait la vérité. À plus forte raison, il est étrange que le désir amoureux soit non pas seulement dirigé vers le savoir, mais que ce savoir ne soit pas possible sans lui. Mais ce n'est pas la seule raison de s'étonner. Le désir dont parle Platon, c'est l'amour, terme qui comprend le désir sexuel. Or Platon, d'une part, fait référence de manière explicite à l'attirance sexuelle, mais, de plus, ne l'utilise pas comme une métaphore. L'amour de la sagesse n'est pas, ou pas seulement, une expression destinée à nous faire comprendre l'aspiration du philosophe à l'image du désir : la philosophie est véritablement ce désir sexuel, mais dont l'objet a été changé. On trouvera donc dans les dialogues des descriptions du désir philosophique en des termes sexuels des plus explicites. Ces descriptions concernent tant la jouissance sexuelle que la sexualité en tant que fonction de reproduction, ce qui inclut en particulier la fécondation et l'accouchement. Comment Platon conçoit-il ce rapport de la sexualité et de la philosophie et pourquoi fait-il ce rapprochement ? === Qu'est-ce que savoir ? === === Quelle est la place du philosophe dans la cité ? === '''Lecture conseillée''' : ''Apologie de Socrate'' '''Contexte'''<br/> Socrate prononce plusieurs discours devant les juges d'Athènes. Il est en effet accusé de corrompre la jeunesse et d'introduire de nouvelles divinités. Pourtant le philosophe passait son temps à interroger les gens et n'enseignait aucune doctrine en particulier, mais pensait avoir une activité politique de première importance en soumettant ces concitoyens à l'épreuve de ses objections. Qu'est-ce qui fait que le philosophe est malvenu dans la cité et y a-t-il une place s'il risque la mort en exerçant son activité ? == La vie éthique == === Qu'est-ce que l'âme ? === === Qu'est-ce que se connaître soi-même ? === === Qu'est-ce que la vertu ? === === Quel est le bien suprême de la vie ? === ''Philèbe'' === Doit-on craindre la mort ? === '''Lecture conseillée''' : ''Phédon'' '''Contexte'''<br/> Socrate s'entretient avec ses proches le jour de sa mort. L'agitation de ceux-ci contraste avec le calme du philosophe ; ils sont en effet bouleversés à l'idée que la mort est l'anéantissement de la personne et est donc une perte inestimable que rien ne peut consoler. Pourquoi Socrate pense-t-il que la mort n'est pas à craindre ? L'attitude de Socrate à l'égard de la mort est tout aussi essentielle pour la philosophie que son refus de désobéir aux lois : si Socrate se mettait à se lamenter de sa mort prochaine, quelle serait donc le sérieux d'une pratique qui prétend apprendre à vivre en prenant soin de la destinée de l'âme ? La vie philosophique ne serait qu'une grande hypocrisie. == La politique == === Qu'est-ce que la politique ? === === Qu'est-ce qu'une cité juste ? === ''La République'' === Pourquoi agir selon la justice, si l'on peut tirer profit de l'injustice ? === ''La République'' === Pourquoi obéir aux lois ? === '''Lecture conseillée''' : ''Criton'' '''Contexte'''<br/> Quelques jours avant son exécution, Socrate reçoit la visite en prison d'un vieil ami. Celui-ci lui propose de s'évader. Socrate refuse, et lui explique pourquoi il se doit de se soumettre à sa condamnation. Pourquoi Socrate choisit-il d'obéir à la loi, alors que cette obéissance le conduit à une mort injuste ? == L'art == === [[Pour lire Platon/Faut-il contrôler l'art ?|Faut-il contrôler l'art ?]] === '''Lecture conseillée''' : ''La République'' '''Contexte'''<br/> Dans ''La République'', Platon donne une très large place à l'examen du rôle que doit être celui du théâtre dans la cité juste. Cet examen est parallèle à l'institution de la philosophie comme principe et comme fin de la cité. Cette place suffit à montrer que Platon fait de l'art, et en particulier de la tragédie, un objet philosophique à part entière. Cela conduit à se demander pourquoi Platon pose, dans un texte sur les rapports entre philosophie et politique, le problème du rôle civique de l'art, d'autant plus que ce texte est ouvertement hostile au théâtre et le présente comme une menace. Si cette hostilité de Platon se manifeste au cours de réflexions portant sur les institutions de la cité, c'est donc que l'art comporte à ses yeux un risque politique. Quelle est donc cette menace ? ([[Pour lire Platon/Faut-il contrôler l'art ?|lire la suite...]]) == Erreurs fréquentes == Afin d'aider le débutant, nous donnons ici une liste d'erreurs d'interprétation fréquentes, erreurs commises également par les philosophes ! Nous ne donnons que des erreurs vraiment évidentes, que le texte même de Platon réfute. === Platon diviserait le monde en deux === Platon diviserait le monde en deux : d'un côté des réalités éternelles et de l'autre des réalités sensibles. Cette interprétation vient sans doute de certaines écoles platoniciennes et du christianisme, du moins dans les hérésies gnostiques. Au XIXeme siècle, Nietzsche l'a reprise à son compte, accusant Platon de dévaloriser le monde sensible. En réalité, pour Platon, il n'y a qu'une seule et unique réalité : les réalités intelligibles. Le monde sensible est formé d'après leur modèle et n'a de réalité qu'en tant qu'il a un rapport avec ces réalités. Autrement dit, la réalité du monde sensible est aussi intelligible. === Platon aurait inventé l'amour platonique === Platon n'a jamais défendu l'abstinence sexuelle : au contraire, le {{VocPlat|Corps|corps}} doit être en bonne santé pour ne pas être un obstacle à la pensée. Cela implique de ne pas se priver des plaisirs sensibles, nourriture, boisson et sexualité. Mais, dans chacun de ces plaisirs, il faut faire preuve de {{VocPlat|Tempérance|tempérance}}, pour éviter l'excès inverse de l'ascétisme. Lorsque Platon évoque le renoncement, dans le cadre de certaines relations, aux plaisirs charnels, il ne parle pas de supprimer le désir sexuel, mais de le reporter sur une réalité plus élevée, objet de l'{{VocPlat|Amour|amour}} philosophique. Cet amour n'est donc pas purement spirituel. L'amour pour un autre être apparaît dans cette conception comme un moyen. En ce sens, il y a bien là quelque chose de l'amour platonique qui est apparu pendant la Renaissance ; mais on ne peut les confondre, car Platon conçoit toujours l'amour comme désir sexuel, jamais comme une pure spiritualité. === L'âme serait composée de parties === Pour Platon, l'âme serait composée d'une partie intellectuelle, d'une partie ardente et d'une partie désirante. En réalité, bien que les traducteurs emploient parfois le mot ''partie'', l'âme est composée de puissances (de capacités ou de facultés) qui sont des activités de l'âme qui se définissent d'après les objets sur lesquels elles s'exercent. === Chaque groupe de la cité juste possèderait une vertu === On représente souvent la cité juste décrite par Platon comme une cité composée de trois classes possédant chacune une vertu : les gouvernants sont sages ; les gardiens sont courageux ; les producteurs sont tempérants. Cette représentation est fausse puisque les gouvernants sont sages, courageux et tempérants, les gardiens sont courageux et tempérants et ils reçoivent une éducation dans le but de devenir sages, et les producteurs tempérants. === La voix intérieure de Socrate serait celle d'un démon === L'expression ''démon de Socrate'' est utilisée par Plutarque. Mais elle n'apparaît nulle part dans les dialogues. Il est donc faux d'affirmer que Socrate entendrait un démon intérieur. Il reste que nous ne savons pas quel statut donner à cette voix qui est qualifiée de signe démonique : il peut tout aussi bien s'agir d'un dialogue de l'âme de Socrate avec elle-même. 33ecka0zik6d933dnxqflhd5506suzb 763975 763974 2026-04-19T06:13:04Z PandaMystique 119061 PandaMystique a déplacé la page [[Pour lire Platon/Introduction par les mythes]] vers [[Pour lire Platon/Introduction par les dialogues]] par-dessus une redirection 763974 wikitext text/x-wiki {{Remarque|Ce chapitre est actuellement en cours de rédaction et son contenu n'est pour le moment pas au point.}} Le premier chapitre nous a permis de faire connaissance avec Platon. Nous allons à présent faire connaissance avec sa pensée. Voici comment nous organiserons ce chapitre. Chaque section abordera un domaine à travers une série de questions. Pour chacune de ces questions, nous donnerons un exposé succinct du contexte, une réponse détaillée et parfois une discussion argumentée des thèses de Platon et de leur actualité. Enfin, chaque section proposera la lecture de quelques extraits des dialogues. Nous prenons le parti de faire une première présentation de la pensée de Platon d'une manière qui pourra paraître trop systématique et artificielle. Cette manière n'a cependant pour objectif que d'éviter au débutant d'être noyé sous la multiplicité des sujets traités dans les dialogues. Selon les cas, les réponses pourront être très détaillées, mais nous privilégierons toujours une rédaction utilisant des mots familiers, plutôt qu'un vocabulaire philosophique spécifique. En ce sens, nous ne rechercherons pas l'exactitude absolue, mais la compréhension des problèmes soulevés. Les discussions des thèses de Platon auront pour but d'élargir la réflexion. Comme pour le premier chapitre, la liste des questions proposées n'est pas close. {{Pour lire Platon}} == Le philosophe == === Qu'est-ce que l'amour de la sagesse ? === '''Lecture conseillée''' : ''Phèdre'', ''Le Banquet'' '''Contexte'''<br/> L'étymologie du mot ''philosophie'' est sans doute connue par un très grande nombre de personnes : la philosophie est l'amour de la sagesse ou du savoir. Mais, quoique connue, cette signification n'étonne peut-être pas autant qu'elle le devrait. La lecture de Platon va nous montrer de quelle manière cet amour peut nous paraître déroutant, voir nous choquer. Tout d'abord, être sage, ou connaître, semble se rapporter à un état intellectuel. Pour Platon, il s'agit de la contemplation des réalités vraies. Or, il ne va pas de soi que l'on puisse dire qu'une réalité qui à avoir avec nos facultés intellectuelles puisse susciter une affection, et encore moins une affection tel qu'un désir. On peut ressentir de la joie à trouver une vérité, mais ce sentiment n'est pas ce qui fait la vérité. À plus forte raison, il est étrange que le désir amoureux soit non pas seulement dirigé vers le savoir, mais que ce savoir ne soit pas possible sans lui. Mais ce n'est pas la seule raison de s'étonner. Le désir dont parle Platon, c'est l'amour, terme qui comprend le désir sexuel. Or Platon, d'une part, fait référence de manière explicite à l'attirance sexuelle, mais, de plus, ne l'utilise pas comme une métaphore. L'amour de la sagesse n'est pas, ou pas seulement, une expression destinée à nous faire comprendre l'aspiration du philosophe à l'image du désir : la philosophie est véritablement ce désir sexuel, mais dont l'objet a été changé. On trouvera donc dans les dialogues des descriptions du désir philosophique en des termes sexuels des plus explicites. Ces descriptions concernent tant la jouissance sexuelle que la sexualité en tant que fonction de reproduction, ce qui inclut en particulier la fécondation et l'accouchement. Comment Platon conçoit-il ce rapport de la sexualité et de la philosophie et pourquoi fait-il ce rapprochement ? === Qu'est-ce que savoir ? === === Quelle est la place du philosophe dans la cité ? === '''Lecture conseillée''' : ''Apologie de Socrate'' '''Contexte'''<br/> Socrate prononce plusieurs discours devant les juges d'Athènes. Il est en effet accusé de corrompre la jeunesse et d'introduire de nouvelles divinités. Pourtant le philosophe passait son temps à interroger les gens et n'enseignait aucune doctrine en particulier, mais pensait avoir une activité politique de première importance en soumettant ces concitoyens à l'épreuve de ses objections. Qu'est-ce qui fait que le philosophe est malvenu dans la cité et y a-t-il une place s'il risque la mort en exerçant son activité ? == La vie éthique == === Qu'est-ce que l'âme ? === === Qu'est-ce que se connaître soi-même ? === === Qu'est-ce que la vertu ? === === Quel est le bien suprême de la vie ? === ''Philèbe'' === Doit-on craindre la mort ? === '''Lecture conseillée''' : ''Phédon'' '''Contexte'''<br/> Socrate s'entretient avec ses proches le jour de sa mort. L'agitation de ceux-ci contraste avec le calme du philosophe ; ils sont en effet bouleversés à l'idée que la mort est l'anéantissement de la personne et est donc une perte inestimable que rien ne peut consoler. Pourquoi Socrate pense-t-il que la mort n'est pas à craindre ? L'attitude de Socrate à l'égard de la mort est tout aussi essentielle pour la philosophie que son refus de désobéir aux lois : si Socrate se mettait à se lamenter de sa mort prochaine, quelle serait donc le sérieux d'une pratique qui prétend apprendre à vivre en prenant soin de la destinée de l'âme ? La vie philosophique ne serait qu'une grande hypocrisie. == La politique == === Qu'est-ce que la politique ? === === Qu'est-ce qu'une cité juste ? === ''La République'' === Pourquoi agir selon la justice, si l'on peut tirer profit de l'injustice ? === ''La République'' === Pourquoi obéir aux lois ? === '''Lecture conseillée''' : ''Criton'' '''Contexte'''<br/> Quelques jours avant son exécution, Socrate reçoit la visite en prison d'un vieil ami. Celui-ci lui propose de s'évader. Socrate refuse, et lui explique pourquoi il se doit de se soumettre à sa condamnation. Pourquoi Socrate choisit-il d'obéir à la loi, alors que cette obéissance le conduit à une mort injuste ? == L'art == === [[Pour lire Platon/Faut-il contrôler l'art ?|Faut-il contrôler l'art ?]] === '''Lecture conseillée''' : ''La République'' '''Contexte'''<br/> Dans ''La République'', Platon donne une très large place à l'examen du rôle que doit être celui du théâtre dans la cité juste. Cet examen est parallèle à l'institution de la philosophie comme principe et comme fin de la cité. Cette place suffit à montrer que Platon fait de l'art, et en particulier de la tragédie, un objet philosophique à part entière. Cela conduit à se demander pourquoi Platon pose, dans un texte sur les rapports entre philosophie et politique, le problème du rôle civique de l'art, d'autant plus que ce texte est ouvertement hostile au théâtre et le présente comme une menace. Si cette hostilité de Platon se manifeste au cours de réflexions portant sur les institutions de la cité, c'est donc que l'art comporte à ses yeux un risque politique. Quelle est donc cette menace ? ([[Pour lire Platon/Faut-il contrôler l'art ?|lire la suite...]]) == Erreurs fréquentes == Afin d'aider le débutant, nous donnons ici une liste d'erreurs d'interprétation fréquentes, erreurs commises également par les philosophes ! Nous ne donnons que des erreurs vraiment évidentes, que le texte même de Platon réfute. === Platon diviserait le monde en deux === Platon diviserait le monde en deux : d'un côté des réalités éternelles et de l'autre des réalités sensibles. Cette interprétation vient sans doute de certaines écoles platoniciennes et du christianisme, du moins dans les hérésies gnostiques. Au XIXeme siècle, Nietzsche l'a reprise à son compte, accusant Platon de dévaloriser le monde sensible. En réalité, pour Platon, il n'y a qu'une seule et unique réalité : les réalités intelligibles. Le monde sensible est formé d'après leur modèle et n'a de réalité qu'en tant qu'il a un rapport avec ces réalités. Autrement dit, la réalité du monde sensible est aussi intelligible. === Platon aurait inventé l'amour platonique === Platon n'a jamais défendu l'abstinence sexuelle : au contraire, le {{VocPlat|Corps|corps}} doit être en bonne santé pour ne pas être un obstacle à la pensée. Cela implique de ne pas se priver des plaisirs sensibles, nourriture, boisson et sexualité. Mais, dans chacun de ces plaisirs, il faut faire preuve de {{VocPlat|Tempérance|tempérance}}, pour éviter l'excès inverse de l'ascétisme. Lorsque Platon évoque le renoncement, dans le cadre de certaines relations, aux plaisirs charnels, il ne parle pas de supprimer le désir sexuel, mais de le reporter sur une réalité plus élevée, objet de l'{{VocPlat|Amour|amour}} philosophique. Cet amour n'est donc pas purement spirituel. L'amour pour un autre être apparaît dans cette conception comme un moyen. En ce sens, il y a bien là quelque chose de l'amour platonique qui est apparu pendant la Renaissance ; mais on ne peut les confondre, car Platon conçoit toujours l'amour comme désir sexuel, jamais comme une pure spiritualité. === L'âme serait composée de parties === Pour Platon, l'âme serait composée d'une partie intellectuelle, d'une partie ardente et d'une partie désirante. En réalité, bien que les traducteurs emploient parfois le mot ''partie'', l'âme est composée de puissances (de capacités ou de facultés) qui sont des activités de l'âme qui se définissent d'après les objets sur lesquels elles s'exercent. === Chaque groupe de la cité juste possèderait une vertu === On représente souvent la cité juste décrite par Platon comme une cité composée de trois classes possédant chacune une vertu : les gouvernants sont sages ; les gardiens sont courageux ; les producteurs sont tempérants. Cette représentation est fausse puisque les gouvernants sont sages, courageux et tempérants, les gardiens sont courageux et tempérants et ils reçoivent une éducation dans le but de devenir sages, et les producteurs tempérants. === La voix intérieure de Socrate serait celle d'un démon === L'expression ''démon de Socrate'' est utilisée par Plutarque. Mais elle n'apparaît nulle part dans les dialogues. Il est donc faux d'affirmer que Socrate entendrait un démon intérieur. Il reste que nous ne savons pas quel statut donner à cette voix qui est qualifiée de signe démonique : il peut tout aussi bien s'agir d'un dialogue de l'âme de Socrate avec elle-même. 33ecka0zik6d933dnxqflhd5506suzb Fonctionnement d'un ordinateur/L'abstraction mémoire et la mémoire virtuelle 0 65813 763968 763888 2026-04-18T20:56:01Z Mewtow 31375 /* Le cache de descripteur de segment */ 763968 wikitext text/x-wiki Pour introduire ce chapitre, nous devons faire un rappel sur le concept d{{'}}'''espace d'adressage'''. Pour rappel, un espace d'adressage correspond à l'ensemble des adresses utilisables par le processeur. Par exemple, si je prends un processeur 16 bits, il peut adresser en tout 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. Intuitivement, on s'attend à ce qu'il y ait correspondance avec les adresses envoyées à la mémoire RAM. J'entends par là que l'adresse 1209 de l'espace d'adressage correspond à l'adresse 1209 en mémoire RAM. C'est là une hypothèse parfaitement raisonnable et on voit mal comment ce pourrait ne pas être le cas. Mais sachez qu'il existe des techniques d{{'}}'''abstraction mémoire''' qui font que ce n'est pas le cas. Avec ces techniques, l'adresse 1209 de l'espace d'adressage correspond en réalité à l'adresse 9999 en mémoire RAM, voire n'est pas en RAM. L'abstraction mémoire fait que les adresses de l'espace d'adressage sont des adresses fictives, qui doivent être traduites en adresses mémoires réelles pour être utilisées. Les adresses de l'espace d'adressage portent le nom d{{'}}'''adresses logiques''', alors que les adresses de la mémoire RAM sont appelées '''adresses physiques'''. ==L'abstraction mémoire implémente plusieurs fonctionnalités complémentaires== L'utilité de l'abstraction matérielle n'est pas évidente, mais sachez qu'elle est si utile que tous les processeurs modernes la prennent en charge. Elle sert notamment à implémenter la mémoire virtuelle, que nous aborderons dans ce qui suit. La plupart de ces fonctionnalités manipulent la relation entre adresses logiques et physique. Dans le cas le plus simple, une adresse logique correspond à une seule adresse physique. Mais beaucoup de fonctionnalités avancées ne respectent pas cette règle. ===L'abstraction matérielle des processus=== Les systèmes d'exploitation modernes sont dits multi-tâche, à savoir qu'ils sont capables d'exécuter plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Toutefois, cela amène un paquet de problèmes qu'il faut résoudre au mieux. Par exemple, les programmes exécutés doivent se partager la mémoire RAM, ce qui ne vient pas sans problèmes. Le problème principal est que les programmes ne doivent pas lire ou écrire dans les données d'un autre, sans quoi on se retrouverait rapidement avec des problèmes. Il faut donc introduire des mécanismes d{{'}}'''isolement des processus''', pour isoler les programmes les uns des autres. Un de ces mécanismes est l{{'}}'''abstraction matérielle des processus''', une technique qui fait que chaque programme a son propre espace d'adressage. Chaque programme a l'impression d'avoir accès à tout l'espace d'adressage, de l'adresse 0 à l'adresse maximale gérée par le processeur. Évidemment, il s'agit d'une illusion maintenue justement grâce à la traduction d'adresse. Les espaces d'adressage contiennent des adresses logiques, les adresses de la RAM sont des adresses physiques, la nécessité de l'abstraction mémoire est évidente. Implémenter l'abstraction mémoire peut se faire de plusieurs manières. Mais dans tous les cas, il faut que la correspondance adresse logique - physique change d'un programme à l'autre. Ce qui est normal, vu que les deux processus sont placés à des endroits différents en RAM physique. La conséquence est qu'avec l'abstraction mémoire, une adresse logique correspond à plusieurs adresses physiques. Une même adresse logique dans deux processus différents correspond à deux adresses phsiques différentes, une par processus. Une adresse logique dans un processus correspondra à l'adresse physique X, la même adresse dans un autre processus correspondra à l'adresse Y. Les adresses physiques qui partagent la même adresse logique sont alors appelées des '''adresses homonymes'''. Le choix de la bonne adresse étant réalisé par un mécanisme matériel et dépend du programme en cours. Le mécanisme pour choisir la bonne adresse dépend du processeur, mais il y en a deux grands types : * La première consiste à utiliser l'identifiant de processus CPU, vu au chapitre précédent. C'est, pour rappel, un numéro attribué à chaque processus par le processeur. L'identifiant du processus en cours d'exécution est mémorisé dans un registre du processeur. La traduction d'adresse utilise cet identifiant, en plus de l'adresse logique, pour déterminer l'adresse physique. * La seconde solution mémorise les correspondances adresses logiques-physique dans des tables en mémoire RAM, qui sont différentes pour chaque programme. Les tables sont accédées à chaque accès mémoire, afin de déterminer l'adresse physique. ===Le partage de la mémoire=== L'isolation des processus est très importante sur les systèmes d'exploitation modernes. Cependant, il existe quelques situations où elle doit être contournée ou du moins mise en pause. Les situations sont multiples : gestion de bibliothèques partagées, communication entre processus, usage de ''threads'', etc. Elles impliquent toutes un '''partage de mémoire''', à savoir qu'une portion de mémoire RAM est partagée entre plusieurs programmes. Le partage de mémoire est une sorte de brèche de l'isolation des processus, mais qui est autorisée car elle est utile. Un cas intéressant est celui des '''bibliothèques partagées'''. Les bibliothèques sont des collections de fonctions regroupées ensemble, dans une seule unité de code. Un programme qui utilise une bibliothèque peut appeler n’importe quelle fonction présente dans la bibliothèque. La bibliothèque peut être simplement inclue dans le programme lui-même, on parle alors de bibliothèques statiques. De telles bibliothèques fonctionnent très bien, mais avec un petit défaut pour les bibliothèques très utilisées : plusieurs programmes qui utilisent la même bibliothèque vont chacun l'inclure dans leur code, ce qui fera doublon. Pour éviter cela, les OS modernes gèrent des bibliothèques partagées, à savoir qu'un seul exemplaire de la bibliothèque est partagé entre plusieurs programmes. Chaque programme peut exécuter une fonction de la bibliothèque quand il le souhaite, en effectuant un branchement adéquat. Mais cela implique que la bibliothèque soit présente dans l'espace d'adressage du programme en question. Une bibliothèque est donc présente dans plusieurs espaces d'adressage, alors qu'il n'y en a qu'un seul exemplaire en mémoire RAM. [[File:Ogg vorbis libs and application dia.svg|centre|vignette|upright=2|Exemple de bibliothèques, avec Ogg vorbis.]] D'autres situations demandent de partager de la mémoire entre deux programmes. Par exemple, les systèmes d'exploitation modernes gèrent nativement des systèmes de '''communication inter-processus''', très utilisés par les programmes modernes pour échanger des données. Et la plupart demandant de partager un bout de mémoire entre processus, même si c'est seulement temporairement. Typiquement, deux processus partagent un intervalle d'adresse où l'un écrit les données à l'autre, l'autre lisant les données envoyées. Une dernière utilisation de la mémoire partagée est l{{'}}'''accès direct au noyau'''. Sur les systèmes d'exploitations moderne, dans l'espace d'adressage de chaque programme, les adresses hautes sont remplies avec une partie du noyau ! Évidemment, ces adresses sont accessibles uniquement en lecture, pas en écriture. Pas question de modifier le noyau de l'OS ! De plus, il s'agit d'une portion du noyau dont on sait que la consultation ne pose pas de problèmes de sécurité. Le programme peut lire des données dans cette portion du noyau, mais aussi exécuter les fonctions du noyau qui sont dedans. L'idée est d'éviter des appels systèmes trop fréquents. Au lieu d'effectuer un véritable appel système, avec une interruption logicielle, le programme peut exécuter des appels systèmes simplifiés, de simples appels de fonctions couplés avec un changement de niveau de privilège (passage en espace noyau nécessaire). [[File:AMD64-canonical--48-bit.png|vignette|Répartition des adresses entre noyau (jaune/orange) et programme (verte), sur les systèmes x86-64 bits, avec des adresses physiques de 48 bits.]] L'espace d'adressage est donc séparé en deux portions : l'OS d'un côté, le programme de l'autre. La répartition des adresses entre noyau et programme varie suivant l'OS ou le processeur utilisé. Sur les PC x86 32 bits, Linux attribuait 3 gigas pour les programmes et 1 giga pour le noyau, Windows attribuait 2 gigas à chacun. Sur les systèmes x86 64 bits, l'espace d'adressage d'un programme est coupé en trois, comme illustré ci-contre : une partie basse de 2^48 octets, une partie haute de même taille, et un bloc d'adresses invalides entre les deux. Les adresses basses sont utilisées pour le programme, les adresses hautes pour le noyau, il n'y a rien entre les deux. Avec le partage de mémoire, plusieurs adresses logiques correspondent à la même adresse physique. Tel processus verra la zone de mémoire partagée à l'adresse X, l'autre la verra à l'adresse Y. Mais il s'agira de la même portion de mémoire physique, avec une seule adresse physique. En clair, lorsque deux processus partagent une même zone de mémoire, la zone sera mappées à des adresses logiques différentes. Les adresses logiques sont alors appelées des '''adresses synonymes''', terme qui trahit le fait qu'elles correspondent à la même adresse physique. ===La mémoire virtuelle=== Toutes les adresses ne sont pas forcément occupées par de la mémoire RAM, s'il n'y a pas assez de RAM installée. Par exemple, un processeur 32 bits peut adresser 4 gibioctets de RAM, même si seulement 3 gibioctets sont installés dans l'ordinateur. L'espace d'adressage contient donc 1 gigas d'adresses inutilisées, et il faut éviter ce surplus d'adresses pose problème. Sans mémoire virtuelle, seule la mémoire réellement installée est utilisable. Si un programme utilise trop de mémoire, il est censé se rendre compte qu'il n'a pas accès à tout l'espace d'adressage. Quand il demandera au système d'exploitation de lui réserver de la mémoire, le système d'exploitation le préviendra qu'il n'y a plus de mémoire libre. Par exemple, si un programme tente d'utiliser 4 gibioctets sur un ordinateur avec 3 gibioctets de mémoire, il ne pourra pas. Pareil s'il veut utiliser 2 gibioctets de mémoire sur un ordinateur avec 4 gibioctets, mais dont 3 gibioctets sont déjà utilisés par d'autres programmes. Dans les deux cas, l'illusion tombe à plat. Les techniques de '''mémoire virtuelle''' font que l'espace d'adressage est utilisable au complet, même s'il n'y a pas assez de mémoire installée dans l'ordinateur ou que d'autres programmes utilisent de la RAM. Par exemple, sur un processeur 32 bits, le programme aura accès à 4 gibioctets de RAM, même si d'autres programmes utilisent la RAM, même s'il n'y a que 2 gibioctets de RAM d'installés dans l'ordinateur. Pour cela, on utilise une partie des mémoires de masse (disques durs) d'un ordinateur en remplacement de la mémoire physique manquante. Le système d'exploitation crée sur le disque dur un fichier, appelé le ''swapfile'' ou '''fichier de ''swap''''', qui est utilisé comme mémoire RAM supplémentaire. Il mémorise le surplus de données et de programmes qui ne peut pas être mis en mémoire RAM. [[File:Vm1.png|centre|vignette|upright=2.0|Mémoire virtuelle et fichier de Swap.]] Une technique naïve de mémoire virtuelle serait la suivante. Avant de l'aborder, précisons qu'il s'agit d'une technique abordée à but pédagogique, mais qui n'est implémentée nulle part tellement elle est lente et inefficace. Un espace d'adressage de 4 gigas ne contient que 3 gigas de RAM, ce qui fait 1 giga d'adresses inutilisées. Les accès mémoire aux 3 gigas de RAM se font normalement, mais l'accès aux adresses inutilisées lève une exception matérielle "Memory Unavailable". La routine d'interruption de cette exception accède alors au ''swapfile'' et récupère les données associées à cette adresse. La mémoire virtuelle est alors émulée par le système d'exploitation. Le défaut de cette méthode est que l'accès au giga manquant est toujours très lent, parce qu'il se fait depuis le disque dur. D'autres techniques de mémoire virtuelle logicielle font beaucoup mieux, mais nous allons les passer sous silence, vu qu'on peut faire mieux, avec l'aide du matériel. L'idée est de charger les données dont le programme a besoin dans la RAM, et de déplacer les autres sur le disque dur. Par exemple, imaginons la situation suivante : un programme a besoin de 4 gigas de mémoire, mais ne dispose que de 2 gigas de mémoire installée. On peut imaginer découper l'espace d'adressage en 2 blocs de 2 gigas, qui sont chargés à la demande. Si le programme accède aux adresses basses, on charge les 2 gigas d'adresse basse en RAM. S'il accède aux adresses hautes, on charge les 2 gigas d'adresse haute dans la RAM après avoir copié les adresses basses sur le ''swapfile''. On perd du temps dans les copies de données entre RAM et ''swapfile'', mais on gagne en performance vu que tous les accès mémoire se font en RAM. Du fait de la localité temporelle, le programme utilise les données chargées depuis le swapfile durant un bon moment avant de passer au bloc suivant. La RAM est alors utilisée comme une sorte de cache alors que les données sont placées dans une mémoire fictive représentée par l'espace d'adressage et qui correspond au disque dur. Mais avec cette technique, la correspondance entre adresses du programme et adresses de la RAM change au cours du temps. Les adresses de la RAM correspondent d'abord aux adresses basses, puis aux adresses hautes, et ainsi de suite. On a donc besoin d'abstraction mémoire. Les correspondances entre adresse logique et physique peuvent varier avec le temps, ce qui permet de déplacer des données de la RAM vers le disque dur ou inversement. Une adresse logique peut correspondre à une adresse physique, ou bien à une donnée swappée sur le disque dur. C'est l'unité de traduction d'adresse qui se charge de faire la différence. Si une correspondance entre adresse logique et physique est trouvée, elle l'utilise pour traduire les adresses. Si aucune correspondance n'est trouvée, alors elle laisse la main au système d'exploitation pour charger la donnée en RAM. Une fois la donnée chargée en RAM, les correspondances entre adresse logique et physiques sont modifiées de manière à ce que l'adresse logique pointe vers la donnée chargée. ===L'extension d'adressage=== Une autre fonctionnalité rendue possible par l'abstraction mémoire est l{{'}}'''extension d'adressage'''. Elle permet d'utiliser plus de mémoire que l'espace d'adressage ne le permet. Par exemple, utiliser 7 gigas de RAM sur un processeur 32 bits, dont l'espace d'adressage ne gère que 4 gigas. L'extension d'adresse est l'exact inverse de la mémoire virtuelle. La mémoire virtuelle sert quand on a moins de mémoire que d'adresses, l'extension d'adresse sert quand on a plus de mémoire que d'adresses. Il y a quelques chapitres, nous avions vu que c'est possible via la commutation de banques. Mais l'abstraction mémoire est une méthode alternative. Que ce soit avec la commutation de banques ou avec l'abstraction mémoire, les adresses envoyées à la mémoire doivent être plus longues que les adresses gérées par le processeur. La différence est que l'abstraction mémoire étend les adresses d'une manière différente. Une implémentation possible de l'extension d'adressage fait usage de l'abstraction matérielle des processus. Chaque processus a son propre espace d'adressage, mais ceux-ci sont placés à des endroits différents dans la mémoire physique. Par exemple, sur un ordinateur avec 16 gigas de RAM, mais un espace d'adressage de 2 gigas, on peut remplir la RAM en lançant 8 processus différents et chaque processus aura accès à un bloc de 2 gigas de RAM, pas plus, il ne peut pas dépasser cette limite. Ainsi, chaque processus est limité par son espace d'adressage, mais on remplit la mémoire avec plusieurs processus, ce qui compense. Il s'agit là de l'implémentation la plus simple, qui a en plus l'avantage d'avoir la meilleure compatibilité logicielle. De simples changements dans le système d'exploitation suffisent à l'implémenter. [[File:Extension de l'espace d'adressage.png|centre|vignette|upright=1.5|Extension de l'espace d'adressage]] Un autre implémentation donne plusieurs espaces d'adressage différents à chaque processus, et a donc accès à autant de mémoire que permis par la somme de ces espaces d'adressage. Par exemple, sur un ordinateur avec 16 gigas de RAM et un espace d'adressage de 4 gigas, un programme peut utiliser toute la RAM en utilisant 4 espaces d'adressage distincts. On passe d'un espace d'adressage à l'autre en changeant la correspondance adresse logique-physique. L'inconvénient est que la compatibilité logicielle est assez mauvaise. Modifier l'OS ne suffit pas, les programmeurs doivent impérativement concevoir leurs programmes pour qu'ils utilisent explicitement plusieurs espaces d'adressage. Les deux implémentations font usage des adresses logiques homonymes, mais à l'intérieur d'un même processus. Pour rappel, cela veut dire qu'une adresse logique correspond à des adresses physiques différentes. Rien d'étonnant vu qu'on utilise plusieurs espaces d'adressage, comme pour l'abstraction des processus, sauf que cette fois-ci, on a plusieurs espaces d'adressage par processus. Prenons l'exemple où on a 8 gigas de RAM sur un processeur 32 bits, dont l'espace d'adressage ne gère que 4 gigas. L'idée est qu'une adresse correspondra à une adresse dans les premiers 4 gigas, ou dans les seconds 4 gigas. L'adresse logique X correspondra d'abord à une adresse physique dans les premiers 4 gigas, puis à une adresse physique dans les seconds 4 gigas. ===La protection mémoire=== La '''protection mémoire''' regroupe des techniques très différentes les unes des autres, qui visent à améliorer la sécurité des programmes et des systèmes d'exploitation. Elles visent à empêcher de lire, d'écrire ou d'exécuter certaines portions de mémoire. Sans elle, les programmes peuvent techniquement lire ou écrire les données des autres, ce qui causent des situations non-prévues par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. La première technique de protection mémoire est l{{'}}'''isolation des processus''', qu'on a vue plus haut. Elle garantit que chaque programme n'a accès qu'à certaines portions dédiées de la mémoire et rend le reste de la mémoire inaccessible en lecture et en écriture. Le système d'exploitation attribue à chaque programme une ou plusieurs portions de mémoire rien que pour lui, auquel aucun autre programme ne peut accéder. Un tel programme, isolé des autres, s'appelle un '''processus''', d'où le nom de cet objectif. Toute tentative d'accès à une partie de la mémoire non autorisée déclenche une exception matérielle (rappelez-vous le chapitre sur les interruptions) qui est traitée par une routine du système d'exploitation. Généralement, le programme fautif est sauvagement arrêté et un message d'erreur est affiché à l'écran. La '''protection de l'espace exécutable''' empêche d’exécuter quoique ce soit provenant de certaines zones de la mémoire. En effet, certaines portions de la mémoire sont censées contenir uniquement des données, sans aucun programme ou code exécutable. Cependant, des virus informatiques peuvent se cacher dedans et d’exécuter depuis celles-ci. Ou encore, des failles de sécurités peuvent permettre à un attaquant d'injecter du code exécutable malicieux dans des données, ce qui peut lui permettre de lire les données manipulées par un programme, prendre le contrôle de la machine, injecter des virus, ou autre. Pour éviter cela, le système d'exploitation peut marquer certaines zones mémoire comme n'étant pas exécutable. Toute tentative d’exécuter du code localisé dans ces zones entraîne la levée d'une exception ou d'une erreur et le système d'exploitation réagit en conséquence. Là encore, le processeur doit détecter les exécutions non autorisées. D'autres méthodes de protection mémoire visent à limiter des actions dangereuses. Pour cela, le processeur et l'OS gèrent des '''droits d'accès''', qui interdisent certaines actions pour des programmes non-autorisés. Lorsqu'on exécute une opération interdite, le système d’exploitation et/ou le processeur réagissent en conséquence. La première technique de ce genre n'est autre que la séparation entre espace noyau et utilisateur, vue dans le chapitre sur les interruptions. Mais il y en a d'autres, comme nous le verrons dans ce chapitre. ==La MMU== La traduction des adresses logiques en adresses physiques se fait par un circuit spécialisé appelé la '''''Memory Management Unit''''' (MMU), qui est souvent intégré directement dans l'interface mémoire. La MMU est souvent associée à une ou plusieurs mémoires caches, qui visent à accélérer la traduction d'adresses logiques en adresses physiques. En effet, nous verrons plus bas que la traduction d'adresse demande d'accéder à des tableaux, gérés par le système d'exploitation, qui sont en mémoire RAM. Aussi, les processeurs modernes incorporent des mémoires caches appelées des '''''Translation Lookaside Buffers''''', ou encore TLB. Nous nous pouvons pas parler des TLB pour le moment, car nous n'avons pas encore abordé le chapitre sur les mémoires caches, mais un chapitre entier sera dédié aux TLB d'ici peu. [[File:MMU principle updated.png|centre|vignette|upright=2|MMU.]] ===Les MMU intégrées au processeur=== D'ordinaire, la MMU est intégrée au processeur. Et elle peut l'être de deux manières. La première en fait un circuit séparé, relié au bus d'adresse. La seconde fusionne la MMU avec l'unité de calcul d'adresse. La première solution est surtout utilisée avec une technique d'abstraction mémoire appelée la pagination, alors que l'autre l'est avec une autre méthode appelée la segmentation. La raison est que la traduction d'adresse avec la segmentation est assez simple : elle demande d'additionner le contenu d'un registre avec l'adresse logique, ce qui est le genre de calcul qu'une unité de calcul d'adresse sait déjà faire. La fusion est donc assez évidente. Pour donner un exemple, l'Intel 8086 fusionnait l'unité de calcul d'adresse et la MMU. Précisément, il utilisait un même additionneur pour incrémenter le ''program counter'' et effectuer des calculs d'adresse liés à la segmentation. Il aurait été logique d'ajouter les pointeurs de pile avec, mais ce n'était pas possible. La raison est que le pointeur de pile ne peut pas être envoyé directement sur le bus d'adresse, vu qu'il doit passer par une phase de traduction en adresse physique liée à la segmentation. [[File:80186 arch.png|centre|vignette|upright=2|Intel 8086, microarchitecture.]] ===Les MMU séparées du processeur, sur la carte mère=== Il a existé des processeurs avec une MMU externe, soudée sur la carte mère. Par exemple, les processeurs Motorola 68000 et 68010 pouvaient être combinés avec une MMU de type Motorola 68451. Elle supportait des versions simplifiées de la segmentation et de la pagination. Au minimum, elle ajoutait un support de la protection mémoire contre certains accès non-autorisés. La gestion de la mémoire virtuelle proprement dit n'était possible que si le processeur utilisé était un Motorola 68010, en raison de la manière dont le 68000 gérait ses accès mémoire. La MMU 68451 gérait un espace d'adressage de 16 mébioctets, découpé en maximum 32 pages/segments. On pouvait dépasser cette limite de 32 segments/pages en combinant plusieurs 68451. Le Motorola 68851 était une MMU qui était prévue pour fonctionner de paire avec le Motorola 68020. Elle gérait la pagination pour un espace d'adressage de 32 bits. Les processeurs suivants, les 68030, 68040, et 68060, avaient une MMU interne au processeur. ==La relocation matérielle== Pour rappel, les systèmes d'exploitation moderne permettent de lancer plusieurs programmes en même temps et les laissent se partager la mémoire. Dans le cas le plus simple, qui n'est pas celui des OS modernes, le système d'exploitation découpe la mémoire en blocs d'adresses contiguës qui sont appelés des '''segments''', ou encore des ''partitions mémoire''. Les segments correspondent à un bloc de mémoire RAM. C'est-à-dire qu'un segment de 259 mébioctets sera un segment continu de 259 mébioctets dans la mémoire physique comme dans la mémoire logique. Dans ce qui suit, un segment contient un programme en cours d'exécution, comme illustré ci-dessous. [[File:CPT Memory Addressable.svg|centre|vignette|upright=2|Espace d'adressage segmenté.]] Le système d'exploitation mémorise la position de chaque segment en mémoire, ainsi que d'autres informations annexes. Le tout est regroupé dans la '''table de segment''', un tableau dont chaque case est attribuée à un programme/segment. La table des segments est un tableau numéroté, chaque segment ayant un numéro qui précise sa position dans le tableau. Chaque case, chaque entrée, contient un '''descripteur de segment''' qui regroupe plusieurs informations sur le segment : son adresse de base, sa taille, diverses informations. ===La relocation avec la relocation matérielle : le registre de base=== Un segment peut être placé n'importe où en RAM physique et sa position en RAM change à chaque exécution. Le programme est chargé à une adresse, celle du début du segment, qui change à chaque chargement du programme. Et toutes les adresses utilisées par le programme doivent être corrigées lors du chargement du programme, généralement par l'OS. Cette correction s'appelle la '''relocation''', et elle consiste à ajouter l'adresse de début du segment à chaque adresse manipulée par le programme. [[File:Relocation assistée par matériel.png|centre|vignette|upright=2.5|Relocation.]] La relocation matérielle fait que la relocation est faite par le processeur, pas par l'OS. La relocation est intégrée dans le processeur par l'intégration d'un registre : le '''registre de base''', aussi appelé '''registre de relocation'''. Il mémorise l'adresse à laquelle commence le segment, la première adresse du programme. Pour effectuer la relocation, le processeur ajoute automatiquement l'adresse de base à chaque accès mémoire, en allant la chercher dans le registre de relocation. [[File:Registre de base de segment.png|centre|vignette|upright=2|Registre de base de segment.]] Le processeur s'occupe de la relocation des segments et le programme compilé n'en voit rien. Pour le dire autrement, les programmes manipulent des adresses logiques, qui sont traduites par le processeur en adresses physiques. La traduction se fait en ajoutant le contenu du registre de relocation à l'adresse logique. De plus, cette méthode fait que chaque programme a son propre espace d'adressage. [[File:CPU created logical address presentation.png|centre|vignette|upright=2|Traduction d'adresse avec la relocation matérielle.]] Le système d'exploitation mémorise les adresses de base pour chaque programme, dans la table des segments. Le registre de base est mis à jour automatiquement lors de chaque changement de segment. Pour cela, le registre de base est accessible via certaines instructions, accessibles en espace noyau, plus rarement en espace utilisateur. Le registre de segment est censé être adressé implicitement, vu qu'il est unique. Si ce n'est pas le cas, il est possible d'écrire dans ce registre de segment, qui est alors adressable. ===La protection mémoire avec la relocation matérielle : le registre limite=== Sans restrictions supplémentaires, la taille maximale d'un segment est égale à la taille complète de l'espace d'adressage. Sur les processeurs 32 bits, un segment a une taille maximale de 2^32 octets, soit 4 gibioctets. Mais il est possible de limiter la taille du segment à 2 gibioctets, 1 gibioctet, 64 Kibioctets, ou toute autre taille. La limite est définie lors de la création du segment, mais elle peut cependant évoluer au cours de l'exécution du programme, grâce à l'allocation mémoire. Le processeur vérifie à chaque accès mémoire que celui-ci se fait bien dans le segment, qu'il ne déborde pas en-dehors. C'est possible qu'une adresse calculée sorte du segment, à la suite d'un bug ou d'une erreur de programmation, voire pire. Et le processeur doit éviter de tels '''débordements de segments'''. A chaque accès mémoire, le processeur compare l'adresse accédée et vérifie qu'elle est bien dans le segment. Pour cela, il y a deux solutions. La première part du principe que le segment est placé en mémoire entre l'adresse de base et l'adresse limite. Il suffit de mémoriser l'adresse limite, l'adresse physique à ne pas dépasser. Une autre solution mémorise la taille du segment. La table des segments doit donc mémoriser, en plus de l'adresse de base : soit l'adresse maximale du segment, soit la taille du segment. D'autres informations peuvent être ajoutées, comme on le verra plus tard, mais cela complexifie la table des segments. De plus, le processeur se voit ajouter un '''registre limite''', qui mémorise soit la taille du segment, soit l'adresse limite. Les deux registres, base et limite, sont utilisés pour vérifier si un programme qui lit/écrit de la mémoire en-dehors de son segment attitré : au-delà pour le registre limite, en-deça pour le registre de base. Le processeur vérifie pour chaque accès mémoire ne déborde pas au-delà du segment qui lui est allouée, ce qui n'arrive que si l'adresse d'accès dépasse la valeur du registre limite. Pour les accès en-dessous du segment, il suffit de vérifier si l'addition de relocation déborde, tout débordement signifiant erreur de protection mémoire. [[File:Registre limite.png|centre|vignette|upright=2|Registre limite]] Utiliser la taille du segment a de nombreux avantages. L'un d'entre eux se manifeste quand on déplace un segment en mémoire RAM. Le descripteur doit alors être mis à jour, et c'est plus facile quand on utilise la taille du segment. Si on utilise l'adresse limite, il faut mettre à jour à la fois l'adresse de base et l'adresse limite, dans le descripteur. En utilisant la taille, seule l'adresse de base doit être modifiée, vu que le segment n'a pas changé de taille. Un autre avantage est lié aux performances, mais nous devons faire un détour pour le comprendre. La taille du segment est équivalent à l'adresse logique maximale possible. Par exemple, si un segment fait 256 octets, les adresses logiques possibles vont de 0 à 255, 256 est donc à la fois la taille du segment et l'adresse logique à partir de laquelle on déborde du segment. Et cela marche si on remplace 256 par n'importe quelle valeur : vu que le segment commence à l'adresse 0, sa taille en octets indique l'adresse de dépassement. Interpréter la taille du segment comme une adresse logique fait que les tests avec le registre limite sont plus performants, voyons pourquoi. En utilisant l'adresse physique limite, on doit faire la relocation, puis comparer l'adresse calculée avec l'adresse limite. Le calcul d'adresse doit se faire avant la vérification. En utilisant la taille, on doit comparer l'adresse logique avec la taille du segment. On peut alors faire le test de débordement avant ou pendant la relocation. Les deux peuvent être faits en parallèle, dans deux circuits distincts, ce qui améliore un peu le temps d'un accès mémoire. Quelques processeurs en ont profité, mais on verra cela dans la section sur la segmentation. [[File:Comparaison entre adresse limite physique et logique.png|centre|vignette|upright=2|Comparaison entre adresse limite physique et logique]] Les registres de base et limite sont altérés uniquement par le système d'exploitation et ne sont accessibles qu'en espace noyau. Lorsque le système d'exploitation charge un programme, ou reprend son exécution, il charge les adresses de début/fin du segment dans ces registres. D'ailleurs, ces deux registres doivent être sauvegardés et restaurés lors de chaque interruption. Par contre, et c'est assez évident, ils ne le sont pas lors d'un appel de fonction. Cela fait une différence de plus entre interruption et appels de fonctions. : Il faut noter que le registre limite et le registre de base sont parfois fusionnés en un seul registre, qui contient un descripteur de segment tout entier. Pour information, la relocation matérielle avec un registre limite a été implémentée sur plusieurs processeurs assez anciens, notamment sur les anciens supercalculateurs de marque CDC. Un exemple est le fameux CDC 6600, qui implémentait cette technique. ===La mémoire virtuelle avec la relocation matérielle=== Il est possible d'implémenter la mémoire virtuelle avec la relocation matérielle. Pour cela, il faut swapper des segments entiers sur le disque dur. Les segments sont placés en mémoire RAM et leur taille évolue au fur et à mesure que les programmes demandent du rab de mémoire RAM. Lorsque la mémoire est pleine, ou qu'un programme demande plus de mémoire que disponible, des segments entiers sont sauvegardés dans le ''swapfile'', pour faire de la place. Faire ainsi de demande juste de mémoriser si un segment est en mémoire RAM ou non, ainsi que la position des segments swappés dans le ''swapfile''. Pour cela, il faut modifier la table des segments, afin d'ajouter un '''bit de swap''' qui précise si le segment en question est swappé ou non. Lorsque le système d'exploitation veut swapper un segment, il le copie dans le ''swapfile'' et met ce bit à 1. Lorsque l'OS recharge ce segment en RAM, il remet ce bit à 0. La gestion de la position des segments dans le ''swapfile'' est le fait d'une structure de données séparée de la table des segments. L'OS exécute chaque programme l'un après l'autre, à tour de rôle. Lorsque le tour d'un programme arrive, il consulte la table des segments pour récupérer les adresses de base et limite, mais il vérifie aussi le bit de swap. Si le bit de swap est à 0, alors l'OS se contente de charger les adresses de base et limite dans les registres adéquats. Mais sinon, il démarre une routine d'interruption qui charge le segment voulu en RAM, depuis le ''swapfile''. C'est seulement une fois le segment chargé que l'on connait son adresse de base/limite et que le chargement des registres de relocation peut se faire. Un défaut évident de cette méthode est que l'on swappe des programmes entiers, qui sont généralement assez imposants. Les segments font généralement plusieurs centaines de mébioctets, pour ne pas dire plusieurs gibioctets, à l'époque actuelle. Ils étaient plus petits dans l'ancien temps, mais la mémoire était alors plus lente. Toujours est-il que la copie sur le disque dur des segments est donc longue, lente, et pas vraiment compatible avec le fait que les programmes s'exécutent à tour de rôle. Et ca explique pourquoi la relocation matérielle n'est presque jamais utilisée avec de la mémoire virtuelle. ===L'extension d'adressage avec la relocation matérielle=== Passons maintenant à la dernière fonctionnalité implémentable avec la traduction d'adresse : l'extension d'adressage. Elle permet d'utiliser plus de mémoire que ne le permet l'espace d'adressage. Par exemple, utiliser plus de 64 kibioctets de mémoire sur un processeur 16 bits. Pour cela, les adresses envoyées à la mémoire doivent être plus longues que les adresses gérées par le processeur. L'extension des adresses se fait assez simplement avec la relocation matérielle : il suffit que le registre de base soit plus long. Prenons l'exemple d'un processeur aux adresses de 16 bits, mais qui est reliée à un bus d'adresse de 24 bits. L'espace d'adressage fait juste 64 kibioctets, mais le bus d'adresse gère 16 mébioctets de RAM. On peut utiliser les 16 mébioctets de RAM à une condition : que le registre de base fasse 24 bits, pas 16. Un défaut de cette approche est qu'un programme ne peut pas utiliser plus de mémoire que ce que permet l'espace d'adressage. Mais par contre, on peut placer chaque programme dans des portions différentes de mémoire. Imaginons par exemple que l'on ait un processeur 16 bits, mais un bus d'adresse de 20 bits. Il est alors possible de découper la mémoire en 16 blocs de 64 kibioctets, chacun attribué à un segment/programme, qu'on sélectionne avec les 4 bits de poids fort de l'adresse. Il suffit de faire démarrer les segments au bon endroit en RAM, et cela demande juste que le registre de base le permette. C'est une sorte d'émulation de la commutation de banques. ==La segmentation en mode réel des processeurs x86== Avant de passer à la suite, nous allons voir la technique de segmentation de l'Intel 8086, un des tout premiers processeurs 16 bits. Il s'agissait d'une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, ce qui le place à part des autres formes de segmentation. Il s'agit d'une amélioration de la relocation matérielle, qui avait pour but de permettre d'utiliser plus de 64 kibioctets de mémoire, ce qui était la limite maximale sur les processeurs 16 bits de l'époque. Par la suite, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'ancienne forme de segmentation fut alors appelé le '''mode réel''', et la nouvelle forme de segmentation fut appelée le '''mode protégé'''. Le mode protégé rajoute la protection mémoire, en ajoutant des registres limite et une gestion des droits d'accès aux segments, absents en mode réel. De plus, il ajoute un support de la mémoire virtuelle grâce à l'utilisation d'une des segments digne de ce nom, table qui est absente en mode réel ! Pour le moment, voyons le mode réel. ===Les segments en mode réel=== [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La segmentation en mode réel sépare la pile, le tas, le code machine et les données constantes dans quatre segments distincts. * Le segment '''''text''''', qui contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente, des constantes, des variables globales, etc. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. Un point important est que sur ces processeurs, il n'y a pas de table des segments proprement dit. Chaque programme gére de lui-même les adresses de base des segments qu'il manipule. Il n'est en rien aidé par une table des segments gérée par le système d'exploitation. ===Les registres de segments en mode réel=== Chaque segment subit la relocation indépendamment des autres. Pour cela, le processeur intégre plusieurs registres de base, un par segment. Notons que cette solution ne marche que si le nombre de segments par programme est limité, à une dizaine de segments tout au plus. Les processeurs x86 utilisaient cette méthode, et n'associaient que 4 à 6 registres de segments par programme. Les processeurs 8086 et le 286 avaient quatre registres de segment : un pour le code, un autre pour les données, et un pour la pile, le quatrième étant un registre facultatif laissé à l'appréciation du programmeur. Ils sont nommés CS (''code segment''), DS (''data segment''), SS (''Stack segment''), et ES (''Extra segment''). Le 386 rajouta deux registres, les registres FS et GS, qui sont utilisés pour les segments de données. Les processeurs post-386 ont donc 6 registres de segment. Les registres CS et SS sont adressés implicitement, en fonction de l'instruction exécutée. Les instructions de la pile manipulent le segment associé à la pile, le chargement des instructions se fait dans le segment de code, les instructions arithmétiques et logiques vont chercher leurs opérandes sur le tas, etc. Et donc, toutes les instructions sont chargées depuis le segment pointé par CS, les instructions de gestion de la pile (PUSH et POP) utilisent le segment pointé par SS. Les segments DS et ES sont, eux aussi, adressés implicitement. Pour cela, les instructions LOAD/STORE sont dupliquées : il y a une instruction LOAD pour le segment DS, une autre pour le segment ES. D'autres instructions lisent leurs opérandes dans un segment par défaut, mais on peut changer ce choix par défaut en précisant le segment voulu. Un exemple est celui de l'instruction CMPSB, qui compare deux octets/bytes : le premier est chargé depuis le segment DS, le second depuis le segment ES. Un autre exemple est celui de l'instruction MOV avec un opérande en mémoire. Elle lit l'opérande en mémoire depuis le segment DS par défaut. Il est possible de préciser le segment de destination si celui-ci n'est pas DS. Par exemple, l'instruction MOV [A], AX écrit le contenu du registre AX dans l'adresse A du segment DS. Par contre, l'instruction MOV ES:[A], copie le contenu du registre AX das l'adresse A, mais dans le segment ES. ===La traduction d'adresse en mode réel=== La segmentation en mode réel a pour seul but de permettre à un programme de dépasser la limite des 64 KB autorisée par les adresses de 16 bits. L'idée est que chaque segment a droit à son propre espace de 64 KB. On a ainsi 64 Kb pour le code machine, 64 KB pour la pile, 64 KB pour un segment de données, etc. Les registres de segment mémorisaient la base du segment, les adresses calculées par l'ALU étant des ''offsets''. Ce sont tous des registres de 16 bits, mais ils ne mémorisent pas des adresses physiques de 16 bits, comme nous allons le voir. [[File:Table des segments dans un banc de registres.png|centre|vignette|upright=2|Table des segments dans un banc de registres.]] L'Intel 8086 utilisait des adresses de 20 bits, ce qui permet d'adresser 1 mébioctet de RAM. Vous pouvez vous demander comment on peut obtenir des adresses de 20 bits alors que les registres de segments font tous 16 bits ? Cela tient à la manière dont sont calculées les adresses physiques. Le registre de segment n'est pas additionné tel quel avec le décalage : à la place, le registre de segment est décalé de 4 rangs vers la gauche. Le décalage de 4 rangs vers la gauche fait que chaque segment a une adresse qui est multiple de 16. Le fait que le décalage soit de 16 bits fait que les segments ont une taille de 64 kibioctets. {|class="wikitable" |- | <code>&nbsp; </code><code style="background:#DED">0000 0110 1110 1111</code><code>0000</code> | Registre de segment - | 16 bits, décalé de 4 bits vers la gauche |- | <code>+ &nbsp;&nbsp;&nbsp;&nbsp; </code><code style="background:#DDF">0001 0010 0011 0100</code> | Décalage/''Offset'' | 16 bits |- | colspan="3" | |- | <code>&nbsp; </code><code style="background:#FDF">0000 1000 0001 0010 0100</code> | Adresse finale | 20 bits |} Vous aurez peut-être remarqué que le calcul peut déborder, dépasser 20 bits. Mais nous reviendrons là-dessus plus bas. L'essentiel est que la MMU pour la segmentation en mode réel se résume à quelques registres et des additionneurs/soustracteurs. Un exemple est l'Intel 8086, un des tout premier processeur Intel. Le processeur était découpé en deux portions : l'interface mémoire et le reste du processeur. L'interface mémoire est appelée la '''''Bus Interface Unit''''', et le reste du processeur est appelé l{{'}}'''''Execution Unit'''''. L'interface mémoire contenait les registres de segment, au nombre de 4, ainsi qu'un additionneur utilisé pour traduire les adresses logiques en adresses physiques. Elle contenait aussi une file d'attente où étaient préchargées les instructions. Sur le 8086, la MMU est fusionnée avec les circuits de gestion du ''program counter''. Les registres de segment sont regroupés avec le ''program counter'' dans un même banc de registres. Au lieu d'utiliser un additionneur séparé pour le ''program counter'' et un autre pour le calcul de l'adresse physique, un seul additionneur est utilisé pour les deux. L'idée était de partager l'additionneur, qui servait à la fois à incrémenter le ''program counter'' et pour gérer la segmentation. En somme, il n'y a pas vraiment de MMU dédiée, mais un super-circuit en charge du Fetch et de la mémoire virtuelle, ainsi que du préchargement des instructions. Nous en reparlerons au chapitre suivant. [[File:80186 arch.png|centre|vignette|upright=2|Architecture du 8086, du 80186 et de ses variantes.]] La MMU du 286 était fusionnée avec l'unité de calcul d'adresse. Elle contient les registres de segments, un comparateur pour détecter les accès hors-segment, et plusieurs additionneurs. Il y a un additionneur pour les calculs d'adresse proprement dit, suivi d'un additionneur pour la relocation. [[File:Intel i80286 arch.svg|centre|vignette|upright=3|Intel i80286 arch]] ===La segmentation en mode réel accepte plusieurs segments de code/données=== Les programmes peuvent parfaitement répartir leur code machine dans plusieurs segments de code. La limite de 64 KB par segment est en effet assez limitante, et il n'était pas rare qu'un programme stocke son code dans deux ou trois segments. Il en est de même avec les données, qui peuvent être réparties dans deux ou trois segments séparés. La seule exception est la pile : elle est forcément dans un segment unique et ne peut pas dépasser 64 KB. Pour gérer plusieurs segments de code/donnée, il faut changer de segment à la volée suivant les besoins, en modifiant les registres de segment. Il s'agit de la technique de '''commutation de segment'''. Pour cela, tous les registres de segment, à l'exception de CS, peuvent être altérés par une instruction d'accès mémoire, soit avec une instruction MOV, soit en y copiant le sommet de la pile avec une instruction de dépilage POP. L'absence de sécurité fait que la gestion de ces registres est le fait du programmeur, qui doit redoubler de prudence pour ne pas faire n'importe quoi. Pour le code machine, le répartir dans plusieurs segments posait des problèmes au niveau des branchements. Si la plupart des branchements sautaient vers une instruction dans le même segment, quelques rares branchements sautaient vers du code machine dans un autre segment. Intel avait prévu le coup et disposait de deux instructions de branchement différentes pour ces deux situations : les '''''near jumps''''' et les '''''far jumps'''''. Les premiers sont des branchements normaux, qui précisent juste l'adresse à laquelle brancher, qui correspond à la position de la fonction dans le segment. Les seconds branchent vers une instruction dans un autre segment, et doivent préciser deux choses : l'adresse de base du segment de destination, et la position de la destination dans le segment. Le branchement met à jour le registre CS avec l'adresse de base, avant de faire le branchement. Ces derniers étaient plus lents, car on n'avait pas à changer de segment et mettre à jour l'état du processeur. Il y avait la même pour l'instruction d'appel de fonction, avec deux versions de cette instruction. La première version, le '''''near call''''' est un appel de fonction normal, la fonction appelée est dans le segment en cours. Avec la seconde version, le '''''far call''''', la fonction appelée est dans un segment différent. L'instruction a là aussi besoin de deux opérandes : l'adresse de base du segment de destination, et la position de la fonction dans le segment. Un ''far call'' met à jour le registre CS avec l'adresse de base, ce qui fait que les ''far call'' sont plus lents que les ''near call''. Il existe aussi la même chose, pour les instructions de retour de fonction, avec une instruction de retour de fonction normale et une instruction de retour qui renvoie vers un autre segment, qui sont respectivement appelées '''''near return''''' et '''''far return'''''. Là encore, il faut préciser l'adresse du segment de destination dans le second cas. La même chose est possible pour les segments de données. Sauf que cette fois-ci, ce sont les pointeurs qui sont modifiés. pour rappel, les pointeurs sont, en programmation, des variables qui contiennent des adresses. Lors de la compilation, ces pointeurs sont placés soit dans un registre, soit dans les instructions (adressage absolu), ou autres. Ici, il existe deux types de pointeurs, appelés '''''near pointer''''' et '''''far pointer'''''. Vous l'avez deviné, les premiers sont utilisés pour localiser les données dans le segment en cours d'utilisation, alors que les seconds pointent vers une donnée dans un autre segment. Là encore, la différence est que le premier se contente de donner la position dans le segment, alors que les seconds rajoutent l'adresse de base du segment. Les premiers font 16 bits, alors que les seconds en font 32 : 16 bits pour l'adresse de base et 16 pour l{{'}}''offset''. ===L'occupation de l'espace d'adressage par les segments=== Nous venons de voir qu'un programme pouvait utiliser plus de 4-6 segments, avec la commutation de segment. Mais d'autres programmes faisaient l'inverse, à savoir qu'ils se débrouillaient avec seulement 1 ou 2 segments. Suivant le nombre de segments utilisés, la configuration des registres n'était pas la même. Les configurations possibles sont appelées des ''modèle mémoire'', et il y en a en tout 6. En voici la liste : {| class="wikitable" |- ! Modèle mémoire !! Configuration des segments !! Configuration des registres || Pointeurs utilisés || Branchements utilisés |- | Tiny* || Segment unique pour tout le programme || CS=DS=SS || ''near'' uniquement || ''near'' uniquement |- | Small || Segment de donnée séparé du segment de code, pile dans le segment de données || DS=SS || ''near'' uniquement || ''near'' uniquement |- | Medium || Plusieurs segments de code unique, un seul segment de données || CS, DS et SS sont différents || ''near'' et ''far'' || ''near'' uniquement |- | Compact || Segment de code unique, plusieurs segments de données || CS, DS et SS sont différents || ''near'' uniquement || ''near'' et ''far'' |- | Large || Plusieurs segments de code, plusieurs segments de données || CS, DS et SS sont différents || ''near'' et ''far'' || ''near'' et ''far'' |} Un programme est censé utiliser maximum 4-6 segments de 64 KB, ce qui permet d'adresser maximum 64 * 6 = 384 KB de RAM, soit bien moins que le mébioctet de mémoire théoriquement adressable. Mais ce défaut est en réalité contourné par la commutation de segment, qui permettait d'adresser la totalité de la RAM si besoin. Une second manière de contourner cette limite est que plusieurs processus peuvent s'exécuter sur un seul processeur, si l'OS le permet. Ce n'était pas le cas à l'époque du DOS, qui était un OS mono-programmé, mais c'était en théorie possible. La limite est de 6 segments par programme/processus, en exécuter plusieurs permet d'utiliser toute la mémoire disponible rapidement. [[File:Overlapping realmode segments.svg|vignette|Segments qui se recouvrent en mode réel.]] Vous remarquerez qu'avec des registres de segments de 16 bits, on peut gérer 65536 segments différents, chacun de 64 KB. Et 65 536 segments de 64 kibioctets, ça ne rentre pas dans le mébioctet de mémoire permis avec des adresses de 20 bits. La raison est que plusieurs couples segment+''offset'' pointent vers la même adresse. En tout, chaque adresse peut être adressée par 4096 couples segment+''offset'' différents. L'avantage de cette méthode est que des segments peuvent se recouvrir, à savoir que la fin de l'un se situe dans le début de l'autre, comme illustré ci-contre. Cela permet en théorie de partager de la mémoire entre deux processus. Mais la technique est tout sauf pratique et est donc peu utilisée. Elle demande de placer minutieusement les segments en RAM, et les données à partager dans les segments. En pratique, les programmeurs et OS utilisent des segments qui ne se recouvrent pas et sont disjoints en RAM. Le nombre maximal de segments disjoints se calcule en prenant la taille de la RAM, qu'on divise par la taille d'un segment. Le calcul donne : 1024 kibioctets / 64 kibioctets = 16 segments disjoints. Un autre calcul prend le nombre de segments divisé par le nombre d'adresses aliasées, ce qui donne 65536 / 4096 = 16. Seulement 16 segments, c'est peu. En comptant les segments utilisés par l'OS et ceux utilisés par le programme, la limite est vite atteinte si le programme utilise la commutation de segment. ===Le mode réel sur les 286 et plus : la ligne d'adresse A20=== Pour résumer, le registre de segment contient des adresses de 20 bits, dont les 4 bits de poids faible sont à 0. Et il se voit ajouter un ''offset'' de 16 bits. Intéressons-nous un peu à l'adresse maximale que l'on peut calculer avec ce système. Nous allons l'appeler l{{'}}'''adresse maximale de segmentation'''. Elle vaut : {|class="wikitable" |- | <code>&nbsp; </code><code style="background:#DED">1111 1111 1111 1111</code><code>0000</code> | Registre de segment - | 16 bits, décalé de 4 bits vers la gauche |- | <code>+ &nbsp;&nbsp;&nbsp;&nbsp; </code><code style="background:#DDF">1111 1111 1111 1111</code> | Décalage/''Offset'' | 16 bits |- | colspan="3" | |- | <code>&nbsp; </code><code style="background:#FDF">1 0000 1111 1111 1110 1111</code> | Adresse finale | 20 bits |} Le résultat n'est pas l'adresse maximale codée sur 20 bits, car l'addition déborde. Elle donne un résultat qui dépasse l'adresse maximale permis par les 20 bits, il y a un 21ème bit en plus. De plus, les 20 bits de poids faible ont une valeur bien précise. Ils donnent la différence entre l'adresse maximale permise sur 20 bit, et l'adresse maximale de segmentation. Les bits 1111 1111 1110 1111 traduits en binaire donnent 65 519; auxquels il faut ajouter l'adresse 1 0000 0000 0000 0000. En tout, cela fait 65 520 octets adressables en trop. En clair : on dépasse la limite du mébioctet de 65 520 octets. Le résultat est alors très différent selon que l'on parle des processeurs avant le 286 ou après. Avant le 286, le bus d'adresse faisait exactement 20 bits. Les adresses calculées ne pouvaient pas dépasser 20 bits. L'addition générait donc un débordement d'entier, géré en arithmétique modulaire. En clair, les bits de poids fort au-delà du vingtième sont perdus. Le calcul de l'adresse débordait et retournait au début de la mémoire, sur les 65 520 premiers octets de la mémoire RAM. [[File:IBM PC Memory areas.svg|vignette|IBM PC Memory Map, la ''High memory area'' est en jaune.]] Le 80286 en mode réel gère des adresses de base de 24 bits, soit 4 bits de plus que le 8086. Le résultat est qu'il n'y a pas de débordement. Les bits de poids fort sont conservés, même au-delà du 20ème. En clair, la segmentation permettait de réellement adresser 65 530 octets au-delà de la limite de 1 mébioctet. La portion de mémoire adressable était appelé la '''''High memory area''''', qu'on va abrévier en HMA. {| class="wikitable" |+ Espace d'adressage du 286 |- ! Adresses en héxadécimal !! Zone de mémoire |- | 10 FFF0 à FF FFFF || Mémoire étendue, au-delà du premier mébioctet |- | 10 0000 à 10 FFEF || ''High Memory Area'' |- | 0 à 0F FFFF || Mémoire adressable en mode réel |} En conséquence, les applications peuvent utiliser plus d'un mébioctet de RAM, mais au prix d'une rétrocompatibilité imparfaite. Quelques programmes DOS ne marchaient pus à cause de ça. D'autres fonctionnaient convenablement et pouvaient adresser les 65 520 octets en plus. Pour résoudre ce problème, les carte mères ajoutaient un petit circuit relié au 21ème bit d'adresse, nommé A20 (pas d'erreur, les fils du bus d'adresse sont numérotés à partir de 0). Le circuit en question pouvait mettre à zéro le fil d'adresse, ou au contraire le laisser tranquille. En le forçant à 0, le calcul des adresses déborde comme dans le mode réel des 8086. Mais s'il ne le fait pas, la ''high memory area'' est adressable. Le circuit était une simple porte ET, qui combinait le 21ème bit d'adresse avec un '''signal de commande A20''' provenant d'ailleurs. Le signal de commande A20 était géré par le contrôleur de clavier, qui était soudé à la carte mère. Le contrôleur en question ne gérait pas que le clavier, il pouvait aussi RESET le processeur, alors gérer le signal de commande A20 n'était pas si problématique. Quitte à avoir un microcontrôleur sur la carte mère, autant s'en servir au maximum... La gestion du bus d'adresse étaitdonc gérable au clavier. D'autres carte mères faisaient autrement et préféraient ajouter un interrupteur, pour activer ou non la mise à 0 du 21ème bit d'adresse. : Il faut noter que le signal de commande A20 était mis à 1 en mode protégé, afin que le 21ème bit d'adresse soit activé. Le 386 ajouta deux registres de segment, les registres FS et GS, ainsi que le '''mode ''virtual 8086'''''. Ce dernier permet d’exécuter des programmes en mode réel alors que le système d'exploitation s'exécute en mode protégé. C'est une technique de virtualisation matérielle qui permet d'émuler un 8086 sur un 386. L'avantage est que la compatibilité avec les programmes anciens écrits pour le 8086 est conservée, tout en profitant de la protection mémoire. Tous les processeurs x86 qui ont suivi supportent ce mode virtuel 8086. ==La segmentation avec une table des segments== La '''segmentation avec une table des segments''' est apparue sur des processeurs assez anciens, le tout premier étant le Burrough 5000. Elle a ensuite été utilisée sur les processeurs x86 des PCs, à partir du 286 d'Intel. Elle est aujourd'hui abandonnée sur les jeux d'instruction x86. Tout comme la segmentation en mode réel, la segmentation attribue plusieurs segments par programmes ! Et cela a des répercutions sur la manière dont la traduction d'adresse est effectuée. ===Pourquoi plusieurs segments par programme ?=== L'utilité d'avoir plusieurs segments par programme n'est pas évidente, mais elle le devient quand on se plonge dans le passé. Dans le passé, les programmeurs devaient faire avec une quantité de mémoire limitée et il n'était pas rare que certains programmes utilisent plus de mémoire que disponible sur la machine. Mais les programmeurs concevaient leurs programmes en fonction. [[File:Overlay Programming.svg|vignette|upright=1|Overlay Programming]] L'idée était d'implémenter un système de mémoire virtuelle, mais émulé en logiciel, appelé l{{'}}'''''overlaying'''''. Le programme était découpé en plusieurs morceaux, appelés des ''overlays''. Les ''overlays'' les plus importants étaient en permanence en RAM, mais les autres étaient faisaient un va-et-vient entre RAM et disque dur. Ils étaient chargés en RAM lors de leur utilisation, puis sauvegardés sur le disque dur quand ils étaient inutilisés. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. Le matériel n'intervenait pas, comme c'est le cas avec la mémoire virtuelle. Avec la segmentation, un programme peut utiliser la technique des ''overlays'', mais avec l'aide du matériel. Il suffit de mettre chaque ''overlay'' dans son propre segment, et laisser la segmentation faire. Les segments sont swappés en tout ou rien : on doit swapper tout un segment en entier. L'intérêt est que la gestion du ''swapping'' est grandement facilitée, vu que c'est le système d'exploitation qui s'occupe de swapper les segments sur le disque dur ou de charger des segments en RAM. Pas besoin pour le programmeur de coder quoique ce soit. Par contre, cela demande l'intervention du programmeur, qui doit découper le programme en segments/''overlays'' de lui-même. Sans cela, la segmentation n'est pas très utile. L{{'}}''overlaying'' est une forme de '''segmentation à granularité grossière''', à savoir que le programme est découpé en segments de grande taille. L'usage classique est d'avoir un segment pour la pile, un autre pour le code exécutable, un autre pour le reste. Éventuellement, on peut découper les trois segments précédents en deux ou trois segments, rarement au-delà. Les segments sont alors peu nombreux, guère plus d'une dizaine par programme. D'où le terme de ''granularité grossière''. La '''segmentation à granularité fine''' pousse le concept encore plus loin. Avec elle, il y a idéalement un segment par entité manipulée par le programme, un segment pour chaque structure de donnée et/ou chaque objet. Par exemple, un tableau aura son propre segment, ce qui est idéal pour détecter les accès hors tableau. Pour les listes chainées, chaque élément de la liste aura son propre segment. Et ainsi de suite, chaque variable agrégée (non-primitive), chaque structure de donnée, chaque objet, chaque instance d'une classe, a son propre segment. Diverses fonctionnalités supplémentaires peuvent être ajoutées, ce qui transforme le processeur en véritable processeur orienté objet, mais passons ces détails pour le moment. Vu que les segments correspondent à des objets manipulés par le programme, on peut deviner que leur nombre évolue au cours du temps. En effet, les programmes modernes peuvent demander au système d'exploitation du rab de mémoire pour allouer une nouvelle structure de données. Avec la segmentation à granularité fine, cela demande d'allouer un nouveau segment à chaque nouvelle allocation mémoire, à chaque création d'une nouvelle structure de données ou d'un objet. De plus, les programmes peuvent libérer de la mémoire, en supprimant les structures de données ou objets dont ils n'ont plus besoin. Avec la segmentation à granularité fine, cela revient à détruire le segment alloué pour ces objets/structures de données. Le nombre de segments est donc dynamique, il change au cours de l'exécution du programme. ===Les tables de segments avec la segmentation=== La présence de plusieurs segments par programme a un impact sur la table des segments. Avec la relocation matérielle, elle conte nait un segment par programme. Chaque entrée, chaque ligne de la table des segment, mémorisait l'adresse de base, l'adresse limite, un bit de présence pour la mémoire virtuelle et des autorisations liées à la protection mémoire. Avec la segmentation, les choses sont plus compliquées, car il y a plusieurs segments par programme. Les entrées ne sont pas modifiées, mais elles sont organisées différemment. Avec cette forme de segmentation, la table des segments doit respecter plusieurs contraintes. Premièrement, il y a plusieurs segments par programmes. Deuxièmement, le nombre de segments est variable : certains programmes se contenteront d'un seul segment, d'autres de dizaine, d'autres plusieurs centaines, etc. Il y a typiquement deux manières de faire : soit utiliser une table des segments uniques, utiliser une table des segment par programme. Il est possible d'utiliser une table des segment unique qui mémorise tous les segments de tous les processus, système d'exploitation inclut. On parle alors de '''table des segment globale'''. Mais cette solution n'est pas utilisée avec la segmentation proprement dite. Elle est utilisée sur les architectures à capacité qu'on détaillera vers la fin du chapitre, dans une section dédiée. A la place, la segmentation utilise une table de segment par processus/programme, chacun ayant une '''table des segment locale'''. Dans les faits, les choses sont plus compliquées. Le système d'exploitation doit savoir où se trouvent les tables de segment locale pour chaque programme. Pour cela, il a besoin d'utiliser une table de segment globale, dont chaque entrée pointe non pas vers un segment, mais vers une table de segment locale. Lorsque l'OS effectue une commutation de contexte, il lit la table des segment globale, pour récupérer un pointeur vers celle-ci. Ce pointeur est alors chargé dans un registre du processeur, qui mémorise l'adresse de la table locale, ce qui sert lors des accès mémoire. Une telle organisation fait que les segments d'un processus/programme sont invisibles pour les autres, il y a une certaine forme de sécurité. Un programme ne connait que sa table de segments locale, il n'a pas accès directement à la table des segments globales. Tout accès mémoire se passera à travers la table de segment locale, il ne sait pas où se trouvent les autres tables de segment locales. Les processeurs x86 sont dans ce cas : ils utilisent une table de segment globale couplée à autant de table des segments qu'il y a de processus en cours d'exécution. La table des segments globale s'appelle la '''''Global Descriptor Table''''' et elle peut contenir 8192 segments maximum, ce qui permet le support de 8192 processus différents. Les tables de segments locales sont appelées les '''''Local Descriptor Table''''' et elles font aussi 8192 segments maximum, ce qui fait 8192 segments par programme maximum. Il faut noter que la table de segment globale peut mémoriser des pointeurs vers les routines d'interruption, certaines données partagées (le tampon mémoire pour le clavier) et quelques autres choses, qui n'ont pas leur place dans les tables de segment locales. ===La relocation avec la segmentation=== La table des segments locale mémorise les adresses de base et limite de chaque segment, ainsi que d'autres méta-données. Les informations pour un segment sont regroupés dans un '''descripteur de segment''', qui est codé sur plusieurs octets, et qui regroupe : adresse de base, adresse limite, bit de présence en RAM, méta-données de protection mémoire. La table des segments est un tableau dans lequel les descripteurs de segment sont placés les uns à la suite des autres en mémoire RAM. La table des segments est donc un tableau de segment. Les segments d'un programme sont numérotés, le nombre s'appelant un '''indice de segment''', appelé '''sélecteur de segment''' dans la terminologie Intel. L'indice de segment n'est autre que l'indice du segment dans ce tableau. [[File:Global Descriptor table.png|centre|vignette|upright=2|Table des segments locale.]] Il n'y a pas de registre de segment proprement dit, qui mémoriserait l'adresse de base. A la place, les segments sont adressés de manière indirecte. A la place, les registres de segment mémorisent des sélecteurs de segment. Ils sont utilisés pour lire l'adresse de base/limite dans la table de segment en mémoire RAM. Pour cela, un registre mémorise l'adresse de la table de segment locale, sa position en mémoire RAM. Toute lecture ou écriture se fait en deux temps, en deux accès mémoire, consécutifs. Premièrement, le numéro de segment est utilisé pour adresser la table des segment. La lecture récupère alors un pointeur vers ce segment. Deuxièmement, ce pointeur est utilisé pour faire la lecture ou écriture. Plus précisément, la première lecture récupère un descripteur de segment qui contient l'adresse de base, le pointeur voulu, mais aussi l'adresse limite et d'autres informations. [[File:Segmentation avec table des segments.png|centre|vignette|upright=2|Segmentation avec table des segments]] L'accès à la table des segments se fait automatiquement à chaque accès mémoire. La conséquence est que chaque accès mémoire demande d'en faire deux : un pour lire la table des segments, l'autre pour l'accès lui-même. Il s'agit en quelque sorte d'une forme d'adressage indirect mémoire. Un point important est que si le premier accès ne fait qu'une simple lecture dans un tableau, le second accès implique des calculs d'adresse. En effet, le premier accès récupère l'adresse de base du segment, mais le second accès sélectionne une donnée dans le segment, ce qui demande de calculer son adresse. L'adresse finale se déduit en combinant l'adresse de base avec un décalage (''offset'') qui donne la position de la donnée dans ce segment. L'indice de segment est utilisé pour récupérer l'adresse de base du segment. Une fois cette adresse de base connue, on lui additionne le décalage pour obtenir l'adresse finale. [[File:Table des segments.png|centre|vignette|upright=2|Traduction d'adresse avec une table des segments.]] Pour effectuer automatiquement l'accès à la table des segments, le processeur doit contenir un registre supplémentaire, qui contient l'adresse de la table de segment, afin de la localiser en mémoire RAM. Nous appellerons ce registre le '''pointeur de table'''. Le pointeur de table est combiné avec l'indice de segment pour adresser le descripteur de segment adéquat. [[File:Segment 2.svg|centre|vignette|upright=2|Traduction d'adresse avec une table des segments, ici appelée table globale des de"scripteurs (terminologie des processeurs Intel x86).]] Un point important est que la table des segments n'est pas accessible pour le programme en cours d'exécution. Il ne peut pas lire le contenu de la table des segments, et encore moins la modifier. L'accès se fait seulement de manière indirecte, en faisant usage des indices de segments, mais c'est un adressage indirect. Seul le système d'exploitation peut lire ou écrire la table des segments directement. Plus haut, j'ai dit que tout accès mémoire impliquait deux accès mémoire : un pour charger le descripteur de segment, un autre pour la lecture/écriture proprement dite. Cependant, cela aurait un impact bien trop grand sur les performances. Dans les faits, les processeurs avec segmentations intégraient un '''cache de descripteurs de segments''', pour limiter la casse. Quand un descripteur de segment est lu depuis la RAM, il est copié dans ce cache. Les accès ultérieurs accédent au descripteur dans le cache, pas besoin de passer par la RAM. L'intel 386 avait un cache de ce type. ===La protection mémoire : les accès hors-segments=== Comme avec la relocation matérielle, le processeur détecte les débordements de segment. Pour cela, il compare l'adresse logique accédée avec l'adresse limite, ou compare la taille limite avec le décalage. De nombreux processeurs, comme l'Intel 386, préféraient utiliser la taille du segment, pour une question d'optimisation. En effet, si on compare l'adresse finale avec l'adresse limite, on doit faire la relocation avant de comparer l'adresse relocatée. Mais en utilisant la taille, ce n'est pas le cas : on peut faire la comparaison avant, pendant ou après la relocation. Un détail à prendre en compte est la taille de la donnée accédée. Sans cela, la comparaison serait très simple : on vérifie si ''décalage <= taille du segment'', ou on compare des adresses de la même manière. Mais imaginez qu'on accède à une donnée de 4 octets : il se peut que l'adresse de ces 4 octets rentre dans le segment, mais que quelques octets débordent. Par exemple, les deux premiers octets sont dans le segment, mais pas les deux suivants. La vraie comparaison est alors : ''décalage + 4 octets <= taille du segment''. Mais il est possible de faire le calcul autrement, et quelques processeurs comme l'Intel 386 ne s'en sont pas privé. Il calculait la différence ''taille du segment - décalage'', et vérifiait le résultat. Le processeur gérait des données de 1, 2 et 4 octets, ce qui fait que le résultat devait être entre 0 et 3. Le processeur prenait le résultat de la soustraction, et vérifiait alors que les 30 bits de poids fort valaient bien 0. Il vérifiait aussi que les deux bits de poids faible avaient la bonne valeur. [[File:Vm7.svg|centre|vignette|upright=2|Traduction d'adresse avec vérification des accès hors-segment.]] Une nouveauté fait son apparition avec la segmentation : la '''gestion des droits d'accès'''. Par exemple, il est possible d'interdire d'exécuter le contenu d'un segment, ce qui fournit une protection contre certaines failles de sécurité ou certains virus. Lorsqu'on exécute une opération interdite, le processeur lève une exception matérielle, à charge du système d'exploitation de gérer la situation. Pour cela, chaque segment se voit attribuer un certain nombre d'autorisations d'accès qui indiquent si l'on peut lire ou écrire dedans, si celui-ci contient un programme exécutable, etc. Les autorisations pour chaque segment sont placées dans le descripteur de segment. Elles se résument généralement à quelques bits, qui indiquent si le segment est accesible en lecture/écriture ou exécutable. Le tout est souvent concaténé dans un ou deux '''octets de droits d'accès'''. L'implémentation de la protection mémoire dépend du CPU considéré. Les CPU microcodés peuvent en théorie utiliser le microcode. Lorsqu'une instruction mémoire s'exécute, le microcode effectue trois étapes : lire le descripteur de segment, faire les tests de protection mémoire, exécuter la lecture/écriture ou lever une exception. Létape de test est réalisée avec un ou plusieurs micro-branchements. Par exemple, une écriture va tester le bit R/W du descripteur, qui indique si on peut écrire dans le segment, en utilisant un micro-branchement. Le micro-branchement enverra vers une routine du microcode en cas d'erreur. Les tests de protection mémoire demandent cependant de tester beaucoup de conditions différentes. Par exemple, le CPU Intel 386 testait moins d'une dizaine de conditions pour certaines instructions. Il est cependant possible de faire plusieurs comparaisons en parallèle en rusant un peu. Il suffit de mémoriser les octets de droits d'accès dans un registre interne, de masquer les bits non-pertinents, et de faire une comparaison avec une constante adéquate, qui encode la valeur que doivent avoir ces bits. Une solution alternative utiliser un circuit combinatoire pour faire les tests de protection mémoire. Les tests sont alors faits en parallèles, plutôt qu'un par un par des micro-branchements. Par contre, le cout en matériel est assez important. Il faut ajouter ce circuit combinatoire, ce qui demande pas mal de circuits. ===La mémoire virtuelle avec la segmentation=== La mémoire virtuelle est une fonctionnalité souvent implémentée sur les processeurs qui gèrent la segmentation, alors que les processeurs avec relocation matérielle s'en passaient. Il faut dire que l'implémentation de la mémoire virtuelle est beaucoup plus simple avec la segmentation, comparé à la relocation matérielle. Le remplacement des registres de base par des sélecteurs de segment facilite grandement l'implémentation. Le problème de la mémoire virtuelle est que les segments peuvent être swappés sur le disque dur n'importe quand, sans que le programme soit prévu. Le swapping est réalisé par une interruption de l'OS, qui peut interrompre le programme n'importe quand. Et si un segment est swappé, le registre de base correspondant devient invalide, il point sur une adresse en RAM où le segment était, mais n'est plus. De plus, les segments peuvent être déplacés en mémoire, là encore n'importe quand et d'une manière invisible par le programme, ce qui fait que les registres de base adéquats doivent être modifiés. Si le programme entier est swappé d'un coup, comme avec la relocation matérielle simple, cela ne pose pas de problèmes. Mais dès qu'on utilise plusieurs registres de base par programme, les choses deviennent soudainement plus compliquées. Le problème est qu'il n'y a pas de mécanismes pour choisir et invalider le registre de base adéquat quand un segment est déplacé/swappé. En théorie, on pourrait imaginer des systèmes qui résolvent le problème au niveau de l'OS, mais tous ont des problèmes qui font que l'implémentation est compliquée ou que les performances sont ridicules. L'usage d'une table des segments accédée à chaque accès résout complètement le problème. La table des segments est accédée à chaque accès mémoire, elle sait si le segment est swappé ou non, chaque accès vérifie si le segment est en mémoire et quelle est son adresse de base. On peut changer le segment de place n'importe quand, le prochain accès récupérera des informations à jour dans la table des segments. L'implémentation de la mémoire virtuelle avec la segmentation est simple : il suffit d'ajouter un bit dans les descripteurs de segments, qui indique si le segment est swappé ou non. Tout le reste, la gestion de ce bit, du swap, et tout ce qui est nécessaire, est délégué au système d'exploitation. Lors de chaque accès mémoire, le processeur vérifie ce bit avant de faire la traduction d'adresse, et déclenche une exception matérielle si le bit indique que le segment est swappé. L'exception matérielle est gérée par l'OS. ===Le partage de segments=== Il est possible de partager un segment entre plusieurs applications. Cela peut servir pour partager des données entre deux programmes : un segment de données partagées est alors partagé entre deux programmes. Partager un segment de code est utile pour les bibliothèques partagées : la bibliothèque est placée dans un segment dédié, qui est partagé entre les programmes qui l'utilisent. Partager un segment de code est aussi utile quand plusieurs instances d'une même application sont lancés simultanément : le code n'ayant pas de raison de changer, celui-ci est partagé entre toutes les instances. Mais ce n'est là qu'un exemple. La première solution pour cela est de configurer les tables de segment convenablement. Le même segment peut avoir des droits d'accès différents selon les processus. Les adresses de base/limite sont identiques, mais les tables des segments ont alors des droits d'accès différents. Mais cette méthode de partage des segments a plusieurs défauts. Premièrement, les sélecteurs de segments ne sont pas les mêmes d'un processus à l'autre, pour un même segment. Le segment partagé peut correspondre au segment numéro 80 dans le premier processus, au segment numéro 1092 dans le second processus. Rien n'impose que les sélecteurs de segment soient les mêmes d'un processus à l'autre, pour un segment identique. Deuxièmement, les adresses limite et de base sont dupliquées dans plusieurs tables de segments. En soi, cette redondance est un souci mineur. Mais une autre conséquence est une question de sécurité : que se passe-t-il si jamais un processus a une table des segments corrompue ? Il se peut que pour un segment identique, deux processus n'aient pas la même adresse limite, ce qui peut causer des failles de sécurité. Un processus peut alors subir un débordement de tampon, ou tout autre forme d'attaque. [[File:Vm9.png|centre|vignette|upright=2|Illustration du partage d'un segment entre deux applications.]] Une seconde solution, complémentaire, utilise une table de segment globale, qui mémorise des segments partagés ou accessibles par tous les processus. Les défauts de la méthode précédente disparaissent avec cette technique : un segment est identifié par un sélecteur unique pour tous les processus, il n'y a pas de duplication des descripteurs de segment. Par contre, elle a plusieurs défauts. Le défaut principal est que cette table des segments est accessible par tous les processus, impossible de ne partager ses segments qu'avec certains pas avec les autres. Un autre défaut est que les droits d'accès à un segment partagé sont identiques pour tous les processus. Impossible d'avoir un segment partagé accessible en lecture seule pour un processus, mais accessible en écriture pour un autre. Il est possible de corriger ces défauts, mais nous en parlerons dans la section sur les architectures à capacité. ===L'extension d'adresse avec la segmentation=== L'extension d'adresse est possible avec la segmentation, de la même manière qu'avec la relocation matérielle. Il suffit juste que les adresses de base soient aussi grandes que le bus d'adresse. Mais il y a une différence avec la relocation matérielle : un même programme peut utiliser plus de mémoire qu'il n'y en a dans l'espace d'adressage. La raison est simple : un segment peut prendre tout l'espace d'adressage, et il y a plusieurs segments par programme. Pour donner un exemple, prenons un processeur 16 bits, qui peut adresser 64 kibioctets, associé à une mémoire de 4 mébioctets. Il est possible de placer le code machine dans les premiers 64k de la mémoire, la pile du programme dans les 64k suivants, le tas dans les 64k encore après, et ainsi de suite. Le programme dépasse donc les 64k de mémoire de l'espace d'adressage. Ce genre de chose est impossible avec la relocation, où un programme est limité par l'espace d'adressage. ===Le mode protégé des processeurs x86=== L'Intel 80286, aussi appelé 286, ajouta un mode de segmentation séparé du mode réel, qui ajoute une protection mémoire à la segmentation, ce qui lui vaut le nom de '''mode protégé'''. Dans ce mode, les registres de segment ne contiennent pas des adresses de base, mais des sélecteurs de segments qui sont utilisés pour l'accès à la table des segments en mémoire RAM. Le 286 bootait en mode réel, puis le système d'exploitation devait faire quelques manipulations pour passer en mode protégé. Le 286 était pensé pour être rétrocompatible au maximum avec le 80186. Mais les différences entre le 286 et le 8086 étaient majeures, au point que les applications devaient être réécrites intégralement pour profiter du mode protégé. Un mode de compatibilité permettait cependant aux applications destinées au 8086 de fonctionner, avec même de meilleures performances. Aussi, le mode protégé resta inutilisé sur la plupart des applications exécutées sur le 286. Vint ensuite le processeur 80386, renommé en 386 quelques années plus tard. Sur ce processeur, les modes réel et protégé sont conservés tel quel, à une différence près : toutes les adresses passent à 32 bits, qu'il s'agisse des adresses de base, limite ou des ''offsets''. Le processeur peut donc adresser un grand nombre de segments : 2^32, soit plus de 4 milliards. Les segments grandissent aussi et passent de 64 KB maximum à 4 gibioctets maximum. Mais surtout : le 386 ajouta le support de la pagination en plus de la segmentation. Ces modifications ont été conservées sur les processeurs 32 bits ultérieurs. Les processeurs x86 gèrent deux types de tables des segments : une table locale pour chaque processus, et une table globale partagée entre tous les processus. Il ne peut y avoir qu'une table locale d'active, vu que le processeur ne peut exécuter qu'un seul processus en même temps. Chaque table locale définit 8192 segments, pareil pour la table globale. La table globale est utilisée pour les segments du noyau et la mémoire partagée entre processus. Un défaut est qu'un segment partagé par la table globale est visible par tous les processus, avec les mêmes droits d'accès. Ce qui fait que cette méthode était peu utilisée en pratique. La table globale mémorise aussi des pointeurs vers les tables locales, avec un descripteur de segment par table locale. Sur les processeurs x86 32 bits, un descripteur de segment est organisé comme suit, pour les architectures 32 bits. On y trouve l'adresse de base et la taille limite, ainsi que de nombreux bits de contrôle. Le premier groupe de bits de contrôle est l'octet en bleu à droite. Il contient : * le bit P qui indique que l'entrée contient un descripteur valide, qu'elle n'est pas vide ; * deux bits DPL qui indiquent le niveau de privilège du segment (noyau, utilisateur, les deux intermédiaires spécifiques au x86) ; * un bit S qui précise si le segment est de type système (utiles pour l'OS) ou un segment de code/données. * un champ Type qui contient les bits suivants : ** un bit E qui indique si le segment contient du code exécutable ou non ; ** le bit RW qui indique s'il est en lecture seule ou non ;; ** Un bit A qui indique que le segment a récemment été accédé, information utile pour l'OS; ** un bit DC assez spécifiques. En haut à gauche, en bleu, on trouve deux bits : * Le bit G indique comment interpréter la taille contenue dans le descripteur : 0 si la taille est exprimée en octets, 1 si la taille est un nombre de pages de 4 kibioctets. Ce bit précise si on utilise la segmentation seule, ou combinée avec la pagination. * Le bit DB précise si l'on utilise des segments en mode de compatibilité 16 bits ou des segments 32 bits. [[File:SegmentDescriptor.svg|centre|vignette|upright=3|Segment Descriptor]] Les indices de segment sont appelés des sélecteurs de segment. Ils ont une taille de 16 bits, mais 3 bits sont utilisés pour encoder des méta-données. Le numéro de segment est donc codé sur 13 bits, ce qui permettait de gérer maximum 8192 segments par table de segment (locale ou globale). Les 16 bits sont organisés comme suit : * 13 bits pour le numéro du segment dans la table des segments, l'indice de segment proprement dit ; * un bit qui précise s'il faut accéder à la table des segments globale ou locale ; * deux bits qui indiquent le niveau de privilège de l'accès au segment (les 4 niveaux de protection, dont l'espace noyau et utilisateur). [[File:SegmentSelector.svg|centre|vignette|upright=1.5|Sélecteur de segment 16 bit.]] En tout, l'indice permet de gérer 8192 segments pour la table locale et 8192 segments de la table globale. ====L'implémentation de la protection mémoire sur le 386==== Le CPU 386 était le premier à implémenter la protection mémoire avec des segments. Pour cela, il intégrait une '''''Protection Test Unit''''', séparée du microcode, qu'on va abrévier en PTU. Précisément, il s'agissait d'un PLA (''Programmable Logic Array''), une sorte d'intermédiaire entre circuit logique fait sur mesure et mémoire ROM, qu'on a déjà abordé dans le chapitre sur les mémoires ROM. Mais cette unité ne faisait pas tout, le microcode était aussi impliqué. La protection mémoire teste la valeur des bits P, S, X, E, R/W. Elle teste aussi les niveaux de privilège, avec deux bits DPL et CPL. En tout, le processeur pouvait tester 148 conditions différentes en parallèle dans la PTU. Cependant, les niveaux de privilèges étaient pré-traités par le microcode. Le microcode vérifiait aussi s'il y avait une erreur en terme d’anneau mémoire, avec par "exemple un segment en mode noyau accédé alors que le CPU est en espace utilisateur. Il fournissait alors un résultat sur deux bits, qui indiquait s'il y avait une erreur ou non, que la PTU utilisait. Mais toutes les conditions n'étaient pas pertinentes à un instant t. Par exemple, il est pertinent de vérifier si le bit R/W était cohérent si l'instruction à exécuter est une écriture. Mais il n'y a pas besoin de tester le bit E qui indique qu'un segment est exécutable ou non, pour une lecture. En tout, le processeur pouvait se retrouver dans 33 situations possibles, chacune demandant de tester un sous-ensemble des 148 conditions. Pour préciser quel sous-ensembles tester, la PTU recevait un code opération, généré par le microcode. Pour faire les tests de protection mémoire, le microcode avait une micro-opération nommée ''protection test operation'', qui envoyait les droits d'accès à la PTU. Lors de l'exécution d'une ''protection test operation'', le PLA recevait un descripteur de segment, lu depuis la mémoire RAM, ainsi qu'un code opération provenant du microcode. {|class="wikitable" |+ Entrée de la ''Protection Test Unit'' |- ! 15 - 14 !! 13 - 12 !! 11 !! 10 !! 9 !! 8 !! 7 !! 6 !! 5-0 |- | P1 , P2 || || P || S || X || E || R/W || A || Code opération |- | Niveaux de privilèges cohérents/erreur || || Segment présent en mémoire ou swappé || S || X || Segment exécutable ou non || Segment accesible en lecture/écriture || Segment récemment accédé || Code opération |} Il fournissait en sortie un bit qui indiquait si une erreur de protection mémoire avait eu lieu ou non. Il fournissait aussi une adresse de 12 bits, utilisée seulement en cas d'erruer. Elle pointait dans le microcode, sur un code levant une exception en cas d'erreur. Enfin, la PTU fournissait 4 bits pouvant être testés par un branchement dans le microcode. L'un d'entre eux demandait de tester s'il y a un accès hors-limite, les autres étaient assez peu reliés à la protection mémoire. Un détail est que le chargement du descripteur de segment est réalisé par une fonction dans le microcode. Elle est appliquée pour toutes les instructions ou situations qui demandent de faire un accès mémoire. Et les tests de protection mémoire sont réalisés dans cette fonction, pas après elle. Vu qu'il s'agit d'une fonction exécutée quelque soit l'instruction, le microcode doit transférer le code opération à cette fonction. Le microcode est pour cela associé à un registre interne, dans lequel le code opération est mémorisé, avant d'appeler la fonction. Le microcode a une micro-opération PTSAV (''Protection Save'') pour mémoriser le code opération dans ce registre. Dans la fonction qui charge le descripteur, une micro-opération PTOVRR (''Protection Override'') lit le code opération dans ce registre, et lance les tests nécessaires. Il faut noter que le PLA était certes plus rapide que de tester les conditions une par une, mais il était assez lent. La PTU mettait environ 3 cycles d'horloges pour rendre son résultat. Le microcode en profitait alors pour exécuter des micro-opérations durant ces 3 cycles d'attente. Par exemple, le microcode pouvait en profiter pour lire l'adresse de base dans le descripteur, si elle n'a pas été chargée avant (les descripteur était chargé en deux fois). Il fallait cependant que les trois micro-opérations soient valides, peu importe qu'il y ait une erreur de protection mémoire ou non. Ou du moins, elles produisaient un résultat qui n'est pas utilisé en cas d'erreur. Si ce n'était pas possible, le microcode ajoutait des NOP pendant ce temps d'attente de 3 cycles. Le bit A du descripteur de segment indique que le segment a récemment été accédé. Il est mis à jour après les tests de protection mémoire, quand ceux-ci indiquent que l'accès mémoire est autorisé. Le bit A est mis à 1 si la PTU l'autorise. Pour cela, la PTU utilise un des 4 bits de sortie mentionnés plus haut : l'un d'entre eux indique que le bit A doit être mis à 1. La mise à jour est ensuite réalisée par le microcode, qui utilise trois micro-opérations pour le mettre à jour. ====Le cache de descripteur de segment==== Pour améliorer les performances, le 386 intégrait un '''cache de descripteurs de segment''', aussi appelé le cache de descripteurs. Lorsqu'un descripteur état chargé pour la première fois, il était copié dans ce ache de descripteurs de segment. Les accès mémoire ultérieur lisaient le descripteur de segment depuis ce cache, pas depuis la table des segments en RAM. Un point important est que le cache de descripteurs gère aussi bien les segments en mode réel qu'en mode protégé. Récupérer l'adresse de base depuis cache se fait un peu différemment en mode réel et protégé, mais le cache gère cela tout seul. Idem pour récupérer la taille/adresse limite. Il faut noter que ce cache avait un petit problème : il n'était pas cohérent avec la mémoire RAM. Par cohérent, on veut dire que si on modifie la table des segments en mémoire RAM, la copie du descripteur dans le cache n'est pas mise à jour. Le seul moyen pour la mettre à jour est de recharger de force le descripteur, ce qui demande de faire des manipulations assez complexes. Les deux dernières propriétés étaient à l'origine d'une fonctionnalité non-prévue, celle de l''''''unreal mode'''''. Il s'agissait d'un mode réel amélioré, capable d'utiliser des segments de 4 gibioctets et des adresses de 32 bits. Passer en mode ''unreal'' pouvait se faire de deux manières. La première demandait d'entrer en mode protégé pour configurer des segments de grande taille, de charger leurs descripteurs dans le cache de descripteur, puis de revenir en mode réel. En mode réel, les descripteurs dans le cache étaient encore disponibles et on pouvait les lire dans le cache. Une autre solution utilisait une instruction non-documentées : l'instruction LOADALL. Elle permettait de charger les descripteurs de segments dans le cache de descripteurs. ====Le ''Hardware task switching'' des CPU x86==== Les systèmes d’exploitation modernes peuvent lancer plusieurs logiciels en même temps. Les logiciels sont alors exécutés à tour de rôle. Passer d'un programme à un autre est ce qui s'appelle une commutation de contexte. Lors d'une commutation de contexte, l'état du processeur est sauvegardé, afin que le programme stoppé puisse reprendre là où il était. Il arrivera un moment où le programme stoppé redémarrera et il doit reprendre dans l'état exact où il s'est arrêté. Deuxièmement, le programme à qui c'est le tour restaure son état. Cela lui permet de revenir là où il était avant d'être stoppé. Il y a donc une sauvegarde et une restauration des registres. Divers processeurs incorporent des optimisations matérielles pour rendre la commutation de contexte plus rapide. Ils peuvent sauvegarder et restaurer les registres du processeur automatiquement lors d'une interruption de commutation de contexte. Les registres sont sauvegardés dans des structures de données en mémoire RAM, appelées des '''contextes matériels'''. Sur les processeurs x86, il s'agit de la technique d{{'}}''Hardware Task Switching''. Fait intéressant, le ''Hardware Task Switching'' se base beaucoup sur les segments mémoires. Avec ''Hardware Task Switching'', chaque contexte matériel est mémorisé dans son propre segment mémoire, séparé des autres. Les segments pour les contextes matériels sont appelés des '''''Task State Segment''''' (TSS). Un TSS mémorise tous les registres généraux, le registre d'état, les pointeurs de pile, le ''program counter'' et quelques registres de contrôle du processeur. Par contre, les registres flottants ne sont pas sauvegardés, de même que certaines registres dit SIMD que nous n'avons pas encore abordé. Et c'est un défaut qui fait que le ''Hardware Task Switching'' n'est plus utilisé. Le programme en cours d'exécution connait l'adresse du TSS qui lui est attribué, car elle est mémorisée dans un registre appelé le '''''Task Register'''''. En plus de pointer sur le TSS, ce registre contient aussi les adresses de base et limite du segment en cours. Pour être plus précis, le ''Task Register'' ne mémorise pas vraiment l'adresse du TSS. A la place, elle mémorise le numéro du segment, le numéro du TSS. Le numéro est codé sur 16 bits, ce qui explique que 65 536 segments sont adressables. Les instructions LDR et STR permettent de lire/écrire ce numéro de segment dans le ''Task Register''. Le démarrage d'un programme a lieu automatiquement dans plusieurs circonstances. La première est une instruction de branchement CALL ou JMP adéquate. Le branchement fournit non pas une adresse à laquelle brancher, mais un numéro de segment qui pointe vers un TSS. Cela permet à une routine du système d'exploitation de restaurer les registres et de démarrer le programme en une seule instruction de branchement. Une seconde circonstance est une interruption matérielle ou une exception, mais nous la mettons de côté. Le ''Task Register'' est alors initialisé avec le numéro de segment fournit. S'en suit la procédure suivante : * Le ''Task Register'' est utilisé pour adresser la table des segments, pour récupérer un pointeur vers le TSS associé. * Le pointeur est utilisé pour une seconde lecture, qui adresse le TSS directement. Celle-ci restaure les registres du processeur. En clair, on va lire le ''TSS descriptor'' dans la GDT, puis on l'utilise pour restaurer les registres du processeur. [[File:Hardware Task Switching x86.png|centre|vignette|upright=2|Hardware Task Switching x86]] ===La segmentation sur les processeurs Burrough B5000 et plus=== Le Burrough B5000 est un très vieil ordinateur, commercialisé à partir de l'année 1961. Ses successeurs reprennent globalement la même architecture. C'était une machine à pile, doublé d'une architecture taguée, choses très rare de nos jours. Mais ce qui va nous intéresser dans ce chapitre est que ce processeur incorporait la segmentation, avec cependant une différence de taille : un programme avait accès à un grand nombre de segments. La limite était de 1024 segments par programme ! Il va de soi que des segments plus petits favorise l'implémentation de la mémoire virtuelle, mais complexifie la relocation et le reste, comme nous allons le voir. Le processeur gère deux types de segments : les segments de données et de procédure/fonction. Les premiers mémorisent un bloc de données, dont le contenu est laissé à l'appréciation du programmeur. Les seconds sont des segments qui contiennent chacun une procédure, une fonction. L'usage des segments est donc différent de ce qu'on a sur les processeurs x86, qui n'avaient qu'un segment unique pour l'intégralité du code machine. Un seul segment de code machine x86 est découpé en un grand nombre de segments de code sur les processeurs Burrough. La table des segments contenait 1024 entrées de 48 bits chacune. Fait intéressant, chaque entrée de la table des segments pouvait mémoriser non seulement un descripteur de segment, mais aussi une valeur flottante ou d'autres types de données ! Parler de table des segments est donc quelque peu trompeur, car cette table ne gère pas que des segments, mais aussi des données. La documentation appelaiat cette table la '''''Program Reference Table''''', ou PRT. La raison de ce choix quelque peu bizarre est que les instructions ne gèrent pas d'adresses proprement dit. Tous les accès mémoire à des données en-dehors de la pile passent par la segmentation, ils précisent tous un indice de segment et un ''offset''. Pour éviter d'allouer un segment pour chaque donnée, les concepteurs du processeur ont décidé qu'une entrée pouvait contenir directement la donnée entière à lire/écrire. La PRT supporte trois types de segments/descripteurs : les descripteurs de données, les descripteurs de programme et les descripteurs d'entrées-sorties. Les premiers décrivent des segments de données. Les seconds sont associés aux segments de procédure/fonction et sont utilisés pour les appels de fonction (qui passent, eux aussi, par la segmentation). Le dernier type de descripteurs sert pour les appels systèmes et les communications avec l'OS ou les périphériques. Chaque entrée de la PRT contient un ''tag'', une suite de bit qui indique le type de l'entrée : est-ce qu'elle contient un descripteur de segment, une donnée, autre. Les descripteurs contiennent aussi un ''bit de présence'' qui indique si le segment a été swappé ou non. Car oui, les segments pouvaient être swappés sur ce processeur, ce qui n'est pas étonnant vu que les segments sont plus petits sur cette architecture. Le descripteur contient aussi l'adresse de base du segment ainsi que sa taille, et diverses informations pour le retrouver sur le disque dur s'il est swappé. : L'adresse mémorisée ne faisait que 15 bits, ce qui permettait d'adresse 32 kibi-mots, soit 192 kibioctets de mémoire. Diverses techniques d'extension d'adressage étaient disponibles pour contourner cette limitation. Outre l'usage de l{{'}}''overlay'', le processeur et l'OS géraient aussi des identifiants d'espace d'adressage et en fournissaient plusieurs par processus. Les processeurs Borrough suivants utilisaient des adresses plus grandes, de 20 bits, ce qui tempérait le problème. [[File:B6700Word.jpg|centre|vignette|upright=2|Structure d'un mot mémoire sur le B6700.]] ==Les architectures à capacités== Les architectures à capacité utilisent la segmentation à granularité fine, mais ajoutent des mécanismes de protection mémoire assez particuliers, qui font que les architectures à capacité se démarquent du reste. Les architectures de ce type sont très rares et sont des processeurs assez anciens. Le premier d'entre eux était le Plessey System 250, qui date de 1969. Il fu suivi par le CAP computer, vendu entre les années 70 et 77. En 1978, le System/38 d'IBM a eu un petit succès commercial. En 1980, la Flex machine a aussi été vendue, mais à très peu d'examplaires, comme les autres architectures à capacité. Et enfin, en 1981, l'architecture à capacité la plus connue, l'Intel iAPX 432 a été commercialisée. Depuis, la seule architecture de ce type est en cours de développement. Il s'agit de l'architecture CHERI, dont la mise en projet date de 2014. ===Le partage de la mémoire sur les architectures à capacités=== Le partage de segment est grandement modifié sur les architectures à capacité. Avec la segmentation normale, il y a une table de segment par processus. Les conséquences sont assez nombreuses, mais la principale est que partager un segment entre plusieurs processus est compliqué. Les défauts ont été évoqués plus haut. Les sélecteurs de segments ne sont pas les mêmes d'un processus à l'autre, pour un même segment. De plus, les adresses limite et de base sont dupliquées dans plusieurs tables de segments, et cela peut causer des problèmes de sécurité si une table des segments est modifiée et pas l'autre. Et il y a d'autres problèmes, tout aussi importants. [[File:Partage des segments avec la segmentation.png|centre|vignette|upright=1.5|Partage des segments avec la segmentation]] A l'opposé, les architectures à capacité utilisent une table des segments unique pour tous les processus. La table des segments unique sera appelée dans de ce qui suit la '''table des segments globale''', ou encore la table globale. En conséquence, les adresses de base et limite ne sont présentes qu'en un seul exemplaire par segment, au lieu d'être dupliquées dans autant de processus que nécessaire. De plus, cela garantit que l'indice de segment est le même quel que soit le processus qui l'utilise. Un défaut de cette approche est au niveau des droits d'accès. Avec la segmentation normale, les droits d'accès pour un segment sont censés changer d'un processus à l'autre. Par exemple, tel processus a accès en lecture seule au segment, l'autre seulement en écriture, etc. Mais ici, avec une table des segments uniques, cela ne marche plus : incorporer les droits d'accès dans la table des segments ferait que tous les processus auraient les mêmes droits d'accès au segment. Et il faut trouver une solution. ===Les capacités sont des pointeurs protégés=== Pour éviter cela, les droits d'accès sont combinés avec les sélecteurs de segments. Les sélecteurs des segments sont remplacés par des '''capacités''', des pointeurs particuliers formés en concaténant l'indice de segment avec les droits d'accès à ce segment. Si un programme veut accéder à une adresse, il fournit une capacité de la forme "sélecteur:droits d'accès", et un décalage qui indique la position de l'adresse dans le segment. Il est impossible d'accéder à un segment sans avoir la capacité associée, c'est là une sécurité importante. Un accès mémoire demande que l'on ait la capacité pour sélectionner le bon segment, mais aussi que les droits d'accès en permettent l'accès demandé. Par contre, les capacités peuvent être passées d'un programme à un autre sans problème, les deux programmes pourront accéder à un segment tant qu'ils disposent de la capacité associée. [[File:Comparaison entre capacités et adresses segmentées.png|centre|vignette|upright=2.5|Comparaison entre capacités et adresses segmentées]] Mais cette solution a deux problèmes très liés. Au niveau des sélecteurs de segment, le problème est que les sélecteur ont une portée globale. Avant, l'indice de segment était interne à un programme, un sélecteur ne permettait pas d'accéder au segment d'un autre programme. Sur les architectures à capacité, les sélecteurs ont une portée globale. Si un programme arrive à forger un sélecteur qui pointe vers un segment d'un autre programme, il peut théoriquement y accéder, à condition que les droits d'accès le permettent. Et c'est là qu'intervient le second problème : les droits d'accès ne sont plus protégés par l'espace noyau. Les droits d'accès étaient dans la table de segment, accessible uniquement en espace noyau, ce qui empêchait un processus de les modifier. Avec une capacité, il faut ajouter des mécanismes de protection qui empêchent un programme de modifier les droits d'accès à un segment et de générer un indice de segment non-prévu. La première sécurité est qu'un programme ne peut pas créer une capacité, seul le système d'exploitation le peut. Les capacités sont forgées lors de l'allocation mémoire, ce qui est du ressort de l'OS. Pour rappel, un programme qui veut du rab de mémoire RAM peut demander au système d'exploitation de lui allouer de la mémoire supplémentaire. Le système d'exploitation renvoie alors un pointeurs qui pointe vers un nouveau segment. Le pointeur est une capacité. Il doit être impossible de forger une capacité, en-dehors d'une demande d'allocation mémoire effectuée par l'OS. Typiquement, la forge d'une capacité se fait avec des instructions du processeur, que seul l'OS peut éxecuter (pensez à une instruction qui n'est accessible qu'en espace noyau). La seconde protection est que les capacités ne peuvent pas être modifiées sans raison valable, que ce soit pour l'indice de segment ou les droits d'accès. L'indice de segment ne peut pas être modifié, quelqu'en soit la raison. Pour les droits d'accès, la situation est plus compliquée. Il est possible de modifier ses droits d'accès, mais sous conditions. Réduire les droits d'accès d'une capacité est possible, que ce soit en espace noyau ou utilisateur, pas l'OS ou un programme utilisateur, avec une instruction dédiée. Mais augmenter les droits d'accès, seul l'OS peut le faire avec une instruction précise, souvent exécutable seulement en espace noyau. Les capacités peuvent être copiées, et même transférées d'un processus à un autre. Les capacités peuvent être détruites, ce qui permet de libérer la mémoire utilisée par un segment. La copie d'une capacité est contrôlée par l'OS et ne peut se faire que sous conditions. La destruction d'une capacité est par contre possible par tous les processus. La destruction ne signifie pas que le segment est effacé, il est possible que d'autres processus utilisent encore des copies de la capacité, et donc le segment associé. On verra quand la mémoire est libérée plus bas. Protéger les capacités demande plusieurs conditions. Premièrement, le processeur doit faire la distinction entre une capacité et une donnée. Deuxièmement, les capacités ne peuvent être modifiées que par des instructions spécifiques, dont l'exécution est protégée, réservée au noyau. En clair, il doit y avoir une séparation matérielle des capacités, qui sont placées dans des registres séparés. Pour cela, deux solutions sont possibles : soit les capacités remplacent les adresses et sont dispersées en mémoire, soit elles sont regroupées dans un segment protégé. ====La liste des capacités==== Avec la première solution, on regroupe les capacités dans un segment protégé. Chaque programme a accès à un certain nombre de segments et à autant de capacités. Les capacités d'un programme sont souvent regroupées dans une '''liste de capacités''', appelée la '''''C-list'''''. Elle est généralement placée en mémoire RAM. Elle est ce qu'il reste de la table des segments du processus, sauf que cette table ne contient pas les adresses du segment, qui sont dans la table globale. Tout se passe comme si la table des segments de chaque processus est donc scindée en deux : la table globale partagée entre tous les processus contient les informations sur les limites des segments, la ''C-list'' mémorise les droits d'accès et les sélecteurs pour identifier chaque segment. C'est un niveau d'indirection supplémentaire par rapport à la segmentation usuelle. [[File:Architectures à capacité.png|centre|vignette|upright=2|Architectures à capacité]] La liste de capacité est lisible par le programme, qui peut copier librement les capacités dans les registres. Par contre, la liste des capacités est protégée en écriture. Pour le programme, il est impossible de modifier les capacités dedans, impossible d'en rajouter, d'en forger, d'en retirer. De même, il ne peut pas accéder aux segments des autres programmes : il n'a pas les capacités pour adresser ces segments. Pour protéger la ''C-list'' en écriture, la solution la plus utilisée consiste à placer la ''C-list'' dans un segment dédié. Le processeur gère donc plusieurs types de segments : les segments de capacité pour les ''C-list'', les autres types segments pour le reste. Un défaut de cette approche est que les adresses/capacités sont séparées des données. Or, les programmeurs mixent souvent adresses et données, notamment quand ils doivent manipuler des structures de données comme des listes chainées, des arbres, des graphes, etc. L'usage d'une ''C-list'' permet de se passer de la séparation entre espace noyau et utilisateur ! Les segments de capacité sont eux-mêmes adressés par leur propre capacité, avec une capacité par segment de capacité. Le programme a accès à la liste de capacité, comme l'OS, mais leurs droits d'accès ne sont pas les mêmes. Le programme a une capacité vers la ''C-list'' qui n'autorise pas l'écriture, l'OS a une autre capacité qui accepte l'écriture. Les programmes ne pourront pas forger les capacités permettant de modifier les segments de capacité. Une méthode alternative est de ne permettre l'accès aux segments de capacité qu'en espace noyau, mais elle est redondante avec la méthode précédente et moins puissante. ====Les capacités dispersées, les architectures taguées==== Une solution alternative laisse les capacités dispersées en mémoire. Les capacités remplacent les adresses/pointeurs, et elles se trouvent aux mêmes endroits : sur la pile, dans le tas. Comme c'est le cas dans les programmes modernes, chaque allocation mémoire renvoie une capacité, que le programme gére comme il veut. Il peut les mettre dans des structures de données, les placer sur la pile, dans des variables en mémoire, etc. Mais il faut alors distinguer si un mot mémoire contient une capacité ou une autre donnée, les deux ne devant pas être mixés. Pour cela, chaque mot mémoire se voit attribuer un certain bit qui indique s'il s'agit d'un pointeur/capacité ou d'autre chose. Mais cela demande un support matériel, ce qui fait que le processeur devient ce qu'on appelle une ''architecture à tags'', ou ''tagged architectures''. Ici, elles indiquent si le mot mémoire contient une adresse:capacité ou une donnée. [[File:Architectures à capacité sans liste de capacité.png|centre|vignette|upright=2|Architectures à capacité sans liste de capacité]] L'inconvénient est le cout en matériel de cette solution. Il faut ajouter un bit à chaque case mémoire, le processeur doit vérifier les tags avant chaque opération d'accès mémoire, etc. De plus, tous les mots mémoire ont la même taille, ce qui force les capacités à avoir la même taille qu'un entier. Ce qui est compliqué. ===Les registres de capacité=== Les architectures à capacité disposent de registres spécialisés pour les capacités, séparés pour les entiers. La raison principale est une question de sécurité, mais aussi une solution pragmatique au fait que capacités et entiers n'ont pas la même taille. Les registres dédiés aux capacités ne mémorisent pas toujours des capacités proprement dites. A la place, ils mémorisent des descripteurs de segment, qui contiennent l'adresse de base, limite et les droits d'accès. Ils sont utilisés pour la relocation des accès mémoire ultérieurs. Ils sont en réalité identiques aux registres de relocation, voire aux registres de segments. Leur utilité est d'accélérer la relocation, entre autres. Les processeurs à capacité ne gèrent pas d'adresses proprement dit, comme pour la segmentation avec plusieurs registres de relocation. Les accès mémoire doivent préciser deux choses : à quel segment on veut accéder, à quelle position dans le segment se trouve la donnée accédée. La première information se trouve dans le mal nommé "registre de capacité", la seconde information est fournie par l'instruction d'accès mémoire soit dans un registre (Base+Index), soit en adressage base+''offset''. Les registres de capacités sont accessibles à travers des instructions spécialisées. Le processeur ajoute des instructions LOAD/STORE pour les échanges entre table des segments et registres de capacité. Ces instructions sont disponibles en espace utilisateur, pas seulement en espace noyau. Lors du chargement d'une capacité dans ces registres, le processeur vérifie que la capacité chargée est valide, et que les droits d'accès sont corrects. Puis, il accède à la table des segments, récupère les adresses de base et limite, et les mémorise dans le registre de capacité. Les droits d'accès et d'autres méta-données sont aussi mémorisées dans le registre de capacité. En somme, l'instruction de chargement prend une capacité et charge un descripteur de segment dans le registre. Avec ce genre de mécanismes, il devient difficile d’exécuter certains types d'attaques, ce qui est un gage de sureté de fonctionnement indéniable. Du moins, c'est la théorie, car tout repose sur l'intégrité des listes de capacité. Si on peut modifier celles-ci, alors il devient facile de pouvoir accéder à des objets auxquels on n’aurait pas eu droit. ===Le recyclage de mémoire matériel=== Les architectures à capacité séparent les adresses/capacités des nombres entiers. Et cela facilite grandement l'implémentation de la ''garbage collection'', ou '''recyclage de la mémoire''', à savoir un ensemble de techniques logicielles qui visent à libérer la mémoire inutilisée. Rappelons que les programmes peuvent demander à l'OS un rab de mémoire pour y placer quelque chose, généralement une structure de donnée ou un objet. Mais il arrive un moment où cet objet n'est plus utilisé par le programme. Il peut alors demander à l'OS de libérer la portion de mémoire réservée. Sur les architectures à capacité, cela revient à libérer un segment, devenu inutile. La mémoire utilisée par ce segment est alors considérée comme libre, et peut être utilisée pour autre chose. Mais il arrive que les programmes ne libèrent pas le segment en question. Soit parce que le programmeur a mal codé son programme, soit parce que le compilateur n'a pas fait du bon travail ou pour d'autres raisons. Pour éviter cela, les langages de programmation actuels incorporent des '''''garbage collectors''''', des morceaux de code qui scannent la mémoire et détectent les segments inutiles. Pour cela, ils doivent identifier les adresses manipulées par le programme. Si une adresse pointe vers un objet, alors celui-ci est accessible, il sera potentiellement utilisé dans le futur. Mais si aucune adresse ne pointe vers l'objet, alors il est inaccessible et ne sera plus jamais utilisé dans le futur. On peut libérer les objets inaccessibles. Identifier les adresses est cependant très compliqué sur les architectures normales. Sur les processeurs modernes, les ''garbage collectors'' scannent la pile à la recherche des adresses, et considèrent tout mot mémoire comme une adresse potentielle. Mais les architectures à capacité rendent le recyclage de la mémoire très facile. Un segment est accessible si le programme dispose d'une capacité qui pointe vers ce segment, rien de plus. Et les capacités sont facilement identifiables : soit elles sont dans la liste des capacités, soit on peut les identifier à partir de leur ''tag''. Le recyclage de mémoire était parfois implémenté directement en matériel. En soi, son implémentation est assez simple, et peu être réalisé dans le microcode d'un processeur. Une autre solution consiste à utiliser un second processeur, spécialement dédié au recyclage de mémoire, qui exécute un programme spécialement codé pour. Le programme en question est placé dans une mémoire ROM, reliée directement à ce second processeur. ===L'intel iAPX 432=== Voyons maintenat une architecture à capacité assez connue : l'Intel iAPX 432. Oui, vous avez bien lu : Intel a bel et bien réalisé un processeur orienté objet dans sa jeunesse. La conception du processeur Intel iAPX 432 commença en 1975, afin de créer un successeur digne de ce nom aux processeurs 8008 et 8080. La conception du processeur Intel iAPX 432 commença en 1975, afin de créer un successeur digne de ce nom aux processeurs 8008 et 8080. Ce processeur s'est très faiblement vendu en raison de ses performances assez désastreuses et de défauts techniques certains. Par exemple, ce processeur était une machine à pile à une époque où celles-ci étaient tombées en désuétude, il ne pouvait pas effectuer directement de calculs avec des constantes entières autres que 0 et 1, ses instructions avaient un alignement bizarre (elles étaient bit-alignées). Il avait été conçu pour maximiser la compatibilité avec le langage ADA, un langage assez peu utilisé, sans compter que le compilateur pour ce processeur était mauvais. ====Les segments prédéfinis de l'Intel iAPX 432==== L'Intel iAPX432 gère plusieurs types de segments. Rien d'étonnant à cela, les Burrough géraient eux aussi plusieurs types de segments, à savoir des segments de programmes, des segments de données, et des segments d'I/O. C'est la même chose sur l'Intel iAPX 432, mais en bien pire ! Les segments de données sont des segments génériques, dans lequels on peut mettre ce qu'on veut, suivant les besoins du programmeur. Ils sont tous découpés en deux parties de tailles égales : une partie contenant les données de l'objet et une partie pour les capacités. Les capacités d'un segment pointent vers d'autres segments, ce qui permet de créer des structures de données assez complexes. La ligne de démarcation peut être placée n'importe où dans le segment, les deux portions ne sont pas de taille identique, elles ont des tailles qui varient de segment en segment. Il est même possible de réserver le segment entier à des données sans y mettre de capacités, ou inversement. Les capacités et données sont adressées à partir de la ligne de démarcation, qui sert d'adresse de base du segment. Suivant l'instruction utilisée, le processeur accède à la bonne portion du segment. Le processeur supporte aussi d'autres segments pré-définis, qui sont surtout utilisés par le système d'exploitation : * Des segments d'instructions, qui contiennent du code exécutable, typiquement un programme ou des fonctions, parfois des ''threads''. * Des segments de processus, qui mémorisent des processus entiers. Ces segments contiennent des capacités qui pointent vers d'autres segments, notamment un ou plusieurs segments de code, et des segments de données. * Des segments de domaine, pour les modules ou bibliothèques dynamiques. * Des segments de contexte, utilisés pour mémoriser l'état d'un processus, utilisés par l'OS pour faire de la commutation de contexte. * Des segments de message, utilisés pour la communication entre processus par l'intermédiaire de messages. * Et bien d'autres encores. Sur l'Intel iAPX 432, chaque processus est considéré comme un objet à part entière, qui a son propre segment de processus. De même, l'état du processeur (le programme qu'il est en train d’exécuter, son état, etc.) est stocké en mémoire dans un segment de contexte. Il en est de même pour chaque fonction présente en mémoire : elle était encapsulée dans un segment, sur lequel seules quelques manipulations étaient possibles (l’exécuter, notamment). Et ne parlons pas des appels de fonctions qui stockaient l'état de l'appelé directement dans un objet spécial. Bref, de nombreux objets système sont prédéfinis par le processeur : les objets stockant des fonctions, les objets stockant des processus, etc. L'Intel 432 possédait dans ses circuits un ''garbage collector'' matériel. Pour faciliter son fonctionnement, certains bits de l'objet permettaient de savoir si l'objet en question pouvait être supprimé ou non. ====Le support de la segmentation sur l'Intel iAPX 432==== La table des segments est une table hiérarchique, à deux niveaux. Le premier niveau est une ''Object Table Directory'', qui réside toujours en mémoire RAM. Elle contient des descripteurs qui pointent vers des tables secondaires, appelées des ''Object Table''. Il y a plusieurs ''Object Table'', typiquement une par processus. Plusieurs processus peuvent partager la même ''Object Table''. Les ''Object Table'' peuvent être swappées, mais pas l{{'}}''Object Table Directory''. Une capacité tient compte de l'organisation hiérarchique de la table des segments. Elle contient un indice qui précise quelle ''Object Table'' utiliser, et l'indice du segment dans cette ''Object Table''. Le premier indice adresse l{{'}}''Object Table Directory'' et récupère un descripteur de segment qui pointe sur la bonne ''Object Table''. Le second indice est alors utilisé pour lire l'adresse de base adéquate dans cette ''Object Table''. La capacité contient aussi des droits d'accès en lecture, écriture, suppression et copie. Il y a aussi un champ pour le type, qu'on verra plus bas. Au fait : les capacités étaient appelées des ''Access Descriptors'' dans la documentation officielle. Une capacité fait 32 bits, avec un octet utilisé pour les droits d'accès, laissant 24 bits pour adresser les segments. Le processeur gérait jusqu'à 2^24 segments/objets différents, pouvant mesurer jusqu'à 64 kibioctets chacun, ce qui fait 2^40 adresses différentes, soit 1024 gibioctets. Les 24 bits pour adresser les segments sont partagés moitié-moitié pour l'adressage des tables, ce qui fait 4096 ''Object Table'' différentes dans l{{'}}''Object Table Directory'', et chaque ''Object Table'' contient 4096 segments. ====Le jeu d'instruction de l'Intel iAPX 432==== L'Intel iAPX 432 est une machine à pile. Le jeu d'instruction de l'Intel iAPX 432 gère pas moins de 230 instructions différentes. Il gére deux types d'instructions : les instructions normales, et celles qui manipulent des segments/objets. Les premières permettent de manipuler des nombres entiers, des caractères, des chaînes de caractères, des tableaux, etc. Les secondes sont spécialement dédiées à la manipulation des capacités. Il y a une instruction pour copier une capacité, une autre pour invalider une capacité, une autre pour augmenter ses droits d'accès (instruction sécurisée, exécutable seulement sous certaines conditions), une autre pour restreindre ses droits d'accès. deux autres instructions créent un segment et renvoient la capacité associée, la première créant un segment typé, l'autre non. le processeur gérait aussi des instructions spécialement dédiées à la programmation système et idéales pour programmer des systèmes d'exploitation. De nombreuses instructions permettaient ainsi de commuter des processus, faire des transferts de messages entre processus, etc. Environ 40 % du micro-code était ainsi spécialement dédié à ces instructions spéciales. Les instructions sont de longueur variable et peuvent prendre n'importe quelle taille comprise entre 10 et 300 bits, sans vraiment de restriction de taille. Les bits d'une instruction sont regroupés en 4 grands blocs, 4 champs, qui ont chacun une signification particulière. * Le premier est l'opcode de l'instruction. * Le champ référence, doit être interprété différemment suivant la donnée à manipuler. Si cette donnée est un entier, un caractère ou un flottant, ce champ indique l'emplacement de la donnée en mémoire. Alors que si l'instruction manipule un objet, ce champ spécifie la capacité de l'objet en question. Ce champ est assez complexe et il est sacrément bien organisé. * Le champ format, n'utilise que 4 bits et a pour but de préciser si les données à manipuler sont en mémoire ou sur la pile. * Le champ classe permet de dire combien de données différentes l'instruction va devoir manipuler, et quelles seront leurs tailles. [[File:Encodage des instructions de l'Intel iAPX-432.png|centre|vignette|upright=2|Encodage des instructions de l'Intel iAPX-432.]] ====Le support de l'orienté objet sur l'Intel iAPX 432==== L'Intel 432 permet de définir des objets, qui correspondent aux classes des langages orientés objets. L'Intel 432 permet, à partir de fonctions définies par le programmeur, de créer des '''''domain objects''''', qui correspondent à une classe. Un ''domain object'' est un segment de capacité, dont les capacités pointent vers des fonctions ou un/plusieurs objets. Les fonctions et les objets sont chacun placés dans un segment. Une partie des fonctions/objets sont publics, ce qui signifie qu'ils sont accessibles en lecture par l'extérieur. Les autres sont privées, inaccessibles aussi bien en lecture qu'en écriture. L'exécution d'une fonction demande que le branchement fournisse deux choses : une capacité vers le ''domain object'', et la position de la fonction à exécuter dans le segment. La position permet de localiser la capacité de la fonction à exécuter. En clair, on accède au ''domain object'' d'abord, pour récupérer la capacité qui pointe vers la fonction à exécuter. Il est aussi possible pour le programmeur de définir de nouveaux types non supportés par le processeur, en faisant appel au système d'exploitation de l'ordinateur. Au niveau du processeur, chaque objet est typé au niveau de son object descriptor : celui-ci contient des informations qui permettent de déterminer le type de l'objet. Chaque type se voit attribuer un domain object qui contient toutes les fonctions capables de manipuler les objets de ce type et que l'on appelle le type manager. Lorsque l'on veut manipuler un objet d'un certain type, il suffit d'accéder à une capacité spéciale (le TCO) qui pointera dans ce type manager et qui précisera quel est l'objet à manipuler (en sélectionnant la bonne entrée dans la liste de capacité). Le type d'un objet prédéfini par le processeur est ainsi spécifié par une suite de 8 bits, tandis que le type d'un objet défini par le programmeur est défini par la capacité spéciale pointant vers son type manager. ===Conclusion=== Pour ceux qui veulent en savoir plus, je conseille la lecture de ce livre, disponible gratuitement sur internet (merci à l'auteur pour cette mise à disposition) : * [https://homes.cs.washington.edu/~levy/capabook/ Capability-Based Computer Systems]. Voici un document qui décrit le fonctionnement de l'Intel iAPX432 : * [https://homes.cs.washington.edu/~levy/capabook/Chapter9.pdf The Intel iAPX 432 ] ==La pagination== Avec la pagination, la mémoire est découpée en blocs de taille fixe, appelés des '''pages mémoires'''. La taille des pages varie suivant le processeur et le système d'exploitation et tourne souvent autour de 4 kibioctets. Mais elles sont de taille fixe : on ne peut pas en changer la taille. C'est la différence avec les segments, qui sont de taille variable. Le contenu d'une page en mémoire fictive est rigoureusement le même que le contenu de la page correspondante en mémoire physique. L'espace d'adressage est découpé en '''pages logiques''', alors que la mémoire physique est découpée en '''pages physique''' de même taille. Les pages logiques correspondent soit à une page physique, soit à une page swappée sur le disque dur. Quand une page logique est associée à une page physique, les deux ont le même contenu, mais pas les mêmes adresses. Les pages logiques sont numérotées, en partant de 0, afin de pouvoir les identifier/sélectionner. Même chose pour les pages physiques, qui sont elles aussi numérotées en partant de 0. [[File:Principe de la pagination.png|centre|vignette|upright=2|Principe de la pagination.]] Pour information, le tout premier processeur avec un système de mémoire virtuelle était le super-ordinateur Atlas. Il utilisait la pagination, et non la segmentation. Mais il fallu du temps avant que la méthode de la pagination prenne son essor dans les processeurs commerciaux x86. Un point important est que la pagination implique une coopération entre OS et hardware, les deux étant fortement mélés. Une partie des informations de cette section auraient tout autant leur place dans le wikilivre sur les systèmes d'exploitation, mais il est plus simple d'en parler ici. ===La mémoire virtuelle : le ''swapping'' et le remplacement des pages mémoires=== Le système d'exploitation mémorise des informations sur toutes les pages existantes dans une '''table des pages'''. C'est un tableau où chaque ligne est associée à une page logique. Une ligne contient un bit ''Valid'' qui indique si la page logique associée est swappée sur le disque dur ou non, et la position de la page physique correspondante en mémoire RAM. Elle peut aussi contenir des bits pour la protection mémoire, et bien d'autres. Les lignes sont aussi appelées des ''entrées de la table des pages'' [[File:Gestionnaire de mémoire virtuelle - Pagination et swapping.png|centre|vignette|upright=2|Table des pages.]] De plus, le système d'exploitation conserve une '''liste des pages vides'''. Le nom est assez clair : c'est une liste de toutes les pages de la mémoire physique qui sont inutilisées, qui ne sont allouées à aucun processus. Ces pages sont de la mémoire libre, utilisable à volonté. La liste des pages vides est mise à jour à chaque fois qu'un programme réserve de la mémoire, des pages sont alors prises dans cette liste et sont allouées au programme demandeur. ====Les défauts de page==== Lorsque l'on veut traduire l'adresse logique d'une page mémoire, le processeur vérifie le bit ''Valid'' et l'adresse physique. Si le bit ''Valid'' est à 1 et que l'adresse physique est présente, la traduction d'adresse s'effectue normalement. Mais si ce n'est pas le cas, l'entrée de la table des pages ne contient pas de quoi faire la traduction d'adresse. Soit parce que la page est swappée sur le disque dur et qu'il faut la copier en RAM, soit parce que les droits d'accès ne le permettent pas, soit parce que la page n'a pas encore été allouée, etc. On fait alors face à un '''défaut de page'''. Un défaut de page a lieu quand la MMU ne peut pas associer l'adresse logique à une adresse physique, quelque qu'en soit la raison. Il existe deux types de défauts de page : mineurs et majeurs. Un '''défaut de page majeur''' a lieu quand on veut accéder à une page déplacée sur le disque dur. Un défaut de page majeur lève une exception matérielle dont la routine rapatriera la page en mémoire RAM. S'il y a de la place en mémoire RAM, il suffit d'allouer une page vide et d'y copier la page chargée depuis le disque dur. Mais si ce n'est par le cas, on va devoir faire de la place en RAM en déplaçant une page mémoire de la RAM vers le disque dur. Dans tous les cas, c'est le système d'exploitation qui s'occupe du chargement de la page, le processeur n'est pas impliqué. Une fois la page chargée, la table des pages est mise à jour et la traduction d'adresse peut recommencer. Si je dis recommencer, c'est car l'accès mémoire initial est rejoué à l'identique, sauf que la traduction d'adresse réussit cette fois-ci. Un '''défaut de page mineur''' a lieu dans des circonstances pas très intuitives : la page est en mémoire physique, mais l'adresse physique de la page n'est pas accessible. Par exemple, il est possible que des sécurités empêchent de faire la traduction d'adresse, pour des raisons de protection mémoire. Une autre raison est la gestion des adresses synonymes, qui surviennent quand on utilise des libraires partagées entre programmes, de la communication inter-processus, des optimisations de type ''copy-on-write'', etc. Enfin, une dernière raison est que la page a été allouée à un programme par le système d'exploitation, mais qu'il n'a pas encore attribué sa position en mémoire. Pour comprendre comment c'est possible, parlons rapidement de l'allocation paresseuse. Imaginons qu'un programme fasse une demande d'allocation mémoire et se voit donc attribuer une ou plusieurs pages logiques. L'OS peut alors réagir de deux manières différentes. La première est d'attribuer une page physique immédiatement, en même temps que la page logique. En faisant ainsi, on ne peut pas avoir de défaut mineur, sauf en cas de problème de protection mémoire. Cette solution est simple, on l'appelle l{{'}}'''allocation immédiate'''. Une autre solution consiste à attribuer une page logique, mais l'allocation de la page physique se fait plus tard. Elle a lieu la première fois que le programme tente d'écrire/lire dans la page physique. Un défaut mineur a lieu, et c'est lui qui force l'OS à attribuer une page physique pour la page logique demandée. On parle alors d{{'}}'''allocation paresseuse'''. L'avantage est que l'on gagne en performance si des pages logiques sont allouées mais utilisées, ce qui peut arriver. Une optimisation permise par l'existence des défauts mineurs est le '''''copy-on-write'''''. Le but est d'optimiser la copie d'une page logique dans une autre. L'idée est que la copie est retardée quand elle est vraiment nécessaire, à savoir quand on écrit dans la copie. Tant que l'on ne modifie pas la copie, les deux pages logiques, originelle et copiée, pointent vers la même page physique. A quoi bon avoir deux copies avec le même contenu ? Par contre, la page physique est marquée en lecture seule. La moindre écriture déclenche une erreur de protection mémoire, et un défaut mineur. Celui-ci est géré par l'OS, qui effectue alors la copie dans une nouvelle page physique. Je viens de dire que le système d'exploitation gère les défauts de page majeurs/mineurs. Un défaut de page déclenche une exception matérielle, qui passe la main au système d'exploitation. Le système d'exploitation doit alors déterminer ce qui a levé l'exception, notamment identifier si c'est un défaut de page mineur ou majeur. Pour cela, le processeur a un ou plusieurs '''registres de statut''' qui indique l'état du processeur, qui sont utiles pour gérer les défauts de page. Ils indiquent quelle est l'adresse fautive, si l'accès était une lecture ou écriture, si l'accès a eu lieu en espace noyau ou utilisateur (les espaces mémoire ne sont pas les mêmes), etc. Les registres en question varient grandement d'une architecture de processeur à l'autre, aussi on ne peut pas dire grand chose de plus sur le sujet. Le reste est de toute façon à voir dans un cours sur les systèmes d'exploitation. ====Le remplacement des pages==== Les pages virtuelles font référence soit à une page en mémoire physique, soit à une page sur le disque dur. Mais l'on ne peut pas lire une page directement depuis le disque dur. Les pages sur le disque dur doivent être chargées en RAM, avant d'être utilisables. Ce n'est possible que si on a une page mémoire vide, libre. Si ce n'est pas le cas, on doit faire de la place en swappant une page sur le disque dur. Les pages font ainsi une sorte de va et vient entre le fichier d'échange et la RAM, suivant les besoins. Tout cela est effectué par une routine d'interruption du système d'exploitation, le processeur n'ayant pas vraiment de rôle là-dedans. Supposons que l'on veuille faire de la place en RAM pour une nouvelle page. Dans une implémentation naïve, on trouve une page à évincer de la mémoire, qui est copiée dans le ''swapfile''. Toutes les pages évincées sont alors copiées sur le disque dur, à chaque remplacement. Néanmoins, cette implémentation naïve peut cependant être améliorée si on tient compte d'un point important : si la page a été modifiée depuis le dernier accès. Si le programme/processeur a écrit dans la page, alors celle-ci a été modifiée et doit être sauvegardée sur le ''swapfile'' si elle est évincée. Par contre, si ce n'est pas le cas, la page est soit initialisée, soit déjà présente à l'identique dans le ''swapfile''. Mais cette optimisation demande de savoir si une écriture a eu lieu dans la page. Pour cela, on ajoute un '''''dirty bit''''' à chaque entrée de la table des pages, juste à côté du bit ''Valid''. Il indique si une écriture a eu lieu dans la page depuis qu'elle a été chargée en RAM. Ce bit est mis à jour par le processeur, automatiquement, lors d'une écriture. Par contre, il est remis à zéro par le système d'exploitation, quand la page est chargée en RAM. Si le programme se voit allouer de la mémoire, il reçoit une page vide, et ce bit est initialisé à 0. Il est mis à 1 si la mémoire est utilisée. Quand la page est ensuite swappée sur le disque dur, ce bit est remis à 0 après la sauvegarde. Sur la majorité des systèmes d'exploitation, il est possible d'interdire le déplacement de certaines pages sur le disque dur. Ces pages restent alors en mémoire RAM durant un temps plus ou moins long, parfois en permanence. Cette possibilité simplifie la vie des programmeurs qui conçoivent des systèmes d'exploitation : essayez d'exécuter l'interruption pour les défauts de page alors que la page contenant le code de l'interruption est placée sur le disque dur ! Là encore, cela demande d'ajouter un bit dans chaque entrée de la table des pages, qui indique si la page est swappable ou non. Le bit en question s'appelle souvent le '''bit ''swappable'''''. ====Les algorithmes de remplacement des pages pris en charge par l'OS==== Le choix de la page doit être fait avec le plus grand soin et il existe différents algorithmes qui permettent de décider quelle page supprimer de la RAM. Leur but est de swapper des pages qui ne seront pas accédées dans le futur, pour éviter d'avoir à faire triop de va-et-vient entre RAM et ''swapfile''. Les données qui sont censées être accédées dans le futur doivent rester en RAM et ne pas être swappées, autant que possible. Les algorithmes les plus simples pour le choix de page à évincer sont les suivants. Le plus simple est un algorithme aléatoire : on choisit la page au hasard. Mine de rien, cet algorithme est très simple à implémenter et très rapide à exécuter. Il ne demande pas de modifier la table des pages, ni même d'accéder à celle-ci pour faire son choix. Ses performances sont surprenamment correctes, bien que largement en-dessous de tous les autres algorithmes. L'algorithme FIFO supprime la donnée qui a été chargée dans la mémoire avant toutes les autres. Cet algorithme fonctionne bien quand un programme manipule des tableaux de grande taille, mais fonctionne assez mal dans le cas général. L'algorithme LRU supprime la donnée qui été lue ou écrite pour la dernière fois avant toutes les autres. C'est théoriquement le plus efficace dans la majorité des situations. Malheureusement, son implémentation est assez complexe et les OS doivent modifier la table des pages pour l'implémenter. L'algorithme le plus utilisé de nos jours est l{{'}}'''algorithme NRU''' (''Not Recently Used''), une simplification drastique du LRU. Il fait la différence entre les pages accédées il y a longtemps et celles accédées récemment, d'une manière très binaire. Les deux types de page sont appelés respectivement les '''pages froides''' et les '''pages chaudes'''. L'OS swappe en priorité les pages froides et ne swappe de page chaude que si aucune page froide n'est présente. L'algorithme est simple : il choisit la page à évincer au hasard parmi une page froide. Si aucune page froide n'est présente, alors il swappe au hasard une page chaude. Pour implémenter l'algorithme NRU, l'OS mémorise, dans chaque entrée de la table des pages, si la page associée est froide ou chaude. Pour cela, il met à 0 ou 1 un bit dédié : le '''bit ''Accessed'''''. La différence avec le bit ''dirty'' est que le bit ''dirty'' est mis à jour uniquement lors des écritures, alors que le bit ''Accessed'' l'est aussi lors d'une lecture. Uen lecture met à 1 le bit ''Accessed'', mais ne touche pas au bit ''dirty''. Les écritures mettent les deux bits à 1. Implémenter l'algorithme NRU demande juste de mettre à jour le bit ''Accessed'' de chaque entrée de la table des pages. Et sur les architectures modernes, le processeur s'en charge automatiquement. A chaque accès mémoire, que ce soit en lecture ou en écriture, le processeur met à 1 ce bit. Par contre, le système d'exploitation le met à 0 à intervalles réguliers. En conséquence, quand un remplacement de page doit avoir lieu, les pages chaudes ont de bonnes chances d'avoir le bit ''Accessed'' à 1, alors que les pages froides l'ont à 0. Ce n'est pas certain, et on peut se trouver dans des cas où ce n'est pas le cas. Par exemple, si un remplacement a lieu juste après la remise à zéro des bits ''Accessed''. Le choix de la page à remplacer est donc imparfait, mais fonctionne bien en pratique. Tous les algorithmes précédents ont chacun deux variantes : une locale, et une globale. Avec la version locale, la page qui va être rapatriée sur le disque dur est une page réservée au programme qui est la cause du page miss. Avec la version globale, le système d'exploitation va choisir la page à virer parmi toutes les pages présentes en mémoire vive. ===La protection mémoire avec la pagination=== Avec la pagination, chaque page a des '''droits d'accès''' précis, qui permettent d'autoriser ou interdire les accès en lecture, écriture, exécution, etc. La table des pages mémorise les autorisations pour chaque page, sous la forme d'une suite de bits où chaque bit autorise/interdit une opération bien précise. En pratique, les tables de pages modernes disposent de trois bits : un qui autorise/interdit les accès en lecture, un qui autorise/interdit les accès en écriture, un qui autorise/interdit l'éxecution du contenu de la page. Le format exact de la suite de bits a cependant changé dans le temps sur les processeurs x86 modernes. Par exemple, avant le passage au 64 bits, les CPU et OS ne pouvaient pas marquer une page mémoire comme non-exécutable. C'est seulement avec le passage au 64 bits qu'a été ajouté un bit pour interdire l'exécution de code depuis une page. Ce bit, nommé '''bit NX''', est à 0 si la page n'est pas exécutable et à 1 sinon. Le processeur vérifie à chaque chargement d'instruction si le bit NX de page lue est à 1. Sinon, il lève une exception matérielle et laisse la main à l'OS. Une amélioration de cette protection est la technique dite du '''''Write XOR Execute''''', abréviée WxX. Elle consiste à interdire les pages d'être à la fois accessibles en écriture et exécutables. Il est possible de changer les autorisations en cours de route, ceci dit. Les premiers IBM 360 disposaient d'un mécanisme de protection mémoire totalement différent, sans registres limite/base. Ce mécanisme de protection attribue à chaque programme une '''clé de protection''', qui consiste en un nombre unique de 4 bits (chaque programme a donc une clé différente de ses collègues). La mémoire est fragmentée en blocs de même taille, de 2 kibioctets. Le processeur mémorise, pour chacun de ses blocs, la clé de protection du programme qui a réservé ce bloc. À chaque accès mémoire, le processeur compare la clé de protection du programme en cours d’exécution et celle du bloc de mémoire de destination. Si les deux clés sont différentes, alors un programme a effectué un accès hors des clous et il se fait sauvagement arrêter. ===La traduction d'adresse avec la pagination=== Comme dit plus haut, les pages sont numérotées, de 0 à une valeur maximale, afin de les identifier. Le numéro en question est appelé le '''numéro de page'''. Il est utilisé pour dire au processeur : je veux lire une donnée dans la page numéro 20, la page numéro 90, etc. Une fois qu'on a le numéro de page, on doit alors préciser la position de la donnée dans la page, appelé le '''décalage''', ou encore l{{'}}''offset''. Le numéro de page et le décalage se déduisent à partir de l'adresse, en divisant l'adresse par la taille de la page. Le quotient obtenu donne le numéro de la page, alors que le reste est le décalage. Les processeurs actuels utilisent tous des pages dont la taille est une puissance de deux, ce qui fait que ce calcul est fortement simplifié. Sous cette condition, le numéro de page correspond aux bits de poids fort de l'adresse, alors que le décalage est dans les bits de poids faible. Le numéro de page existe en deux versions : un numéro de page physique qui identifie une page en mémoire physique, et un numéro de page logique qui identifie une page dans la mémoire virtuelle. Traduire l'adresse logique en adresse physique demande de remplacer le numéro de la page logique en un numéro de page physique. [[File:Phycical address.JPG|centre|vignette|upright=2|Traduction d'adresse avec la pagination.]] ====Les tables des pages simples==== Dans le cas le plus simple, il n'y a qu'une seule table des pages, qui est adressée par les numéros de page logique. La table des pages est un vulgaire tableau d'adresses physiques, placées les unes à la suite des autres. Avec cette méthode, la table des pages a autant d'entrée qu'il y a de pages logiques en mémoire virtuelle. Accéder à la mémoire nécessite donc d’accéder d'abord à la table des pages en mémoire, de calculer l'adresse de l'entrée voulue, et d’y accéder. [[File:Table des pages.png|centre|vignette|upright=2|Table des pages.]] La table des pages est souvent stockée dans la mémoire RAM, son adresse est connue du processeur, mémorisée dans un registre spécialisé du processeur. Le processeur effectue automatiquement le calcul d'adresse à partir de l'adresse de base et du numéro de page logique. [[File:Address translation (32-bit).png|centre|vignette|upright=2|Address translation (32-bit)]] ====Les tables des pages inversées==== Sur certains systèmes, notamment sur les architectures 64 bits ou plus, le nombre de pages est très important. Sur les ordinateurs x86 récents, les adresses sont en pratique de 48 bits, les bits de poids fort étant ignorés en pratique, ce qui fait en tout 68 719 476 736 pages. Chaque entrée de la table des pages fait au minimum 48 bits, mais fait plus en pratique : partons sur 64 bits par entrée, soit 8 octets. Cela fait 549 755 813 888 octets pour la table des pages, soit plusieurs centaines de gibioctets ! Une table des pages normale serait tout simplement impraticable. Pour résoudre ce problème, on a inventé les '''tables des pages inversées'''. L'idée derrière celles-ci est l'inverse de la méthode précédente. La méthode précédente stocke, pour chaque page logique, son numéro de page physique. Les tables des pages inversées font l'inverse : elles stockent, pour chaque numéro de page physique, la page logique qui correspond. Avec cette méthode table des pages contient ainsi autant d'entrées qu'il y a de pages physiques. Elle est donc plus petite qu'avant, vu que la mémoire physique est plus petite que la mémoire virtuelle. Quand le processeur veut convertir une adresse virtuelle en adresse physique, la MMU recherche le numéro de page de l'adresse virtuelle dans la table des pages. Le numéro de l'entrée à laquelle se trouve ce morceau d'adresse virtuelle est le morceau de l'adresse physique. Pour faciliter le processus de recherche dans la page, la table des pages inversée est ce que l'on appelle une table de hachage. C'est cette solution qui est utilisée sur les processeurs Power PC. [[File:Table des pages inversée.jpg|centre|vignette|upright=2|Table des pages inversée.]] ====Les tables des pages multiples par espace d'adressage==== Dans les deux cas précédents, il y a une table des pages unique. Cependant, les concepteurs de processeurs et de systèmes d'exploitation ont remarqué que les adresses les plus hautes et/ou les plus basses sont les plus utilisées, alors que les adresses situées au milieu de l'espace d'adressage sont peu utilisées en raison du fonctionnement de la pile et du tas. Il y a donc une partie de la table des pages qui ne sert à rien et est utilisé pour des adresses inutilisées. C'est une source d'économie d'autant plus importante que les tables des pages sont de plus en plus grosses. Pour profiter de cette observation, les concepteurs d'OS ont décidé de découper l'espace d'adressage en plusieurs sous-espaces d'adressage de taille identique : certains localisés dans les adresses basses, d'autres au milieu, d'autres tout en haut, etc. Et vu que l'espace d'adressage est scindé en plusieurs parties, la table des pages l'est aussi, elle est découpée en plusieurs sous-tables. Si un sous-espace d'adressage n'est pas utilisé, il n'y a pas besoin d'utiliser de la mémoire pour stocker la table des pages associée. On ne stocke que les tables des pages pour les espaces d'adressage utilisés, ceux qui contiennent au moins une donnée. L'utilisation de plusieurs tables des pages ne fonctionne que si le système d'exploitation connaît l'adresse de chaque table des pages (celle de la première entrée). Pour cela, le système d'exploitation utilise une super-table des pages, qui stocke les adresses de début des sous-tables de chaque sous-espace. En clair, la table des pages est organisé en deux niveaux, la super-table étant le premier niveau et les sous-tables étant le second niveau. L'adresse est structurée de manière à tirer profit de cette organisation. Les bits de poids fort de l'adresse sélectionnent quelle table de second niveau utiliser, les bits du milieu de l'adresse sélectionne la page dans la table de second niveau et le reste est interprété comme un ''offset''. Un accès à la table des pages se fait comme suit. Les bits de poids fort de l'adresse sont envoyés à la table de premier niveau, et sont utilisés pour récupérer l'adresse de la table de second niveau adéquate. Les bits au milieu de l'adresse sont envoyés à la table de second niveau, pour récupérer le numéro de page physique. Le tout est combiné avec l{{'}}''offset'' pour obtenir l'adresse physique finale. [[File:Table des pages hiérarchique.png|centre|vignette|upright=2|Table des pages hiérarchique.]] On peut aussi aller plus loin et découper la table des pages de manière hiérarchique, chaque sous-espace d'adressage étant lui aussi découpé en sous-espaces d'adressages. On a alors une table de premier niveau, plusieurs tables de second niveau, encore plus de tables de troisième niveau, et ainsi de suite. Cela peut aller jusqu'à 5 niveaux sur les processeurs x86 64 bits modernes. On parle alors de '''tables des pages emboitées'''. Dans ce cours, la table des pages désigne l'ensemble des différents niveaux de cette organisation, toutes les tables inclus. Seules les tables du dernier niveau mémorisent des numéros de page physiques, les autres tables mémorisant des pointeurs, des adresses vers le début des tables de niveau inférieur. Un exemple sera donné plus bas, dans la section suivante. ====L'exemple des processeurs x86==== Pour rendre les explications précédentes plus concrètes, nous allons prendre l'exemple des processeur x86 anciens, de type 32 bits. Les processeurs de ce type utilisaient deux types de tables des pages : une table des page unique et une table des page hiérarchique. Les deux étaient utilisées dans cas séparés. La table des page unique était utilisée pour les pages larges et encore seulement en l'absence de la technologie ''physical adress extension'', dont on parlera plus bas. Les autres cas utilisaient une table des page hiérarchique, à deux niveaux, trois niveaux, voire plus. Une table des pages unique était utilisée pour les pages larges (de 2 mébioctets et plus). Pour les pages de 4 mébioctets, il y avait une unique table des pages, adressée par les 10 bits de poids fort de l'adresse, les bits restants servant comme ''offset''. La table des pages contenait 1024 entrées de 4 octets chacune, ce qui fait en tout 4 kibioctet pour la table des pages. La table des page était alignée en mémoire sur un bloc de 4 kibioctet (sa taille). [[File:X86 Paging 4M.svg|centre|vignette|upright=2|X86 Paging 4M]] Pour les pages de 4 kibioctets, les processeurs x86-32 bits utilisaient une table des page hiérarchique à deux niveaux. Les 10 bits de poids fort l'adresse adressaient la table des page maitre, appelée le directoire des pages (''page directory''), les 10 bits précédents servaient de numéro de page logique, et les 12 bits restants servaient à indiquer la position de l'octet dans la table des pages. Les entrées de chaque table des pages, mineure ou majeure, faisaient 32 bits, soit 4 octets. Vous remarquerez que la table des page majeure a la même taille que la table des page unique obtenue avec des pages larges (de 4 mébioctets). [[File:X86 Paging 4K.svg|centre|vignette|upright=2|X86 Paging 4K]] La technique du '''''physical adress extension''''' (PAE), utilisée depuis le Pentium Pro, permettait aux processeurs x86 32 bits d'adresser plus de 4 gibioctets de mémoire, en utilisant des adresses physiques de 64 bits. Les adresses virtuelles de 32 bits étaient traduites en adresses physiques de 64 bits grâce à une table des pages adaptée. Cette technologie permettait d'adresser plus de 4 gibioctets de mémoire au total, mais avec quelques limitations. Notamment, chaque programme ne pouvait utiliser que 4 gibioctets de mémoire RAM pour lui seul. Mais en lançant plusieurs programmes, on pouvait dépasser les 4 gibioctets au total. Pour cela, les entrées de la table des pages passaient à 64 bits au lieu de 32 auparavant. La table des pages gardait 2 niveaux pour les pages larges en PAE. [[File:X86 Paging PAE 2M.svg|centre|vignette|upright=2|X86 Paging PAE 2M]] Par contre, pour les pages de 4 kibioctets en PAE, elle était modifiée de manière à ajouter un niveau de hiérarchie, passant de deux niveaux à trois. [[File:X86 Paging PAE 4K.svg|centre|vignette|upright=2|X86 Paging PAE 4K]] En 64 bits, la table des pages est une table des page hiérarchique avec 5 niveaux. Seuls les 48 bits de poids faible des adresses sont utilisés, les 16 restants étant ignorés. [[File:X86 Paging 64bit.svg|centre|vignette|upright=2|X86 Paging 64bit]] ====Les circuits liés à la gestion de la table des pages==== En théorie, la table des pages est censée être accédée à chaque accès mémoire. Mais pour éviter d'avoir à lire la table des pages en mémoire RAM à chaque accès mémoire, les concepteurs de processeurs ont décidé d'implanter un cache dédié, le '''''translation lookaside buffer''''', ou TLB. Le TLB stocke au minimum de quoi faire la traduction entre adresse virtuelle et adresse physique, à savoir une correspondance entre numéro de page logique et numéro de page physique. Pour faire plus général, il stocke des entrées de la table des pages. [[File:MMU principle updated.png|centre|vignette|upright=2.0|MMU avec une TLB.]] Les accès à la table des pages sont gérés de deux façons : soit le processeur gère tout seul la situation, soit il délègue cette tâche au système d’exploitation. Sur les processeurs anciens, le système d'exploitation gère le parcours de la table des pages. Mais cette solution logicielle n'a pas de bonnes performances. D'autres processeurs gèrent eux-mêmes le défaut d'accès à la TLB et vont chercher d'eux-mêmes les informations nécessaires dans la table des pages. Ils disposent de circuits, les '''''page table walkers''''' (PTW), qui s'occupent eux-mêmes du défaut. Les ''page table walkers'' contiennent des registres qui leur permettent de faire leur travail. Le plus important est celui qui mémorise la position de la table des pages en mémoire RAM, dont nous avons parlé plus haut. Les PTW ont besoin, pour faire leur travail, de mémoriser l'adresse physique de la table des pages, ou du moins l'adresse de la table des pages de niveau 1 pour des tables des pages hiérarchiques. Mais d'autres registres existent. Toutes les informations nécessaires pour gérer les défauts de TLB sont stockées dans des registres spécialisés appelés des '''tampons de PTW''' (PTW buffers). ===L'abstraction matérielle des processus : une table des pages par processus=== [[File:Memoire virtuelle.svg|vignette|Mémoire virtuelle]] Il est possible d'implémenter l'abstraction matérielle des processus avec la pagination. En clair, chaque programme lancé sur l'ordinateur dispose de son propre espace d'adressage, ce qui fait que la même adresse logique ne pointera pas sur la même adresse physique dans deux programmes différents. Pour cela, il y a plusieurs méthodes. ====L'usage d'une table des pages unique avec un identifiant de processus dans chaque entrée==== La première solution n'utilise qu'une seule table des pages, mais chaque entrée est associée à un processus. Pour cela, chaque entrée contient un '''identifiant de processus''', un numéro qui précise pour quel processus, pour quel espace d'adressage, la correspondance est valide. La page des tables peut aussi contenir des entrées qui sont valides pour tous les processus en même temps. L'intérêt n'est pas évident, mais il le devient quand on se rappelle que le noyau de l'OS est mappé dans le haut de l'espace d'adressage. Et peu importe l'espace d'adressage, le noyau est toujours mappé de manière identique, les mêmes adresses logiques adressant la même adresse mémoire. En conséquence, les correspondances adresse physique-logique sont les mêmes pour le noyau, peu importe l'espace d'adressage. Dans ce cas, la correspondance est mémorisée dans une entrée, mais sans identifiant de processus. A la place, l'entrée contient un '''bit ''global''''', qui précise que cette correspondance est valide pour tous les processus. Le bit global accélère rapidement la traduction d'adresse pour l'accès au noyau. Un défaut de cette méthode est que le partage d'une page entre plusieurs processus est presque impossible. Impossible de partager une page avec seulement certains processus et pas d'autres : soit on partage une page avec tous les processus, soit on l'alloue avec un seul processus. ====L'usage de plusieurs tables des pages==== Une solution alternative, plus simple, utilise une table des pages par processus lancé sur l'ordinateur, une table des pages unique par espace d'adressage. À chaque changement de processus, le registre qui mémorise la position de la table des pages est modifié pour pointer sur la bonne. C'est le système d'exploitation qui se charge de cette mise à jour. Avec cette méthode, il est possible de partager une ou plusieurs pages entre plusieurs processus, en configurant les tables des pages convenablement. Les pages partagées sont mappées dans l'espace d'adressage de plusieurs processus, mais pas forcément au même endroit, pas forcément dans les mêmes adresses logiques. On peut placer la page partagée à l'adresse logique 0x0FFF pour un processus, à l'adresse logique 0xFF00 pour un autre processus, etc. Par contre, les entrées de la table des pages pour ces adresses pointent vers la même adresse physique. [[File:Vm5.png|centre|vignette|upright=2|Tables des pages de plusieurs processus.]] ===La taille des pages=== La taille des pages varie suivant le processeur et le système d'exploitation et tourne souvent autour de 4 kibioctets. Les processeurs actuels gèrent plusieurs tailles différentes pour les pages : 4 kibioctets par défaut, 2 mébioctets, voire 1 à 4 gibioctets pour les pages les plus larges. Les pages de 4 kibioctets sont les pages par défaut, les autres tailles de page sont appelées des ''pages larges''. La taille optimale pour les pages dépend de nombreux paramètres et il n'y a pas de taille qui convienne à tout le monde. Certaines applications gagnent à utiliser des pages larges, d'autres vont au contraire perdre drastiquement en performance en les utilisant. Le désavantage principal des pages larges est qu'elles favorisent la fragmentation mémoire. Si un programme veut réserver une portion de mémoire, pour une structure de donnée quelconque, il doit réserver une portion dont la taille est multiple de la taille d'une page. Par exemple, un programme ayant besoin de 110 kibioctets allouera 28 pages de 4 kibioctets, soit 120 kibioctets : 2 kibioctets seront perdus. Par contre, avec des pages larges de 2 mébioctets, on aura une perte de 2048 - 110 = 1938 kibioctets. En somme, des morceaux de mémoire seront perdus, car les pages sont trop grandes pour les données qu'on veut y mettre. Le résultat est que le programme qui utilise les pages larges utilisent plus de mémoire et ce d'autant plus qu'il utilise des données de petite taille. Un autre désavantage est qu'elles se marient mal avec certaines techniques d'optimisations de type ''copy-on-write''. Mais l'avantage est que la traduction des adresses est plus performante. Une taille des pages plus élevée signifie moins de pages, donc des tables des pages plus petites. Et des pages des tables plus petites n'ont pas besoin de beaucoup de niveaux de hiérarchie, voire peuvent se limiter à des tables des pages simples, ce qui rend la traduction d'adresse plus simple et plus rapide. De plus, les programmes ont une certaine localité spatiale, qui font qu'ils accèdent souvent à des données proches. La traduction d'adresse peut alors profiter de systèmes de mise en cache dont nous parlerons dans le prochain chapitre, et ces systèmes de cache marchent nettement mieux avec des pages larges. Il faut noter que la taille des pages est presque toujours une puissance de deux. Cela a de nombreux avantages, mais n'est pas une nécessité. Par exemple, le tout premier processeur avec de la pagination, le super-ordinateur Atlas, avait des pages de 3 kibioctets. L'avantage principal est que la traduction de l'adresse physique en adresse logique est trivial avec une puissance de deux. Cela garantit que l'on peut diviser l'adresse en un numéro de page et un ''offset'' : la traduction demande juste de remplacer les bits de poids forts par le numéro de page voulu. Sans cela, la traduction d'adresse implique des divisions et des multiplications, qui sont des opérations assez couteuses. ===Les entrées de la table des pages=== Avant de poursuivre, faisons un rapide rappel sur les entrées de la table des pages. Nous venons de voir que la table des pages contient de nombreuses informations : un bit ''valid'' pour la mémoire virtuelle, des bits ''dirty'' et ''accessed'' utilisés par l'OS, des bits de protection mémoire, un bit ''global'' et un potentiellement un identifiant de processus, etc. Étudions rapidement le format de la table des pages sur un processeur x86 32 bits. * Elle contient d'abord le numéro de page physique. * Les bits AVL sont inutilisés et peuvent être configurés à loisir par l'OS. * Le bit G est le bit ''global''. * Le bit PS vaut 0 pour une page de 4 kibioctets, mais est mis à 1 pour une page de 4 mébioctets dans le cas où le processus utilise des pages larges. * Le bit D est le bit ''dirty''. * Le bit A est le bit ''accessed''. * Le bit PCD indique que la page ne peut pas être cachée, dans le sens où le processeur ne peut copier son contenu dans le cache et doit toujours lire ou écrire cette page directement dans la RAM. * Le bit PWT indique que les écritures doivent mettre à jour le cache et la page en RAM (dans le chapitre sur le cache, on verra qu'il force le cache à se comporter comme un cache ''write-through'' pour cette page). * Le bit U/S précise si la page est accessible en mode noyau ou utilisateur. * Le bit R/W indique si la page est accessible en écriture, toutes les pages sont par défaut accessibles en lecture. * Le bit P est le bit ''valid''. [[File:PDE.png|centre|vignette|upright=2.5|Table des pages des processeurs Intel 32 bits.]] ==Comparaison des différentes techniques d'abstraction mémoire== Pour résumer, l'abstraction mémoire permet de gérer : la relocation, la protection mémoire, l'isolation des processus, la mémoire virtuelle, l'extension de l'espace d'adressage, le partage de mémoire, etc. Elles sont souvent implémentées en même temps. Ce qui fait qu'elles sont souvent confondues, alors que ce sont des concepts sont différents. Ces liens sont résumés dans le tableau ci-dessous. {|class="wikitable" |- ! ! colspan="5" | Avec abstraction mémoire ! rowspan="2" | Sans abstraction mémoire |- ! ! Relocation matérielle ! Segmentation en mode réel (x86) ! Segmentation, général ! Architectures à capacités ! Pagination |- ! Abstraction matérielle des processus | colspan="4" | Oui, relocation matérielle | Oui, liée à la traduction d'adresse | Impossible |- ! Mémoire virtuelle | colspan="2" | Non, sauf émulation logicielle | colspan="3" | Oui, gérée par le processeur et l'OS | Non, sauf émulation logicielle |- ! Extension de l'espace d'adressage | colspan="2" | Oui : registre de base élargi | colspan="2" | Oui : adresse de base élargie dans la table des segments | ''Physical Adress Extension'' des processeurs 32 bits | Commutation de banques |- ! Protection mémoire | Registre limite | Aucune | colspan="2" | Registre limite, droits d'accès aux segments | Gestion des droits d'accès aux pages | Possible, méthodes variées |- ! Partage de mémoire | colspan="2" | Non | colspan="2" | Segment partagés | Pages partagées | Possible, méthodes variées |} ===Les différents types de segmentation=== La segmentation regroupe plusieurs techniques franchement différentes, qui auraient gagné à être nommées différemment. La principale différence est l'usage de registres de relocation versus des registres de sélecteurs de segments. L'usage de registres de relocation est le fait de la relocation matérielle, mais aussi de la segmentation en mode réel des CPU x86. Par contre, l'usage de sélecteurs de segments est le fait des autres formes de segmentation, architectures à capacité inclues. La différence entre les deux est le nombre de segments. L'usage de registres de relocation fait que le CPU ne gère qu'un petit nombre de segments de grande taille. La mémoire virtuelle est donc rarement implémentée vu que swapper des segments de grande taille est trop long, l'impact sur les performances est trop important. Sans compter que l'usage de registres de base se marie très mal avec la mémoire virtuelle. Vu qu'un segment peut être swappé ou déplacée n'importe quand, il faut invalider les registres de base au moment du swap/déplacement, ce qui n'est pas chose aisée. Aucun processeur ne gère cela, les méthodes pour n'existent tout simplement pas. L'usage de registres de base implique que la mémoire virtuelle est absente. La protection mémoire est aussi plus limitée avec l'usage de registres de relocation. Elle se limite à des registres limite, mais la gestion des droits d'accès est limitée. En théorie, la segmentation en mode réel pourrait implémenter une version limitée de protection mémoire, avec une protection de l'espace exécutable. Mais ca n'a jamais été fait en pratique sur les processeurs x86. Le partage de la mémoire est aussi difficile sur les architectures avec des registres de base. L'absence de table des segments fait que le partage d'un segment est basiquement impossible sans utiliser des méthodes complétement tordues, qui ne sont jamais implémentées en pratique. ===Segmentation versus pagination=== Par rapport à la pagination, la segmentation a des avantages et des inconvénients. Tous sont liés aux propriétés des segments et pages : les segments sont de grande taille et de taille variable, les pages sont petites et de taille fixe. L'avantage principal de la segmentation est sa rapidité. Le fait que les segments sont de grande taille fait qu'on a pas besoin d'équivalent aux tables des pages inversée ou multiple, juste d'une table des segments toute simple. De plus, les échanges entre table des pages/segments et registres sont plus rares avec la segmentation. Par exemple, si un programme utilise un segment de 2 gigas, tous les accès dans le segment se feront avec une seule consultation de la table des segments. Alors qu'avec la pagination, il faudra une consultation de la table des pages chaque bloc de 4 kibioctet, au minimum. Mais les désavantages sont nombreux. Le système d'exploitation doit agencer les segments en RAM, et c'est une tâche complexe. Le fait que les segments puisse changer de taille rend le tout encore plus complexe. Par exemple, si on colle les segments les uns à la suite des autres, changer la taille d'un segment demande de réorganiser tous les segments en RAM, ce qui demande énormément de copies RAM-RAM. Une autre possibilité est de laisser assez d'espace entre les segments, mais cet espace est alors gâché, dans le sens où on ne peut pas y placer un nouveau segment. Swapper un segment est aussi très long, vu que les segments sont de grande taille, alors que swapper une page est très rapide. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=L'espace d'adressage du processeur | prevText=L'espace d'adressage du processeur | next=Les méthodes de synchronisation entre processeur et périphériques | nextText=Les méthodes de synchronisation entre processeur et périphériques }} </noinclude> 4h9e8c6dcdnbn45sx154ovn95jsx4ry Fonctionnement d'un ordinateur/Architectures multiprocesseurs et multicœurs 0 65962 763969 763679 2026-04-19T00:29:32Z Mewtow 31375 /* Le réseau d'interconnexion entre plusieurs cœurs */ 763969 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple est le réseau du processeur Celle, utilisé sur la PS3. Le réseau était un réseau en anneau, composé de 12 intermédiaires appelés ''Ramps''. Les processeurs étaient reliés chacun à un intermédiaire, chaque intermédiaire communiquait avec deux autres ''ramps'' ce qui formait un anneau de ''ramps''. Les données circulaient sur cet anneau en passant d'un ''ramp'' au suivant. Les données pouvaient parcourir l'anneau soit dans le sens horaires, soit dans le sens anti-horaire. Les ramps étaient prévus pour, ils avaient un bus pour le sens horaire, et un autre bus pour le sens anti-horaire. Pour être précis, ils avaient deux bus pour chaque sens, ce qui permettait de transférer deux données à chaque cycle d'horloge. [[File:Réseau d'interconnexion du processeur CELL.png|centre|vignette|upright=2|Réseau d'interconnexion du processeur CELL]] Un autre xemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Les interruptions inter-processeurs== Les '''interruptions inter-processeurs''' sont des interruptions déclenchées sur un processeur et exécutées sur un autre. Elles sont très utiles pour le système d'exploitation, pour des raisons qu'on ne peut pas expliquer ici. Disons simplement qu'elles permettent de répartir des programmes/''threads'' sur plusieurs processeurs. Un programme/''thread'' est démarré par une interruption, et le système d'exploitation détermine sur quel processeur elle doit être exécutée. L'utilité des interruptions inter-processeur est assez variée. Autrefois, elles servaient aussi pour la cohérence des caches, mais nous détaillerons cela dans un futur chapitre. Une interruption inter-processeurs peut être envoyée soit à un cœur bien précis, soit à n'importe quel cœur, soit à tous les cœurs, voire même revenir à l'envoyeur. Tout dépend de ce que décide le système d'exploitation. Les trois situations ne sont pas identiques, sur un point : comment préciser quel est le processeur de destination ? Si on envoie une interruption à un cœur bien précis, il faut préciser quel est le cœur qui réceptionne l'interruption. Pour cela, il n'y a pas 36 solutions : on numérote les processeurs/cœurs avec un '''numéro de processeur'''. Ce numéro leur est soit attribué au démarrage par le BIOS, soit est gravé dans leur silicium pour les processeurs multicœurs. Pour le reste, les interruptions inter-processeurs sont identiques aux interruptions normales. Elles ont un système de priorités, certaines devant passer avant les autres, là encore défini par des ''Interrupt Request Levels'' (IRQLs) ou quelque chose de similaire. Il peut y avoir des interruptions inter-processeur de type logicielles, à savoir lancées par une instruction machine. Par exemple, sur les IBM System/360 et les ''mainframes'' z/Architecture, le processeur avait une instruction SIGNAL PROCESSOR pour déclencher des interruptions logicielles inter-processeur. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Pour expliquer comment, nous allons étudier le cas des CPU x86, mais les implémentations ARM ou autres sont très similaires. L'ancien contrôleur d'interruption 8259A ne gérait pas les interruptions inter-processeurs, ce qui fait que les cartes mères multiprocesseurs devaient incorporer un contrôleur d'interruption spécial en complément. Par contre, son successeur, l'APIC, les gérait nativement. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le '''''local APIC''''', qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un '''''IO-APIC''''', qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un '''bus APIC''' spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] Le déclenchement d'une interruption inter-processeur se fait en écrivant dans un registre appelé l''''''Interrupt Command Register'''''. Un détail important est que l'écriture se fait dans le ''local APIC'' de l'envoyer, du processeur qui veut envoyer une interruption, pas dans le registre du processeur qui doit réceptionner l'interruption ! Le registre est composé de deux registres de 32 bits, et mémorise : le numéro du processeur de destination, le mode de transfert (à tous, à un cœur, etc), le numéro du vecteur d'interruption (pour préciser quelle interruption exécuter), et quelques informations supplémentaires. À charge de l'IO-APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le multiprocesseur/multicœur asymétrique== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. Un exemple de multicoeurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> ajluzhy5e7jg3vjlr1dnigg7zh2q964 763970 763969 2026-04-19T00:38:54Z Mewtow 31375 /* Le réseau d'interconnexion entre plusieurs cœurs */ 763970 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple est le réseau du processeur Celle, utilisé sur la PS3. Les transferts sur ce bus se font par paquets de 128 bits. Le réseau était composé de 12 intermédiaires appelés ''Ramps''. Chaque intermédiaire communiquait avec deux autres ''ramps'' ce qui formait un anneau de ''ramps''. Les paquets de 128 bits circulaient sur cet anneau en passant d'un ''ramp'' au suivant, soit dans le sens horaires, soit dans le sens anti-horaire. Avant d'entrer dans l'anneau, les processeurs envoient une requête à un circuit d'arbitrage, qui commande les transferts de données sur l'anneau. Si celui-ci accepte la requête, la donnée est envoyée au ''ramp'' et circule dans l'anneau. Le sens de transfert, horaire ou anti-horaire, est choisit de manière à prendre le chemin le plus court. Les paquets passent alors de ''ramp'' en ''ramp''. Chaque ''ramp'' reçoit le paquet, regarde l'adresse de destination du paquet, et décide de la suite. Soit le paquet lui est destiné, il le traite. Sinon, le il transmet au ''ramp'' suivant. Le bus permet de transférer 4 paquets de 128 bits par cycle. Pour cela, les ''ramps'' avaient quatre bus, chacun de 128 bits : deux bus pour le sens horaire, et deux autres bus pour le sens anti-horaire. Il peut y avoir 3 transferts simultanés, tant que les paquets ne se recouvrent pas, mais les transferts sont de taille arbitraire. Le réseau d'interconnexion avait une fréquence de 1,6 GHz. Le débit binaire de ce réseau en anneau est donc de 128 * 4 * 1,6 GHz = 204,8 GB/s. Il s'agit d'un maximum théorique, qui n'est jamais atteint en pratique. [[File:Réseau d'interconnexion du processeur CELL.png|centre|vignette|upright=2|Réseau d'interconnexion du processeur CELL]] Un autre xemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Les interruptions inter-processeurs== Les '''interruptions inter-processeurs''' sont des interruptions déclenchées sur un processeur et exécutées sur un autre. Elles sont très utiles pour le système d'exploitation, pour des raisons qu'on ne peut pas expliquer ici. Disons simplement qu'elles permettent de répartir des programmes/''threads'' sur plusieurs processeurs. Un programme/''thread'' est démarré par une interruption, et le système d'exploitation détermine sur quel processeur elle doit être exécutée. L'utilité des interruptions inter-processeur est assez variée. Autrefois, elles servaient aussi pour la cohérence des caches, mais nous détaillerons cela dans un futur chapitre. Une interruption inter-processeurs peut être envoyée soit à un cœur bien précis, soit à n'importe quel cœur, soit à tous les cœurs, voire même revenir à l'envoyeur. Tout dépend de ce que décide le système d'exploitation. Les trois situations ne sont pas identiques, sur un point : comment préciser quel est le processeur de destination ? Si on envoie une interruption à un cœur bien précis, il faut préciser quel est le cœur qui réceptionne l'interruption. Pour cela, il n'y a pas 36 solutions : on numérote les processeurs/cœurs avec un '''numéro de processeur'''. Ce numéro leur est soit attribué au démarrage par le BIOS, soit est gravé dans leur silicium pour les processeurs multicœurs. Pour le reste, les interruptions inter-processeurs sont identiques aux interruptions normales. Elles ont un système de priorités, certaines devant passer avant les autres, là encore défini par des ''Interrupt Request Levels'' (IRQLs) ou quelque chose de similaire. Il peut y avoir des interruptions inter-processeur de type logicielles, à savoir lancées par une instruction machine. Par exemple, sur les IBM System/360 et les ''mainframes'' z/Architecture, le processeur avait une instruction SIGNAL PROCESSOR pour déclencher des interruptions logicielles inter-processeur. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Pour expliquer comment, nous allons étudier le cas des CPU x86, mais les implémentations ARM ou autres sont très similaires. L'ancien contrôleur d'interruption 8259A ne gérait pas les interruptions inter-processeurs, ce qui fait que les cartes mères multiprocesseurs devaient incorporer un contrôleur d'interruption spécial en complément. Par contre, son successeur, l'APIC, les gérait nativement. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le '''''local APIC''''', qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un '''''IO-APIC''''', qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un '''bus APIC''' spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] Le déclenchement d'une interruption inter-processeur se fait en écrivant dans un registre appelé l''''''Interrupt Command Register'''''. Un détail important est que l'écriture se fait dans le ''local APIC'' de l'envoyer, du processeur qui veut envoyer une interruption, pas dans le registre du processeur qui doit réceptionner l'interruption ! Le registre est composé de deux registres de 32 bits, et mémorise : le numéro du processeur de destination, le mode de transfert (à tous, à un cœur, etc), le numéro du vecteur d'interruption (pour préciser quelle interruption exécuter), et quelques informations supplémentaires. À charge de l'IO-APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le multiprocesseur/multicœur asymétrique== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. Un exemple de multicoeurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 2sn0z5hehxg6f9r090igxhnr8wc19ej 763971 763970 2026-04-19T00:39:54Z Mewtow 31375 /* Le réseau d'interconnexion entre plusieurs cœurs */ 763971 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple est le réseau du processeur Celle, utilisé sur la PS3. Les transferts sur ce bus se font par paquets de 128 bits. Le réseau était composé de 12 intermédiaires appelés ''Ramps''. Chaque intermédiaire communiquait avec deux autres ''ramps'' ce qui formait un anneau de ''ramps''. Les paquets de 128 bits circulaient sur cet anneau en passant d'un ''ramp'' au suivant, soit dans le sens horaires, soit dans le sens anti-horaire. Avant d'entrer dans l'anneau, les processeurs envoient une requête à un circuit d'arbitrage, qui commande les transferts de données sur l'anneau. Si celui-ci accepte la requête, la donnée est envoyée au ''ramp'' et circule dans l'anneau. Il faut noter que le contrôleur mémoire a la priorité sur tout le reste. Le sens de transfert, horaire ou anti-horaire, est choisit de manière à prendre le chemin le plus court. Les paquets passent alors de ''ramp'' en ''ramp''. Chaque ''ramp'' reçoit le paquet, regarde l'adresse de destination du paquet, et décide de la suite. Soit le paquet lui est destiné, il le traite. Sinon, le il transmet au ''ramp'' suivant. Le bus permet de transférer 4 paquets de 128 bits par cycle. Pour cela, les ''ramps'' avaient quatre bus, chacun de 128 bits : deux bus pour le sens horaire, et deux autres bus pour le sens anti-horaire. Il peut y avoir 3 transferts simultanés, tant que les paquets ne se recouvrent pas, mais les transferts sont de taille arbitraire. Le réseau d'interconnexion avait une fréquence de 1,6 GHz. Le débit binaire de ce réseau en anneau est donc de 128 * 4 * 1,6 GHz = 204,8 GB/s. Il s'agit d'un maximum théorique, qui n'est jamais atteint en pratique. [[File:Réseau d'interconnexion du processeur CELL.png|centre|vignette|upright=2|Réseau d'interconnexion du processeur CELL]] Un autre xemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Les interruptions inter-processeurs== Les '''interruptions inter-processeurs''' sont des interruptions déclenchées sur un processeur et exécutées sur un autre. Elles sont très utiles pour le système d'exploitation, pour des raisons qu'on ne peut pas expliquer ici. Disons simplement qu'elles permettent de répartir des programmes/''threads'' sur plusieurs processeurs. Un programme/''thread'' est démarré par une interruption, et le système d'exploitation détermine sur quel processeur elle doit être exécutée. L'utilité des interruptions inter-processeur est assez variée. Autrefois, elles servaient aussi pour la cohérence des caches, mais nous détaillerons cela dans un futur chapitre. Une interruption inter-processeurs peut être envoyée soit à un cœur bien précis, soit à n'importe quel cœur, soit à tous les cœurs, voire même revenir à l'envoyeur. Tout dépend de ce que décide le système d'exploitation. Les trois situations ne sont pas identiques, sur un point : comment préciser quel est le processeur de destination ? Si on envoie une interruption à un cœur bien précis, il faut préciser quel est le cœur qui réceptionne l'interruption. Pour cela, il n'y a pas 36 solutions : on numérote les processeurs/cœurs avec un '''numéro de processeur'''. Ce numéro leur est soit attribué au démarrage par le BIOS, soit est gravé dans leur silicium pour les processeurs multicœurs. Pour le reste, les interruptions inter-processeurs sont identiques aux interruptions normales. Elles ont un système de priorités, certaines devant passer avant les autres, là encore défini par des ''Interrupt Request Levels'' (IRQLs) ou quelque chose de similaire. Il peut y avoir des interruptions inter-processeur de type logicielles, à savoir lancées par une instruction machine. Par exemple, sur les IBM System/360 et les ''mainframes'' z/Architecture, le processeur avait une instruction SIGNAL PROCESSOR pour déclencher des interruptions logicielles inter-processeur. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Pour expliquer comment, nous allons étudier le cas des CPU x86, mais les implémentations ARM ou autres sont très similaires. L'ancien contrôleur d'interruption 8259A ne gérait pas les interruptions inter-processeurs, ce qui fait que les cartes mères multiprocesseurs devaient incorporer un contrôleur d'interruption spécial en complément. Par contre, son successeur, l'APIC, les gérait nativement. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le '''''local APIC''''', qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un '''''IO-APIC''''', qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un '''bus APIC''' spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] Le déclenchement d'une interruption inter-processeur se fait en écrivant dans un registre appelé l''''''Interrupt Command Register'''''. Un détail important est que l'écriture se fait dans le ''local APIC'' de l'envoyer, du processeur qui veut envoyer une interruption, pas dans le registre du processeur qui doit réceptionner l'interruption ! Le registre est composé de deux registres de 32 bits, et mémorise : le numéro du processeur de destination, le mode de transfert (à tous, à un cœur, etc), le numéro du vecteur d'interruption (pour préciser quelle interruption exécuter), et quelques informations supplémentaires. À charge de l'IO-APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le multiprocesseur/multicœur asymétrique== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. Un exemple de multicoeurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 15xn0upppipb9l3ccfz56ipw9mdy0vy 763972 763971 2026-04-19T00:43:42Z Mewtow 31375 /* Le réseau d'interconnexion entre plusieurs cœurs */ 763972 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple est le réseau du processeur Celle, utilisé sur la PS3. Les transferts sur ce bus se font par paquets de 128 bits. Le réseau était composé de 12 intermédiaires appelés ''Ramps''. Chaque intermédiaire communiquait avec deux autres ''ramps'' ce qui formait un anneau de ''ramps''. Les paquets de 128 bits circulaient sur cet anneau en passant d'un ''ramp'' au suivant, soit dans le sens horaires, soit dans le sens anti-horaire. Avant d'entrer dans l'anneau, les processeurs envoient une requête à un circuit d'arbitrage, qui commande les transferts de données sur l'anneau. Si celui-ci accepte la requête, la donnée est envoyée au ''ramp'' et circule dans l'anneau. Il faut noter que le contrôleur mémoire a la priorité sur tout le reste. Le sens de transfert, horaire ou anti-horaire, est choisit de manière à prendre le chemin le plus court. Les paquets passent alors de ''ramp'' en ''ramp''. Chaque ''ramp'' reçoit le paquet, regarde l'adresse de destination du paquet, et décide de la suite. Soit le paquet lui est destiné, il le traite. Sinon, le il transmet au ''ramp'' suivant. Le bus permet de transférer 4 paquets de 128 bits par cycle. Pour cela, les ''ramps'' avaient quatre bus, chacun de 128 bits : deux bus pour le sens horaire, et deux autres bus pour le sens anti-horaire. Il peut y avoir 3 transferts simultanés, tant que les paquets ne se recouvrent pas, mais les transferts sont de taille arbitraire. Le réseau d'interconnexion avait une fréquence de 1,6 GHz. Si vous faites les calculs, vous tomberez cependant sur un débit binaire théorique de plusieurs centaines de gibioctets par secondes, qui n'est cependant jamais atteint en pratique. [[File:Réseau d'interconnexion du processeur CELL.png|centre|vignette|upright=2|Réseau d'interconnexion du processeur CELL]] Un autre xemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Les interruptions inter-processeurs== Les '''interruptions inter-processeurs''' sont des interruptions déclenchées sur un processeur et exécutées sur un autre. Elles sont très utiles pour le système d'exploitation, pour des raisons qu'on ne peut pas expliquer ici. Disons simplement qu'elles permettent de répartir des programmes/''threads'' sur plusieurs processeurs. Un programme/''thread'' est démarré par une interruption, et le système d'exploitation détermine sur quel processeur elle doit être exécutée. L'utilité des interruptions inter-processeur est assez variée. Autrefois, elles servaient aussi pour la cohérence des caches, mais nous détaillerons cela dans un futur chapitre. Une interruption inter-processeurs peut être envoyée soit à un cœur bien précis, soit à n'importe quel cœur, soit à tous les cœurs, voire même revenir à l'envoyeur. Tout dépend de ce que décide le système d'exploitation. Les trois situations ne sont pas identiques, sur un point : comment préciser quel est le processeur de destination ? Si on envoie une interruption à un cœur bien précis, il faut préciser quel est le cœur qui réceptionne l'interruption. Pour cela, il n'y a pas 36 solutions : on numérote les processeurs/cœurs avec un '''numéro de processeur'''. Ce numéro leur est soit attribué au démarrage par le BIOS, soit est gravé dans leur silicium pour les processeurs multicœurs. Pour le reste, les interruptions inter-processeurs sont identiques aux interruptions normales. Elles ont un système de priorités, certaines devant passer avant les autres, là encore défini par des ''Interrupt Request Levels'' (IRQLs) ou quelque chose de similaire. Il peut y avoir des interruptions inter-processeur de type logicielles, à savoir lancées par une instruction machine. Par exemple, sur les IBM System/360 et les ''mainframes'' z/Architecture, le processeur avait une instruction SIGNAL PROCESSOR pour déclencher des interruptions logicielles inter-processeur. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Pour expliquer comment, nous allons étudier le cas des CPU x86, mais les implémentations ARM ou autres sont très similaires. L'ancien contrôleur d'interruption 8259A ne gérait pas les interruptions inter-processeurs, ce qui fait que les cartes mères multiprocesseurs devaient incorporer un contrôleur d'interruption spécial en complément. Par contre, son successeur, l'APIC, les gérait nativement. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le '''''local APIC''''', qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un '''''IO-APIC''''', qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un '''bus APIC''' spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] Le déclenchement d'une interruption inter-processeur se fait en écrivant dans un registre appelé l''''''Interrupt Command Register'''''. Un détail important est que l'écriture se fait dans le ''local APIC'' de l'envoyer, du processeur qui veut envoyer une interruption, pas dans le registre du processeur qui doit réceptionner l'interruption ! Le registre est composé de deux registres de 32 bits, et mémorise : le numéro du processeur de destination, le mode de transfert (à tous, à un cœur, etc), le numéro du vecteur d'interruption (pour préciser quelle interruption exécuter), et quelques informations supplémentaires. À charge de l'IO-APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le multiprocesseur/multicœur asymétrique== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. Un exemple de multicoeurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 6km7ozb52idjto3zkom8kyo801q5vlx 763973 763972 2026-04-19T00:45:53Z Mewtow 31375 /* Le réseau d'interconnexion entre plusieurs cœurs */ 763973 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple est le réseau du processeur Celle, utilisé sur la PS3. Les transferts sur ce bus se font par paquets de 128 bits. Le réseau était composé de 12 intermédiaires appelés ''Ramps''. Chaque intermédiaire communiquait avec deux autres ''ramps'' ce qui formait un anneau de ''ramps''. Les paquets de 128 bits circulaient sur cet anneau en passant d'un ''ramp'' au suivant, soit dans le sens horaires, soit dans le sens anti-horaire. Avant d'entrer dans l'anneau, les processeurs envoient une requête à un circuit d'arbitrage, qui commande les transferts de données sur l'anneau. Si celui-ci accepte la requête, la donnée est envoyée au ''ramp'' et circule dans l'anneau. Il faut noter que le contrôleur mémoire a la priorité sur tout le reste. Le sens de transfert, horaire ou anti-horaire, est choisit de manière à prendre le chemin le plus court. Les paquets passent alors de ''ramp'' en ''ramp''. Chaque ''ramp'' reçoit le paquet, regarde l'adresse de destination du paquet, et décide de la suite. Soit le paquet lui est destiné, il le traite. Sinon, le il transmet au ''ramp'' suivant. Le bus permet de transférer 4 paquets de 128 bits par cycle. Pour cela, les ''ramps'' avaient quatre bus, chacun de 128 bits : deux bus pour le sens horaire, et deux autres bus pour le sens anti-horaire. Il peut y avoir 3 transferts simultanés, tant que les paquets ne se recouvrent pas, mais les transferts sont de taille arbitraire. Le réseau d'interconnexion avait une fréquence de 1,6 GHz. Si vous faites les calculs, vous tomberez cependant sur un débit binaire théorique d'environ 300 gibioctets par secondes, qui n'est cependant jamais atteint en pratique, pour des raisons techniques liées à la cohérence des caches. La limite théorique est en pratique plus proche des 200 gibioctets par secondes, en tenant compte de ces limitations théoriques. [[File:Réseau d'interconnexion du processeur CELL.png|centre|vignette|upright=2|Réseau d'interconnexion du processeur CELL]] Un autre xemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Les interruptions inter-processeurs== Les '''interruptions inter-processeurs''' sont des interruptions déclenchées sur un processeur et exécutées sur un autre. Elles sont très utiles pour le système d'exploitation, pour des raisons qu'on ne peut pas expliquer ici. Disons simplement qu'elles permettent de répartir des programmes/''threads'' sur plusieurs processeurs. Un programme/''thread'' est démarré par une interruption, et le système d'exploitation détermine sur quel processeur elle doit être exécutée. L'utilité des interruptions inter-processeur est assez variée. Autrefois, elles servaient aussi pour la cohérence des caches, mais nous détaillerons cela dans un futur chapitre. Une interruption inter-processeurs peut être envoyée soit à un cœur bien précis, soit à n'importe quel cœur, soit à tous les cœurs, voire même revenir à l'envoyeur. Tout dépend de ce que décide le système d'exploitation. Les trois situations ne sont pas identiques, sur un point : comment préciser quel est le processeur de destination ? Si on envoie une interruption à un cœur bien précis, il faut préciser quel est le cœur qui réceptionne l'interruption. Pour cela, il n'y a pas 36 solutions : on numérote les processeurs/cœurs avec un '''numéro de processeur'''. Ce numéro leur est soit attribué au démarrage par le BIOS, soit est gravé dans leur silicium pour les processeurs multicœurs. Pour le reste, les interruptions inter-processeurs sont identiques aux interruptions normales. Elles ont un système de priorités, certaines devant passer avant les autres, là encore défini par des ''Interrupt Request Levels'' (IRQLs) ou quelque chose de similaire. Il peut y avoir des interruptions inter-processeur de type logicielles, à savoir lancées par une instruction machine. Par exemple, sur les IBM System/360 et les ''mainframes'' z/Architecture, le processeur avait une instruction SIGNAL PROCESSOR pour déclencher des interruptions logicielles inter-processeur. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Pour expliquer comment, nous allons étudier le cas des CPU x86, mais les implémentations ARM ou autres sont très similaires. L'ancien contrôleur d'interruption 8259A ne gérait pas les interruptions inter-processeurs, ce qui fait que les cartes mères multiprocesseurs devaient incorporer un contrôleur d'interruption spécial en complément. Par contre, son successeur, l'APIC, les gérait nativement. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le '''''local APIC''''', qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un '''''IO-APIC''''', qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un '''bus APIC''' spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] Le déclenchement d'une interruption inter-processeur se fait en écrivant dans un registre appelé l''''''Interrupt Command Register'''''. Un détail important est que l'écriture se fait dans le ''local APIC'' de l'envoyer, du processeur qui veut envoyer une interruption, pas dans le registre du processeur qui doit réceptionner l'interruption ! Le registre est composé de deux registres de 32 bits, et mémorise : le numéro du processeur de destination, le mode de transfert (à tous, à un cœur, etc), le numéro du vecteur d'interruption (pour préciser quelle interruption exécuter), et quelques informations supplémentaires. À charge de l'IO-APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le multiprocesseur/multicœur asymétrique== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. Un exemple de multicoeurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il était conçu spécifiquement pour cette console. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Cette fois-ci, les coprocesseurs sont intégrés dans le même processeur. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> pqrl79h4e7ftystf7cq7e60mhyz3lwv Pour lire Platon/Premiers pas 0 66394 763982 642535 2026-04-19T06:41:06Z PandaMystique 119061 Redirection supprimée vers [[Pour lire Platon/Platon et les mythes]] 763982 wikitext text/x-wiki Ce chapitre est une foire aux questions à l'usage des grands débutants : il doit permettre d'acquérir une culture générale élémentaire sur Platon. Dans ce but, on ne propose ici que des réponses courtes en termes non techniques sur des sujets généraux, sans jamais entrer dans les détails. Le chapitre est présenté sous forme de questions afin que le lecteur puisse trouver facilement des réponses à des questions qu'il peut se poser en commençant l'étude de la pensée de Platon. La liste des questions n'est pas fermée, et vous pouvez proposer vos propres questions afin d'améliorer l'utilité de chapitre. {{Pour lire Platon}} == Qui est Platon ? == Platon est un philosophe grec qui a vécu au Veme et IVeme avant J.C., il y a environ 2400 ans. Il appartenait à une famille aristocratique de la ville d'Athènes qui était l'une des plus puissantes cités et l'un des grands centres culturels de la [[Grec ancien/La grèce antique|Grèce antique]]. Platon est l'un des tous premiers et l'un des plus importants philosophes occidentaux. C'est lui qui a donné la première définition élaborée du mot « philosophie ». == Pourquoi Platon a-t-il écrit ? == Platon est issu d'une famille aristocratique. Son éducation le destinait à une carrière politique. Plusieurs facteurs l'ont détourné de cette voie. La rencontre avec Socrate et l'exécution de ce dernier ; la corruption d'Athènes et, en particulier, les régimes arbitraires qui ont suivis la défaite contre Sparte. Dès lors, Platon a jugé nécessaire de réfléchir à ce que doit être une cité juste et a cherché à concilier pensée philosophique et action politique. == Qu'a-t-il écrit ? == Nous avons, sous le nom de Platon, plus d'une trentaine de dialogues, un recueil de définitions et quelques lettres. Tous ces textes ne sont pas de manière certaine l'œuvre de Platon. On classe ces dialogues en trois groupes selon le degré de certitude de leur authenticité : les dialogues qui sont bien l'œuvre de Platon, les dialogues douteux pour lesquels il y incertitude, et enfin les dialogues dont il est certain qu'ils ne sont pas de sa main. Il peut bien sûr y avoir des désaccords entre les spécialistes sur la question de savoir dans quel groupe classer certains dialogues. == Dans quel ordre Platon a-t-il écrit ses dialogues ? == Nous n'en savons rien. Le classement qui fait l'objet du plus large accord consiste à ranger les dialogues en trois groupes : jeunesse, maturité et vieillesse. Ce classement possède une certaine objectivité dans la mesure où il est le résultat d'une analyse du style et du vocabulaire des textes. Mais, au sein de chaque groupe, il est impossible de déterminer un ordre chronologique, et certains dialogues peuvent être classés par certains commentateurs dans un groupe ou un autre selon l'interprétation qu'ils font de leur contenu. Par exemple, un dialogue de jeunesse peut sembler contenir des thèses qui le rattachent plutôt aux dialogues de la maturité. Mais, dans ce genre d'argumentations, les certitudes sont faibles. La question de la chronologie ne paraît de toute façon pas globalement essentielle pour lire Platon. == Pourquoi Platon a-t-il écrit des dialogues et pas des traités ? == On peut en donner plusieurs raisons. Les dialogues de Platon ne sont ni des cours ni des exposés de sa pensée, mais des conversations dans le cadre de la vie quotidienne de l'Athènes de la fin Vème siècle avt. J.C. La conversation permet aux personnages de discuter de leurs opinions et de voir s'ils sont capables de répondre aux objections qui peuvent apparaître au cours de leur examen. La conversation est donc une manière de philosopher qui suppose que les protagonistes acceptent de prendre en compte des avis différents pour progresser dans la recherche de la vérité. La forme du dialogue illustre ainsi l'effort sur soi que doivent consentir les interlocuteurs, alors qu'un traité ne permet pas un tel échange avec le lecteur. L'importance aux yeux de Platon de la forme du dialogue se traduit aussi par le fait qu'un dialogue ne se termine pas forcement sur une réussite ; mais l'échec de la recherche aura tout de même permis de mettre des opinions à l'épreuve de la réfutation, ce qui représente un gain véritable puisque les personnages se seront délivrés de certains de leurs préjugés, à défaut de découvrir la vérité. Une autre raison est l'attitude de Socrate à l'égard des interlocuteurs qui sont peu aptes à se conformer à ce type de discussions : Socrate feint l'ignorance de manière à les pousser à s'exprimer avec trop d'assurance, ce qui permet de les placer plus facilement face à leurs contradictions et à leur ignorance. Il est évident que cette dramatisation de la discussion serait difficile dans un traité. == Qui sont les personnages des dialogues de Platon ? == Les personnages des dialogues sont de jeunes athéniens, des savants étrangers, des artistes, des devins, des amis de Socrate ou des proches de Platon. Enfin, le personnage qui revient dans presque tous les dialogues de Platon est son maître, Socrate. Les personnages jeunes illustrent l'attirance de Socrate pour les jeunes garçons, attirance érotique qui se place dans le cadre de la pédérastie grecque, mais que Socrate transforme en rapport maître/élève d'où la relation charnelle est exclue, puisque le soin de l'âme est substitué à l'attirance pour les beaux corps. Lorsque les personnages sont des savants, des artistes, des devins ou des sophistes, il s'agit généralement pour le personnage de Socrate d'en montrer l'ignorance et les prétentions vaines, ce qui donne parfois lieu à des échanges violents. Dans certains cas, les personnages sont des disciples très connus de Socrate, mais ils sont surtout fameux pour leur rôle funeste dans l'histoire grecque. C'est le cas de Critias, de Charmide (oncle de Platon) et d'Alcibiade. La conversation montre alors que Socrate les a encouragé dans le voie de la philosophie, mais que ceux-ci ont échoué à comprendre de quoi Socrate leur parlait, par exemple lorsque la conversation tourne autour de la sagesse et de l'art politique. Ces dialogues ont donc un but apologétique. == Pourquoi Socrate est-il le personnage principal des dialogues de Platon ? == Le dialogue socratique est un genre qui n'est pas propre à Platon, qu'il n'a sans doute pas inventé, et il s'est développé tout au long du IVeme siècle avant J.C. La mise en scène de Socrate est une forme d'apologie d'un philosophe dont la mort par exécution a choqué ses disciples qui ont eu à cœur de servir sa mémoire. À partir des grands dialogues de vieillesse, cependant, le personnage de Socrate tend à s'effacer, et il n'est plus présent dans ''Les Lois''. Une raison que l'on donne à cet effacement est que les thèses de Platon sont alors trop différentes de ce que les contemporains savaient du Socrate historique. == Le personnage de Socrate est-il une image fidèle du Socrate historique ? == Comme cette mise en scène de Socrate est aussi une création littéraire et philosophique, cela rend difficile la distinction entre ce qui revient à Platon et ce que l'on devrait attribuer à Socrate. Ce problème n'a pas reçu de solution satisfaisante parmi les commentateurs. Quelques thèses présentes dans les dialogues sont en tout cas attribuées à Socrate, comme l'importance de la définition des concepts et le premier rôle de l'investigation morale, illustrées par les dialogues dits socratiques. == De quoi Platon parle-t-il dans ses dialogues ? == Les sujets sont très nombreux et il ne saurait être question d'en faire une liste ou un résumé. On peut toutefois en donner une idée générale de la manière suivante : un certain nombre de dialogues (que l'on regroupe sous le nom de ''dialogues socratiques''), qui sont souvent les plus courts, ont pour sujet des problèmes moraux, tels que le courage, la vertu, le mensonge, l'amitié, la piété, la justice. Dans l'ensemble, ces dialogues tournent autour de l'idée que la vertu est une connaissance. Ces dialogues éthiques culminent dans le ''Ménon'' et le ''Protagoras''. D'autres dialogues ont pour objet la politique. C'est le cas de ''La République'', du ''Politique'' et des ''Lois''. D'autres abordent longuement des questions métaphysiques telles que l'être, le non-être, la connaissance (en particulier le problème de la connaissance des réalités vraies). On peut proposer, par souci d'une présentation générale structurée, de soutenir que tous ces sujets sont liés entre eux par ce que l'on appelle la théorie des formes : cette théorie formule l'hypothèse de l'existence de réalités immuables, modèles du monde sensible et changeant. La connaissance de ces formes est pour le philosophe ce qui permet de fonder la morale et la politique, deux domaines dont le but est le soin de l'âme qui est l'un des soucis majeur de Platon. == Les dialogues de Platon forment-ils un système philosophique ? == La réponse à cette question varie selon les interprètes et il est difficile d'en donner une idée sans entrer dans des détails qui dépasseraient le cadre de cette introduction. On ne proposera donc ici qu'une réponse schématique en deux parties. Si l'on considère que Platon n'a pas exposé sa pensée dans ses dialogues, mais dans un enseignement oral pour nous aujourd'hui perdu, ou si l'on considère simplement que les dialogues sont d'abord écrits pour éprouver la réflexion du lecteur, sans rien lui imposer, alors il est clair que les textes ne nous mettent pas vraiment en présence d'un ensemble ordonné et cohérent de thèses philosophiques que l'on pourrait désigner du nom de ''système''. Si, au contraire, on estime que Platon a exposé ses idées dans les dialogues, alors l'ensemble des textes forment un tout, même s'ils ne présentent pas de manière évidente un système de pensée. Cette interprétation, qui est la plus répandue, s'accompagne généralement de l'idée qu'il faut distinguer au sein des dialogues des groupes qui expriment un certain avancement de la pensée de Platon. Aussi serait-ce moins à un système formé d'un seul bloc que l'on aurait à faire, qu'à une pensée cohérente et systématique en évolution, comme en témoigne le fait que les dialogues sont toujours présentés sous la forme de recherches sur un sujet. == Pourquoi lire un auteur qui est mort il y a 24 siècles ? == La civilisation de Platon et ses valeurs ne sont pas les nôtres et elles sont très éloignées de nous dans le temps. Même en considérant que la Grèce est pour nous l'origine des sciences et de la philosophie et que notre civilisation européenne n'aurait pu exister sans la culture de l'Antiquité, les manières de regarder le monde, la nature, l'homme, la vie et bien d'autres choses semblent trop éloignées des préoccupations de l'homme moderne pour nous intéresser. Que pourrait donc nous apporter la lecture des œuvres de Platon ? Les questions que posent Platon dans ses dialogues ne sont pas différentes des questions que l'on trouve dans toutes les cultures, partout dans le monde, à toutes les époques : qu'est-ce que l'univers ? que peut-on connaître ? qu'est-ce que l'homme ? qu'est-ce que la mort ? Platon écrit donc sur des sujets qui ont toujours été actuels : le sens de l'existence, la question de la meilleure vie à mener, le mal, la justice, l'amitié, l'amour, la sexualité, le plaisir, l'art. Mais sur ces questions qui continuent de nous concerner aujourd'hui, Platon apporte des réponses à partir d'une perspective culturellement différente de la nôtre. Cette différence de point de vue que l'on trouve dans les dialogues peut nous aider à prendre du recul par rapport à des opinions qui nous semblent être des évidences parce que notre culture nous les a apprises dès notre plus jeune âge. Bien sûr, parmi ces réponses, certaines sont scientifiquement obsolètes ou ne sont plus philosophiquement soutenues de nos jours : il vaut mieux lire un ouvrage d'astrophysique qu'un dialogue de Platon pour apprendre quelque chose sur l'univers. Il est toutefois conseillé d'être attentif à la manière dont Platon aborde ces sujets, car, même si sa représentation de l'univers est fausse et que la lecture de ses textes sur ces sujets peut être des plus ennuyeuses, les cadres de sa pensée ne sont pas pour ces raisons automatiquement invalides. Enfin, toutes les réponses de Platon ne sont pas si éloignées de nous : sur certains sujets, nous réfléchissons encore aujourd'hui dans le cadre de pensée défini par Platon. Ce point sera illustré dans le prochain chapitre. 6twb0jiqc9oa54hwhbjtwtzbq4ib9me Les cartes graphiques/Les Render Output Target 0 67394 763938 763892 2026-04-18T13:17:15Z Mewtow 31375 /* Les autres fonctions des ROPs */ 763938 wikitext text/x-wiki Pour rappel, les étapes précédentes du pipeline graphiques manipulaient non pas des pixels, mais des fragments. Pour rappel, la distinction entre fragment et pixel est pertinente quand plusieurs objets sont l'un derrière l'autre. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. La couleur finale dépend de la couleur de tous ces points d'intersection. Intuitivement, l'objet le plus proche est censé cacher les autres et c'est donc lui qui décide de la couleur du pixel, mais cela demande de déterminer quel est l'objet le plus proche. De plus, certains objets sont transparents et la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Tout demande de calculer un pseudo-pixel pour chaque point d'intersection et de combiner leurs couleurs pour obtenir le résultat final. Les pseudo-pixels en question sont des '''fragments'''. Chaque fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont donc combinés pour obtenir la couleur finale de ce pixel. Pour résumer, la profondeur des fragments doit être gérée, de même que la transparence, etc. Et c'est justement le rôle de l'étage du pipeline que nous allons voir maintenant. Ces opérations sont réalisées dans un circuit qu'on nomme le '''Raster Operations Pipeline''' (ROP), aussi appelé ''Render Output Target'', situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. ==Les fonctions des ROP== Les ROP incorporent plusieurs fonctionnalités qui sont assez diverses. Leur seul lien est qu'il est préférable de les implémenter en matériel plutôt qu'en logiciel, et en dehors des unités de textures. Il s'agit de fonctionnalités assez simples, basiques, mais nécessaires au fonctionnement de tout rendu 3D. Elles ont aussi pour particularité de beaucoup accéder à la mémoire vidéo. C'est la raison pour laquelle le ROP est situé en fin de pipeline, proche de la mémoire vidéo. Voyons quelles sont ces fonctionnalités. ===Les effets classiques du ROP=== Sa fonction la plus importante est l'élimination des pixels cachés, grâce au tampon de profondeur. Pour chaque fragment, il lit le pixel correspondant dans le tampon de profondeur, fait la comparaison de profondeur, et met à jour le tampon de profondeur. Nous en avons déjà beaucoup parlé dans les chapitres précédents, notamment dans le chapitre sur les bases du rendu 3D et dans celui sur le rastériseur (avec l'élimination précoce des pixels cachés). Une autre fonction est le mélange ''alpha'', pour gérer la transparence, qu'on a là encore vu dans le chapitre sur les bases du rendu 3D. Là encore, les ROPs lisent, pour chaque fragment, le pixel correspondant dans le ''framebuffer'', font le mélange ''alpha'', et enregistrent le résultat dans le ''framebuffer''. Le mélange ''alpha'' est supporté sur tous les ROPs, depuis les premières cartes graphiques, et est encore supporté jusqu'à ce jour. Par contre, ce n'est pas le cas qui est du test ''alpha''. Ce dernier était pris en charge dans les ROPs jusqu'à DirectX 9, mais est maintenant émulé dans les ''pixel shaders'' depuis DirectX 10. Il en est de même pour les effets de brouillard. Ils impliquent à la fois du mélange ''alpha'' mais aussi la coordonnée de profondeur, ce qui en fait que leur implémentation dans les ROPs parait logique. Aussi, les premières cartes graphiques calculaient le brouillard dans les ROP, en fonction de la coordonnée de profondeur du fragment. De nos jours, il est calculé par les ''pixel shaders'' et les ROP n'incorporent plus de technique de brouillard spécialisée. Les ROPs ont d'autres fonctions, plus méconnues, qu'on n'a pas abordé dans les chapitres précédents. ===Le tampon de ''stencil''=== Le '''''stencil''''' est une fonctionnalité des API graphiques qui existe depuis très longtemps. Il sert pour générer des effets graphiques très variés, qu'il serait vain de lister ici. Il a notamment été utilisé pour calculer des ombres volumétriques (le moteur de DOOM 3 en faisait grand usage à la base), des réflexions simples, des ''shadowmaps'', et bien d'autres. Pour le résumer, on peut le voir comme une sorte de tampon de profondeur où la coordonnée z est remplacée par u octet dont le programmeur peut faire ce qu'il veut. L'idée est que chaque pixel/fragment se voit attribuer une valeur entière, généralement codée sur un octet, que les programmeurs peuvent faire varier à loisir. L'octet ajouté est appelé l''''octet de ''stencil'''''. Il a une certaine valeur, qui est calculée par la carte graphique, généralement par les ''shaders''. Il ne remplace pas la coordonnée de profondeur, mais s'ajoute à celle-ci. Les octets de ''stencil'' sont placés dans le tampon de profondeur. L'ensemble forme un tableau qui associe 32 bits à chaque" pixel : 24 bits pour une coordonnée z, 8 pour l'octet de ''stencil''. Lors du passage d'un fragment les ROPs, la carte graphique lit le pixel correspondant, dans le tampon de profondeur. Il récupère la coordonnée z, mais aussi l'octet de ''stencil''. Puis il compare l'octet de ''stencil'' avec celui du fragment traité. Si le test échoue, le fragment ne passe pas à l'étape de test de profondeur et est abandonné. S'il passe, le tampon de ''stencil'' est mis à jour. Par mis à jour, on veut dire que le ROP peut faire diverses manipulations dessus : l'incrémenter, le décrémenter, le mettre à 0, inverser ses bits, remplacer par l'octet de ''stencil'' du fragment, etc. Les opérations possibles sont bien plus nombreuses qu'avec le tampon de profondeur, qui se contente de remplacer la coordonnée z par celle du fragment. ===Les autres fonctions des ROPs=== Les ROPS implémentent aussi des techniques utilisées sur les ''blitters'' des anciennes cartes d'affichage 2D, comme l'application d''''opérations logiques''' sur chaque pixel enregistré dans le ''framebuffer''. Les opérations logiques en question peuvent prendre une à deux opérandes. Les opérandes sont soit un pixel lu dans le ''framebuffer'', soit un fragment envoyé au ROP. Les opérations logiques à un opérande peuvent inverser, mettre à 0 ou à 1 le pixel dans le ''framebuffer'', ou faire la même chose sur le fragment envoyé en opérande. Les opérations à deux opérandes lisent un pixel dans le framebuffer, et font un ET/OU/XOR avec le fragment opérande (un opérande peut être inversé). Elles sont utilisées pour faire du traitement d'image ou du rendu 2D, rarement pour du rendu 3D. Les ROPs gèrent aussi des '''masques d'écritures''', qui permettent de décider si un pixel doit être écrit ou non en mémoire. Il est possible d'inhiber certaines écritures dans le ''framebuffer'', le tampon de profondeur ou le tampon de stencil. Inhiber la mise à jour d'un pixel dans le tampon de profondeur est utile pour gérer la transparence. Si un pixel est transparent, même partiellement, il ne faut pas mettre à jour le tampon de profondeur, et cela peut être géré par ce système de masquage. Les masquages des couleurs permettent de ne modifier qu'une seule composante R/G/B au lieu de modifier les trois en même temps, pour faire certains effets visuels. ==L'architecture matérielle d'un ROP== Les ROP contiennent des circuits pour gérer la profondeur des fragments. Ils effectuent un test de profondeur, à savoir que les fragments correspondant à un même pixel sont comparés pour savoir lequel est devant l'autre. Ils contiennent aussi des circuits pour gérer la transparence des fragments. Le ROP gère aussi l'antialiasing, de concert avec l'unité de rastérisation. D'autres fonctionnalités annexes sont parfois implémentées dans les ROP. Par exemple, les vielles cartes graphiques implémentaient les effets de brouillards dans les ROPs. Le tout est suivi d'une unité qui enregistre le résultat final en mémoire, où masques et opérations logiques sont appliqués. Les différentes opérations du ROP doivent se faire dans un certain ordre. Par exemple, gérer la transparence demande que les calculs de profondeur se fassent globalement après ou pendant le mélange ''alpha''. Ou encore, les masques et opérations logiques se font à la toute fin du rendu. L'ordre des opérations est censé être le suivant : test ''alpha'', test du ''stencil'', test de profondeur, mélange ''alpha''. Du moins, la carte graphique doit donner l'impression que c'est le cas. Elle peut optimiser le tout en traitant le tampon de profondeur, de couleur et de ''stencil'' en même temps, mais donner les résultats adéquats. Un ROP est typiquement organisé comme illustré ci-dessous. Notons que les circuits de gestion de la profondeur et de la transparence sont séparés dans les schémas, mais il s'agit là d'une commodité qui ne reflète pas forcément l'implémentation matérielle. Et si ces deux circuits sont séparés, ils communiquent entre eux, notamment pour gérer la profondeur des fragments transparents. [[File:Render Output Pipeline-processor.png|centre|vignette|upright=2|Render Output Pipeline-processor]] Les ROPs récupèrent les fragments calculés par les pixels shaders et/ou les unités de texture, via un circuit d'interconnexion spécialisé. Chaque ROP est connecté à toutes les unités de ''shader'', même si la connexion n'est pas forcément directe. Toute unité de ''shader'' peut envoyer des pixels à n'importe quel ROP. Les circuits d'interconnexion sont généralement des réseaux d'interconnexion de type ''crossbar'', comme illustré ci-contre (le premier rectangle rouge). Le ROP effectue beaucoup de lectures et écritures en mémoire vidéo. Or, la bande passante mémoire est limitée, ce qui fait que le ROP est un goulot d'étranglement assez important pour le rendu 3D. Heureusement, de nombreuses optimisations permettent d'optimiser le tout. Elles agissent sur la lecture du tampon de profondeur, mais aussi sur le ''framebuffer''. ===Le ''fast clear'' du ''framebuffer''=== Une première optimisation porte sur le ''framebuffer''. Le ''framebuffer''est souvent réutilisé d'une image sur l'autre. Quand une image a été envoyée à l'écran, le ''framebuffer'' est remis à zéro pour accueillir une nouvelle image. Et ce avec ou sans ''double buffering''. La mise à zéro est censée se faire en remettant réellement le ''framebuffer'' à zéro, en écrivant des 0 pour chaque pixel du ''framebuffer''. Mais il y a moyen de s'en passer. Pour cela, l'idée est que le ''framebuffer'' est découpé en ''tiles'', des carrés de 4, 8, 16 pixels de côté. Les ''tiles'' ont généralement la même taille que les ''tiles'' utilisées pour la rastérisation, mais passons sur ce détail. L'idée est de mémoriser, pour chaque ''tile'', si elle est mise à 0 ou non. Il suffit de cela d'un seul bit par ''tile'', appelé le bit RESET. L'ensemble des bits RESET est mémorisé dans une petite mémoire SRAM, intégrée aux ROPs. Lorsqu'on souhaite remettre à zéro le ''framebuffer'', il suffit de mettre à 0 tous les bits RESET dans cette SRAM, pas besoin d’accéder à la mémoire vidéo. Avant toute lecture dans le ''framebuffer'', le ROP lit cette SRAM pour vérifier si la ''tile'' en question a été remise à 0. Si ce n'est pas le cas, il lit le pixel voulu depuis le ''framebuffer''. Mais si c'est le cas, alors le ROP ne fait pas la lecture et fournit un pixel à zéro à la place, qui est utilisé pour le mélange ''alpha'' ou autre. La moindre écriture dans une ''tile'' met le bit RESET à 0 : la ''tile'' entière est considérée comme non-remise à zéro, même si un seul pixel a été modifié dedans. Notons que l'usage d'une granularité par ''tile'' est un compromis. On peut ne peut pas utiliser un bit par pixel, car cela demanderait d'utiliser une SRAM énorme. De même, utiliser un seul bit pour tout le ''framebuffer'' ruinerait totalement l'optimisation : le ''framebuffer'' entier serait considéré comme non-RESET dès la première écriture d'un pixel dedans, on ne sauverait qu'un nombre trop limité d'accès mémoire. ===La z-compression=== La technique de '''z-compression''' compresse le tampon de profondeur. Plus précisément, elle découpe le tampon de profondeur en ''tiles'', en blocs carrés, qui sont compressés séparément les uns des autres. La taille des ''tiles'' est souvent la même que celle utilisée par le rastériseur pour la rastérisation grossière. Par exemple, la ''z-compression'' des cartes graphiques ATI radeon 9800, découpait le tampon de profondeur en ''tiles'' de 8 * 8 fragments, et les encodait avec un algorithme nommé DDPCM (''Differential differential pulse code modulation''). Précisons que cette compression ne change pas la taille occupée par le tampon de profondeur, mais seulement la quantité de données lue/écrite. La raison est que les ''tiles'' doivent avoir une place fixe en mémoire. Par exemple, si une ''tile'' non-compressée prend 64 octets, on trouvera une ''tile'' tous les 64 octets en mémoire vidéo, afin de simplifier les calculs d'adresse, afin que le ROP sache facilement où se trouve la ''tile'' à lire/écrire. Avec une vraie compression, les ''tiles'' se trouveraient à des endroits très variables d'une image à l'autre. Par contre, la z-compression réduit la quantité de données écrite dans le tampon de profondeur. Par exemple, au lieu d'écrire une ''tile'' non-compressée de 64 octets, on écrira une ''tile'' de seulement 6 octets, les 58 octets restants étant pas lus ou écrits. On obtient un gain en performance, pas en mémoire. [[File:AMD HyperZ.svg|centre|vignette|upright=2|AMD HyperZ]] Le format de compression ajoute un bit par ''tile'', qui indique si elle est compressée ou non. Le bit qui indique si la ''tile'' est compressée permet de laisser certaines ''tiles'' non-compressés, dans le cas où la compression ne permet pas de gagner de la place. La compression ajoute souvent un second bit, qui indique si la ''tile'' est à zéro ou non, sur le même modèle que pour le ''framebuffer''. Il accélère la remise à zéro du tampon de profondeur. Au lieu de réellement remettre tout le tampon de profondeur à 0, il suffit de réécrire un bit par ''tile''. Le gain en nombre d'accès mémoire peut se révéler assez impressionnant. Les deux bits en question peuvent être placés à deux endroits différents. La première solution serait d'utiliser une portion de la mémoire vidéo, mais cela demanderait de faire deux lectures par accès au tampon de profondeur. La vraie solution est d'utiliser une SRAM reliée aux ROPs, qui est assez grande pour mémoriser tout le tampon de profondeur, du moins avec deux bits par ''tile''. ===Le cache de profondeur=== Une optimisation complémentaire ajoute une ou plusieurs mémoires caches dans le ROP, dans le circuit de profondeur. Ce '''cache de profondeur''' stocke des portions du tampon de profondeur qui ont été lues ou écrite récemment. Comme cela, pas besoin de les recharger plusieurs fois : on charge un bloc une fois pour toutes, et on le conserve pour gérer les fragments qui suivent. Sur certaines cartes graphiques, les données dans le cache de profondeur sont stockées sous forme compressées dans le cache de profondeur, là encore pour augmenter la taille effective du cache. D'autres cartes graphiques ont un cache qui stocke des données décompressées dans le cache de profondeur. Tout est question de compromis entre accès rapide au cache et augmentation de la taille du cache. Il faut savoir que les autres unités de la carte graphique peuvent lire le tampon de profondeur, en théorie. Cela peut servir pour certaines techniques de rendu, comme pour le ''shadowmapping''. De ce fait, il arrive que le cache de profondeur contienne des données qui sont copiées dans d'autres caches, comme les caches des processeurs de shaders. Le cache de profondeur n'est pas gardé cohérent avec les autres caches du GPU, ce qui signifie que les écritures dans le cache de profondeur ne sont pas propagées dans les autres caches du GPU. Si on modifie des données dans ce cache, les autres caches qui ont une copie de ces données auront une version périmée de la donnée. C'est souvent un problème, sauf dans le cas du cache de profondeur, pour lequel ce n'est pas nécessaire. Cela évite d'implémenter des techniques de cohérence des caches couteuses en circuits et en performance, alors qu'elles n'auraient pas d'intérêt dans ce cas précis. ===Le ''z-fast pass''=== Le ''z-fast pass'' améliore la performance des '''prépasses z''', une technique utilisée par de nombreux moteurs de jeux vidéo. L'idée est que le moteur de jeu effectue plusieurs passes, chacune faisant un truc précis, la prépasse z étant l'une de ces passes. Lors d'une prépasse z, le moteur de jeu calcule la scène 3D, rastérise l'image, et remplit le tampon de profondeur uniquement. Il ne place pas de textures, ne calcule pas de pixels shaders, il se préoccupe uniquement des coordonnées de profondeur des pixels. Au final, le rendu ne donne que le tampon de profondeur, qui est utilisé par les passes suivantes. L'utilité est très variable, mais il y a deux raisons pour effectuer une prépasse z : la performance, mais aussi certains effets graphiques. Par exemple, les effets d'occlusion ambiante "''screen space''" utilisent le tampon de profondeur pour faire leur travail. Il en est de même pour les ''shadowmaps'', qui effectuent une prépasse z par ombre à afficher. Une autre utilisation est que cela permet d'utiliser élimination des pixels cachés très performante. On effectue une prépasse z pour calculer le tampon de profondeur final, qui est ensuite utilisé par les passes suivantes pour éliminer les pixels cachés. Ainsi, les pixels cachés ne sont pas texturés et pixel shadés, avec certitude. Toujours est-il qu'une prépasse z utilise les ROP "à moitié", dans le sens où seul le tampon de profondeur est utilisé, par la gestion des couleurs. Mais il se trouve que les circuits qui servent pour le mélange ''alpha'' peuvent être réutilisés pour faire les comparaisons de profondeur ! Le résultat est que les ROP peuvent fonctionner à double vitesse lors d'une prépasse z ! Cela demande cependant de concevoir les circuits du ROP pour en profiter. L'optimisation est parfois appelée le '''''z-fast pass'''''. Tous les GPU depuis la Geforce FX en sont capables. Il y a cependant quelques contraintes. Premièrement, le ROP doit être configuré de manière à n’accéder qu'au tampon de profondeur, ils ne doivent pas dessiner dans le ''framebuffer''. Le mélange '''alpha'' doit être désactivé, de même que l'alpha-test. D'autres contraintes supplémentaires sont parfois présentes, surtout sur les vieux GPUs. Par exemple, l'antialiasing doit être désactivé lors de la prépasse z. Et mine de rien, cela ne marche que pour les prépasses z pures. Par exemple, certaines techniques de rendu différé augmentent la prépasse z pour que celle-ci ne calcule pas que le tampon de profondeur, mais aussi d'autres informations comme les normales : elles ne profitent pas de cette optimisation. ==Pourquoi ne pas émuler les ROPs dans les ''pixel shader'' ?== Les ROPs effectuent plusieurs opérations basiques, mais les deux plus importantes sont la gestion du tampon de profondeur et de la transparence. Par transparence, on veut parler du mélange ''alpha''. Pour la gestion du tampon de profondeur, on veut parler du ''z-test'', qui compare la profondeur de deux pixels/fragments. Il s'agit d'opérations simples, qu'un processeur de shader peut faire sans problèmes. Par exemple, le ''z-test'' demande de faire plusieurs étapes : * calculer l'adresse du pixel dans le tampon de profondeur ; * lire le pixel dans le tampon de profondeur ; * Faire la comparaison entre profondeurs ; * Si le résultat de la comparaison est okay : ** écrire la nouvelle valeur z dans le tampon de profondeur, et écrire le nouveau pixel dedans. Le mélange ''alpha'' demande lui de : * calculer l'adresse du pixel dans le ''framebuffer'' ; * lire le pixel dans le ''framebuffer'' ; * faire des additions et multiplications pour le mélange ''alpha'' : * écrire le nouveau pixel dans le ''framebuffer''. Pour résumer il faut pouvoir faire : calcul d'adresse, lecture, écriture, addition, multiplication et comparaisons. Et toutes ces opérations sont supportées nativement par les processeurs de shaders, ce sont des instructions communes. Il est donc possible d'émuler les ROPs dans les pixels shaders. En pratique, c'est assez rare, et il y a une bonne explication à cela. ===Les GPU de type ''sort-last'' doivent "trier les pixels"=== Émuler les ROPs dans un ''pixel shader'' est trivial, comme on vient de le voir. Sauf que cela ne marche que si le GPU fait le rendu un pixel à la fois. Le tampon de profondeur est conçu pour traiter un pixel à la fois, idem pour le mélange ''alpha''. Mais si on ne traite pas l'image pixel par pixel, alors les deux algorithmes dysfonctionnent. Donc, tout va bien s'il n'y a qu'un seul processeur de ''pixel shader'', et que celui-ci est conçu pour ne traiter qu'un pixel à la fois, qu'une seule instance de ''shader''. Mais cela ne marche pas sur les GPU modernes, qui ont non seulement près d'une centaine de processeurs de shaders, chacun étant conçu pour traiter une centaine de fragments/pixels en même temps ! Pour donner un exemple, imaginons la situation illustrée ci-dessous. Supposons que l'on ait assez de processeurs de shaders pour traiter plusieurs triangles en même temps. Par malchance, les processeurs rendent en même temps deux triangles opaques qui se recouvrent à l'écran. Là où ils se recouvrent, les deux triangles vont générer deux fragments par pixel, et un seul sera le bon. Pas de chance, les deux fragments sont rendus en parallèle dans deux processeurs séparés. Les deux processeurs lisent la même donnée dans le tampon de profondeur et les deux fragments passent le ''z-test'', car ils n'ont aucun moyen de savoir la coordonnée z en cours de traitement dans l'autre processeur. Les deux processeurs vont alors écrire leur résultat en mémoire et c'est premier arrivé, premier servi. Le résultat n'est pas forcément celui attendu : le pixel le plus proche peut être écrit avant le plus lointain, ou inversement. [[File:Situation où faire le z-test dans les pixel shaders dysfonctionne.png|centre|vignette|upright=2|Situation où faire le z-test dans les pixel shaders dysfonctionne]] Pour obtenir un bon rendu, le GPU doit forcer le z-test à se faire fragment par fragment, du moins quand on regarde un pixel individuel. Il reste possible de traiter des pixels différents en parallèle, mais pas deux fragments d'un même pixel. En utilisant des processeurs de shaders qui travaillent en parallèle, cette contrainte est parfois brisée et le rendu donne des résultats incorrects. Le tampon de profondeur n'est pas conçu pour être parallélisé, idem pour le mélange ''alpha''. Il faut donc une sorte de point de synchronisation dans le pipeline pour éviter tout problème. Et c'est à ça que servent les ROPs. Une explication alternative est la suivante. Les fragments doivent être rendus dans l'ordre de soumission. C'est à dire que des pixels qui sortent du rastériseur dans un certain ordre doivent être enregistré en mémoire dans ce même ordre. Et les pixels ne quittent pas le rastériseur dans le désordre non plus : des triangles arrivant au rastériseur dans un certain ordre seront traité dans ce même ordre. Le problème est que les GPU étant massivement parallèle, les triangles et pixels seront traités dans le désordre, avant que leurs résultats soient remis en ordre. Pour les triangles/sommets, la remise en ordre se fait au niveau de l'assemblage de primitives, juste avant le rastériseur. Pour les fragments, la remise en ordre se fait dans les ROPs, en sortie des ''shaders'', à la fin du pipeline. Du moins, sur les GPU de type ''sort-last''. Les ROPs ne font donc pas que faire le ''z-test'' et le mélange ''alpha'', ils ont des circuits de remise en ordre pour ça, qui sont de plus fortement mélés au tampon de profondeur et au ''framebuffer''. ===Le traitement parallèle des fragments sur les GPU ''sort-last''=== Plus haut, j'ai dit qu'il doit être impossible de traiter en même temps deux fragments d'un même pixel. Mais il reste possible de traiter des pixels différents en parallèle, mais pas deux fragments d'un même pixel. Reste à faire en sorte de ne pas envoyer deux fragments d'un même pixel dans les processeurs de shaders. Une solution pour cela serait de mémoriser, pour chaque pixel, si un ''pixel shader'' est en train de le traiter. Il suffit de mémoriser un bit par pixel pour cela, dans une table d'utilisation, concrètement une petite mémoire. Elle serait mise à jour par les processeurs de shaders, et consultée par le rastériseur. Quand le rastériseur génère un fragment, il consulte cette table, pour vérifier s'il y a conflit avec les fragments en cours de traitement. Il attend si c'est le cas, le pixel shader finira par finir de traiter le pixel au bout d'un moment. Mais l'inconvénient de cette solution est qu'elle a besoin d'une mémoire partagée par tous les processeurs de shaders, qui est difficile à concevoir sans faire des concessions en termes de performances. Une autre solution serait de mémoriser tous les pixels en cours de traitement. Quand le rastériseur génère un fragment, il mémorise les coordonnées x,y de ce fragment à l'écran, dans une '''table des pixels occupés'''. Dès qu'un pixel shader se termine, la table des pixels occupés est mise à jour. Le rastériseur consulte cette table quand il génère un fragment, afin de détecter les conflits. S'il y a conflit, le rastériseur attend que le fragment conflictuel, en cours de traitement dans le pixel shader, soit traité. L’inconvénient de la solution précédente est que la table des pixels occupés est techniquement une mémoire associative, une sorte de mémoire cache, qui est plus complexe qu'une simple RAM. Il est très difficile de créer une mémoire de ce genre qui soit capable de mémoriser plusieurs dizaines ou centaine de milliers de pixels, pour gérer une centaine de processeurs de shaders. Par contre, elle fonctionne pas trop mal pour un petit nombre de processeurs de shaders, qui fonctionnent à basse fréquence. Cela explique que les GPU pour PC ont des ROPs séparés des processeurs de ''shaders'' : ces GPU ont beaucoup trop de processeurs de shaders. Par contre, quelques cartes graphiques destinées les smartphones et autres appareils mobiles émulent les ROPs dans les ''pixel shaders''. Mais il y a une bonne raison à cela : non seulement, ils n'ont que très peu de processeurs de shader, mais ce sont aussi des GPU en rendu à tuiles. L'avantage est qu'ils rendent une tile à la fois, ce qui fait qu'il y a besoin de tester les conflits entre fragments à l'intérieur d'une tile, pas pour l'écran complet. Et cela simplifie grandement les circuits de test, notamment la table des pixels occupés, qui est bien plus petite. {{NavChapitre | book=Les cartes graphiques | prev=Les unités de texture | prevText=Les unités de texture | next=Les écritures en VRAM hors ROPs | nextText=Les écritures en VRAM hors ROPs }}{{autocat}} hbkljl3vvd579l6ozktqs1inrcmu38w 763939 763938 2026-04-18T13:18:09Z Mewtow 31375 /* Les autres fonctions des ROPs */ 763939 wikitext text/x-wiki Pour rappel, les étapes précédentes du pipeline graphiques manipulaient non pas des pixels, mais des fragments. Pour rappel, la distinction entre fragment et pixel est pertinente quand plusieurs objets sont l'un derrière l'autre. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. La couleur finale dépend de la couleur de tous ces points d'intersection. Intuitivement, l'objet le plus proche est censé cacher les autres et c'est donc lui qui décide de la couleur du pixel, mais cela demande de déterminer quel est l'objet le plus proche. De plus, certains objets sont transparents et la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Tout demande de calculer un pseudo-pixel pour chaque point d'intersection et de combiner leurs couleurs pour obtenir le résultat final. Les pseudo-pixels en question sont des '''fragments'''. Chaque fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont donc combinés pour obtenir la couleur finale de ce pixel. Pour résumer, la profondeur des fragments doit être gérée, de même que la transparence, etc. Et c'est justement le rôle de l'étage du pipeline que nous allons voir maintenant. Ces opérations sont réalisées dans un circuit qu'on nomme le '''Raster Operations Pipeline''' (ROP), aussi appelé ''Render Output Target'', situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. ==Les fonctions des ROP== Les ROP incorporent plusieurs fonctionnalités qui sont assez diverses. Leur seul lien est qu'il est préférable de les implémenter en matériel plutôt qu'en logiciel, et en dehors des unités de textures. Il s'agit de fonctionnalités assez simples, basiques, mais nécessaires au fonctionnement de tout rendu 3D. Elles ont aussi pour particularité de beaucoup accéder à la mémoire vidéo. C'est la raison pour laquelle le ROP est situé en fin de pipeline, proche de la mémoire vidéo. Voyons quelles sont ces fonctionnalités. ===Les effets classiques du ROP=== Sa fonction la plus importante est l'élimination des pixels cachés, grâce au tampon de profondeur. Pour chaque fragment, il lit le pixel correspondant dans le tampon de profondeur, fait la comparaison de profondeur, et met à jour le tampon de profondeur. Nous en avons déjà beaucoup parlé dans les chapitres précédents, notamment dans le chapitre sur les bases du rendu 3D et dans celui sur le rastériseur (avec l'élimination précoce des pixels cachés). Une autre fonction est le mélange ''alpha'', pour gérer la transparence, qu'on a là encore vu dans le chapitre sur les bases du rendu 3D. Là encore, les ROPs lisent, pour chaque fragment, le pixel correspondant dans le ''framebuffer'', font le mélange ''alpha'', et enregistrent le résultat dans le ''framebuffer''. Le mélange ''alpha'' est supporté sur tous les ROPs, depuis les premières cartes graphiques, et est encore supporté jusqu'à ce jour. Par contre, ce n'est pas le cas qui est du test ''alpha''. Ce dernier était pris en charge dans les ROPs jusqu'à DirectX 9, mais est maintenant émulé dans les ''pixel shaders'' depuis DirectX 10. Il en est de même pour les effets de brouillard. Ils impliquent à la fois du mélange ''alpha'' mais aussi la coordonnée de profondeur, ce qui en fait que leur implémentation dans les ROPs parait logique. Aussi, les premières cartes graphiques calculaient le brouillard dans les ROP, en fonction de la coordonnée de profondeur du fragment. De nos jours, il est calculé par les ''pixel shaders'' et les ROP n'incorporent plus de technique de brouillard spécialisée. Les ROPs ont d'autres fonctions, plus méconnues, qu'on n'a pas abordé dans les chapitres précédents. ===Le tampon de ''stencil''=== Le '''''stencil''''' est une fonctionnalité des API graphiques qui existe depuis très longtemps. Il sert pour générer des effets graphiques très variés, qu'il serait vain de lister ici. Il a notamment été utilisé pour calculer des ombres volumétriques (le moteur de DOOM 3 en faisait grand usage à la base), des réflexions simples, des ''shadowmaps'', et bien d'autres. Pour le résumer, on peut le voir comme une sorte de tampon de profondeur où la coordonnée z est remplacée par u octet dont le programmeur peut faire ce qu'il veut. L'idée est que chaque pixel/fragment se voit attribuer une valeur entière, généralement codée sur un octet, que les programmeurs peuvent faire varier à loisir. L'octet ajouté est appelé l''''octet de ''stencil'''''. Il a une certaine valeur, qui est calculée par la carte graphique, généralement par les ''shaders''. Il ne remplace pas la coordonnée de profondeur, mais s'ajoute à celle-ci. Les octets de ''stencil'' sont placés dans le tampon de profondeur. L'ensemble forme un tableau qui associe 32 bits à chaque" pixel : 24 bits pour une coordonnée z, 8 pour l'octet de ''stencil''. Lors du passage d'un fragment les ROPs, la carte graphique lit le pixel correspondant, dans le tampon de profondeur. Il récupère la coordonnée z, mais aussi l'octet de ''stencil''. Puis il compare l'octet de ''stencil'' avec celui du fragment traité. Si le test échoue, le fragment ne passe pas à l'étape de test de profondeur et est abandonné. S'il passe, le tampon de ''stencil'' est mis à jour. Par mis à jour, on veut dire que le ROP peut faire diverses manipulations dessus : l'incrémenter, le décrémenter, le mettre à 0, inverser ses bits, remplacer par l'octet de ''stencil'' du fragment, etc. Les opérations possibles sont bien plus nombreuses qu'avec le tampon de profondeur, qui se contente de remplacer la coordonnée z par celle du fragment. ===Les fonctions héritées des ''blitters'' 2D=== Les ROPS implémentent aussi des techniques utilisées sur les ''blitters'' des anciennes cartes d'affichage 2D, comme l'application d''''opérations logiques''' sur chaque pixel enregistré dans le ''framebuffer''. Les opérations logiques en question peuvent prendre une à deux opérandes. Les opérandes sont soit un pixel lu dans le ''framebuffer'', soit un fragment envoyé au ROP. Les opérations logiques à un opérande peuvent inverser, mettre à 0 ou à 1 le pixel dans le ''framebuffer'', ou faire la même chose sur le fragment envoyé en opérande. Les opérations à deux opérandes lisent un pixel dans le framebuffer, et font un ET/OU/XOR avec le fragment opérande (un opérande peut être inversé). Elles sont utilisées pour faire du traitement d'image ou du rendu 2D, rarement pour du rendu 3D. Les ROPs gèrent aussi des '''masques d'écritures''', qui permettent de décider si un pixel doit être écrit ou non en mémoire. Il est possible d'inhiber certaines écritures dans le ''framebuffer'', le tampon de profondeur ou le tampon de stencil. Inhiber la mise à jour d'un pixel dans le tampon de profondeur est utile pour gérer la transparence. Si un pixel est transparent, même partiellement, il ne faut pas mettre à jour le tampon de profondeur, et cela peut être géré par ce système de masquage. Les masquages des couleurs permettent de ne modifier qu'une seule composante R/G/B au lieu de modifier les trois en même temps, pour faire certains effets visuels. ==L'architecture matérielle d'un ROP== Les ROP contiennent des circuits pour gérer la profondeur des fragments. Ils effectuent un test de profondeur, à savoir que les fragments correspondant à un même pixel sont comparés pour savoir lequel est devant l'autre. Ils contiennent aussi des circuits pour gérer la transparence des fragments. Le ROP gère aussi l'antialiasing, de concert avec l'unité de rastérisation. D'autres fonctionnalités annexes sont parfois implémentées dans les ROP. Par exemple, les vielles cartes graphiques implémentaient les effets de brouillards dans les ROPs. Le tout est suivi d'une unité qui enregistre le résultat final en mémoire, où masques et opérations logiques sont appliqués. Les différentes opérations du ROP doivent se faire dans un certain ordre. Par exemple, gérer la transparence demande que les calculs de profondeur se fassent globalement après ou pendant le mélange ''alpha''. Ou encore, les masques et opérations logiques se font à la toute fin du rendu. L'ordre des opérations est censé être le suivant : test ''alpha'', test du ''stencil'', test de profondeur, mélange ''alpha''. Du moins, la carte graphique doit donner l'impression que c'est le cas. Elle peut optimiser le tout en traitant le tampon de profondeur, de couleur et de ''stencil'' en même temps, mais donner les résultats adéquats. Un ROP est typiquement organisé comme illustré ci-dessous. Notons que les circuits de gestion de la profondeur et de la transparence sont séparés dans les schémas, mais il s'agit là d'une commodité qui ne reflète pas forcément l'implémentation matérielle. Et si ces deux circuits sont séparés, ils communiquent entre eux, notamment pour gérer la profondeur des fragments transparents. [[File:Render Output Pipeline-processor.png|centre|vignette|upright=2|Render Output Pipeline-processor]] Les ROPs récupèrent les fragments calculés par les pixels shaders et/ou les unités de texture, via un circuit d'interconnexion spécialisé. Chaque ROP est connecté à toutes les unités de ''shader'', même si la connexion n'est pas forcément directe. Toute unité de ''shader'' peut envoyer des pixels à n'importe quel ROP. Les circuits d'interconnexion sont généralement des réseaux d'interconnexion de type ''crossbar'', comme illustré ci-contre (le premier rectangle rouge). Le ROP effectue beaucoup de lectures et écritures en mémoire vidéo. Or, la bande passante mémoire est limitée, ce qui fait que le ROP est un goulot d'étranglement assez important pour le rendu 3D. Heureusement, de nombreuses optimisations permettent d'optimiser le tout. Elles agissent sur la lecture du tampon de profondeur, mais aussi sur le ''framebuffer''. ===Le ''fast clear'' du ''framebuffer''=== Une première optimisation porte sur le ''framebuffer''. Le ''framebuffer''est souvent réutilisé d'une image sur l'autre. Quand une image a été envoyée à l'écran, le ''framebuffer'' est remis à zéro pour accueillir une nouvelle image. Et ce avec ou sans ''double buffering''. La mise à zéro est censée se faire en remettant réellement le ''framebuffer'' à zéro, en écrivant des 0 pour chaque pixel du ''framebuffer''. Mais il y a moyen de s'en passer. Pour cela, l'idée est que le ''framebuffer'' est découpé en ''tiles'', des carrés de 4, 8, 16 pixels de côté. Les ''tiles'' ont généralement la même taille que les ''tiles'' utilisées pour la rastérisation, mais passons sur ce détail. L'idée est de mémoriser, pour chaque ''tile'', si elle est mise à 0 ou non. Il suffit de cela d'un seul bit par ''tile'', appelé le bit RESET. L'ensemble des bits RESET est mémorisé dans une petite mémoire SRAM, intégrée aux ROPs. Lorsqu'on souhaite remettre à zéro le ''framebuffer'', il suffit de mettre à 0 tous les bits RESET dans cette SRAM, pas besoin d’accéder à la mémoire vidéo. Avant toute lecture dans le ''framebuffer'', le ROP lit cette SRAM pour vérifier si la ''tile'' en question a été remise à 0. Si ce n'est pas le cas, il lit le pixel voulu depuis le ''framebuffer''. Mais si c'est le cas, alors le ROP ne fait pas la lecture et fournit un pixel à zéro à la place, qui est utilisé pour le mélange ''alpha'' ou autre. La moindre écriture dans une ''tile'' met le bit RESET à 0 : la ''tile'' entière est considérée comme non-remise à zéro, même si un seul pixel a été modifié dedans. Notons que l'usage d'une granularité par ''tile'' est un compromis. On peut ne peut pas utiliser un bit par pixel, car cela demanderait d'utiliser une SRAM énorme. De même, utiliser un seul bit pour tout le ''framebuffer'' ruinerait totalement l'optimisation : le ''framebuffer'' entier serait considéré comme non-RESET dès la première écriture d'un pixel dedans, on ne sauverait qu'un nombre trop limité d'accès mémoire. ===La z-compression=== La technique de '''z-compression''' compresse le tampon de profondeur. Plus précisément, elle découpe le tampon de profondeur en ''tiles'', en blocs carrés, qui sont compressés séparément les uns des autres. La taille des ''tiles'' est souvent la même que celle utilisée par le rastériseur pour la rastérisation grossière. Par exemple, la ''z-compression'' des cartes graphiques ATI radeon 9800, découpait le tampon de profondeur en ''tiles'' de 8 * 8 fragments, et les encodait avec un algorithme nommé DDPCM (''Differential differential pulse code modulation''). Précisons que cette compression ne change pas la taille occupée par le tampon de profondeur, mais seulement la quantité de données lue/écrite. La raison est que les ''tiles'' doivent avoir une place fixe en mémoire. Par exemple, si une ''tile'' non-compressée prend 64 octets, on trouvera une ''tile'' tous les 64 octets en mémoire vidéo, afin de simplifier les calculs d'adresse, afin que le ROP sache facilement où se trouve la ''tile'' à lire/écrire. Avec une vraie compression, les ''tiles'' se trouveraient à des endroits très variables d'une image à l'autre. Par contre, la z-compression réduit la quantité de données écrite dans le tampon de profondeur. Par exemple, au lieu d'écrire une ''tile'' non-compressée de 64 octets, on écrira une ''tile'' de seulement 6 octets, les 58 octets restants étant pas lus ou écrits. On obtient un gain en performance, pas en mémoire. [[File:AMD HyperZ.svg|centre|vignette|upright=2|AMD HyperZ]] Le format de compression ajoute un bit par ''tile'', qui indique si elle est compressée ou non. Le bit qui indique si la ''tile'' est compressée permet de laisser certaines ''tiles'' non-compressés, dans le cas où la compression ne permet pas de gagner de la place. La compression ajoute souvent un second bit, qui indique si la ''tile'' est à zéro ou non, sur le même modèle que pour le ''framebuffer''. Il accélère la remise à zéro du tampon de profondeur. Au lieu de réellement remettre tout le tampon de profondeur à 0, il suffit de réécrire un bit par ''tile''. Le gain en nombre d'accès mémoire peut se révéler assez impressionnant. Les deux bits en question peuvent être placés à deux endroits différents. La première solution serait d'utiliser une portion de la mémoire vidéo, mais cela demanderait de faire deux lectures par accès au tampon de profondeur. La vraie solution est d'utiliser une SRAM reliée aux ROPs, qui est assez grande pour mémoriser tout le tampon de profondeur, du moins avec deux bits par ''tile''. ===Le cache de profondeur=== Une optimisation complémentaire ajoute une ou plusieurs mémoires caches dans le ROP, dans le circuit de profondeur. Ce '''cache de profondeur''' stocke des portions du tampon de profondeur qui ont été lues ou écrite récemment. Comme cela, pas besoin de les recharger plusieurs fois : on charge un bloc une fois pour toutes, et on le conserve pour gérer les fragments qui suivent. Sur certaines cartes graphiques, les données dans le cache de profondeur sont stockées sous forme compressées dans le cache de profondeur, là encore pour augmenter la taille effective du cache. D'autres cartes graphiques ont un cache qui stocke des données décompressées dans le cache de profondeur. Tout est question de compromis entre accès rapide au cache et augmentation de la taille du cache. Il faut savoir que les autres unités de la carte graphique peuvent lire le tampon de profondeur, en théorie. Cela peut servir pour certaines techniques de rendu, comme pour le ''shadowmapping''. De ce fait, il arrive que le cache de profondeur contienne des données qui sont copiées dans d'autres caches, comme les caches des processeurs de shaders. Le cache de profondeur n'est pas gardé cohérent avec les autres caches du GPU, ce qui signifie que les écritures dans le cache de profondeur ne sont pas propagées dans les autres caches du GPU. Si on modifie des données dans ce cache, les autres caches qui ont une copie de ces données auront une version périmée de la donnée. C'est souvent un problème, sauf dans le cas du cache de profondeur, pour lequel ce n'est pas nécessaire. Cela évite d'implémenter des techniques de cohérence des caches couteuses en circuits et en performance, alors qu'elles n'auraient pas d'intérêt dans ce cas précis. ===Le ''z-fast pass''=== Le ''z-fast pass'' améliore la performance des '''prépasses z''', une technique utilisée par de nombreux moteurs de jeux vidéo. L'idée est que le moteur de jeu effectue plusieurs passes, chacune faisant un truc précis, la prépasse z étant l'une de ces passes. Lors d'une prépasse z, le moteur de jeu calcule la scène 3D, rastérise l'image, et remplit le tampon de profondeur uniquement. Il ne place pas de textures, ne calcule pas de pixels shaders, il se préoccupe uniquement des coordonnées de profondeur des pixels. Au final, le rendu ne donne que le tampon de profondeur, qui est utilisé par les passes suivantes. L'utilité est très variable, mais il y a deux raisons pour effectuer une prépasse z : la performance, mais aussi certains effets graphiques. Par exemple, les effets d'occlusion ambiante "''screen space''" utilisent le tampon de profondeur pour faire leur travail. Il en est de même pour les ''shadowmaps'', qui effectuent une prépasse z par ombre à afficher. Une autre utilisation est que cela permet d'utiliser élimination des pixels cachés très performante. On effectue une prépasse z pour calculer le tampon de profondeur final, qui est ensuite utilisé par les passes suivantes pour éliminer les pixels cachés. Ainsi, les pixels cachés ne sont pas texturés et pixel shadés, avec certitude. Toujours est-il qu'une prépasse z utilise les ROP "à moitié", dans le sens où seul le tampon de profondeur est utilisé, par la gestion des couleurs. Mais il se trouve que les circuits qui servent pour le mélange ''alpha'' peuvent être réutilisés pour faire les comparaisons de profondeur ! Le résultat est que les ROP peuvent fonctionner à double vitesse lors d'une prépasse z ! Cela demande cependant de concevoir les circuits du ROP pour en profiter. L'optimisation est parfois appelée le '''''z-fast pass'''''. Tous les GPU depuis la Geforce FX en sont capables. Il y a cependant quelques contraintes. Premièrement, le ROP doit être configuré de manière à n’accéder qu'au tampon de profondeur, ils ne doivent pas dessiner dans le ''framebuffer''. Le mélange '''alpha'' doit être désactivé, de même que l'alpha-test. D'autres contraintes supplémentaires sont parfois présentes, surtout sur les vieux GPUs. Par exemple, l'antialiasing doit être désactivé lors de la prépasse z. Et mine de rien, cela ne marche que pour les prépasses z pures. Par exemple, certaines techniques de rendu différé augmentent la prépasse z pour que celle-ci ne calcule pas que le tampon de profondeur, mais aussi d'autres informations comme les normales : elles ne profitent pas de cette optimisation. ==Pourquoi ne pas émuler les ROPs dans les ''pixel shader'' ?== Les ROPs effectuent plusieurs opérations basiques, mais les deux plus importantes sont la gestion du tampon de profondeur et de la transparence. Par transparence, on veut parler du mélange ''alpha''. Pour la gestion du tampon de profondeur, on veut parler du ''z-test'', qui compare la profondeur de deux pixels/fragments. Il s'agit d'opérations simples, qu'un processeur de shader peut faire sans problèmes. Par exemple, le ''z-test'' demande de faire plusieurs étapes : * calculer l'adresse du pixel dans le tampon de profondeur ; * lire le pixel dans le tampon de profondeur ; * Faire la comparaison entre profondeurs ; * Si le résultat de la comparaison est okay : ** écrire la nouvelle valeur z dans le tampon de profondeur, et écrire le nouveau pixel dedans. Le mélange ''alpha'' demande lui de : * calculer l'adresse du pixel dans le ''framebuffer'' ; * lire le pixel dans le ''framebuffer'' ; * faire des additions et multiplications pour le mélange ''alpha'' : * écrire le nouveau pixel dans le ''framebuffer''. Pour résumer il faut pouvoir faire : calcul d'adresse, lecture, écriture, addition, multiplication et comparaisons. Et toutes ces opérations sont supportées nativement par les processeurs de shaders, ce sont des instructions communes. Il est donc possible d'émuler les ROPs dans les pixels shaders. En pratique, c'est assez rare, et il y a une bonne explication à cela. ===Les GPU de type ''sort-last'' doivent "trier les pixels"=== Émuler les ROPs dans un ''pixel shader'' est trivial, comme on vient de le voir. Sauf que cela ne marche que si le GPU fait le rendu un pixel à la fois. Le tampon de profondeur est conçu pour traiter un pixel à la fois, idem pour le mélange ''alpha''. Mais si on ne traite pas l'image pixel par pixel, alors les deux algorithmes dysfonctionnent. Donc, tout va bien s'il n'y a qu'un seul processeur de ''pixel shader'', et que celui-ci est conçu pour ne traiter qu'un pixel à la fois, qu'une seule instance de ''shader''. Mais cela ne marche pas sur les GPU modernes, qui ont non seulement près d'une centaine de processeurs de shaders, chacun étant conçu pour traiter une centaine de fragments/pixels en même temps ! Pour donner un exemple, imaginons la situation illustrée ci-dessous. Supposons que l'on ait assez de processeurs de shaders pour traiter plusieurs triangles en même temps. Par malchance, les processeurs rendent en même temps deux triangles opaques qui se recouvrent à l'écran. Là où ils se recouvrent, les deux triangles vont générer deux fragments par pixel, et un seul sera le bon. Pas de chance, les deux fragments sont rendus en parallèle dans deux processeurs séparés. Les deux processeurs lisent la même donnée dans le tampon de profondeur et les deux fragments passent le ''z-test'', car ils n'ont aucun moyen de savoir la coordonnée z en cours de traitement dans l'autre processeur. Les deux processeurs vont alors écrire leur résultat en mémoire et c'est premier arrivé, premier servi. Le résultat n'est pas forcément celui attendu : le pixel le plus proche peut être écrit avant le plus lointain, ou inversement. [[File:Situation où faire le z-test dans les pixel shaders dysfonctionne.png|centre|vignette|upright=2|Situation où faire le z-test dans les pixel shaders dysfonctionne]] Pour obtenir un bon rendu, le GPU doit forcer le z-test à se faire fragment par fragment, du moins quand on regarde un pixel individuel. Il reste possible de traiter des pixels différents en parallèle, mais pas deux fragments d'un même pixel. En utilisant des processeurs de shaders qui travaillent en parallèle, cette contrainte est parfois brisée et le rendu donne des résultats incorrects. Le tampon de profondeur n'est pas conçu pour être parallélisé, idem pour le mélange ''alpha''. Il faut donc une sorte de point de synchronisation dans le pipeline pour éviter tout problème. Et c'est à ça que servent les ROPs. Une explication alternative est la suivante. Les fragments doivent être rendus dans l'ordre de soumission. C'est à dire que des pixels qui sortent du rastériseur dans un certain ordre doivent être enregistré en mémoire dans ce même ordre. Et les pixels ne quittent pas le rastériseur dans le désordre non plus : des triangles arrivant au rastériseur dans un certain ordre seront traité dans ce même ordre. Le problème est que les GPU étant massivement parallèle, les triangles et pixels seront traités dans le désordre, avant que leurs résultats soient remis en ordre. Pour les triangles/sommets, la remise en ordre se fait au niveau de l'assemblage de primitives, juste avant le rastériseur. Pour les fragments, la remise en ordre se fait dans les ROPs, en sortie des ''shaders'', à la fin du pipeline. Du moins, sur les GPU de type ''sort-last''. Les ROPs ne font donc pas que faire le ''z-test'' et le mélange ''alpha'', ils ont des circuits de remise en ordre pour ça, qui sont de plus fortement mélés au tampon de profondeur et au ''framebuffer''. ===Le traitement parallèle des fragments sur les GPU ''sort-last''=== Plus haut, j'ai dit qu'il doit être impossible de traiter en même temps deux fragments d'un même pixel. Mais il reste possible de traiter des pixels différents en parallèle, mais pas deux fragments d'un même pixel. Reste à faire en sorte de ne pas envoyer deux fragments d'un même pixel dans les processeurs de shaders. Une solution pour cela serait de mémoriser, pour chaque pixel, si un ''pixel shader'' est en train de le traiter. Il suffit de mémoriser un bit par pixel pour cela, dans une table d'utilisation, concrètement une petite mémoire. Elle serait mise à jour par les processeurs de shaders, et consultée par le rastériseur. Quand le rastériseur génère un fragment, il consulte cette table, pour vérifier s'il y a conflit avec les fragments en cours de traitement. Il attend si c'est le cas, le pixel shader finira par finir de traiter le pixel au bout d'un moment. Mais l'inconvénient de cette solution est qu'elle a besoin d'une mémoire partagée par tous les processeurs de shaders, qui est difficile à concevoir sans faire des concessions en termes de performances. Une autre solution serait de mémoriser tous les pixels en cours de traitement. Quand le rastériseur génère un fragment, il mémorise les coordonnées x,y de ce fragment à l'écran, dans une '''table des pixels occupés'''. Dès qu'un pixel shader se termine, la table des pixels occupés est mise à jour. Le rastériseur consulte cette table quand il génère un fragment, afin de détecter les conflits. S'il y a conflit, le rastériseur attend que le fragment conflictuel, en cours de traitement dans le pixel shader, soit traité. L’inconvénient de la solution précédente est que la table des pixels occupés est techniquement une mémoire associative, une sorte de mémoire cache, qui est plus complexe qu'une simple RAM. Il est très difficile de créer une mémoire de ce genre qui soit capable de mémoriser plusieurs dizaines ou centaine de milliers de pixels, pour gérer une centaine de processeurs de shaders. Par contre, elle fonctionne pas trop mal pour un petit nombre de processeurs de shaders, qui fonctionnent à basse fréquence. Cela explique que les GPU pour PC ont des ROPs séparés des processeurs de ''shaders'' : ces GPU ont beaucoup trop de processeurs de shaders. Par contre, quelques cartes graphiques destinées les smartphones et autres appareils mobiles émulent les ROPs dans les ''pixel shaders''. Mais il y a une bonne raison à cela : non seulement, ils n'ont que très peu de processeurs de shader, mais ce sont aussi des GPU en rendu à tuiles. L'avantage est qu'ils rendent une tile à la fois, ce qui fait qu'il y a besoin de tester les conflits entre fragments à l'intérieur d'une tile, pas pour l'écran complet. Et cela simplifie grandement les circuits de test, notamment la table des pixels occupés, qui est bien plus petite. {{NavChapitre | book=Les cartes graphiques | prev=Les unités de texture | prevText=Les unités de texture | next=Les écritures en VRAM hors ROPs | nextText=Les écritures en VRAM hors ROPs }}{{autocat}} fzzdwe8i6enb20a0i0k7bil4wjcm818 763940 763939 2026-04-18T13:18:42Z Mewtow 31375 /* Les effets classiques du ROP */ 763940 wikitext text/x-wiki Pour rappel, les étapes précédentes du pipeline graphiques manipulaient non pas des pixels, mais des fragments. Pour rappel, la distinction entre fragment et pixel est pertinente quand plusieurs objets sont l'un derrière l'autre. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. La couleur finale dépend de la couleur de tous ces points d'intersection. Intuitivement, l'objet le plus proche est censé cacher les autres et c'est donc lui qui décide de la couleur du pixel, mais cela demande de déterminer quel est l'objet le plus proche. De plus, certains objets sont transparents et la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Tout demande de calculer un pseudo-pixel pour chaque point d'intersection et de combiner leurs couleurs pour obtenir le résultat final. Les pseudo-pixels en question sont des '''fragments'''. Chaque fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont donc combinés pour obtenir la couleur finale de ce pixel. Pour résumer, la profondeur des fragments doit être gérée, de même que la transparence, etc. Et c'est justement le rôle de l'étage du pipeline que nous allons voir maintenant. Ces opérations sont réalisées dans un circuit qu'on nomme le '''Raster Operations Pipeline''' (ROP), aussi appelé ''Render Output Target'', situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. ==Les fonctions des ROP== Les ROP incorporent plusieurs fonctionnalités qui sont assez diverses. Leur seul lien est qu'il est préférable de les implémenter en matériel plutôt qu'en logiciel, et en dehors des unités de textures. Il s'agit de fonctionnalités assez simples, basiques, mais nécessaires au fonctionnement de tout rendu 3D. Elles ont aussi pour particularité de beaucoup accéder à la mémoire vidéo. C'est la raison pour laquelle le ROP est situé en fin de pipeline, proche de la mémoire vidéo. Voyons quelles sont ces fonctionnalités. ===Le mélange ''alpha'' et le ''z-buffer''=== Sa fonction la plus importante est l'élimination des pixels cachés, grâce au tampon de profondeur. Pour chaque fragment, il lit le pixel correspondant dans le tampon de profondeur, fait la comparaison de profondeur, et met à jour le tampon de profondeur. Nous en avons déjà beaucoup parlé dans les chapitres précédents, notamment dans le chapitre sur les bases du rendu 3D et dans celui sur le rastériseur (avec l'élimination précoce des pixels cachés). Une autre fonction est le mélange ''alpha'', pour gérer la transparence, qu'on a là encore vu dans le chapitre sur les bases du rendu 3D. Là encore, les ROPs lisent, pour chaque fragment, le pixel correspondant dans le ''framebuffer'', font le mélange ''alpha'', et enregistrent le résultat dans le ''framebuffer''. Le mélange ''alpha'' est supporté sur tous les ROPs, depuis les premières cartes graphiques, et est encore supporté jusqu'à ce jour. Par contre, ce n'est pas le cas qui est du test ''alpha''. Ce dernier était pris en charge dans les ROPs jusqu'à DirectX 9, mais est maintenant émulé dans les ''pixel shaders'' depuis DirectX 10. Il en est de même pour les effets de brouillard. Ils impliquent à la fois du mélange ''alpha'' mais aussi la coordonnée de profondeur, ce qui en fait que leur implémentation dans les ROPs parait logique. Aussi, les premières cartes graphiques calculaient le brouillard dans les ROP, en fonction de la coordonnée de profondeur du fragment. De nos jours, il est calculé par les ''pixel shaders'' et les ROP n'incorporent plus de technique de brouillard spécialisée. Les ROPs ont d'autres fonctions, plus méconnues, qu'on n'a pas abordé dans les chapitres précédents. ===Le tampon de ''stencil''=== Le '''''stencil''''' est une fonctionnalité des API graphiques qui existe depuis très longtemps. Il sert pour générer des effets graphiques très variés, qu'il serait vain de lister ici. Il a notamment été utilisé pour calculer des ombres volumétriques (le moteur de DOOM 3 en faisait grand usage à la base), des réflexions simples, des ''shadowmaps'', et bien d'autres. Pour le résumer, on peut le voir comme une sorte de tampon de profondeur où la coordonnée z est remplacée par u octet dont le programmeur peut faire ce qu'il veut. L'idée est que chaque pixel/fragment se voit attribuer une valeur entière, généralement codée sur un octet, que les programmeurs peuvent faire varier à loisir. L'octet ajouté est appelé l''''octet de ''stencil'''''. Il a une certaine valeur, qui est calculée par la carte graphique, généralement par les ''shaders''. Il ne remplace pas la coordonnée de profondeur, mais s'ajoute à celle-ci. Les octets de ''stencil'' sont placés dans le tampon de profondeur. L'ensemble forme un tableau qui associe 32 bits à chaque" pixel : 24 bits pour une coordonnée z, 8 pour l'octet de ''stencil''. Lors du passage d'un fragment les ROPs, la carte graphique lit le pixel correspondant, dans le tampon de profondeur. Il récupère la coordonnée z, mais aussi l'octet de ''stencil''. Puis il compare l'octet de ''stencil'' avec celui du fragment traité. Si le test échoue, le fragment ne passe pas à l'étape de test de profondeur et est abandonné. S'il passe, le tampon de ''stencil'' est mis à jour. Par mis à jour, on veut dire que le ROP peut faire diverses manipulations dessus : l'incrémenter, le décrémenter, le mettre à 0, inverser ses bits, remplacer par l'octet de ''stencil'' du fragment, etc. Les opérations possibles sont bien plus nombreuses qu'avec le tampon de profondeur, qui se contente de remplacer la coordonnée z par celle du fragment. ===Les fonctions héritées des ''blitters'' 2D=== Les ROPS implémentent aussi des techniques utilisées sur les ''blitters'' des anciennes cartes d'affichage 2D, comme l'application d''''opérations logiques''' sur chaque pixel enregistré dans le ''framebuffer''. Les opérations logiques en question peuvent prendre une à deux opérandes. Les opérandes sont soit un pixel lu dans le ''framebuffer'', soit un fragment envoyé au ROP. Les opérations logiques à un opérande peuvent inverser, mettre à 0 ou à 1 le pixel dans le ''framebuffer'', ou faire la même chose sur le fragment envoyé en opérande. Les opérations à deux opérandes lisent un pixel dans le framebuffer, et font un ET/OU/XOR avec le fragment opérande (un opérande peut être inversé). Elles sont utilisées pour faire du traitement d'image ou du rendu 2D, rarement pour du rendu 3D. Les ROPs gèrent aussi des '''masques d'écritures''', qui permettent de décider si un pixel doit être écrit ou non en mémoire. Il est possible d'inhiber certaines écritures dans le ''framebuffer'', le tampon de profondeur ou le tampon de stencil. Inhiber la mise à jour d'un pixel dans le tampon de profondeur est utile pour gérer la transparence. Si un pixel est transparent, même partiellement, il ne faut pas mettre à jour le tampon de profondeur, et cela peut être géré par ce système de masquage. Les masquages des couleurs permettent de ne modifier qu'une seule composante R/G/B au lieu de modifier les trois en même temps, pour faire certains effets visuels. ==L'architecture matérielle d'un ROP== Les ROP contiennent des circuits pour gérer la profondeur des fragments. Ils effectuent un test de profondeur, à savoir que les fragments correspondant à un même pixel sont comparés pour savoir lequel est devant l'autre. Ils contiennent aussi des circuits pour gérer la transparence des fragments. Le ROP gère aussi l'antialiasing, de concert avec l'unité de rastérisation. D'autres fonctionnalités annexes sont parfois implémentées dans les ROP. Par exemple, les vielles cartes graphiques implémentaient les effets de brouillards dans les ROPs. Le tout est suivi d'une unité qui enregistre le résultat final en mémoire, où masques et opérations logiques sont appliqués. Les différentes opérations du ROP doivent se faire dans un certain ordre. Par exemple, gérer la transparence demande que les calculs de profondeur se fassent globalement après ou pendant le mélange ''alpha''. Ou encore, les masques et opérations logiques se font à la toute fin du rendu. L'ordre des opérations est censé être le suivant : test ''alpha'', test du ''stencil'', test de profondeur, mélange ''alpha''. Du moins, la carte graphique doit donner l'impression que c'est le cas. Elle peut optimiser le tout en traitant le tampon de profondeur, de couleur et de ''stencil'' en même temps, mais donner les résultats adéquats. Un ROP est typiquement organisé comme illustré ci-dessous. Notons que les circuits de gestion de la profondeur et de la transparence sont séparés dans les schémas, mais il s'agit là d'une commodité qui ne reflète pas forcément l'implémentation matérielle. Et si ces deux circuits sont séparés, ils communiquent entre eux, notamment pour gérer la profondeur des fragments transparents. [[File:Render Output Pipeline-processor.png|centre|vignette|upright=2|Render Output Pipeline-processor]] Les ROPs récupèrent les fragments calculés par les pixels shaders et/ou les unités de texture, via un circuit d'interconnexion spécialisé. Chaque ROP est connecté à toutes les unités de ''shader'', même si la connexion n'est pas forcément directe. Toute unité de ''shader'' peut envoyer des pixels à n'importe quel ROP. Les circuits d'interconnexion sont généralement des réseaux d'interconnexion de type ''crossbar'', comme illustré ci-contre (le premier rectangle rouge). Le ROP effectue beaucoup de lectures et écritures en mémoire vidéo. Or, la bande passante mémoire est limitée, ce qui fait que le ROP est un goulot d'étranglement assez important pour le rendu 3D. Heureusement, de nombreuses optimisations permettent d'optimiser le tout. Elles agissent sur la lecture du tampon de profondeur, mais aussi sur le ''framebuffer''. ===Le ''fast clear'' du ''framebuffer''=== Une première optimisation porte sur le ''framebuffer''. Le ''framebuffer''est souvent réutilisé d'une image sur l'autre. Quand une image a été envoyée à l'écran, le ''framebuffer'' est remis à zéro pour accueillir une nouvelle image. Et ce avec ou sans ''double buffering''. La mise à zéro est censée se faire en remettant réellement le ''framebuffer'' à zéro, en écrivant des 0 pour chaque pixel du ''framebuffer''. Mais il y a moyen de s'en passer. Pour cela, l'idée est que le ''framebuffer'' est découpé en ''tiles'', des carrés de 4, 8, 16 pixels de côté. Les ''tiles'' ont généralement la même taille que les ''tiles'' utilisées pour la rastérisation, mais passons sur ce détail. L'idée est de mémoriser, pour chaque ''tile'', si elle est mise à 0 ou non. Il suffit de cela d'un seul bit par ''tile'', appelé le bit RESET. L'ensemble des bits RESET est mémorisé dans une petite mémoire SRAM, intégrée aux ROPs. Lorsqu'on souhaite remettre à zéro le ''framebuffer'', il suffit de mettre à 0 tous les bits RESET dans cette SRAM, pas besoin d’accéder à la mémoire vidéo. Avant toute lecture dans le ''framebuffer'', le ROP lit cette SRAM pour vérifier si la ''tile'' en question a été remise à 0. Si ce n'est pas le cas, il lit le pixel voulu depuis le ''framebuffer''. Mais si c'est le cas, alors le ROP ne fait pas la lecture et fournit un pixel à zéro à la place, qui est utilisé pour le mélange ''alpha'' ou autre. La moindre écriture dans une ''tile'' met le bit RESET à 0 : la ''tile'' entière est considérée comme non-remise à zéro, même si un seul pixel a été modifié dedans. Notons que l'usage d'une granularité par ''tile'' est un compromis. On peut ne peut pas utiliser un bit par pixel, car cela demanderait d'utiliser une SRAM énorme. De même, utiliser un seul bit pour tout le ''framebuffer'' ruinerait totalement l'optimisation : le ''framebuffer'' entier serait considéré comme non-RESET dès la première écriture d'un pixel dedans, on ne sauverait qu'un nombre trop limité d'accès mémoire. ===La z-compression=== La technique de '''z-compression''' compresse le tampon de profondeur. Plus précisément, elle découpe le tampon de profondeur en ''tiles'', en blocs carrés, qui sont compressés séparément les uns des autres. La taille des ''tiles'' est souvent la même que celle utilisée par le rastériseur pour la rastérisation grossière. Par exemple, la ''z-compression'' des cartes graphiques ATI radeon 9800, découpait le tampon de profondeur en ''tiles'' de 8 * 8 fragments, et les encodait avec un algorithme nommé DDPCM (''Differential differential pulse code modulation''). Précisons que cette compression ne change pas la taille occupée par le tampon de profondeur, mais seulement la quantité de données lue/écrite. La raison est que les ''tiles'' doivent avoir une place fixe en mémoire. Par exemple, si une ''tile'' non-compressée prend 64 octets, on trouvera une ''tile'' tous les 64 octets en mémoire vidéo, afin de simplifier les calculs d'adresse, afin que le ROP sache facilement où se trouve la ''tile'' à lire/écrire. Avec une vraie compression, les ''tiles'' se trouveraient à des endroits très variables d'une image à l'autre. Par contre, la z-compression réduit la quantité de données écrite dans le tampon de profondeur. Par exemple, au lieu d'écrire une ''tile'' non-compressée de 64 octets, on écrira une ''tile'' de seulement 6 octets, les 58 octets restants étant pas lus ou écrits. On obtient un gain en performance, pas en mémoire. [[File:AMD HyperZ.svg|centre|vignette|upright=2|AMD HyperZ]] Le format de compression ajoute un bit par ''tile'', qui indique si elle est compressée ou non. Le bit qui indique si la ''tile'' est compressée permet de laisser certaines ''tiles'' non-compressés, dans le cas où la compression ne permet pas de gagner de la place. La compression ajoute souvent un second bit, qui indique si la ''tile'' est à zéro ou non, sur le même modèle que pour le ''framebuffer''. Il accélère la remise à zéro du tampon de profondeur. Au lieu de réellement remettre tout le tampon de profondeur à 0, il suffit de réécrire un bit par ''tile''. Le gain en nombre d'accès mémoire peut se révéler assez impressionnant. Les deux bits en question peuvent être placés à deux endroits différents. La première solution serait d'utiliser une portion de la mémoire vidéo, mais cela demanderait de faire deux lectures par accès au tampon de profondeur. La vraie solution est d'utiliser une SRAM reliée aux ROPs, qui est assez grande pour mémoriser tout le tampon de profondeur, du moins avec deux bits par ''tile''. ===Le cache de profondeur=== Une optimisation complémentaire ajoute une ou plusieurs mémoires caches dans le ROP, dans le circuit de profondeur. Ce '''cache de profondeur''' stocke des portions du tampon de profondeur qui ont été lues ou écrite récemment. Comme cela, pas besoin de les recharger plusieurs fois : on charge un bloc une fois pour toutes, et on le conserve pour gérer les fragments qui suivent. Sur certaines cartes graphiques, les données dans le cache de profondeur sont stockées sous forme compressées dans le cache de profondeur, là encore pour augmenter la taille effective du cache. D'autres cartes graphiques ont un cache qui stocke des données décompressées dans le cache de profondeur. Tout est question de compromis entre accès rapide au cache et augmentation de la taille du cache. Il faut savoir que les autres unités de la carte graphique peuvent lire le tampon de profondeur, en théorie. Cela peut servir pour certaines techniques de rendu, comme pour le ''shadowmapping''. De ce fait, il arrive que le cache de profondeur contienne des données qui sont copiées dans d'autres caches, comme les caches des processeurs de shaders. Le cache de profondeur n'est pas gardé cohérent avec les autres caches du GPU, ce qui signifie que les écritures dans le cache de profondeur ne sont pas propagées dans les autres caches du GPU. Si on modifie des données dans ce cache, les autres caches qui ont une copie de ces données auront une version périmée de la donnée. C'est souvent un problème, sauf dans le cas du cache de profondeur, pour lequel ce n'est pas nécessaire. Cela évite d'implémenter des techniques de cohérence des caches couteuses en circuits et en performance, alors qu'elles n'auraient pas d'intérêt dans ce cas précis. ===Le ''z-fast pass''=== Le ''z-fast pass'' améliore la performance des '''prépasses z''', une technique utilisée par de nombreux moteurs de jeux vidéo. L'idée est que le moteur de jeu effectue plusieurs passes, chacune faisant un truc précis, la prépasse z étant l'une de ces passes. Lors d'une prépasse z, le moteur de jeu calcule la scène 3D, rastérise l'image, et remplit le tampon de profondeur uniquement. Il ne place pas de textures, ne calcule pas de pixels shaders, il se préoccupe uniquement des coordonnées de profondeur des pixels. Au final, le rendu ne donne que le tampon de profondeur, qui est utilisé par les passes suivantes. L'utilité est très variable, mais il y a deux raisons pour effectuer une prépasse z : la performance, mais aussi certains effets graphiques. Par exemple, les effets d'occlusion ambiante "''screen space''" utilisent le tampon de profondeur pour faire leur travail. Il en est de même pour les ''shadowmaps'', qui effectuent une prépasse z par ombre à afficher. Une autre utilisation est que cela permet d'utiliser élimination des pixels cachés très performante. On effectue une prépasse z pour calculer le tampon de profondeur final, qui est ensuite utilisé par les passes suivantes pour éliminer les pixels cachés. Ainsi, les pixels cachés ne sont pas texturés et pixel shadés, avec certitude. Toujours est-il qu'une prépasse z utilise les ROP "à moitié", dans le sens où seul le tampon de profondeur est utilisé, par la gestion des couleurs. Mais il se trouve que les circuits qui servent pour le mélange ''alpha'' peuvent être réutilisés pour faire les comparaisons de profondeur ! Le résultat est que les ROP peuvent fonctionner à double vitesse lors d'une prépasse z ! Cela demande cependant de concevoir les circuits du ROP pour en profiter. L'optimisation est parfois appelée le '''''z-fast pass'''''. Tous les GPU depuis la Geforce FX en sont capables. Il y a cependant quelques contraintes. Premièrement, le ROP doit être configuré de manière à n’accéder qu'au tampon de profondeur, ils ne doivent pas dessiner dans le ''framebuffer''. Le mélange '''alpha'' doit être désactivé, de même que l'alpha-test. D'autres contraintes supplémentaires sont parfois présentes, surtout sur les vieux GPUs. Par exemple, l'antialiasing doit être désactivé lors de la prépasse z. Et mine de rien, cela ne marche que pour les prépasses z pures. Par exemple, certaines techniques de rendu différé augmentent la prépasse z pour que celle-ci ne calcule pas que le tampon de profondeur, mais aussi d'autres informations comme les normales : elles ne profitent pas de cette optimisation. ==Pourquoi ne pas émuler les ROPs dans les ''pixel shader'' ?== Les ROPs effectuent plusieurs opérations basiques, mais les deux plus importantes sont la gestion du tampon de profondeur et de la transparence. Par transparence, on veut parler du mélange ''alpha''. Pour la gestion du tampon de profondeur, on veut parler du ''z-test'', qui compare la profondeur de deux pixels/fragments. Il s'agit d'opérations simples, qu'un processeur de shader peut faire sans problèmes. Par exemple, le ''z-test'' demande de faire plusieurs étapes : * calculer l'adresse du pixel dans le tampon de profondeur ; * lire le pixel dans le tampon de profondeur ; * Faire la comparaison entre profondeurs ; * Si le résultat de la comparaison est okay : ** écrire la nouvelle valeur z dans le tampon de profondeur, et écrire le nouveau pixel dedans. Le mélange ''alpha'' demande lui de : * calculer l'adresse du pixel dans le ''framebuffer'' ; * lire le pixel dans le ''framebuffer'' ; * faire des additions et multiplications pour le mélange ''alpha'' : * écrire le nouveau pixel dans le ''framebuffer''. Pour résumer il faut pouvoir faire : calcul d'adresse, lecture, écriture, addition, multiplication et comparaisons. Et toutes ces opérations sont supportées nativement par les processeurs de shaders, ce sont des instructions communes. Il est donc possible d'émuler les ROPs dans les pixels shaders. En pratique, c'est assez rare, et il y a une bonne explication à cela. ===Les GPU de type ''sort-last'' doivent "trier les pixels"=== Émuler les ROPs dans un ''pixel shader'' est trivial, comme on vient de le voir. Sauf que cela ne marche que si le GPU fait le rendu un pixel à la fois. Le tampon de profondeur est conçu pour traiter un pixel à la fois, idem pour le mélange ''alpha''. Mais si on ne traite pas l'image pixel par pixel, alors les deux algorithmes dysfonctionnent. Donc, tout va bien s'il n'y a qu'un seul processeur de ''pixel shader'', et que celui-ci est conçu pour ne traiter qu'un pixel à la fois, qu'une seule instance de ''shader''. Mais cela ne marche pas sur les GPU modernes, qui ont non seulement près d'une centaine de processeurs de shaders, chacun étant conçu pour traiter une centaine de fragments/pixels en même temps ! Pour donner un exemple, imaginons la situation illustrée ci-dessous. Supposons que l'on ait assez de processeurs de shaders pour traiter plusieurs triangles en même temps. Par malchance, les processeurs rendent en même temps deux triangles opaques qui se recouvrent à l'écran. Là où ils se recouvrent, les deux triangles vont générer deux fragments par pixel, et un seul sera le bon. Pas de chance, les deux fragments sont rendus en parallèle dans deux processeurs séparés. Les deux processeurs lisent la même donnée dans le tampon de profondeur et les deux fragments passent le ''z-test'', car ils n'ont aucun moyen de savoir la coordonnée z en cours de traitement dans l'autre processeur. Les deux processeurs vont alors écrire leur résultat en mémoire et c'est premier arrivé, premier servi. Le résultat n'est pas forcément celui attendu : le pixel le plus proche peut être écrit avant le plus lointain, ou inversement. [[File:Situation où faire le z-test dans les pixel shaders dysfonctionne.png|centre|vignette|upright=2|Situation où faire le z-test dans les pixel shaders dysfonctionne]] Pour obtenir un bon rendu, le GPU doit forcer le z-test à se faire fragment par fragment, du moins quand on regarde un pixel individuel. Il reste possible de traiter des pixels différents en parallèle, mais pas deux fragments d'un même pixel. En utilisant des processeurs de shaders qui travaillent en parallèle, cette contrainte est parfois brisée et le rendu donne des résultats incorrects. Le tampon de profondeur n'est pas conçu pour être parallélisé, idem pour le mélange ''alpha''. Il faut donc une sorte de point de synchronisation dans le pipeline pour éviter tout problème. Et c'est à ça que servent les ROPs. Une explication alternative est la suivante. Les fragments doivent être rendus dans l'ordre de soumission. C'est à dire que des pixels qui sortent du rastériseur dans un certain ordre doivent être enregistré en mémoire dans ce même ordre. Et les pixels ne quittent pas le rastériseur dans le désordre non plus : des triangles arrivant au rastériseur dans un certain ordre seront traité dans ce même ordre. Le problème est que les GPU étant massivement parallèle, les triangles et pixels seront traités dans le désordre, avant que leurs résultats soient remis en ordre. Pour les triangles/sommets, la remise en ordre se fait au niveau de l'assemblage de primitives, juste avant le rastériseur. Pour les fragments, la remise en ordre se fait dans les ROPs, en sortie des ''shaders'', à la fin du pipeline. Du moins, sur les GPU de type ''sort-last''. Les ROPs ne font donc pas que faire le ''z-test'' et le mélange ''alpha'', ils ont des circuits de remise en ordre pour ça, qui sont de plus fortement mélés au tampon de profondeur et au ''framebuffer''. ===Le traitement parallèle des fragments sur les GPU ''sort-last''=== Plus haut, j'ai dit qu'il doit être impossible de traiter en même temps deux fragments d'un même pixel. Mais il reste possible de traiter des pixels différents en parallèle, mais pas deux fragments d'un même pixel. Reste à faire en sorte de ne pas envoyer deux fragments d'un même pixel dans les processeurs de shaders. Une solution pour cela serait de mémoriser, pour chaque pixel, si un ''pixel shader'' est en train de le traiter. Il suffit de mémoriser un bit par pixel pour cela, dans une table d'utilisation, concrètement une petite mémoire. Elle serait mise à jour par les processeurs de shaders, et consultée par le rastériseur. Quand le rastériseur génère un fragment, il consulte cette table, pour vérifier s'il y a conflit avec les fragments en cours de traitement. Il attend si c'est le cas, le pixel shader finira par finir de traiter le pixel au bout d'un moment. Mais l'inconvénient de cette solution est qu'elle a besoin d'une mémoire partagée par tous les processeurs de shaders, qui est difficile à concevoir sans faire des concessions en termes de performances. Une autre solution serait de mémoriser tous les pixels en cours de traitement. Quand le rastériseur génère un fragment, il mémorise les coordonnées x,y de ce fragment à l'écran, dans une '''table des pixels occupés'''. Dès qu'un pixel shader se termine, la table des pixels occupés est mise à jour. Le rastériseur consulte cette table quand il génère un fragment, afin de détecter les conflits. S'il y a conflit, le rastériseur attend que le fragment conflictuel, en cours de traitement dans le pixel shader, soit traité. L’inconvénient de la solution précédente est que la table des pixels occupés est techniquement une mémoire associative, une sorte de mémoire cache, qui est plus complexe qu'une simple RAM. Il est très difficile de créer une mémoire de ce genre qui soit capable de mémoriser plusieurs dizaines ou centaine de milliers de pixels, pour gérer une centaine de processeurs de shaders. Par contre, elle fonctionne pas trop mal pour un petit nombre de processeurs de shaders, qui fonctionnent à basse fréquence. Cela explique que les GPU pour PC ont des ROPs séparés des processeurs de ''shaders'' : ces GPU ont beaucoup trop de processeurs de shaders. Par contre, quelques cartes graphiques destinées les smartphones et autres appareils mobiles émulent les ROPs dans les ''pixel shaders''. Mais il y a une bonne raison à cela : non seulement, ils n'ont que très peu de processeurs de shader, mais ce sont aussi des GPU en rendu à tuiles. L'avantage est qu'ils rendent une tile à la fois, ce qui fait qu'il y a besoin de tester les conflits entre fragments à l'intérieur d'une tile, pas pour l'écran complet. Et cela simplifie grandement les circuits de test, notamment la table des pixels occupés, qui est bien plus petite. {{NavChapitre | book=Les cartes graphiques | prev=Les unités de texture | prevText=Les unités de texture | next=Les écritures en VRAM hors ROPs | nextText=Les écritures en VRAM hors ROPs }}{{autocat}} dqjq6nzihf24ldbgf1dgrhwm84bzd75 Les cartes graphiques/Les unités de texture 0 67395 763941 763424 2026-04-18T13:19:51Z Mewtow 31375 /* La normalisation des coordonnées */ 763941 wikitext text/x-wiki [[File:Texture mapping.png|vignette|''Texture mapping'']] Les '''textures''' sont des images que l'on va plaquer sur la surface d'un objet, du papier peint en quelque sorte. Les cartes graphiques supportent divers formats de textures, qui indiquent comment les pixels de l'image sont stockés en mémoire : RGB, RGBA, niveaux de gris, etc. Une texture est donc composée de "pixels", comme toute image numérique. Pour bien faire la différence entre les pixels d'une texture, et les pixels de l'écran, les pixels d'une texture sont couramment appelés des ''texels''. ==Le placage de textures inverse== Pour rappel, plaquer une texture sur un objet consiste à attribuer un texel à chaque sommet, ce qui est fait lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet. Chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. Dans les faits, on n'utilise pas de coordonnées entières de ce type. Les coordonnées de texture sont deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale. Le nom donnée à cette technique de description des coordonnées de texture s'appelle l''''''UV Mapping'''''. [[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]] Les API 3D modernes gèrent des textures en trois dimensions, ce qui ajoute une troisième coordonnée de texture notée w. Dans ce qui va suivre, nous allons passer les textures en trois dimensions sous silence. Elles ne sont pas très utilisées, la quasi-totalité des jeux vidéo et applications 3D utilisant des textures en deux dimensions. Par contre, le matériel doit gérer les textures 3D, ce qui le rend plus complexe que prévu. Il faut ajouter quelques circuits pour, de quoi gérer la troisième coordonnée de texture, etc. Lors de la rastérisation, chaque fragment se voit attribuer un sommet, et donc la coordonnée de texture qui va avec. Si un pixel est situé pile sur un sommet, la coordonnée de texture de ce sommet est attribuée au pixel. Si ce n'est pas le cas, la coordonnée de texture finale est interpolée à partir des coordonnées des trois sommets du triangle rastérisé. L'interpolation en question a lieu dans l'étape de rastérisation, comme nous l'avons vu dans le chapitre précédent. Le fait qu'il y ait une interpolation fait que les coordonnées du pixel gagent à être des nombres flottants. On pourrait faire une interpolation avec des coordonnées de texture entières, mais les arrondis et autres imprécisions de calcul donneraient un résultat graphiquement pas terrible, et empêcheraient d'utiliser les techniques de filtrage de texture que nous verrons dans ce chapitre. À partir de ces coordonnées de texture, la carte graphique calcule l'adresse du texel qui correspond, et se charge de le lire. Et toute la magie a lieu dans ce calcul d'adresse, qui part de coordonnées de texture flottante, pour arriver à une adresse mémoire. Le calcul de l'adresse du texel se fait en plusieurs étapes, que nous allons voir ci-dessous. La première étape convertit les coordonnées flottantes en coordonnées entières, qui disent à quel ligne et colonne se trouve le texel voulu dans la texture. L'étape suivante transforme ces coordonnées x,y entières en adresse mémoire. ===La normalisation des coordonnées=== J'ai dit plus haut que les coordonnées de texture sont des coordonnées flottantes, comprises entre 0 et 1. Mais il faut savoir que les pixels shaders peuvent modifier celles-ci pour mettre en œuvre certains effets graphiques. Et le résultat peut alors se retrouver en-dehors de l'intervalle 0,1. C'est quelque chose de voulu et qui est traité par la carte graphique automatiquement, sans que ce soit une erreur. Au contraire, la manière dont la carte graphique traite cette situation permet d'implémenter des effets graphiques comme des textures en damier ou en miroir. [[File:Clamp tile.jpg|vignette|Clamp tile]] Il existe globalement trois méthodes très simples pour gérer cette situation, qui sont appelés des '''modes d'adressage de texture'''. La première méthode est de faire en sorte que le résultat sature. Si une coordonnée est inférieur à 0, alors on la remplace par un zéro. Si elle est supérieure à 1, on la ramène à 1. Avec cette méthode, tout se passe comme si les bords de la texture étaient étendus et remplissaient tout l'espace autour de la texture. Le tout est illustré ci-dessous. Ce mode d'accès aux textures est appelé le '''''clamp'''''. Une autre solution retire la partie entière de la coordonnée, elle coupe tout ce qui dépasse 1. Pour le dire autrement, elle calcule le résultat modulo 1 de la coordonnée. Le résultat est que tout se passe comme si la texture était répétée à l'infini et qu'elle pavait le plan. Une autre méthode remplit les coordonnées qui sortent de l’intervalle 0,1 avec une couleur préétablie, configurée par le programmeur. ===La conversion des coordonnées de textures flottantes en adresse mémoire=== Une fois la normalisation effectuée, les coordonnées de texture sont utilisées pour lire le texel voulu. Pour cela, les coordonnées de texte sont transformées en adresse mémoire, adresse qui pointe sur le texel ayant ces cordonnées. Pour cela, la première étape est de transformer les coordonnées flottantes u,v en coordonnées entières x,y qui pointent sur un texel. Pour cela, il suffit de multiplier les coordonnées flottantes u,v par la résolution de la texture accédée. Pour un écran de résolution <math>\text{height,width}</math>, le calcul est le suivant : : <math>x = u \times \text{width}</math> : <math>y = v \times \text{height}</math> Le résultat est un nombre avec une partie entière et une partie fractionnaire. La partie entière des deux coordonnées donne la position x,y voulue, et la partie fractionnaire est conservée pour le filtrage de textures, mais passons cela sous silence pour le moment. La seconde étape prend les coordonnées entières x,y et calcule l'adresse mémoire du texel. L'adresse dépend de la position de la texture en mémoire, précisément de son début, son premier texel, mais aussi de la position du texel par rapport au début de la texture. Et calculer cette position intra-texture dépend de la manière dont les texels sont stockés en mémoire. ====Les textures naïves==== Les programmeurs qui lisent ce cours s'attendent certainement à ce que la texture soit stockée en mémoire ligne par ligne, ou colonne par colonne. Cela veut dire que le premier pixel en partant d'en haut à gauche est stocké en premier, puis celui immédiatement à sa droite, puis celui encore à droite, et ainsi de suite. Une fois qu'on arrive à la fin d'une ligne, on passe à la ligne suivante, en-dessous. Cette organisation ligne par ligne s'appele l'organisation '''''row major order'''''. On peut faire pareil, mais colonne par colonne, ce qui donne le '''''column major order'''''. [[File:Speicheranordnung Feld.svg|centre|vignette|upright=2|Row et column major order.]] Maintenant, supposons que la texture commence à l'adresse <math>A_\text{texture}</math>, qui est l'adresse du premier texel. La texture a une résolution de <math>\text{width}</math> texels de large et <math>\text{height}</math> texels de haut. Par définition, les coordonnées X et Y des texels commencent à 0, ce qui fait que le pixel en haut à gauche a les coordonnées 0,0. L'adresse du pixel se calcule comme suit : : <math>A_\text{pixel} = A_\text{texture} + (\text{taille d'une ligne en octets} \times Y) + (\text{taille d'un texel en octets} \times X)</math> La taille d'un pixel en mémoire est notée T. La taille d'une ligne en mémoire est de <math>width \times T</math>, par définition, vu qu'elle fait <math>width</math> texels. On a donc : : <math>A_\text{pixel} = A_\text{texture} + (width \times T \times Y) + (T \times X)</math> La formule se réécrit comme suit : : <math>A_\text{pixel} = A_\text{texture} + T \times (width \times Y + X)</math> Le calcul d'adresse est donc assez simple. Malheureusement, les textures ne sont pas stockées de cette manière en mémoire vidéo. En effet, elle se marie mal avec les opérations de filtrage de texture que nous allons voir dans ce qui suit. Le filtrage d'un texel dépend de ses voisins du dessus et du dessous. Le fait que la texture n'est pas forcément parcourue ligne par ligne fait que stocker une texture ligne par ligne n'est pas l'idéal. De même, les textures sont déformées par la perspective. L'affichage de la texture ne se fait alors pas ligne par ligne, mais en parcourant la texture en diagonale, l'angle de la diagonale correspondant approximativement à l'angle que fait la verticale de la texture avec le regard. Vu qu'on ne connait pas à l'avance l'angle que fera la diagonale de parcours, on doit ruser. ====Les textures tilées==== Une première solution à ce problème est celle des '''textures tilées'''. Avec ces textures, l'image de la texture est découpée en ''tiles'', des rectangles ou en carrés de taille fixe, généralement des carrés de 4 pixels de côté. Les tiles ont une largeur et une longueur égales, afin de simplifier les calculs : on divise X et Y par le même nombre. De plus, leur largeur et leur longueur sont une puissance de deux, afin de simplifier les calculs d'adresse. Les ''tiles'' sont alors mémorisée les unes après les autres dans le fichier de la texture. [[File:Texture tilée.png|centre|vignette|upright=2|Texture tilée]] La formule de calcul d'adresse vue plus haut doit être adaptée pour tenir compte des tiles. Pour cela, il faut remplacer la taille d'un texel par la taille d'une tile, et que la largeur de la texture soit exprimée en nombre de tiles. De plus, on doit adapter les coordonnées des texels pour donner des coordonnées de tile. Généralement, les tiles sont des carrés de N pixels de côté, ce qui fait qu'on peut regrouper les lignes et les colonnes par paquets de N. Il suffit donc de diviser Y et X pour obtenir les coordonnées de la tile, de même que la larguer. La formule pour calculer la position de la énième tile est alors la suivante : : <math>\text{adresse d'une tile} = \text{adresse du début de la texture} + \text{Taille mémoire d'une tile} \times \left( {\text{Width} \over N} \times {Y \over N} + {X \over N} \right)</math> On peut réécrire le tout comme suit : : <math>\text{adresse d'une tile} = \text{adresse du début de la texture} + K \times \left( {Y \over N} + X \right)</math>, avec K une constante connue à la compilation des shaders. Vu que les tiles sont carrées avec une largeur qui est une puissance de deux, la multiplication par la taille d'une tile en mémoire se simplifie : on passe d'une multiplication entière à des décalages de bits. Même chose pour le calcul de l'adresse de la tile à partir des coordonnées x,y : ils impliquent des divisions par une puissance de deux, qui deviennent de simples décalages. La position d'un pixel dans une tile dépend du format de la texture, mais peut se calculer avec quelques calculs arithmétiques simples. Dans les cas les plus simples, les pixels sont mémorisés ligne par ligne, ou colonne par colonne. Mais ce n'est pas systématiquement le cas. Toujours est-il que les calculs pour déterminer l'adresse sont simples, et ne demandent que quelques additions ou multiplications. Mais avec les formats de texture utilisés actuellement, les tiles sont chargées en entier dans le cache de texture, sans compter que diverses techniques de compression viennent mettre le bazar, comme on le verra dans la suite de cours. Un avantage de l'organisation en tiles est qu'elle se marie bien avec le parcours des textures. On peut parcourir une texture dans tous les sens, horizontal, vertical, ou diagonal, on sait que les prochains pixels ont de fortes chances d'être dans la même tile. Si on rentre dans une tile par la gauche en haut, on a encore quelques pixels à parcourir dans la tile, par exemple. De même, le filtrage de textures est facilité. On verra dans ce qui va suivre que le filtrage de texture a besoin de lire des blocs de 4 texels, des carrés de 2 pixels de côté. Avec l'organisation en tile, on est certain que les 4 texels seront dans la même tile, sauf s'ils ont le malheur d'être tout au bord d'une tile. Ce dernier cas est assez rare, et il l'est d'autant plus que les tiles sont grandes. Enfin, un dernier avantage est que les tiles sont généralement assez petites pour tenir tout entier dans une ligne de cache. Le cache de texture est donc utilisé à merveille, ce qui rend les accès aux textures plus rapides. ====Les textures basées sur des ''z-order curves''==== Les formats de textures théoriquement optimaux utilisent une '''''Z-order curve''''', illustrée ci-dessous. L'idée est de découper la texture en quatre rectangles identiques, et de stocker ceux-ci les uns à la suite des autres. L'intérieur de ces rectangles est lui aussi découpé en quatre rectangles, et ainsi de suite. Au final, l'ordre des pixels en mémoire est celui illustré ci-dessous. [[File:Z-CURVE.svg|centre|vignette|upright=2|Construction d'une ''Z-order curve''.]] Les texels sont stockés les uns à la suite des autres dans la mémoire, en suivant l'ordre donnée par la ''Z-order curve''. Le calcul d'adresse calcule la position du texel en mémoire, par rapport au début de la texture, et ajoute l'adresse du début de la texture. Mais tout le défi est de calculer la position d'un texel en mémoire, à partir des coordonnées x,y. Le calcul peut sembler très compliqué, mais il n'en est rien. Le calcul demande juste de regarder les bits des deux coordonnées et de les combiner d'une manière particulièrement simple. Il suffit de placer le bit de poids fort de la coordonnée x, suivi de celui de la coordonnée y, et de faire ainsi de suite en passant aux bits suivants. [[File:Zcurve45bits.png|centre|vignette|upright=1.5|Calcul de la position d'un élément dans une ''Z-order curve'' à partir des coordonnées x et y.]] L'avantage d'une telle organisation est que la textures est découpées en ''tiles'' rectangulaires d'une certaine taille, elles-mêmes découpées en ''tiles'' plus petites, etc. Et il se trouve que cette organisation est parfaite pour le cache de texture. L'idéal pour le cache de texture est de charger une ''tile'' complète dans le cache de textures. Quand on accède à un texel, on s'assure que la ''tile'' complète soit chargée. Mais cela demande de connaitre à l'avance la taille d'une ''tile''. Les formats de texture fournissent généralement une ''tile'' carré de 4 pixels de côté, mais cela donnerait un cache trop petit pour être vraiment utile. Avec cette méthode, on s'assure qu'il y ait une ''tile'' avec la taille optimale. Les ''tiles'' étant découpées en ''tiles'' plus petites, elles-mêmes découpées, et ainsi de suite, on s'assure que la texture est découpées en ''tiles'' de taille variées. Il y aura au moins une ''tile'' qui rentrera tout pile dans le cache. ===Les techniques de rendu à textures multiples=== Nous venons de voir comment une texture est plaquée sur un objet 3D, ou une surface comme un sol. Pour résumer, le calcul de l'adresse d'un texel prend la position du texel par rapport au début de la texture, et ajoute l'adresse du début de la texture. L'adresse mémoire de la texture est connue au moment où le pilote de la carte graphique place la texture dans la mémoire vidéo, et cette information est transmise au matériel par l'intermédiaire du processeur de commande, puis passée aux processeurs de shaders et à l'unité de texture. Le tout est couplé à d'autres informations, la plus importante étant la ''taille de la texture en octets'', pour éviter de déborder lors des accès à la texture. Néanmoins, il s'agit là du cas le plus simple. Certaines techniques de rendu demandent de choisir la texture à plaquer parmi un ensemble de plusieurs textures. Les techniques en question sont assez variées et n'ont pas grand chose en commun. Les plus connues sont le ''mip-mapping'', le ''cube-mapping'' et les textures virtuelles. Le ''mip-mapping'' sert à filtrer les textures, chose qu'on expliquera plus tard, le ''cube-mapping'' sert à simuler des réflexions sur un objet en plaquant une texture de l'environnement dessus, les textures virtuelles sont une optimisation pour les textures des terrains de grande taille. Mais malgré leurs différences, elles demandent de choisir quelle texture plaquer entre plusieurs textures de base. En clair, l'adresse de base de la texture varie selon la situation. Voyons-les dans le détail. ==L'implémentation matérielle du placage de textures== Pour résumer, la lecture d'un texel demande d'effectuer plusieurs étapes. Dans le cas le plus simple, sans ''mip-mapping'' ou ''cubemapping'', on doit effectuer les étapes suivantes : * Il faut d'abord normaliser les coordonnées de texture pour qu'elles tombent dans l'intervalle [0,1] en fonction du mode d'adressage désiré. * Ensuite, les coordonnées u,v doivent être converties en coordonnées entières, ce qui demande une multiplication flottante. * Enfin, l'adresse finale est calculée à partir des coordonnées entières et en ajoutant l'adresse de base de la texture (et éventuellement avec d'autres calculs arithmétiques suivant le format de la texture). Tout cela pourrait être fait par le pixel shaders, mais cela implique beaucoup de calculs répétitifs et d'opérations arithmétiques assez lourdes, avec des multiplications flottantes, des additions et des multiplications entières, etc. Faire faire tous ces calculs par les shaders serait couteux en performance, sans compter que les shaders deviendraient plus gros et que cela aurait des conséquences sur le cache d'instruction. De plus, certaines de ces étapes peuvent se faire en parallèle, comme les deux premières, ce qui colle mal avec l'aspect sériel des shaders. Aussi, les processeurs de shaders incorporent une unité de calcul d'adresse spéciale pour faire ces calculs directement en matériel. L'unité de calcul en question est dans l'unité de texture. Cette dernière contient donc au minimum deux circuits : un circuit de calcul d'adresse, et un circuit d'accès à la mémoire. Toute la difficulté tient dans le calcul d'adresse, plus que dans le circuit de lecture. Le calcul d'adresse est conceptuellement réalisé en deux étapes. La première étape qui transforme les coordonnées u,v en coordonnées x,y qui donne le numéro de la ligne et de la colonne du texel dans la texture. La seconde étape prend ces deux coordonnées x,y, l'adresse de la texture, et détermine l'adresse de la tile à lire. [[File:Unité de texture simple.png|centre|vignette|upright=2|Unité de texture simple]] ===La gestion des accès mémoire=== Enfin, l'unité de texture doit tenir compte du fait que la mémoire vidéo met du temps à lire une texture. En théorie, l'unité de texture ne devrait pas accepter de nouvelle demande de lecture tant que celle en cours n'est pas terminée. Mais faire ainsi demanderait de bloquer tout le pipeline, de l'''input assembler'' au unités de''shaders'', ce qui est tout sauf pratique et nuirait grandement aux performances. Une solution alternative consiste à mettre en attente les demandes de lectures de texture pendant que la mémoire est occupée. La manière la plus simple d'implémenter des accès mémoire multiples est de les mettre en attente dans une petite mémoire FIFO. Cela implique que les accès mémoire s’exécutent dans l'ordre demandé par le ''shader'' et/ou l'unité de rastérisation, il n'y a pas de réorganisation des accès mémoire ou d’exécution dans le désordre des accès mémoire. [[File:Texture prefetching.png|centre|vignette|upright=1.5|Accès mémoire simultanés.]] Évidemment, quand la mémoire FIFO est pleine, le pipeline est alors totalement bloqué. Le rasteriser est prévenu que l'unité de texture ne peut pas accepter de nouvelle lecture de texture. En pratique, la FIFO est généralement d'une taille respectable et permet de mettre en attente beaucoup de demandes de lecture de texture. Il faut de plus noter qu'il y a une FIFO par processeur de ''shader'' sur les cartes graphiques modernes. Quand elle est pleine, le processeur cesse d'exécuter de nouveaux accès mémoire, mais peut continuer à exécuter des ''shaders'' dans les autres unités de calcul, pas besoin de bloquer complétement le pipeline. ===L'intégration du cache de textures=== Il faut noter que les unités de texture incorporent aussi un cache de texture, voire plusieurs. L'intégration des caches de texture avec la mémoire FIFO précédente est quelque peu compliqué, car il faut garantir que les lectures de texture se fassent dans le bon ordre. On ne peut pas exécuter une lecture dans le cache alors que des lectures précédentes sont en attente de lecture en mémoire vidéo. Et cela pose un gros problème : une lecture dans le cache de texture prend quelques dizaines de cycles d'horloge, alors qu'une lecture en mémoire vidéo en prend facilement 400 à 800 cycles, parfois plus. Et cela fait que l'ordre des accès mémoire peut s'inverser. Prenons par exemple un accès au cache précédé et suivi par deux accès en mémoire vidéo. Le premier démarre au cycle 1, et se termine au cycle numéro 400. L'accès au cache commence au cycle 2 et se termine 20 cycles après, au cycle numéro 22. En clair, la lecture dans le cache s'est terminée avant l'accès mémoire qui le précède. Les textures ne sont donc plus lues dans l'ordre. Et il faut trouver une solution pour éviter cela. La solution est de retarder les lectures dans le cache tant que tous les accès précédents ne sont pas terminés. Mais pour retarder les lectures en question, il faut d'abord savoir si la lecture atterrit dans le cache ou non, ce qui demande d'accéder au cache. On fait face à un dilemme : on veut retarder les accès au cache, mais les différencier des lectures déclenchant des accès mémoire demande d'accéder au cache en premier lieu. La solution est décrite dans l'article "Prefetching in a Texture Cache Architecture" par Igehy et ses collègues. Elle se base sur deux idées combinées ensemble. La première idée est de séparer l'accès au cache en deux : une étape qui vérifie si les texels à lire sont dans le cache, et une étape qui accède aux données dans le cache lui-même. Un cache de texture est donc composé de deux circuits principaux. Le premier vérifie la présence des texels dans le cache. Il reçoit l'adresse mémoire à lire, et détermine si une copie de la donnée associée est dans le cache ou non. Pour cela, il utilise un système de tags qu'on ne détaillera pas ici, mais qui donne son nom à l'unité de vérification : l''''unité de tag'''. Ensuite, en plus de l'unité de tags, il y a une mémoire qui stocke les données, la mémoire cache proprement dite. Par simplicité, cette mémoire est une simple mémoire RAM adressable avec des adresses mémoires des plus normales, chaque ligne de cache correspondant à une adresse. Ce genre de cache séparé en deux mémoires est appelé un ''phased cache'', pour ceux qui veulent en savoir plus. [[File:Phased cache.png|centre|vignette|upright=1.5|Phased cache]] La seconde idée est de retarder l'accès au cache entre les deux phases. La première étape d'un accès mémoire vérifie si la donnée est dans le cache ou non. Puis, on retarde la lecture des données, pour attendre que toutes les lectures précédentes soient terminées. Et enfin, troisième étape : la lecture des texels dans la mémoire cache proprement dite. Les accès mémoire passant par la mémoire vidéo se font de la même manière, à une différence près : la lecture dans le cache est remplacée par la lecture en mémoire vidéo. Tout démarre avec une demande à l'unité de tags, qui vérifie si le texel est dans le cache ou non. Puis on retarde l'accès tant que la mémoire vidéo est occupée, puis on effectue la lecture en mémoire vidéo. Si ce n'est pas le cas, l'accès mémoire est envoyé à la mémoire vidéo comme précédemment, à savoir qu'il est mis en attente dans une mémoire FIFO, puis envoyé à la mémoire vidéo dès que celle-ci est libre. Mais en sortie de la mémoire, la donnée lue est envoyée dans le cache de texture, par dans l'unité de filtrage. Pour savoir où placer la donnée lue, l'unité de tag a réservé une ligne de cache précise, une adresse bien précise. L'adresse en question est disponible en lisant une autre mémoire FIFO, qui a mis en attente l'adresse en question, en attendant que l'accès mémoire se termine. La donnée est alors écrite dans le cache, puis lue par l'unité de filtrage de textures. Pour une lecture dans le cache, le déroulement est similaire, mais sans le passage par la mémoire. La lecture fait une demande à l'unité de tag, et celle-ci répond que la donnée est bien dans le cache. Elle place alors l'adresse à lire dans la file d'attente. Une fois que les accès mémoire précédents sont terminés, l'adresse sort de la file d'attente et est envoyée à la mémoire de données. La lecture s'effectue, les texels sont envoyés à l'unité de filtrage de textures. La seule différence avec un ''phased cache'' normal est l'insertion de l'adresse à lire dans une FIFO qui vise à mettre en attente [[File:Unité de texture avec un cache de texture.png|centre|vignette|upright=2.0|Unité de texture avec un cache de texture]] Pour résumer, l'implémentation précédente garantit une exécution des lectures dans leur ordre d'arrivée. Et pour cela, elle retarde les lectures dans le cache tant que les lectures en mémoire précédentes ne sont pas terminées. L'accès au cache est plus rapide que l'accès en mémoire vidéo, mais le retard ajouté pour garantir l'ordre des lectures fait que le temps d'accès est très long. ==Le mip-mapping== Le '''mip-mapping''' a pour but de légèrement améliorer les graphismes des objets lointains, tout en rendant les calculs de texture plus rapides. Formellement, le ''mip-mapping'' est une technique de filtrage de texture, mais nous l'abordons maintenant car elle est surtout liée au calcul d'adresse. Les unités de texture ont des circuits de filtrage de texture séparés des circuits de ''mip-mapping'' et de calcul d'adresse, d'où le fait que nous en parlons séparément. Le problème résolu par le ''mip-mapping'' est le rendu des textures lointaines. Si une texture est plaquée sur un objet lointain, une bonne partie des détails est invisible pour l'utilisateur. Un pixel de l'écran est associé à plusieurs texels. Idéalement, la carte graphiques devrait lire tous ces texels et en faire une sorte de moyenne pondérée, pour calculer la couleur finale du pixel. Mais dans les faits, ce serait très gourmand et compliqué à implémenter en hardware. Une solution serait de ne garder que quelque texels, mais cela a tendance à créer des artefacts visuels (les textures affichées ont tendance à pixeliser). Le ''mip-mapping'' permet de réduire ces deux problèmes en même temps en précalculant cette moyenne pondérée pour des distances prédéfinies. L'idée est d'utiliser plusieurs exemplaires d'une même texture à des résolutions différentes, chaque exemplaire étant adapté à une certaine distance. Par exemple, une texture sera stocké avec un exemplaire de 512 * 512 pixels, un autre de 256 * 256, un autre de 128 * 128 et ainsi de suite jusqu’à un dernier exemplaire de 32 * 32 pixel. Chaque exemplaire correspond à un '''niveau de détail''', aussi appelé ''Level Of Detail'' (abrévié en LOD). La résolution utilisée diminue d'autant plus que l'objet est situé loin de la caméra. Les objets proches seront rendus avec la texture 512*512, ceux plus lointains seront rendus avec la texture de résolution 256*256, les textures 128*128 seront utilisées encore plus loin, et ainsi de suite jusqu'aux objets les plus lointains qui sont rendus avec la texture la plus petite de 32*32. [[File:MipMap Example STS101.jpg|centre|vignette|upright=2|Exemples de mip-maps.]] Le ''mip-mapping'' améliore grandement la qualité d'image. L'image d'exemple ci-dessous le montre assez bien. [[File:Mipmapping example.png|centre|vignette|upright=2|Exemple de mipmapping.]] Pour faciliter les calculs d'adresse, les LOD d'une même texture sont stockées les uns après les autres en mémoire (dans un tableau, comme diraient les programmeurs). Ainsi, pas besoin de se souvenir de la position en mémoire de chaque LOD : l'adresse de la texture de base, et quelques astuces arithmétiques suffisent. Prenons le cas où la texture de base a une taille L. le premier exemplaire est à l'adresse 0, le second niveau de détail est à l'adresse L, le troisième à l'adresse L + L/4, le suivant à l'adresse L + L/4 + L/16, et ainsi de suite. Le calcul d'adresse demande juste connaître le niveau de détails souhaité et l'adresse de base de la texture. Le niveau de détail voulu est calculé par les pixel shaders, en fonction de la coordonnée de profondeur du pixel à traiter. Évidemment, cette technique consomme de la mémoire vidéo, vu que chaque texture est dupliquée en plusieurs exemplaires, en plusieurs LOD. Dans le détail, la technique du mip-mapping prend au maximum 33% de mémoire en plus (sans compression). Cela vient du fait qu'en prenant une texture dexu fois plus petite, elle prend 4 fois moins de mémoire : 2 fois moins de pixels en largeur, et 2 fois moins en hauteur. Donc, si je pars d'une texture de base contenant X pixels, la totalité des LODs, texture de base comprise, prendra X + (X/4) + (X/16) + (X/256) + … Un petit calcul de limite donne 4/3 * X, soit 33% de plus. ===L'implémentation du mip-mapping dans l'unité de texture=== Le ''mip-mapping'' est lui aussi pris en charge par l'unité de calcul d'adresse, car cette technique change l'adresse de base de la texture. La gestion du ''mip-mapping'' est cependant assez complexe. Il est possible de laisser le pixel shader calculer quel niveau de détail utiliser, en fonction de la coordonnée de profondeur z du pixel à afficher. La carte graphique détermine alors automatiquement quelle texture lire, quel niveau de détail, automatiquement. Elle détermine aussi la bonne résolution pour la texture, qui est égal à la résolution de la texture de base, divisée par le niveau de détail. Pour résumer, le niveau de détail est envoyé aux unités de texture, qui s'occupent de calculer l'adresse de base et la résolution adéquates. Quelques calculs arithmétiques simples, donc, qui s'implémentent facilement avec quelques circuits. Mais une autre méthode laisse la carte graphique déterminer le niveau de détail par elle-même. Dans ce cas, cela demande, outre les deux coordonnées de texture, de calculer la dérivée de ces deux coordonnées dans le sens horizontal et vertical, ce qui fait quatre dérivées (deux dérivées horizontales, deux verticales). Les quatre dérivées sont les suivantes : : <math>\frac{du}{dx}</math>, <math>\frac{dv}{dx}</math>, <math>\frac{du}{dy}</math>, <math>\frac{dv}{dy}</math> Un bon moyen pour obtenir les dérivées demande de regrouper les pixels par groupes de 4 et de faire la différence entre leurs coordonnées de texture respectives. On peut calculer les deux dérivées horizontales en comparant les deux pixels sur la même ligne, et les deux dérivées verticales en comparant les deux pixels sur la même colonne. Mais cela demande de rastériser les pixels par groupes de 4, par ''quads''. Et c'est ce qui est fait sur les cartes graphiques actuelles, qui rastérisent des groupes de 4 pixels à la fois. [[File:Texture sampler unit with mipmapping.png|centre|vignette|upright=2.0|Unité de texture avec mipmapping.]] Malheureusement, le calcul exact utilisé pour le choix de la mip-map dépend du GPU considéré et peu de chose est connu quant à ces algorithmes. Il est possible d'inférer le comportement à partir d'observations, mais guère plus. Pour ceux qui veulent en savoir plus, je conseille la lecture de cet article de blog : * [https://pema.dev/2025/05/09/mipmaps-too-much-detail/ Mipmap selection in too much detail] ==Le filtrage de textures== Plaquer des textures sans autre forme de procès ne suffit pas à garantir des graphismes d'une qualité époustouflante. La raison est que les sommets et les texels ne tombent pas tout pile sur un pixel de l'écran : le sommet associé au texel peut être un petit peu trop en haut, ou trop à gauche, etc. Une explication plus concrète fait intervenir les coordonnées de texture. Souvenez-vous que lorsque l'on traduit une coordonnée de texture u,v en coordonnées x,y, on obtient un résultat qui ne tombe pas forcément juste. Souvent, le résultat a une partie fractionnaire. Si celle-ci est non-nulle, cela signifie que le texel/sommet n'est pas situé exactement sur le pixel voulu et que celui-ci est situé à une certaine distance. Concrètement, le pixel tombe entre quatre texels, comme indiqué ci-dessous. [[File:Filtrage texture.png|centre|vignette|upright=2.0|Position du pixel par rapport aux texels.]] Pour résoudre ce problème, on doit utiliser différentes techniques d'interpolation, aussi appelées techniques de '''filtrage de texture''', qui visent à calculer la couleur du pixel final en fonction des texels qui l'entourent. Il existe de nombreux types de filtrage de textures, qu'il s'agisse du filtrage linéaire, bilinéaire, trilinéaire, anisotropique et bien d'autres. Tous ont besoin d'avoir certaines informations qui sont généralement fournies par les circuits de calcul d'adresse. La première est clairement la partie fractionnaire des coordonnées x,y. La seconde est la dérivée de ces deux coordonnées dans le sens horizontal et vertical., ce qui fait quatre dérivées (deux dérivées horizontales, deux verticales). Toujours est-il que le filtrage de texture est une opération assez lourde, qui demande beaucoup de calculs arithmétiques. On pourrait en théorie le faire dans les pixels shaders, mais le cout en performance serait absolument insoutenable. Aussi, les cartes graphiques intègrent toutes un circuit dédié au filtrage de texture, le ''texture sampler''. Même les plus anciennes cartes graphiques incorporent une unité de filtrage de texture, ce qui nous montre à quel point cette opération est importante. [[File:Texture unit.png|centre|vignette|upright=2.0|Unité de texture.]] On peut configurer la carte graphique de manière à ce qu'elle fasse soit du filtrage bilinéaire, soit du filtrage trilinéaire, on peut configurer le niveau de filtrage anisotropique, etc. Cela peut se faire dans les options de la carte graphique, mais cela peut aussi être géré par l'application. La majorité des jeux vidéos permettent de régler cela dans les options. Ces réglages ne concernent pas la texture elle-même, mais plutôt la manière dont l'unité de texture doit fonctionner. Ces réglages sur l''''état de l'unité de texture''' sont mémorisés quelque part, soit dans l'unité de texture elle-même, soit fournies avec la ressource de texture elle-même, tout dépend de la carte graphique. Certaines cartes graphiques mémorisent ces réglages dans les unités de texture ou dans le processeur de commande, et tout changement demande alors de réinitialiser l'état des unités de texture, ce qui prend un peu de temps. D'autres placent ces réglages dans les ressources de texture elles-mêmes, ce qui rend les modifications de configuration plus rapides, mais demande plus de circuits. D'autres cartes graphiques mélangent les deux options, certains réglages étant globaux, d'autres transmis avec la texture. Bref, difficile de faire des généralités, tout dépend du matériel et le pilote de la carte graphique cache tout cela sous le tapis. Maintenant que cela est dit, voyons quelles sont les différentes méthodes de filtrage de texture et comment la carte graphique fait pour les calculer. ===Le filtrage au plus proche=== La méthode de filtrage la plus simple consiste à colorier avec le texel le plus proche. Cela revient tout simplement à ne pas tenir compte de la partie fractionnaire des coordonnées x,y, ce qui est très simple à implémenter en matériel. C'est ce que l'on appelle le '''filtrage au plus proche''', aussi appelé ''nearest filtering''. Autant être franc, le résultat est assez pixelisé et peu agréable à l’œil. Par contre, le résultat est très rapide à calculer, vu qu'il ne demande aucun calcul à proprement parler. Elle ne fait pas appel à la parti fractionnaire des coordonnées entières de texture, ni aux dérivées de ces coordonnées. On peut combiner cette technique avec le mip-mapping, ce qui donne un résultat bien meilleur, bien que loin d'être satisfaisant. Au passage, toutes les techniques de filtrage de texture peuvent se combiner avec du mip-mapping, certaines ne pouvant pas faire sans. [[File:Interpolation-nearest.svg|centre|vignette|Filtrage de texture au plus proche.]] ===Le filtrage linéaire=== Le filtrage le plus simple est le '''filtrage linéaire'''. Il effectue une interpolation linéaire entre deux mip-maps, deux niveaux de détails. Pour comprendre l'idée, nous allons prendre une situation très simple, avec une texture carrée de 512 texels de côté. Le mip-mapping crée plusieurs textures : une de 256 texels de côté, une de 128 texels, une de 64, etc. Maintenant, la texture est sur un objet à une certaine distance de l'écran, vu de face. Le résultat est qu'elle correspond à l'écran à un carré de 300 pixels de côté (pas d'erreur : pixels, pas texels). Dans ce cas, la texture se trouve entre deux mip-maps : celle de 512 pixels de côté, celle de 256. Laquelle choisir ? Le filtrage au plus proche prend la texture de 512 pixels de côté. Le filtrage linéaire lui, fait autrement. Vu que la texture est entre deux mip-maps, l'idée est de prendre le texel au plus proche dans chaque texture et de faire une sorte de moyenne appelée l'interpolation linéaire. L'interpolation par du principe que la couleur varie entre les deux texels en suivant une fonction affine, illustrée ci-dessous. Ce ne serait évidemment pas le cas dans le monde réel, mais on supposer cela donne une bonne approximation de ce à quoi ressemblerait une texture à plus haute résolution. On peut alors calculer la couleur du pixel par une simple moyenne pondérée par la distance. Le résultat est que les transitions entre deux niveaux de détails sont plus lisses, moins abruptes. [[File:Lin interp -é.png|centre|vignette|upright=2.0|Interpolation linéaire.]] ===Le filtrage bilinéaire=== Le filtrage bilinéaire effectue une sorte de moyenne pondérée des quatre texels les plus proches du pixel à afficher. Pour cela, rappelez-vous ce qui a été dit plus haut : les coordonnées x,y d'un pixel ont une partie entière et une partie fractionnaire. Le filtrage au plus proche élimine les parties fractionnaires, ce qui donne une coordonnée x,y. Avec le filtrage bilinéaire, on prend les texels de coordonnées (x,y) ; (x+1,y) ; (x,y+1) ; (x+1,y+1), le pixel étant entre ces 4 texels. Mais le filtrage ne fait pas qu'une simple moyenne, il prend en compte les parties fractionnaires pour faire la moyenne. En effet, le pixel n'est pas au milieu du carré de texel, il est quelque part mais est souvent plus proche d'un texel que des autres. Et il faut donc pondérer la moyenne par les distances aux 4 texels. Pour cela, la moyenne est calculée à partir d'interpolations linéaires. Avec 4 pixels, nous allons devoir calculer la couleur de deux points intermédiaires. La couleur de ces deux points se calcule par interpolation linéaire, et il suffit d'utiliser une troisième interpolation linéaire pour obtenir le résultat. [[File:Bilin3.png|centre|vignette|upright=2|Filtrage bilinéaire de texture.]] Le circuit qui permet de faire l'interpolation bilinéaire est particulièrement simple. On trouve un circuit de chaque pour chaque composante de couleur de chaque texel : un pour le rouge, un pour le vert, un pour le bleu, et un pour la transparence. Chacun de ces circuit est composé de sous-circuits chargés d'effectuer une interpolation linéaire, reliés comme suit. [[File:Texture sampler unit.png|centre|vignette|Unité de filtrage bilinéaire.]] Vous noterez que le filtrage bilinéaire accède à 4 pixels en même temps. Fort heureusement, les textures sont stockées de manière à ce qu'on puisse charger les 4 pixels en une fois, comme on l'a vu plus haut. Le filtrage bilinéaire a de fortes chances que les 4 pixels filtrés soient dans la même ''tile'', la seule exception étant quand ils sont tout juste sur le bord d'une ''tile''. : La console de jeu Nintendo 64 n'utilise que trois pixels au lieu de quatre dans son interpolation bilinéaire, qui en devient une interpolation quasi-bilinéaire. La raison derrière ce choix est une question de performances, comme beaucoup de décisions de ce genre. Le résultat est un rendu imparfait de certaines textures. ===Le filtrage trilinéaire=== Avec le filtrage bilinéaire, des discontinuités apparaissent sur certaines surfaces. Par exemple, pensez à une texture de sol : elle est appliquée plusieurs fois sur toute la surface du sol. A une certaine distance, le LOD utilisé change brutalement et passe par exemple de 512*512 à 256*256, ce qui est visible pour un joueur attentif. De telles transitions sont lissées grâce au filtrage linéaire, il n'y a plus qu'à le combiner avec le filtrage bilinéaire. Rien d’incompatible : le premier filtre l'intérieur d'une mip-map, le second combine deux mip-maps. Le filtrage trilinéaire prend les deux mip-maps les plus proches, fait un filtrage bilinéaire avec chacune, puis fait une « une moyenne » pondérée entre les deux résultats. Le circuit de filtrage trilinéaire existe en plusieurs versions. La plus simple, illustrée ci-dessous, effectue deux filtrages bilinéaires en parallèle, dans deux circuits séparés, puis combine leurs résultats avec un circuit d'interpolation linéaire. Mais ce circuit nécessite de charger 8 texels simultanément. Qui plus est, ces 8 texels ne sont pas consécutifs en mémoire, car ils sont dans deux niveaux de détails/mip-maps différents. [[File:Parallel trilinear filtering.png|centre|vignette|upright=2.0|Unité de filtrage trilinéaire parallèle.]] Vu qu'on lit des texels dans deux mip-maps, les texels sont lus en deux fois : 4 texels provenant de la première mip-map, suivis par les 4 texels de l'autre mip-map. Les 4 premiers texels doivent donc être mis en attente dans des registres, en attendant que les 4 autres arrivent. Une amélioration du circuit précédent gère cela en ajoutant des registres. Il lit les 4 premiers texels, les filtre avec une interpolation bilinéaire, et mémorise le résultat dans un registre. Puis, il lit les 4 autres texels, les filtre, et met le résultat dans un second registre. A ce moment là, un circuit d'interpolation linéaire finit le travail. On économise donc un circuit d'interpolation bilinéaire, sans que les performances soient trop impactées. [[File:Filtrage trilineaire.png|centre|vignette|upright=1.0|Unité de filtrage trilineaire série.]] Modifier le circuit de filtrage ne suffit pas. Comme je l'ai dit plus haut, la dernière étape d'interpolation linéaire utilise des coefficients, qui lui sont fournis par des registres. Seul problème : entre le temps où ceux-ci sont calculés par l'unité de mip-mapping, et le moment où les texels sont chargés depuis la mémoire, il se passe beaucoup de temps. Le problème, c'est que les unités de texture sont souvent pipelinées : elles peuvent démarrer une lecture de texture sans attendre que les précédentes soient terminées. À chaque cycle d'horloge, une nouvelle lecture de texels peut commencer. La mémoire vidéo est conçue pour supporter ce genre de chose. Cela a une conséquence : durant les 400 à 800 cycles d'attente entre le calcul des coefficients, et la disponibilité des texels, entre 400 et 800 coefficients sont produits : un par cycle. Autant vous dire que mémoriser 400 à 800 ensembles de coefficient prend beaucoup de registres. ===Le filtrage anisotrope=== D'autres artefacts peuvent survenir lors de l'application d'une texture, la perspective pouvant déformer les textures et entraîner l'apparition de flou. La raison à cela est que les techniques de filtrage de texture précédentes partent du principe que la texture est vue de face. Prenez une texture carrée, par exemple. Vue de face, elle ressemble à un carré sur l'écran. Mais tournez la caméra, de manière à voir la texture de biais, avec un angle, et vous verrez que la forme de la texture sur l'écran est un trapèze, pas un carré. Cette déformation liée à la perspective n'est pas prise en compte par les méthodes de filtrage de texture précédentes. Pour le dire autrement, les techniques de filtrage précédentes partent du principe que les 4 texels qui entourent un pixel forment un carré, ce qui est vrai si la texture est vue de face, sans angle, mais ne l'est pas si la texture n'est pas perpendiculaire à l'axe de la caméra. Du point de vue de la caméra, les 4 texels forment un trapèze d'autant moins proche d'un carré que l'angle est grand. Pour corriger cela, les chercheurs ont inventé le '''filtrage anisotrope'''. En fait, je devrais plutôt dire : LES filtrages anisotropes. Il en existe un grand nombre, dont certains ne sont pas utilisés dans les cartes graphiques actuelles, soit car ils trop gourmand en accès mémoires et en calculs pour être efficaces, soit car ils ne sont pas pratiques à mettre en œuvre. Il est très difficile de savoir quelles sont les techniques de filtrage de texture utilisées par les cartes graphiques, qu'elles soient récentes ou anciennes. Beaucoup de ces technologies sont brevetées ou gardées secrètes, et il faudrait vraiment creuser les brevets déposés par les fabricants de GPU pour en savoir plus. Les algorithmes en question seraient de plus difficiles à comprendre, les méthodes mathématiques cachées derrière ces méthodes de filtrage n'étant pas des plus simple. [[File:Anisotropic filtering en.png|centre|vignette|upright=2|Exemple de filtrage anisotrope.]] ==La compression de textures== Les textures les plus grosses peuvent aller jusqu'au mébioctet, ce qui est beaucoup. Pour limiter la casse, les textures sont compressées. La '''compression de texture''' réduit la taille des textures, ce qui peut se faire avec ou sans perte de qualité. Elle entraîne souvent une légère perte de qualité lors de la compression. Toutefois, cette perte peut être compensée en utilisant des textures à résolution plus grande. Mais il s'agit là d'une technique très simple, beaucoup plus simple que les techniques que nous allons voir dans cette section. Nous allons voir quelque algorithmes de compression de textures de complexité intermédiaire, mais n'allons pas voir l'état de l'art. Il existe des formats de texture plus récents que ceux qui nous allons aborder, comme l{{'}}''Ericsson Texture Compression'' ou l{{'}}''Adaptive Scalable Texture Compression'', plus complexes et plus efficaces. Notons que les textures sont compressées dans les fichiers du jeu, mais aussi en mémoire vidéo. Les textures sont décompressées lors de la lecture. Pour cela, la carte graphique contient alors un circuit, capable de décompresser les textures lorsqu'on les lit en mémoire vidéo. Les cartes graphiques supportent un grand nombre de formats de textures, au niveau du circuit de décompression. Du fait que les textures sont décompressées à la volée, les techniques de compression utilisées sont assez particulières. La carte graphique ne peut pas décompresser une texture entière avant de pouvoir l'utiliser dans un ''pixel shader''. A la place, on doit pouvoir lire un morceau de texture, et le décompresser à la volée. On ne peut utiliser les méthodes de compression du JPEG, ou d'autres formats de compression d'image. Ces dernières ne permettent pas de décompresser une image morceau par morceau. Pour permettre une décompression/compression à la volée, les textures sont des textures tilées, généralement découpées en tiles de 4 * 4 texels. Les ''tiles'' sont compressées indépendamment les unes des autres. Et surtout, avec ou sans compression, la position des tiles en mémoire ne change pas. On trouve toujours une tile tous les T octets, peu importe que la tile soit compressée ou non. Par contre, une tile compressée n'occupera pas T octets, mais moins, là où une tile compressée occupera la totalité des T octets. En clair, compresser une tile fait qu'il y a des vides entre deux tiles dans al mémoire vidéo, mais ne change rien à leur place en mémoire vidéo qui est prédéterminée, peu importe que la texture soit compressée ou non. L'intérêt de la compression de textures n'est pas de réduire la taille de la texture en mémoire vidéo, mais de réduire la quantité de données à lire/écrire en mémoire vidéo. Au lieu de lire T octets pour une tile non-compressée, on pourra en lire moins. ===La palette indicée et la technique de ''Vector quantization''=== La technique de compression des textures la plus simple est celle de la '''palette indicée''', que l'on a entraperçue dans le chapitre sur les cartes d'affichage. La technique de '''''vector quantization''''' peut être vue comme une amélioration de la palette, qui travaille non pas sur des texels, mais sur des ''tiles''. À l'intérieur de la carte graphique, on trouve une table qui stocke toutes les ''tiles'' possibles. Chaque ''tile'' se voit attribuer un numéro, et la texture sera composé d'une suite de ces numéros. Quelques anciennes cartes graphiques ATI, ainsi que quelques cartes utilisées dans l’embarqué utilisent ce genre de compression. ===Les algorithmes de ''Block Truncation coding''=== La première technique de compression élaborée est celle du '''''Block Truncation Coding''''', qui ne marche que pour les images en niveaux de gris. Le BTC ne mémorise que deux niveaux de gris par ''tile'', que nous appellerons couleur 1 et couleur 2, les deux niveaux de gris n'étant pas le même d'une ''tile'' à l'autre. Chaque pixel d'une ''tile'' est obligatoirement colorié avec un de ces niveaux de gris. Pour chaque pixel d'une ''tile'', on mémorise sa couleur avec un bit : 0 pour couleur 1, et 1 pour couleur 2. Chaque ''tile'' est donc codée par deux entiers, qui codent chacun un niveau de gris, et une suite de bits pour les pixels proprement dit. Le circuit de décompression est alors vraiment très simple, comme illustré ci-dessous. [[File:Block Truncation coding.jpg|centre|vignette|upright=2.0|Block Truncation coding.]] La technique du BTC peut être appliquée non pas du des niveaux de gris, mais pour chaque composante Rouge, Vert et Bleu. Dans ces conditions, chaque ''tile'' est séparée en trois sous-''tiles'' : un sous-bloc pour la composante verte, un autre pour le rouge, et un dernier pour le bleu. Cela prend donc trois fois plus de place en mémoire que le BTC pur, mais cela permet de gérer les images couleur. ===Le format de compression S3TC / DXTC=== L'algorithme de '''Color Cell Compression''', ou CCC, améliore le BTC pour qu'il gère des couleurs autre que des niveaux de gris. Ce CCC remplace les deux niveaux de gris par deux couleurs. Une ''tile'' est donc codée avec un entier 32 bits par couleur, et une suite de bits pour les pixels. Le circuit de décompression est identique à celui utilisé pour le BTC. [[File:Color Cell Compression.jpg|centre|vignette|Color Cell Compression.]] [[File:Dxt1-memory-layout.png|vignette|Dxt1 et ''color cell compression''.]] Le format de compression de texture utilisé de base par Direct X, le DXTC, est une version amliorée de l'algorithme précédent. Il est décliné en plusieurs versions : DXTC1, DXTC2, etc. La première version du DXTC est une sorte d'amélioration du CCC : il ajoute une gestion minimale de transparence, et découpe la texture à compresser en ''tiles'' de 4 pixels de côté. La différence, c'est que la couleur finale d'un texel est un mélange des deux couleurs attribuée au bloc. Pour indiquer comment faire ce mélange, on trouve deux bits de contrôle par texel. Si jamais la couleur 1 < couleur2, ces deux bits sont à interpréter comme suit : * 00 = Couleur1 * 01 = Couleur2 * 10 = (2 * Couleur1 + Couleur2) / 3 * 11 = (Couleur1 + 2 * Couleur2) / 3 Sinon, les deux bits sont à interpréter comme suit : * 00 = Couleur1 * 01 = Couleur2 * 10 = (Couleur1 + Couleur2) / 2 * 11 = Transparent [[File:DXTC.jpg|centre|vignette|DXTC.]] Le circuit de décompression du DXTC ressemble alors à ceci : [[File:Circuit de décompression du DXTC.jpg|centre|vignette|upright=2.0|Circuit de décompression du DXTC.]] ===Les format DXTC 2, 3, 4 et 5 : l'ajout de la transparence=== Pour combler les limitations du DXT1, le format DXT2 a fait son apparition. Il a rapidement été remplacé par le DXT3, lui-même replacé par le DXT4 et par le DXT5. Dans le DXT3, la transparence fait son apparition. Pour cela, on ajoute 64 bits par ''tile'' pour stocker des informations de transparence : 4 bits par texel. Le tout est suivi d'un bloc de 64 bits identique au bloc du DXT1. [[File:Dxt23-memory-layout.png|centre|vignette|Dxt 2 et 3.]] Dans le DXT4 et le DXT5, la méthode utilisée pour compresser les couleurs l'est aussi pour les valeurs de transparence. L'information de transparence est stockée par un en-tête contenant deux valeurs de transparence, le tout suivi d'une matrice qui attribue trois bits à chaque texel. En fonction de la valeur des trois bits, les deux valeurs de transparence sont combinées pour donner la valeur de transparence finale. Le tout est suivi d'un bloc de 64 bits identique à celui qu'on trouve dans le DXT1. [[File:Dxt45-memory-layout.png|centre|vignette|Dxt 4 et 5.]] ===Le format de compression PVRTC=== Passons maintenant à un format de compression de texture un peu moins connu, mais pourtant omniprésent dans notre vie quotidienne : le PVRTC. Ce format de texture est utilisé notamment dans les cartes graphiques de marque PowerVR. Vous ne connaissez peut-être pas cette marque, et c'est normal : elle travaille surtout dans les cartes graphiques embarquées. Ses cartes se trouvent notamment dans l'ipad, l'iPhone, et bien d'autres smartphones actuels. Avec le PVRTC, les textures sont encore une fois découpées en ''tiles'' de 4 texels par 4, mais la ressemblance avec le DXTC s’arrête là. Chacque ''tile'' est codée avec : * une couleur codée sur 16 bits ; * une couleur codée sur 15 bits ; * 32 bits qui servent à indiquer comment mélanger les deux couleurs ; * et un bit de modulation, qui permet de configurer l’interprétation des bits de mélange. Les 32 bits qui indiquent comment mélanger les couleurs sont une collection de 2 paquets de 2 bits. Chacun de ces deux bits permet de préciser comment calculer la couleur d'un texel du bloc de 4*4. ==Annexe : le ''normal-mapping'' hardware== [[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]] Maintenant, parlons un peu du ''normal mapping'' et de son implémentation dans les unités de texture. Pour rappel, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser. Et pour comprendre quel est le rapport avec les textures, nous allons devoir faire quelques rappels sur l'éclairage par pixel. [[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]] L'éclairage se fait en utilisant de nombreux calculs, qu'on a détaillé dans le chapitre sur les bases du rendu 3D. Ceux-ci utilisent la normale d'un sommet, à savoir un vecteur orienté à la verticale de la surface. En théorie, il y a une normale par sommet, ce qui fait que les calculs d'éclairage doivent se faire au niveau géométrique, avant la rastérisation. Mais pour obtenir un éclairage de meilleure qualité, il y a des techniques qui calcule l'éclairage d'une scène 3D pixel par pixel. Une première technique d'éclairage par pixel interpole les normales lors de l'étape de rastérisation. On obtient alors un éclairage de Phong. Une autre solution est celle du ''normal mapping''. Le '''''normal mapping''''' précalcule les normales d'une surface dans une texture, appelée la ''normal map''. L'éclairage est alors réalisé par un pixel shader, qui lit les normales depuis cette texture et fait les calculs d'éclairage avec. ===L'usage de textures non-compressées pour les ''normal maps''=== La ''normal map'' est une texture, donc. Mais ce n'est pas une texture comme une autre. Elle mémorise un vecteur pour chaque texel, pas une couleur. Il est possible d'utiliser les formats de textures non-compressés, même si cela ne donne pas de bons résultats. Après tout, un vecteur est codé par trois coordonnées x,y,z, il est possible de coder chacune avec un octet et de packager cela dans une texture RGB classique. La coordonnée x va dans la composante Rouge, la coordonnée Y dans la composante Bleu, et la coordonnée z dans la composante z. Une première optimisation est de ne pas mémoriser les trois coordonnées, mais seulement deux d'entre elles et de calculer la troisième. En effet, les normales sont des vecteurs ''normalisés'', c'est à dire que leur longueur vaut 1, par construction. Vu que la taille est connue à l'avance, on peut en déduire la coordonnée z à partir des coordonnées x et y, avec l'usage du théorème de Pythagore. Par définition, <math>z^2 = 1 - x^2 - y^2</math>. Le calcul peut être fait dans le ''pixel shader'' sans problème. : Pour être précis, il faut que les normales soient définies d'une certaine manière pour que ça marche. Les normales ne sont pas définies dans le même système de coordonnées que le modèle 3D, mais dans un autre système appelé l'''espace tangent''. Notons que l'accès à la ''normal map'' se fait comme pour n'importe quelle texture : les texels sont lus en mémoire vidéo, puis le filtrage de texture est appliqué, et enfin le tout est envoyé au ''pixel shader''. En théorie, le filtrage effectue une sorte d'interpolation des normales automatique, ce qui permet de trouver la normale à un pixel précis, même s'il n'est pas sur un texel. Les solutions précédentes demandent d'utiliser des textures non-compressées, qui utilisent beaucoup de mémoire vidéo. Utiliser des textures compressées pourrait sembler résoudre le problème. Mais l'usage de textures compressées marche très mal pour les ''normal maps'', elles ne permettent pas d'obtenir une qualité ou une compression suffisante. Aussi, des formats de texture dédiés aux ''normal maps'' ont été inventés. ===Le format de texture 3Dc=== Le problème est que le rendu final n'est pas très beau. Et les opérations de filtrage ne donnent pas de très bons résultats. Pour remédier à ces problèmes, des formats de texture spécialisés pour les ''normal map'' ont été inventés. Le premier d'entre eux est le '''3Dc''', aussi appelé BC5 pour ''Block Compression 5''. Il utilise un octet par texel, au lieu de trois avec une texture RGB normale non-compressée. Et surtout : il est géré en matériel, directement dans l'unité de texture, qui peut décompresser les textures 3Dc et les filtrer ! Avec le 3Dc, seules coordonnées sont mémorisées, la troisième est calculée, comme dit plus haut. Pour le reste, la compression de la ''normal map'' ressemble un peu à celle des textures. La ''normal map'' est découpée en blocs de 4 par 4 texels de côté, chacune étant encodée en tenant compte de certaines redondances. Dans un bloc, les coordonnées x sont dans un certain intervalle, allant de <math>x_{min}</math> à <math>x_{max}</math>, idem pour la coordonnée y qui est dans l'intervalle <math>y_{min}</math>, <math>y_{max}</math>. L'intervalle <math>x_{min}</math> à <math>x_{max}</math> est coupé en 8 parts égales, ce qui fait 8 valeurs possibles dans cet intervalle, chacune correspondant à une valeur x possible pour une normale du bloc. La normale est donc encodée en précisant quelle valeur est la bonne, en utilisant un indice de 3 bits pour cela. La valeur exacte de la coordonnée x se calcule à partir de l'indice comme suit : : <math>x = x_{min} + \left( \frac{x_{min} - x_{min}}{8} \right) \times \text{indice} </math> Pour encoder un bloc de 4 par 4 normales, il faut donc : * Un octet pour chaque coordonnée <math>x_{min}</math>, <math>x_{max}</math>, <math>y_{min}</math>, <math>y_{max}</math>. * 6 bits par normale : 3 pour encoder le décalage de la coordonnée x, 3 pour la coordonnée y. Le tout permet d'encoder un bloc de 16 normales en seulement 128 bits, soit un octet par normale. Le ''pixel shader'' se débrouille pour décompresser un bloc. Il effectue notamment le calcul du dessus, pour retrouver la coordonnée x. Les calculs étant particulièrement simples, le cout en performance est très faible. Et ce d'autant plus qu'ils demandent juste de faire des additions et des multiplications entières, que l'unité scalaire peut faire en parallèle d'autres opérations plus gourmandes. Et le cout en calculs est de toute façon compensé par l'économie de mémoire et de bande passante lié à la compression : diviser la taille des données par trois, ca a un sacré impact ! ==Annexe : les ''shadowmap'' hardware== Les anciens GPU, notamment la Geforce FX, avaient des fonctionnalités spécifiques pour le calcul des ombres. Dans la plupart des jeux vidéos de l'époque, et même de maintenant, les ombres sont calculées avec la technique des ''shadowmap''. L'idée est assez simple sur le principe : un pixel est dans l'ombre quand il est invisible depuis une source de lumière. L'idée est que le rendu est réalisé en plusieurs passes, avec une passe par source de lumière et une passe finale pour calculer l'image finale. Nous allons expliquer la technique avec une seule source de lumière, et allons utiliser l'exemple de la scène ci-dessous. [[File:7fin.png|centre|vignette|Scène 3D d'exemple.]] ===La technique du ''shadowmapping''=== [[File:2shadowmap.png|vignette|Résultat de la première passe : ''shadowmap''..]] La première passe rend l'image depuis le point de vue de la source de lumière. Cette première passe ne rend pas les couleurs de la scène, elle ne s'intéresse qu'à la profondeur des pixels. Le résultat est que l'image ne rend que le tampon de profondeur. Celui-ci est ensuite réutilisé comme texture pour la passe suivante. La texture en question est appelée la '''''shadownmap'''''. La perspective utilisée, ainsi que le ''view frustrum'', dépend de la source de lumière. Pour une source de lumière qui émet un cône de lumière, le ''view frustrum'' de l'image rendue doit contenir tout le cone de lumière, et doit coller le plus possible à celui-ci. Pour une source directionnelle, comme le soleil, une perspective orthographique est utilisée. La seconde passe rend l'image du point de vue de la caméra, pour rendre l'image finale. Elle rend l'image finale, qui est composée de pixels, chacun ayant une position à l'écran x,y, et une profondeur z. Les coordonnées sont transformées pour obtenir la position de ce pixel depuis le point de vue de la caméra. Une simple multiplication de matrice suffit, rien de bien compliqué, un shader peut le faire. [[File:5failed.png|vignette|Résultat du test des comparaisons.]] Après cette étape, on a alors les coordonnées x,y,z de ce pixel du point de vue de la caméra, et la ''shadowmap''. Il est alors possible d'accéder à la ''shadowmap'' au même endroit, à la même place que le pixel testé, aux mêmes coordonnées x,y. Si la profondeur du pixel est supérieure à celle de la shadowmap au même endroit, alors le pixel est situé derrière la surface visible, donc est dans l'ombre. Sinon, il n'est pas dans l'ombre. Le même procédé est répété sur chaque pixel de l'écran. ===Les optimisations hardware du ''shadowmapping''=== La technique des ''shadowmap'' demande donc de calculer une texture ''shadowmap'', puis de lire celle-ci et de faire des comparaisons de profondeur. Les GPU comme la Geforce FX intégraient du matériel dans les unités de texture pour faciliter ce travail. Les unités de texture pouvaient lire les ''shadowmap'', et faire la comparaison de profondeur toutes seules, elles avaient des circuits pour. Il suffisait de leur fournir le pixel à tester, ses coordonnées x,y,z, et l'adresse de la ''shadowmap''. Les unités de texture renvoyaient alors un résultat valant 0 ou 1 : 1 si le pixel est dans l'ombre, 0 sinon. Elles pouvaient même effectuer du filtrage de texture sur les ''shadowmap''. Mais le filtrage était différent de celui utilisé sur les autres textures : moyenner des valeurs de profondeur ne marche pas bien. Elles utilisaient des techniques de filtrage différentes : elles faisaient les tests de comparaison, puis faisaient la moyenne des résultats. Ainsi, pour du filtrage bilinéaire, elles lisaient 4 texels dans la ''shadowmap'', puis faisaient 4 tests de comparaison, et moyennaient les 4 résultats. ==Annexe : le cube-mapping== [[File:Cube mapped reflection example 2.JPG|vignette|Exemple de reflets environnementaux.]] L''''environnement-mapping''' simule les réflexions et autres effets graphiques sur une surface ou un objet 3D. L'idée est de plaquer une texture pré-calculée pour simuler l'effet de l'environnement sur un modèle 3D. Il en existe plusieurs versions différentes, mais la seule utilisée de nos jours est le ''cube-mapping'', où la texture de l'environnement est plaquée sur un cube, d'où son nom. Le cube en question est utilisé différemment suivant ce que l'on cherche à faire avec le ''cube-mapping''. ===Les utilisations du ''cubemapping''=== Les deux utilisations principales sont le rendu du ciel et des décors, et les réflexions sur la surface des objets. Dans les deux cas, l'idée est de précalculer ce que l'on voit du point de vue de la caméra. On place la caméra dans la scène 3D, on place un cube centré sur la caméra, le cube est texturé avec ce que l'on voit de l'environnement depuis la caméra/l'objet de son point de vue. [[File:Panorama cube map.png|centre|vignette|upright=2|L'illustration montre en premier lieu une ''cubemap'' avec les six faces mises en évidence, puis quel environnement 3D elle permet de simuler, le troisième illustration montrant comment la ''cubemap'' est utilisée pour simuler l'environnement.]] Le rendu du ciel et des décors lointains dans les jeux vidéo se base sur des '''''skybox''''', à savoir un cube centré sur la caméra, sur lequel on ajoute des textures de ciel ou de décors lointains. Le cube est recouvert par une texture, qui correspond à ce que l'on voit quand on dirige le regard de la caméra vers cette face. Contrairement à ce qu'on pourrait croire, la skybox n'est pas les limites de la scène 3D, les limites du niveau d'un jeu vidéo ou quoique ce soit d'autre de lié à la physique de la scène 3D. La skybox est centrée sur la caméra, elle suit la caméra dans son mouvement. Centrer la skybox sur la caméra permet de simuler des décors très lointains, suffisamment lointain pour qu'on n'ait pas l'illusion de s'en rapprocher en se déplaçant dans la map. De plus, cela évite d'avoir à faire trop de calculs à chaque fois que l'on bouge la caméra. La texture plaquée sur le cube est une texture unique, elle-même découpée en six sous-textures, une par face du cube. [[File:Skybox example.png|centre|vignette|upright=2|Exemple de Skybox.]] [[File:Cube mapped reflection example.jpg|vignette|Réflexions calculées par une ''cubemap''.]] Le ''cube-mapping'' est aussi utilisé pour des reflets. L'idée est de simuler les reflets en plaquant une texture pré-calculée sur l'objet réflecteur. La texture pré-calculée est un dessin de l'environnement qui se reflète sur l'objet, un dessin du reflet à afficher. En la plaquant la texture sur l'objet, on simule ainsi des reflets de l'environnement, mais on ne peut pas calculer d'autres reflets comme les reflets objets mobiles comme les personnages. Et il se trouve que la texture pré-calculée est une ''cubemap''. Pour les environnements ouverts, c'est la ''skybox'' qui est utilisée, ce qui permet de simuler les reflets dans les flaques d'eau ou dans des lacs/océans/autres. Pour les environnements intérieurs, c'est une cubemap spécifique qui utilisée. Par exemple, pour l'intérieur d'une maison, on a une ''cubemap'' par pièce de la maison. Les reflets se calculent en précisant quelle ''cubemap'' appliquer sur l'objet en fonction de la direction du regard. [[File:Cube map level.png|centre|vignette|Cube map de l'intérieur d'une pièce d'un niveau de jeux vidéo.]] ===L'implémentation matérielle du ''cubemapping''=== Toujours est-il que les textures utilisées pour le ''cubemapping'', appelées des ''cubemaps'', sont en réalité la concaténation de six textures différentes. En mémoire vidéo, la ''cubemap'' est stockée comme six textures les unes à la suite des autres. Lors du rendu, on doit préciser quelle face du cube utiliser, ce qui fait 6 possibilités. On a le même problème qu'avec les niveaux de détail, sauf que ce sont les faces d'une ''cubemap'' qui remplacent les textures de niveaux de détails. L'accès en mémoire doit donc préciser quelle portion de la ''cubemap'' il faut accéder. Et l'accès mémoire se complexifie donc. Surtout que l'accès en question varie beaucoup suivant l'API graphique utilisée, et donc suivant la carte graphique. Le support des ''cubemaps'' dépend de l'API 3D, et surtout de si elle date ou si elle est récente : * Les API 3D très anciennes ne gérent pas nativement les ''cubemaps'', qui doivent être émulées en logiciel en utilisant six textures différentes. Le pixel shader décide donc quelle ''cubemap'' utiliser, avec quelques calculs sur la direction du regard. * Les API 3D récentes gèrent nativement les ''cubemaps''. Pour les versions les plus vielles de ces API, les six faces sont numérotées et l'accès à une ''cubemap'' précise quel face utiliser en donnant son numéro. La carte graphique choisit alors automatiquement la bonne texture. Mais cela demande de laisser le calcul de la bonne face au pixel shader. * Dans les API 3D modenres, les ''cubemap'' sont gérées comme des textures en trois dimensions, adressées avec trois coordonnées u,v,w. La carte graphique utilise ces trois coordonnées de manière à en déduire quelle est la face pertinente, mais aussi les coordonnées u,v dans la texture de la face. ==Annexe : les textures virtuelles== Les '''textures virtuelles''' sont une optimisation des textures normales, qui visent à accélérer le rendu de terrains de grande taille. Imaginez par exemple un monde assez ouvert, comme un environnement en forêt ou en montagne, avec une grande distance de visibilité. Avec de tels terrains, le "sol" est recouvert par une texture de sol unique qui recouvre tout le terrain. Elle ne se répète pas, est de très grande taille, et peut parfois recouvrir toute la map ! Mais il n'y a pas assez de mémoire vidéo pour mémoriser la texture toute entière. La seule solution est la suivante : une partie de la texture est placée en mémoire vidéo, le reste est soit placé en mémoire RAM ou sur le disque dur. Pour cela, le moteur de jeu utilise une optimisation ingénieuse, basée sur une observation assez basique : une bonne partie de la texture est visible, mais le reste est caché par des arbres, des habitations ou d'autres obstacles. Une optimisation possible de ne garder en mémoire vidéo que les portions visibles de la texture, pas les portions cachées. Une autre optimisation mélange textures virtuelles et ''mip-mapping''. L'idée est que pour les portions lointaines d'une texture, la texture utilisée est une ''mip-map'' de basse résolution. L'idée est alors de ne charger que la ''mip-map'' adéquate, pas les autres niveaux de détail. En clair, la texture de base n'est pas chargée en mémoire vidéo, mais la ''mip-map'' basse résolution l'est. ===Une texture à deux niveaux=== L'implémentation des textures virtuelles découpe les méga-textures en ''tiles'', en morceaux rectangulaires de taille modeste. En clair, le terrain est découpé en morceau rectangulaires/carrés. Seules les tiles nécessaires sont chargées en mémoire vidéo, pas les autres. Par exemple, les ''tiles'' non-visibles ne sont pas placées en mémoire vidéo, seules les ''tiles'' visibles le sont. De même, il y a une ''tile'' par niveau de mip-map : seul la tile correspondant le niveau adéquat est en mémoire vidéo, les autres niveaux de détail ne sont pas chargés. On peut faire une analogie avec la mémoire virtuelle, où les données sont découpées en pages, qui sont chargées en mémoire RAM à la demande, suivant les besoins, les données pouvant être swappées sur le disque dur si elles sont peu utilisées. Sauf qu'ici, il s'agit de textures qui sont découpées en pages chargées à la demande en mémoire vidéo, depuis la RAM système. Une texture virtuelle est en réalité un système à deux niveaux : une liste de ''tiles'' et les ''tiles'' elles-mêmes. La liste de ''tiles'' est appelée un '''atlas de texture''', c'est un peu l'équivalent de la ''tilemap'' pour le rendu 2D. Rendre une texture demande de calculer quelle ''tile'' contient le texel à afficher, consulter la ''tile'' en question, puis récupérer le texel adéquat dans cette ''tile''. La ''tile'' est donc une texture, mais la texture à charger est choisie parmi un ensemble, qui est ici l'atlas de texture. ===L'implémentation : logicielle versus matérielle=== Les textures virtuelles ont été utilisées pour la première fois par les jeux Rage 1 et 2 d'IdSoftware, et quelques jeux ultérieurs comme DOOM 2016. IdSoftware les appelait des '''''mega-textures'''''. L'optimisation permettait des gains en performance assez impressionnants. Le jeu Rage 1 utilisait une texture carrée unique de 128k pixels de côté pour rendre le terrain. En théorie, une telle texture devrait prendre 64 giga-octets, mais le jeu tournait correctement avec 512 méga-octets de RAM, poussivement avec seulement 256 méga-octets de RAM. De nos jours, les textures virtuelles sont supportées par beaucoup de jeux vidéos, les moteurs les plus courants gèrent de telles textures de manière logicielles. Mais quelques GPU récents supportent les textures virtuelles. Sur les GPU récents, l'atlas de texture est géré nativement par le matériel. Le GPU choisit quelle ''tile'', quelle texture choisir pour rendre le texel adéquat. Pour cela, le GPU calcule quelle ''tile'' charger, consulte l'atlas de texture, et lit la texture de ''tile'' adéquate. Mais l'implémentation sur les GPU récents a de nombreuses limitations. La limitation la plus importante est que la taille des textures virtuelles ne peut pas dépasser la taille d'une texture normale, soit 32768 pixels de côté pour une texture carrée environ sur les GPU de 2020. De plus, le chargement d'une ''tile'' est très lent. En clair, dès qu'on veut changer de niveau de mip-map pour une tile, ou dès qu'une tile devient visible, le chargement de la tile peut facilement prendre plusieurs centaines de millisecondes. Le filtrage de texture est très complexe avec des textures virtuelles, ce qui fait que le filtrage de texture virtuelle est souvent soumis à des limitations que les textures normales n'ont pas, notamment pour le filtrage anisotropique. {{NavChapitre | book=Les cartes graphiques | prev=Le rasterizeur | prevText=Le rasterizeur | next=Les Render Output Target | nextText=Les Render Output Target }}{{autocat}} jnpsvzv00djq3n0h2ius7k8c5zdrtxm 763944 763941 2026-04-18T13:42:45Z Mewtow 31375 /* Les techniques de rendu à textures multiples */ 763944 wikitext text/x-wiki [[File:Texture mapping.png|vignette|''Texture mapping'']] Les '''textures''' sont des images que l'on va plaquer sur la surface d'un objet, du papier peint en quelque sorte. Les cartes graphiques supportent divers formats de textures, qui indiquent comment les pixels de l'image sont stockés en mémoire : RGB, RGBA, niveaux de gris, etc. Une texture est donc composée de "pixels", comme toute image numérique. Pour bien faire la différence entre les pixels d'une texture, et les pixels de l'écran, les pixels d'une texture sont couramment appelés des ''texels''. ==Le placage de textures inverse== Pour rappel, plaquer une texture sur un objet consiste à attribuer un texel à chaque sommet, ce qui est fait lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet. Chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. Dans les faits, on n'utilise pas de coordonnées entières de ce type. Les coordonnées de texture sont deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale. Le nom donnée à cette technique de description des coordonnées de texture s'appelle l''''''UV Mapping'''''. [[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]] Les API 3D modernes gèrent des textures en trois dimensions, ce qui ajoute une troisième coordonnée de texture notée w. Dans ce qui va suivre, nous allons passer les textures en trois dimensions sous silence. Elles ne sont pas très utilisées, la quasi-totalité des jeux vidéo et applications 3D utilisant des textures en deux dimensions. Par contre, le matériel doit gérer les textures 3D, ce qui le rend plus complexe que prévu. Il faut ajouter quelques circuits pour, de quoi gérer la troisième coordonnée de texture, etc. Lors de la rastérisation, chaque fragment se voit attribuer un sommet, et donc la coordonnée de texture qui va avec. Si un pixel est situé pile sur un sommet, la coordonnée de texture de ce sommet est attribuée au pixel. Si ce n'est pas le cas, la coordonnée de texture finale est interpolée à partir des coordonnées des trois sommets du triangle rastérisé. L'interpolation en question a lieu dans l'étape de rastérisation, comme nous l'avons vu dans le chapitre précédent. Le fait qu'il y ait une interpolation fait que les coordonnées du pixel gagent à être des nombres flottants. On pourrait faire une interpolation avec des coordonnées de texture entières, mais les arrondis et autres imprécisions de calcul donneraient un résultat graphiquement pas terrible, et empêcheraient d'utiliser les techniques de filtrage de texture que nous verrons dans ce chapitre. À partir de ces coordonnées de texture, la carte graphique calcule l'adresse du texel qui correspond, et se charge de le lire. Et toute la magie a lieu dans ce calcul d'adresse, qui part de coordonnées de texture flottante, pour arriver à une adresse mémoire. Le calcul de l'adresse du texel se fait en plusieurs étapes, que nous allons voir ci-dessous. La première étape convertit les coordonnées flottantes en coordonnées entières, qui disent à quel ligne et colonne se trouve le texel voulu dans la texture. L'étape suivante transforme ces coordonnées x,y entières en adresse mémoire. ===La normalisation des coordonnées=== J'ai dit plus haut que les coordonnées de texture sont des coordonnées flottantes, comprises entre 0 et 1. Mais il faut savoir que les pixels shaders peuvent modifier celles-ci pour mettre en œuvre certains effets graphiques. Et le résultat peut alors se retrouver en-dehors de l'intervalle 0,1. C'est quelque chose de voulu et qui est traité par la carte graphique automatiquement, sans que ce soit une erreur. Au contraire, la manière dont la carte graphique traite cette situation permet d'implémenter des effets graphiques comme des textures en damier ou en miroir. [[File:Clamp tile.jpg|vignette|Clamp tile]] Il existe globalement trois méthodes très simples pour gérer cette situation, qui sont appelés des '''modes d'adressage de texture'''. La première méthode est de faire en sorte que le résultat sature. Si une coordonnée est inférieur à 0, alors on la remplace par un zéro. Si elle est supérieure à 1, on la ramène à 1. Avec cette méthode, tout se passe comme si les bords de la texture étaient étendus et remplissaient tout l'espace autour de la texture. Le tout est illustré ci-dessous. Ce mode d'accès aux textures est appelé le '''''clamp'''''. Une autre solution retire la partie entière de la coordonnée, elle coupe tout ce qui dépasse 1. Pour le dire autrement, elle calcule le résultat modulo 1 de la coordonnée. Le résultat est que tout se passe comme si la texture était répétée à l'infini et qu'elle pavait le plan. Une autre méthode remplit les coordonnées qui sortent de l’intervalle 0,1 avec une couleur préétablie, configurée par le programmeur. ===La conversion des coordonnées de textures flottantes en adresse mémoire=== Une fois la normalisation effectuée, les coordonnées de texture sont utilisées pour lire le texel voulu. Pour cela, les coordonnées de texte sont transformées en adresse mémoire, adresse qui pointe sur le texel ayant ces cordonnées. Pour cela, la première étape est de transformer les coordonnées flottantes u,v en coordonnées entières x,y qui pointent sur un texel. Pour cela, il suffit de multiplier les coordonnées flottantes u,v par la résolution de la texture accédée. Pour un écran de résolution <math>\text{height,width}</math>, le calcul est le suivant : : <math>x = u \times \text{width}</math> : <math>y = v \times \text{height}</math> Le résultat est un nombre avec une partie entière et une partie fractionnaire. La partie entière des deux coordonnées donne la position x,y voulue, et la partie fractionnaire est conservée pour le filtrage de textures, mais passons cela sous silence pour le moment. La seconde étape prend les coordonnées entières x,y et calcule l'adresse mémoire du texel. L'adresse dépend de la position de la texture en mémoire, précisément de son début, son premier texel, mais aussi de la position du texel par rapport au début de la texture. Et calculer cette position intra-texture dépend de la manière dont les texels sont stockés en mémoire. ====Les textures naïves==== Les programmeurs qui lisent ce cours s'attendent certainement à ce que la texture soit stockée en mémoire ligne par ligne, ou colonne par colonne. Cela veut dire que le premier pixel en partant d'en haut à gauche est stocké en premier, puis celui immédiatement à sa droite, puis celui encore à droite, et ainsi de suite. Une fois qu'on arrive à la fin d'une ligne, on passe à la ligne suivante, en-dessous. Cette organisation ligne par ligne s'appele l'organisation '''''row major order'''''. On peut faire pareil, mais colonne par colonne, ce qui donne le '''''column major order'''''. [[File:Speicheranordnung Feld.svg|centre|vignette|upright=2|Row et column major order.]] Maintenant, supposons que la texture commence à l'adresse <math>A_\text{texture}</math>, qui est l'adresse du premier texel. La texture a une résolution de <math>\text{width}</math> texels de large et <math>\text{height}</math> texels de haut. Par définition, les coordonnées X et Y des texels commencent à 0, ce qui fait que le pixel en haut à gauche a les coordonnées 0,0. L'adresse du pixel se calcule comme suit : : <math>A_\text{pixel} = A_\text{texture} + (\text{taille d'une ligne en octets} \times Y) + (\text{taille d'un texel en octets} \times X)</math> La taille d'un pixel en mémoire est notée T. La taille d'une ligne en mémoire est de <math>width \times T</math>, par définition, vu qu'elle fait <math>width</math> texels. On a donc : : <math>A_\text{pixel} = A_\text{texture} + (width \times T \times Y) + (T \times X)</math> La formule se réécrit comme suit : : <math>A_\text{pixel} = A_\text{texture} + T \times (width \times Y + X)</math> Le calcul d'adresse est donc assez simple. Malheureusement, les textures ne sont pas stockées de cette manière en mémoire vidéo. En effet, elle se marie mal avec les opérations de filtrage de texture que nous allons voir dans ce qui suit. Le filtrage d'un texel dépend de ses voisins du dessus et du dessous. Le fait que la texture n'est pas forcément parcourue ligne par ligne fait que stocker une texture ligne par ligne n'est pas l'idéal. De même, les textures sont déformées par la perspective. L'affichage de la texture ne se fait alors pas ligne par ligne, mais en parcourant la texture en diagonale, l'angle de la diagonale correspondant approximativement à l'angle que fait la verticale de la texture avec le regard. Vu qu'on ne connait pas à l'avance l'angle que fera la diagonale de parcours, on doit ruser. ====Les textures tilées==== Une première solution à ce problème est celle des '''textures tilées'''. Avec ces textures, l'image de la texture est découpée en ''tiles'', des rectangles ou en carrés de taille fixe, généralement des carrés de 4 pixels de côté. Les tiles ont une largeur et une longueur égales, afin de simplifier les calculs : on divise X et Y par le même nombre. De plus, leur largeur et leur longueur sont une puissance de deux, afin de simplifier les calculs d'adresse. Les ''tiles'' sont alors mémorisée les unes après les autres dans le fichier de la texture. [[File:Texture tilée.png|centre|vignette|upright=2|Texture tilée]] La formule de calcul d'adresse vue plus haut doit être adaptée pour tenir compte des tiles. Pour cela, il faut remplacer la taille d'un texel par la taille d'une tile, et que la largeur de la texture soit exprimée en nombre de tiles. De plus, on doit adapter les coordonnées des texels pour donner des coordonnées de tile. Généralement, les tiles sont des carrés de N pixels de côté, ce qui fait qu'on peut regrouper les lignes et les colonnes par paquets de N. Il suffit donc de diviser Y et X pour obtenir les coordonnées de la tile, de même que la larguer. La formule pour calculer la position de la énième tile est alors la suivante : : <math>\text{adresse d'une tile} = \text{adresse du début de la texture} + \text{Taille mémoire d'une tile} \times \left( {\text{Width} \over N} \times {Y \over N} + {X \over N} \right)</math> On peut réécrire le tout comme suit : : <math>\text{adresse d'une tile} = \text{adresse du début de la texture} + K \times \left( {Y \over N} + X \right)</math>, avec K une constante connue à la compilation des shaders. Vu que les tiles sont carrées avec une largeur qui est une puissance de deux, la multiplication par la taille d'une tile en mémoire se simplifie : on passe d'une multiplication entière à des décalages de bits. Même chose pour le calcul de l'adresse de la tile à partir des coordonnées x,y : ils impliquent des divisions par une puissance de deux, qui deviennent de simples décalages. La position d'un pixel dans une tile dépend du format de la texture, mais peut se calculer avec quelques calculs arithmétiques simples. Dans les cas les plus simples, les pixels sont mémorisés ligne par ligne, ou colonne par colonne. Mais ce n'est pas systématiquement le cas. Toujours est-il que les calculs pour déterminer l'adresse sont simples, et ne demandent que quelques additions ou multiplications. Mais avec les formats de texture utilisés actuellement, les tiles sont chargées en entier dans le cache de texture, sans compter que diverses techniques de compression viennent mettre le bazar, comme on le verra dans la suite de cours. Un avantage de l'organisation en tiles est qu'elle se marie bien avec le parcours des textures. On peut parcourir une texture dans tous les sens, horizontal, vertical, ou diagonal, on sait que les prochains pixels ont de fortes chances d'être dans la même tile. Si on rentre dans une tile par la gauche en haut, on a encore quelques pixels à parcourir dans la tile, par exemple. De même, le filtrage de textures est facilité. On verra dans ce qui va suivre que le filtrage de texture a besoin de lire des blocs de 4 texels, des carrés de 2 pixels de côté. Avec l'organisation en tile, on est certain que les 4 texels seront dans la même tile, sauf s'ils ont le malheur d'être tout au bord d'une tile. Ce dernier cas est assez rare, et il l'est d'autant plus que les tiles sont grandes. Enfin, un dernier avantage est que les tiles sont généralement assez petites pour tenir tout entier dans une ligne de cache. Le cache de texture est donc utilisé à merveille, ce qui rend les accès aux textures plus rapides. ====Les textures basées sur des ''z-order curves''==== Les formats de textures théoriquement optimaux utilisent une '''''Z-order curve''''', illustrée ci-dessous. L'idée est de découper la texture en quatre rectangles identiques, et de stocker ceux-ci les uns à la suite des autres. L'intérieur de ces rectangles est lui aussi découpé en quatre rectangles, et ainsi de suite. Au final, l'ordre des pixels en mémoire est celui illustré ci-dessous. [[File:Z-CURVE.svg|centre|vignette|upright=2|Construction d'une ''Z-order curve''.]] Les texels sont stockés les uns à la suite des autres dans la mémoire, en suivant l'ordre donnée par la ''Z-order curve''. Le calcul d'adresse calcule la position du texel en mémoire, par rapport au début de la texture, et ajoute l'adresse du début de la texture. Mais tout le défi est de calculer la position d'un texel en mémoire, à partir des coordonnées x,y. Le calcul peut sembler très compliqué, mais il n'en est rien. Le calcul demande juste de regarder les bits des deux coordonnées et de les combiner d'une manière particulièrement simple. Il suffit de placer le bit de poids fort de la coordonnée x, suivi de celui de la coordonnée y, et de faire ainsi de suite en passant aux bits suivants. [[File:Zcurve45bits.png|centre|vignette|upright=1.5|Calcul de la position d'un élément dans une ''Z-order curve'' à partir des coordonnées x et y.]] L'avantage d'une telle organisation est que la textures est découpées en ''tiles'' rectangulaires d'une certaine taille, elles-mêmes découpées en ''tiles'' plus petites, etc. Et il se trouve que cette organisation est parfaite pour le cache de texture. L'idéal pour le cache de texture est de charger une ''tile'' complète dans le cache de textures. Quand on accède à un texel, on s'assure que la ''tile'' complète soit chargée. Mais cela demande de connaitre à l'avance la taille d'une ''tile''. Les formats de texture fournissent généralement une ''tile'' carré de 4 pixels de côté, mais cela donnerait un cache trop petit pour être vraiment utile. Avec cette méthode, on s'assure qu'il y ait une ''tile'' avec la taille optimale. Les ''tiles'' étant découpées en ''tiles'' plus petites, elles-mêmes découpées, et ainsi de suite, on s'assure que la texture est découpées en ''tiles'' de taille variées. Il y aura au moins une ''tile'' qui rentrera tout pile dans le cache. ==L'implémentation matérielle du placage de textures== Pour résumer, la lecture d'un texel demande d'effectuer plusieurs étapes. Dans le cas le plus simple, sans ''mip-mapping'' ou ''cubemapping'', on doit effectuer les étapes suivantes : * Il faut d'abord normaliser les coordonnées de texture pour qu'elles tombent dans l'intervalle [0,1] en fonction du mode d'adressage désiré. * Ensuite, les coordonnées u,v doivent être converties en coordonnées entières, ce qui demande une multiplication flottante. * Enfin, l'adresse finale est calculée à partir des coordonnées entières et en ajoutant l'adresse de base de la texture (et éventuellement avec d'autres calculs arithmétiques suivant le format de la texture). Tout cela pourrait être fait par le pixel shaders, mais cela implique beaucoup de calculs répétitifs et d'opérations arithmétiques assez lourdes, avec des multiplications flottantes, des additions et des multiplications entières, etc. Faire faire tous ces calculs par les shaders serait couteux en performance, sans compter que les shaders deviendraient plus gros et que cela aurait des conséquences sur le cache d'instruction. De plus, certaines de ces étapes peuvent se faire en parallèle, comme les deux premières, ce qui colle mal avec l'aspect sériel des shaders. Aussi, les processeurs de shaders incorporent une unité de calcul d'adresse spéciale pour faire ces calculs directement en matériel. L'unité de calcul en question est dans l'unité de texture. Cette dernière contient donc au minimum deux circuits : un circuit de calcul d'adresse, et un circuit d'accès à la mémoire. Toute la difficulté tient dans le calcul d'adresse, plus que dans le circuit de lecture. Le calcul d'adresse est conceptuellement réalisé en deux étapes. La première étape qui transforme les coordonnées u,v en coordonnées x,y qui donne le numéro de la ligne et de la colonne du texel dans la texture. La seconde étape prend ces deux coordonnées x,y, l'adresse de la texture, et détermine l'adresse de la tile à lire. [[File:Unité de texture simple.png|centre|vignette|upright=2|Unité de texture simple]] ===La gestion des accès mémoire=== Enfin, l'unité de texture doit tenir compte du fait que la mémoire vidéo met du temps à lire une texture. En théorie, l'unité de texture ne devrait pas accepter de nouvelle demande de lecture tant que celle en cours n'est pas terminée. Mais faire ainsi demanderait de bloquer tout le pipeline, de l'''input assembler'' au unités de''shaders'', ce qui est tout sauf pratique et nuirait grandement aux performances. Une solution alternative consiste à mettre en attente les demandes de lectures de texture pendant que la mémoire est occupée. La manière la plus simple d'implémenter des accès mémoire multiples est de les mettre en attente dans une petite mémoire FIFO. Cela implique que les accès mémoire s’exécutent dans l'ordre demandé par le ''shader'' et/ou l'unité de rastérisation, il n'y a pas de réorganisation des accès mémoire ou d’exécution dans le désordre des accès mémoire. [[File:Texture prefetching.png|centre|vignette|upright=1.5|Accès mémoire simultanés.]] Évidemment, quand la mémoire FIFO est pleine, le pipeline est alors totalement bloqué. Le rasteriser est prévenu que l'unité de texture ne peut pas accepter de nouvelle lecture de texture. En pratique, la FIFO est généralement d'une taille respectable et permet de mettre en attente beaucoup de demandes de lecture de texture. Il faut de plus noter qu'il y a une FIFO par processeur de ''shader'' sur les cartes graphiques modernes. Quand elle est pleine, le processeur cesse d'exécuter de nouveaux accès mémoire, mais peut continuer à exécuter des ''shaders'' dans les autres unités de calcul, pas besoin de bloquer complétement le pipeline. ===L'intégration du cache de textures=== Il faut noter que les unités de texture incorporent aussi un cache de texture, voire plusieurs. L'intégration des caches de texture avec la mémoire FIFO précédente est quelque peu compliqué, car il faut garantir que les lectures de texture se fassent dans le bon ordre. On ne peut pas exécuter une lecture dans le cache alors que des lectures précédentes sont en attente de lecture en mémoire vidéo. Et cela pose un gros problème : une lecture dans le cache de texture prend quelques dizaines de cycles d'horloge, alors qu'une lecture en mémoire vidéo en prend facilement 400 à 800 cycles, parfois plus. Et cela fait que l'ordre des accès mémoire peut s'inverser. Prenons par exemple un accès au cache précédé et suivi par deux accès en mémoire vidéo. Le premier démarre au cycle 1, et se termine au cycle numéro 400. L'accès au cache commence au cycle 2 et se termine 20 cycles après, au cycle numéro 22. En clair, la lecture dans le cache s'est terminée avant l'accès mémoire qui le précède. Les textures ne sont donc plus lues dans l'ordre. Et il faut trouver une solution pour éviter cela. La solution est de retarder les lectures dans le cache tant que tous les accès précédents ne sont pas terminés. Mais pour retarder les lectures en question, il faut d'abord savoir si la lecture atterrit dans le cache ou non, ce qui demande d'accéder au cache. On fait face à un dilemme : on veut retarder les accès au cache, mais les différencier des lectures déclenchant des accès mémoire demande d'accéder au cache en premier lieu. La solution est décrite dans l'article "Prefetching in a Texture Cache Architecture" par Igehy et ses collègues. Elle se base sur deux idées combinées ensemble. La première idée est de séparer l'accès au cache en deux : une étape qui vérifie si les texels à lire sont dans le cache, et une étape qui accède aux données dans le cache lui-même. Un cache de texture est donc composé de deux circuits principaux. Le premier vérifie la présence des texels dans le cache. Il reçoit l'adresse mémoire à lire, et détermine si une copie de la donnée associée est dans le cache ou non. Pour cela, il utilise un système de tags qu'on ne détaillera pas ici, mais qui donne son nom à l'unité de vérification : l''''unité de tag'''. Ensuite, en plus de l'unité de tags, il y a une mémoire qui stocke les données, la mémoire cache proprement dite. Par simplicité, cette mémoire est une simple mémoire RAM adressable avec des adresses mémoires des plus normales, chaque ligne de cache correspondant à une adresse. Ce genre de cache séparé en deux mémoires est appelé un ''phased cache'', pour ceux qui veulent en savoir plus. [[File:Phased cache.png|centre|vignette|upright=1.5|Phased cache]] La seconde idée est de retarder l'accès au cache entre les deux phases. La première étape d'un accès mémoire vérifie si la donnée est dans le cache ou non. Puis, on retarde la lecture des données, pour attendre que toutes les lectures précédentes soient terminées. Et enfin, troisième étape : la lecture des texels dans la mémoire cache proprement dite. Les accès mémoire passant par la mémoire vidéo se font de la même manière, à une différence près : la lecture dans le cache est remplacée par la lecture en mémoire vidéo. Tout démarre avec une demande à l'unité de tags, qui vérifie si le texel est dans le cache ou non. Puis on retarde l'accès tant que la mémoire vidéo est occupée, puis on effectue la lecture en mémoire vidéo. Si ce n'est pas le cas, l'accès mémoire est envoyé à la mémoire vidéo comme précédemment, à savoir qu'il est mis en attente dans une mémoire FIFO, puis envoyé à la mémoire vidéo dès que celle-ci est libre. Mais en sortie de la mémoire, la donnée lue est envoyée dans le cache de texture, par dans l'unité de filtrage. Pour savoir où placer la donnée lue, l'unité de tag a réservé une ligne de cache précise, une adresse bien précise. L'adresse en question est disponible en lisant une autre mémoire FIFO, qui a mis en attente l'adresse en question, en attendant que l'accès mémoire se termine. La donnée est alors écrite dans le cache, puis lue par l'unité de filtrage de textures. Pour une lecture dans le cache, le déroulement est similaire, mais sans le passage par la mémoire. La lecture fait une demande à l'unité de tag, et celle-ci répond que la donnée est bien dans le cache. Elle place alors l'adresse à lire dans la file d'attente. Une fois que les accès mémoire précédents sont terminés, l'adresse sort de la file d'attente et est envoyée à la mémoire de données. La lecture s'effectue, les texels sont envoyés à l'unité de filtrage de textures. La seule différence avec un ''phased cache'' normal est l'insertion de l'adresse à lire dans une FIFO qui vise à mettre en attente [[File:Unité de texture avec un cache de texture.png|centre|vignette|upright=2.0|Unité de texture avec un cache de texture]] Pour résumer, l'implémentation précédente garantit une exécution des lectures dans leur ordre d'arrivée. Et pour cela, elle retarde les lectures dans le cache tant que les lectures en mémoire précédentes ne sont pas terminées. L'accès au cache est plus rapide que l'accès en mémoire vidéo, mais le retard ajouté pour garantir l'ordre des lectures fait que le temps d'accès est très long. ==Le mip-mapping== Le '''mip-mapping''' a pour but de légèrement améliorer les graphismes des objets lointains, tout en rendant les calculs de texture plus rapides. Formellement, le ''mip-mapping'' est une technique de filtrage de texture, mais nous l'abordons maintenant car elle est surtout liée au calcul d'adresse. Les unités de texture ont des circuits de filtrage de texture séparés des circuits de ''mip-mapping'' et de calcul d'adresse, d'où le fait que nous en parlons séparément. Le problème résolu par le ''mip-mapping'' est le rendu des textures lointaines. Si une texture est plaquée sur un objet lointain, une bonne partie des détails est invisible pour l'utilisateur. Un pixel de l'écran est associé à plusieurs texels. Idéalement, la carte graphiques devrait lire tous ces texels et en faire une sorte de moyenne pondérée, pour calculer la couleur finale du pixel. Mais dans les faits, ce serait très gourmand et compliqué à implémenter en hardware. Une solution serait de ne garder que quelque texels, mais cela a tendance à créer des artefacts visuels (les textures affichées ont tendance à pixeliser). Le ''mip-mapping'' permet de réduire ces deux problèmes en même temps en précalculant cette moyenne pondérée pour des distances prédéfinies. L'idée est d'utiliser plusieurs exemplaires d'une même texture à des résolutions différentes, chaque exemplaire étant adapté à une certaine distance. Par exemple, une texture sera stocké avec un exemplaire de 512 * 512 pixels, un autre de 256 * 256, un autre de 128 * 128 et ainsi de suite jusqu’à un dernier exemplaire de 32 * 32 pixel. Chaque exemplaire correspond à un '''niveau de détail''', aussi appelé ''Level Of Detail'' (abrévié en LOD). La résolution utilisée diminue d'autant plus que l'objet est situé loin de la caméra. Les objets proches seront rendus avec la texture 512*512, ceux plus lointains seront rendus avec la texture de résolution 256*256, les textures 128*128 seront utilisées encore plus loin, et ainsi de suite jusqu'aux objets les plus lointains qui sont rendus avec la texture la plus petite de 32*32. [[File:MipMap Example STS101.jpg|centre|vignette|upright=2|Exemples de mip-maps.]] Le ''mip-mapping'' améliore grandement la qualité d'image. L'image d'exemple ci-dessous le montre assez bien. [[File:Mipmapping example.png|centre|vignette|upright=2|Exemple de mipmapping.]] Pour faciliter les calculs d'adresse, les LOD d'une même texture sont stockées les uns après les autres en mémoire (dans un tableau, comme diraient les programmeurs). Ainsi, pas besoin de se souvenir de la position en mémoire de chaque LOD : l'adresse de la texture de base, et quelques astuces arithmétiques suffisent. Prenons le cas où la texture de base a une taille L. le premier exemplaire est à l'adresse 0, le second niveau de détail est à l'adresse L, le troisième à l'adresse L + L/4, le suivant à l'adresse L + L/4 + L/16, et ainsi de suite. Le calcul d'adresse demande juste connaître le niveau de détails souhaité et l'adresse de base de la texture. Le niveau de détail voulu est calculé par les pixel shaders, en fonction de la coordonnée de profondeur du pixel à traiter. Évidemment, cette technique consomme de la mémoire vidéo, vu que chaque texture est dupliquée en plusieurs exemplaires, en plusieurs LOD. Dans le détail, la technique du mip-mapping prend au maximum 33% de mémoire en plus (sans compression). Cela vient du fait qu'en prenant une texture dexu fois plus petite, elle prend 4 fois moins de mémoire : 2 fois moins de pixels en largeur, et 2 fois moins en hauteur. Donc, si je pars d'une texture de base contenant X pixels, la totalité des LODs, texture de base comprise, prendra X + (X/4) + (X/16) + (X/256) + … Un petit calcul de limite donne 4/3 * X, soit 33% de plus. ===L'implémentation du mip-mapping dans l'unité de texture=== Le ''mip-mapping'' est lui aussi pris en charge par l'unité de calcul d'adresse, car cette technique change l'adresse de base de la texture. La gestion du ''mip-mapping'' est cependant assez complexe. Il est possible de laisser le pixel shader calculer quel niveau de détail utiliser, en fonction de la coordonnée de profondeur z du pixel à afficher. La carte graphique détermine alors automatiquement quelle texture lire, quel niveau de détail, automatiquement. Elle détermine aussi la bonne résolution pour la texture, qui est égal à la résolution de la texture de base, divisée par le niveau de détail. Pour résumer, le niveau de détail est envoyé aux unités de texture, qui s'occupent de calculer l'adresse de base et la résolution adéquates. Quelques calculs arithmétiques simples, donc, qui s'implémentent facilement avec quelques circuits. Mais une autre méthode laisse la carte graphique déterminer le niveau de détail par elle-même. Dans ce cas, cela demande, outre les deux coordonnées de texture, de calculer la dérivée de ces deux coordonnées dans le sens horizontal et vertical, ce qui fait quatre dérivées (deux dérivées horizontales, deux verticales). Les quatre dérivées sont les suivantes : : <math>\frac{du}{dx}</math>, <math>\frac{dv}{dx}</math>, <math>\frac{du}{dy}</math>, <math>\frac{dv}{dy}</math> Un bon moyen pour obtenir les dérivées demande de regrouper les pixels par groupes de 4 et de faire la différence entre leurs coordonnées de texture respectives. On peut calculer les deux dérivées horizontales en comparant les deux pixels sur la même ligne, et les deux dérivées verticales en comparant les deux pixels sur la même colonne. Mais cela demande de rastériser les pixels par groupes de 4, par ''quads''. Et c'est ce qui est fait sur les cartes graphiques actuelles, qui rastérisent des groupes de 4 pixels à la fois. [[File:Texture sampler unit with mipmapping.png|centre|vignette|upright=2.0|Unité de texture avec mipmapping.]] Malheureusement, le calcul exact utilisé pour le choix de la mip-map dépend du GPU considéré et peu de chose est connu quant à ces algorithmes. Il est possible d'inférer le comportement à partir d'observations, mais guère plus. Pour ceux qui veulent en savoir plus, je conseille la lecture de cet article de blog : * [https://pema.dev/2025/05/09/mipmaps-too-much-detail/ Mipmap selection in too much detail] ==Le filtrage de textures== Plaquer des textures sans autre forme de procès ne suffit pas à garantir des graphismes d'une qualité époustouflante. La raison est que les sommets et les texels ne tombent pas tout pile sur un pixel de l'écran : le sommet associé au texel peut être un petit peu trop en haut, ou trop à gauche, etc. Une explication plus concrète fait intervenir les coordonnées de texture. Souvenez-vous que lorsque l'on traduit une coordonnée de texture u,v en coordonnées x,y, on obtient un résultat qui ne tombe pas forcément juste. Souvent, le résultat a une partie fractionnaire. Si celle-ci est non-nulle, cela signifie que le texel/sommet n'est pas situé exactement sur le pixel voulu et que celui-ci est situé à une certaine distance. Concrètement, le pixel tombe entre quatre texels, comme indiqué ci-dessous. [[File:Filtrage texture.png|centre|vignette|upright=2.0|Position du pixel par rapport aux texels.]] Pour résoudre ce problème, on doit utiliser différentes techniques d'interpolation, aussi appelées techniques de '''filtrage de texture''', qui visent à calculer la couleur du pixel final en fonction des texels qui l'entourent. Il existe de nombreux types de filtrage de textures, qu'il s'agisse du filtrage linéaire, bilinéaire, trilinéaire, anisotropique et bien d'autres. Tous ont besoin d'avoir certaines informations qui sont généralement fournies par les circuits de calcul d'adresse. La première est clairement la partie fractionnaire des coordonnées x,y. La seconde est la dérivée de ces deux coordonnées dans le sens horizontal et vertical., ce qui fait quatre dérivées (deux dérivées horizontales, deux verticales). Toujours est-il que le filtrage de texture est une opération assez lourde, qui demande beaucoup de calculs arithmétiques. On pourrait en théorie le faire dans les pixels shaders, mais le cout en performance serait absolument insoutenable. Aussi, les cartes graphiques intègrent toutes un circuit dédié au filtrage de texture, le ''texture sampler''. Même les plus anciennes cartes graphiques incorporent une unité de filtrage de texture, ce qui nous montre à quel point cette opération est importante. [[File:Texture unit.png|centre|vignette|upright=2.0|Unité de texture.]] On peut configurer la carte graphique de manière à ce qu'elle fasse soit du filtrage bilinéaire, soit du filtrage trilinéaire, on peut configurer le niveau de filtrage anisotropique, etc. Cela peut se faire dans les options de la carte graphique, mais cela peut aussi être géré par l'application. La majorité des jeux vidéos permettent de régler cela dans les options. Ces réglages ne concernent pas la texture elle-même, mais plutôt la manière dont l'unité de texture doit fonctionner. Ces réglages sur l''''état de l'unité de texture''' sont mémorisés quelque part, soit dans l'unité de texture elle-même, soit fournies avec la ressource de texture elle-même, tout dépend de la carte graphique. Certaines cartes graphiques mémorisent ces réglages dans les unités de texture ou dans le processeur de commande, et tout changement demande alors de réinitialiser l'état des unités de texture, ce qui prend un peu de temps. D'autres placent ces réglages dans les ressources de texture elles-mêmes, ce qui rend les modifications de configuration plus rapides, mais demande plus de circuits. D'autres cartes graphiques mélangent les deux options, certains réglages étant globaux, d'autres transmis avec la texture. Bref, difficile de faire des généralités, tout dépend du matériel et le pilote de la carte graphique cache tout cela sous le tapis. Maintenant que cela est dit, voyons quelles sont les différentes méthodes de filtrage de texture et comment la carte graphique fait pour les calculer. ===Le filtrage au plus proche=== La méthode de filtrage la plus simple consiste à colorier avec le texel le plus proche. Cela revient tout simplement à ne pas tenir compte de la partie fractionnaire des coordonnées x,y, ce qui est très simple à implémenter en matériel. C'est ce que l'on appelle le '''filtrage au plus proche''', aussi appelé ''nearest filtering''. Autant être franc, le résultat est assez pixelisé et peu agréable à l’œil. Par contre, le résultat est très rapide à calculer, vu qu'il ne demande aucun calcul à proprement parler. Elle ne fait pas appel à la parti fractionnaire des coordonnées entières de texture, ni aux dérivées de ces coordonnées. On peut combiner cette technique avec le mip-mapping, ce qui donne un résultat bien meilleur, bien que loin d'être satisfaisant. Au passage, toutes les techniques de filtrage de texture peuvent se combiner avec du mip-mapping, certaines ne pouvant pas faire sans. [[File:Interpolation-nearest.svg|centre|vignette|Filtrage de texture au plus proche.]] ===Le filtrage linéaire=== Le filtrage le plus simple est le '''filtrage linéaire'''. Il effectue une interpolation linéaire entre deux mip-maps, deux niveaux de détails. Pour comprendre l'idée, nous allons prendre une situation très simple, avec une texture carrée de 512 texels de côté. Le mip-mapping crée plusieurs textures : une de 256 texels de côté, une de 128 texels, une de 64, etc. Maintenant, la texture est sur un objet à une certaine distance de l'écran, vu de face. Le résultat est qu'elle correspond à l'écran à un carré de 300 pixels de côté (pas d'erreur : pixels, pas texels). Dans ce cas, la texture se trouve entre deux mip-maps : celle de 512 pixels de côté, celle de 256. Laquelle choisir ? Le filtrage au plus proche prend la texture de 512 pixels de côté. Le filtrage linéaire lui, fait autrement. Vu que la texture est entre deux mip-maps, l'idée est de prendre le texel au plus proche dans chaque texture et de faire une sorte de moyenne appelée l'interpolation linéaire. L'interpolation par du principe que la couleur varie entre les deux texels en suivant une fonction affine, illustrée ci-dessous. Ce ne serait évidemment pas le cas dans le monde réel, mais on supposer cela donne une bonne approximation de ce à quoi ressemblerait une texture à plus haute résolution. On peut alors calculer la couleur du pixel par une simple moyenne pondérée par la distance. Le résultat est que les transitions entre deux niveaux de détails sont plus lisses, moins abruptes. [[File:Lin interp -é.png|centre|vignette|upright=2.0|Interpolation linéaire.]] ===Le filtrage bilinéaire=== Le filtrage bilinéaire effectue une sorte de moyenne pondérée des quatre texels les plus proches du pixel à afficher. Pour cela, rappelez-vous ce qui a été dit plus haut : les coordonnées x,y d'un pixel ont une partie entière et une partie fractionnaire. Le filtrage au plus proche élimine les parties fractionnaires, ce qui donne une coordonnée x,y. Avec le filtrage bilinéaire, on prend les texels de coordonnées (x,y) ; (x+1,y) ; (x,y+1) ; (x+1,y+1), le pixel étant entre ces 4 texels. Mais le filtrage ne fait pas qu'une simple moyenne, il prend en compte les parties fractionnaires pour faire la moyenne. En effet, le pixel n'est pas au milieu du carré de texel, il est quelque part mais est souvent plus proche d'un texel que des autres. Et il faut donc pondérer la moyenne par les distances aux 4 texels. Pour cela, la moyenne est calculée à partir d'interpolations linéaires. Avec 4 pixels, nous allons devoir calculer la couleur de deux points intermédiaires. La couleur de ces deux points se calcule par interpolation linéaire, et il suffit d'utiliser une troisième interpolation linéaire pour obtenir le résultat. [[File:Bilin3.png|centre|vignette|upright=2|Filtrage bilinéaire de texture.]] Le circuit qui permet de faire l'interpolation bilinéaire est particulièrement simple. On trouve un circuit de chaque pour chaque composante de couleur de chaque texel : un pour le rouge, un pour le vert, un pour le bleu, et un pour la transparence. Chacun de ces circuit est composé de sous-circuits chargés d'effectuer une interpolation linéaire, reliés comme suit. [[File:Texture sampler unit.png|centre|vignette|Unité de filtrage bilinéaire.]] Vous noterez que le filtrage bilinéaire accède à 4 pixels en même temps. Fort heureusement, les textures sont stockées de manière à ce qu'on puisse charger les 4 pixels en une fois, comme on l'a vu plus haut. Le filtrage bilinéaire a de fortes chances que les 4 pixels filtrés soient dans la même ''tile'', la seule exception étant quand ils sont tout juste sur le bord d'une ''tile''. : La console de jeu Nintendo 64 n'utilise que trois pixels au lieu de quatre dans son interpolation bilinéaire, qui en devient une interpolation quasi-bilinéaire. La raison derrière ce choix est une question de performances, comme beaucoup de décisions de ce genre. Le résultat est un rendu imparfait de certaines textures. ===Le filtrage trilinéaire=== Avec le filtrage bilinéaire, des discontinuités apparaissent sur certaines surfaces. Par exemple, pensez à une texture de sol : elle est appliquée plusieurs fois sur toute la surface du sol. A une certaine distance, le LOD utilisé change brutalement et passe par exemple de 512*512 à 256*256, ce qui est visible pour un joueur attentif. De telles transitions sont lissées grâce au filtrage linéaire, il n'y a plus qu'à le combiner avec le filtrage bilinéaire. Rien d’incompatible : le premier filtre l'intérieur d'une mip-map, le second combine deux mip-maps. Le filtrage trilinéaire prend les deux mip-maps les plus proches, fait un filtrage bilinéaire avec chacune, puis fait une « une moyenne » pondérée entre les deux résultats. Le circuit de filtrage trilinéaire existe en plusieurs versions. La plus simple, illustrée ci-dessous, effectue deux filtrages bilinéaires en parallèle, dans deux circuits séparés, puis combine leurs résultats avec un circuit d'interpolation linéaire. Mais ce circuit nécessite de charger 8 texels simultanément. Qui plus est, ces 8 texels ne sont pas consécutifs en mémoire, car ils sont dans deux niveaux de détails/mip-maps différents. [[File:Parallel trilinear filtering.png|centre|vignette|upright=2.0|Unité de filtrage trilinéaire parallèle.]] Vu qu'on lit des texels dans deux mip-maps, les texels sont lus en deux fois : 4 texels provenant de la première mip-map, suivis par les 4 texels de l'autre mip-map. Les 4 premiers texels doivent donc être mis en attente dans des registres, en attendant que les 4 autres arrivent. Une amélioration du circuit précédent gère cela en ajoutant des registres. Il lit les 4 premiers texels, les filtre avec une interpolation bilinéaire, et mémorise le résultat dans un registre. Puis, il lit les 4 autres texels, les filtre, et met le résultat dans un second registre. A ce moment là, un circuit d'interpolation linéaire finit le travail. On économise donc un circuit d'interpolation bilinéaire, sans que les performances soient trop impactées. [[File:Filtrage trilineaire.png|centre|vignette|upright=1.0|Unité de filtrage trilineaire série.]] Modifier le circuit de filtrage ne suffit pas. Comme je l'ai dit plus haut, la dernière étape d'interpolation linéaire utilise des coefficients, qui lui sont fournis par des registres. Seul problème : entre le temps où ceux-ci sont calculés par l'unité de mip-mapping, et le moment où les texels sont chargés depuis la mémoire, il se passe beaucoup de temps. Le problème, c'est que les unités de texture sont souvent pipelinées : elles peuvent démarrer une lecture de texture sans attendre que les précédentes soient terminées. À chaque cycle d'horloge, une nouvelle lecture de texels peut commencer. La mémoire vidéo est conçue pour supporter ce genre de chose. Cela a une conséquence : durant les 400 à 800 cycles d'attente entre le calcul des coefficients, et la disponibilité des texels, entre 400 et 800 coefficients sont produits : un par cycle. Autant vous dire que mémoriser 400 à 800 ensembles de coefficient prend beaucoup de registres. ===Le filtrage anisotrope=== D'autres artefacts peuvent survenir lors de l'application d'une texture, la perspective pouvant déformer les textures et entraîner l'apparition de flou. La raison à cela est que les techniques de filtrage de texture précédentes partent du principe que la texture est vue de face. Prenez une texture carrée, par exemple. Vue de face, elle ressemble à un carré sur l'écran. Mais tournez la caméra, de manière à voir la texture de biais, avec un angle, et vous verrez que la forme de la texture sur l'écran est un trapèze, pas un carré. Cette déformation liée à la perspective n'est pas prise en compte par les méthodes de filtrage de texture précédentes. Pour le dire autrement, les techniques de filtrage précédentes partent du principe que les 4 texels qui entourent un pixel forment un carré, ce qui est vrai si la texture est vue de face, sans angle, mais ne l'est pas si la texture n'est pas perpendiculaire à l'axe de la caméra. Du point de vue de la caméra, les 4 texels forment un trapèze d'autant moins proche d'un carré que l'angle est grand. Pour corriger cela, les chercheurs ont inventé le '''filtrage anisotrope'''. En fait, je devrais plutôt dire : LES filtrages anisotropes. Il en existe un grand nombre, dont certains ne sont pas utilisés dans les cartes graphiques actuelles, soit car ils trop gourmand en accès mémoires et en calculs pour être efficaces, soit car ils ne sont pas pratiques à mettre en œuvre. Il est très difficile de savoir quelles sont les techniques de filtrage de texture utilisées par les cartes graphiques, qu'elles soient récentes ou anciennes. Beaucoup de ces technologies sont brevetées ou gardées secrètes, et il faudrait vraiment creuser les brevets déposés par les fabricants de GPU pour en savoir plus. Les algorithmes en question seraient de plus difficiles à comprendre, les méthodes mathématiques cachées derrière ces méthodes de filtrage n'étant pas des plus simple. [[File:Anisotropic filtering en.png|centre|vignette|upright=2|Exemple de filtrage anisotrope.]] ==La compression de textures== Les textures les plus grosses peuvent aller jusqu'au mébioctet, ce qui est beaucoup. Pour limiter la casse, les textures sont compressées. La '''compression de texture''' réduit la taille des textures, ce qui peut se faire avec ou sans perte de qualité. Elle entraîne souvent une légère perte de qualité lors de la compression. Toutefois, cette perte peut être compensée en utilisant des textures à résolution plus grande. Mais il s'agit là d'une technique très simple, beaucoup plus simple que les techniques que nous allons voir dans cette section. Nous allons voir quelque algorithmes de compression de textures de complexité intermédiaire, mais n'allons pas voir l'état de l'art. Il existe des formats de texture plus récents que ceux qui nous allons aborder, comme l{{'}}''Ericsson Texture Compression'' ou l{{'}}''Adaptive Scalable Texture Compression'', plus complexes et plus efficaces. Notons que les textures sont compressées dans les fichiers du jeu, mais aussi en mémoire vidéo. Les textures sont décompressées lors de la lecture. Pour cela, la carte graphique contient alors un circuit, capable de décompresser les textures lorsqu'on les lit en mémoire vidéo. Les cartes graphiques supportent un grand nombre de formats de textures, au niveau du circuit de décompression. Du fait que les textures sont décompressées à la volée, les techniques de compression utilisées sont assez particulières. La carte graphique ne peut pas décompresser une texture entière avant de pouvoir l'utiliser dans un ''pixel shader''. A la place, on doit pouvoir lire un morceau de texture, et le décompresser à la volée. On ne peut utiliser les méthodes de compression du JPEG, ou d'autres formats de compression d'image. Ces dernières ne permettent pas de décompresser une image morceau par morceau. Pour permettre une décompression/compression à la volée, les textures sont des textures tilées, généralement découpées en tiles de 4 * 4 texels. Les ''tiles'' sont compressées indépendamment les unes des autres. Et surtout, avec ou sans compression, la position des tiles en mémoire ne change pas. On trouve toujours une tile tous les T octets, peu importe que la tile soit compressée ou non. Par contre, une tile compressée n'occupera pas T octets, mais moins, là où une tile compressée occupera la totalité des T octets. En clair, compresser une tile fait qu'il y a des vides entre deux tiles dans al mémoire vidéo, mais ne change rien à leur place en mémoire vidéo qui est prédéterminée, peu importe que la texture soit compressée ou non. L'intérêt de la compression de textures n'est pas de réduire la taille de la texture en mémoire vidéo, mais de réduire la quantité de données à lire/écrire en mémoire vidéo. Au lieu de lire T octets pour une tile non-compressée, on pourra en lire moins. ===La palette indicée et la technique de ''Vector quantization''=== La technique de compression des textures la plus simple est celle de la '''palette indicée''', que l'on a entraperçue dans le chapitre sur les cartes d'affichage. La technique de '''''vector quantization''''' peut être vue comme une amélioration de la palette, qui travaille non pas sur des texels, mais sur des ''tiles''. À l'intérieur de la carte graphique, on trouve une table qui stocke toutes les ''tiles'' possibles. Chaque ''tile'' se voit attribuer un numéro, et la texture sera composé d'une suite de ces numéros. Quelques anciennes cartes graphiques ATI, ainsi que quelques cartes utilisées dans l’embarqué utilisent ce genre de compression. ===Les algorithmes de ''Block Truncation coding''=== La première technique de compression élaborée est celle du '''''Block Truncation Coding''''', qui ne marche que pour les images en niveaux de gris. Le BTC ne mémorise que deux niveaux de gris par ''tile'', que nous appellerons couleur 1 et couleur 2, les deux niveaux de gris n'étant pas le même d'une ''tile'' à l'autre. Chaque pixel d'une ''tile'' est obligatoirement colorié avec un de ces niveaux de gris. Pour chaque pixel d'une ''tile'', on mémorise sa couleur avec un bit : 0 pour couleur 1, et 1 pour couleur 2. Chaque ''tile'' est donc codée par deux entiers, qui codent chacun un niveau de gris, et une suite de bits pour les pixels proprement dit. Le circuit de décompression est alors vraiment très simple, comme illustré ci-dessous. [[File:Block Truncation coding.jpg|centre|vignette|upright=2.0|Block Truncation coding.]] La technique du BTC peut être appliquée non pas du des niveaux de gris, mais pour chaque composante Rouge, Vert et Bleu. Dans ces conditions, chaque ''tile'' est séparée en trois sous-''tiles'' : un sous-bloc pour la composante verte, un autre pour le rouge, et un dernier pour le bleu. Cela prend donc trois fois plus de place en mémoire que le BTC pur, mais cela permet de gérer les images couleur. ===Le format de compression S3TC / DXTC=== L'algorithme de '''Color Cell Compression''', ou CCC, améliore le BTC pour qu'il gère des couleurs autre que des niveaux de gris. Ce CCC remplace les deux niveaux de gris par deux couleurs. Une ''tile'' est donc codée avec un entier 32 bits par couleur, et une suite de bits pour les pixels. Le circuit de décompression est identique à celui utilisé pour le BTC. [[File:Color Cell Compression.jpg|centre|vignette|Color Cell Compression.]] [[File:Dxt1-memory-layout.png|vignette|Dxt1 et ''color cell compression''.]] Le format de compression de texture utilisé de base par Direct X, le DXTC, est une version amliorée de l'algorithme précédent. Il est décliné en plusieurs versions : DXTC1, DXTC2, etc. La première version du DXTC est une sorte d'amélioration du CCC : il ajoute une gestion minimale de transparence, et découpe la texture à compresser en ''tiles'' de 4 pixels de côté. La différence, c'est que la couleur finale d'un texel est un mélange des deux couleurs attribuée au bloc. Pour indiquer comment faire ce mélange, on trouve deux bits de contrôle par texel. Si jamais la couleur 1 < couleur2, ces deux bits sont à interpréter comme suit : * 00 = Couleur1 * 01 = Couleur2 * 10 = (2 * Couleur1 + Couleur2) / 3 * 11 = (Couleur1 + 2 * Couleur2) / 3 Sinon, les deux bits sont à interpréter comme suit : * 00 = Couleur1 * 01 = Couleur2 * 10 = (Couleur1 + Couleur2) / 2 * 11 = Transparent [[File:DXTC.jpg|centre|vignette|DXTC.]] Le circuit de décompression du DXTC ressemble alors à ceci : [[File:Circuit de décompression du DXTC.jpg|centre|vignette|upright=2.0|Circuit de décompression du DXTC.]] ===Les format DXTC 2, 3, 4 et 5 : l'ajout de la transparence=== Pour combler les limitations du DXT1, le format DXT2 a fait son apparition. Il a rapidement été remplacé par le DXT3, lui-même replacé par le DXT4 et par le DXT5. Dans le DXT3, la transparence fait son apparition. Pour cela, on ajoute 64 bits par ''tile'' pour stocker des informations de transparence : 4 bits par texel. Le tout est suivi d'un bloc de 64 bits identique au bloc du DXT1. [[File:Dxt23-memory-layout.png|centre|vignette|Dxt 2 et 3.]] Dans le DXT4 et le DXT5, la méthode utilisée pour compresser les couleurs l'est aussi pour les valeurs de transparence. L'information de transparence est stockée par un en-tête contenant deux valeurs de transparence, le tout suivi d'une matrice qui attribue trois bits à chaque texel. En fonction de la valeur des trois bits, les deux valeurs de transparence sont combinées pour donner la valeur de transparence finale. Le tout est suivi d'un bloc de 64 bits identique à celui qu'on trouve dans le DXT1. [[File:Dxt45-memory-layout.png|centre|vignette|Dxt 4 et 5.]] ===Le format de compression PVRTC=== Passons maintenant à un format de compression de texture un peu moins connu, mais pourtant omniprésent dans notre vie quotidienne : le PVRTC. Ce format de texture est utilisé notamment dans les cartes graphiques de marque PowerVR. Vous ne connaissez peut-être pas cette marque, et c'est normal : elle travaille surtout dans les cartes graphiques embarquées. Ses cartes se trouvent notamment dans l'ipad, l'iPhone, et bien d'autres smartphones actuels. Avec le PVRTC, les textures sont encore une fois découpées en ''tiles'' de 4 texels par 4, mais la ressemblance avec le DXTC s’arrête là. Chacque ''tile'' est codée avec : * une couleur codée sur 16 bits ; * une couleur codée sur 15 bits ; * 32 bits qui servent à indiquer comment mélanger les deux couleurs ; * et un bit de modulation, qui permet de configurer l’interprétation des bits de mélange. Les 32 bits qui indiquent comment mélanger les couleurs sont une collection de 2 paquets de 2 bits. Chacun de ces deux bits permet de préciser comment calculer la couleur d'un texel du bloc de 4*4. ==Annexe : le ''normal-mapping'' hardware== [[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]] Maintenant, parlons un peu du ''normal mapping'' et de son implémentation dans les unités de texture. Pour rappel, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser. Et pour comprendre quel est le rapport avec les textures, nous allons devoir faire quelques rappels sur l'éclairage par pixel. [[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]] L'éclairage se fait en utilisant de nombreux calculs, qu'on a détaillé dans le chapitre sur les bases du rendu 3D. Ceux-ci utilisent la normale d'un sommet, à savoir un vecteur orienté à la verticale de la surface. En théorie, il y a une normale par sommet, ce qui fait que les calculs d'éclairage doivent se faire au niveau géométrique, avant la rastérisation. Mais pour obtenir un éclairage de meilleure qualité, il y a des techniques qui calcule l'éclairage d'une scène 3D pixel par pixel. Une première technique d'éclairage par pixel interpole les normales lors de l'étape de rastérisation. On obtient alors un éclairage de Phong. Une autre solution est celle du ''normal mapping''. Le '''''normal mapping''''' précalcule les normales d'une surface dans une texture, appelée la ''normal map''. L'éclairage est alors réalisé par un pixel shader, qui lit les normales depuis cette texture et fait les calculs d'éclairage avec. ===L'usage de textures non-compressées pour les ''normal maps''=== La ''normal map'' est une texture, donc. Mais ce n'est pas une texture comme une autre. Elle mémorise un vecteur pour chaque texel, pas une couleur. Il est possible d'utiliser les formats de textures non-compressés, même si cela ne donne pas de bons résultats. Après tout, un vecteur est codé par trois coordonnées x,y,z, il est possible de coder chacune avec un octet et de packager cela dans une texture RGB classique. La coordonnée x va dans la composante Rouge, la coordonnée Y dans la composante Bleu, et la coordonnée z dans la composante z. Une première optimisation est de ne pas mémoriser les trois coordonnées, mais seulement deux d'entre elles et de calculer la troisième. En effet, les normales sont des vecteurs ''normalisés'', c'est à dire que leur longueur vaut 1, par construction. Vu que la taille est connue à l'avance, on peut en déduire la coordonnée z à partir des coordonnées x et y, avec l'usage du théorème de Pythagore. Par définition, <math>z^2 = 1 - x^2 - y^2</math>. Le calcul peut être fait dans le ''pixel shader'' sans problème. : Pour être précis, il faut que les normales soient définies d'une certaine manière pour que ça marche. Les normales ne sont pas définies dans le même système de coordonnées que le modèle 3D, mais dans un autre système appelé l'''espace tangent''. Notons que l'accès à la ''normal map'' se fait comme pour n'importe quelle texture : les texels sont lus en mémoire vidéo, puis le filtrage de texture est appliqué, et enfin le tout est envoyé au ''pixel shader''. En théorie, le filtrage effectue une sorte d'interpolation des normales automatique, ce qui permet de trouver la normale à un pixel précis, même s'il n'est pas sur un texel. Les solutions précédentes demandent d'utiliser des textures non-compressées, qui utilisent beaucoup de mémoire vidéo. Utiliser des textures compressées pourrait sembler résoudre le problème. Mais l'usage de textures compressées marche très mal pour les ''normal maps'', elles ne permettent pas d'obtenir une qualité ou une compression suffisante. Aussi, des formats de texture dédiés aux ''normal maps'' ont été inventés. ===Le format de texture 3Dc=== Le problème est que le rendu final n'est pas très beau. Et les opérations de filtrage ne donnent pas de très bons résultats. Pour remédier à ces problèmes, des formats de texture spécialisés pour les ''normal map'' ont été inventés. Le premier d'entre eux est le '''3Dc''', aussi appelé BC5 pour ''Block Compression 5''. Il utilise un octet par texel, au lieu de trois avec une texture RGB normale non-compressée. Et surtout : il est géré en matériel, directement dans l'unité de texture, qui peut décompresser les textures 3Dc et les filtrer ! Avec le 3Dc, seules coordonnées sont mémorisées, la troisième est calculée, comme dit plus haut. Pour le reste, la compression de la ''normal map'' ressemble un peu à celle des textures. La ''normal map'' est découpée en blocs de 4 par 4 texels de côté, chacune étant encodée en tenant compte de certaines redondances. Dans un bloc, les coordonnées x sont dans un certain intervalle, allant de <math>x_{min}</math> à <math>x_{max}</math>, idem pour la coordonnée y qui est dans l'intervalle <math>y_{min}</math>, <math>y_{max}</math>. L'intervalle <math>x_{min}</math> à <math>x_{max}</math> est coupé en 8 parts égales, ce qui fait 8 valeurs possibles dans cet intervalle, chacune correspondant à une valeur x possible pour une normale du bloc. La normale est donc encodée en précisant quelle valeur est la bonne, en utilisant un indice de 3 bits pour cela. La valeur exacte de la coordonnée x se calcule à partir de l'indice comme suit : : <math>x = x_{min} + \left( \frac{x_{min} - x_{min}}{8} \right) \times \text{indice} </math> Pour encoder un bloc de 4 par 4 normales, il faut donc : * Un octet pour chaque coordonnée <math>x_{min}</math>, <math>x_{max}</math>, <math>y_{min}</math>, <math>y_{max}</math>. * 6 bits par normale : 3 pour encoder le décalage de la coordonnée x, 3 pour la coordonnée y. Le tout permet d'encoder un bloc de 16 normales en seulement 128 bits, soit un octet par normale. Le ''pixel shader'' se débrouille pour décompresser un bloc. Il effectue notamment le calcul du dessus, pour retrouver la coordonnée x. Les calculs étant particulièrement simples, le cout en performance est très faible. Et ce d'autant plus qu'ils demandent juste de faire des additions et des multiplications entières, que l'unité scalaire peut faire en parallèle d'autres opérations plus gourmandes. Et le cout en calculs est de toute façon compensé par l'économie de mémoire et de bande passante lié à la compression : diviser la taille des données par trois, ca a un sacré impact ! ==Annexe : les ''shadowmap'' hardware== Les anciens GPU, notamment la Geforce FX, avaient des fonctionnalités spécifiques pour le calcul des ombres. Dans la plupart des jeux vidéos de l'époque, et même de maintenant, les ombres sont calculées avec la technique des ''shadowmap''. L'idée est assez simple sur le principe : un pixel est dans l'ombre quand il est invisible depuis une source de lumière. L'idée est que le rendu est réalisé en plusieurs passes, avec une passe par source de lumière et une passe finale pour calculer l'image finale. Nous allons expliquer la technique avec une seule source de lumière, et allons utiliser l'exemple de la scène ci-dessous. [[File:7fin.png|centre|vignette|Scène 3D d'exemple.]] ===La technique du ''shadowmapping''=== [[File:2shadowmap.png|vignette|Résultat de la première passe : ''shadowmap''..]] La première passe rend l'image depuis le point de vue de la source de lumière. Cette première passe ne rend pas les couleurs de la scène, elle ne s'intéresse qu'à la profondeur des pixels. Le résultat est que l'image ne rend que le tampon de profondeur. Celui-ci est ensuite réutilisé comme texture pour la passe suivante. La texture en question est appelée la '''''shadownmap'''''. La perspective utilisée, ainsi que le ''view frustrum'', dépend de la source de lumière. Pour une source de lumière qui émet un cône de lumière, le ''view frustrum'' de l'image rendue doit contenir tout le cone de lumière, et doit coller le plus possible à celui-ci. Pour une source directionnelle, comme le soleil, une perspective orthographique est utilisée. La seconde passe rend l'image du point de vue de la caméra, pour rendre l'image finale. Elle rend l'image finale, qui est composée de pixels, chacun ayant une position à l'écran x,y, et une profondeur z. Les coordonnées sont transformées pour obtenir la position de ce pixel depuis le point de vue de la caméra. Une simple multiplication de matrice suffit, rien de bien compliqué, un shader peut le faire. [[File:5failed.png|vignette|Résultat du test des comparaisons.]] Après cette étape, on a alors les coordonnées x,y,z de ce pixel du point de vue de la caméra, et la ''shadowmap''. Il est alors possible d'accéder à la ''shadowmap'' au même endroit, à la même place que le pixel testé, aux mêmes coordonnées x,y. Si la profondeur du pixel est supérieure à celle de la shadowmap au même endroit, alors le pixel est situé derrière la surface visible, donc est dans l'ombre. Sinon, il n'est pas dans l'ombre. Le même procédé est répété sur chaque pixel de l'écran. ===Les optimisations hardware du ''shadowmapping''=== La technique des ''shadowmap'' demande donc de calculer une texture ''shadowmap'', puis de lire celle-ci et de faire des comparaisons de profondeur. Les GPU comme la Geforce FX intégraient du matériel dans les unités de texture pour faciliter ce travail. Les unités de texture pouvaient lire les ''shadowmap'', et faire la comparaison de profondeur toutes seules, elles avaient des circuits pour. Il suffisait de leur fournir le pixel à tester, ses coordonnées x,y,z, et l'adresse de la ''shadowmap''. Les unités de texture renvoyaient alors un résultat valant 0 ou 1 : 1 si le pixel est dans l'ombre, 0 sinon. Elles pouvaient même effectuer du filtrage de texture sur les ''shadowmap''. Mais le filtrage était différent de celui utilisé sur les autres textures : moyenner des valeurs de profondeur ne marche pas bien. Elles utilisaient des techniques de filtrage différentes : elles faisaient les tests de comparaison, puis faisaient la moyenne des résultats. Ainsi, pour du filtrage bilinéaire, elles lisaient 4 texels dans la ''shadowmap'', puis faisaient 4 tests de comparaison, et moyennaient les 4 résultats. ==Annexe : le cube-mapping== [[File:Cube mapped reflection example 2.JPG|vignette|Exemple de reflets environnementaux.]] L''''environnement-mapping''' simule les réflexions et autres effets graphiques sur une surface ou un objet 3D. L'idée est de plaquer une texture pré-calculée pour simuler l'effet de l'environnement sur un modèle 3D. Il en existe plusieurs versions différentes, mais la seule utilisée de nos jours est le ''cube-mapping'', où la texture de l'environnement est plaquée sur un cube, d'où son nom. Le cube en question est utilisé différemment suivant ce que l'on cherche à faire avec le ''cube-mapping''. ===Les utilisations du ''cubemapping''=== Les deux utilisations principales sont le rendu du ciel et des décors, et les réflexions sur la surface des objets. Dans les deux cas, l'idée est de précalculer ce que l'on voit du point de vue de la caméra. On place la caméra dans la scène 3D, on place un cube centré sur la caméra, le cube est texturé avec ce que l'on voit de l'environnement depuis la caméra/l'objet de son point de vue. [[File:Panorama cube map.png|centre|vignette|upright=2|L'illustration montre en premier lieu une ''cubemap'' avec les six faces mises en évidence, puis quel environnement 3D elle permet de simuler, le troisième illustration montrant comment la ''cubemap'' est utilisée pour simuler l'environnement.]] Le rendu du ciel et des décors lointains dans les jeux vidéo se base sur des '''''skybox''''', à savoir un cube centré sur la caméra, sur lequel on ajoute des textures de ciel ou de décors lointains. Le cube est recouvert par une texture, qui correspond à ce que l'on voit quand on dirige le regard de la caméra vers cette face. Contrairement à ce qu'on pourrait croire, la skybox n'est pas les limites de la scène 3D, les limites du niveau d'un jeu vidéo ou quoique ce soit d'autre de lié à la physique de la scène 3D. La skybox est centrée sur la caméra, elle suit la caméra dans son mouvement. Centrer la skybox sur la caméra permet de simuler des décors très lointains, suffisamment lointain pour qu'on n'ait pas l'illusion de s'en rapprocher en se déplaçant dans la map. De plus, cela évite d'avoir à faire trop de calculs à chaque fois que l'on bouge la caméra. La texture plaquée sur le cube est une texture unique, elle-même découpée en six sous-textures, une par face du cube. [[File:Skybox example.png|centre|vignette|upright=2|Exemple de Skybox.]] [[File:Cube mapped reflection example.jpg|vignette|Réflexions calculées par une ''cubemap''.]] Le ''cube-mapping'' est aussi utilisé pour des reflets. L'idée est de simuler les reflets en plaquant une texture pré-calculée sur l'objet réflecteur. La texture pré-calculée est un dessin de l'environnement qui se reflète sur l'objet, un dessin du reflet à afficher. En la plaquant la texture sur l'objet, on simule ainsi des reflets de l'environnement, mais on ne peut pas calculer d'autres reflets comme les reflets objets mobiles comme les personnages. Et il se trouve que la texture pré-calculée est une ''cubemap''. Pour les environnements ouverts, c'est la ''skybox'' qui est utilisée, ce qui permet de simuler les reflets dans les flaques d'eau ou dans des lacs/océans/autres. Pour les environnements intérieurs, c'est une cubemap spécifique qui utilisée. Par exemple, pour l'intérieur d'une maison, on a une ''cubemap'' par pièce de la maison. Les reflets se calculent en précisant quelle ''cubemap'' appliquer sur l'objet en fonction de la direction du regard. [[File:Cube map level.png|centre|vignette|Cube map de l'intérieur d'une pièce d'un niveau de jeux vidéo.]] ===L'implémentation matérielle du ''cubemapping''=== Toujours est-il que les textures utilisées pour le ''cubemapping'', appelées des ''cubemaps'', sont en réalité la concaténation de six textures différentes. En mémoire vidéo, la ''cubemap'' est stockée comme six textures les unes à la suite des autres. Lors du rendu, on doit préciser quelle face du cube utiliser, ce qui fait 6 possibilités. On a le même problème qu'avec les niveaux de détail, sauf que ce sont les faces d'une ''cubemap'' qui remplacent les textures de niveaux de détails. L'accès en mémoire doit donc préciser quelle portion de la ''cubemap'' il faut accéder. Et l'accès mémoire se complexifie donc. Surtout que l'accès en question varie beaucoup suivant l'API graphique utilisée, et donc suivant la carte graphique. Le support des ''cubemaps'' dépend de l'API 3D, et surtout de si elle date ou si elle est récente : * Les API 3D très anciennes ne gérent pas nativement les ''cubemaps'', qui doivent être émulées en logiciel en utilisant six textures différentes. Le pixel shader décide donc quelle ''cubemap'' utiliser, avec quelques calculs sur la direction du regard. * Les API 3D récentes gèrent nativement les ''cubemaps''. Pour les versions les plus vielles de ces API, les six faces sont numérotées et l'accès à une ''cubemap'' précise quel face utiliser en donnant son numéro. La carte graphique choisit alors automatiquement la bonne texture. Mais cela demande de laisser le calcul de la bonne face au pixel shader. * Dans les API 3D modenres, les ''cubemap'' sont gérées comme des textures en trois dimensions, adressées avec trois coordonnées u,v,w. La carte graphique utilise ces trois coordonnées de manière à en déduire quelle est la face pertinente, mais aussi les coordonnées u,v dans la texture de la face. ==Annexe : les textures virtuelles== Les '''textures virtuelles''' sont une optimisation des textures normales, qui visent à accélérer le rendu de terrains de grande taille. Imaginez par exemple un monde assez ouvert, comme un environnement en forêt ou en montagne, avec une grande distance de visibilité. Avec de tels terrains, le "sol" est recouvert par une texture de sol unique qui recouvre tout le terrain. Elle ne se répète pas, est de très grande taille, et peut parfois recouvrir toute la map ! Mais il n'y a pas assez de mémoire vidéo pour mémoriser la texture toute entière. La seule solution est la suivante : une partie de la texture est placée en mémoire vidéo, le reste est soit placé en mémoire RAM ou sur le disque dur. Pour cela, le moteur de jeu utilise une optimisation ingénieuse, basée sur une observation assez basique : une bonne partie de la texture est visible, mais le reste est caché par des arbres, des habitations ou d'autres obstacles. Une optimisation possible de ne garder en mémoire vidéo que les portions visibles de la texture, pas les portions cachées. Une autre optimisation mélange textures virtuelles et ''mip-mapping''. L'idée est que pour les portions lointaines d'une texture, la texture utilisée est une ''mip-map'' de basse résolution. L'idée est alors de ne charger que la ''mip-map'' adéquate, pas les autres niveaux de détail. En clair, la texture de base n'est pas chargée en mémoire vidéo, mais la ''mip-map'' basse résolution l'est. ===Une texture à deux niveaux=== L'implémentation des textures virtuelles découpe les méga-textures en ''tiles'', en morceaux rectangulaires de taille modeste. En clair, le terrain est découpé en morceau rectangulaires/carrés. Seules les tiles nécessaires sont chargées en mémoire vidéo, pas les autres. Par exemple, les ''tiles'' non-visibles ne sont pas placées en mémoire vidéo, seules les ''tiles'' visibles le sont. De même, il y a une ''tile'' par niveau de mip-map : seul la tile correspondant le niveau adéquat est en mémoire vidéo, les autres niveaux de détail ne sont pas chargés. On peut faire une analogie avec la mémoire virtuelle, où les données sont découpées en pages, qui sont chargées en mémoire RAM à la demande, suivant les besoins, les données pouvant être swappées sur le disque dur si elles sont peu utilisées. Sauf qu'ici, il s'agit de textures qui sont découpées en pages chargées à la demande en mémoire vidéo, depuis la RAM système. Une texture virtuelle est en réalité un système à deux niveaux : une liste de ''tiles'' et les ''tiles'' elles-mêmes. La liste de ''tiles'' est appelée un '''atlas de texture''', c'est un peu l'équivalent de la ''tilemap'' pour le rendu 2D. Rendre une texture demande de calculer quelle ''tile'' contient le texel à afficher, consulter la ''tile'' en question, puis récupérer le texel adéquat dans cette ''tile''. La ''tile'' est donc une texture, mais la texture à charger est choisie parmi un ensemble, qui est ici l'atlas de texture. ===L'implémentation : logicielle versus matérielle=== Les textures virtuelles ont été utilisées pour la première fois par les jeux Rage 1 et 2 d'IdSoftware, et quelques jeux ultérieurs comme DOOM 2016. IdSoftware les appelait des '''''mega-textures'''''. L'optimisation permettait des gains en performance assez impressionnants. Le jeu Rage 1 utilisait une texture carrée unique de 128k pixels de côté pour rendre le terrain. En théorie, une telle texture devrait prendre 64 giga-octets, mais le jeu tournait correctement avec 512 méga-octets de RAM, poussivement avec seulement 256 méga-octets de RAM. De nos jours, les textures virtuelles sont supportées par beaucoup de jeux vidéos, les moteurs les plus courants gèrent de telles textures de manière logicielles. Mais quelques GPU récents supportent les textures virtuelles. Sur les GPU récents, l'atlas de texture est géré nativement par le matériel. Le GPU choisit quelle ''tile'', quelle texture choisir pour rendre le texel adéquat. Pour cela, le GPU calcule quelle ''tile'' charger, consulte l'atlas de texture, et lit la texture de ''tile'' adéquate. Mais l'implémentation sur les GPU récents a de nombreuses limitations. La limitation la plus importante est que la taille des textures virtuelles ne peut pas dépasser la taille d'une texture normale, soit 32768 pixels de côté pour une texture carrée environ sur les GPU de 2020. De plus, le chargement d'une ''tile'' est très lent. En clair, dès qu'on veut changer de niveau de mip-map pour une tile, ou dès qu'une tile devient visible, le chargement de la tile peut facilement prendre plusieurs centaines de millisecondes. Le filtrage de texture est très complexe avec des textures virtuelles, ce qui fait que le filtrage de texture virtuelle est souvent soumis à des limitations que les textures normales n'ont pas, notamment pour le filtrage anisotropique. {{NavChapitre | book=Les cartes graphiques | prev=Le rasterizeur | prevText=Le rasterizeur | next=Les Render Output Target | nextText=Les Render Output Target }}{{autocat}} t3zyus8ldk1hhrpz9xs5iseqhbdjz9q 763954 763944 2026-04-18T15:32:33Z Mewtow 31375 /* L'implémentation matérielle du placage de textures */ 763954 wikitext text/x-wiki [[File:Texture mapping.png|vignette|''Texture mapping'']] Les '''textures''' sont des images que l'on va plaquer sur la surface d'un objet, du papier peint en quelque sorte. Les cartes graphiques supportent divers formats de textures, qui indiquent comment les pixels de l'image sont stockés en mémoire : RGB, RGBA, niveaux de gris, etc. Une texture est donc composée de "pixels", comme toute image numérique. Pour bien faire la différence entre les pixels d'une texture, et les pixels de l'écran, les pixels d'une texture sont couramment appelés des ''texels''. ==Le placage de textures inverse== Pour rappel, plaquer une texture sur un objet consiste à attribuer un texel à chaque sommet, ce qui est fait lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet. Chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. Dans les faits, on n'utilise pas de coordonnées entières de ce type. Les coordonnées de texture sont deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale. Le nom donnée à cette technique de description des coordonnées de texture s'appelle l''''''UV Mapping'''''. [[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]] Les API 3D modernes gèrent des textures en trois dimensions, ce qui ajoute une troisième coordonnée de texture notée w. Dans ce qui va suivre, nous allons passer les textures en trois dimensions sous silence. Elles ne sont pas très utilisées, la quasi-totalité des jeux vidéo et applications 3D utilisant des textures en deux dimensions. Par contre, le matériel doit gérer les textures 3D, ce qui le rend plus complexe que prévu. Il faut ajouter quelques circuits pour, de quoi gérer la troisième coordonnée de texture, etc. Lors de la rastérisation, chaque fragment se voit attribuer un sommet, et donc la coordonnée de texture qui va avec. Si un pixel est situé pile sur un sommet, la coordonnée de texture de ce sommet est attribuée au pixel. Si ce n'est pas le cas, la coordonnée de texture finale est interpolée à partir des coordonnées des trois sommets du triangle rastérisé. L'interpolation en question a lieu dans l'étape de rastérisation, comme nous l'avons vu dans le chapitre précédent. Le fait qu'il y ait une interpolation fait que les coordonnées du pixel gagent à être des nombres flottants. On pourrait faire une interpolation avec des coordonnées de texture entières, mais les arrondis et autres imprécisions de calcul donneraient un résultat graphiquement pas terrible, et empêcheraient d'utiliser les techniques de filtrage de texture que nous verrons dans ce chapitre. À partir de ces coordonnées de texture, la carte graphique calcule l'adresse du texel qui correspond, et se charge de le lire. Et toute la magie a lieu dans ce calcul d'adresse, qui part de coordonnées de texture flottante, pour arriver à une adresse mémoire. Le calcul de l'adresse du texel se fait en plusieurs étapes, que nous allons voir ci-dessous. La première étape convertit les coordonnées flottantes en coordonnées entières, qui disent à quel ligne et colonne se trouve le texel voulu dans la texture. L'étape suivante transforme ces coordonnées x,y entières en adresse mémoire. ===La normalisation des coordonnées=== J'ai dit plus haut que les coordonnées de texture sont des coordonnées flottantes, comprises entre 0 et 1. Mais il faut savoir que les pixels shaders peuvent modifier celles-ci pour mettre en œuvre certains effets graphiques. Et le résultat peut alors se retrouver en-dehors de l'intervalle 0,1. C'est quelque chose de voulu et qui est traité par la carte graphique automatiquement, sans que ce soit une erreur. Au contraire, la manière dont la carte graphique traite cette situation permet d'implémenter des effets graphiques comme des textures en damier ou en miroir. [[File:Clamp tile.jpg|vignette|Clamp tile]] Il existe globalement trois méthodes très simples pour gérer cette situation, qui sont appelés des '''modes d'adressage de texture'''. La première méthode est de faire en sorte que le résultat sature. Si une coordonnée est inférieur à 0, alors on la remplace par un zéro. Si elle est supérieure à 1, on la ramène à 1. Avec cette méthode, tout se passe comme si les bords de la texture étaient étendus et remplissaient tout l'espace autour de la texture. Le tout est illustré ci-dessous. Ce mode d'accès aux textures est appelé le '''''clamp'''''. Une autre solution retire la partie entière de la coordonnée, elle coupe tout ce qui dépasse 1. Pour le dire autrement, elle calcule le résultat modulo 1 de la coordonnée. Le résultat est que tout se passe comme si la texture était répétée à l'infini et qu'elle pavait le plan. Une autre méthode remplit les coordonnées qui sortent de l’intervalle 0,1 avec une couleur préétablie, configurée par le programmeur. ===La conversion des coordonnées de textures flottantes en adresse mémoire=== Une fois la normalisation effectuée, les coordonnées de texture sont utilisées pour lire le texel voulu. Pour cela, les coordonnées de texte sont transformées en adresse mémoire, adresse qui pointe sur le texel ayant ces cordonnées. Pour cela, la première étape est de transformer les coordonnées flottantes u,v en coordonnées entières x,y qui pointent sur un texel. Pour cela, il suffit de multiplier les coordonnées flottantes u,v par la résolution de la texture accédée. Pour un écran de résolution <math>\text{height,width}</math>, le calcul est le suivant : : <math>x = u \times \text{width}</math> : <math>y = v \times \text{height}</math> Le résultat est un nombre avec une partie entière et une partie fractionnaire. La partie entière des deux coordonnées donne la position x,y voulue, et la partie fractionnaire est conservée pour le filtrage de textures, mais passons cela sous silence pour le moment. La seconde étape prend les coordonnées entières x,y et calcule l'adresse mémoire du texel. L'adresse dépend de la position de la texture en mémoire, précisément de son début, son premier texel, mais aussi de la position du texel par rapport au début de la texture. Et calculer cette position intra-texture dépend de la manière dont les texels sont stockés en mémoire. ====Les textures naïves==== Les programmeurs qui lisent ce cours s'attendent certainement à ce que la texture soit stockée en mémoire ligne par ligne, ou colonne par colonne. Cela veut dire que le premier pixel en partant d'en haut à gauche est stocké en premier, puis celui immédiatement à sa droite, puis celui encore à droite, et ainsi de suite. Une fois qu'on arrive à la fin d'une ligne, on passe à la ligne suivante, en-dessous. Cette organisation ligne par ligne s'appele l'organisation '''''row major order'''''. On peut faire pareil, mais colonne par colonne, ce qui donne le '''''column major order'''''. [[File:Speicheranordnung Feld.svg|centre|vignette|upright=2|Row et column major order.]] Maintenant, supposons que la texture commence à l'adresse <math>A_\text{texture}</math>, qui est l'adresse du premier texel. La texture a une résolution de <math>\text{width}</math> texels de large et <math>\text{height}</math> texels de haut. Par définition, les coordonnées X et Y des texels commencent à 0, ce qui fait que le pixel en haut à gauche a les coordonnées 0,0. L'adresse du pixel se calcule comme suit : : <math>A_\text{pixel} = A_\text{texture} + (\text{taille d'une ligne en octets} \times Y) + (\text{taille d'un texel en octets} \times X)</math> La taille d'un pixel en mémoire est notée T. La taille d'une ligne en mémoire est de <math>width \times T</math>, par définition, vu qu'elle fait <math>width</math> texels. On a donc : : <math>A_\text{pixel} = A_\text{texture} + (width \times T \times Y) + (T \times X)</math> La formule se réécrit comme suit : : <math>A_\text{pixel} = A_\text{texture} + T \times (width \times Y + X)</math> Le calcul d'adresse est donc assez simple. Malheureusement, les textures ne sont pas stockées de cette manière en mémoire vidéo. En effet, elle se marie mal avec les opérations de filtrage de texture que nous allons voir dans ce qui suit. Le filtrage d'un texel dépend de ses voisins du dessus et du dessous. Le fait que la texture n'est pas forcément parcourue ligne par ligne fait que stocker une texture ligne par ligne n'est pas l'idéal. De même, les textures sont déformées par la perspective. L'affichage de la texture ne se fait alors pas ligne par ligne, mais en parcourant la texture en diagonale, l'angle de la diagonale correspondant approximativement à l'angle que fait la verticale de la texture avec le regard. Vu qu'on ne connait pas à l'avance l'angle que fera la diagonale de parcours, on doit ruser. ====Les textures tilées==== Une première solution à ce problème est celle des '''textures tilées'''. Avec ces textures, l'image de la texture est découpée en ''tiles'', des rectangles ou en carrés de taille fixe, généralement des carrés de 4 pixels de côté. Les tiles ont une largeur et une longueur égales, afin de simplifier les calculs : on divise X et Y par le même nombre. De plus, leur largeur et leur longueur sont une puissance de deux, afin de simplifier les calculs d'adresse. Les ''tiles'' sont alors mémorisée les unes après les autres dans le fichier de la texture. [[File:Texture tilée.png|centre|vignette|upright=2|Texture tilée]] La formule de calcul d'adresse vue plus haut doit être adaptée pour tenir compte des tiles. Pour cela, il faut remplacer la taille d'un texel par la taille d'une tile, et que la largeur de la texture soit exprimée en nombre de tiles. De plus, on doit adapter les coordonnées des texels pour donner des coordonnées de tile. Généralement, les tiles sont des carrés de N pixels de côté, ce qui fait qu'on peut regrouper les lignes et les colonnes par paquets de N. Il suffit donc de diviser Y et X pour obtenir les coordonnées de la tile, de même que la larguer. La formule pour calculer la position de la énième tile est alors la suivante : : <math>\text{adresse d'une tile} = \text{adresse du début de la texture} + \text{Taille mémoire d'une tile} \times \left( {\text{Width} \over N} \times {Y \over N} + {X \over N} \right)</math> On peut réécrire le tout comme suit : : <math>\text{adresse d'une tile} = \text{adresse du début de la texture} + K \times \left( {Y \over N} + X \right)</math>, avec K une constante connue à la compilation des shaders. Vu que les tiles sont carrées avec une largeur qui est une puissance de deux, la multiplication par la taille d'une tile en mémoire se simplifie : on passe d'une multiplication entière à des décalages de bits. Même chose pour le calcul de l'adresse de la tile à partir des coordonnées x,y : ils impliquent des divisions par une puissance de deux, qui deviennent de simples décalages. La position d'un pixel dans une tile dépend du format de la texture, mais peut se calculer avec quelques calculs arithmétiques simples. Dans les cas les plus simples, les pixels sont mémorisés ligne par ligne, ou colonne par colonne. Mais ce n'est pas systématiquement le cas. Toujours est-il que les calculs pour déterminer l'adresse sont simples, et ne demandent que quelques additions ou multiplications. Mais avec les formats de texture utilisés actuellement, les tiles sont chargées en entier dans le cache de texture, sans compter que diverses techniques de compression viennent mettre le bazar, comme on le verra dans la suite de cours. Un avantage de l'organisation en tiles est qu'elle se marie bien avec le parcours des textures. On peut parcourir une texture dans tous les sens, horizontal, vertical, ou diagonal, on sait que les prochains pixels ont de fortes chances d'être dans la même tile. Si on rentre dans une tile par la gauche en haut, on a encore quelques pixels à parcourir dans la tile, par exemple. De même, le filtrage de textures est facilité. On verra dans ce qui va suivre que le filtrage de texture a besoin de lire des blocs de 4 texels, des carrés de 2 pixels de côté. Avec l'organisation en tile, on est certain que les 4 texels seront dans la même tile, sauf s'ils ont le malheur d'être tout au bord d'une tile. Ce dernier cas est assez rare, et il l'est d'autant plus que les tiles sont grandes. Enfin, un dernier avantage est que les tiles sont généralement assez petites pour tenir tout entier dans une ligne de cache. Le cache de texture est donc utilisé à merveille, ce qui rend les accès aux textures plus rapides. ====Les textures basées sur des ''z-order curves''==== Les formats de textures théoriquement optimaux utilisent une '''''Z-order curve''''', illustrée ci-dessous. L'idée est de découper la texture en quatre rectangles identiques, et de stocker ceux-ci les uns à la suite des autres. L'intérieur de ces rectangles est lui aussi découpé en quatre rectangles, et ainsi de suite. Au final, l'ordre des pixels en mémoire est celui illustré ci-dessous. [[File:Z-CURVE.svg|centre|vignette|upright=2|Construction d'une ''Z-order curve''.]] Les texels sont stockés les uns à la suite des autres dans la mémoire, en suivant l'ordre donnée par la ''Z-order curve''. Le calcul d'adresse calcule la position du texel en mémoire, par rapport au début de la texture, et ajoute l'adresse du début de la texture. Mais tout le défi est de calculer la position d'un texel en mémoire, à partir des coordonnées x,y. Le calcul peut sembler très compliqué, mais il n'en est rien. Le calcul demande juste de regarder les bits des deux coordonnées et de les combiner d'une manière particulièrement simple. Il suffit de placer le bit de poids fort de la coordonnée x, suivi de celui de la coordonnée y, et de faire ainsi de suite en passant aux bits suivants. [[File:Zcurve45bits.png|centre|vignette|upright=1.5|Calcul de la position d'un élément dans une ''Z-order curve'' à partir des coordonnées x et y.]] L'avantage d'une telle organisation est que la textures est découpées en ''tiles'' rectangulaires d'une certaine taille, elles-mêmes découpées en ''tiles'' plus petites, etc. Et il se trouve que cette organisation est parfaite pour le cache de texture. L'idéal pour le cache de texture est de charger une ''tile'' complète dans le cache de textures. Quand on accède à un texel, on s'assure que la ''tile'' complète soit chargée. Mais cela demande de connaitre à l'avance la taille d'une ''tile''. Les formats de texture fournissent généralement une ''tile'' carré de 4 pixels de côté, mais cela donnerait un cache trop petit pour être vraiment utile. Avec cette méthode, on s'assure qu'il y ait une ''tile'' avec la taille optimale. Les ''tiles'' étant découpées en ''tiles'' plus petites, elles-mêmes découpées, et ainsi de suite, on s'assure que la texture est découpées en ''tiles'' de taille variées. Il y aura au moins une ''tile'' qui rentrera tout pile dans le cache. ==L'implémentation matérielle du placage de textures== Pour résumer, la lecture d'un texel demande d'effectuer plusieurs étapes. Dans le cas le plus simple, sans ''mip-mapping'' ou ''cubemapping'', on doit effectuer les étapes suivantes : * Il faut d'abord normaliser les coordonnées de texture pour qu'elles tombent dans l'intervalle [0,1] en fonction du mode d'adressage désiré. * Ensuite, les coordonnées u,v doivent être converties en coordonnées entières, ce qui demande une multiplication flottante. * Enfin, l'adresse finale est calculée à partir des coordonnées entières et en ajoutant l'adresse de base de la texture (et éventuellement avec d'autres calculs arithmétiques suivant le format de la texture). Tout cela pourrait être fait par le pixel shaders, mais cela implique beaucoup de calculs répétitifs et d'opérations arithmétiques assez lourdes, avec des multiplications flottantes, des additions et des multiplications entières, etc. Faire faire tous ces calculs par les shaders serait couteux en performance, sans compter que les shaders deviendraient plus gros et que cela aurait des conséquences sur le cache d'instruction. De plus, certaines de ces étapes peuvent se faire en parallèle, comme les deux premières, ce qui colle mal avec l'aspect sériel des shaders. Aussi, l'unité de texture des processeurs de shaders incorpore une unité de calcul d'adresse spéciale pour faire ces calculs directement en matériel. L'unité de texture contient donc au minimum deux circuits : un circuit de calcul d'adresse, et un circuit d'accès à la mémoire. Toute la difficulté tient dans le calcul d'adresse, plus que dans le circuit de lecture. Le calcul d'adresse est conceptuellement réalisé en deux étapes. La première étape qui transforme les coordonnées u,v en coordonnées x,y qui donne le numéro de la ligne et de la colonne du texel dans la texture. La seconde étape prend ces deux coordonnées x,y et l'adresse de la texture, puis détermine l'adresse de la tile à lire. [[File:Unité de texture simple.png|centre|vignette|upright=2|Unité de texture simple]] ===La gestion des accès mémoire=== L'unité de texture doit tenir compte du fait que la mémoire vidéo met du temps à lire une texture. En théorie, l'unité de texture ne devrait pas accepter de nouvelle demande de lecture tant que celle en cours n'est pas terminée. Mais faire ainsi demanderait de bloquer tout le pipeline, de l'''input assembler'' au unités de''shaders'', ce qui est tout sauf pratique et nuirait grandement aux performances. La solution est de mettre en attente les lectures de texture pendant que la mémoire est occupée. La manière la plus simple est de les mettre en attente dans une mémoire FIFO. Cela implique que les accès mémoire s’exécutent dans l'ordre demandé par le ''shader'' et/ou l'unité de rastérisation, il n'y a pas de réorganisation des accès mémoire ou d’exécution dans le désordre des accès mémoire. [[File:Texture prefetching.png|centre|vignette|upright=1.5|Accès mémoire simultanés.]] Évidemment, quand la mémoire FIFO est pleine, l'unité de texture est totalement bloquée. Le rasteriseur est prévenu que l'unité de texture ne peut pas accepter de nouvelle lecture de texture. En pratique, la FIFO est généralement d'une taille respectable et permet de mettre en attente beaucoup de lectures. Il faut de plus noter qu'il y a une FIFO par processeur de ''shader'' sur les cartes graphiques modernes. Quand elle est pleine, le processeur cesse d'exécuter de nouveaux accès mémoire, mais peut continuer à exécuter des ''shaders'' dans les autres unités de calcul, pas besoin de bloquer complétement le pipeline. ===L'intégration du cache de textures=== Il faut noter que les unités de texture incorporent aussi un cache de texture, voire plusieurs. L'intégration des caches de texture avec la mémoire FIFO précédente est quelque peu compliqué, car il faut garantir que les lectures de texture se fassent dans le bon ordre. On ne peut pas exécuter une lecture dans le cache alors que des lectures précédentes sont en attente de lecture en mémoire vidéo. Et cela pose un gros problème : une lecture dans le cache de texture prend quelques dizaines de cycles d'horloge, alors qu'une lecture en mémoire vidéo en prend facilement 400 à 800 cycles, parfois plus. Et cela fait que l'ordre des accès mémoire peut s'inverser. Prenons par exemple un accès au cache précédé et suivi par deux accès en mémoire vidéo. Le premier démarre au cycle 1, et se termine au cycle numéro 400. L'accès au cache commence au cycle 2 et se termine 20 cycles après, au cycle numéro 22. En clair, la lecture dans le cache s'est terminée avant l'accès mémoire qui le précède. Les textures ne sont donc plus lues dans l'ordre. Et il faut trouver une solution pour éviter cela. La solution est de retarder les lectures dans le cache tant que tous les accès précédents ne sont pas terminés. Mais pour retarder les lectures en question, il faut d'abord savoir si la lecture atterrit dans le cache ou non, ce qui demande d'accéder au cache. On fait face à un dilemme : on veut retarder les accès au cache, mais les différencier des lectures déclenchant des accès mémoire demande d'accéder au cache en premier lieu. La solution est décrite dans l'article "Prefetching in a Texture Cache Architecture" par Igehy et ses collègues. Elle se base sur deux idées combinées ensemble. La première idée est de séparer l'accès au cache en deux : une étape qui vérifie si les texels à lire sont dans le cache, et une étape qui accède aux données dans le cache lui-même. Un cache de texture est donc composé de deux circuits principaux. Le premier vérifie la présence des texels dans le cache. Il reçoit l'adresse mémoire à lire, et détermine si une copie de la donnée associée est dans le cache ou non. Pour cela, il utilise un système de tags qu'on ne détaillera pas ici, mais qui donne son nom à l'unité de vérification : l''''unité de tag'''. Ensuite, en plus de l'unité de tags, il y a une mémoire qui stocke les données, la mémoire cache proprement dite. Par simplicité, cette mémoire est une simple mémoire RAM adressable avec des adresses mémoires des plus normales, chaque ligne de cache correspondant à une adresse. Ce genre de cache séparé en deux mémoires est appelé un ''phased cache'', pour ceux qui veulent en savoir plus. [[File:Phased cache.png|centre|vignette|upright=1.5|Phased cache]] La seconde idée est de retarder l'accès au cache entre les deux phases. La première étape d'un accès mémoire vérifie si la donnée est dans le cache ou non. Puis, on retarde la lecture des données, pour attendre que toutes les lectures précédentes soient terminées. Et enfin, troisième étape : la lecture des texels dans la mémoire cache proprement dite. Les accès mémoire passant par la mémoire vidéo se font de la même manière, à une différence près : la lecture dans le cache est remplacée par la lecture en mémoire vidéo. Tout démarre avec une demande à l'unité de tags, qui vérifie si le texel est dans le cache ou non. Puis on retarde l'accès tant que la mémoire vidéo est occupée, puis on effectue la lecture en mémoire vidéo. Si ce n'est pas le cas, l'accès mémoire est envoyé à la mémoire vidéo comme précédemment, à savoir qu'il est mis en attente dans une mémoire FIFO, puis envoyé à la mémoire vidéo dès que celle-ci est libre. Mais en sortie de la mémoire, la donnée lue est envoyée dans le cache de texture, par dans l'unité de filtrage. Pour savoir où placer la donnée lue, l'unité de tag a réservé une ligne de cache précise, une adresse bien précise. L'adresse en question est disponible en lisant une autre mémoire FIFO, qui a mis en attente l'adresse en question, en attendant que l'accès mémoire se termine. La donnée est alors écrite dans le cache, puis lue par l'unité de filtrage de textures. Pour une lecture dans le cache, le déroulement est similaire, mais sans le passage par la mémoire. La lecture fait une demande à l'unité de tag, et celle-ci répond que la donnée est bien dans le cache. Elle place alors l'adresse à lire dans la file d'attente. Une fois que les accès mémoire précédents sont terminés, l'adresse sort de la file d'attente et est envoyée à la mémoire de données. La lecture s'effectue, les texels sont envoyés à l'unité de filtrage de textures. La seule différence avec un ''phased cache'' normal est l'insertion de l'adresse à lire dans une FIFO qui vise à mettre en attente [[File:Unité de texture avec un cache de texture.png|centre|vignette|upright=2.0|Unité de texture avec un cache de texture]] Pour résumer, l'implémentation précédente garantit une exécution des lectures dans leur ordre d'arrivée. Et pour cela, elle retarde les lectures dans le cache tant que les lectures en mémoire précédentes ne sont pas terminées. L'accès au cache est plus rapide que l'accès en mémoire vidéo, mais le retard ajouté pour garantir l'ordre des lectures fait que le temps d'accès est très long. ==Le mip-mapping== Le '''mip-mapping''' a pour but de légèrement améliorer les graphismes des objets lointains, tout en rendant les calculs de texture plus rapides. Formellement, le ''mip-mapping'' est une technique de filtrage de texture, mais nous l'abordons maintenant car elle est surtout liée au calcul d'adresse. Les unités de texture ont des circuits de filtrage de texture séparés des circuits de ''mip-mapping'' et de calcul d'adresse, d'où le fait que nous en parlons séparément. Le problème résolu par le ''mip-mapping'' est le rendu des textures lointaines. Si une texture est plaquée sur un objet lointain, une bonne partie des détails est invisible pour l'utilisateur. Un pixel de l'écran est associé à plusieurs texels. Idéalement, la carte graphiques devrait lire tous ces texels et en faire une sorte de moyenne pondérée, pour calculer la couleur finale du pixel. Mais dans les faits, ce serait très gourmand et compliqué à implémenter en hardware. Une solution serait de ne garder que quelque texels, mais cela a tendance à créer des artefacts visuels (les textures affichées ont tendance à pixeliser). Le ''mip-mapping'' permet de réduire ces deux problèmes en même temps en précalculant cette moyenne pondérée pour des distances prédéfinies. L'idée est d'utiliser plusieurs exemplaires d'une même texture à des résolutions différentes, chaque exemplaire étant adapté à une certaine distance. Par exemple, une texture sera stocké avec un exemplaire de 512 * 512 pixels, un autre de 256 * 256, un autre de 128 * 128 et ainsi de suite jusqu’à un dernier exemplaire de 32 * 32 pixel. Chaque exemplaire correspond à un '''niveau de détail''', aussi appelé ''Level Of Detail'' (abrévié en LOD). La résolution utilisée diminue d'autant plus que l'objet est situé loin de la caméra. Les objets proches seront rendus avec la texture 512*512, ceux plus lointains seront rendus avec la texture de résolution 256*256, les textures 128*128 seront utilisées encore plus loin, et ainsi de suite jusqu'aux objets les plus lointains qui sont rendus avec la texture la plus petite de 32*32. [[File:MipMap Example STS101.jpg|centre|vignette|upright=2|Exemples de mip-maps.]] Le ''mip-mapping'' améliore grandement la qualité d'image. L'image d'exemple ci-dessous le montre assez bien. [[File:Mipmapping example.png|centre|vignette|upright=2|Exemple de mipmapping.]] Pour faciliter les calculs d'adresse, les LOD d'une même texture sont stockées les uns après les autres en mémoire (dans un tableau, comme diraient les programmeurs). Ainsi, pas besoin de se souvenir de la position en mémoire de chaque LOD : l'adresse de la texture de base, et quelques astuces arithmétiques suffisent. Prenons le cas où la texture de base a une taille L. le premier exemplaire est à l'adresse 0, le second niveau de détail est à l'adresse L, le troisième à l'adresse L + L/4, le suivant à l'adresse L + L/4 + L/16, et ainsi de suite. Le calcul d'adresse demande juste connaître le niveau de détails souhaité et l'adresse de base de la texture. Le niveau de détail voulu est calculé par les pixel shaders, en fonction de la coordonnée de profondeur du pixel à traiter. Évidemment, cette technique consomme de la mémoire vidéo, vu que chaque texture est dupliquée en plusieurs exemplaires, en plusieurs LOD. Dans le détail, la technique du mip-mapping prend au maximum 33% de mémoire en plus (sans compression). Cela vient du fait qu'en prenant une texture dexu fois plus petite, elle prend 4 fois moins de mémoire : 2 fois moins de pixels en largeur, et 2 fois moins en hauteur. Donc, si je pars d'une texture de base contenant X pixels, la totalité des LODs, texture de base comprise, prendra X + (X/4) + (X/16) + (X/256) + … Un petit calcul de limite donne 4/3 * X, soit 33% de plus. ===L'implémentation du mip-mapping dans l'unité de texture=== Le ''mip-mapping'' est lui aussi pris en charge par l'unité de calcul d'adresse, car cette technique change l'adresse de base de la texture. La gestion du ''mip-mapping'' est cependant assez complexe. Il est possible de laisser le pixel shader calculer quel niveau de détail utiliser, en fonction de la coordonnée de profondeur z du pixel à afficher. La carte graphique détermine alors automatiquement quelle texture lire, quel niveau de détail, automatiquement. Elle détermine aussi la bonne résolution pour la texture, qui est égal à la résolution de la texture de base, divisée par le niveau de détail. Pour résumer, le niveau de détail est envoyé aux unités de texture, qui s'occupent de calculer l'adresse de base et la résolution adéquates. Quelques calculs arithmétiques simples, donc, qui s'implémentent facilement avec quelques circuits. Mais une autre méthode laisse la carte graphique déterminer le niveau de détail par elle-même. Dans ce cas, cela demande, outre les deux coordonnées de texture, de calculer la dérivée de ces deux coordonnées dans le sens horizontal et vertical, ce qui fait quatre dérivées (deux dérivées horizontales, deux verticales). Les quatre dérivées sont les suivantes : : <math>\frac{du}{dx}</math>, <math>\frac{dv}{dx}</math>, <math>\frac{du}{dy}</math>, <math>\frac{dv}{dy}</math> Un bon moyen pour obtenir les dérivées demande de regrouper les pixels par groupes de 4 et de faire la différence entre leurs coordonnées de texture respectives. On peut calculer les deux dérivées horizontales en comparant les deux pixels sur la même ligne, et les deux dérivées verticales en comparant les deux pixels sur la même colonne. Mais cela demande de rastériser les pixels par groupes de 4, par ''quads''. Et c'est ce qui est fait sur les cartes graphiques actuelles, qui rastérisent des groupes de 4 pixels à la fois. [[File:Texture sampler unit with mipmapping.png|centre|vignette|upright=2.0|Unité de texture avec mipmapping.]] Malheureusement, le calcul exact utilisé pour le choix de la mip-map dépend du GPU considéré et peu de chose est connu quant à ces algorithmes. Il est possible d'inférer le comportement à partir d'observations, mais guère plus. Pour ceux qui veulent en savoir plus, je conseille la lecture de cet article de blog : * [https://pema.dev/2025/05/09/mipmaps-too-much-detail/ Mipmap selection in too much detail] ==Le filtrage de textures== Plaquer des textures sans autre forme de procès ne suffit pas à garantir des graphismes d'une qualité époustouflante. La raison est que les sommets et les texels ne tombent pas tout pile sur un pixel de l'écran : le sommet associé au texel peut être un petit peu trop en haut, ou trop à gauche, etc. Une explication plus concrète fait intervenir les coordonnées de texture. Souvenez-vous que lorsque l'on traduit une coordonnée de texture u,v en coordonnées x,y, on obtient un résultat qui ne tombe pas forcément juste. Souvent, le résultat a une partie fractionnaire. Si celle-ci est non-nulle, cela signifie que le texel/sommet n'est pas situé exactement sur le pixel voulu et que celui-ci est situé à une certaine distance. Concrètement, le pixel tombe entre quatre texels, comme indiqué ci-dessous. [[File:Filtrage texture.png|centre|vignette|upright=2.0|Position du pixel par rapport aux texels.]] Pour résoudre ce problème, on doit utiliser différentes techniques d'interpolation, aussi appelées techniques de '''filtrage de texture''', qui visent à calculer la couleur du pixel final en fonction des texels qui l'entourent. Il existe de nombreux types de filtrage de textures, qu'il s'agisse du filtrage linéaire, bilinéaire, trilinéaire, anisotropique et bien d'autres. Tous ont besoin d'avoir certaines informations qui sont généralement fournies par les circuits de calcul d'adresse. La première est clairement la partie fractionnaire des coordonnées x,y. La seconde est la dérivée de ces deux coordonnées dans le sens horizontal et vertical., ce qui fait quatre dérivées (deux dérivées horizontales, deux verticales). Toujours est-il que le filtrage de texture est une opération assez lourde, qui demande beaucoup de calculs arithmétiques. On pourrait en théorie le faire dans les pixels shaders, mais le cout en performance serait absolument insoutenable. Aussi, les cartes graphiques intègrent toutes un circuit dédié au filtrage de texture, le ''texture sampler''. Même les plus anciennes cartes graphiques incorporent une unité de filtrage de texture, ce qui nous montre à quel point cette opération est importante. [[File:Texture unit.png|centre|vignette|upright=2.0|Unité de texture.]] On peut configurer la carte graphique de manière à ce qu'elle fasse soit du filtrage bilinéaire, soit du filtrage trilinéaire, on peut configurer le niveau de filtrage anisotropique, etc. Cela peut se faire dans les options de la carte graphique, mais cela peut aussi être géré par l'application. La majorité des jeux vidéos permettent de régler cela dans les options. Ces réglages ne concernent pas la texture elle-même, mais plutôt la manière dont l'unité de texture doit fonctionner. Ces réglages sur l''''état de l'unité de texture''' sont mémorisés quelque part, soit dans l'unité de texture elle-même, soit fournies avec la ressource de texture elle-même, tout dépend de la carte graphique. Certaines cartes graphiques mémorisent ces réglages dans les unités de texture ou dans le processeur de commande, et tout changement demande alors de réinitialiser l'état des unités de texture, ce qui prend un peu de temps. D'autres placent ces réglages dans les ressources de texture elles-mêmes, ce qui rend les modifications de configuration plus rapides, mais demande plus de circuits. D'autres cartes graphiques mélangent les deux options, certains réglages étant globaux, d'autres transmis avec la texture. Bref, difficile de faire des généralités, tout dépend du matériel et le pilote de la carte graphique cache tout cela sous le tapis. Maintenant que cela est dit, voyons quelles sont les différentes méthodes de filtrage de texture et comment la carte graphique fait pour les calculer. ===Le filtrage au plus proche=== La méthode de filtrage la plus simple consiste à colorier avec le texel le plus proche. Cela revient tout simplement à ne pas tenir compte de la partie fractionnaire des coordonnées x,y, ce qui est très simple à implémenter en matériel. C'est ce que l'on appelle le '''filtrage au plus proche''', aussi appelé ''nearest filtering''. Autant être franc, le résultat est assez pixelisé et peu agréable à l’œil. Par contre, le résultat est très rapide à calculer, vu qu'il ne demande aucun calcul à proprement parler. Elle ne fait pas appel à la parti fractionnaire des coordonnées entières de texture, ni aux dérivées de ces coordonnées. On peut combiner cette technique avec le mip-mapping, ce qui donne un résultat bien meilleur, bien que loin d'être satisfaisant. Au passage, toutes les techniques de filtrage de texture peuvent se combiner avec du mip-mapping, certaines ne pouvant pas faire sans. [[File:Interpolation-nearest.svg|centre|vignette|Filtrage de texture au plus proche.]] ===Le filtrage linéaire=== Le filtrage le plus simple est le '''filtrage linéaire'''. Il effectue une interpolation linéaire entre deux mip-maps, deux niveaux de détails. Pour comprendre l'idée, nous allons prendre une situation très simple, avec une texture carrée de 512 texels de côté. Le mip-mapping crée plusieurs textures : une de 256 texels de côté, une de 128 texels, une de 64, etc. Maintenant, la texture est sur un objet à une certaine distance de l'écran, vu de face. Le résultat est qu'elle correspond à l'écran à un carré de 300 pixels de côté (pas d'erreur : pixels, pas texels). Dans ce cas, la texture se trouve entre deux mip-maps : celle de 512 pixels de côté, celle de 256. Laquelle choisir ? Le filtrage au plus proche prend la texture de 512 pixels de côté. Le filtrage linéaire lui, fait autrement. Vu que la texture est entre deux mip-maps, l'idée est de prendre le texel au plus proche dans chaque texture et de faire une sorte de moyenne appelée l'interpolation linéaire. L'interpolation par du principe que la couleur varie entre les deux texels en suivant une fonction affine, illustrée ci-dessous. Ce ne serait évidemment pas le cas dans le monde réel, mais on supposer cela donne une bonne approximation de ce à quoi ressemblerait une texture à plus haute résolution. On peut alors calculer la couleur du pixel par une simple moyenne pondérée par la distance. Le résultat est que les transitions entre deux niveaux de détails sont plus lisses, moins abruptes. [[File:Lin interp -é.png|centre|vignette|upright=2.0|Interpolation linéaire.]] ===Le filtrage bilinéaire=== Le filtrage bilinéaire effectue une sorte de moyenne pondérée des quatre texels les plus proches du pixel à afficher. Pour cela, rappelez-vous ce qui a été dit plus haut : les coordonnées x,y d'un pixel ont une partie entière et une partie fractionnaire. Le filtrage au plus proche élimine les parties fractionnaires, ce qui donne une coordonnée x,y. Avec le filtrage bilinéaire, on prend les texels de coordonnées (x,y) ; (x+1,y) ; (x,y+1) ; (x+1,y+1), le pixel étant entre ces 4 texels. Mais le filtrage ne fait pas qu'une simple moyenne, il prend en compte les parties fractionnaires pour faire la moyenne. En effet, le pixel n'est pas au milieu du carré de texel, il est quelque part mais est souvent plus proche d'un texel que des autres. Et il faut donc pondérer la moyenne par les distances aux 4 texels. Pour cela, la moyenne est calculée à partir d'interpolations linéaires. Avec 4 pixels, nous allons devoir calculer la couleur de deux points intermédiaires. La couleur de ces deux points se calcule par interpolation linéaire, et il suffit d'utiliser une troisième interpolation linéaire pour obtenir le résultat. [[File:Bilin3.png|centre|vignette|upright=2|Filtrage bilinéaire de texture.]] Le circuit qui permet de faire l'interpolation bilinéaire est particulièrement simple. On trouve un circuit de chaque pour chaque composante de couleur de chaque texel : un pour le rouge, un pour le vert, un pour le bleu, et un pour la transparence. Chacun de ces circuit est composé de sous-circuits chargés d'effectuer une interpolation linéaire, reliés comme suit. [[File:Texture sampler unit.png|centre|vignette|Unité de filtrage bilinéaire.]] Vous noterez que le filtrage bilinéaire accède à 4 pixels en même temps. Fort heureusement, les textures sont stockées de manière à ce qu'on puisse charger les 4 pixels en une fois, comme on l'a vu plus haut. Le filtrage bilinéaire a de fortes chances que les 4 pixels filtrés soient dans la même ''tile'', la seule exception étant quand ils sont tout juste sur le bord d'une ''tile''. : La console de jeu Nintendo 64 n'utilise que trois pixels au lieu de quatre dans son interpolation bilinéaire, qui en devient une interpolation quasi-bilinéaire. La raison derrière ce choix est une question de performances, comme beaucoup de décisions de ce genre. Le résultat est un rendu imparfait de certaines textures. ===Le filtrage trilinéaire=== Avec le filtrage bilinéaire, des discontinuités apparaissent sur certaines surfaces. Par exemple, pensez à une texture de sol : elle est appliquée plusieurs fois sur toute la surface du sol. A une certaine distance, le LOD utilisé change brutalement et passe par exemple de 512*512 à 256*256, ce qui est visible pour un joueur attentif. De telles transitions sont lissées grâce au filtrage linéaire, il n'y a plus qu'à le combiner avec le filtrage bilinéaire. Rien d’incompatible : le premier filtre l'intérieur d'une mip-map, le second combine deux mip-maps. Le filtrage trilinéaire prend les deux mip-maps les plus proches, fait un filtrage bilinéaire avec chacune, puis fait une « une moyenne » pondérée entre les deux résultats. Le circuit de filtrage trilinéaire existe en plusieurs versions. La plus simple, illustrée ci-dessous, effectue deux filtrages bilinéaires en parallèle, dans deux circuits séparés, puis combine leurs résultats avec un circuit d'interpolation linéaire. Mais ce circuit nécessite de charger 8 texels simultanément. Qui plus est, ces 8 texels ne sont pas consécutifs en mémoire, car ils sont dans deux niveaux de détails/mip-maps différents. [[File:Parallel trilinear filtering.png|centre|vignette|upright=2.0|Unité de filtrage trilinéaire parallèle.]] Vu qu'on lit des texels dans deux mip-maps, les texels sont lus en deux fois : 4 texels provenant de la première mip-map, suivis par les 4 texels de l'autre mip-map. Les 4 premiers texels doivent donc être mis en attente dans des registres, en attendant que les 4 autres arrivent. Une amélioration du circuit précédent gère cela en ajoutant des registres. Il lit les 4 premiers texels, les filtre avec une interpolation bilinéaire, et mémorise le résultat dans un registre. Puis, il lit les 4 autres texels, les filtre, et met le résultat dans un second registre. A ce moment là, un circuit d'interpolation linéaire finit le travail. On économise donc un circuit d'interpolation bilinéaire, sans que les performances soient trop impactées. [[File:Filtrage trilineaire.png|centre|vignette|upright=1.0|Unité de filtrage trilineaire série.]] Modifier le circuit de filtrage ne suffit pas. Comme je l'ai dit plus haut, la dernière étape d'interpolation linéaire utilise des coefficients, qui lui sont fournis par des registres. Seul problème : entre le temps où ceux-ci sont calculés par l'unité de mip-mapping, et le moment où les texels sont chargés depuis la mémoire, il se passe beaucoup de temps. Le problème, c'est que les unités de texture sont souvent pipelinées : elles peuvent démarrer une lecture de texture sans attendre que les précédentes soient terminées. À chaque cycle d'horloge, une nouvelle lecture de texels peut commencer. La mémoire vidéo est conçue pour supporter ce genre de chose. Cela a une conséquence : durant les 400 à 800 cycles d'attente entre le calcul des coefficients, et la disponibilité des texels, entre 400 et 800 coefficients sont produits : un par cycle. Autant vous dire que mémoriser 400 à 800 ensembles de coefficient prend beaucoup de registres. ===Le filtrage anisotrope=== D'autres artefacts peuvent survenir lors de l'application d'une texture, la perspective pouvant déformer les textures et entraîner l'apparition de flou. La raison à cela est que les techniques de filtrage de texture précédentes partent du principe que la texture est vue de face. Prenez une texture carrée, par exemple. Vue de face, elle ressemble à un carré sur l'écran. Mais tournez la caméra, de manière à voir la texture de biais, avec un angle, et vous verrez que la forme de la texture sur l'écran est un trapèze, pas un carré. Cette déformation liée à la perspective n'est pas prise en compte par les méthodes de filtrage de texture précédentes. Pour le dire autrement, les techniques de filtrage précédentes partent du principe que les 4 texels qui entourent un pixel forment un carré, ce qui est vrai si la texture est vue de face, sans angle, mais ne l'est pas si la texture n'est pas perpendiculaire à l'axe de la caméra. Du point de vue de la caméra, les 4 texels forment un trapèze d'autant moins proche d'un carré que l'angle est grand. Pour corriger cela, les chercheurs ont inventé le '''filtrage anisotrope'''. En fait, je devrais plutôt dire : LES filtrages anisotropes. Il en existe un grand nombre, dont certains ne sont pas utilisés dans les cartes graphiques actuelles, soit car ils trop gourmand en accès mémoires et en calculs pour être efficaces, soit car ils ne sont pas pratiques à mettre en œuvre. Il est très difficile de savoir quelles sont les techniques de filtrage de texture utilisées par les cartes graphiques, qu'elles soient récentes ou anciennes. Beaucoup de ces technologies sont brevetées ou gardées secrètes, et il faudrait vraiment creuser les brevets déposés par les fabricants de GPU pour en savoir plus. Les algorithmes en question seraient de plus difficiles à comprendre, les méthodes mathématiques cachées derrière ces méthodes de filtrage n'étant pas des plus simple. [[File:Anisotropic filtering en.png|centre|vignette|upright=2|Exemple de filtrage anisotrope.]] ==La compression de textures== Les textures les plus grosses peuvent aller jusqu'au mébioctet, ce qui est beaucoup. Pour limiter la casse, les textures sont compressées. La '''compression de texture''' réduit la taille des textures, ce qui peut se faire avec ou sans perte de qualité. Elle entraîne souvent une légère perte de qualité lors de la compression. Toutefois, cette perte peut être compensée en utilisant des textures à résolution plus grande. Mais il s'agit là d'une technique très simple, beaucoup plus simple que les techniques que nous allons voir dans cette section. Nous allons voir quelque algorithmes de compression de textures de complexité intermédiaire, mais n'allons pas voir l'état de l'art. Il existe des formats de texture plus récents que ceux qui nous allons aborder, comme l{{'}}''Ericsson Texture Compression'' ou l{{'}}''Adaptive Scalable Texture Compression'', plus complexes et plus efficaces. Notons que les textures sont compressées dans les fichiers du jeu, mais aussi en mémoire vidéo. Les textures sont décompressées lors de la lecture. Pour cela, la carte graphique contient alors un circuit, capable de décompresser les textures lorsqu'on les lit en mémoire vidéo. Les cartes graphiques supportent un grand nombre de formats de textures, au niveau du circuit de décompression. Du fait que les textures sont décompressées à la volée, les techniques de compression utilisées sont assez particulières. La carte graphique ne peut pas décompresser une texture entière avant de pouvoir l'utiliser dans un ''pixel shader''. A la place, on doit pouvoir lire un morceau de texture, et le décompresser à la volée. On ne peut utiliser les méthodes de compression du JPEG, ou d'autres formats de compression d'image. Ces dernières ne permettent pas de décompresser une image morceau par morceau. Pour permettre une décompression/compression à la volée, les textures sont des textures tilées, généralement découpées en tiles de 4 * 4 texels. Les ''tiles'' sont compressées indépendamment les unes des autres. Et surtout, avec ou sans compression, la position des tiles en mémoire ne change pas. On trouve toujours une tile tous les T octets, peu importe que la tile soit compressée ou non. Par contre, une tile compressée n'occupera pas T octets, mais moins, là où une tile compressée occupera la totalité des T octets. En clair, compresser une tile fait qu'il y a des vides entre deux tiles dans al mémoire vidéo, mais ne change rien à leur place en mémoire vidéo qui est prédéterminée, peu importe que la texture soit compressée ou non. L'intérêt de la compression de textures n'est pas de réduire la taille de la texture en mémoire vidéo, mais de réduire la quantité de données à lire/écrire en mémoire vidéo. Au lieu de lire T octets pour une tile non-compressée, on pourra en lire moins. ===La palette indicée et la technique de ''Vector quantization''=== La technique de compression des textures la plus simple est celle de la '''palette indicée''', que l'on a entraperçue dans le chapitre sur les cartes d'affichage. La technique de '''''vector quantization''''' peut être vue comme une amélioration de la palette, qui travaille non pas sur des texels, mais sur des ''tiles''. À l'intérieur de la carte graphique, on trouve une table qui stocke toutes les ''tiles'' possibles. Chaque ''tile'' se voit attribuer un numéro, et la texture sera composé d'une suite de ces numéros. Quelques anciennes cartes graphiques ATI, ainsi que quelques cartes utilisées dans l’embarqué utilisent ce genre de compression. ===Les algorithmes de ''Block Truncation coding''=== La première technique de compression élaborée est celle du '''''Block Truncation Coding''''', qui ne marche que pour les images en niveaux de gris. Le BTC ne mémorise que deux niveaux de gris par ''tile'', que nous appellerons couleur 1 et couleur 2, les deux niveaux de gris n'étant pas le même d'une ''tile'' à l'autre. Chaque pixel d'une ''tile'' est obligatoirement colorié avec un de ces niveaux de gris. Pour chaque pixel d'une ''tile'', on mémorise sa couleur avec un bit : 0 pour couleur 1, et 1 pour couleur 2. Chaque ''tile'' est donc codée par deux entiers, qui codent chacun un niveau de gris, et une suite de bits pour les pixels proprement dit. Le circuit de décompression est alors vraiment très simple, comme illustré ci-dessous. [[File:Block Truncation coding.jpg|centre|vignette|upright=2.0|Block Truncation coding.]] La technique du BTC peut être appliquée non pas du des niveaux de gris, mais pour chaque composante Rouge, Vert et Bleu. Dans ces conditions, chaque ''tile'' est séparée en trois sous-''tiles'' : un sous-bloc pour la composante verte, un autre pour le rouge, et un dernier pour le bleu. Cela prend donc trois fois plus de place en mémoire que le BTC pur, mais cela permet de gérer les images couleur. ===Le format de compression S3TC / DXTC=== L'algorithme de '''Color Cell Compression''', ou CCC, améliore le BTC pour qu'il gère des couleurs autre que des niveaux de gris. Ce CCC remplace les deux niveaux de gris par deux couleurs. Une ''tile'' est donc codée avec un entier 32 bits par couleur, et une suite de bits pour les pixels. Le circuit de décompression est identique à celui utilisé pour le BTC. [[File:Color Cell Compression.jpg|centre|vignette|Color Cell Compression.]] [[File:Dxt1-memory-layout.png|vignette|Dxt1 et ''color cell compression''.]] Le format de compression de texture utilisé de base par Direct X, le DXTC, est une version amliorée de l'algorithme précédent. Il est décliné en plusieurs versions : DXTC1, DXTC2, etc. La première version du DXTC est une sorte d'amélioration du CCC : il ajoute une gestion minimale de transparence, et découpe la texture à compresser en ''tiles'' de 4 pixels de côté. La différence, c'est que la couleur finale d'un texel est un mélange des deux couleurs attribuée au bloc. Pour indiquer comment faire ce mélange, on trouve deux bits de contrôle par texel. Si jamais la couleur 1 < couleur2, ces deux bits sont à interpréter comme suit : * 00 = Couleur1 * 01 = Couleur2 * 10 = (2 * Couleur1 + Couleur2) / 3 * 11 = (Couleur1 + 2 * Couleur2) / 3 Sinon, les deux bits sont à interpréter comme suit : * 00 = Couleur1 * 01 = Couleur2 * 10 = (Couleur1 + Couleur2) / 2 * 11 = Transparent [[File:DXTC.jpg|centre|vignette|DXTC.]] Le circuit de décompression du DXTC ressemble alors à ceci : [[File:Circuit de décompression du DXTC.jpg|centre|vignette|upright=2.0|Circuit de décompression du DXTC.]] ===Les format DXTC 2, 3, 4 et 5 : l'ajout de la transparence=== Pour combler les limitations du DXT1, le format DXT2 a fait son apparition. Il a rapidement été remplacé par le DXT3, lui-même replacé par le DXT4 et par le DXT5. Dans le DXT3, la transparence fait son apparition. Pour cela, on ajoute 64 bits par ''tile'' pour stocker des informations de transparence : 4 bits par texel. Le tout est suivi d'un bloc de 64 bits identique au bloc du DXT1. [[File:Dxt23-memory-layout.png|centre|vignette|Dxt 2 et 3.]] Dans le DXT4 et le DXT5, la méthode utilisée pour compresser les couleurs l'est aussi pour les valeurs de transparence. L'information de transparence est stockée par un en-tête contenant deux valeurs de transparence, le tout suivi d'une matrice qui attribue trois bits à chaque texel. En fonction de la valeur des trois bits, les deux valeurs de transparence sont combinées pour donner la valeur de transparence finale. Le tout est suivi d'un bloc de 64 bits identique à celui qu'on trouve dans le DXT1. [[File:Dxt45-memory-layout.png|centre|vignette|Dxt 4 et 5.]] ===Le format de compression PVRTC=== Passons maintenant à un format de compression de texture un peu moins connu, mais pourtant omniprésent dans notre vie quotidienne : le PVRTC. Ce format de texture est utilisé notamment dans les cartes graphiques de marque PowerVR. Vous ne connaissez peut-être pas cette marque, et c'est normal : elle travaille surtout dans les cartes graphiques embarquées. Ses cartes se trouvent notamment dans l'ipad, l'iPhone, et bien d'autres smartphones actuels. Avec le PVRTC, les textures sont encore une fois découpées en ''tiles'' de 4 texels par 4, mais la ressemblance avec le DXTC s’arrête là. Chacque ''tile'' est codée avec : * une couleur codée sur 16 bits ; * une couleur codée sur 15 bits ; * 32 bits qui servent à indiquer comment mélanger les deux couleurs ; * et un bit de modulation, qui permet de configurer l’interprétation des bits de mélange. Les 32 bits qui indiquent comment mélanger les couleurs sont une collection de 2 paquets de 2 bits. Chacun de ces deux bits permet de préciser comment calculer la couleur d'un texel du bloc de 4*4. ==Annexe : le ''normal-mapping'' hardware== [[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]] Maintenant, parlons un peu du ''normal mapping'' et de son implémentation dans les unités de texture. Pour rappel, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser. Et pour comprendre quel est le rapport avec les textures, nous allons devoir faire quelques rappels sur l'éclairage par pixel. [[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]] L'éclairage se fait en utilisant de nombreux calculs, qu'on a détaillé dans le chapitre sur les bases du rendu 3D. Ceux-ci utilisent la normale d'un sommet, à savoir un vecteur orienté à la verticale de la surface. En théorie, il y a une normale par sommet, ce qui fait que les calculs d'éclairage doivent se faire au niveau géométrique, avant la rastérisation. Mais pour obtenir un éclairage de meilleure qualité, il y a des techniques qui calcule l'éclairage d'une scène 3D pixel par pixel. Une première technique d'éclairage par pixel interpole les normales lors de l'étape de rastérisation. On obtient alors un éclairage de Phong. Une autre solution est celle du ''normal mapping''. Le '''''normal mapping''''' précalcule les normales d'une surface dans une texture, appelée la ''normal map''. L'éclairage est alors réalisé par un pixel shader, qui lit les normales depuis cette texture et fait les calculs d'éclairage avec. ===L'usage de textures non-compressées pour les ''normal maps''=== La ''normal map'' est une texture, donc. Mais ce n'est pas une texture comme une autre. Elle mémorise un vecteur pour chaque texel, pas une couleur. Il est possible d'utiliser les formats de textures non-compressés, même si cela ne donne pas de bons résultats. Après tout, un vecteur est codé par trois coordonnées x,y,z, il est possible de coder chacune avec un octet et de packager cela dans une texture RGB classique. La coordonnée x va dans la composante Rouge, la coordonnée Y dans la composante Bleu, et la coordonnée z dans la composante z. Une première optimisation est de ne pas mémoriser les trois coordonnées, mais seulement deux d'entre elles et de calculer la troisième. En effet, les normales sont des vecteurs ''normalisés'', c'est à dire que leur longueur vaut 1, par construction. Vu que la taille est connue à l'avance, on peut en déduire la coordonnée z à partir des coordonnées x et y, avec l'usage du théorème de Pythagore. Par définition, <math>z^2 = 1 - x^2 - y^2</math>. Le calcul peut être fait dans le ''pixel shader'' sans problème. : Pour être précis, il faut que les normales soient définies d'une certaine manière pour que ça marche. Les normales ne sont pas définies dans le même système de coordonnées que le modèle 3D, mais dans un autre système appelé l'''espace tangent''. Notons que l'accès à la ''normal map'' se fait comme pour n'importe quelle texture : les texels sont lus en mémoire vidéo, puis le filtrage de texture est appliqué, et enfin le tout est envoyé au ''pixel shader''. En théorie, le filtrage effectue une sorte d'interpolation des normales automatique, ce qui permet de trouver la normale à un pixel précis, même s'il n'est pas sur un texel. Les solutions précédentes demandent d'utiliser des textures non-compressées, qui utilisent beaucoup de mémoire vidéo. Utiliser des textures compressées pourrait sembler résoudre le problème. Mais l'usage de textures compressées marche très mal pour les ''normal maps'', elles ne permettent pas d'obtenir une qualité ou une compression suffisante. Aussi, des formats de texture dédiés aux ''normal maps'' ont été inventés. ===Le format de texture 3Dc=== Le problème est que le rendu final n'est pas très beau. Et les opérations de filtrage ne donnent pas de très bons résultats. Pour remédier à ces problèmes, des formats de texture spécialisés pour les ''normal map'' ont été inventés. Le premier d'entre eux est le '''3Dc''', aussi appelé BC5 pour ''Block Compression 5''. Il utilise un octet par texel, au lieu de trois avec une texture RGB normale non-compressée. Et surtout : il est géré en matériel, directement dans l'unité de texture, qui peut décompresser les textures 3Dc et les filtrer ! Avec le 3Dc, seules coordonnées sont mémorisées, la troisième est calculée, comme dit plus haut. Pour le reste, la compression de la ''normal map'' ressemble un peu à celle des textures. La ''normal map'' est découpée en blocs de 4 par 4 texels de côté, chacune étant encodée en tenant compte de certaines redondances. Dans un bloc, les coordonnées x sont dans un certain intervalle, allant de <math>x_{min}</math> à <math>x_{max}</math>, idem pour la coordonnée y qui est dans l'intervalle <math>y_{min}</math>, <math>y_{max}</math>. L'intervalle <math>x_{min}</math> à <math>x_{max}</math> est coupé en 8 parts égales, ce qui fait 8 valeurs possibles dans cet intervalle, chacune correspondant à une valeur x possible pour une normale du bloc. La normale est donc encodée en précisant quelle valeur est la bonne, en utilisant un indice de 3 bits pour cela. La valeur exacte de la coordonnée x se calcule à partir de l'indice comme suit : : <math>x = x_{min} + \left( \frac{x_{min} - x_{min}}{8} \right) \times \text{indice} </math> Pour encoder un bloc de 4 par 4 normales, il faut donc : * Un octet pour chaque coordonnée <math>x_{min}</math>, <math>x_{max}</math>, <math>y_{min}</math>, <math>y_{max}</math>. * 6 bits par normale : 3 pour encoder le décalage de la coordonnée x, 3 pour la coordonnée y. Le tout permet d'encoder un bloc de 16 normales en seulement 128 bits, soit un octet par normale. Le ''pixel shader'' se débrouille pour décompresser un bloc. Il effectue notamment le calcul du dessus, pour retrouver la coordonnée x. Les calculs étant particulièrement simples, le cout en performance est très faible. Et ce d'autant plus qu'ils demandent juste de faire des additions et des multiplications entières, que l'unité scalaire peut faire en parallèle d'autres opérations plus gourmandes. Et le cout en calculs est de toute façon compensé par l'économie de mémoire et de bande passante lié à la compression : diviser la taille des données par trois, ca a un sacré impact ! ==Annexe : les ''shadowmap'' hardware== Les anciens GPU, notamment la Geforce FX, avaient des fonctionnalités spécifiques pour le calcul des ombres. Dans la plupart des jeux vidéos de l'époque, et même de maintenant, les ombres sont calculées avec la technique des ''shadowmap''. L'idée est assez simple sur le principe : un pixel est dans l'ombre quand il est invisible depuis une source de lumière. L'idée est que le rendu est réalisé en plusieurs passes, avec une passe par source de lumière et une passe finale pour calculer l'image finale. Nous allons expliquer la technique avec une seule source de lumière, et allons utiliser l'exemple de la scène ci-dessous. [[File:7fin.png|centre|vignette|Scène 3D d'exemple.]] ===La technique du ''shadowmapping''=== [[File:2shadowmap.png|vignette|Résultat de la première passe : ''shadowmap''..]] La première passe rend l'image depuis le point de vue de la source de lumière. Cette première passe ne rend pas les couleurs de la scène, elle ne s'intéresse qu'à la profondeur des pixels. Le résultat est que l'image ne rend que le tampon de profondeur. Celui-ci est ensuite réutilisé comme texture pour la passe suivante. La texture en question est appelée la '''''shadownmap'''''. La perspective utilisée, ainsi que le ''view frustrum'', dépend de la source de lumière. Pour une source de lumière qui émet un cône de lumière, le ''view frustrum'' de l'image rendue doit contenir tout le cone de lumière, et doit coller le plus possible à celui-ci. Pour une source directionnelle, comme le soleil, une perspective orthographique est utilisée. La seconde passe rend l'image du point de vue de la caméra, pour rendre l'image finale. Elle rend l'image finale, qui est composée de pixels, chacun ayant une position à l'écran x,y, et une profondeur z. Les coordonnées sont transformées pour obtenir la position de ce pixel depuis le point de vue de la caméra. Une simple multiplication de matrice suffit, rien de bien compliqué, un shader peut le faire. [[File:5failed.png|vignette|Résultat du test des comparaisons.]] Après cette étape, on a alors les coordonnées x,y,z de ce pixel du point de vue de la caméra, et la ''shadowmap''. Il est alors possible d'accéder à la ''shadowmap'' au même endroit, à la même place que le pixel testé, aux mêmes coordonnées x,y. Si la profondeur du pixel est supérieure à celle de la shadowmap au même endroit, alors le pixel est situé derrière la surface visible, donc est dans l'ombre. Sinon, il n'est pas dans l'ombre. Le même procédé est répété sur chaque pixel de l'écran. ===Les optimisations hardware du ''shadowmapping''=== La technique des ''shadowmap'' demande donc de calculer une texture ''shadowmap'', puis de lire celle-ci et de faire des comparaisons de profondeur. Les GPU comme la Geforce FX intégraient du matériel dans les unités de texture pour faciliter ce travail. Les unités de texture pouvaient lire les ''shadowmap'', et faire la comparaison de profondeur toutes seules, elles avaient des circuits pour. Il suffisait de leur fournir le pixel à tester, ses coordonnées x,y,z, et l'adresse de la ''shadowmap''. Les unités de texture renvoyaient alors un résultat valant 0 ou 1 : 1 si le pixel est dans l'ombre, 0 sinon. Elles pouvaient même effectuer du filtrage de texture sur les ''shadowmap''. Mais le filtrage était différent de celui utilisé sur les autres textures : moyenner des valeurs de profondeur ne marche pas bien. Elles utilisaient des techniques de filtrage différentes : elles faisaient les tests de comparaison, puis faisaient la moyenne des résultats. Ainsi, pour du filtrage bilinéaire, elles lisaient 4 texels dans la ''shadowmap'', puis faisaient 4 tests de comparaison, et moyennaient les 4 résultats. ==Annexe : le cube-mapping== [[File:Cube mapped reflection example 2.JPG|vignette|Exemple de reflets environnementaux.]] L''''environnement-mapping''' simule les réflexions et autres effets graphiques sur une surface ou un objet 3D. L'idée est de plaquer une texture pré-calculée pour simuler l'effet de l'environnement sur un modèle 3D. Il en existe plusieurs versions différentes, mais la seule utilisée de nos jours est le ''cube-mapping'', où la texture de l'environnement est plaquée sur un cube, d'où son nom. Le cube en question est utilisé différemment suivant ce que l'on cherche à faire avec le ''cube-mapping''. ===Les utilisations du ''cubemapping''=== Les deux utilisations principales sont le rendu du ciel et des décors, et les réflexions sur la surface des objets. Dans les deux cas, l'idée est de précalculer ce que l'on voit du point de vue de la caméra. On place la caméra dans la scène 3D, on place un cube centré sur la caméra, le cube est texturé avec ce que l'on voit de l'environnement depuis la caméra/l'objet de son point de vue. [[File:Panorama cube map.png|centre|vignette|upright=2|L'illustration montre en premier lieu une ''cubemap'' avec les six faces mises en évidence, puis quel environnement 3D elle permet de simuler, le troisième illustration montrant comment la ''cubemap'' est utilisée pour simuler l'environnement.]] Le rendu du ciel et des décors lointains dans les jeux vidéo se base sur des '''''skybox''''', à savoir un cube centré sur la caméra, sur lequel on ajoute des textures de ciel ou de décors lointains. Le cube est recouvert par une texture, qui correspond à ce que l'on voit quand on dirige le regard de la caméra vers cette face. Contrairement à ce qu'on pourrait croire, la skybox n'est pas les limites de la scène 3D, les limites du niveau d'un jeu vidéo ou quoique ce soit d'autre de lié à la physique de la scène 3D. La skybox est centrée sur la caméra, elle suit la caméra dans son mouvement. Centrer la skybox sur la caméra permet de simuler des décors très lointains, suffisamment lointain pour qu'on n'ait pas l'illusion de s'en rapprocher en se déplaçant dans la map. De plus, cela évite d'avoir à faire trop de calculs à chaque fois que l'on bouge la caméra. La texture plaquée sur le cube est une texture unique, elle-même découpée en six sous-textures, une par face du cube. [[File:Skybox example.png|centre|vignette|upright=2|Exemple de Skybox.]] [[File:Cube mapped reflection example.jpg|vignette|Réflexions calculées par une ''cubemap''.]] Le ''cube-mapping'' est aussi utilisé pour des reflets. L'idée est de simuler les reflets en plaquant une texture pré-calculée sur l'objet réflecteur. La texture pré-calculée est un dessin de l'environnement qui se reflète sur l'objet, un dessin du reflet à afficher. En la plaquant la texture sur l'objet, on simule ainsi des reflets de l'environnement, mais on ne peut pas calculer d'autres reflets comme les reflets objets mobiles comme les personnages. Et il se trouve que la texture pré-calculée est une ''cubemap''. Pour les environnements ouverts, c'est la ''skybox'' qui est utilisée, ce qui permet de simuler les reflets dans les flaques d'eau ou dans des lacs/océans/autres. Pour les environnements intérieurs, c'est une cubemap spécifique qui utilisée. Par exemple, pour l'intérieur d'une maison, on a une ''cubemap'' par pièce de la maison. Les reflets se calculent en précisant quelle ''cubemap'' appliquer sur l'objet en fonction de la direction du regard. [[File:Cube map level.png|centre|vignette|Cube map de l'intérieur d'une pièce d'un niveau de jeux vidéo.]] ===L'implémentation matérielle du ''cubemapping''=== Toujours est-il que les textures utilisées pour le ''cubemapping'', appelées des ''cubemaps'', sont en réalité la concaténation de six textures différentes. En mémoire vidéo, la ''cubemap'' est stockée comme six textures les unes à la suite des autres. Lors du rendu, on doit préciser quelle face du cube utiliser, ce qui fait 6 possibilités. On a le même problème qu'avec les niveaux de détail, sauf que ce sont les faces d'une ''cubemap'' qui remplacent les textures de niveaux de détails. L'accès en mémoire doit donc préciser quelle portion de la ''cubemap'' il faut accéder. Et l'accès mémoire se complexifie donc. Surtout que l'accès en question varie beaucoup suivant l'API graphique utilisée, et donc suivant la carte graphique. Le support des ''cubemaps'' dépend de l'API 3D, et surtout de si elle date ou si elle est récente : * Les API 3D très anciennes ne gérent pas nativement les ''cubemaps'', qui doivent être émulées en logiciel en utilisant six textures différentes. Le pixel shader décide donc quelle ''cubemap'' utiliser, avec quelques calculs sur la direction du regard. * Les API 3D récentes gèrent nativement les ''cubemaps''. Pour les versions les plus vielles de ces API, les six faces sont numérotées et l'accès à une ''cubemap'' précise quel face utiliser en donnant son numéro. La carte graphique choisit alors automatiquement la bonne texture. Mais cela demande de laisser le calcul de la bonne face au pixel shader. * Dans les API 3D modenres, les ''cubemap'' sont gérées comme des textures en trois dimensions, adressées avec trois coordonnées u,v,w. La carte graphique utilise ces trois coordonnées de manière à en déduire quelle est la face pertinente, mais aussi les coordonnées u,v dans la texture de la face. ==Annexe : les textures virtuelles== Les '''textures virtuelles''' sont une optimisation des textures normales, qui visent à accélérer le rendu de terrains de grande taille. Imaginez par exemple un monde assez ouvert, comme un environnement en forêt ou en montagne, avec une grande distance de visibilité. Avec de tels terrains, le "sol" est recouvert par une texture de sol unique qui recouvre tout le terrain. Elle ne se répète pas, est de très grande taille, et peut parfois recouvrir toute la map ! Mais il n'y a pas assez de mémoire vidéo pour mémoriser la texture toute entière. La seule solution est la suivante : une partie de la texture est placée en mémoire vidéo, le reste est soit placé en mémoire RAM ou sur le disque dur. Pour cela, le moteur de jeu utilise une optimisation ingénieuse, basée sur une observation assez basique : une bonne partie de la texture est visible, mais le reste est caché par des arbres, des habitations ou d'autres obstacles. Une optimisation possible de ne garder en mémoire vidéo que les portions visibles de la texture, pas les portions cachées. Une autre optimisation mélange textures virtuelles et ''mip-mapping''. L'idée est que pour les portions lointaines d'une texture, la texture utilisée est une ''mip-map'' de basse résolution. L'idée est alors de ne charger que la ''mip-map'' adéquate, pas les autres niveaux de détail. En clair, la texture de base n'est pas chargée en mémoire vidéo, mais la ''mip-map'' basse résolution l'est. ===Une texture à deux niveaux=== L'implémentation des textures virtuelles découpe les méga-textures en ''tiles'', en morceaux rectangulaires de taille modeste. En clair, le terrain est découpé en morceau rectangulaires/carrés. Seules les tiles nécessaires sont chargées en mémoire vidéo, pas les autres. Par exemple, les ''tiles'' non-visibles ne sont pas placées en mémoire vidéo, seules les ''tiles'' visibles le sont. De même, il y a une ''tile'' par niveau de mip-map : seul la tile correspondant le niveau adéquat est en mémoire vidéo, les autres niveaux de détail ne sont pas chargés. On peut faire une analogie avec la mémoire virtuelle, où les données sont découpées en pages, qui sont chargées en mémoire RAM à la demande, suivant les besoins, les données pouvant être swappées sur le disque dur si elles sont peu utilisées. Sauf qu'ici, il s'agit de textures qui sont découpées en pages chargées à la demande en mémoire vidéo, depuis la RAM système. Une texture virtuelle est en réalité un système à deux niveaux : une liste de ''tiles'' et les ''tiles'' elles-mêmes. La liste de ''tiles'' est appelée un '''atlas de texture''', c'est un peu l'équivalent de la ''tilemap'' pour le rendu 2D. Rendre une texture demande de calculer quelle ''tile'' contient le texel à afficher, consulter la ''tile'' en question, puis récupérer le texel adéquat dans cette ''tile''. La ''tile'' est donc une texture, mais la texture à charger est choisie parmi un ensemble, qui est ici l'atlas de texture. ===L'implémentation : logicielle versus matérielle=== Les textures virtuelles ont été utilisées pour la première fois par les jeux Rage 1 et 2 d'IdSoftware, et quelques jeux ultérieurs comme DOOM 2016. IdSoftware les appelait des '''''mega-textures'''''. L'optimisation permettait des gains en performance assez impressionnants. Le jeu Rage 1 utilisait une texture carrée unique de 128k pixels de côté pour rendre le terrain. En théorie, une telle texture devrait prendre 64 giga-octets, mais le jeu tournait correctement avec 512 méga-octets de RAM, poussivement avec seulement 256 méga-octets de RAM. De nos jours, les textures virtuelles sont supportées par beaucoup de jeux vidéos, les moteurs les plus courants gèrent de telles textures de manière logicielles. Mais quelques GPU récents supportent les textures virtuelles. Sur les GPU récents, l'atlas de texture est géré nativement par le matériel. Le GPU choisit quelle ''tile'', quelle texture choisir pour rendre le texel adéquat. Pour cela, le GPU calcule quelle ''tile'' charger, consulte l'atlas de texture, et lit la texture de ''tile'' adéquate. Mais l'implémentation sur les GPU récents a de nombreuses limitations. La limitation la plus importante est que la taille des textures virtuelles ne peut pas dépasser la taille d'une texture normale, soit 32768 pixels de côté pour une texture carrée environ sur les GPU de 2020. De plus, le chargement d'une ''tile'' est très lent. En clair, dès qu'on veut changer de niveau de mip-map pour une tile, ou dès qu'une tile devient visible, le chargement de la tile peut facilement prendre plusieurs centaines de millisecondes. Le filtrage de texture est très complexe avec des textures virtuelles, ce qui fait que le filtrage de texture virtuelle est souvent soumis à des limitations que les textures normales n'ont pas, notamment pour le filtrage anisotropique. {{NavChapitre | book=Les cartes graphiques | prev=Le rasterizeur | prevText=Le rasterizeur | next=Les Render Output Target | nextText=Les Render Output Target }}{{autocat}} 11qnet14dh90yccwcihttoxvvaeeyat Pour lire Platon/Sommaire 0 75119 763985 642557 2026-04-19T06:44:08Z PandaMystique 119061 763985 wikitext text/x-wiki * [[Pour lire Platon/Premiers pas|Premiers pas]] * [[Pour lire Platon/Conseils pour la lecture|Conseils pour la lecture]] * [[Pour lire Platon/Introduction par les mythes|Introduction par les mythes]] * [[Pour lire Platon/Faut-il contrôler l'art ?|Faut-il contrôler l'art ?]] * [[Pour lire Platon/Vocabulaire|Vocabulaire]] ** [[Pour lire Platon/Vocabulaire/Beau|Beau]] * [[Pour lire Platon/Études de quelques passages des dialogues|Études de quelques passages des dialogues]] * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] ** [[Pour lire Platon/Guide des dialogues/Introduction|Introduction]] ** [[Pour lire Platon/Guide des dialogues/Charmide|Charmide]] ** [[Pour lire Platon/Guide des dialogues/Ion|Ion]] ** [[Pour lire Platon/Guide des dialogues/Ménon|Ménon]] ** [[Pour lire Platon/Guide des dialogues/Parménide|Parménide]] * [[Pour lire Platon/Bibliographie|Bibliographie]] {{AutoCat}} 95zwfg3xisfv2go66u4rdzpkndfyq8d 763986 763985 2026-04-19T06:45:18Z PandaMystique 119061 763986 wikitext text/x-wiki * [[Pour lire Platon/Premiers pas|Premiers pas]] * [[Pour lire Platon/Conseils pour la lecture|Conseils pour la lecture]] * [[Pour lire Platon/Introduction par les dialogues|Introduction par les dialogues]] * [[Pour lire Platon/Introduction par les mythes|Introduction par les mythes]] * [[Pour lire Platon/Guide des dialogues|Guide des dialogues]] * [[Pour lire Platon/Vocabulaire|Vocabulaire]] * [[Pour lire Platon/Bibliographie|Bibliographie]] {{AutoCat}} 7c5ia2yg1s36c8fdbbhf45ft3htl399 Les cartes graphiques/Le rendu d'une scène 3D : concepts de base 0 79234 763952 763840 2026-04-18T14:05:47Z Mewtow 31375 /* Le mélange alpha */ 763952 wikitext text/x-wiki Le premier jeu à utiliser de la "vraie 3D" texturée fut le jeu Quake, premier du nom. Et depuis sa sortie, la grande majorité des jeux vidéo utilisent de la 3D, même s'il existe encore quelques jeux en 2D. Face à la prolifération des jeux vidéo en 3D, les fabricants de cartes graphiques ont inventé les cartes accélératrices 3D, des cartes vidéo capables d'accélérer le rendu en 3D. Dans ce chapitre, nous allons voir comment elles fonctionnent et comment elles ont évolué dans le temps. Pour comprendre comment celles-ci fonctionnent, il faut faire quelques rapides rappels sur les bases du rendu 3D. ==Les bases du rendu 3D== Une '''scène 3D''' est composée d'un espace en trois dimensions, dans laquelle le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple parallélogramme. Un des coins de ce parallélogramme sert d’origine à un système de coordonnées : il est à la position (0, 0, 0), et les axes partent de ce point en suivant les arêtes. Les objets seront placés à des coordonnées bien précises dans ce parallélogramme. ===Les objets 3D et leur géométrie=== [[File:Dolphin triangle mesh.png|vignette|Illustration d'un dauphin, représenté avec des triangles.]] Dans la quasi-totalité des jeux vidéo actuels, les objets et la scène 3D sont modélisés par un assemblage de triangles collés les uns aux autres, ce qui porte le nom de '''maillage''', (''mesh'' en anglais). Il a été tenté dans le passé d'utiliser des quadrilatères (rendu dit en ''quad'') ou d'autres polygones, mais les contraintes techniques ont fait que ces solutions n'ont pas été retenues. [[File:CG WIKI.jpg|centre|vignette|upright=2|Exemple de modèle 3D.]] Les modèles 3D sont définis par leurs sommets, aussi appelés '''vertices''' dans le domaine du rendu 3D. Chaque sommet possède trois coordonnées, qui indiquent sa position dans la scène 3D : abscisse, ordonnée, profondeur. Les sommets sont regroupés en triangles, qui sont formés en combinant trois sommets entre eux. Les anciennes cartes graphiques géraient aussi d'autres formes géométriques, comme des points, des lignes, ou des quadrilatères. Les quadrilatères étaient appelés des ''quads'', et ce terme reviendra occasionnellement dans ce cours. De telles formes basiques, gérées nativement, sont appelées des '''primitives'''. La représentation exacte d'un objet est donc une liste plus ou moins structurée de sommets. La liste doit préciser les coordonnées de chaque sommet, ainsi que comment les relier pour former des triangles. Pour cela, l'objet est représenté par une structure qui contient la liste des sommets, mais aussi de quoi savoir quels sont les sommets reliés entre eux par un segment. Nous en dirons plus dans le chapitre sur le rendu de la géométrie. ===La caméra : le point de vue depuis l'écran=== Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par : * une position ; * par la direction du regard (un vecteur). A la caméra, il faut ajouter tout ce qui permet de déterminer le '''champ de vision'''. Le champ de vision contient tout ce qui est visible à l'écran. Et sa forme dépend de la perspective utilisée. Dans le cas le plus courant dans les jeux vidéos en 3D, il correspond à une '''pyramide de vision''' dont la pointe est la caméra, et dont les faces sont délimitées par les bords de l'écran. A l'intérieur de la pyramide, il y a un rectangle qui représente l'écran du joueur, appelé le '''''viewport'''''. [[File:ViewFrustum.jpg|centre|vignette|upright=2|Caméra.]] [[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]] La majorité des jeux vidéos ajoutent deux plans : * un ''near plane'' en-deça duquel les objets ne sont pas affichés. Il élimine du champ de vision les objets trop proches. * Un ''far plane'', un '''plan limite''' au-delà duquel on ne voit plus les objets. Il élimine les objets trop lointains. Avec ces deux plans, le champ de vision de la caméra est donc un volume en forme de pyramide tronquée, appelé le '''''view frustum'''''. Le tout est parfois appelée, bien que par abus de langage, la pyramide de vision. Avec d'autres perspectives moins utilisées, le ''view frustum'' est un pavé, mais nous n'en parlerons pas plus dans le cadre de ce cours car elles ne sont presque pas utilisés dans les jeux vidéos actuels. ===Les textures=== Tout objet à rendre en 3D est donc composé d'un assemblage de triangles, et ceux-ci sont éclairés et coloriés par divers algorithmes. Pour rajouter de la couleur, les objets sont recouverts par des '''textures''', des images qui servent de papier peint à un objet. Un objet géométrique est donc recouvert par une ou plusieurs textures qui permettent de le colorier ou de lui appliquer du relief. [[File:Texture+Mapping.jpg|centre|vignette|upright=2|Texture Mapping]] Notons que les textures sont des images comme les autres, codées pixel par pixel. Pour faire la différence entre les pixels de l'écran et les pixels d'une texture, on appelle ces derniers des '''texels'''. Ce terme est assez important, aussi profitez-en pour le mémoriser, nous le réutiliserons dans quelques chapitres. Un autre point lié au fait que les textures sont des images est leur compression, leur format. N'allez pas croire que les textures sont stockées dans un fichier .jpg, .png ou tout autre format de ce genre. Les textures utilisent des formats spécialisés, comme le DXTC1, le S3TC ou d'autres, plus adaptés à leur rôle de texture. Mais qu'il s'agisse d'images normales (.jpg, .png ou autres) ou de textures, toutes sont compressées. Les textures sont compressées pour prendre moins de mémoire. Songez que la compression de texture est terriblement efficace, souvent capable de diviser par 6 la mémoire occupée par une texture. S'en est au point où les textures restent compressées sur le disque dur, mais aussi dans la mémoire vidéo ! Nous en reparlerons dans le chapitre sur la mémoire d'une carte graphique. Plaquer une texture sur un objet peut se faire de deux manières, qui portent les noms de placage de texture inverse et direct. Le placage de texture direct a été utilisé au tout début de la 3D, sur des bornes d'arcade et les consoles de jeu 3DO, PS1, Sega Saturn. De nos jours, on utilise uniquement la technique de placage de texture inverse. Les deux seront décrites dans le détail plus bas. ===La différence entre rastérisation et lancer de rayons=== [[File:Render Types.png|vignette|Même géométrie, plusieurs rendus différents.]] Les techniques de rendu 3D sont nombreuses, mais on peut les classer en deux grands types : le ''lancer de rayons'' et la ''rasterization''. Sans décrire les deux techniques, sachez cependant que le lancer de rayon n'est pas beaucoup utilisé pour les jeux vidéo. Il est surtout utilisé dans la production de films d'animation, d'effets spéciaux, ou d'autres rendu spéciaux. Dans les jeux vidéos, il est surtout utilisé pour quelques effets graphiques, la rasterization restant le mode de rendu principal. La raison principale est que le lancer de rayons demande beaucoup de puissance de calcul. Une autre raison est que créer des cartes accélératrices pour le lancer de rayons n'est pas simple. Il a existé des cartes accélératrices permettant d'accélérer le rendu en lancer de rayons, mais elles sont restées confidentielles. Les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, mais ils restent d'un usage marginal et servent de compléments au rendu par rastérization. Un chapitre entier sera dédié aux cartes accélératrices de lancer de rayons et nous verrons pourquoi le lancer de rayons est difficile à implémenter avec des performances convenables, ce qui explique que les jeux vidéo utilisent la ''rasterization''. La rastérisation est structurée autour de trois étapes principales : * Une étape purement logicielle, effectuée par le processeur, où le moteur physique calcule la géométrie de la scène 3D. * Une étape de '''traitement de la géométrie''', qui gère tout ce qui a trait aux sommets et triangles. * Une étape de '''rastérisation''' qui détermine sur quels pixels de l'écran est affiché le triangle. * Une étape de '''traitement des pixels''', qui colorie les pixels et gère les textures. [[File:Graphics pipeline 2 en.svg|centre|vignette|upright=2.5|Pipeline graphique basique.]] Il existe plusieurs rendus différents et la rastérisation ne se fait pas de la même manière selon le rendu utilisé. Il existe des rendus sans textures, d'autres avec, d'autres avec éclairage, d'autres sans, etc. Par contre, l'étape de calcul de la géométrie est la même quel que soit le rendu ! Mieux : le calcul de la géométrie se fait de la même manière entre rastérisation et lancer de rayons, il est le même quelle que soit la technique de rendu 3D utilisée. Les trois étapes précédentes sont réalisées dans des circuits ou processeurs séparés, comme on le verra plus tard. Et cela permet d'utiliser la technique dite du '''pipeline'''. Concrètement, supposons que la carte graphique traite les données par paquets de triangles (en réalité, c'est des paquets de sommets, mais passons). L'étape de traitement de la géométrie peut travailler sur un paquet de triangle, pendant que le paquet précédent est dans l'étape de rastérisation, et que le paquet encore précédent est en train de traiter ses pixels. Cela permet de traiter trois paquets de triangles en même temps, mais à des états d'avancements différents. Mieux que cela : le traitement de la géométrie est lui-même composé d'une succession de sous-étapes, la rasterisation est elle-même découpée en plusieurs sous-étapes, et ainsi de suite. Le nombre d'étapes pour une carte graphique moderne dépasse la dizaine. La rastérisation calcule un rendu 3D avec une suite d'étapes consécutives qui doivent s'enchainer dans un ordre bien précis. L'ensemble de ces étapes est appelé le '''pipeline graphique''', qui sera détaillé dans ce qui suit. ==Le calcul de la géométrie== Le calcul de la géométrie regroupe plusieurs manipulations différentes. La principale demande juste de placer les modèles 3D dans la scène, de placer les objets dans le monde. Puis, il faut centrer la scène 3D sur la caméra. Les deux changements ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène du point de vue de la caméra, et un autre qui corrige la perspective. ===Les trois étapes de transformation=== La première étape place les objets 3D dans la scène 3D. Un modèle 3D est représentée par un ensemble de sommets, qui sont reliés pour former sa surface. Les données du modèle 3D indiquent, pour chaque sommet, sa position par rapport au centre de l'objet qui a les coordonnées (0, 0, 0). La première étape place l'objet 3D à une position dans la scène 3D, déterminée par le moteur physique, qui a des coordonnées (X, Y, Z). Une fois placé dans la scène 3D, le centre de l'objet passe donc des coordonnées (0, 0, 0) aux coordonnées (X, Y, Z) et tous les sommets de l'objet doivent être mis à jour. De plus, l'objet a une certaine orientation : il faut aussi le faire tourner. Enfin, l'objet peut aussi subir une mise à l'échelle : on peut le gonfler ou le faire rapetisser, du moment que cela ne modifie pas sa forme, mais simplement sa taille. En clair, le modèle 3D subit une translation, une rotation et une mise à l'échelle, les trois impliquant une modification des coordonnées des sommets.. [[File:Similarity and congruence transformations.svg|centre|vignette|upright=1.5|Transformations géométriques possibles pour chaque triangle.]] Une fois le placement des différents objets effectué, la carte graphique effectue un changement de coordonnées pour centrer le monde sur la caméra. Au lieu de considérer un des bords de la scène 3D comme étant le point de coordonnées (0, 0, 0), il va passer dans le référentiel de la caméra. Après cette transformation, le point de coordonnées (0, 0, 0) sera la caméra. La direction de la vue du joueur sera alignée avec l'axe de la profondeur (l'axe Z). [[File:View transform.svg|centre|vignette|upright=2|Étape de transformation dans un environnement en deux dimensions : avant et après. On voit que l'on centre le monde sur la position de la caméra et dans sa direction.]] Enfin, il faut aussi corriger la perspective, ce qui est le fait de l'étape de projection, qui modifie la forme du ''view frustum'' sans en modifier le contenu. Différents types de perspective existent et celles-ci ont un impact différent les unes des autres sur le ''view frustum''. Dans le cas qui nous intéresse, le ''view frustum'' passe d’une forme de trapèze tridimensionnel à une forme de pavé dont l'écran est une des faces. ===Les changements de coordonnées se font via des multiplications de matrices=== Les trois étapes précédentes demande de faire des changements de coordonnées, chaque sommet voyant ses coordonnées remplacées par de nouvelles. Or, un changement de coordonnée s'effectue assez simplement, avec des matrices, à savoir des tableaux organisés en lignes et en colonnes avec un nombre dans chaque case. Un changement de coordonnées se fait simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc. Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur-coordonnées (X, Y, Z) d'un sommet par une matrice adéquate. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes. Or, la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions ce détail. Il se trouve que multiplier des matrices amène certaines simplifications. Au lieu de faire plusieurs multiplications de matrices, il est possible de fusionner les matrices en une seule, ce qui permet de simplifier les calculs. Ce qui fait que le placement des objets, changement de repère pour centrer la caméra, et d'autres traitements forts différents sont regroupés ensemble. Le traitement de la géométrie implique, sans surprise, des calculs de géométrie dans l'espace. Et cela implique des opérations mathématiques aux noms barbares : produits scalaires, produits vectoriels, et autres calculs impliquant des vecteurs et/ou des matrices. Et les calculs vectoriels/matriciels impliquent beaucoup d'additions, de soustractions, de multiplications, de division, mais aussi des opérations plus complexes : calculs trigonométriques, racines carrées, inverse d'une racine carrée, etc. Au final, un simple processeur peut faire ce genre de calculs, si on lui fournit le programme adéquat, l'implémentation est assez aisée. Mais on peut aussi implémenter le tout avec un circuit spécialisé, non-programmable. Les deux solutions sont possibles, tant que le circuit dispose d'assez de puissance de calcul. Les cartes graphiques anciennes contenaient un ou plusieurs circuits de multiplication de matrices spécialisés dans l'étape de transformation. Chacun de ces circuits prend un sommet et renvoie le sommet transformé. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Pour plus d'efficacité, les cartes graphiques comportent plusieurs de ces circuits, afin de pouvoir traiter plusieurs sommets en même temps. ==L'élimination des surfaces cachées== Un point important du rendu 3D est que ce que certaines portions de la scène 3D ne sont pas visibles depuis la caméra. Et idéalement, les portions de la scène 3D qui ne sont pas visibles à l'écran ne doivent pas être calculées. A quoi bon calculer des choses qui ne seront pas affichées ? Ce serait gâcher de la puissance de calcul. Et pour cela, de nombreuses optimisations visent à éliminer les calculs inutiles. Elles sont regroupées sous les termes de '''''clipping''''' ou de '''''culling'''''. La différence entre ''culling'' et ''clipping'' n'est pas fixée et la terminologie n'est pas claire. Dans ce qui va suivre, nous n'utiliserons que le terme ''culling''. Les cartes graphiques modernes embarquent diverses méthodes de ''culling'' pour abandonner les calculs quand elles s’aperçoivent que ceux-ci portent sur une partie non-affichée de l'image. Cela fait des économies de puissance de calcul assez appréciables et un gain en performance assez important. Précisons que le ''culling'' peut être plus ou moins précoce suivant le type de rendu 3D utilisé, mais nous verrons cela dans la suite du chapitre. ===Les différentes formes de ''culling''/''clipping''=== La première forme de ''culling'' est le '''''view frustum culling''''', dont le nom indique qu'il s'agit de l'élimination de tout ce qui est situé en-dehors du ''view frustum''. Ce qui est en-dehors du champ de vision de la caméra n'est pas affiché à l'écran n'est pas calculé ou rendu, dans une certaine mesure. Le ''view frustum culling'' est assez trivial : il suffit d'éliminer ce qui n'est pas dans le ''view frustum'' avec quelques calculs de coordonnées assez simples. Quelques subtilités surviennent quand un triangle est partiellement dans le ''view frustrum'', ce qui arrive parfois si le triangle est sur un bord de l'écran. Mais rien d'insurmontable. [[File:View frustum culling.svg|centre|vignette|upright=1|''View frustum culling'' : les parties potentiellement visibles sont en vert, celles invisibles en rouge et celles partiellement visibles en bleu.]] Les autres formes de ''culling'' visent à éliminer ce qui est dans le ''view frustum'', mais qui n'est pas visible depuis la caméra. Pensez à des objets cachés par un autre objet plus proche, par exemple. Ou encore, pensez aux faces à l'arrière d'un objet opaque qui sont cachées par l'avant. Ces deux cas correspondent à deux types de ''culling''. L'élimination des objets masqués par d'autres est appelé l'''occlusion culling''. L'élimination des parties arrières d'un objet est appelé le ''back-face culling''. Dans les deux cas, nous parlerons d''''élimination des surfaces cachées'''. [[File:Occlusion culling example PL.svg|centre|vignette|''Occlusion culling'' : les objets en bleu sont visibles, ceux en rouge sont masqués par les objets en bleu.]] Le lancer de rayons n'a pas besoin d'éliminer les surfaces cachées, il ne calcule que les surfaces visibles. Par contre, la rastérisation demande d'éliminer les surfaces cachées. Sans cela, le rendu est incorrect dans le pire des cas, ou alors le rendu calcule des surfaces invisibles pour rien. Il existe de nombreux algorithmes logiciels pour implémenter l'élimination des surfaces cachées, mais la carte graphique peut aussi s'en charger. L'''occlusion culling'' demande de connaitre la distance à la caméra de chaque triangle. La distance à la caméra est appelée la '''profondeur''' du triangle. Elle est déterminée à l'étape de rastérisation et est calculée à chaque sommet. Lors de la rastérisation, chaque sommet se voit attribuer trois coordonnées : deux coordonnées x et y qui indiquent sa position à l'écran, et une coordonnée de profondeur notée z. ===L'algorithme du peintre=== Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les triangles du plus lointain au plus proche. L'idée est que si deux triangles se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Il ne s'agit ni plus ni moins que de l''''algorithme du peintre'''. [[File:Polygons cross.svg|vignette|Polygons cross]] [[File:Painters problem.svg|vignette|Painters problem]] Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal. Un autre problème est que l'algorithme demande de trier les triangles d'une scène 3D selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU. Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une amélioration de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment. ===Le tampon de profondeur=== [[File:Z-buffer no text.jpg|vignette|Z-buffer correspondant à un rendu]] Une autre solution utilise ce qu'on appelle un '''tampon de profondeur''', aussi appelé un ''z-buffer''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel. Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale, celle du ''far plane'' du ''viewfrustum''. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un triangle a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu. Il est éliminé (sauf si transparence il y a) et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, l'objet est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur. [[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]] Il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans la suite de ce cours, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives. Toutes les cartes graphiques modernes utilisent un système de ''z-buffer''. C'est la seule solution pour avoir des performances dignes de ce nom. Il faut cependant noter qu'elles utilisent des tampons de profondeur légèrement modifiés, qui ne mémorisent pas la coordonnée de profondeur, mais une valeur dérivée. Pour simplifier, ils ne mémorisent pas la coordonnée de profondeur z, mais son inverse 1/z. Les raisons à cela ne peuvent pas encore être expliquées à ce moment du cours, aussi nous allons simplement dire que c'est une histoire de correction de perspective. Les coordonnées z et 1/z sont codées sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne : [[File:Z-fighting.png|centre|vignette|Z-fighting]] Un défaut du tampon de profondeur est qu'il ne gère pas correctement les objets transparents. Dès que de la transparence est présente dans une scène 3D, le tampon de profondeur ne peut pas être utilisé. Une solution pour cela est de rendre une scène 3D en deux phases : une pour les objets opaques, une avec les objets transparents. La où on rend les objets opaques utilise le tampon de profondeur, mais il est désactivé lors de la seconde. ==La rastérisation et les textures== Dans cette section, nous allons voir ensemble l'étape de rastérisation et l'étape de traitement des pixels. La rastérisation est difficile à expliquer, surtout que son rôle exact dépend de la technique de rendu utilisée. Pour simplifier, elle projette un rendu en 3D sur un écran en 2D. Une autre explication tout aussi vague est qu'elle s'occupe la traduction des triangles en un affichage pixelisé à l'écran. Elle détermine à quoi ressemble la scène visible sur l'écran. C'est par exemple lors de cette étape que sont appliquées certaines techniques de ''culling'', qui éliminent les portions non-visibles de l'image, ainsi qu'une correction de la perspective et diverses opérations d'interpolation dont nous parlerons dans plusieurs chapitres. La rastérisation et placage de textures sont deux opérations très liées entre elles. Il existe deux manières principales pour lier les textures à la géométrie : la méthode directe et la méthode inverse (''UV Mapping''). Et les deux font que la rastérisation se fait de manière très différente. Précisons cependant que les rendus les plus simples n'utilisent pas de textures du tout. Ils se contentent de colorier les triangles, voire d'un simple rendu en fil de fer basé sur du tracé de lignes. Dans la suite de cette section, nous allons voir les quatre types de rendu principaux : le rendu en fils de fer, le rendu colorié, et deux rendus utilisant des textures. ===Le rendu en fil de fer=== [[File:Obj lineremoval.png|vignette|Rendu en fil de fer d'un objet 3D.]] Le '''rendu 3D en fils de fer''' est illustré ci-contre. Il s'agit d'un rendu assez ancien, utilisé au tout début de la 3D, sur des machines qu'on aurait du mal à appeler ordinateurs. Il se contente de tracer des lignes à l'écran, lignes qui connectent deux sommets, qui ne sont autres que les arêtes de la géométrie de la scène rendue. Le tout était suffisant pour réaliser quelques jeux vidéos rudimentaires. Les tout premiers jeux vidéos utilisaient ce rendu, l'un d'entre eux étant Maze War, le tout premier FPS. {| |[[File:Maze war.jpg|vignette|Maze war]] |[[File:Maze representation using wireframes 2022-01-10.gif|centre|vignette|Maze representation using wireframes 2022-01-10]] |} Le monde est calculé en 3D, il y a toujours un calcul de la géométrie, la scène est rastérisée normalement, les portions invisbles de l'image sont retirées, mais il n'y a pas d'application de textures après rastérisation. A la place, un algorithme de tracé de ligne trace les lignes à l'écran. Quand un triangle passe l'étape de rastérisation, l'étape de rastérisation fournit la position des trois sommets sur l'écran. En clair, elle fournit les coordonnées de trois pixels, un par sommet. A la suite, un algorithme de tracé de ligne trace trois lignes, une par paire de sommet. L'implémentation demande juste d'avoir une unité de calcul géométrique, une unité de rastérisation, et un VDC qui supporte le tracé de lignes. Elle est donc assez simple et ne demande pas de circuits de gestion des textures ni de ROP. Le VDC écrit directement dans le ''framebuffer'' les lignes à tracer. Il a existé des proto-cartes graphiques spécialisées dans ce genre de rendu, comme le '''''Line Drawing System-1''''' de l'entreprise Eans & Sutherland. Nous détaillerons son fonctionnement dans quelques chapitres. ===Le rendu à primitives colorées=== [[File:MiniFighter.png|vignette|upright=1|Exemple de rendu pouvant être obtenu avec des sommets colorés.]] Une amélioration du rendu précédent utilise des triangles/''quads'' coloriés. Chaque triangle ou ''quad'' est associé à une couleur, et cette couleur est dessinée sur le triangle/''quad''après la rastérisation. Le rendu est une amélioration du rendu en fils de fer. L'idée est que chaque triangle/''quad'' est associé à une couleur, qui est dessinée sur le triangle/''quad'' après la rastérisation. La technique est nommée ''colored vertices'' en anglais, nous parlerons de '''rendu à maillage coloré'''. [[File:Malla irregular de triángulos modelizando una superficie convexa.png|centre|vignette|upright=2|Maillage coloré.]] La couleur est propagée lors des calculs géométriques et de la rastérisation, sans subir de modifications. Une fois un rendu en fils de fer effectué, la couleur du triangle est récupérée. Le triangle/''quad'' rendu correspond à un triangle/''quad'' à l'écran. Et l'intérieur de ce triangle/''quad'' est colorié avec la couleur transmise. Pour cela, on utilise encore une fois une fonction du VDC : celle du remplissage de figure géométrique. Nous l’avions vu en parlant des VDC à accélération 2D, mais elle est souvent prise en charge par les ''blitters''. Ils peuvent remplir une figure géométrique avec une couleur unique, on réutilise cette fonction pour colorier le triangle/''quad''. L'étape de rastérisation fournit les coordonnées des sommets de la figure géométrique, le ''blitter'' les utilise pour colorier la figure géométrique. Niveau matériel, quelques bornes d'arcade ont utilisé ce rendu. La toute première borne d'arcade utilisant le rendu à maillage coloré est celle du jeu I Robot, d'Atari, sorti en 1983. Par la suite, dès 1988, les cartes d'arcades Namco System 21 et les bornes d'arcades Sega Model 1 utilisaient ce genre de rendu. On peut s'en rendre compte en regardant les graphismes des jeux tournant sur ces bornes d'arcade. Des jeux comme Virtua Racing, Virtua Fighter ou Virtua Formula sont assez parlants à ce niveau. Leurs graphismes sont assez anguleux et on voit qu'ils sont basés sur des triangles uniformément colorés. Pour ceux qui veulent en savoir plus sur la toute première borne d'arcade en rendu à maillage colorée, la borne ''I Robot'' d'Atari, voici une vidéo youtube à ce sujet : * [https://www.youtube.com/watch?v=6miEkPENsT0 I Robot d'Atari, le pionnier de la 3D Flat.] ===Le placage de textures direct=== Les deux rendus précédents sont très simples, mais n'utilisent pas de textures. Et il est temps de voir les deux rendus qui utilisent des textures. Il y en a deux types, appelés rendu avec placage de texture direct et indirect, nous allons voir le '''rendu par placage de texture direct''' en premier. Et nous l'appellerons ''rendu direct'' dans ce qui suit, pour simplifier les explications. L'idée est assez simple et peut utiliser aussi bien des triangles que des ''quads'', mais nous allons partir du principe qu'elle utilise des '''''quads''''', à savoir que les objets 3D sont composés de quadrilatères. Lorsqu'un ''quad'' est rastérisé, sa forme à l'écran est un rectangle déformé par la perspective. On obtient un rectangle si le ''quad'' est vu de face, un trapèze si on le voit de biais. Et le ''sprite'' doit être déformé de la même manière que le ''quad''. L'idée est que tout quad est associé à une texture, à un sprite. La figure géométrique qui correspond à un ''quad'' à l'écran est remplie non pas par une couleur uniforme, mais par un ''sprite'' rectangulaire. Il suffit techniquement de recopier le ''sprite'' à l'écran, c'est à dire dans la figure géométrique, au bon endroit dans le ''framebuffer''. Le rendu direct est en effet un intermédiaire entre rendu 2D à base de ''sprite'' et rendu 3D moderne. La géométrie est rendue en 3D pour générer des ''quads'', mais ces ''quads'' ne servent à guider la copie des sprites/textures dans le ''framebuffer''. [[File:TextureMapping.png|centre|vignette|upright=2|Exemple caricatural de placage de texture sur un ''quad''.]] La subtilité est que le sprite est déformé de manière à rentrer dans un quadrilatère, qui n'est pas forcément un rectangle à l'écran, mais est déformé par la perspective et son orientation en 3D. Le sprite doit être déformé de deux manières : il doit être agrandi/réduit en fonction de la taille de la figure affichée à l'écran, tourné en fonction de l'orientation du ''quad'', déformé pour gérer la perspective. Pour cela, il faut connaitre les coordonnées de profondeur de chaque bord d'un ''quad'', et de faire quelques calculs. N'importe quel VDC incluant un ''blitter'' avec une gestion du zoom/rotation des sprites peut le faire. : Si on veut avoir de beaux graphismes, il vaut mieux appliquer un filtre pour lisser le sprite envoyé dans le trapèze, filtre qui se résume à une opération d'interpolation et n'est pas très différent du filtrage de texture qui lisse les textures à l'écran. Un autre point est que les ''quads'' doivent être rendus du plus lointain au plus proche. Sans cela, on obtient rapidement des erreurs de rendu. L'idée est que si deux quads se chevauchent, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. L'écriture du sprite du second quad écrasera les données du premier quad, pour les portions recouvertes, lors de l'écriture du sprite dans le ''framebuffer''. Quelque chose qui devrait vous rappeler le rendu 2D, où les sprites sont rendus du plus lointain au plus proche. Le rendu inverse utilise très souvent des triangles pour la géométrie, alors que le rendu direct a tendance à utiliser des ''quads'', mais il ne s'agit pas d'une différence stricte. L'usage de triangles/''quads'' peut se faire aussi bien avec un rendu direct comme avec un rendu inverse. Cependant, le rendu en ''quad'' se marie très bien au rendu direct, alors que le rendu en triangle colle mieux au rendu inverse. L'avantage de cette technique est qu'on parcourt les textures dans un ordre bien précis. Par exemple, on peut parcourir la texture ligne par ligne, l'exploiter par blocs de 4*4 pixels, etc. Et accéder à une texture de manière prédictible se marie bien avec l'usage de mémoires caches, ce qui est un avantage en matière de performances. Mais un même pixel du ''framebuffer'' est écrit plusieurs fois quand plusieurs quads se superposent, alors que le rendu inverse gère la situation avec une seule écriture (sauf si usage de la transparence). De plus, la gestion de la transparence était compliquée et les jeux devaient ruser en utilisation des solutions logicielles assez complexes. Niveau implémentation matérielle, une carte graphique en rendu direct demande juste trois circuits. Le premier est un circuit de calcul géométrique, qui rend la scène 3D. Le tri des quads est souvent réalisé par le processeur principal, et non pas par un circuit séparé. Toutes les étapes au-delà de l'étape de rastérisation étaient prises en charge par un VDC amélioré, qui écrivait des sprites/textures directement dans le ''framebuffer''. {|class="wikitable" |- ! Géométrie | Processeurs dédiés programmé pour émuler le pipeline graphique |- ! Tri des quads du plus lointain au plus proche | Processeur principal (implémentation logicielle) |- ! Application des textures | ''Blitter'' amélioré, capable de faire tourner et de zoomer sur des ''sprites''. |} L'implémentation était très simple et réutilisait des composants déjà existants : des VDC 2D pour l'application des textures, des processeurs dédiés pour la géométrie. Les unités de calcul de la géométrie étaient généralement implémentées avec un ou plusieurs processeurs dédiés. Vu qu'on savait déjà effectuer le rendu géométrique en logiciel, pas besoin de créer un circuit sur mesure. Il suffisait de dédier un processeur spécialisé rien que pour les calculs géométriques et on lui faisait exécuter un code déjà bien connu à la base. En clair, ils utilisaient un code spécifique pour émuler un circuit fixe. C'était clairement la solution la plus adaptée pour l'époque. Les unités géométriques étaient des processeurs RISC, normalement utilisés dans l'embarqué ou sur des serveurs. Elles utilisaient parfois des DSP. Pour rappel, les DSP des processeurs de traitement de signal assez communs, pas spécialement dédiés aux rendu 3D, mais spécialisé dans le traitement de signal audio, vidéo et autre. Ils avaient un jeu d'instruction assez proche de celui des cartes graphiques actuelles, et supportaient de nombreuses instructions utiles pour le rendu 3D. [[File:Sega ST-V Dynamite Deka PCB 20100324.jpg|vignette|Sega ST-V Dynamite Deka PCB 20100324]] Le rendu direct a été utilisé dans la période de transition entre rendu 2D et rendu 3D, car il était très adapté pour faire cette transition. Coupler un VDC à un processeur pour la géométrie était particulièrement simple à l'époque. Le rendu direct a été utilisé sur des bornes d'arcade dès les années 90. Outre les bornes d'arcade, quelques consoles de 5ème génération utilisaient le rendu direct, avec les mêmes solutions matérielles. La géométrie était calculée sur plusieurs processeurs dédiés. Le reste du pipeline était géré par un VDC 2D qui implémentait le placage de textures. Deux consoles étaient dans ce cas : la 3DO, et la Sega Saturn. Le rendu direct est aujourd'hui abandonné. ===Le placage de textures inverse=== Le rendu précédent, le rendu direct, permet d'appliquer des textures directement dans le ''framebuffer''. Mais comme dit plus haut, il existe une seconde technique pour plaquer des textures, appelé le '''placage de texture inverse''', aussi appelé l'''UV Mapping''. Elle associe une texture complète pour un modèle 3D,contrairement au placage de tecture direct qui associe une texture par ''quad''/triangle. L'idée est que l'on attribue un texel à chaque sommet. Plus précisémment, chaque sommet est associé à des '''coordonnées de texture''', qui précisent quelle texture appliquer, mais aussi où se situe le texel à appliquer dans la texture. Par exemple, la coordonnée de texture peut dire : je veux le pixel qui est à ligne 5, colonne 27 dans cette texture. La correspondance entre texture et géométrie est réalisée lorsque les créateurs de jeu vidéo conçoivent le modèle de l'objet. [[File:Texture Mapping example.png|centre|vignette|upright=2|Exemple de placage de texture.]] Dans les faits, on n'utilise pas de coordonnées entières de ce type, mais deux nombres flottants compris entre 0 et 1. La coordonnée 0,0 correspond au texel en bas à gauche, celui de coordonnée 1,1 est tout en haut à droite. L'avantage est que ces coordonnées sont indépendantes de la résolution de la texture, ce qui aura des avantages pour certaines techniques de rendu, comme le ''mip-mapping''. Les deux coordonnées de texture sont notées u,v avec DirectX, ou encore s,t dans le cas général : u est la coordonnée horizontale, v la verticale. [[File:UVMapping.png|centre|vignette|upright=2|UV Mapping]] Avec le placage de texture inverse, la rastérisation se fait grosso-modo en trois étapes : la rastérisation proprement dite, le placage de textures, et les opérations finales qui écrivent un pixel dans le ''framebuffer''. Au niveau du matériel, ainsi que dans la plupart des API 3D, les trois étapes sont réalisées par des circuits séparés. [[File:01 3D-Rasterung-a.svg|vignette|Illustration du principe de la rasterization. La surface correspondant à l'écran est subdivisée en pixels carrés, de coordonnées x et y. La caméra est placée au point e. Pour chaque pixel, on trace une droite qui part de la caméra et qui passe par le pixel considéré. L'intersection entre une surface et cette droite se fait en un point, appartenant à un triangle.]] Lors de la rasterisation, chaque triangle se voit attribuer un ou plusieurs pixels à l'écran. Pour bien comprendre, imaginez une ligne droite qui part de caméra et qui passe par un pixel sur le plan de l'écran. Cette ligne intersecte 0, 1 ou plusieurs objets dans la scène 3D. Les triangles situés ces intersections entre cette ligne et les objets rencontrés seront associés au pixel correspondant. L'étape de rastérisation prend en entrée un triangle et renvoie la coordonnée x,y du pixel associé. Il s'agit là d'une simplification, car un triangle tend à occuper plusieurs pixels sur l'écran. L'étape de rastérisation fournit la liste de tous les pixels occupés par un triangle, et les traite un par un. Quand un triangle est rastérisé, le rasteriseur détermine la coordonnée x,y du premier pixel, applique une texture dessus, puis passe au suivant, et rebelote jusqu'à ce que tous les pixels occupés par le triangles aient été traités. L'implémentation matérielle du placage de texture inverse est beaucoup plus complexe que pour les autres techniques. Pour être franc, nous allons passer le reste du cours à parler de l'implémentation matérielle du placage de texture inverse, ce qui prendra plus d'une dizaine de chapitres. ==La transparence, les fragments et les ROPs== Dans ce qui suit, nous allons parler uniquement de la rastérisation avec placage de textures inverse. Les autres formes de rastérisation ne seront pas abordées. La raison est que tous les GPUs modernes utilisent cette forme de rastérisation, les exceptions étant rares. De même, ils utilisent un tampon de profondeur, pour l'élimination des surfaces cachées. La rastérisation effectue donc des calculs géométriques, suivis d'une étape de rastérisation, puis de placage des textures. Ces trois étapes sont réalisées par une unité géométrique, une unité de rastérisation, et un circuit de placage de textures. Du moins sur le principe, car les cartes graphiques modernes ont fortement optimisé l'implémentation et n'ont pas hésité à fusionner certains circuits. Mais nous verrons cela en temps voulu, nous n'allons pas résumer plusieurs décennies d'innovation technologique en quelques paragraphes. {|class="wikitable" |- ! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders'' |- | Géométrie | Rastérisation | Placage de textures |} Mais où mettre le tampon de profondeur ? Intuitivement, on se dit qu'il vaut mieux faire l'élimination des surfaces cachées le plus tôt possible, dès que la coordonnée de profondeur est connue. Et elle est connu à l'étape de rastérisation, une fois les sommets transformés. {|class="wikitable" |- ! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders'' |- | Géométrie | Rastérisation | Tampon de profondeur | Placage de textures |} En réalité, la profondeur des fragments est gérée par un circuit appelé le '''''Raster Operations Pipeline''''' (ROP), situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo. Il est placé à la fin du pipeline pour gérer correctement la transparence. Et nous allons voir pourquoi la transparence est gérée à la fin du pipeline. {|class="wikitable" |- ! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders'' |- | Géométrie | Rastérisation | Placage de textures | ''Raster Operations Pipeline'' |} ===Le mélange ''alpha''=== La transparence se manifeste quand plusieurs objets sont l'un derrière l'autre. Histoire de simplifier les explications, nous allons d'abord voir le cas où un objet semi-transparent est devant un objet opaque. La couleur perçue est alors un mélange de la couleur de l'objet opaque et celle de l'objet semi-transparent. Le mélange dépend d'à quel point l'objet semi-transparent est transparent. Avec un objet parfaitement transparent, seul l'objet opaque est visible. Avec un objet à moitié transparent, la couleur finale sera pour moitié celle de l'objet opaque, pour moitié celle de l'objet semi-transparent. Et c'est pareil pour les cas intermédiaires entre un objet totalement transparent et un objet totalement opaque. La transparence d'un objet/pixel est définie par un nombre, appelé la '''composante ''alpha'''''. Plus la composante alpha est élevée, plus le pixel est opaque. Elle vaut 0 pour un objet opaque et 1 pour un objet transparent. Elle est ajoutée aux composantes RGB, ce qui fait que tout fragment contient une "couleur de transparence" en plus des couleurs RGB. Elle agit comme un coefficient qui dit comment mélanger la couleur d'un objet transparent et d'un objet opaque. Le calcul de la transparence est une moyenne pondérée par la composante alpha. On parle alors d''''''alpha blending'''''. : <math>\text{Couleur finale} = \alpha \times \text{Couleur de l'objet transparent} + (1 - \alpha) \times \text{Couleur de l'objet opaque}</math> [[File:Texture splatting.png|centre|vignette|upright=2.0|Calcul de transparence. La première ligne montre le produit pour l'objet transparent, la seconde ligne est celle de l'objet opaque. La troisième ligne est celle de l'addition finale.]] Maintenant, qu'en est-il du cas où plusieurs objets sont superposés ? Si vous tracez une demi-droite dont l'origine est la caméra et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points, un point par objet sur la ligne du regarde. Sans transparence, l'objet le plus proche cache tous les autres et c'est donc lui qui décide de la couleur du pixel. Mais avec un objet transparent, la couleur finale est un mélange de la couleur de plusieurs points d'intersection. Il faut donc calculer un pseudo-pixel pour chaque point d'intersection, auquel on donne le nom de '''fragment'''. Un fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont combinés pour obtenir la couleur finale de ce pixel. Il est possible d'utiliser le mélange ''alpha'' pour cela. Il suffit de faire le mélange ''alpha'' entre le fragment qui vient d'être calculé, et le pixel dans le ''framebuffer''. Pour cela, le fragment a une composante ''alpha'', qui est ajouté aux trois couleurs RGB. Le pixel déjà dans le ''framebuffer'' est un résultat temporaire, né du mélange ''alpha'' de tous les fragments précédents. Un défaut de cette méthode est qu'elle fonctionne assez mal avec un tampon de profondeur. Si le tampon de profondeur est activé, le mélange ''alpha'' ne fonctionne que si les objets sont rendus du plus lointain au plus proche. Et procéder dans cet ordre a un défaut : on dessine des objets dans le ''framebuffer'', pour qu'ensuite les objets devant écrasent ce qui a déjà été dessiné. Un même pixel peut donc être dessiné plusieurs fois, dont une seule sera pertinente. Et ces écritures utilisent de la bande passante mémoire, qui est une ressource précieuse sur un GPU moderne. Il s'agit d'un phénomène appelé '''''overdraw''''', ou sur-dessinage en français. Quelques optimisations permettent d'éliminer l'''overdraw'' en rendant les objets du plus proche au plus lointain, d'autres permettent de dessiner des objets dans un ordre arbitraire, mais nous ne pouvons pas en parler ici. Beaucoup de moteurs 3D rendent séparément les objets opaques et transparents. Une première passe rend les objets opaques, puis les objets transparents sont rendus dans une seconde passe. Les objets opaques sont rendus dans le désordre, ce qui fait qu'on n'a pas à les trier, alors que les objets transparents doivent être triés selon leur distance. un autre avantage est que le mélange ''alpha'' est désactivé lors de la première passe, alors que c'est la mise à jour du tampon de profondeur qui est désactivé lors de la seconde passe, ce qui augmente un peu les performances dans les deux cas. ===Le test ''alpha''=== Le test ''alpha'' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous ou au-dessus d'un seuil, le fragment est simplement abandonné. Le seuil en question est configurable, de même que la comparaison utilisée : on peut éliminer le fragment si sa transparence est au-dessus d'un certain seuil, en-dessous, égal, différent, etc. Il s'agit d'une optimisation qui est utile dans certains scénarios spécifiques. Par exemple, si l'objet a une transparence très élevée, du genre 95%, autant le compter comme complétement transparent, afin d'éviter des opérations de mélange ''alpha''. En effet, les opérations de mélange ''alpha'' sont très lentes, car elles demandent de faire des opérations de lecture-écriture en mémoire vidéo : on lit un pixel dans le ''framebuffer'', on applique le mélange ''alpha'' et on écrit le résultat en mémoire vidéo. L'''alpha test'' permet donc de gagner en performance au prix d'une baisse de la qualité d'image. Il y a cependant des cas où l'usage du test ''alpha'' est primordial, au-delà d'une question de performances. Un exemple classique est celui du rendu du feuillage dans un jeu 3D. Un feuillage est composé en assemblant plusieurs images de feuilles. Chaque feuille est un carré sur lequel on place une texture de feuille, qui est opaque pour la partie verte des feuilles, transparente pour le reste. Les carrés ne sont cependant pas superposés, mais s'intersectent fortement, ce qui fait que le mélange ''alpha'' ne donne pas de bons résultats. L'usage du test ''alpha'' permet d'obtenir un rendu correct. Pour d'informations via ce lien : * [https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f Anti-aliased Alpha Test: The Esoteric Alpha To Coverage]. ===Les effets de brouillard=== Les '''effets de brouillard''' sont nécessaires dans certains jeux vidéo pour l'ambiance (pensez à des jeux d'horreur comme Silent Hill), mais ils ont surtout été utilisés pour économiser des calculs. L'idée est de ne pas calculer les graphismes au-delà d'une certaine distance, sans que cela se voie. Le ''view frustum'' utilise alors un plan limite, au-delà duquel on ne voit pas les objets. Mais ce plan limite donne une cassure inesthétique dans le rendu. Pour masquer cette cassure, les programmeurs ajoutaient un effet de brouillard. Les objets au-delà du plan limite étaient totalement dans le brouillard, puis ce brouillard se réduisait progressivement en se rapprochant de la caméra, avant de s'annuler à partir d'une certaine distance. Pour calculer le brouillard, on effectue un mélange ''alpha'' entre la couleur du pixel et une ''couleur de brouillard''. La différence est que l'on n'utilise pas la transparence pour faire le mélange, mais un '''coefficient de brouillard''', noté <math>\text{fog}(z)</math>. : <math>\text{Couleur finale} = \text{fog}(z) \times \text{Couleur de brouillard} + [ 1 - \text{fog}(z) ] \times \text{Couleur du pixel}</math> Le coefficient de brouillard dépend de la coordonnée de profondeur, de la distance du pixel par rapport à la caméra. Le brouillard démarre à une distance <math>z_{fog-start}</math>, et masque totalement les objets à partir d'une distance <math>z_{fog-end}</math>. Entre les deux, le coefficient de brouillard dépend de la distance. OpenGL autorise trois formules de calcul suivantes : : <math>\text{fog}(z) = \frac{z_{fog-end} - z}{z_{fog-end} - z_{fog-start}}</math> : <math>\text{fog}(z) = e^{- k \times z}</math> : <math>\text{fog}(z) = e^{- (k \times z)^2}</math> ==L'éclairage d'une scène 3D== L'éclairage d'une scène 3D calcule les ombres, mais aussi la luminosité de chaque pixel, ainsi que bien d'autres effets graphiques. Les algorithmes d'éclairage ont longtemps été implémentés directement en matériel, les cartes graphiques géraient l'éclairage dans des circuits spécialisés. Aussi, il est important de voir ces algorithmes d'éclairage. Il est possible d'implémenter l'éclairage à deux endroits différents du pipeline : juste avant la rastérisation, et après la rastérisation. ===Les sources de lumière et les couleurs associées=== L'éclairage d'une scène 3D provient de sources de lumières, comme des lampes, des torches, le soleil, etc. Il existe de nombreux types de sources de lumière, et nous n'allons parler que des principales. Elles sont au nombre de quatre et elles sont illustrées ci-dessous. [[File:3udUJ.gif|centre|vignette|upright=2|Types de sources de lumière.]] [[File:Graphics lightmodel directional.png|vignette|upright=1.0|Source de lumière directionnelle.]] Les '''sources directionnelles''' servent à modéliser des sources de lumière très éloignées, comme le soleil ou la lune. Elles sont simplement définies par un vecteur qui indique la direction de la lumière, rien de plus. Les '''sources ponctuelles''' sont des points, qui émettent de la lumière dans toutes les directions. Elles sont définies par une position, et une intensité lumineuse, éventuellement la couleur de la lumière émise. Il existe deyux types de sources de lumière ponctuelles. * Le premières émettent de manière égale dans toutes les directions. Elles sont appelées des ''point light'' dans le schéma du dessus. * Les secondes émettent de la lumière dans une '''direction privilégiée'''. L'exemple le plus parlant est celui d'une lampe-torche : elle émet de la lumière "tout droit", dans la direction où la lampe est orientée. Elles sont appelées des ''sport light'' dans le schéma du dessus. La direction privilégiée est un vecteur, notée v dans le schéma du dessous. [[File:Graphics lightmodel ambient.png|vignette|upright=1.0|Lumière ambiante.]] En théorie, la lumière rebondit sur les surfaces et a tendance à se disperser un peu partout à force de rebondir. C'est ce qui explique qu'on arrive à voir à l'intérieur d'une pièce si une fenêtre est ouverte. Il en résulte un certain '''éclairage ambiant''', qui est assez difficile à représenter dans un moteur de rendu 3D. Auparavant, l'éclairage ambiant était simulé par une lumière égale en tout point de la scène 3D, appelée simplement la '''lumière ambiante'''. Précisément, on suppose que la lumière ambiante en un point vient de toutes les directions et a une intensité constante, identique dans toutes les directions. Le tout est illustré ci-contre. C'est assez irréaliste, mais ça donne une bonne approximation de la lumière ambiante. ===La lumière incidente : le terme géométrique=== Pour simplifier, nous allons supposer que l'éclairage est calculé pour chaque sommet, pas par triangle. C'est de loin le cas le plus courant, aussi ce n'est pas une simplification abusive. La lumière qui arrive sur un sommet est appelée la '''lumière incidente'''. La couleur d'un sommet dépend de deux choses : la lumière incidente directe, comment il réfléchit cette lumière. Mathématiquement, il est possible de résumer cela avec le produit de deux termes : l'intensité de la lumière incidente, une fonction qui indique comment la surface réfléchit la lumière incidente. La fonction en question est appelée la '''réflectivité bidirectionnelle'''. Le terme anglais est ''bidirectional reflectance distribution function'', abrévié en BRDF, et nous utiliserons cette abréviation dans ce qui suit. : <math>\text{Couleur finale} = \text{Lumière incidente} \times BRDF(...)</math> La lumière incidente vient soit directement des sources de lumière, soit de la lumière qui a rebondit sur d'autres objets proches. La première est appelée la lumière directe, celle qui vient des rebonds s'appelle la lumière indirecte. Pour simplifier, la lumière indirecte est gérée par la lumière ambiante, nous passons sous silence les techniques d'illumination globale. En clair : nous allons nous limiter au cas où la lumière incidente vient directement d'une source de lumière, pas d'un rebond. Intuitivement, la lumière incidente est simplement égale à l'intensité de la source de lumière. Sauf que ce n'est qu'une approximation, et une assez mauvaise. En réalité, l'approximation est bonne si la lumière arrive proche de la verticale, mais elle est d'autant plus mauvaise que la lumière arrive penchée, voire rasante. La raison : la lumière incidente sera étalée sur une surface plus grande, si elle arrive penchée. Si vous vous souvenez de vos cours de collège, c'est le même principe qui explique les saisons. La lumière du soleil est proche de la verticale en été, mais est de plus en plus penché quand on s'avance vers l'Hiver. La lumière solaire est donc étalée sur une surface plus grande, ce qui fait qu'un point de la surface recevra moins de lumière, celle-ci étant diluée, étalée. [[File:Radiación solar.png|centre|vignette|upright=2|Exemple avec la lumière solaire.]] [[File:Angle of incidence.svg|vignette|upright=1|Angle d'incidence.]] En clair, tout dépend de l''''angle d'incidence''' de la lumière. Reste à voir comment calculer cet angle. La lumière incidente est définie par un vecteur, qui part de la source de lumière et atterrit sur le sommet considéré. Imaginez simplement que ce vecteur suit un rayon lumineux provenant de la source de lumière. Le vecteur pour la lumière incidente sera noté L. L'angle d'incidence est l'angle que fait ce vecteur avec la verticale de la surface, au niveau du sommet considéré. [[File:Graphics lightmodel ptsource.png|vignette|Normale de la surface.]] Pour cela, les calculs d'éclairage ont besoin de connaitre la verticale d'un sommet. Un sommet est donc associé à un vecteur, appelé la '''normale''', qui indique la verticale en ce point. Deux sommets différents peuvent avoir deux normales différentes, même s'ils sont proches. Elles sont d'autant plus différentes que la surface est rugueuse, non-lisse. La normale est prédéterminée lors de la création du modèle 3D, il n'y a pas besoin de le calculer. Par contre, elle est modifiée lors de l'étape de transformation, quand on place le modèle 3D dans la scène 3D. Les deux autres vecteurs sont à calculer à chaque image, car ils changent quand on bouge le sommet. La lumière qui arrive sur la surface dépend de l'angle entre la normale et le vecteur L. Précisément, elle dépend du cosinus de cet angle. En multipliant ce cosinus avec l'intensité de la lumière, on a la lumière arrivante. La couleur finale d'un pixel est donc : : <math>\text{Couleur finale} = I \times \cos{(N, L)} \times BRDF(...)</math> Le terme <math>I \times \cos{N, L}</math> ne dépend pas de la surface considérée. Juste de la position de la source de lumière, de la position du sommet et de son orientation par rapport à la lumière. Aussi, il est parfois appelé le '''terme géométrique''', en opposition aux propriétés de la surface. Les propriétés de la surface sont définies par un '''''material''''', qui indique comment il réfléchit la lumière, ainsi que sa texture. ===Le produit scalaire de deux vecteurs=== Calculer le terme géométrique demande de calculer le cosinus d'un angle. Et il n'est pas le seul : les autres calculs d'éclairage que nous allons voir demandent de calculer des cosinus. Or, les calculs trigonométriques sont très gourmands pour le GPU. Pour éviter le calcul d'un cosinus, les GPU utilisent une opération mathématique appelée le ''produit scalaire''. Le produit scalaire agit sur deux vecteurs, que l'on notera A et B. Un produit scalaire prend : la longueur des deux vecteurs, et l'angle entre les deux vecteurs noté <math>\omega</math>. Le produit scalaire est équivalent à la formule suivante : : <math>\text{Produit scalaire de deux vecteurs A et B} = \vec{A} \cdot \vec{B} = A \times B \times \cos{(\omega)}</math>, avec A et B la longueur des deux vecteurs A et B. L'avantage est que le produit scalaire se calcule simplement avec des additions, soustractions et multiplications, des opérations que les cartes graphiques savent faire très facilement. Le produit scalaire de deux vecteurs de coordonnées x,y,z est le suivant : : <math>\vec{A} \cdot \vec{B} = x_A \times x_B + y_A \times y_B + z_A \times z_B</math> En clair, on multiplie les coordonnées identiques, et on additionne les résultats. Rien de compliqué. Un avantage est que tous les vecteurs vus précédemment sont normalisés, à savoir qu'ils ont une longueur qui vaut 1. Ainsi, le calcul du produit scalaire devient équivalent au calcul du produit scalaire. ===La réflexion de la lumière sur la surface=== [[File:Ray Diagram 2.svg|vignette|Reflection de la lumière sur une surface parfaitement lisse.]] Maintenant que nous venons de voir le terme géométrique, voyons le BRDF, qui définit comment la surface de l'objet 3D réfléchit la lumière. Vos cours de collège vous ont sans doute appris que la lumière est réfléchie avec le même angle d'arrivée. L'angle d'incidence et l'angle de réflexion sont égaux, comme illustré ci-contre. On parle alors de '''réflexion parfaite'''. Mais cela ne vaut que pour une surface parfaitement lisse, comme un miroir parfait. Dans la réalité, une surface a tendance à renvoyer des rayons dans toutes les directions. La raison est qu'une surface réelle est rugueuse, avec de petites aspérités et des micro-reliefs, qui renvoient la lumière dans des directions "aléatoires". La lumière « rebondit » sur la surface de l'objet et une partie s'éparpille dans un peu toutes les directions. On parle alors de '''réflexion diffuse'''. {| |- |[[File:Dioptre reflexion diffuse speculaire refraction.svg|vignette|upright=1.4|Différence entre réflexion diffuse et spéculaire.]] |[[File:Diffuse reflection.svg|vignette|upright=1|Réflexion diffuse.]] |} Maintenant, imaginons que la surface n'ait qu'une réflexion diffuse, pas d'autres formes de réflexion. Et imaginons aussi que cette réflexion diffuse soit parfaite, à savoir que la lumière réfléchie soit renvoyée à l'identique dans toutes les directions, sans aucune direction privilégiée. On a alors le ''material'' le plus simple qui soit, appelé un '''''diffuse material'''''. Vu que la lumière est réfléchie à l'identique dans toutes les directions, elle sera identique peu importe où on place la caméra. La lumière finale ne dépend donc que des propriété de la surface, que de sa couleur. En clair, il suffit de donner une '''couleur diffuse''' à chaque sommet. La couleur diffuse est simplement multipliée par le terme géométrique, pour obtenir la lumière réfléchie finale. Rien de plus, rien de moins. Cela donne l'équation suivante, avec les termes suivants : * L est le vecteur pour la lumière incidente ; * N est la normale du sommet ; * I est l'intensité de la source de lumière ; * <math>C_d</math> est la couleur diffuse. : <math>\text{Illumination diffuse} = C_d \times \left[ I \times (\vec{N} \cdot \vec{L}) \right]</math> Rajoutons maintenant l'effet de la lumière ambiante à un ''material'' de ce genre. Pour rappel, la lumière ambiante vient de toutes les directions à part égale, ce qui fait que son angle d'incidence n'a donc pas d'effet. L'intensité de la lumière ambiante est déterminée lors de la création de la scène 3D, c'est une constante qui n'a pas à être calculée. Pour obtenir l'effet de la lumière ambiante sur un objet, il suffit de multiplier sa couleur diffuse par l'intensité de la lumière ambiante. Cependant, de nombreux moteurs de jeux ajoutent une '''couleur ambiante''', différente de la couleur diffuse. : <math>\text{Illumination ambiante} = C_a \times I_a</math> avec <math>C_a</math> la couleur ambiante du point de surface et <math>I_a</math> l'intensité de la lumière ambiante. En plus de la réflexion diffuse parfaite, de nombreux matériaux ajoutent une '''réflexion spéculaire''', qui n'est pas exactement la réflexion parfaite, en est très proche. Les rayons réfléchis sont très proches de la direction de réflexion parfaite, et s'atténuent très vite en s'en éloignant. Le résultat ressemble à une sorte de petit "point blanc", très lumineux, orienté vers la source de lumière, appelé le '''''specular highlight'''''. La réflexion diffuse est prédominante pour les matériaux rugueux, alors que la réflexion spéculaire est dominante sur les matériaux métalliques ou très lisses. [[File:Phong components version 4.png|centre|vignette|upright=3.0|Couleurs utilisées dans l'algorithme de Phong.]] [[File:Phong Vectors.svg|vignette|Vecteurs utilisés dans l'algorithme de Phong (et dans le calcul de l'éclairage, de manière générale).]] Pour calculer la réflexion spéculaire, il faut d'abord connaitre le vecteur pour la réflexion parfaite, que nous noterons R dans ce qui suit. Le vecteur R peut se calculer avec la formule ci-dessous : : <math>\vec{R} = 2 (\vec{L} \cdot \vec{N}) \times \vec{N} - \vec{L} </math> La réflexion spéculaire dépend de l'angle entre la direction du regard et la normale : plus celui-ci est proche de l'angle de réflexion parfaite, plus la réflexion spéculaire sera intense. Le vecteur pour la direction du regard sera noté V, pour vue ou vision. La réflexion spéculaire est une fonction qui dépend de l'angle entre les vecteurs R et V. Le calcul de la réflexion spéculaire utilise une '''couleur spéculaire''', qui est l'équivalent de la couleur diffuse pour la réflexion spéculaire. : <math>\text{BRDF spéculaire} = C_s \times f(\vec{R} \cdot \vec{V}) </math> La fonction varie grandement d'un modèle de calcul spéculaire à l'autre. Aussi, je ne rentre pas dans le détail. L'essentiel est que vous compreniez que le calcul de l'éclairage utilise de nombreux calculs géométriques, réalisés avec des produits scalaires. Les calculs géométriques utilisent la couleur d'un sommet, la normale du sommet, et le vecteur de la lumière incidente. Les autres informations sont calculées à l'exécution. ===Les algorithmes d'éclairage basiques : par triangle, par sommet et par pixel=== Dans tout ce qui a été dit précédemment, l'éclairage est calculé pour chaque sommet. Il attribue une illumination/couleur à chaque sommet de la scène 3D, ce qui fait qu'on parle d''''éclairage par sommet''', ou ''vertex lighting''. Il est assez rudimentaire et donne un éclairage très brut, mais il peut être réalisé avant l'étape de rastérisation. Mais une fois qu'on a obtenu la couleur des sommets, reste à colorier les triangles. Et pour cela, il y a deux manières de faire, qui sont appelées l'éclairage plat et l'éclairage de Gouraud. [[File:D3D Shading Triangles.png|vignette|Dans ce dessin, le triangle a un sommet de couleur bleu foncé, un autre de couleur rouge et un autre de couleur bleu clair. L’interpolation plate et de Gouraud donnent des résultats bien différents.]] L''''éclairage plat''' calcule l'éclairage triangle par triangle. Il y a plusieurs manières de faire pour ça, mais la plus simple colorie un triangle avec la couleur moyenne des trois sommets. Une autre possibilité fait les calculs d'éclairage triangle par triangle, en utilisant une normale par triangle et non par sommet, idem pour les couleurs ambiante/spéculaire/diffuse. Mais c'est plus rare car cela demande de placer la normale quelque part dans le triangle, ce qui rajoute des informations. L''''éclairage de Gouraud''' effectue lui aussi une moyenne de la couleur de chaque sommet, sauf que celle-ci est pondérée par la distance du sommet avec le pixel. Plus le pixel est loin d'un sommet, plus son coefficient est petit. Typiquement, le coefficient varie entre 0 et 1 : de 1 si le pixel est sur le sommet, à 0 si le pixel est sur un des sommets adjacents. La moyenne effectuée est généralement une interpolation bilinéaire, mais n'importe quel algorithme d'interpolation peut marcher, qu'il soit simplement linéaire, bilinéaire, cubique, hyperbolique. L'étape d'interpolation est prise en charge par l'étape de rastérisation, qui effectue cette moyenne automatiquement. L'éclairage par sommet a eu son heure de gloire, mais il est maintenant remplacé par l''''éclairage par pixel''' (''per-pixel lighting''), qui calcule l'éclairage pixel par pixel. En clair, l’éclairage est finalisé après l'étape de rastérisation, il ne se fait pas qu'au niveau de la géométrie. Il existe plusieurs types d'éclairage par pixel, mais on peut les classer en deux grands types : l'éclairage de Phong et le ''bump/normal mapping''. L''''éclairage de Phong''' calcule l'éclairage pixel par pixel. Avec cet algorithme, la géométrie n'est pas éclairée : les couleurs des sommets ne sont pas calculées. A la place, les normales sont envoyées à l'étape de rastérisation, qui effectue une opération d'interpolation, qui renvoie une normale pour chaque pixel. Les calculs d'éclairage utilisent alors ces normales pour faire les calculs d'éclairage pour chaque pixel. La technique du '''''normal mapping''''' est assez simple à expliquer, sans compter que plusieurs cartes graphiques l'ont implémentée directement dans leurs circuits. Là où l'éclairage de Phong interpole les normales pour chaque pixel, le ''normal-mapping'' précalcule les normales d'une surface dans une texture, appelée la ''normal-map''. Lors des calculs d'éclairage, la carte graphique lit les normales adéquates directement depuis cette texture, puis fait les calculs d'éclairage avec. [[File:WallSimpleAndNormalMapping.png|centre|vignette|upright=2|Différence sans et avec ''normal-mapping''.]] Avec cette technique, l'éclairage n'est pas géré par pixel, mais par texel, ce qui fait qu'il a une qualité de rendu un peu inférieure à un vrai éclairage de Phong, mais bien supérieure à un éclairage par sommet. Par contre, les techniques de ''normal mapping'' permettent d'ajouter du relief et des détails sur des surfaces planes en jouant sur l'éclairage. Elles permettent ainsi de simplifier grandement la géométrie rendue, tout en utilisant l'éclairage pour compenser. [[File:Bump mapping.png|centre|vignette|upright=2|Bump mapping]] L'éclairage par pixel a une qualité d'éclairage supérieure aux techniques d'éclairage par sommet, mais il est aussi plus gourmand. L'éclairage par pixel est utilisé dans presque tous les jeux vidéo depuis DOOM 3, en raison de sa meilleure qualité, mais cela n'aurait pas été possible si le matériel n'avait pas évolué de manière à incorporer des algorithmes d'éclairage matériel assez puissants, avant de basculer sur un éclairage programmable. La différence entre l'éclairage par pixel et par sommet se voit assez facilement à l'écran. L'éclairage plat donne un éclairage assez carré, avec des frontières assez nettes. L'éclairage de Gouraud donne des ombres plus lisses, dans une certaine mesure, mais pèche à rendre correctement les reflets spéculaires. L'éclairage de Phong est de meilleure qualité, surtout pour les reflets spéculaires. es trois algorithmes peuvent être implémentés soit dans la carte graphique, soit en logiciel. Nous verrons comment les cartes graphiques peuvent implémenter ces algorithmes, dans les deux prochains chapitres. {| |- |[[File:Per face lighting.png|vignette|upright=1|Flat shading]] |[[File:Per vertex lighting.png|vignette|upright=1|Gouraud Shading]] |[[File:Per fragment lighting.png|vignette|upright=1|Phong Shading]] |- |[[File:Per face lighting example.png|vignette|upright=1|Flat shading]] |[[File:Per vertex lighting example.png|vignette|upright=1|Gouraud Shading]] |[[File:Per fragment lighting example.png|vignette|upright=1|Phong Shading]] |} ===Les ''shaders'' : des programmes exécutés sur le GPU=== Maintenant que nous venons de voir les algorithmes d'éclairages, il est temps de voir comment les réaliser sur une carte graphique. Nous venons de voir qu'il y a une différence entre l'éclairage par pixel et par sommet. Intuitivement, l'éclairage par sommet devrait se faire avec les calculs géométriques, alors que l'éclairage par pixel devrait se faire après avoir appliqué les textures. Les toutes premières cartes graphiques ne géraient ni l'éclairage par sommet, ni l'éclairage par pixel. Elles laissaient les calculs géométriques au CPU. Par la suite, la Geforce 256 a intégré '''circuit de ''Transform & Lightning''''', qui s'occupait de tous les calculs géométriques, éclairage par sommet inclus (d'où le L de T&L). Elle gérait alors l'éclairage par sommet, mais un algorithme particulier, qui n'était pas très flexible. Il ne gérait que des ''material'' bien précis (des ''Phong materials''), rien de plus. {|class="wikitable" |- ! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders'' |- | Unité de T&L : géométrie | Rastérisation | Placage de textures | ''Raster Operations Pipeline'' |} L'amélioration suivante est venue sur la Geforce 3 : l'unité de T&L est devenue programmable. Au vu le grand nombre d'algorithmes d'éclairages possibles et le grand nombre de ''materials'' possibles, c'était la seule voie possibles. Les programmeurs pouvaient programmer leurs propres algorithmes d'éclairage par sommet, même s'ils devaient aussi programmer les étapes de transformation et de projection. Mais nous détaillerons cela dans un chapitre dédié sur l'historique des GPUs. Ce qui est important est que la Geforce 3 a introduit une fonctionnalité absolument cruciale pour le rendu 3D moderne : les '''''shaders'''''. Il s'agit de programmes informatiques exécutés par la carte graphique, qui servaient initialement à coder des algorithmes d'éclairage. D'où leur nom : ''shader'' pour ''shading'' (éclairage en anglais). Cependant, l'usage modernes des shaders dépasse le cadre des algorithmes d'éclairage. L'avantage est que cela simplifie grandement l'implémentation des algorithmes d'éclairage. Pas besoin de les intégrer dans la carte graphique pour les utiliser, pas besoin d'un circuit distinct pour chaque algorithme. Sans shaders, si la carte graphique ne gère pas un algorithme d'éclairage, on ne peut pas l'utiliser. A la rigueur, il est parfois possible de l'émuler avec des contournements logiciels, mais au prix de performances souvent désastreuses. Avec des shaders, il est possible de programmer l'algorithme d'éclairage de notre choix, pour l'exécuter sur la carte graphique, avec des performances plus que convenables. [[File:Implémentation de l'éclairage sur les cartes graphiques.png|vignette|Implémentation de l'éclairage sur les cartes graphiques]] Il existe plusieurs types de shaders, mais les deux principaux sont les '''''vertex shaders''''' et les '''''pixel shaders'''''. Les pixels shaders s'occupent de l'éclairage par pixel, leur nom est assez parlent. Les vertex shaders s'occupent de l'éclairage par sommet, mais aussi des étapes de transformation/projection. Je parle bien des trois étapes de transformation vues plus haut, qui effectuent des calculs de transformation de coordonnées avec des matrices. La raison à cela est que les calculs de transformation ressemblent beaucoup aux calculs d'éclairage par sommet. Ils impliquent tous deux des calculs vectoriels, comme des produits scalaires et des produits vectoriels, qui agissent sur des sommets/triangles. Si la carte graphique incorpore un processeur de shader capable de faire de tels calculs, alors il peut servir pour les deux. Pour implémenter les shaders, il a fallu ajouter des processeurs à la carte graphique. Les processeurs en question exécutent les shaders, ils peuvent lire ou écrire dans des textures, mais ne font rien d'autres. Les ''vertex shaders'' font tout ce qui a trait à la géométrie, ils remplacent l'unité de T&L. Les pixels shaders sont entre la rastérisation et les ROPs, ils sont très liés à l'unité de texture. {|class="wikitable" |- ! colspan="4" | Cartes accélératrices PC, avant l'arrivée des ''shaders'' |- | rowspan="2" class="f_rouge" | ''Vertex shader'' | rowspan="2" | Rastérisation | Placage de textures | rowspan="2" |''Raster Operations Pipeline'' |- | class="f_rouge" | ''Pixel shader'' |} {{NavChapitre | book=Les cartes graphiques | prev=Les cartes d'affichage des anciens PC | prevText=Les cartes d'affichage des anciens PC | next=Avant les GPUs : les cartes accélératrices 3D | nextText=Avant les GPUs : les cartes accélératrices 3D }}{{autocat}} 0g4zv11w8fcrdp66w1zhnu81vwndg7t Fonctionnement d'un ordinateur/L'espace d'adressage du processeur 0 79337 763965 762741 2026-04-18T20:40:03Z Mewtow 31375 /* Le mode réel : l'adressage sur 20 bits */ 763965 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] Sur une '''architecture Harvard modifiée''', le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans les registres généraux. Le processeur doit pour cela avoir des instructions de lecture dédiées. En clair, il y a une instruction LOAD pour lire dans la mémoire ROM, une autre pour lire dans la RAM. [[File:Espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Espaces d'adressage sur une archi harvard modifiée]] Une autre solution utilise des instructions de copie, qui copient les constantes en mémoire RAM, donc d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== Les ordinateurs modernes sont des architectures Von Neumann, avec un seul espace d'adressage, qui est partagé suivant les besoins. Et un point important est que le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage, sauf pour ce qui est réservé aux périphériques et à la mémoire ROM. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, au moins en partie. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Et il faut alors résoudre un problème : le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. [[File:Partitions mémoire.png|vignette|upright=1|Partitions mémoire]] Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous partons du principe qu'un programme est égal à un segment, pour simplifier les explications. Mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent réclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Pour éviter cela, il suffit de vérifier qu'un programme accède bien à son segment et pas ailleurs. Il suffit de vérifier si l'adresse lue/écrite est dans le bon intervalle. Il suffit alors d'utiliser deux registres limites, rien de plus. Mais au-delà de cette simple vérification, si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Dans ce qui suit, nous allons étudier l'espace d'adressage des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui géraient donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la '''mémoire conventionnelle'''. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Le premier kibioctet de la mémoire conventionnelle est réservée au vecteur d'interruption, le reste est réservé à de la RAM. Les 640 kibioctets ne sont pas forcément remplit par de la RAM, il peut y avoir des adresses vides si l'ordinateur n'a pas installé assez de RAM. La mémoire haute est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était libre. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui était appelé via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui crée une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur. Elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, une erreur est levée. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Il s'agit des techniques ''Top Byte Ignore'' d'ARM, ''Upper Address Ignore'' d'AMD, et ''Linear Address Masking'' d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> hvheatlbhlqpuqkbntvtodmjz0e4cxb 763966 763965 2026-04-18T20:41:36Z Mewtow 31375 /* Le mode réel : l'adressage sur 20 bits */ 763966 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] Sur une '''architecture Harvard modifiée''', le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans les registres généraux. Le processeur doit pour cela avoir des instructions de lecture dédiées. En clair, il y a une instruction LOAD pour lire dans la mémoire ROM, une autre pour lire dans la RAM. [[File:Espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Espaces d'adressage sur une archi harvard modifiée]] Une autre solution utilise des instructions de copie, qui copient les constantes en mémoire RAM, donc d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== Les ordinateurs modernes sont des architectures Von Neumann, avec un seul espace d'adressage, qui est partagé suivant les besoins. Et un point important est que le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage, sauf pour ce qui est réservé aux périphériques et à la mémoire ROM. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, au moins en partie. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Et il faut alors résoudre un problème : le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. [[File:Partitions mémoire.png|vignette|upright=1|Partitions mémoire]] Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous partons du principe qu'un programme est égal à un segment, pour simplifier les explications. Mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent réclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Pour éviter cela, il suffit de vérifier qu'un programme accède bien à son segment et pas ailleurs. Il suffit de vérifier si l'adresse lue/écrite est dans le bon intervalle. Il suffit alors d'utiliser deux registres limites, rien de plus. Mais au-delà de cette simple vérification, si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Dans ce qui suit, nous allons étudier l'espace d'adressage des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui géraient donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, le processeur utilisait la segmentation, que nous verrons dans le prochain chapitre. Pour le moment, nous allons dire que la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. C'est une simplification qui ne fera pas de mal. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la '''mémoire conventionnelle'''. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Le premier kibioctet de la mémoire conventionnelle est réservée au vecteur d'interruption, le reste est réservé à de la RAM. Les 640 kibioctets ne sont pas forcément remplit par de la RAM, il peut y avoir des adresses vides si l'ordinateur n'a pas installé assez de RAM. La mémoire haute est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était libre. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui était appelé via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui crée une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur. Elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, une erreur est levée. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Il s'agit des techniques ''Top Byte Ignore'' d'ARM, ''Upper Address Ignore'' d'AMD, et ''Linear Address Masking'' d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> lp1o1gsaqka8uylvlowvlolx72d9h4e 763967 763966 2026-04-18T20:42:04Z Mewtow 31375 /* Le mode réel : l'adressage sur 20 bits */ 763967 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] Sur une '''architecture Harvard modifiée''', le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans les registres généraux. Le processeur doit pour cela avoir des instructions de lecture dédiées. En clair, il y a une instruction LOAD pour lire dans la mémoire ROM, une autre pour lire dans la RAM. [[File:Espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Espaces d'adressage sur une archi harvard modifiée]] Une autre solution utilise des instructions de copie, qui copient les constantes en mémoire RAM, donc d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== Les ordinateurs modernes sont des architectures Von Neumann, avec un seul espace d'adressage, qui est partagé suivant les besoins. Et un point important est que le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage, sauf pour ce qui est réservé aux périphériques et à la mémoire ROM. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, au moins en partie. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Et il faut alors résoudre un problème : le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. [[File:Partitions mémoire.png|vignette|upright=1|Partitions mémoire]] Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous partons du principe qu'un programme est égal à un segment, pour simplifier les explications. Mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent réclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Pour éviter cela, il suffit de vérifier qu'un programme accède bien à son segment et pas ailleurs. Il suffit de vérifier si l'adresse lue/écrite est dans le bon intervalle. Il suffit alors d'utiliser deux registres limites, rien de plus. Mais au-delà de cette simple vérification, si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Dans ce qui suit, nous allons étudier l'espace d'adressage des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui géraient donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, le processeur utilisait la segmentation, que nous verrons dans le prochain chapitre. Pour le moment, nous allons dire que la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. C'est une simplification qui ne fera pas de mal. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la '''mémoire conventionnelle'''. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. La mémoire haute est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était libre. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui était appelé via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui crée une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur. Elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, une erreur est levée. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Il s'agit des techniques ''Top Byte Ignore'' d'ARM, ''Upper Address Ignore'' d'AMD, et ''Linear Address Masking'' d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> ncmxj1bzlgnfppn9c5n62y9tjs3ewfu Les cartes graphiques/Avant les GPUs : les cartes accélératrices 3D 0 81913 763960 763568 2026-04-18T16:52:14Z Mewtow 31375 /* Les cartes graphiques en mode immédiat et à tuile */ 763960 wikitext text/x-wiki Dans le chapitre précédent, nous avons vu les bases du rendu 3D. Nous avons parlé de textures, de rastérisation, des calculs d'éclairage, et de bien d'autres choses. Vers la fin du chapitre, nous avons parlé des shaders, des programmes informatiques exécutés sur la carte graphique. Mais ils n'ont pas été toujours présents ! Les anciennes cartes graphiques faisaient sans shaders. Elles étaient autrefois appelées des '''cartes accélératrices 3D''', encore que la terminologie ne soit pas très précise.Nous les opposerons aux cartes graphiques capables d'exécuter des shaders, qui sont couramment appelées des '''Graphic Processing Units''', des GPUs. L'introduction des shaders a grandement modifié l'architecture des cartes graphiques. Il a fallu ajouter des processeurs pour exécuter les shaders, qui n'étaient pas là avant. Par contre, les circuits déjà présents ont été conservés, intégrés aux processeurs de shaders, ou remplacés par ceux-ci. D'un point de vue pédagogique, il est préférable de voir les cartes accélératrices 3D, avant de voir comment elles ont évolués vers des GPUs. Et nous allons voir cela dans deux chapitres. Ce chapitre portera sur les cartes accélératrices 3D, sans shaders, alors que le suivant expliquera comment s'est passée la transition vers les GPUs. : Nous allons nous concentrer sur les cartes graphiques à placage de texture inverse, le placage de texture direct ayant déjà été abordé dans le chapitre précédent. ==L'architecture d'une carte graphique 3D== Une carte accélératrice 3D est un carte d'affichage à laquelle on aurait rajouté des circuits de rendu 3D. Elle incorpore donc tous les circuits présents sur une carte d'affichage : un VDC, une interface avec le bus, une mémoire vidéo, des circuits d’interfaçage avec l'écran, un contrôleur DMA, etc. Le VDC s'occupe de l'affichage et éventuellement du rendu 2D, mais ne s'occupe pas du traitement de la 3D. Du moins, c'est le cas sur les cartes à placage de texture inverse. Le placage de texture direct utilise au contraire un VDC avec accélération 2D très performant, comme nous l'avons vu au chapitre précédent. Mais nous mettons ce cas particulier de côté. La carte accélératrice 3D reçoit des commandes graphiques, qui proviennent du pilote de la carte graphique, exécuté sur le processeur. les commandes en question sont très variées, avec des commandes de rendu 3D, de rendu 2D, de décodage/encodage vidéo, des transferts DMA, et bien d'autres. Mais nous allons nous concentrer sur les commandes de rendu 3D, qui demandent à la carte accélératrice 3D de faire une opération de rendu 3D. Pour cela, elles précisent quel tampon de sommet utiliser, quelles textures utiliser, quels shaders sont nécessaires, etc. La carte accélératrice 3D traite ces commandes grâce à deux circuits : des circuits de rendu 3D, et un chef d'orchestre qui dirige ces circuits de rendu pour qu'ils exécutent la commande demandée. Le chef d'orchestre s'appelle le '''processeur de commandes''', et il sera vu en détail dans quelques chapitres. Pour le moment, nous allons juste dire qu'il s'occupe de la logistique, de la répartition du travail. Pour les commandes de rendu 3D, il commande les différentes étapes du pipeline graphique et s'assure que les étapes s’exécutent dans le bon ordre. [[File:Architecture globale d'une carte 3D.png|centre|vignette|upright=2|Architecture globale d'une carte 3D]] Les circuits de rendu 3D regroupent des circuits hétérogènes, aux fonctions fort différentes. Dans le cas le plus simple, il y a un circuit pour chaque étape du pipeline graphique. De tels circuits sont appelés des '''unités de traitement graphique'''. On trouve ainsi une unité pour le placage de textures, une unité de traitement de la géométrie, une unité de rasterization, une unité d'enregistrement des pixels en mémoire appelée ROP, etc. Les anciennes cartes graphiques fonctionnaient ainsi, mais on verra que les cartes graphiques modernes font un petit peu différemment. Pour simplifier les explications, nous allons séparer la carte graphique en deux gros circuits bien distincts. En réalité, ils sont souvent séparés en sous-circuits plus petits, mais laissons cela de côté pour le moment. * Les '''unités géométriques''' pour les calculs géométriques ; * Les '''pipelines de pixel''' qui rastérisent l'image, plaquent les textures, et autres. Les unités géométriques manipulent des triangles, sommets ou polygones, donc des données géométriques. Les unités de pixel font tout le reste, mais le gros de leur travail est de manipuler des pixels ou des texels. Dans ce chapitre, on considère que les deux sont des circuits fixes, nous verrons leur évolution vers des processeurs programmables dans le prochain chapitre. ===Les circuits de traitement des pixels=== Parlons un peu plus en détail des pipelines de pixels. Pour mieux comprendre ce qu'elles font, il est intéressant de regarder ce qu'il y a dans un pipeline de pixel. Un pipeline de pixel effectue plusieurs opérations les unes à la suite, dans un ordre bien précis. Et cela explique l'usage du terme "pipeline" pour les désigner. Et ces opérations sont souvent réalisées par des circuits séparés, qui sont : * Un '''rastériseur''' qui fait le lien entre triangles et pixels ; * Une '''unité de texture''' qui lit les textures et les plaque sur les modèles 3D ; * Un '''ROP''' (''Raster Operation Pipeline''), qui gère grossièrement le tampon de profondeur (''z-buffer''). Le circuit de '''rastérisation''' prend en charge la rastérisation proprement dite. Pour rappel, la rastérisation projette une scène 3D sur l'écran. Elle fait passer d'une scène 3D à un écran en 2D avec des pixels. Lors de la rastérisation, chaque sommet est associé à un ou plusieurs pixels, à savoir les pixels qu'il occupe à l'écran. Elle fournit aussi diverses informations utiles pour la suite du pipeline graphique : la profondeur du sommet associé au pixel, les coordonnées de textures qui permettent de colorier le pixel. L'étape de '''placage de texture''' lit la texture associée au modèle 3D et identifie le texel adéquat avec les coordonnées textures, pour colorier le pixel. On travaille pixel par pixel, on récupère le texel associé à chaque pixel. Soit l'inverse du placage de texture direct, qui traversait une texture texel par texel, pour recopier le texel dans le pixel adéquat. Après l'étape de placage de textures, la carte graphique enregistre le résultat en mémoire. Lors de cette étape, divers traitements de '''post-traitement''' sont effectués et divers effets peuvent être ajoutés à l'image. Un effet de brouillard peut être ajouté, des tests de profondeur sont effectués pour éliminer certains pixels cachés, l'antialiasing est ajouté, on gère les effets de transparence, etc. Un chapitre entier sera dédié à ces opérations. [[File:Unité post-géométrie d'une carte graphique sans elimination des surfaces cachées.png|centre|vignette|upright=1.5|Unité post-1.5éométrie d'une carte graphique sans elimination des surfaces cachées]] ===Les circuits d'élimination des pixels cachés=== L'élimination des surfaces cachées élimine les triangles invisibles à l'écran, car cachés par un objet opaque. En théorie, elle est prise en charge à la toute fin du pipeline, dans les ROPs, car cela permet de gérer la transparence. En effet, on ne sait pas si une texture transparente sera plaquée sur le triangle ou non. En clair, on doit éliminer les triangles invisibles après le placage de textures, et donc dans les ROP. Les ROPs se chargent à la fois de l’élimination des pixels cachées et de la transparence, les deux s’influençant l'un l'autre. [[File:Unité post-géométrie d'une carte graphique avec elimination des surfaces cachées dans les ROPs.png|centre|vignette|upright=2|Unité post-géométrie d'une carte graphique avec élimination des surfaces cachées dans les ROPs]] Il y a cependant des cas où on sait d'avance que les textures ne sont pas transparentes. Dans ce cas, la carte graphique utilise les circuits d'élimination des pixels cachés juste après la rastérisation. Cela permet d'éliminer à l'avance les triangles dont on sait qu'ils ne seront pas rendus. [[File:Unité post-géométrie d'une carte graphique.png|centre|vignette|upright=2|Unité post-géométrie d'une carte graphique]] Les deux possibilités coexistent sur les cartes graphiques modernes. Une carte graphique moderne peut éliminer les surfaces cachées avant et après la rastérisation, grâce à des techniques d''''''early-z''''' dont nous parlerons plus tard, dans un chapitre dédié sur la rastérisation. ===Les circuits d'éclairage=== Les explications précédentes décrivent une carte graphique qui ne gère pas les techniques d'éclairage, et nous allons remédier à cela immédiatement. L'éclairage a été pris en charge avant même l'arrivée des shaders, dès les années 2000. Par contre, les cartes accélératrices pour PC géraient uniquement l'éclairage par sommet. Elles utilisaient un circuit non-programmable, appelé le '''circuit de ''Transform & Lightning''''', qui effectue les calculs d'éclairage par sommet (le L de T&L), en plus des calculs de transformation (le T de T&L). La première carte graphique à avoir intégré un circuit de T&L était la Geforce 256, la Geforce 1. L'unité de T&L a rapidement été remplacée par les ''vertex shader'', dont nous reparlerons d'ici quelques chapitres. Dès la Geforce 3, ce remplacement été effectué. L'unité de T&L calcule une couleur RGB pour chaque sommet/triangle, appelée la '''couleur de sommet'''. Une fois calculée par l'unité de T&L, la couleur de sommet est envoyée à l'unité de rastérisation. L'unité de rastérisation calcule la couleur des pixels à partir des trois couleurs de sommet. Pour cela, il y a deux méthodes principales, qui correspondent à l'éclairage plat et l'éclairage de Gouraud, qu'on a vu dans le chapitre précédent. Les cartes accélératrices utilisaient généralement l'éclairage de Gouraud. L'éclairage de Gouraud effectue une interpolation, à savoir une sorte de moyenne pondérée de la couleur des trois sommets. L'éclairage de Gouraud demande donc d'ajouter un circuit d'interpolation pour les couleurs des sommets. Il fait normalement partie du circuit de rastérisation, comme on le verra dans le chapitre dédié sur la rastérisation. Pour donner un exemple, la console de jeu Playstation 1 gérait l'éclairage de Gouraud directement en matériel, mais seulement partiellement. Elle n'avait pas de circuit de T&L, ni de ''vertex shaders'', mais intégrait une unité de rastérisation qui interpolait les couleurs de chaque sommet. Enfin, il faut prendre en compte les textures. Pour cela, le pixel texturé est multiplié par la luminosité/couleur calculée par l'unité géométrique. Il y a donc un '''circuit de combinaison''' situé après l'unité de texture qui effectue la combinaison/multiplication. Le circuit de combinaison est parfois configurable, à savoir qu'on peut remplacer la multiplication par une addition ou d'autres opérations. Un tel circuit de combinaison s'appelle alors un '''''combiner''''', dans la vieille nomenclature graphique de l'époque des années 90-2000. [[File:Implémentation de l'éclairage par sommet avec des combiners.png|centre|vignette|upright=2|Implémentation de l'éclairage par sommet avec des combiners]] Il a existé quelques rares cartes graphiques capables de faire de l'éclairage par pixel en matériel. Un exemple de carte graphique capable de faire cela est celle de la Nintendo DS, la PICA200. Créée par une startup japonaise, elle incorporait un circuit de T&L, un éclairage de Phong, du ''cel shading'', des techniques de ''normal-mapping'', de ''Shadow Mapping'', de ''light-mapping'', du ''cubemapping'', de nombreux effets de post-traitement (bloom, effet de flou cinétique, ''motion blur'', rendu HDR, et autres). Pour l'éclairage de Phong, il faut ajouter une unité qui fasse les calculs d'éclairage par pixel, et renvoie son résultat. La couleur de pixel calculée est ensuite combinée avec une texture, avec un ''combiner''. Du moins, si la carte accélératrice supporte les textures... Il faut aussi que le rastériseur interpole les normales, et non des couleurs de sommets comme avec l'éclairage de Gouraud. Les normales sont fournies par l'unité de T&L, ce qui demande une modification assez importante des unités de T&L et du rastériseur. [[File:Implémentation de l'éclairage par pixel avec des combiners.png|centre|vignette|upright=2|Implémentation de l'éclairage par pixel avec des combiners]] Voyons maintenant le ''bump-mapping'' et le ''normal-mapping''. Pour rappel, les deux dernières mémorisent des informations d'éclairage dans une texture en mémoire vidéo. La texture contient des informations de relief pour le ''bump-mapping'', des normales précalculées pour le ''normal-mapping''. Pour cela, l'unité d'éclairage par pixel doit être reliée à l'unité de texture, mais l'implémentation matérielle n'est pas aisée. [[File:Normal mapping matériel.png|centre|vignette|upright=2|Normal mapping matériel]] ==Les cartes graphiques avec plusieurs unités parallèles== Plus haut, nous avons décrit une carte graphique basique, très basique, avec seulement quatre unités. Une unité pour les calculs géométriques, un rastériseur, une unité pour les pixels/textures et un ROP. Cependant, les cartes graphiques ayant cette architecture sont très rares, pour ne pas dire inexistantes. Il n'est pas impossible que les toutes premières cartes graphiques aient suivi à la lettre cette architecture, mais même cela n'est pas sur. La raison : toutes les cartes graphiques dupliquent les circuits précédents pour gagner en performance, mais aussi pour s'adapter aux contraintes du rendu 3D. ===L'amplification des pixels et son impact sur les cartes graphiques=== Un triangle prend une certaine place à l'écran, il recouvre un ou plusieurs pixels lors de l'étape de rastérisation. Le nombre de pixels recouvert dépend fortement du triangle, de sa position, de sa profondeur, etc. Un triangle peut donner quelques pixels lors de l'étape de rastérisation, alors qu'un autre va couvrir 10 fois de pixels, un autre seulement trois fois plus, un autre seulement un pixel, etc. Le cas où un triangle ne recouvre qu'un seul pixel est rare, encore que la tendance commence à changer avec les jeux vidéos récents de la décennie 2020 utilisant l'Unreal Engine et la technologie Nanite. La conséquence est qu'il y a plus de travail à faire sur les pixels que sur les sommets, ce qui a reçu le nom d''''amplification des pixels'''. La conséquence est qu'une unité géométrique prendra un triangle en entrée, l'enverra au rastériseur, qui fournira en sortie un ou plusieurs pixels à éclairer/texturer. Et cette règle un triangle = 1,N pixels fait qu'il y a un déséquilibre entre les calculs géométriques et ce qui suit, que ce soit le placage de textures, l'éclairage par pixel ou l'enregistrement des pixels dans le ''framebuffer''. Et ce déséquilibre a un impact sur la manière dont un conçoit une carte graphique, ancienne comme moderne. S'il y a une seule unité de texture/pixels, alors le rastériseur envoie chaque pixel à texturer/éclairé un par un à l'unité de pixel. Le rastériseur produits ces pixels un par un, avec un algorithme adapté pour. L'unité géométrique attendra le temps que la rastérisation ait fini de traiter tous les pixels du triangle précédent. Elle calculera le prochain triangle pendant ce temps, mais cela ne fera que limiter la casse si beaucoup de pixels sont générés. Mais il est possible de profiter de l'amplification des pixels pour gagner en performances. L'idée est que le rastériseur produit plusieurs pixels en même temps, qui sont envoyés à plusieurs unités de texture et d'éclairage par pixel. Un exemple est illustré ci-dessous, avec une seule unité géométrique, mais quatre unités de texture, quatre unités d'éclairage par pixel, et quatre ROPs. Le rastériseur est conçu pour générer quatre pixels d'un seul coup si nécessaire. [[File:Architecture d'un GPU tenant compte de l'amplification des pixels.png|centre|vignette|upright=2.5|Architecture d'un GPU tenant compte de l'amplification des pixels]] La carte graphique précédente a des performances optimales quand un triangle recouvre 4 pixels : tout est fait en une seule passe. Si un triangle ne recouvre que 1, 2 ou 3 pixels, alors le rastériseur produira 1, 2 ou 3 et certaines unités suivant le rastériseur seront inutilisées. Mais si un triangle recouvre plus de 4 pixels, alors les pixels sont générés, texturés, éclairés et enregistrés en RAM par paquets de 4. En clair, la carte graphique peut s'adapter à l'amplification des pixels, mais pas parfaitement. Les GPU récents ont résolu partiellement ce problème avec un système de ''shaders'' unifiés, mais qu'on ne peut pas expliquer pour le moment. Pour donner un exemple du monde réel, les premières cartes graphique de l'entreprise SGI était de ce type. SGI a été une entreprise pinière dans le domaine du rendu en 3D, qui a opéré dans les années 80-90, avant de progressivement décliner et fermer. Elle a conçu de nombreux systèmes de type ''workstation'', donc destinés aux professionnels, avec des cartes graphiques dédiées. le grand public n'avait pas accès à ce genre de matériel, qui était très cher, vu qu'on n'était qu'au tout début de l'informatique. Nous ne détaillerons pas ces systèmes, car ils géraient leur mémoire vidéo d'une manière assez bizarre : elle était éclatée en plusieurs morceaux fusionnés chacun avec un ROP... Mais ils avaient tous une unité géométrique unique reliée à un rastériseur, qui alimentait plusieurs unités de texture/pixel et ROPs. Plus proche de nous, certaines cartes graphiques pour PC étaient aussi dans ce cas. Les toutes premières cartes graphiques pour PC n'avaient même pas de circuits géométriques, et se contentaient d'un rastériseur, d'unités de texture et de ROPs. Par la suite, la Geforce 256 a introduit une unité géométrique appelée l'unité de T&L. Les cartes graphiques de l'époque ont suivi le mouvement et ont aussi intégrée une unité géométrique presque identique. La Geforce 256 avait une unité géométrique, mais 4 unités de texture, 4 unités d'éclairage par pixel et 4 ROPs. ===Le multitexturing : dupliquer les unités de texture=== Le '''''multi-texturing''''' est une technique très importante pour le rendu 3D moderne. L'idée est de permettre à plusieurs textures de se superposer sur un objet. Divers effets graphiques demandent d'ajouter des textures par-dessus d'autres textures, pour ajouter des détails, du relief, sur une surface pré-existante. Un exemple intéressant vient des jeux de tir : ajouter des impacts de balles sur les murs. Pour cela, on plaque une texture d'impact de balle sur le mur, à la position du tir. Il s'agit là d'un exemple de ''decals'', des petites textures ajoutées sur les murs ou le sol, afin de simuler de la poussière, des impacts de balle, des craquelures, des fissures, des trous, etc. Le ''multi-texturing'' implique que calculer un pixel implique de lire plusieurs textures. En général, un pixel avec ''multi-texturing'' demande de lire deux textures, rarement plus. La carte graphique doit alors être capable d'accéder à deux textures en même temps, ou du moins faire semblant que. De plus, elle doit combiner les deux textures pour générer le pixel voulu, ce qui demande d'ajouter un circuit qui combine deux texels (des pixels de texture) pour donner un pixel. La solution la plus simple est de doubler les unités de texture et de combiner les textures dans l'unité d'éclairage par pixel. Résultat : pour une unité d'éclairage par pixel, on a deux unités de textures. La Geforce 2 et 3 utilisaient cette solution, dont le seul défaut est que la seconde unité de texture était utilisée seulement pour les objets sur lesquels le ''multi-texturing'' était utilisé. Les cartes ATI, le concurrent de l'époque de NVIDIA, aujourd'hui racheté par AMD, triplait les unités de texture. Mais cette possibilité était peu utilisée, la majorité des jeux se dépassant pas deux texture max par pixel. C'est sans doute pour cette raison que ce triplement a été abandonné à la génération suivante, les Radeon 9000 et 8500 se contentant de doubler les unités de texture. {|class="wikitable" |- ! Nom de la carte graphique !! Unités géométriques !! Unité de texture !! Unités de pixel !! ROPs |- ! Geforce 2 d'entrée de gamme | 1 || 2 || 4 || 2 |- ! Geforce 2 milieu/haut de gamme, Geforce 3 | 1 || 4 || 8 || 4 |- ! Radeon R100 bas de gamme | 1 || 1 || 3 || 1 |- ! Radeon R100 autres | 1 || 2 || 6 || 2 |} ===L'usage de plusieurs unités géométriques=== Pour encore augmenter les performances, il est possible d'utiliser plusieurs circuits de calcul géométriques, plusieurs unités géométriques. Et ce peu importe que ces unités soient des processeurs ou des circuits fixes non-programmables. Et pour cela, il existe deux grandes implémentations : utiliser plusieurs processeurs placés en série, ou les mettre en parallèle. Comprendre la première implémentation demande de faire quelques rappels sur les calculs géométriques. ====L'usage d'un pipeline géométrique proprement dit==== Pour rappel, le pipeline géométrique regroupe les quatre étapes suivantes : * L'étape de '''chargement des sommets/triangles''', qui sont lus depuis la mémoire vidéo et injectés dans le pipeline graphique. * L'étape de '''transformation''' effectue deux changements de coordonnées pour chaque sommet. ** Premièrement, elle place les objets au bon endroit dans la scène 3D, ce qui demande de mettre à jour les coordonnées de chaque sommet de chaque modèle. C'est la première étape de calcul : l'''étape de transformation des modèles 3D''. ** Deuxièmement, elle effectue un changement de coordonnées pour centrer l'univers sur la caméra, dans la direction du regard. C'est l'étape de ''transformation de la caméra''. * La phase d''''éclairage''' (en anglais ''lighting'') attribue une couleur à chaque sommet, qui définit son niveau de luminosité : est-ce que le sommet est fortement éclairé ou est-il dans l'ombre ? * La phase d''''assemblage des primitives''' regroupe les sommets en triangles. * Les phases de '''''clipping''''' ou le '''''culling''''' agissent sur des sommets/triangles/primitives, même si elles sont souvent regroupées dans l'étape de rastérisation. Si on met de côté le chargement des sommets/triangles, il est possible de faire tous ces calculs en bloc, dans un seul processeur ou une seule unité de T&L. Mais une autre idée, plus simple, attribue un processeur/circuit pour chaque étape. En faisant cela, on peut traiter plusieurs triangles/sommets en même temps, chacun étant dans une étape différente, chacun dans un processeur/circuit. Ceux qui auront déjà lu un cours d'architecture des ordinateurs reconnaitront la fameuse technique du pipeline, mais appliquée ici à un algorithme plus conséquent. Les processeurs sont en série, et chaque processeur reçoit les résultats du processeur précédent, et envoie son résultat au processeur suivant. Sauf en début ou en bout de chaine, évidemment. Pour donner un exemple, les premières cartes graphiques de SGI utilisaient 10/12 processeurs enchainés l'un à la suite de l'autre. Les 4 premiers géraient les étapes de transformation, les 6 suivants faisaient les opérations de clipping/culling, les deux derniers faisaient la rastérisation proprement dite. Pour lisser les transferts de données, il est possible d'ajouter des mémoires FIFOs entre les processeurs. Comme ça, si un processeur est bloqué par un calcul un peu trop long, cela ne bloque pas les processeurs précédents. A la place, le processeur précédent accumule des résultats dans la mémoire FIFOs, qui seront consommé ultérieurement. En théorie, on peut s'attendre à ce que la performance soit multipliée par le nombre de processeurs. En réalité, les étapes sont rarement équilibrées, certaines étapes prennent beaucoup plus de temps que les autres, ce qui fait que la répartition des calculs n'est pas idéale : certains processeurs attendent que le processeur suivant ait finit son travail. De plus, l'organisation en pipeline entraine des couts de transmission/communication entre étapes, notamment si on utilise des mémoires FIFOs entre processeurs, ce qui est toujours le cas. Cette implémentation n'a été utilisée que sur les toutes premières cartes graphiques, avant l'apparition des PC grand public. Les systèmes SGI, utilisés pour des stations de travail, utilisaient cette architecture, par exemple. Mais elle est totalement abandonnée depuis les années 90. ====L'usage de plusieurs unités géométriques en parallèle==== La seconde solution utilise plusieurs unités géométriques en parallèle. Chaque unité géométrique traite un triangle/sommet de bout en bout, en faisant transformation, éclairage, etc. Mais vu qu'il y en a plusieurs, on peut traiter plusieurs triangles/sommets : un dans chaque unité géométrique. C'est la solution retenue sur toutes les cartes graphiques depuis les années 90. Mais la présence de plusieurs unités géométriques a deux conséquences : il faut alimenter plusieurs unités géométriques en triangles/sommets, il faut gérer l'envoi des triangles au rastériseur. Les deux demandent des solutions distinctes. La répartition du travail sur les unités géométriques est déléguée au processeur de commandes. Il utilise les unités géométriques à tour de rôle : on envoie le premier triangle à la première unité, le second triangle à la seconde unité, le troisième triangle à la troisième, etc. Il s'agit de ce que l'on appelle l''''algorithme du tourniquet''', qui est assez efficace malgré sa simplicité. Il marche assez bien quand tous les triangles/sommets mettent approximativement le même temps pour être traités. Si le temps de calcul varie beaucoup d'un triangle/sommet à l'autre, une solution toute simple détecte quels sont les processeurs de shaders libres et ceux occupés. Il suffit alors d'appliquer l'algorithme du tourniquet seulement sur les processeurs de shaders libres, qui n'ont rien à faire. Un autre problème survient cette fois-ci en sortie des unités géométriques. Comment connecter plusieurs unités géométriques au reste de la carte graphique ? Évidemment, la carte graphique contient plusieurs unités de texture/pixel et plusieurs ROPs. Elle tient compte de l'amplification des pixels, ce qui fait qu'il y a moins d'unités géométriques que d'autres circuits, entre 2 à 8 fois moins environ. Pour créer une carte graphique avec plusieurs unités géométriques, il y a plusieurs solutions, que nous allons détailler dans ce qui suit. Pour les explications, nous allons prendre l'exemple de cartes graphiques avec 2 unités géométriques et 8 unités de texture/pixel, et autant de ROPs. La première solution serait simplement de dupliquer les circuits précédents, en gardant leurs interconnexions. Pour l'exemple, on aurait 2 unités géométriques, chacune connectée à 4 unités de textures/pixels. L'unité géométrique est suivie par un rastériseur qui alimente 4 unités de texture/pixel, comme c'était le cas dans la section précédente. L'implémentation est alors très simple : on a juste à dupliquer les circuits et à modifier le processeur de commande. Il faut aussi modifier les connexions des ROPs à la mémoire vidéo. Mais les interconnexions avec le rastériseur ne sont pas modifiées. Un désavantage est que l'amplification des pixels n'est pas gérée au mieux. Imaginez que l'on ait deux triangles à rastériser, qui génèrent 8 pixels en tout : un qui génère 6 pixels à la rastérisation, l'autre seulement 2. Il n'est pas possible de traiter les 8 pixels générés. Le triangle générant deux pixels va alimenter deux unités de texture/pixels et en laisser deux inutilisées, l'autre triangle sera traité en deux fois (4 pixels, puis 2). La duplication bête et méchante n'utilise donc pas à la perfection les unités de texture/pixel. Une autre solution permet de gérer à la perfection l'amplification des pixels. Elle consiste à utiliser un seul rastériseur à haute performance, sur lequel on connecte les unités géométriques et les unités de texture/pixel. L'idée est que le rastériseur peut recevoir N triangles à la fois et alimenter M unités de texture/pixels. Le rastériseur unique s'occupe de faire plusieurs rastérisations de triangles à la fois, et répartit automatiquement les pixels générés sur les unités de texture/pixel. Pour donner un exemple, le GPU Geforce 6800 de NVIDIA avait 6 unités géométriques, 16 unités faisant à la fois placage de textures et éclairage par pixel, et 16 ROPs. Un point important avec ce GPU est qu'il n'avait qu'un seul rastériseur, détail sur lequel on reviendra dans ce qui suit ! [[File:GeForce 6800.png|centre|vignette|upright=2.5|GeForce 6800, les unités géométriques sont ici appelées les ''vertex processor'', les unités de texture/pixel sont les ''fragment processors'', les ROPs sont les ''pixel blending units''.]] ==Les cartes graphiques en mode immédiat et à tuile== Il est courant de dire qu'il existe deux types de cartes graphiques : celles en mode immédiat, et celles avec un rendu en tuiles (''tiles''). Il s'agit là des deux types principaux de cartes graphiques à l'heure actuelle, mais quelques architectures faisaient autrement dans le passé. Une autre classification, plus générale, sépare les cartes graphiques en cartes graphiques ''sort-last'', ''sort-first'' et ''sort-middle''. Les cartes graphiques en mode immédiat correspondent aux cartes graphiques en mode immédiat, alors que le rendu à tuile est une sous-catégorie des cartes graphiques ''sort-middle''. La différence entre les deux est liée à la manière dont les pixels/primitives sont réparties sur l'écran. Leur existence est liée au fait que les API graphiques imposent que les triangles envoyées à la carte graphique soient traités dans l'ordre. Le tampon de sommets contient en effet une liste de sommets/triangles, qui sont censés être traités dans l'ordre d'arrivée. Et si je dis censé être, c'est parce que la carte graphique ne va pas forcément traiter les triangles/pixels dans l'ordre. A la place, elle va traiter des triangles/pixels en parallèle, et il n'est pas garantit que les résultats sortent des circuits dans l'ordre d'arrivée. Après tout, certains triangles sont traités plus rapidement que d'autres, idem pour les pixels. La carte graphique doit donc remettre les résultats dans l'ordre. L'endroit du pipeline où se fait cette remise en ordre est ce qui fait la différence entre cartes graphioques ''sort last'' et ''sort middle''. Les cartes graphiques ''sort-first'' ont plusieurs pipelines séparés, chacun traitant une partie de l'écran. Ils déterminent la position des triangles à l'écran, puis répartissent les triangles dans les pipelines adéquats. Par exemple, on peut imaginer un GPU ''sort-first'' avec quatre unités séparées, chacune traitant un quart de l'écran. Au tout début du rendu, une unité de répartition détermine la position d'un triangle à l'écran, et l'envoie à l'unité adéquate. Si le triangle est dans le coin inférieur gauche, il sera envoyé à l'unité dédiée à ce coin. S'il est situé au milieu de l'écran, il sera envoyé aux quatre unités, chacune ne traitant les pixels que pour son coin à elle. Les cartes graphiques ''sort-middle'' découpent l'écran en carrés de 4, 8, 16, 32 pixels de côté , qui sont rendus séparément les uns des autres. Les morceaux d'image en question sont appelés des ''tiles'' en anglais, mot que nous avons décidé de ne pas traduire pour ne pas le confondre avec les tuiles du rendu 2D. Il y a une assignation stricte entre une unité de pixel/texture et une ''tile''. Par exemple, sur un système avec deux unités de texture/pixel, la première unité traitera les ''tiles'' paires, l'autre unité les ''tiles'' impaires. Les cartes graphiques ''sort-last'' sont l'extrême inverse. Ils ont des unités banalisées qui se moquent de l'endroit où se trouve un pixel à l'écran. Leurs unités géométriques traitent des polygones sans se préoccuper de leur place à l'écran. Le rastériseur envoie les pixels aux unités de textures/ROPs sans se soucier de leur place à l'écran. Encore que quelques optimisations s'en mêlent pour profiter au mieux des caches de texture et des caches intégrés aux ROPs, mais l'essentiel est qu'il n'y a pas de répartition fixe. Il n'y a pas de logique du type : ce pixel ou ce triangle est à tel endroit à l'écran, on l'envoie vers telle unité de texture/ROP. Ce sont les ROPs qui se chargent d'enregistrer les pixles finaux au bon endroit dans le ''framebuffer''. La gestion de la place des pixels à l'écran se fait donc à la toute fin du pipeline, d'où le nom de ''sort-last''. Pour résumer, les trois types de cartes graphiques se distinguent suivant l'endroit où les triangles/pixels sont répartis suivant leur place à l'écran. Avec le ''sort-first'', ce sont les triangles qui sont triés suivant leur place à l'écran. Le tri a donc lieu avant les unités géométriques. Avec le ''sort-middle'', ce sont les fragments générés par la rastérisation qui sont triés suivant leur place à l'écran, d'où l'existence de ''tiles''. Le tri a lieu entre les unités géométriques et le rastériseur. Les unités géométriques se moquent de la place à l'écran des primitives qu'ils traitent, mais pas les rastériseurs et les unités de texture. Enfin, avec le ''sort-last'', ce sont les pixels finaux qui sont triés selon leur place à l'écran, seuls les ROPs se préoccupent de cette place à l'écran. Concrètement, les cartes graphiques de type ''sort-first'' sont très rares, l'auteur de ce cours n'en connait aucun exemple. Les deux autres types de cartes graphiques sont eux beaucoup plus communs. Reste à voir ce qu'il y a à l'intérieur d'une carte graphique ''sort-middle'' et/ou ''sort-last''. Pour simplifier les explications, nous allons regrouper les circuits de traitement des pixels dans un seul gros circuits appelé le rastériseur, par abus de langage. La carte graphique est donc composée de deux circuits : l'unité géométrique et le mal-nommé rastériseur. Les cartes graphiques ajoutent des mémoires caches pour la géométrie et les textures, afin de rendre leur accès plus rapide. [[File:Carte graphique, généralités.png|centre|vignette|upright=2|Carte graphique, généralités]] ===Les cartes graphiques ''sort-last'', en mode immédiat=== Les cartes graphiques en mode immédiat implémentent le pipeline graphique d'une manière assez évidente. L'unité géométrique envoie des triangles au rastériseur, qui lui-même envoie les pixels à l'unité de texture, qui elle-même envoie le pixel texturé au ROP. Elles effectuent le rendu 3D triangle par tringle, pixel par pixel. Un point important est que pendant que le pixel N est dans les ROP, les pixels N+1 est dans l'unité de texture, le pixel N+2 est dans le rastériseur et le triangle suivant est dans l'unité géométrique. En clair, on n'attend pas qu'un triangle soit affiché pour en démarrer un autre. Un problème est qu'un triangle dans une scène 3D correspond souvent à plusieurs pixels, ce qui fait que la rastérisation prend plus de temps de calcul que la géométrie. En conséquence, il arrive fréquemment que le rastériseur soit occupé, alors que l'unité de géométrie veut lui envoyer des données. Pour éviter tout problème, on insère une petite mémoire entre l'unité géométrique et le rastériseur, qui porte le nom de '''tampon de primitives'''. Elle permet d'accumuler les sommets calculés quand le rastériseur est occupé. [[File:Carte graphique en rendu immédiat.png|centre|vignette|upright=2|Carte graphique en rendu immédiat]] Le tout peut s'adapter à la présence de plusieurs unités géométriques, de plusieurs unités de texture ou processeurs de shaders, tant qu'on conserve un rastériseur unique. Il suffit alors d'adapter le tampon de primitive et le rastériseur. Si on veut rajouter des unités de texture ou des processeurs de pixel shaders, le tampon de primitives n'est pas concerné : il suffit que le rastériseur ait plusieurs sorties, une par unité de texture/pixel shader. Par contre, la présence de plusieurs unités géométriques impacte le tampon de primitive. Avec plusieurs unités géométriques, il y a deux solutions : soit on garde un tampon de primitive unique partagé, soit il y a un tampon de primitive par unité géométrique. Avec la première solution, toutes les unités géométriques sont reliées à un tampon de primitives unique. Le tampon de primitive est conçu pour qu'on puisse écrire plusieurs primitives dedans en même temps. Le rastériseur n'a pas à être modifié. Une autre solution utilise un tampon de primitive par unité géométrique. Le rastériseur peut alors piocher dans plusieurs tampons de primitive, ce qui demande de modifier le rastériseur. Il y a alors un système d'arbitrage, pour que le rastériseur pioche des primitives équitablement dans tous les tampons de primitive, pas question que l'un d'entre eux soit ignoré durant trop longtemps. ===Les cartes graphiques ''sort-middle'' des années 90=== Voyons maintenant les architectures ''sort-middle'' utilisée dans les années 80-90, à une époque où les cartes graphiques grand public n'existaient pas encore. Les cartes graphiques de l’entreprise SGI sont dans ce cas, mais aussi le Pixel Planes 5, et de nombreux autres systèmes graphiques. Elles utilisaient un rendu à ''tile'' assez original. Dans ce qui suit, nous allons décrire l'architecture des systèmes SGI, qui sont représentatifs. L'idée était que l'image était découpée en un nombre de ''tiles'' qui variait selon le système utilisé, mais qui était au minimum de 5 et pouvait aller jusqu'à 20. Et chaque ''tile'' avait sa propre unité de traitement, qui contenait un rastériseur, une unité de texture, un ROP, etc. En clair, la carte graphique contenait entre 5 et 20 unités de traitement séparées, chacune dédiée à une ''tile''. Les triangles sortant des unités géométriques étaient envoyés à toutes les unités de traitement, sans exception. Une fois le triangle réceptionné, l'unité de traitement déterminait si le triangle s'affichait dans la ''tile'' associée ou non. Si c'est le cas, le rastériseur rastérise le triangle, génère les pixels, les textures sont lues, puis le tout est enregistré en mémoire vidéo. Si ce n'est pas le cas, elle abandonne le polygone/triangle reçu. Si le triangle est partiellement dans la ''tile'', le rastériseur génère les pixels qui sont dans la ''tile'', par les autres. Précisons que les cartes de ce style incorporaient un tampon de primitive, ce qui permettait de simplifier la conception de la carte graphique. Sur la carte ''Infinite Reality'', le tampon de primitive faisait 4 méga-octets de RAM, ce qui permettait de mémoriser 65 536 sommets. Sur la carte ''Reality Engine'', il y avait même plusieurs tampons de primitives, un par unité géométrique. Les polygones sortaient des unités géométriques, étaient accumulés dans les tampons de primitives, puis étaient ''broadcastés'' à toutes les unités de traitement. Pour cela, le bus en bleu dans le schéma précédent est en réalité un réseau ''crossbar'' avec un système de ''broadcast''. Une caractéristique de ces architectures est qu'elles mettent le ''framebuffer'' à part de la mémoire vidéo. De plus, ce ''framebuffer'' est lui-même découpée en ''tile''. Sur la carte ''Reality Engine'', le ''framebuffer'' est découpé en 5 à 20 sous-''framebuffer'', un par ''tile''. Et chaque mini-''framebuffer'' est placé dans l'unité de traitement de la ''tile'' associée ! Ainsi, au lieu de connecter 5-20 ROPs à une mémoire vidéo unique, chaque ROP contient une '''''RAM tile''''', qui mémorise la ''tile'' en cours de traitement. Évidemment, cela pose quelques problèmes pour la connexion au VDC, en raison de l'absence de ''framebuffer'' unique, mais rien d'insurmontable. L'architecture est illustrée ci-dessous. : Le Pixel Planes 5 avait un système similaire, mais avait en plus un ''framebuffer'' complet, dans lequel les sous-''framebuffer'' étaient recopiés pour obtenir l'image finale. [[File:Architecture des premières cartes graphiques SGI.png|centre|vignette|upright=2|Architecture des premières cartes graphiques SGI]] Un autre détail de l'architecture est lié à la mémoire pour les textures. Les concepteurs de SGI ont décidé de séparer les textures dans une mémoire à part du reste de la mémoire vidéo. Il n'y a pour ainsi dire pas de mémoire vidéo proprement dit : la géométrie à rendre est dans une mémoire à part, idem pour les textures, et pour le ''framebuffer''. On s'attendrait à ce que la mémoire de texture soit reliée aux 5-20 unités de texture, mais les concepteurs ont décidé de faire autrement. A la place, chaque unité de texture contient une copie de la mémoire de texture, qui est donc dupliquée en 5-20 exemplaires ! Difficile de comprendre la raison de ce choix, mais cela simplifiait sans doute les interconnexions internes de la carte graphique, au prix d'un cout en RAM assez important. ===Les cartes graphiques à rendu à ''tile''=== Les cartes graphiques de SGI, vus précédemment, disposent d'une unité de traitement par ''tile''. Faire ainsi permet de nombreuses optimisations, comme éclater le ''framebuffer'' en plusieurs ''RAM tile''. Mais le cout en matériel est conséquent. Pour économiser des circuits, l'idéal serait d'utiliser moins d'unités de traitement pour les pixels/fragments/textures. Mais pour cela, il faut profondément modifier l'architecture précédente. On perd forcément le lien entre une unité de traitement et une ''tile''. Et cela impose de revoir totalement la manière dont les unités géométriques communiquent avec les unités de traitement. La solution retenue est celle des cartes graphiques à rendu en ''tile'' proprement dit, aussi appelés ''cartes graphiques TBR'' (''Tile Based Rendering''). Les plus simples n'utilisent qu'une seule unité de traitement et n'ont qu'une seule ''RAM tile''. En conséquence, les ''tiles'' sont rendues l'une après l'autre. Au lieu de rendre chaque triangle/polygone l'un après l'autre, la géométrie est intégralement rendue avant de faire la rastérisation. Les triangles sont enregistrés dans la mémoire vidéo et regroupés par ''tile'', avant la rastérisation. La mémoire vidéo contient donc plusieurs paquets de triangles, avec un paquet par ''tile''. Les paquets/''tiles'' sont envoyées au rastériseur un par un, la rastérisation se fait ''tile'' par ''tile''. La ''RAM tile'' existe toujours, même si son utilité est différente. La ''RAM tile'' accélère le rendu d'une ''tile'', car tout ce qui est nécessaire pour rendre une ''tile'' est mémorisé dedans : la ''tile'', le tampon de profondeur, le tampon de stencil et plein d'autres trucs. Pas besoin d’accéder à un gigantesque z-buffer pour toute l'image, juste d'un minuscule z-buffer pour la ''tile'' en cours de traitement, qui tient totalement dans la SRAM. : Il faut noter que les ''tiles'' sont généralement assez petites : 16 ou 32 pixels de côté, rarement plus. En comparaison, les ''tiles'' faisaient 128 pixels de côté pour les cartes de SGI. [[File:Carte graphique en rendu par tiles.png|centre|vignette|upright=2|Carte graphique en rendu par tiles]] Il est possible pour une carte graphique TBR de traiter plusieurs ''tiles'' en même temps, en parallèle, dans des unités séparées. Un exemple est celui du GPU ARM Mali 400, qui dispose d'une unité géométrique (un processeur de ''vertex''), mais 4 processeurs de pixels. Il peut donc traiter quatre ''tiles'' en même temps, chacune étant rendue dans un processeur de pixel dédié. Les 4 processeurs de pixels ont chacun leur propre ''RAM tile'' rien qu'à eux. La présence d'une ''RAM tile'' a de nombreux avantages et impacte grandement l'architecture de la carte graphique. En premier lieu, les ROPs sont drastiquement modifiés. De nombreux GPU TBR n'ont même pas de ROPs ! A la place, les ROPs sont émulés par les processeurs de pixel shader. Les ''pixel shaders'' peuvent lire ou écrire directement dans le ''framebuffer'', sur les GPU TBR, ce qui leur permet d'émuler les ROPs avec des instructions mathématique/mémoire. Le ''driver'' patche automatiquement les ''pixel shader'' pour ajouter de quoi émuler les ROPs à la fin des ''pixel shaders''. Cela garantit une économie de circuits non-négligeable. La présence d'une ''RAM tile'' fait que le tampon de profondeur disparait. Par contre, les cartes graphiques de type TBR doivent enregistrer les triangles en mémoire vidéo, et les trier par paquets. Cela compense partiellement, totalement, ou sur-compense, les économies liées à la ''RAM tile''. Le regroupement des triangles par ''tile'' s'accompagne de quelques optimisations assez sympathiques. Par exemple, les GPU TBR modernes peuvent trier les triangles selon leur profondeur, directement lors du regroupement en paquets. L'avantage est que cela permet à l'élimination des pixels cachés de fonctionner au mieux. L'élimination des pixels cachés fonctionne à la perfection quand les triangles sont triés du plus proche au plus lointain, pour les objets opaques. Les cartes graphiques en mode immédiat ne peuvent pas faire ce tri, mais les cartes graphiques TBR peuvent le faire, soit totalement, soit partiellement. Un autre avantage est que l’antialiasing est plus rapide. Pour ceux qui ne le savent pas, l'antialiasing est une technique qui améliore la qualité d’image, en simulant une résolution supérieure. Une image rendue avec antialiasing aura la même résolution que l'écran, mais n'aura pas certains artefacts liés à une résolution insuffisante. Et l'antialiasing a lieu dans et après la rastérisation, et augmente la résolution du tampon de profondeur et du z-buffer. Les cartes graphiques en mode immédiat disposent d'optimisations pour limiter la casse, mais les ROP font malgré tout beaucoup d'accès mémoire. Avec le rendu en tiles, l'antialising se fait dans la ''RAM tile'', n'a pas besoin de passer par la mémoire vidéo et est donc plus rapide. ===Des compromis différents=== Les cartes graphiques des ordinateurs de bureau ou portables sont toutes en mode immédiat, alors que celles des appareils mobiles, smartphones et autres équipements embarqués ont un rendu en ''tiles''. Les raisons à cela sont multiples, mais la principale est que le rendu en ''tiles'' marche beaucoup mieux pour le rendu en 2D, comparé aux architectures en mode immédiat, ce qui se marie bien aux besoins des smartphones et autres objets connectés. La performance d'une carte graphique est limitée par la quantité d'accès mémoire par seconde. Autant dire que les économiser est primordial. Et les cartes en mode immédiat et par tile ne sont pas égales de ce point de vue. En mode immédiat, le tampon de primitives évite de passer par la mémoire vidéo, mais le z-buffer et le ''framebuffer'' sont très gourmand en accès mémoire. Avec les architectures à tile, c'est l'inverse : la géométrie est enregistrée en mémoire vidéo, mais le tampon de profondeur n'utilise pas la RAM vidéo. Au final, les deux architectures sont optimisées pour deux types de rendus différents. Les cartes à rendu en tile brillent quand la géométrie n'est pas trop compliquée, et que la résolution est grande ou que l'antialising est activé. Les cartes en mode immédiat sont douées pour les scènes géométriquement lourdes, mais avec peu d'accès aux pixels. Le tout est limité par divers caches qui tentent de rendre les accès mémoires moins fréquents, sur les deux types de cartes, mais sans que ce soit une solution miracle. ==La performance des anciennes cartes graphiques 3D== Intuitivement, la performance d'une carte graphique dépend de la performance de chacun de ses circuits : processeur de commande, mémoire vidéo, circuits de rendu 3D, VDC, etc. En pratique, il est rare qu'on soit limité par le VDC ou le processeur de commande. Les seules limitations viennent des circuits de rendu 3D et de la mémoire vidéo. Nous ne pouvons pas aborder la performance de la mémoire vidéo pour le moment. Tout ce que l'on peut dire est qu'il faut qu'elle soit assez rapide pour alimenter le rendu 3D en données. Les circuits de rendu 3D doivent lire des triangles et textures en mémoire vidéo, qui doit être assez rapide pour ça et ne pas les faire attendre. Pour le reste, voyons la performance des circuits de rendu 3D. Il ne nous est là aussi pas possible de détailler ce qui impacte la performance d'un GPU moderne. Dès que des processeurs de shaders sont impliqués, parler de performance demande de connaitre sur le bout des doigts les processeurs de shaders, ce qu'on n'a pas encore vu à ce stade du cours. Par contre, on peut détailler ce qu'il en était pour les anciennes cartes 3D, sans processeurs de shaders. Elles contenaient des ROPs, des unités de texture, un rastériseur et une unité géométrique (l'unité de T&L). Étudions d'abord la performance des unités de texture et des ROPs. Cela nous permettra de parler d'un paramètre qui avait son importance sur les anciennes cartes graphiques, avant les années 2000 : le ''fillrate''. Le '''''fill rate''''', ou taux de remplissage, est une ancienne mesure de performance autrefois utilisée pour comparer les cartes graphiques entre elles. Il s'agit d'une mesure assez approximative, au même titre que la fréquence d'horloge. Concrètement, plus il est élevé, meilleures seront les performances, en théorie. Mais attention : les petites différences de ''fillrate'' ne suffisent pas à rendre un verdict. De plus, il existe deux types distincts de ''fillrate'' : le ''Texture Fillrate'' et le ''Pixel Fillrate''. Voyons d'abord le ''Pixel Fillrate''. ===Le ''pixel fillrate'' : la performance des ROPs=== Le '''''pixel fillrate''''' est le nombre maximal de pixels que la carte graphique peut écrire en mémoire vidéo par seconde. Il est exprimé en ''Méga-Pixels par seconde'' ou en ''Giga-Pixels par seconde'', souvent abréviés en GP/s et MP/s. C'est une unité que vous croisez sans doute pour la première fois et qui mérite quelques explications. Premièrement, dans méga-pixels par seconde, il y a mégapixels. Il s'agit d'une unité pour compter le nombre de pixels d'une image. Un mégapixel signifie tout simplement un million de pixels, un gigapixel signifie un milliard de pixels. Je précise bien un million et un milliard, ce ne sont pas des multiples de 1024, comme on est habitué à en voir en informatique. Le nombre de pixels d'une image augmente avec la résolution utilisée, mais il reste de l'ordre du mégapixel, guère plus. Voici un tableau avec les résolutions les plus utilisées et le nombre de pixels associé. {|class="wikitable" |- ! Résolution !! Nombre de pixels |- | colspan="2" | |- | colspan="2" | Résolutions anciennes en 4:3 |- | 640 × 480 || 307 200 <math>\approx</math> 0,3 MP |- | 800 × 600 || 480 000 = 0,48 MP |- | 1 024 × 768 || 786 432 <math>\approx</math> 0,8 MP |- | 1 280 × 960 || 1 228 800 <math>\approx</math> 1,2 MP |- | 1 600 × 1 200 || 1 920 000 = 1,92 MP |- | colspan="2" | |- | colspan="2" | Résolutions modernes en 16:9 |- | 1 920 × 1 080 || 2 073 600 <math>\approx</math> 2 MP |- | 3 840 × 2 160 (4k) || 8 294 400 <math>\approx</math> 8.3 MP |} Maintenant, regardons ce qui se passe si on veut rendre plusieurs images par secondes. Intuitivement, on se dit qu'il faudra un ''pixel fillrate'' minimal pour cela. Et il se trouve qu'on peut le calculer aisément. Prenons par exemple une image en 1600 × 1200, de 1,92 mégapixels. Si on veut avoir 60 images par secondes, avec cette résolution, cela fait 1,92 * 60 mégapixels par secondes. En clair, le ''pixel fillrate'' minimal se calcule en multipliant la résolution par le ''framerate''. Le ''pixel fillrate'' minimal tourne autour de la centaine de mégapixels par seconde, voire approche le gigapixel par seconde en haute résolution. Les images font entre 1 et 10 mégapixels, pour environ 100 FPS, l'intervalle colle parfaitement. Maintenant, comparons un peu avec ce dont sont capables les GPUs. Les toutes premières cartes graphiques commerciales avaient un ''pixel fillrate'' proche de la centaine de méga-pixels par seconde. Pour donner un exemple, la Geforce 256 avait un ''pixel fillrate'' de 480 MP/s, la Geforce 3 faisait entre 700 et 960 MP/s selon le modèle. De nos jours, le ''pixel fillrate'' est de l'ordre de la centaine de Gigapixels. Pour donner un exemple, les Geforce RTX 5000 ont un ''pixel fillrate'' de 82.3GP/s pour la RTX 5050, à 423.6 GP/S pour la RTX 5090. Les GPU ont un ''pixel fillrate'' qui dépasse de très loin la valeur minimale, ce qui est franchement étrange. La raison à cela est que le ''pixel fillrate'' minimal se calcule sous l'hypothèse que chaque pixel de l'image finale ne sera écrit qu'une seule fois. Mais dans les faits, il est fréquent qu'un pixel soit dessiné plusieurs fois avant d'obtenir l'image finale. La raison principale est liée aux surfaces cachées. Si un objet est derrière un autre, il arrive que celui-ci soit dessiné dans le ''framebuffer'', avant que l'objet devant soit re-dessiné par-dessus. Des pixels ont alors été écrits, puis ré-écrits. Le fait de dessiner un pixel plusieurs fois porte un nom. Il s'agit d'un phénomène d''''''overdraw''''', ou sur-dessinage en français. Le sur-dessinage fait que le ''pixel fillrate'' minimal ne suffit pas en pratique. Pour éviter tout problème, le ''pixel fillrate'' du GPU doit être supérieur au ''pixel fillrate'' minimal, d'environ un ordre de grandeur. L'élimination des surfaces cachées réduit l'''overdraw'', mais elle ne fait pas de miracles. En pratique, le sur-dessinage ne concerne qu'une partie assez mineure des pixels de l'image, et un pixel est rarement écrit plus d'une dizaine de fois. Et les GPus modernes ont un ''pixel fillrate'' tellement démentiel qu'il n'est presque jamais un facteur limitant. Le ''pixel fillrate'' d'un GPU dépend de plusieurs choses : le nombre de ROPs, leur fréquence d'horloge exprimée en MHz/GHz, la bande passante mémoire, et bien d'autres. En théorie, la bande passante mémoire n'est pas un point limitant, les concepteurs du GPU prévoient une mémoire suffisamment rapide pour qu'elle puisse encaisser le ''pixel fillrate'' maximal, tout en ayant encore de la marge pour lire des textures et la géométrie. En clair, le ''pixel fillrate'' est surtout dépendant des ROPs, de leur nombre, de leur vitesse, de leur implémentation. Le ''pixel fillrate'' du GPU est difficile à calculer, mais l'approximation la plus utilisée est la suivante. Elle part du principe qu'un ROP peut écrire un pixel par cycle d'horloge. Ce n'est pas forcément le cas, tout dépend de l'implémentation des ROPs. Certains GPU performants ont des ROPs capables d'écrire des blocs de 8*8 pixels d'un seul coup en mémoire vidéo, alors que d'anciens GPU font avec des ROPs limités, seulement capables d'écrire un pixel tout les 10 cycles d'horloge. Toujours est-il qu'avec cette hypothèse, le ''pixel fillrate'' est égal au nombre de ROPs, multiplié par leur fréquence d'horloge. Je précise "leur" fréquence d'horloge, car il est possible de faire fonctionner l'unité de T&L, les ROPs, les unités de texture et le rastériseur à des fréquences différentes. C'est parfaitement possible, le cout en performance est parfois assez faible, mais le gain en consommation d'énergie est souvent important. Et justement, il a existé des GPU sur lesquels les ROPs avaient une fréquence inférieure à celle du reste du GPU. Dans ce cas, c'est la fréquence des ROPs qui est importante. Mais rassurez-vous : sur la majorité des GPUs actuels, les ROPs vont à la même fréquence que le reste du GPU. ===Le ''texture fillrate'' : la performance des unités de texture=== Le '''''texture fillrate''''' est l'équivalent du ''pixel fillrate'', mais pour les textures. Pour rappel, une texture est avant tout une image, composée de pixels. Pour éviter toute confusion, ces pixels de textures sont appelés ''des texels''. Le ''texture fillrate'' est le nombre de texels que la carte graphique peut plaquer par seconde, dans le meilleur des cas. Il est mesuré en mégatexels par secondes, voire en gigatexels par secondes. L'interprétation de ce chiffre dépend de si on le mesure en entrée ou en sortie des unités de texture. En effet, les unités de texture intègrent des fonctionnalités de filtrage de texture, qui lissent les textures. Ces techniques lisent plusieurs texels et les mélangent pour fournir le texel final, celui envoyé aux unités de ''shader'' ou aux ROPs. La coutume est de le mesurer en sortie des unités de texture. Le nombre en entrée dépend grandement de la bande passante mémoire et du filtrage de texture utilisé, pas celui en sortie. Le ''texture fillrate'' en sortie est le nombre maximal d'opérations de placage de texture par seconde. Là encore, on peut l'estimer en multipliant le nombre d'unités de texture par leur fréquence. Il s'agit évidemment d'une approximation assez peu fiable, car les unités de texture peuvent mettre plusieurs cycles pour plaquer une texture, les filtrer, etc. Le ''texture fillrate'' est bien plus important que le ''pixel fillrate'', surtout pour les GPU modernes. Un point important est que le ''texture fillrate'' a longtemps été égal au ''pixel fillrate''. C'était le cas avant la Geforce 2 de NVIDIA. Les cartes graphiques avaient autant d'unités de texture que de ROP, et les deux fonctionnaient à la même fréquence. Les deux ont commencés à diverger quand le multi-texturing est arrivé, avec la Geforce 2, justement. Le nombre d'unités de texture a doublé comparé aux ROPs, ce qui fait que le ''texture fillrate'' est rapidement devenu le double du ''pixel fillrate''. Sur les GPU modernes, le ''texture fillrate'' est le triple, quadruple, voire octuple du ''pixel fillrate''. ===La performance de l'unité géométrique=== Pour l'unité géométrique, l'équivalent au ''fillrate'' est le '''''polygon throughput'''''. C'est nombre de sommets que l'unité géométrique peut traiter par seconde, exprimé en ''méga-sommets par secondes'', en millions de sommets par seconde. Il dépend de la fréquence et du nombre d'unités géométriques, mais n'est pas exactement le produit des deux. Il varie beaucoup d'une carte graphique à l'autre, mais une approximation souvent utilisée prend le quart du produit fréquence * nombre d'unités géométriques. Il faut noter que cette mesure de performance a survécu à l'arrivée des shaders. Les GPU anciens, avant DirectX 10, avaient des processeurs séparés pour les ''vertex shaders'' et les ''pixel shaders''. Mais les calculs géométriques restaient séparés des autres calculs, ils avaient des unités géométriques dédiées. Quand les processeurs de shaders dit unifiés sont arrivés, la séparation entre géométrie et autres calculs a cédé et cet indicateur a simplement disparu. ===Les autres circuits=== Pour les autres circuits, il n'y a malheureusement pas d'indicateur de performance clair et net comme peut l'être le ''fillrate''. La raison à cela se comprend assez bien quand on regarde comment se calcule le ''fillrate''. C'est juste le produit de la fréquence et d'un nombre d'unités, en l’occurrence des unités de texture ou des ROPs. Le produit signifie que ces unités travaillent en parallèle et qu'elles peuvent chacune traiter un pixel/texel indépendamment des autres. Par contre, sur les anciens GPUs de l'époque, le rastériseur et l'unité géométrique sont un seul et unique circuit. Le nombre d'unité est donc égal à 1, et il ne nous reste plus que la fréquence. {{NavChapitre | book=Les cartes graphiques | prev=Le rendu d'une scène 3D : concepts de base | prevText=Le rendu d'une scène 3D : concepts de base | next=L'évolution vers la programmabilité : les GPUs | nextText=L'évolution vers la programmabilité : les GPUs }} {{autocat}} emigal23i8yan2xxmhfcazh2ymmafa0 763961 763960 2026-04-18T16:52:51Z Mewtow 31375 /* Les cartes graphiques en mode immédiat et à tuile */ 763961 wikitext text/x-wiki Dans le chapitre précédent, nous avons vu les bases du rendu 3D. Nous avons parlé de textures, de rastérisation, des calculs d'éclairage, et de bien d'autres choses. Vers la fin du chapitre, nous avons parlé des shaders, des programmes informatiques exécutés sur la carte graphique. Mais ils n'ont pas été toujours présents ! Les anciennes cartes graphiques faisaient sans shaders. Elles étaient autrefois appelées des '''cartes accélératrices 3D''', encore que la terminologie ne soit pas très précise.Nous les opposerons aux cartes graphiques capables d'exécuter des shaders, qui sont couramment appelées des '''Graphic Processing Units''', des GPUs. L'introduction des shaders a grandement modifié l'architecture des cartes graphiques. Il a fallu ajouter des processeurs pour exécuter les shaders, qui n'étaient pas là avant. Par contre, les circuits déjà présents ont été conservés, intégrés aux processeurs de shaders, ou remplacés par ceux-ci. D'un point de vue pédagogique, il est préférable de voir les cartes accélératrices 3D, avant de voir comment elles ont évolués vers des GPUs. Et nous allons voir cela dans deux chapitres. Ce chapitre portera sur les cartes accélératrices 3D, sans shaders, alors que le suivant expliquera comment s'est passée la transition vers les GPUs. : Nous allons nous concentrer sur les cartes graphiques à placage de texture inverse, le placage de texture direct ayant déjà été abordé dans le chapitre précédent. ==L'architecture d'une carte graphique 3D== Une carte accélératrice 3D est un carte d'affichage à laquelle on aurait rajouté des circuits de rendu 3D. Elle incorpore donc tous les circuits présents sur une carte d'affichage : un VDC, une interface avec le bus, une mémoire vidéo, des circuits d’interfaçage avec l'écran, un contrôleur DMA, etc. Le VDC s'occupe de l'affichage et éventuellement du rendu 2D, mais ne s'occupe pas du traitement de la 3D. Du moins, c'est le cas sur les cartes à placage de texture inverse. Le placage de texture direct utilise au contraire un VDC avec accélération 2D très performant, comme nous l'avons vu au chapitre précédent. Mais nous mettons ce cas particulier de côté. La carte accélératrice 3D reçoit des commandes graphiques, qui proviennent du pilote de la carte graphique, exécuté sur le processeur. les commandes en question sont très variées, avec des commandes de rendu 3D, de rendu 2D, de décodage/encodage vidéo, des transferts DMA, et bien d'autres. Mais nous allons nous concentrer sur les commandes de rendu 3D, qui demandent à la carte accélératrice 3D de faire une opération de rendu 3D. Pour cela, elles précisent quel tampon de sommet utiliser, quelles textures utiliser, quels shaders sont nécessaires, etc. La carte accélératrice 3D traite ces commandes grâce à deux circuits : des circuits de rendu 3D, et un chef d'orchestre qui dirige ces circuits de rendu pour qu'ils exécutent la commande demandée. Le chef d'orchestre s'appelle le '''processeur de commandes''', et il sera vu en détail dans quelques chapitres. Pour le moment, nous allons juste dire qu'il s'occupe de la logistique, de la répartition du travail. Pour les commandes de rendu 3D, il commande les différentes étapes du pipeline graphique et s'assure que les étapes s’exécutent dans le bon ordre. [[File:Architecture globale d'une carte 3D.png|centre|vignette|upright=2|Architecture globale d'une carte 3D]] Les circuits de rendu 3D regroupent des circuits hétérogènes, aux fonctions fort différentes. Dans le cas le plus simple, il y a un circuit pour chaque étape du pipeline graphique. De tels circuits sont appelés des '''unités de traitement graphique'''. On trouve ainsi une unité pour le placage de textures, une unité de traitement de la géométrie, une unité de rasterization, une unité d'enregistrement des pixels en mémoire appelée ROP, etc. Les anciennes cartes graphiques fonctionnaient ainsi, mais on verra que les cartes graphiques modernes font un petit peu différemment. Pour simplifier les explications, nous allons séparer la carte graphique en deux gros circuits bien distincts. En réalité, ils sont souvent séparés en sous-circuits plus petits, mais laissons cela de côté pour le moment. * Les '''unités géométriques''' pour les calculs géométriques ; * Les '''pipelines de pixel''' qui rastérisent l'image, plaquent les textures, et autres. Les unités géométriques manipulent des triangles, sommets ou polygones, donc des données géométriques. Les unités de pixel font tout le reste, mais le gros de leur travail est de manipuler des pixels ou des texels. Dans ce chapitre, on considère que les deux sont des circuits fixes, nous verrons leur évolution vers des processeurs programmables dans le prochain chapitre. ===Les circuits de traitement des pixels=== Parlons un peu plus en détail des pipelines de pixels. Pour mieux comprendre ce qu'elles font, il est intéressant de regarder ce qu'il y a dans un pipeline de pixel. Un pipeline de pixel effectue plusieurs opérations les unes à la suite, dans un ordre bien précis. Et cela explique l'usage du terme "pipeline" pour les désigner. Et ces opérations sont souvent réalisées par des circuits séparés, qui sont : * Un '''rastériseur''' qui fait le lien entre triangles et pixels ; * Une '''unité de texture''' qui lit les textures et les plaque sur les modèles 3D ; * Un '''ROP''' (''Raster Operation Pipeline''), qui gère grossièrement le tampon de profondeur (''z-buffer''). Le circuit de '''rastérisation''' prend en charge la rastérisation proprement dite. Pour rappel, la rastérisation projette une scène 3D sur l'écran. Elle fait passer d'une scène 3D à un écran en 2D avec des pixels. Lors de la rastérisation, chaque sommet est associé à un ou plusieurs pixels, à savoir les pixels qu'il occupe à l'écran. Elle fournit aussi diverses informations utiles pour la suite du pipeline graphique : la profondeur du sommet associé au pixel, les coordonnées de textures qui permettent de colorier le pixel. L'étape de '''placage de texture''' lit la texture associée au modèle 3D et identifie le texel adéquat avec les coordonnées textures, pour colorier le pixel. On travaille pixel par pixel, on récupère le texel associé à chaque pixel. Soit l'inverse du placage de texture direct, qui traversait une texture texel par texel, pour recopier le texel dans le pixel adéquat. Après l'étape de placage de textures, la carte graphique enregistre le résultat en mémoire. Lors de cette étape, divers traitements de '''post-traitement''' sont effectués et divers effets peuvent être ajoutés à l'image. Un effet de brouillard peut être ajouté, des tests de profondeur sont effectués pour éliminer certains pixels cachés, l'antialiasing est ajouté, on gère les effets de transparence, etc. Un chapitre entier sera dédié à ces opérations. [[File:Unité post-géométrie d'une carte graphique sans elimination des surfaces cachées.png|centre|vignette|upright=1.5|Unité post-1.5éométrie d'une carte graphique sans elimination des surfaces cachées]] ===Les circuits d'élimination des pixels cachés=== L'élimination des surfaces cachées élimine les triangles invisibles à l'écran, car cachés par un objet opaque. En théorie, elle est prise en charge à la toute fin du pipeline, dans les ROPs, car cela permet de gérer la transparence. En effet, on ne sait pas si une texture transparente sera plaquée sur le triangle ou non. En clair, on doit éliminer les triangles invisibles après le placage de textures, et donc dans les ROP. Les ROPs se chargent à la fois de l’élimination des pixels cachées et de la transparence, les deux s’influençant l'un l'autre. [[File:Unité post-géométrie d'une carte graphique avec elimination des surfaces cachées dans les ROPs.png|centre|vignette|upright=2|Unité post-géométrie d'une carte graphique avec élimination des surfaces cachées dans les ROPs]] Il y a cependant des cas où on sait d'avance que les textures ne sont pas transparentes. Dans ce cas, la carte graphique utilise les circuits d'élimination des pixels cachés juste après la rastérisation. Cela permet d'éliminer à l'avance les triangles dont on sait qu'ils ne seront pas rendus. [[File:Unité post-géométrie d'une carte graphique.png|centre|vignette|upright=2|Unité post-géométrie d'une carte graphique]] Les deux possibilités coexistent sur les cartes graphiques modernes. Une carte graphique moderne peut éliminer les surfaces cachées avant et après la rastérisation, grâce à des techniques d''''''early-z''''' dont nous parlerons plus tard, dans un chapitre dédié sur la rastérisation. ===Les circuits d'éclairage=== Les explications précédentes décrivent une carte graphique qui ne gère pas les techniques d'éclairage, et nous allons remédier à cela immédiatement. L'éclairage a été pris en charge avant même l'arrivée des shaders, dès les années 2000. Par contre, les cartes accélératrices pour PC géraient uniquement l'éclairage par sommet. Elles utilisaient un circuit non-programmable, appelé le '''circuit de ''Transform & Lightning''''', qui effectue les calculs d'éclairage par sommet (le L de T&L), en plus des calculs de transformation (le T de T&L). La première carte graphique à avoir intégré un circuit de T&L était la Geforce 256, la Geforce 1. L'unité de T&L a rapidement été remplacée par les ''vertex shader'', dont nous reparlerons d'ici quelques chapitres. Dès la Geforce 3, ce remplacement été effectué. L'unité de T&L calcule une couleur RGB pour chaque sommet/triangle, appelée la '''couleur de sommet'''. Une fois calculée par l'unité de T&L, la couleur de sommet est envoyée à l'unité de rastérisation. L'unité de rastérisation calcule la couleur des pixels à partir des trois couleurs de sommet. Pour cela, il y a deux méthodes principales, qui correspondent à l'éclairage plat et l'éclairage de Gouraud, qu'on a vu dans le chapitre précédent. Les cartes accélératrices utilisaient généralement l'éclairage de Gouraud. L'éclairage de Gouraud effectue une interpolation, à savoir une sorte de moyenne pondérée de la couleur des trois sommets. L'éclairage de Gouraud demande donc d'ajouter un circuit d'interpolation pour les couleurs des sommets. Il fait normalement partie du circuit de rastérisation, comme on le verra dans le chapitre dédié sur la rastérisation. Pour donner un exemple, la console de jeu Playstation 1 gérait l'éclairage de Gouraud directement en matériel, mais seulement partiellement. Elle n'avait pas de circuit de T&L, ni de ''vertex shaders'', mais intégrait une unité de rastérisation qui interpolait les couleurs de chaque sommet. Enfin, il faut prendre en compte les textures. Pour cela, le pixel texturé est multiplié par la luminosité/couleur calculée par l'unité géométrique. Il y a donc un '''circuit de combinaison''' situé après l'unité de texture qui effectue la combinaison/multiplication. Le circuit de combinaison est parfois configurable, à savoir qu'on peut remplacer la multiplication par une addition ou d'autres opérations. Un tel circuit de combinaison s'appelle alors un '''''combiner''''', dans la vieille nomenclature graphique de l'époque des années 90-2000. [[File:Implémentation de l'éclairage par sommet avec des combiners.png|centre|vignette|upright=2|Implémentation de l'éclairage par sommet avec des combiners]] Il a existé quelques rares cartes graphiques capables de faire de l'éclairage par pixel en matériel. Un exemple de carte graphique capable de faire cela est celle de la Nintendo DS, la PICA200. Créée par une startup japonaise, elle incorporait un circuit de T&L, un éclairage de Phong, du ''cel shading'', des techniques de ''normal-mapping'', de ''Shadow Mapping'', de ''light-mapping'', du ''cubemapping'', de nombreux effets de post-traitement (bloom, effet de flou cinétique, ''motion blur'', rendu HDR, et autres). Pour l'éclairage de Phong, il faut ajouter une unité qui fasse les calculs d'éclairage par pixel, et renvoie son résultat. La couleur de pixel calculée est ensuite combinée avec une texture, avec un ''combiner''. Du moins, si la carte accélératrice supporte les textures... Il faut aussi que le rastériseur interpole les normales, et non des couleurs de sommets comme avec l'éclairage de Gouraud. Les normales sont fournies par l'unité de T&L, ce qui demande une modification assez importante des unités de T&L et du rastériseur. [[File:Implémentation de l'éclairage par pixel avec des combiners.png|centre|vignette|upright=2|Implémentation de l'éclairage par pixel avec des combiners]] Voyons maintenant le ''bump-mapping'' et le ''normal-mapping''. Pour rappel, les deux dernières mémorisent des informations d'éclairage dans une texture en mémoire vidéo. La texture contient des informations de relief pour le ''bump-mapping'', des normales précalculées pour le ''normal-mapping''. Pour cela, l'unité d'éclairage par pixel doit être reliée à l'unité de texture, mais l'implémentation matérielle n'est pas aisée. [[File:Normal mapping matériel.png|centre|vignette|upright=2|Normal mapping matériel]] ==Les cartes graphiques avec plusieurs unités parallèles== Plus haut, nous avons décrit une carte graphique basique, très basique, avec seulement quatre unités. Une unité pour les calculs géométriques, un rastériseur, une unité pour les pixels/textures et un ROP. Cependant, les cartes graphiques ayant cette architecture sont très rares, pour ne pas dire inexistantes. Il n'est pas impossible que les toutes premières cartes graphiques aient suivi à la lettre cette architecture, mais même cela n'est pas sur. La raison : toutes les cartes graphiques dupliquent les circuits précédents pour gagner en performance, mais aussi pour s'adapter aux contraintes du rendu 3D. ===L'amplification des pixels et son impact sur les cartes graphiques=== Un triangle prend une certaine place à l'écran, il recouvre un ou plusieurs pixels lors de l'étape de rastérisation. Le nombre de pixels recouvert dépend fortement du triangle, de sa position, de sa profondeur, etc. Un triangle peut donner quelques pixels lors de l'étape de rastérisation, alors qu'un autre va couvrir 10 fois de pixels, un autre seulement trois fois plus, un autre seulement un pixel, etc. Le cas où un triangle ne recouvre qu'un seul pixel est rare, encore que la tendance commence à changer avec les jeux vidéos récents de la décennie 2020 utilisant l'Unreal Engine et la technologie Nanite. La conséquence est qu'il y a plus de travail à faire sur les pixels que sur les sommets, ce qui a reçu le nom d''''amplification des pixels'''. La conséquence est qu'une unité géométrique prendra un triangle en entrée, l'enverra au rastériseur, qui fournira en sortie un ou plusieurs pixels à éclairer/texturer. Et cette règle un triangle = 1,N pixels fait qu'il y a un déséquilibre entre les calculs géométriques et ce qui suit, que ce soit le placage de textures, l'éclairage par pixel ou l'enregistrement des pixels dans le ''framebuffer''. Et ce déséquilibre a un impact sur la manière dont un conçoit une carte graphique, ancienne comme moderne. S'il y a une seule unité de texture/pixels, alors le rastériseur envoie chaque pixel à texturer/éclairé un par un à l'unité de pixel. Le rastériseur produits ces pixels un par un, avec un algorithme adapté pour. L'unité géométrique attendra le temps que la rastérisation ait fini de traiter tous les pixels du triangle précédent. Elle calculera le prochain triangle pendant ce temps, mais cela ne fera que limiter la casse si beaucoup de pixels sont générés. Mais il est possible de profiter de l'amplification des pixels pour gagner en performances. L'idée est que le rastériseur produit plusieurs pixels en même temps, qui sont envoyés à plusieurs unités de texture et d'éclairage par pixel. Un exemple est illustré ci-dessous, avec une seule unité géométrique, mais quatre unités de texture, quatre unités d'éclairage par pixel, et quatre ROPs. Le rastériseur est conçu pour générer quatre pixels d'un seul coup si nécessaire. [[File:Architecture d'un GPU tenant compte de l'amplification des pixels.png|centre|vignette|upright=2.5|Architecture d'un GPU tenant compte de l'amplification des pixels]] La carte graphique précédente a des performances optimales quand un triangle recouvre 4 pixels : tout est fait en une seule passe. Si un triangle ne recouvre que 1, 2 ou 3 pixels, alors le rastériseur produira 1, 2 ou 3 et certaines unités suivant le rastériseur seront inutilisées. Mais si un triangle recouvre plus de 4 pixels, alors les pixels sont générés, texturés, éclairés et enregistrés en RAM par paquets de 4. En clair, la carte graphique peut s'adapter à l'amplification des pixels, mais pas parfaitement. Les GPU récents ont résolu partiellement ce problème avec un système de ''shaders'' unifiés, mais qu'on ne peut pas expliquer pour le moment. Pour donner un exemple du monde réel, les premières cartes graphique de l'entreprise SGI était de ce type. SGI a été une entreprise pinière dans le domaine du rendu en 3D, qui a opéré dans les années 80-90, avant de progressivement décliner et fermer. Elle a conçu de nombreux systèmes de type ''workstation'', donc destinés aux professionnels, avec des cartes graphiques dédiées. le grand public n'avait pas accès à ce genre de matériel, qui était très cher, vu qu'on n'était qu'au tout début de l'informatique. Nous ne détaillerons pas ces systèmes, car ils géraient leur mémoire vidéo d'une manière assez bizarre : elle était éclatée en plusieurs morceaux fusionnés chacun avec un ROP... Mais ils avaient tous une unité géométrique unique reliée à un rastériseur, qui alimentait plusieurs unités de texture/pixel et ROPs. Plus proche de nous, certaines cartes graphiques pour PC étaient aussi dans ce cas. Les toutes premières cartes graphiques pour PC n'avaient même pas de circuits géométriques, et se contentaient d'un rastériseur, d'unités de texture et de ROPs. Par la suite, la Geforce 256 a introduit une unité géométrique appelée l'unité de T&L. Les cartes graphiques de l'époque ont suivi le mouvement et ont aussi intégrée une unité géométrique presque identique. La Geforce 256 avait une unité géométrique, mais 4 unités de texture, 4 unités d'éclairage par pixel et 4 ROPs. ===Le multitexturing : dupliquer les unités de texture=== Le '''''multi-texturing''''' est une technique très importante pour le rendu 3D moderne. L'idée est de permettre à plusieurs textures de se superposer sur un objet. Divers effets graphiques demandent d'ajouter des textures par-dessus d'autres textures, pour ajouter des détails, du relief, sur une surface pré-existante. Un exemple intéressant vient des jeux de tir : ajouter des impacts de balles sur les murs. Pour cela, on plaque une texture d'impact de balle sur le mur, à la position du tir. Il s'agit là d'un exemple de ''decals'', des petites textures ajoutées sur les murs ou le sol, afin de simuler de la poussière, des impacts de balle, des craquelures, des fissures, des trous, etc. Le ''multi-texturing'' implique que calculer un pixel implique de lire plusieurs textures. En général, un pixel avec ''multi-texturing'' demande de lire deux textures, rarement plus. La carte graphique doit alors être capable d'accéder à deux textures en même temps, ou du moins faire semblant que. De plus, elle doit combiner les deux textures pour générer le pixel voulu, ce qui demande d'ajouter un circuit qui combine deux texels (des pixels de texture) pour donner un pixel. La solution la plus simple est de doubler les unités de texture et de combiner les textures dans l'unité d'éclairage par pixel. Résultat : pour une unité d'éclairage par pixel, on a deux unités de textures. La Geforce 2 et 3 utilisaient cette solution, dont le seul défaut est que la seconde unité de texture était utilisée seulement pour les objets sur lesquels le ''multi-texturing'' était utilisé. Les cartes ATI, le concurrent de l'époque de NVIDIA, aujourd'hui racheté par AMD, triplait les unités de texture. Mais cette possibilité était peu utilisée, la majorité des jeux se dépassant pas deux texture max par pixel. C'est sans doute pour cette raison que ce triplement a été abandonné à la génération suivante, les Radeon 9000 et 8500 se contentant de doubler les unités de texture. {|class="wikitable" |- ! Nom de la carte graphique !! Unités géométriques !! Unité de texture !! Unités de pixel !! ROPs |- ! Geforce 2 d'entrée de gamme | 1 || 2 || 4 || 2 |- ! Geforce 2 milieu/haut de gamme, Geforce 3 | 1 || 4 || 8 || 4 |- ! Radeon R100 bas de gamme | 1 || 1 || 3 || 1 |- ! Radeon R100 autres | 1 || 2 || 6 || 2 |} ===L'usage de plusieurs unités géométriques=== Pour encore augmenter les performances, il est possible d'utiliser plusieurs circuits de calcul géométriques, plusieurs unités géométriques. Et ce peu importe que ces unités soient des processeurs ou des circuits fixes non-programmables. Et pour cela, il existe deux grandes implémentations : utiliser plusieurs processeurs placés en série, ou les mettre en parallèle. Comprendre la première implémentation demande de faire quelques rappels sur les calculs géométriques. ====L'usage d'un pipeline géométrique proprement dit==== Pour rappel, le pipeline géométrique regroupe les quatre étapes suivantes : * L'étape de '''chargement des sommets/triangles''', qui sont lus depuis la mémoire vidéo et injectés dans le pipeline graphique. * L'étape de '''transformation''' effectue deux changements de coordonnées pour chaque sommet. ** Premièrement, elle place les objets au bon endroit dans la scène 3D, ce qui demande de mettre à jour les coordonnées de chaque sommet de chaque modèle. C'est la première étape de calcul : l'''étape de transformation des modèles 3D''. ** Deuxièmement, elle effectue un changement de coordonnées pour centrer l'univers sur la caméra, dans la direction du regard. C'est l'étape de ''transformation de la caméra''. * La phase d''''éclairage''' (en anglais ''lighting'') attribue une couleur à chaque sommet, qui définit son niveau de luminosité : est-ce que le sommet est fortement éclairé ou est-il dans l'ombre ? * La phase d''''assemblage des primitives''' regroupe les sommets en triangles. * Les phases de '''''clipping''''' ou le '''''culling''''' agissent sur des sommets/triangles/primitives, même si elles sont souvent regroupées dans l'étape de rastérisation. Si on met de côté le chargement des sommets/triangles, il est possible de faire tous ces calculs en bloc, dans un seul processeur ou une seule unité de T&L. Mais une autre idée, plus simple, attribue un processeur/circuit pour chaque étape. En faisant cela, on peut traiter plusieurs triangles/sommets en même temps, chacun étant dans une étape différente, chacun dans un processeur/circuit. Ceux qui auront déjà lu un cours d'architecture des ordinateurs reconnaitront la fameuse technique du pipeline, mais appliquée ici à un algorithme plus conséquent. Les processeurs sont en série, et chaque processeur reçoit les résultats du processeur précédent, et envoie son résultat au processeur suivant. Sauf en début ou en bout de chaine, évidemment. Pour donner un exemple, les premières cartes graphiques de SGI utilisaient 10/12 processeurs enchainés l'un à la suite de l'autre. Les 4 premiers géraient les étapes de transformation, les 6 suivants faisaient les opérations de clipping/culling, les deux derniers faisaient la rastérisation proprement dite. Pour lisser les transferts de données, il est possible d'ajouter des mémoires FIFOs entre les processeurs. Comme ça, si un processeur est bloqué par un calcul un peu trop long, cela ne bloque pas les processeurs précédents. A la place, le processeur précédent accumule des résultats dans la mémoire FIFOs, qui seront consommé ultérieurement. En théorie, on peut s'attendre à ce que la performance soit multipliée par le nombre de processeurs. En réalité, les étapes sont rarement équilibrées, certaines étapes prennent beaucoup plus de temps que les autres, ce qui fait que la répartition des calculs n'est pas idéale : certains processeurs attendent que le processeur suivant ait finit son travail. De plus, l'organisation en pipeline entraine des couts de transmission/communication entre étapes, notamment si on utilise des mémoires FIFOs entre processeurs, ce qui est toujours le cas. Cette implémentation n'a été utilisée que sur les toutes premières cartes graphiques, avant l'apparition des PC grand public. Les systèmes SGI, utilisés pour des stations de travail, utilisaient cette architecture, par exemple. Mais elle est totalement abandonnée depuis les années 90. ====L'usage de plusieurs unités géométriques en parallèle==== La seconde solution utilise plusieurs unités géométriques en parallèle. Chaque unité géométrique traite un triangle/sommet de bout en bout, en faisant transformation, éclairage, etc. Mais vu qu'il y en a plusieurs, on peut traiter plusieurs triangles/sommets : un dans chaque unité géométrique. C'est la solution retenue sur toutes les cartes graphiques depuis les années 90. Mais la présence de plusieurs unités géométriques a deux conséquences : il faut alimenter plusieurs unités géométriques en triangles/sommets, il faut gérer l'envoi des triangles au rastériseur. Les deux demandent des solutions distinctes. La répartition du travail sur les unités géométriques est déléguée au processeur de commandes. Il utilise les unités géométriques à tour de rôle : on envoie le premier triangle à la première unité, le second triangle à la seconde unité, le troisième triangle à la troisième, etc. Il s'agit de ce que l'on appelle l''''algorithme du tourniquet''', qui est assez efficace malgré sa simplicité. Il marche assez bien quand tous les triangles/sommets mettent approximativement le même temps pour être traités. Si le temps de calcul varie beaucoup d'un triangle/sommet à l'autre, une solution toute simple détecte quels sont les processeurs de shaders libres et ceux occupés. Il suffit alors d'appliquer l'algorithme du tourniquet seulement sur les processeurs de shaders libres, qui n'ont rien à faire. Un autre problème survient cette fois-ci en sortie des unités géométriques. Comment connecter plusieurs unités géométriques au reste de la carte graphique ? Évidemment, la carte graphique contient plusieurs unités de texture/pixel et plusieurs ROPs. Elle tient compte de l'amplification des pixels, ce qui fait qu'il y a moins d'unités géométriques que d'autres circuits, entre 2 à 8 fois moins environ. Pour créer une carte graphique avec plusieurs unités géométriques, il y a plusieurs solutions, que nous allons détailler dans ce qui suit. Pour les explications, nous allons prendre l'exemple de cartes graphiques avec 2 unités géométriques et 8 unités de texture/pixel, et autant de ROPs. La première solution serait simplement de dupliquer les circuits précédents, en gardant leurs interconnexions. Pour l'exemple, on aurait 2 unités géométriques, chacune connectée à 4 unités de textures/pixels. L'unité géométrique est suivie par un rastériseur qui alimente 4 unités de texture/pixel, comme c'était le cas dans la section précédente. L'implémentation est alors très simple : on a juste à dupliquer les circuits et à modifier le processeur de commande. Il faut aussi modifier les connexions des ROPs à la mémoire vidéo. Mais les interconnexions avec le rastériseur ne sont pas modifiées. Un désavantage est que l'amplification des pixels n'est pas gérée au mieux. Imaginez que l'on ait deux triangles à rastériser, qui génèrent 8 pixels en tout : un qui génère 6 pixels à la rastérisation, l'autre seulement 2. Il n'est pas possible de traiter les 8 pixels générés. Le triangle générant deux pixels va alimenter deux unités de texture/pixels et en laisser deux inutilisées, l'autre triangle sera traité en deux fois (4 pixels, puis 2). La duplication bête et méchante n'utilise donc pas à la perfection les unités de texture/pixel. Une autre solution permet de gérer à la perfection l'amplification des pixels. Elle consiste à utiliser un seul rastériseur à haute performance, sur lequel on connecte les unités géométriques et les unités de texture/pixel. L'idée est que le rastériseur peut recevoir N triangles à la fois et alimenter M unités de texture/pixels. Le rastériseur unique s'occupe de faire plusieurs rastérisations de triangles à la fois, et répartit automatiquement les pixels générés sur les unités de texture/pixel. Pour donner un exemple, le GPU Geforce 6800 de NVIDIA avait 6 unités géométriques, 16 unités faisant à la fois placage de textures et éclairage par pixel, et 16 ROPs. Un point important avec ce GPU est qu'il n'avait qu'un seul rastériseur, détail sur lequel on reviendra dans ce qui suit ! [[File:GeForce 6800.png|centre|vignette|upright=2.5|GeForce 6800, les unités géométriques sont ici appelées les ''vertex processor'', les unités de texture/pixel sont les ''fragment processors'', les ROPs sont les ''pixel blending units''.]] ==Les cartes graphiques en mode immédiat et à tuile== Il est courant de dire qu'il existe deux types de cartes graphiques : celles en mode immédiat, et celles avec un rendu en tuiles (''tiles''). Il s'agit là des deux types principaux de cartes graphiques à l'heure actuelle, mais quelques architectures faisaient autrement dans le passé. Une autre classification, plus générale, sépare les cartes graphiques en cartes graphiques ''sort-last'', ''sort-first'' et ''sort-middle''. Les cartes graphiques en mode immédiat correspondent aux cartes graphiques en mode immédiat, alors que le rendu à tuile est une sous-catégorie des cartes graphiques ''sort-middle''. La différence entre les deux est liée à la manière dont les pixels/primitives sont réparties sur l'écran. Leur existence est liée au fait que les API graphiques imposent que les triangles envoyées à la carte graphique soient traités dans l'ordre. Le tampon de sommets contient en effet une liste de sommets/triangles, qui sont censés être traités dans l'ordre d'arrivée. Et si je dis censé être, c'est parce que la carte graphique ne va pas forcément traiter les triangles/pixels dans l'ordre. A la place, elle va traiter des triangles/pixels en parallèle, et il n'est pas garantit que les résultats sortent des circuits dans l'ordre d'arrivée. Après tout, certains triangles sont traités plus rapidement que d'autres, idem pour les pixels. La carte graphique doit donc remettre les résultats dans l'ordre. L'endroit du pipeline où se fait cette remise en ordre est ce qui fait la différence entre cartes graphioques ''sort last'' et ''sort middle''. ===Les trois types de cartes graphiques : ''sort-first'', ''sort-middle'' et ''sort-last''=== Les cartes graphiques ''sort-first'' ont plusieurs pipelines séparés, chacun traitant une partie de l'écran. Ils déterminent la position des triangles à l'écran, puis répartissent les triangles dans les pipelines adéquats. Par exemple, on peut imaginer un GPU ''sort-first'' avec quatre unités séparées, chacune traitant un quart de l'écran. Au tout début du rendu, une unité de répartition détermine la position d'un triangle à l'écran, et l'envoie à l'unité adéquate. Si le triangle est dans le coin inférieur gauche, il sera envoyé à l'unité dédiée à ce coin. S'il est situé au milieu de l'écran, il sera envoyé aux quatre unités, chacune ne traitant les pixels que pour son coin à elle. Les cartes graphiques ''sort-middle'' découpent l'écran en carrés de 4, 8, 16, 32 pixels de côté , qui sont rendus séparément les uns des autres. Les morceaux d'image en question sont appelés des ''tiles'' en anglais, mot que nous avons décidé de ne pas traduire pour ne pas le confondre avec les tuiles du rendu 2D. Il y a une assignation stricte entre une unité de pixel/texture et une ''tile''. Par exemple, sur un système avec deux unités de texture/pixel, la première unité traitera les ''tiles'' paires, l'autre unité les ''tiles'' impaires. Les cartes graphiques ''sort-last'' sont l'extrême inverse. Ils ont des unités banalisées qui se moquent de l'endroit où se trouve un pixel à l'écran. Leurs unités géométriques traitent des polygones sans se préoccuper de leur place à l'écran. Le rastériseur envoie les pixels aux unités de textures/ROPs sans se soucier de leur place à l'écran. Encore que quelques optimisations s'en mêlent pour profiter au mieux des caches de texture et des caches intégrés aux ROPs, mais l'essentiel est qu'il n'y a pas de répartition fixe. Il n'y a pas de logique du type : ce pixel ou ce triangle est à tel endroit à l'écran, on l'envoie vers telle unité de texture/ROP. Ce sont les ROPs qui se chargent d'enregistrer les pixles finaux au bon endroit dans le ''framebuffer''. La gestion de la place des pixels à l'écran se fait donc à la toute fin du pipeline, d'où le nom de ''sort-last''. Pour résumer, les trois types de cartes graphiques se distinguent suivant l'endroit où les triangles/pixels sont répartis suivant leur place à l'écran. Avec le ''sort-first'', ce sont les triangles qui sont triés suivant leur place à l'écran. Le tri a donc lieu avant les unités géométriques. Avec le ''sort-middle'', ce sont les fragments générés par la rastérisation qui sont triés suivant leur place à l'écran, d'où l'existence de ''tiles''. Le tri a lieu entre les unités géométriques et le rastériseur. Les unités géométriques se moquent de la place à l'écran des primitives qu'ils traitent, mais pas les rastériseurs et les unités de texture. Enfin, avec le ''sort-last'', ce sont les pixels finaux qui sont triés selon leur place à l'écran, seuls les ROPs se préoccupent de cette place à l'écran. Concrètement, les cartes graphiques de type ''sort-first'' sont très rares, l'auteur de ce cours n'en connait aucun exemple. Les deux autres types de cartes graphiques sont eux beaucoup plus communs. Reste à voir ce qu'il y a à l'intérieur d'une carte graphique ''sort-middle'' et/ou ''sort-last''. Pour simplifier les explications, nous allons regrouper les circuits de traitement des pixels dans un seul gros circuits appelé le rastériseur, par abus de langage. La carte graphique est donc composée de deux circuits : l'unité géométrique et le mal-nommé rastériseur. Les cartes graphiques ajoutent des mémoires caches pour la géométrie et les textures, afin de rendre leur accès plus rapide. [[File:Carte graphique, généralités.png|centre|vignette|upright=2|Carte graphique, généralités]] ===Les cartes graphiques ''sort-last'', en mode immédiat=== Les cartes graphiques en mode immédiat implémentent le pipeline graphique d'une manière assez évidente. L'unité géométrique envoie des triangles au rastériseur, qui lui-même envoie les pixels à l'unité de texture, qui elle-même envoie le pixel texturé au ROP. Elles effectuent le rendu 3D triangle par tringle, pixel par pixel. Un point important est que pendant que le pixel N est dans les ROP, les pixels N+1 est dans l'unité de texture, le pixel N+2 est dans le rastériseur et le triangle suivant est dans l'unité géométrique. En clair, on n'attend pas qu'un triangle soit affiché pour en démarrer un autre. Un problème est qu'un triangle dans une scène 3D correspond souvent à plusieurs pixels, ce qui fait que la rastérisation prend plus de temps de calcul que la géométrie. En conséquence, il arrive fréquemment que le rastériseur soit occupé, alors que l'unité de géométrie veut lui envoyer des données. Pour éviter tout problème, on insère une petite mémoire entre l'unité géométrique et le rastériseur, qui porte le nom de '''tampon de primitives'''. Elle permet d'accumuler les sommets calculés quand le rastériseur est occupé. [[File:Carte graphique en rendu immédiat.png|centre|vignette|upright=2|Carte graphique en rendu immédiat]] Le tout peut s'adapter à la présence de plusieurs unités géométriques, de plusieurs unités de texture ou processeurs de shaders, tant qu'on conserve un rastériseur unique. Il suffit alors d'adapter le tampon de primitive et le rastériseur. Si on veut rajouter des unités de texture ou des processeurs de pixel shaders, le tampon de primitives n'est pas concerné : il suffit que le rastériseur ait plusieurs sorties, une par unité de texture/pixel shader. Par contre, la présence de plusieurs unités géométriques impacte le tampon de primitive. Avec plusieurs unités géométriques, il y a deux solutions : soit on garde un tampon de primitive unique partagé, soit il y a un tampon de primitive par unité géométrique. Avec la première solution, toutes les unités géométriques sont reliées à un tampon de primitives unique. Le tampon de primitive est conçu pour qu'on puisse écrire plusieurs primitives dedans en même temps. Le rastériseur n'a pas à être modifié. Une autre solution utilise un tampon de primitive par unité géométrique. Le rastériseur peut alors piocher dans plusieurs tampons de primitive, ce qui demande de modifier le rastériseur. Il y a alors un système d'arbitrage, pour que le rastériseur pioche des primitives équitablement dans tous les tampons de primitive, pas question que l'un d'entre eux soit ignoré durant trop longtemps. ===Les cartes graphiques ''sort-middle'' des années 90=== Voyons maintenant les architectures ''sort-middle'' utilisée dans les années 80-90, à une époque où les cartes graphiques grand public n'existaient pas encore. Les cartes graphiques de l’entreprise SGI sont dans ce cas, mais aussi le Pixel Planes 5, et de nombreux autres systèmes graphiques. Elles utilisaient un rendu à ''tile'' assez original. Dans ce qui suit, nous allons décrire l'architecture des systèmes SGI, qui sont représentatifs. L'idée était que l'image était découpée en un nombre de ''tiles'' qui variait selon le système utilisé, mais qui était au minimum de 5 et pouvait aller jusqu'à 20. Et chaque ''tile'' avait sa propre unité de traitement, qui contenait un rastériseur, une unité de texture, un ROP, etc. En clair, la carte graphique contenait entre 5 et 20 unités de traitement séparées, chacune dédiée à une ''tile''. Les triangles sortant des unités géométriques étaient envoyés à toutes les unités de traitement, sans exception. Une fois le triangle réceptionné, l'unité de traitement déterminait si le triangle s'affichait dans la ''tile'' associée ou non. Si c'est le cas, le rastériseur rastérise le triangle, génère les pixels, les textures sont lues, puis le tout est enregistré en mémoire vidéo. Si ce n'est pas le cas, elle abandonne le polygone/triangle reçu. Si le triangle est partiellement dans la ''tile'', le rastériseur génère les pixels qui sont dans la ''tile'', par les autres. Précisons que les cartes de ce style incorporaient un tampon de primitive, ce qui permettait de simplifier la conception de la carte graphique. Sur la carte ''Infinite Reality'', le tampon de primitive faisait 4 méga-octets de RAM, ce qui permettait de mémoriser 65 536 sommets. Sur la carte ''Reality Engine'', il y avait même plusieurs tampons de primitives, un par unité géométrique. Les polygones sortaient des unités géométriques, étaient accumulés dans les tampons de primitives, puis étaient ''broadcastés'' à toutes les unités de traitement. Pour cela, le bus en bleu dans le schéma précédent est en réalité un réseau ''crossbar'' avec un système de ''broadcast''. Une caractéristique de ces architectures est qu'elles mettent le ''framebuffer'' à part de la mémoire vidéo. De plus, ce ''framebuffer'' est lui-même découpée en ''tile''. Sur la carte ''Reality Engine'', le ''framebuffer'' est découpé en 5 à 20 sous-''framebuffer'', un par ''tile''. Et chaque mini-''framebuffer'' est placé dans l'unité de traitement de la ''tile'' associée ! Ainsi, au lieu de connecter 5-20 ROPs à une mémoire vidéo unique, chaque ROP contient une '''''RAM tile''''', qui mémorise la ''tile'' en cours de traitement. Évidemment, cela pose quelques problèmes pour la connexion au VDC, en raison de l'absence de ''framebuffer'' unique, mais rien d'insurmontable. L'architecture est illustrée ci-dessous. : Le Pixel Planes 5 avait un système similaire, mais avait en plus un ''framebuffer'' complet, dans lequel les sous-''framebuffer'' étaient recopiés pour obtenir l'image finale. [[File:Architecture des premières cartes graphiques SGI.png|centre|vignette|upright=2|Architecture des premières cartes graphiques SGI]] Un autre détail de l'architecture est lié à la mémoire pour les textures. Les concepteurs de SGI ont décidé de séparer les textures dans une mémoire à part du reste de la mémoire vidéo. Il n'y a pour ainsi dire pas de mémoire vidéo proprement dit : la géométrie à rendre est dans une mémoire à part, idem pour les textures, et pour le ''framebuffer''. On s'attendrait à ce que la mémoire de texture soit reliée aux 5-20 unités de texture, mais les concepteurs ont décidé de faire autrement. A la place, chaque unité de texture contient une copie de la mémoire de texture, qui est donc dupliquée en 5-20 exemplaires ! Difficile de comprendre la raison de ce choix, mais cela simplifiait sans doute les interconnexions internes de la carte graphique, au prix d'un cout en RAM assez important. ===Les cartes graphiques à rendu à ''tile''=== Les cartes graphiques de SGI, vus précédemment, disposent d'une unité de traitement par ''tile''. Faire ainsi permet de nombreuses optimisations, comme éclater le ''framebuffer'' en plusieurs ''RAM tile''. Mais le cout en matériel est conséquent. Pour économiser des circuits, l'idéal serait d'utiliser moins d'unités de traitement pour les pixels/fragments/textures. Mais pour cela, il faut profondément modifier l'architecture précédente. On perd forcément le lien entre une unité de traitement et une ''tile''. Et cela impose de revoir totalement la manière dont les unités géométriques communiquent avec les unités de traitement. La solution retenue est celle des cartes graphiques à rendu en ''tile'' proprement dit, aussi appelés ''cartes graphiques TBR'' (''Tile Based Rendering''). Les plus simples n'utilisent qu'une seule unité de traitement et n'ont qu'une seule ''RAM tile''. En conséquence, les ''tiles'' sont rendues l'une après l'autre. Au lieu de rendre chaque triangle/polygone l'un après l'autre, la géométrie est intégralement rendue avant de faire la rastérisation. Les triangles sont enregistrés dans la mémoire vidéo et regroupés par ''tile'', avant la rastérisation. La mémoire vidéo contient donc plusieurs paquets de triangles, avec un paquet par ''tile''. Les paquets/''tiles'' sont envoyées au rastériseur un par un, la rastérisation se fait ''tile'' par ''tile''. La ''RAM tile'' existe toujours, même si son utilité est différente. La ''RAM tile'' accélère le rendu d'une ''tile'', car tout ce qui est nécessaire pour rendre une ''tile'' est mémorisé dedans : la ''tile'', le tampon de profondeur, le tampon de stencil et plein d'autres trucs. Pas besoin d’accéder à un gigantesque z-buffer pour toute l'image, juste d'un minuscule z-buffer pour la ''tile'' en cours de traitement, qui tient totalement dans la SRAM. : Il faut noter que les ''tiles'' sont généralement assez petites : 16 ou 32 pixels de côté, rarement plus. En comparaison, les ''tiles'' faisaient 128 pixels de côté pour les cartes de SGI. [[File:Carte graphique en rendu par tiles.png|centre|vignette|upright=2|Carte graphique en rendu par tiles]] Il est possible pour une carte graphique TBR de traiter plusieurs ''tiles'' en même temps, en parallèle, dans des unités séparées. Un exemple est celui du GPU ARM Mali 400, qui dispose d'une unité géométrique (un processeur de ''vertex''), mais 4 processeurs de pixels. Il peut donc traiter quatre ''tiles'' en même temps, chacune étant rendue dans un processeur de pixel dédié. Les 4 processeurs de pixels ont chacun leur propre ''RAM tile'' rien qu'à eux. La présence d'une ''RAM tile'' a de nombreux avantages et impacte grandement l'architecture de la carte graphique. En premier lieu, les ROPs sont drastiquement modifiés. De nombreux GPU TBR n'ont même pas de ROPs ! A la place, les ROPs sont émulés par les processeurs de pixel shader. Les ''pixel shaders'' peuvent lire ou écrire directement dans le ''framebuffer'', sur les GPU TBR, ce qui leur permet d'émuler les ROPs avec des instructions mathématique/mémoire. Le ''driver'' patche automatiquement les ''pixel shader'' pour ajouter de quoi émuler les ROPs à la fin des ''pixel shaders''. Cela garantit une économie de circuits non-négligeable. La présence d'une ''RAM tile'' fait que le tampon de profondeur disparait. Par contre, les cartes graphiques de type TBR doivent enregistrer les triangles en mémoire vidéo, et les trier par paquets. Cela compense partiellement, totalement, ou sur-compense, les économies liées à la ''RAM tile''. Le regroupement des triangles par ''tile'' s'accompagne de quelques optimisations assez sympathiques. Par exemple, les GPU TBR modernes peuvent trier les triangles selon leur profondeur, directement lors du regroupement en paquets. L'avantage est que cela permet à l'élimination des pixels cachés de fonctionner au mieux. L'élimination des pixels cachés fonctionne à la perfection quand les triangles sont triés du plus proche au plus lointain, pour les objets opaques. Les cartes graphiques en mode immédiat ne peuvent pas faire ce tri, mais les cartes graphiques TBR peuvent le faire, soit totalement, soit partiellement. Un autre avantage est que l’antialiasing est plus rapide. Pour ceux qui ne le savent pas, l'antialiasing est une technique qui améliore la qualité d’image, en simulant une résolution supérieure. Une image rendue avec antialiasing aura la même résolution que l'écran, mais n'aura pas certains artefacts liés à une résolution insuffisante. Et l'antialiasing a lieu dans et après la rastérisation, et augmente la résolution du tampon de profondeur et du z-buffer. Les cartes graphiques en mode immédiat disposent d'optimisations pour limiter la casse, mais les ROP font malgré tout beaucoup d'accès mémoire. Avec le rendu en tiles, l'antialising se fait dans la ''RAM tile'', n'a pas besoin de passer par la mémoire vidéo et est donc plus rapide. ===Des compromis différents=== Les cartes graphiques des ordinateurs de bureau ou portables sont toutes en mode immédiat, alors que celles des appareils mobiles, smartphones et autres équipements embarqués ont un rendu en ''tiles''. Les raisons à cela sont multiples, mais la principale est que le rendu en ''tiles'' marche beaucoup mieux pour le rendu en 2D, comparé aux architectures en mode immédiat, ce qui se marie bien aux besoins des smartphones et autres objets connectés. La performance d'une carte graphique est limitée par la quantité d'accès mémoire par seconde. Autant dire que les économiser est primordial. Et les cartes en mode immédiat et par tile ne sont pas égales de ce point de vue. En mode immédiat, le tampon de primitives évite de passer par la mémoire vidéo, mais le z-buffer et le ''framebuffer'' sont très gourmand en accès mémoire. Avec les architectures à tile, c'est l'inverse : la géométrie est enregistrée en mémoire vidéo, mais le tampon de profondeur n'utilise pas la RAM vidéo. Au final, les deux architectures sont optimisées pour deux types de rendus différents. Les cartes à rendu en tile brillent quand la géométrie n'est pas trop compliquée, et que la résolution est grande ou que l'antialising est activé. Les cartes en mode immédiat sont douées pour les scènes géométriquement lourdes, mais avec peu d'accès aux pixels. Le tout est limité par divers caches qui tentent de rendre les accès mémoires moins fréquents, sur les deux types de cartes, mais sans que ce soit une solution miracle. ==La performance des anciennes cartes graphiques 3D== Intuitivement, la performance d'une carte graphique dépend de la performance de chacun de ses circuits : processeur de commande, mémoire vidéo, circuits de rendu 3D, VDC, etc. En pratique, il est rare qu'on soit limité par le VDC ou le processeur de commande. Les seules limitations viennent des circuits de rendu 3D et de la mémoire vidéo. Nous ne pouvons pas aborder la performance de la mémoire vidéo pour le moment. Tout ce que l'on peut dire est qu'il faut qu'elle soit assez rapide pour alimenter le rendu 3D en données. Les circuits de rendu 3D doivent lire des triangles et textures en mémoire vidéo, qui doit être assez rapide pour ça et ne pas les faire attendre. Pour le reste, voyons la performance des circuits de rendu 3D. Il ne nous est là aussi pas possible de détailler ce qui impacte la performance d'un GPU moderne. Dès que des processeurs de shaders sont impliqués, parler de performance demande de connaitre sur le bout des doigts les processeurs de shaders, ce qu'on n'a pas encore vu à ce stade du cours. Par contre, on peut détailler ce qu'il en était pour les anciennes cartes 3D, sans processeurs de shaders. Elles contenaient des ROPs, des unités de texture, un rastériseur et une unité géométrique (l'unité de T&L). Étudions d'abord la performance des unités de texture et des ROPs. Cela nous permettra de parler d'un paramètre qui avait son importance sur les anciennes cartes graphiques, avant les années 2000 : le ''fillrate''. Le '''''fill rate''''', ou taux de remplissage, est une ancienne mesure de performance autrefois utilisée pour comparer les cartes graphiques entre elles. Il s'agit d'une mesure assez approximative, au même titre que la fréquence d'horloge. Concrètement, plus il est élevé, meilleures seront les performances, en théorie. Mais attention : les petites différences de ''fillrate'' ne suffisent pas à rendre un verdict. De plus, il existe deux types distincts de ''fillrate'' : le ''Texture Fillrate'' et le ''Pixel Fillrate''. Voyons d'abord le ''Pixel Fillrate''. ===Le ''pixel fillrate'' : la performance des ROPs=== Le '''''pixel fillrate''''' est le nombre maximal de pixels que la carte graphique peut écrire en mémoire vidéo par seconde. Il est exprimé en ''Méga-Pixels par seconde'' ou en ''Giga-Pixels par seconde'', souvent abréviés en GP/s et MP/s. C'est une unité que vous croisez sans doute pour la première fois et qui mérite quelques explications. Premièrement, dans méga-pixels par seconde, il y a mégapixels. Il s'agit d'une unité pour compter le nombre de pixels d'une image. Un mégapixel signifie tout simplement un million de pixels, un gigapixel signifie un milliard de pixels. Je précise bien un million et un milliard, ce ne sont pas des multiples de 1024, comme on est habitué à en voir en informatique. Le nombre de pixels d'une image augmente avec la résolution utilisée, mais il reste de l'ordre du mégapixel, guère plus. Voici un tableau avec les résolutions les plus utilisées et le nombre de pixels associé. {|class="wikitable" |- ! Résolution !! Nombre de pixels |- | colspan="2" | |- | colspan="2" | Résolutions anciennes en 4:3 |- | 640 × 480 || 307 200 <math>\approx</math> 0,3 MP |- | 800 × 600 || 480 000 = 0,48 MP |- | 1 024 × 768 || 786 432 <math>\approx</math> 0,8 MP |- | 1 280 × 960 || 1 228 800 <math>\approx</math> 1,2 MP |- | 1 600 × 1 200 || 1 920 000 = 1,92 MP |- | colspan="2" | |- | colspan="2" | Résolutions modernes en 16:9 |- | 1 920 × 1 080 || 2 073 600 <math>\approx</math> 2 MP |- | 3 840 × 2 160 (4k) || 8 294 400 <math>\approx</math> 8.3 MP |} Maintenant, regardons ce qui se passe si on veut rendre plusieurs images par secondes. Intuitivement, on se dit qu'il faudra un ''pixel fillrate'' minimal pour cela. Et il se trouve qu'on peut le calculer aisément. Prenons par exemple une image en 1600 × 1200, de 1,92 mégapixels. Si on veut avoir 60 images par secondes, avec cette résolution, cela fait 1,92 * 60 mégapixels par secondes. En clair, le ''pixel fillrate'' minimal se calcule en multipliant la résolution par le ''framerate''. Le ''pixel fillrate'' minimal tourne autour de la centaine de mégapixels par seconde, voire approche le gigapixel par seconde en haute résolution. Les images font entre 1 et 10 mégapixels, pour environ 100 FPS, l'intervalle colle parfaitement. Maintenant, comparons un peu avec ce dont sont capables les GPUs. Les toutes premières cartes graphiques commerciales avaient un ''pixel fillrate'' proche de la centaine de méga-pixels par seconde. Pour donner un exemple, la Geforce 256 avait un ''pixel fillrate'' de 480 MP/s, la Geforce 3 faisait entre 700 et 960 MP/s selon le modèle. De nos jours, le ''pixel fillrate'' est de l'ordre de la centaine de Gigapixels. Pour donner un exemple, les Geforce RTX 5000 ont un ''pixel fillrate'' de 82.3GP/s pour la RTX 5050, à 423.6 GP/S pour la RTX 5090. Les GPU ont un ''pixel fillrate'' qui dépasse de très loin la valeur minimale, ce qui est franchement étrange. La raison à cela est que le ''pixel fillrate'' minimal se calcule sous l'hypothèse que chaque pixel de l'image finale ne sera écrit qu'une seule fois. Mais dans les faits, il est fréquent qu'un pixel soit dessiné plusieurs fois avant d'obtenir l'image finale. La raison principale est liée aux surfaces cachées. Si un objet est derrière un autre, il arrive que celui-ci soit dessiné dans le ''framebuffer'', avant que l'objet devant soit re-dessiné par-dessus. Des pixels ont alors été écrits, puis ré-écrits. Le fait de dessiner un pixel plusieurs fois porte un nom. Il s'agit d'un phénomène d''''''overdraw''''', ou sur-dessinage en français. Le sur-dessinage fait que le ''pixel fillrate'' minimal ne suffit pas en pratique. Pour éviter tout problème, le ''pixel fillrate'' du GPU doit être supérieur au ''pixel fillrate'' minimal, d'environ un ordre de grandeur. L'élimination des surfaces cachées réduit l'''overdraw'', mais elle ne fait pas de miracles. En pratique, le sur-dessinage ne concerne qu'une partie assez mineure des pixels de l'image, et un pixel est rarement écrit plus d'une dizaine de fois. Et les GPus modernes ont un ''pixel fillrate'' tellement démentiel qu'il n'est presque jamais un facteur limitant. Le ''pixel fillrate'' d'un GPU dépend de plusieurs choses : le nombre de ROPs, leur fréquence d'horloge exprimée en MHz/GHz, la bande passante mémoire, et bien d'autres. En théorie, la bande passante mémoire n'est pas un point limitant, les concepteurs du GPU prévoient une mémoire suffisamment rapide pour qu'elle puisse encaisser le ''pixel fillrate'' maximal, tout en ayant encore de la marge pour lire des textures et la géométrie. En clair, le ''pixel fillrate'' est surtout dépendant des ROPs, de leur nombre, de leur vitesse, de leur implémentation. Le ''pixel fillrate'' du GPU est difficile à calculer, mais l'approximation la plus utilisée est la suivante. Elle part du principe qu'un ROP peut écrire un pixel par cycle d'horloge. Ce n'est pas forcément le cas, tout dépend de l'implémentation des ROPs. Certains GPU performants ont des ROPs capables d'écrire des blocs de 8*8 pixels d'un seul coup en mémoire vidéo, alors que d'anciens GPU font avec des ROPs limités, seulement capables d'écrire un pixel tout les 10 cycles d'horloge. Toujours est-il qu'avec cette hypothèse, le ''pixel fillrate'' est égal au nombre de ROPs, multiplié par leur fréquence d'horloge. Je précise "leur" fréquence d'horloge, car il est possible de faire fonctionner l'unité de T&L, les ROPs, les unités de texture et le rastériseur à des fréquences différentes. C'est parfaitement possible, le cout en performance est parfois assez faible, mais le gain en consommation d'énergie est souvent important. Et justement, il a existé des GPU sur lesquels les ROPs avaient une fréquence inférieure à celle du reste du GPU. Dans ce cas, c'est la fréquence des ROPs qui est importante. Mais rassurez-vous : sur la majorité des GPUs actuels, les ROPs vont à la même fréquence que le reste du GPU. ===Le ''texture fillrate'' : la performance des unités de texture=== Le '''''texture fillrate''''' est l'équivalent du ''pixel fillrate'', mais pour les textures. Pour rappel, une texture est avant tout une image, composée de pixels. Pour éviter toute confusion, ces pixels de textures sont appelés ''des texels''. Le ''texture fillrate'' est le nombre de texels que la carte graphique peut plaquer par seconde, dans le meilleur des cas. Il est mesuré en mégatexels par secondes, voire en gigatexels par secondes. L'interprétation de ce chiffre dépend de si on le mesure en entrée ou en sortie des unités de texture. En effet, les unités de texture intègrent des fonctionnalités de filtrage de texture, qui lissent les textures. Ces techniques lisent plusieurs texels et les mélangent pour fournir le texel final, celui envoyé aux unités de ''shader'' ou aux ROPs. La coutume est de le mesurer en sortie des unités de texture. Le nombre en entrée dépend grandement de la bande passante mémoire et du filtrage de texture utilisé, pas celui en sortie. Le ''texture fillrate'' en sortie est le nombre maximal d'opérations de placage de texture par seconde. Là encore, on peut l'estimer en multipliant le nombre d'unités de texture par leur fréquence. Il s'agit évidemment d'une approximation assez peu fiable, car les unités de texture peuvent mettre plusieurs cycles pour plaquer une texture, les filtrer, etc. Le ''texture fillrate'' est bien plus important que le ''pixel fillrate'', surtout pour les GPU modernes. Un point important est que le ''texture fillrate'' a longtemps été égal au ''pixel fillrate''. C'était le cas avant la Geforce 2 de NVIDIA. Les cartes graphiques avaient autant d'unités de texture que de ROP, et les deux fonctionnaient à la même fréquence. Les deux ont commencés à diverger quand le multi-texturing est arrivé, avec la Geforce 2, justement. Le nombre d'unités de texture a doublé comparé aux ROPs, ce qui fait que le ''texture fillrate'' est rapidement devenu le double du ''pixel fillrate''. Sur les GPU modernes, le ''texture fillrate'' est le triple, quadruple, voire octuple du ''pixel fillrate''. ===La performance de l'unité géométrique=== Pour l'unité géométrique, l'équivalent au ''fillrate'' est le '''''polygon throughput'''''. C'est nombre de sommets que l'unité géométrique peut traiter par seconde, exprimé en ''méga-sommets par secondes'', en millions de sommets par seconde. Il dépend de la fréquence et du nombre d'unités géométriques, mais n'est pas exactement le produit des deux. Il varie beaucoup d'une carte graphique à l'autre, mais une approximation souvent utilisée prend le quart du produit fréquence * nombre d'unités géométriques. Il faut noter que cette mesure de performance a survécu à l'arrivée des shaders. Les GPU anciens, avant DirectX 10, avaient des processeurs séparés pour les ''vertex shaders'' et les ''pixel shaders''. Mais les calculs géométriques restaient séparés des autres calculs, ils avaient des unités géométriques dédiées. Quand les processeurs de shaders dit unifiés sont arrivés, la séparation entre géométrie et autres calculs a cédé et cet indicateur a simplement disparu. ===Les autres circuits=== Pour les autres circuits, il n'y a malheureusement pas d'indicateur de performance clair et net comme peut l'être le ''fillrate''. La raison à cela se comprend assez bien quand on regarde comment se calcule le ''fillrate''. C'est juste le produit de la fréquence et d'un nombre d'unités, en l’occurrence des unités de texture ou des ROPs. Le produit signifie que ces unités travaillent en parallèle et qu'elles peuvent chacune traiter un pixel/texel indépendamment des autres. Par contre, sur les anciens GPUs de l'époque, le rastériseur et l'unité géométrique sont un seul et unique circuit. Le nombre d'unité est donc égal à 1, et il ne nous reste plus que la fréquence. {{NavChapitre | book=Les cartes graphiques | prev=Le rendu d'une scène 3D : concepts de base | prevText=Le rendu d'une scène 3D : concepts de base | next=L'évolution vers la programmabilité : les GPUs | nextText=L'évolution vers la programmabilité : les GPUs }} {{autocat}} 2ojyqw0f5k37qvmh53d8suvh9164f5l Dictionnaire de philosophie/Anaxagore 0 83143 763995 758700 2026-04-19T08:47:45Z PandaMystique 119061 763995 wikitext text/x-wiki {{DicoPhilo|Anaxagore de Clazomènes}} == Vie et contexte historique == Les données biographiques concernant Anaxagore sont, comme le souligne l'''Oxford Classical Dictionary'', en grande partie « confuses et déroutantes » (''confused and confusing''), et la plupart des anecdotes transmises par la tradition antique doivent être abordées avec une prudence critique. Elles proviennent majoritairement de sources tardives — Diogène Laërce (III{{e}} siècle ap. J.-C.), Plutarque (I{{er}}-II{{e}} siècle), Valère Maxime, etc. — dont les récits sont souvent stylisés, voire légendaires. Anaxagore (en grec ancien Ἀναξαγόρας, « maître de l'assemblée ») naît vers 500 av. J.-C. à Clazomènes, cité grecque d'Ionie située sur la côte occidentale de l'actuelle Turquie, à environ trente kilomètres à l'ouest de l'actuelle Izmir<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 6 (DK 59 A 1). Apollodore d'Athènes, cité par Diogène Laërce, place la naissance d'Anaxagore à la 70{{e}} Olympiade (500-496 av. J.-C.).</ref>. Fils d'Hégésibule (certaines sources mentionnent Eubule), il appartiendrait, selon la tradition ancienne, à une famille aristocratique et aurait possédé un patrimoine important<ref>Diogène Laërce, II, 6-7 ; Valère Maxime, ''Faits et dits mémorables'', VIII, 7, ext. 5.</ref>. Selon Diogène Laërce, reprenant une tradition ancienne, Anaxagore aurait abandonné ses biens à ses proches afin de se consacrer entièrement à la philosophie<ref>Diogène Laërce, II, 7 : « Il négligea ses biens par amour de la sagesse. »</ref>. Une autre tradition, conservée par Valère Maxime, rapporte qu'Anaxagore, revenant d'un long voyage, aurait trouvé ses propriétés en ruine et aurait déclaré : « Si cela n'avait pas péri, c'est moi qui aurais péri »<ref>Valère Maxime, ''Faits et dits mémorables'', VIII, 7, ext. 5.</ref>, sentence que l'auteur romain commente comme témoignant de la plus parfaite sagesse. Ces récits, qu'ils soient authentiques ou pour partie légendaires, illustrent surtout l'image qu'Anaxagore a laissée dans la mémoire doxographique : celle du philosophe contemplatif préférant l'étude du cosmos aux affaires terrestres. Une anecdote célèbre, rapportée par plusieurs sources anciennes, témoigne de ce détachement philosophique. Interrogé sur son attachement à sa patrie, Anaxagore aurait répondu en levant la main vers le ciel : « J'ai un soin extrême de ma patrie », signifiant par là que le véritable philosophe considère l'univers entier comme sa demeure<ref>Plutarque, ''Vie de Périclès'', 4, 3-5 ; Aristote, ''Éthique à Eudème'', 1215b6-8.</ref>. De même, lorsqu'on lui demanda quel était l'homme le plus heureux, il aurait répondu : « Aucun de ceux que tu supposes — il te semblerait un personnage étrange »<ref>Aristote, ''Éthique à Eudème'', 1215b6-8.</ref>. Une autre fois, à quelqu'un qui insistait en lui demandant pour quelle raison on devrait choisir de naître plutôt que de ne pas être, il aurait dit : « Afin de contempler les cieux et l'ordre de l'univers entier »<ref>Aristote, ''Éthique à Eudème'', 1216a11-14.</ref>. === L'arrivée à Athènes et l'activité philosophique === La chronologie exacte de la vie d'Anaxagore demeure l'objet de débats parmi les historiens modernes, en raison de divergences importantes entre les sources anciennes<ref>Leonard Woodbury, « Anaxagoras and Athens », ''Phoenix'', vol. 35, n{{o}} 4, 1981, p. 295-315 ; Jaap Mansfeld, « The Chronology of Anaxagoras' Athenian Period and the Date of His Trial », ''Mnemosyne'', vol. 32-33, 1979-1980, p. 39-69 et p. 85-95 ; Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 33-35.</ref>. Selon la chronologie établie par Apollodore d'Athènes et rapportée par Diogène Laërce, Anaxagore serait arrivé à Athènes vers 480 av. J.-C., à l'âge de vingt ans, au moment des guerres médiques<ref>Diogène Laërce, II, 7. Cette tradition est soutenue par certains chercheurs modernes : Patricia O'Brien, « Anaxagoras and the Diurnal Revolution », ''Phronesis'', vol. 13, 1968, p. 133-143 ; Leonard Woodbury, 1981 ; Daniel Graham, ''Science before Socrates'', Oxford, Oxford University Press, 2006.</ref>. D'autres savants, notamment Jaap Mansfeld, proposent une date plus tardive, vers 456 av. J.-C.<ref>Jaap Mansfeld, 1979-1980 ; cf. également David Sider, ''The Fragments of Anaxagoras'', Sankt Augustin, Academia Verlag, 2005, p. 1-18, pour une discussion approfondie des problèmes chronologiques.</ref>. La question n'est pas définitivement tranchée, mais la majorité des historiens s'accorde sur une période d'activité philosophique à Athènes s'étendant sur près de trente ans, probablement entre 480/456 et 450/437 av. J.-C.<ref>Diogène Laërce, II, 7 ; Malcolm Schofield, 1980, p. 33-35.</ref>. Quelle que soit la date précise de son arrivée, Anaxagore est présenté par la tradition antique comme le premier philosophe à introduire la spéculation cosmologique et la philosophie naturelle ionienne dans la cité athénienne<ref>Clément d'Alexandrie, ''Stromates'', I, 14, 63, 3 (DK 59 A 7) : « Anaxagore transporta d'Ionie à Athènes la philosophie. » Cf. également Plutarque, ''Vie de Périclès'', 4, 2 ; Isocrate, ''Sur l'échange'', 268.</ref>. Athènes, en pleine expansion culturelle et politique après sa victoire sur les Perses à Marathon (490 av. J.-C.) et à Salamine (480 av. J.-C.), deviendra rapidement le centre intellectuel du monde grec. C'est dans ce contexte que la tradition situe l'enseignement d'Anaxagore. La tradition biographique antique — dominée par le récit de Plutarque dans la ''Vie de Périclès'' — rapporte qu'Anaxagore se serait lié à Périclès, l'homme d'État athénien qui dominera la vie politique de la cité de 454 à 431 av. J.-C.<ref>Plutarque, ''Vie de Périclès'', 4-5, 16, 32 (DK 59 A 15, A 17). Platon, ''Phèdre'', 269e-270a, évoque également l'influence d'Anaxagore sur l'éloquence de Périclès.</ref>. Selon cette tradition, Anaxagore aurait compté parmi les maîtres intellectuels de Périclès, et le surnom d'« instructeur de Périclès » lui aurait été attaché dès l'Antiquité<ref>Plutarque, ''Vie de Périclès'', 4, 5 ; Diogène Laërce, II, 10, 13. Les instruments de référence modernes (''Oxford Classical Dictionary'', s.v. « Anaxagoras ») restent toutefois prudents sur la portée exacte de cette relation, dont les témoignages sont tardifs.</ref>. Il convient cependant de ne pas transformer cette tradition en déclaration politique publique avérée : les sources dont nous disposons sont tardives, souvent biographiques ou rhétoriques, et la figure de « l'instructeur de Périclès » appartient en partie à la construction doxographique. Parmi les disciples ou auditeurs d'Anaxagore, les sources anciennes mentionnent également le poète tragique Euripide<ref>Diogène Laërce, II, 10 ; Héracléide du Pont, cité par Aulu-Gelle, ''Nuits attiques'', XV, 20 (DK 59 A 17). Certaines idées cosmologiques présentes dans les pièces d'Euripide semblent refléter l'enseignement d'Anaxagore.</ref>, le musicien et sophiste Damon<ref>Plutarque, ''Vie de Périclès'', 4.</ref>, et peut-être Archélaos, qui aurait ensuite été le maître de Socrate<ref>Diogène Laërce, II, 16, 23 (DK 60 A 1, A 4).</ref>. Concernant Socrate lui-même, la question de savoir s'il a personnellement rencontré Anaxagore demeure controversée. Dans le ''Phédon'', Platon fait dire à Socrate qu'il a entendu quelqu'un lire dans le livre d'Anaxagore — probablement Archélaos<ref>Platon, ''Phédon'', 97b-99d. L'identité du lecteur n'est pas précisée par Platon, mais la tradition ancienne l'a identifié à Archélaos.</ref> —, ce qui suggère que Socrate n'a pas eu de contact direct avec le philosophe de Clazomènes. Par ailleurs, dans l'''Apologie'', Platon fait référence au fait que les livres d'Anaxagore étaient en vente pour une drachme à l'orchestre du théâtre<ref>Platon, ''Apologie'', 26d. Cette remarque témoigne de la large diffusion de l'œuvre d'Anaxagore à Athènes à la fin du V{{e}} siècle av. J.-C.</ref>, ce qui indique que ses idées étaient largement connues à Athènes, même en l'absence d'enseignement oral direct. === L'œuvre écrite === Anaxagore a composé un traité intitulé ''Sur la nature'' (Περὶ φύσεως), rédigé en prose ionienne<ref>Diogène Laërce, II, 6 ; Simplicius, ''Commentaire sur la Physique d'Aristote'', 27, 2 (DK 59 A 41).</ref>. Selon les témoignages anciens, il n'aurait écrit qu'un seul livre<ref>Diogène Laërce, I, 16 ; II, 6-7.</ref>, bien que Platon emploie le pluriel τὰ Ἀναξαγόρου βιβλία dans l'''Apologie''<ref>Platon, ''Apologie'', 26d.</ref>. Cette apparente contradiction s'explique probablement par le fait que l'ouvrage, même relativement court, était copié sur plusieurs rouleaux de papyrus, ce qui était l'usage courant à l'époque pour les œuvres en prose. Diogène Laërce rapporte qu'Anaxagore fut le premier à publier un livre contenant des figures et des diagrammes<ref>Diogène Laërce, II, 11.</ref>, détail qui témoigne de la dimension scientifique et pédagogique qu'on lui prêtait dans l'Antiquité tardive. Le traité d'Anaxagore était rédigé dans un style sobre et dense, caractérisé par une prose ionienne archaïsante<ref>Karl Deichgräber, « Anaxagoras Stil », ''Philologus'', vol. 87, 1933, p. 15-26 ; Hermann Fränkel, ''Dichtung und Philosophie des frühen Griechentums'', Munich, C.H. Beck, 1962, p. 529-547.</ref>. Contrairement aux poèmes philosophiques de Parménide et d'Empédocle, Anaxagore choisit la prose comme véhicule de sa pensée, s'inscrivant ainsi dans la tradition ionienne des philosophes naturalistes. De ce traité, il ne subsiste aujourd'hui qu'une vingtaine de fragments, principalement conservés par Simplicius de Cilicie (VI{{e}} siècle ap. J.-C.) dans ses commentaires sur Aristote<ref>L'édition de référence des fragments et témoignages demeure celle de Hermann Diels et Walther Kranz, ''Die Fragmente der Vorsokratiker'', 6{{e}} édition, Berlin, Weidmann, 1952, vol. II, p. 5-44 (59 A et B). Une édition plus récente est celle d'André Laks et Glenn Most, ''Early Greek Philosophy'', Loeb Classical Library, 9 vol., Cambridge (Mass.), Harvard University Press, 2016, qui a introduit un nouveau système de numérotation ; éditions et traductions anglaises : Patricia Curd, ''Anaxagoras of Clazomenae : Fragments and Testimonia'', Toronto, University of Toronto Press, 2007 ; David Sider, ''The Fragments of Anaxagoras'', Sankt Augustin, Academia Verlag, 2005.</ref>. Simplicius, conscient de la rareté de l'ouvrage d'Anaxagore à son époque, prit soin de citer longuement les premières parties du traité, qui exposaient les principes généraux du système<ref>Simplicius, ''Commentaire sur la Physique d'Aristote'', 155, 23-157, 4 (fragments B1-B4) ; 164, 24-166, 1 (fragment B12).</ref>. === Le procès et l'exil === Les circonstances du départ d'Anaxagore d'Athènes et de sa fin de vie sont particulièrement controversées dans les sources anciennes. Diogène Laërce rapporte pas moins de quatre versions contradictoires du procès d'Anaxagore<ref>Diogène Laërce, II, 12-15. Ces quatre récits divergent sur l'identité de l'accusateur, la nature précise des charges, le déroulement du procès et son issue.</ref>. Selon une première version, attribuée à Sotion, Anaxagore aurait été accusé d'impiété (ἀσέβεια) par Cléon pour avoir déclaré que le soleil était une masse de métal incandescent ; Périclès l'aurait défendu, et Anaxagore aurait été condamné à une amende de cinq talents et à l'exil<ref>Sotion, dans Diogène Laërce, II, 12.</ref>. Selon Satyre, l'accusateur aurait été Thucydide (l'adversaire politique de Périclès, et non l'historien), les charges auraient inclus non seulement l'impiété mais aussi la trahison (sympathies pro-perses ou « médisme »), et Anaxagore aurait été condamné à mort par contumace<ref>Satyre, dans Diogène Laërce, II, 12-13.</ref>. Deux autres versions, attribuées respectivement à Hermippe et à Hiéronymos de Rhodes, présentent des variantes sur le rôle de Périclès et l'issue du procès<ref>Hermippe et Hiéronymos de Rhodes, dans Diogène Laërce, II, 13-14.</ref>. Plutarque, dans la ''Vie de Périclès'', fournit une version différente. Selon lui, un devin et politicien athénien nommé Diopeithès aurait fait voter un décret (le « décret de Diopeithès ») autorisant les poursuites judiciaires contre ceux qui ne reconnaissaient pas les dieux ou enseignaient des doctrines sur les phénomènes célestes<ref>Plutarque, ''Vie de Périclès'', 32, 1-2. Ce décret, daté approximativement de 433-432 av. J.-C., aurait visé Anaxagore mais aussi, indirectement, Périclès.</ref>. Selon cette version, Anaxagore aurait été accusé d'impiété pour avoir soutenu que le soleil n'était pas une divinité mais une pierre incandescente, et que la lune était une terre<ref>Plutarque, ''Vie de Périclès'', 32, 2 ; Platon, ''Apologie'', 26d, où Mélètos accuse Socrate de professer les mêmes opinions qu'Anaxagore.</ref>. Grâce à l'intervention de Périclès, Anaxagore aurait échappé à la condamnation à mort mais aurait dû s'exiler d'Athènes<ref>Plutarque, ''Vie de Périclès'', 32, 5.</ref>. L'historicité même de ce procès a été mise en doute par certains historiens modernes, qui soulignent l'absence de témoignages contemporains et les ressemblances troublantes avec le procès de Socrate<ref>J. A. Davison, « Protagoras, Democritus, and Anaxagoras », ''Classical Quarterly'', vol. 3, 1953, p. 33-45, exprime un scepticisme extrême.</ref>. Néanmoins, la plupart des spécialistes admettent aujourd'hui qu'un procès a bien eu lieu, même si les détails précis nous échappent<ref>Leonard Woodbury, 1981 ; Daniel Graham, 2006, p. 311-318, défendent l'historicité du procès tout en reconnaissant les difficultés chronologiques.</ref>. La date du procès demeure elle aussi incertaine : certains savants la placent vers 450 av. J.-C.<ref>Cette date traditionnelle est défendue notamment dans l'''Oxford Classical Dictionary'', 2{{e}} édition, 1970, s.v. « Anaxagoras ».</ref>, d'autres vers 437-436 av. J.-C.<ref>Mansfeld, 1979-1980.</ref>, d'autres encore vers 433-432 av. J.-C.<ref>Woodbury, 1981.</ref>. Quelle que soit la date précise, Anaxagore se retire à Lampsaque, colonie grecque d'Asie Mineure située sur l'Hellespont (l'actuel détroit des Dardanelles), où, selon la tradition, il est accueilli avec honneur<ref>Diogène Laërce, II, 14-15 ; Valère Maxime, ''Faits et dits mémorables'', VIII, 7, ext. 5.</ref>. Il y meurt en 428 av. J.-C., à l'âge de soixante-douze ans selon Apollodore<ref>Apollodore, dans Diogène Laërce, II, 7.</ref>. Les habitants de Lampsaque lui vouent, selon les sources tardives, un culte et célèbrent sa mémoire pendant plus d'un siècle après sa mort. Ils auraient élevé un autel dédié à l'Esprit et à la Vérité (Νοῦς καὶ Ἀλήθεια) en son honneur, et observé l'anniversaire de sa mort comme un jour férié pour les enfants des écoles<ref>Diogène Laërce, II, 15 ; Élien, ''Histoire variée'', VIII, 19 ; Cicéron, ''Tusculanes'', V, 36, 104.</ref>. Une inscription fut placée sur sa tombe, dont le texte exact ne nous est malheureusement pas parvenu, mais qui témoignait, selon Diogène Laërce, de l'estime dans laquelle les citoyens de Lampsaque tenaient le philosophe<ref>Diogène Laërce, II, 15.</ref>. === Portrait et anecdotes === Les sources anciennes ont conservé de nombreuses anecdotes sur Anaxagore qui, même si leur authenticité historique ne peut être garantie, témoignent de l'image du philosophe dans la tradition biographique antique. Galien rapporte qu'Anaxagore, apprenant la mort de son fils, aurait déclaré avec le plus grand calme : « Je savais qu'il était mortel quand je l'ai engendré »<ref>Galien, ''Des opinions d'Hippocrate et de Platon'', IV, 7, 41 (DK 59 A 33).</ref>. Ces récits contribuèrent à faire d'Anaxagore le modèle, dans la tradition, du philosophe détaché des contingences matérielles et consacré à la contemplation de la nature. Dans le ''Phèdre'' de Platon, Socrate attribue l'éloquence de Périclès à l'influence d'Anaxagore, qui lui aurait appris à s'élever au-dessus des préoccupations quotidiennes par la spéculation sur la nature de l'intelligence (νοῦς) et de la folie<ref>Platon, ''Phèdre'', 269e-270a.</ref>. Cette remarque témoigne de la réputation d'Anaxagore dans l'Athènes classique : celle d'un penseur dont les spéculations abstraites et « météorologiques » (au sens ancien de l'étude des phénomènes célestes) pouvaient paraître étranges ou excessives aux yeux du commun, mais qui exerçaient une fascination certaine sur les esprits cultivés. Cette image se reflète également dans la comédie d'Aristophane, ''Les Nuées'', où les spéculations cosmologiques tournées en ridicule semblent en partie inspirées par les doctrines d'Anaxagore, bien que ce soit Socrate qui soit présenté sur scène<ref>Aristophane, ''Les Nuées'', passim. Cf. Kenneth Dover, ''Aristophanes Clouds'', Oxford, Clarendon Press, 1968, p. xxxii-xlvii, pour la discussion du rapport entre les théories cosmologiques ridiculisées dans la pièce et celles d'Anaxagore.</ref>. === L'école de Lampsaque === À Lampsaque, Anaxagore fonda ou du moins anima une école philosophique qui continua son enseignement après sa mort<ref>Malcolm Schofield, 1980, p. 1 ; W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 266-267.</ref>. Parmi ses disciples à Lampsaque, les sources mentionnent Métrodore de Lampsaque<ref>Diogène Laërce, II, 17 ; Plutarque, ''Des opinions des philosophes'', 888e (DK 61).</ref>, qui poursuivit et développa les interprétations allégoriques d'Homère dans un esprit anaxagoréen. L'influence de l'école de Lampsaque se prolongea au moins jusqu'à la fin du V{{e}} siècle av. J.-C. Cette présence d'Anaxagore à Lampsaque explique peut-être pourquoi ses idées ont continué à circuler et à être discutées dans le monde grec, malgré son départ d'Athènes et le caractère fragmentaire de la tradition de son œuvre écrite. == Les principes métaphysiques fondamentaux == La philosophie d'Anaxagore se construit en réponse directe aux exigences métaphysiques établies par Parménide d'Élée<ref>Patricia Curd, ''The Legacy of Parmenides: Eleatic Monism and Later Presocratic Thought'', Princeton, Princeton University Press, 1998, p. 123-142 ; Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 27-47.</ref>. Dans son poème philosophique, Parménide avait posé deux principes fondamentaux : « l'être est et ne peut pas ne pas être » (ἔστιν εἶναι) et « le non-être n'est pas et ne peut pas être » (οὐκ ἔστι μὴ εἶναι)<ref>Parménide, fragment B2, DK 28 B 2, 3-5 ; cf. également B6, 1-2 et B8, 1-2.</ref>. De ces prémisses, Parménide concluait que toute génération et toute corruption véritables sont impossibles, car elles impliqueraient un passage de l'être au non-être ou du non-être à l'être — transitions logiquement impossibles puisque le non-être ne peut en aucune manière être<ref>Parménide, B8, 6-21 ; Aristote, ''Physique'', I, 8, 191a23-31.</ref>. Plus encore, Parménide soutenait que l'être véritable devait être un, continu, homogène, immuable et éternel<ref>Parménide, B8, 22-25 : « Il n'est pas divisible, puisqu'il est tout entier semblable » (οὐδὲ διαιρετόν ἐστιν, ἐπεὶ πᾶν ἐστιν ὁμοῖον).</ref>. Anaxagore accepte les principes éléatiques fondamentaux — l'impossibilité du passage de l'être au non-être et inversement —, mais refuse d'en tirer la conclusion que le monde sensible et le changement sont illusoires<ref>Aristote, ''Métaphysique'', I, 3, 984a11-16 ; Simplicius, ''Commentaire sur la Physique d'Aristote'', 25, 19-26 (DK 59 A 52).</ref>. Selon Simplicius, « Anaxagore de Clazomènes, qui fut un disciple de la philosophie d'Anaximène, fut le premier à s'opposer à Parménide »<ref>Simplicius, ''Commentaire sur la Physique'', 25, 19-21 (DK 59 A 52). L'affirmation que Anaxagore fut disciple d'Anaximène est chronologiquement problématique et doit être comprise au sens large d'une appartenance à la tradition ionienne.</ref>. Plutôt que de nier la réalité du changement et de la diversité, Anaxagore cherche à fonder une cosmologie conforme aux exigences parménidiennes tout en rendant compte de la multiplicité et du mouvement observables dans la nature<ref>Malcolm Schofield, 1980, p. 38-42 ; G. E. L. Owen, « Eleatic Questions », ''Classical Quarterly'', vol. 10, 1960, p. 84-102.</ref>. Comme l'a remarqué G. E. L. Owen, la phrase d'ouverture du traité d'Anaxagore — « Toutes choses étaient ensemble » — est « manifestement formulée comme une contradiction plate de Parménide sur plusieurs questions majeures »<ref>G. E. L. Owen, « The Place of the Timaeus in Plato's Dialogues », ''Classical Quarterly'', vol. 3, 1953, p. 79-95, cité dans Malcolm Schofield, 1980, p. 64.</ref>. === Le principe de conservation === Le premier principe métaphysique fondamental qu'Anaxagore énonce se trouve dans le fragment B17 : « Les Grecs ne pensent pas correctement au sujet de la génération et de la corruption. Aucune chose ne naît ni ne périt, mais à partir des choses qui sont, il y a mélange et séparation. Ainsi, ils appelleraient correctement la génération "mélange" (σύγκρισις) et la corruption "séparation" (διάκρισις) »<ref>Anaxagore, fragment B17, cité par Simplicius, ''Commentaire sur la Physique'', 163, 20-24 (DK 59 B 17).</ref>. Ce principe de conservation constitue le fondement de toute sa physique et sa réponse directe à l'interdit parménidien. Ce qui apparaît comme génération n'est en réalité qu'un réarrangement d'éléments préexistants qui se mélangent (συμμίσγεται). Ce qui semble être corruption n'est que la dissociation (διακρίνεται) de ces mêmes éléments<ref>Anaxagore, B17 ; cf. également Aristote, ''Génération et corruption'', I, 1, 314a18-20 (DK 59 A 52) : « Anaxagore et d'autres disent que la génération et la corruption sont mélange et séparation. »</ref>. Rien ne naît du néant (ex nihilo nihil fit), rien ne retourne au néant : les composants élémentaires de la réalité existent de toute éternité et demeurent inaltérables dans leur nature propre. Aristote souligne qu'Anaxagore, comme d'autres philosophes naturalistes, fonde explicitement ce principe sur l'axiome parménidien<ref>Aristote, ''Physique'', I, 4, 187a26-31.</ref>. Le principe prend chez Anaxagore une forme plus spécifique encore dans le fragment B10, où il pose la question rhétorique : « Comment le cheveu pourrait-il provenir de ce qui n'est pas cheveu, et la chair de ce qui n'est pas chair ? » (πῶς γὰρ ἂν ἐκ μὴ τριχὸς γένοιτο θρὶξ καὶ σὰρξ ἐκ μὴ σαρκός;)<ref>Anaxagore, fragment B10, conservé dans une scholie à Grégoire de Nazianze (DK 59 B 10).</ref>. Cette formulation, qui deviendra proverbiale dans l'Antiquité, exprime ce que les commentateurs modernes appellent le « principe du semblable par le semblable » (similia similibus) : une substance ne peut provenir que de la même substance, déjà présente sous une forme latente<ref>Aristote, ''Physique'', I, 4, 187a26-29 ; cf. Malcolm Schofield, 1980, p. 51-58 ; Jonathan Barnes, ''The Presocratic Philosophers'', Londres, Routledge, 1982, p. 309-315.</ref>. Ce principe présente une analogie frappante avec celui qu'Antoine-Laurent de Lavoisier formulera en 1789 comme principe de conservation de la matière, et la sentence « Rien ne se perd, rien ne se crée, tout se transforme » en est parfois rapprochée. Il convient cependant de ne pas surinterpréter cette analogie : les mécanismes qu'Anaxagore propose pour expliquer les transformations (mélange et séparation d'ingrédients éternels) sont qualitatifs et ne relèvent pas d'une conception chimique quantitative au sens moderne<ref>Antoine-Laurent de Lavoisier, ''Traité élémentaire de chimie'', Paris, Cuchet, 1789, vol. I, p. 101 : « Rien ne se crée, ni dans les opérations de l'art, ni dans celles de la nature, et l'on peut poser en principe que, dans toute opération, il y a une égale quantité de matière avant et après l'opération. » Le rapprochement avec Anaxagore relève plus de l'analogie conceptuelle rétrospective que d'une filiation historique documentée.</ref>. L'intuition conservatrice d'Anaxagore partage avec le principe lavoisien l'idée que rien ne se perd ni ne se crée dans le devenir physique, mais il serait anachronique d'y voir l'un des principes cardinaux de la chimie moderne. === Tout est dans tout === Le deuxième principe métaphysique fondamental d'Anaxagore s'énonce ainsi : « Il y a une part de tout en toute chose » (ἐν παντὶ παντὸς μοῖρα ἔνεστι, fragment B11)<ref>Anaxagore, fragment B11, cité par Simplicius, ''Commentaire sur la Physique'', 164, 26 (DK 59 B 11).</ref>. Ce principe, souvent désigné par la formule latine ''omnia in omnibus'' (« toutes choses dans toutes choses »), constitue sans doute la thèse la plus caractéristique et la plus déroutante de la philosophie d'Anaxagore<ref>Cf. Gregory Vlastos, « The Physical Theory of Anaxagoras », ''Philosophical Review'', vol. 59, 1950, p. 31-57 ; Colin Strang, « The Physical Theory of Anaxagoras », ''Archiv für Geschichte der Philosophie'', vol. 45, 1963, p. 101-118.</ref>. Il signifie que chaque portion de matière, si infime soit-elle, contient en elle-même des portions de toutes les autres substances qui existent dans l'univers. Il n'existe aucune substance pure, aucun élément qui puisse être isolé de tous les autres<ref>Anaxagore, B6 : « Puisqu'il est impossible qu'il y ait un minimum, il ne serait pas possible que [quelque chose] soit séparé, ni ne vienne à l'être par soi-même » (ἐπεὶ δὲ οὐκ ἔστι τοῦ ὀλίγου τὸ ἐλάχιστον, οὐκ ἂν γένοιτο χωρίς, οὐδὲ γένοιτο καθ᾽ ἑαυτό).</ref>. Dans un morceau d'or, il y a non seulement de l'or en proportion dominante, mais aussi de la chair, des os, du chaud, du froid, du sec, de l'humide, et ainsi de suite pour toutes les qualités et substances possibles<ref>Aristote, ''Métaphysique'', I, 3, 984a13-16 ; Simplicius, ''Commentaire sur la Physique'', 34, 29-35, 9 (DK 59 A 45).</ref>. Cette thèse découle logiquement du principe de conservation et du principe du semblable par le semblable combinés. En effet, comment la chair pourrait-elle provenir du pain et du lait que nous consommons, si le pain et le lait ne contenaient pas déjà de la chair ? La question rhétorique du fragment B10 impose cette conclusion. La nourriture que nous ingérons doit nécessairement contenir en elle-même, quoique de manière imperceptible en raison de la petitesse des parties, toutes les substances qui composent notre corps : cheveux, ongles, veines, artères, nerfs, os<ref>Scholie anonyme à Grégoire de Nazianze, DK 59 B 10 : « Dans le même liquide séminal, il y a des cheveux, des ongles, des veines et des artères, des nerfs et des os, et ils sont imperceptibles en raison de la petitesse des parties (δι᾽ ὀλιγότητα), mais lorsqu'ils croissent, ils se séparent graduellement. »</ref>. C'est par un processus de séparation graduelle (ἀποκρίνεσθαι), au cours de la croissance, que ces éléments préexistants dans la nourriture viennent s'ajouter aux parties correspondantes de notre organisme. Le principe « tout est dans tout » s'applique non seulement aux substances naturelles comme la chair et les os, mais aussi aux qualités opposées. Anaxagore affirme que « le noir est dans le blanc et le blanc dans le noir » et qu'il en va de même pour le lourd et le léger, le chaud et le froid<ref>Anaxagore, B15, cité par Simplicius, ''Commentaire sur le Ciel'', 608, 26 (DK 59 B 15) ; Simplicius, ''Commentaire sur la Physique'', 175, 11-14.</ref>. Ces oppositions qualitatives ne sont pas absolues mais relatives : ce qui nous paraît blanc contient en réalité du noir, mais en proportion si faible que cette part de noir demeure invisible à nos sens. De même, ce qui est léger contient du lourd, et inversement<ref>Anaxagore, B1, B4b ; Aristote, ''Physique'', I, 4, 187b1-7 (DK 59 A 52).</ref>. Cette doctrine a suscité d'intenses débats interprétatifs depuis l'Antiquité. Aristote la présente comme une tentative de résoudre le problème de la nutrition tout en respectant les contraintes éléatiques<ref>Aristote, ''Physique'', III, 4, 203a19-b3 ; cf. W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 289-294.</ref>. Les interprètes modernes se divisent sur la question de savoir si Anaxagore conçoit ces « portions » (μοῖραι) comme des particules infinitésimales ou comme des qualités interpénétrées sans structure corpusculaire déterminée<ref>Pour la première interprétation « particulaire », voir Gregory Vlastos, 1950 ; Aristotle Peck, « Anaxagoras : Predication as a Problem in Physics », ''Classical Quarterly'', vol. 25, 1931, p. 27-37 et p. 112-120. Pour la seconde interprétation « non-particulaire », voir Colin Strang, 1963 ; Daniel Graham, « Was Anaxagoras a Reductionist? », ''Ancient Philosophy'', vol. 24, 2004, p. 1-18 ; Anna Marmodoro, « Anaxagoras's Qualitative Gunk », ''British Journal for the History of Philosophy'', vol. 23, 2015, p. 402-422.</ref>. Quoi qu'il en soit, ce principe constitue la clé de voûte du système physique d'Anaxagore. === Pas de plus petit ni de plus grand === Le troisième principe métaphysique, exposé dans le fragment B3, stipule qu'« il n'y a pas de plus petit dans le petit, mais toujours un plus petit encore, car il est impossible que ce qui est cesse d'être par division (οὐ γάρ ἐστι τοῦ ἀποκεκρίσθαι τὸ ἐλάχιστον). Mais il y a aussi toujours un plus grand que le grand, et il est égal au petit en multitude (πλῆθος) ; mais par rapport à elle-même, chaque chose est à la fois grande et petite »<ref>Anaxagore, fragment B3, cité par Simplicius, ''Commentaire sur la Physique'', 164, 17-20 (DK 59 B 3).</ref>. Ce principe garantit la divisibilité infinie de la matière et s'oppose frontalement à toute conception atomiste de type démocritéen<ref>Leucippe et Démocrite, fragments DK 67-68 ; Aristote, ''Génération et corruption'', I, 2, 315b28-317a2 ; I, 8, 325a23-b5.</ref>. Contrairement à Leucippe et Démocrite, qui postulent l'existence d'atomes insécables (ἄτομα, littéralement « indivisibles »), Anaxagore maintient que toute portion de matière, aussi petite soit-elle, peut encore être divisée, et que cette division révélera toujours la présence de toutes les substances selon le principe « tout est dans tout »<ref>Démocrite, fragments B9, B125, B156, B164 ; cf. Aristote, ''De la génération et de la corruption'', I, 8, 325a23-b5 ; Simplicius, ''Commentaire sur le Ciel'', 294, 33-295, 22 (DK 68 A 37).</ref>. Pour Anaxagore, la divisibilité peut être infinie sans que la matière s'évanouisse dans le néant, précisément parce que chaque fragment, si petit soit-il, contient encore toutes les substances<ref>Anaxagore, B6 : « Puisque les parts du grand et du petit sont égales en nombre, ainsi également toutes choses seraient dans toute chose. Il n'est pas possible qu'elles soient séparées, mais toutes choses ont une part de toute chose » (ἴσαι γὰρ ἀριθμῷ μοῖραί εἰσι καὶ τοῦ μεγάλου καὶ τοῦ σμικροῦ· καὶ οὕτως ἂν εἴη πάντα ἐν παντί).</ref>. Cette infinité dans la petitesse se combine avec l'infinité dans la grandeur. Il n'existe pas de limite supérieure à l'extension d'une substance, pas plus qu'il n'existe de limite inférieure<ref>Anaxagore, B3 : « Mais il y a aussi toujours un plus grand que le grand, et il est égal au petit en multitude » (ἀλλὰ καὶ μεγάλου ἀεὶ ἔστι μεῖζον· καὶ ἴσον ἐστὶ τῷ σμικρῷ πλήθει).</ref>. Cette double infinité – de division et d'extension – constitue un trait distinctif de la métaphysique d'Anaxagore et pose des problèmes interprétatifs considérables aux commentateurs, tant anciens que modernes<ref>Margaret Furth, « A Philosophical Hero? Anaxagoras and the Eleatics », ''Oxford Studies in Ancient Philosophy'', vol. 9, 1991, p. 95-129 ; David Furley, « Anaxagoras, Plato and Naming of Parts », dans ''Presocratic Philosophy: Essays in Honour of Alexander Mourelatos'', éd. Victor Caston et Daniel Graham, Aldershot, Ashgate, 2002, p. 119-126.</ref>. Le principe de non-minimum a une conséquence métaphysique importante : il assure que le mélange universel « tout est dans tout » ne pourra jamais être défait. En effet, puisqu'il n'existe pas de plus petite quantité d'une substance, celle-ci ne pourra jamais être entièrement extraite d'un mélange. On pourra réduire sa proportion indéfiniment, mais elle demeurera toujours présente en quelque mesure<ref>Anaxagore, B6 ; Simplicius, ''Commentaire sur la Physique'', 164, 20-22 : « Car si tout est dans tout et si tout se sépare de tout, alors du prétendu minimum quelque chose de plus petit que lui sera séparé, et du prétendu maximum quelque chose de plus grand que lui a été séparé. »</ref>. Ainsi, le principe de mélange universel est garanti de manière structurelle par le principe de divisibilité infinie. Comme l'a remarqué Colin Strang, « la complexité structurelle n'est pas, dans la théorie d'Anaxagore, fonction de la taille »<ref>Colin Strang, 1963, p. 366 : « Structural complexity is not, on Anaxagoras' theory, a function of size. »</ref>. La justification qu'Anaxagore donne de ce principe est explicitement parménidienne : « car il est impossible que ce qui est cesse d'être » (οὐ γὰρ ἔστι τοῦ εἶναι τὸ μὴ εἶναι)<ref>Anaxagore, B3 ; cette formulation reprend directement l'axiome de Parménide, B2, 3.</ref>. Si l'on pouvait diviser la matière jusqu'à la faire disparaître complètement, cela impliquerait un passage de l'être au non-être, ce qui viole l'interdit fondamental. Toute division, aussi poussée soit-elle, doit donc laisser subsister quelque chose — et ce quelque chose, par application du principe « tout est dans tout », contiendra encore des portions de toutes les substances<ref>Simplicius, ''Commentaire sur la Physique'', 164, 23-165, 1 (commentaire sur B3).</ref>. === Le principe de prédominance === Du principe « tout est dans tout » découle une difficulté évidente : si chaque chose contient une part de toutes les autres, comment expliquer que nous percevions des objets distincts et identifiables ? Comment distinguer l'or de la chair, le noir du blanc, si l'or contient de la chair et la chair contient de l'or, si le noir contient du blanc et le blanc contient du noir ? Anaxagore résout cette difficulté par ce que les commentateurs modernes appellent le « principe de prédominance » (bien qu'Anaxagore lui-même n'emploie pas ce terme technique)<ref>Le terme « principe de prédominance » (predominance principle) a été introduit par les interprètes modernes, notamment Gregory Vlastos, 1950, p. 47-50 ; Malcolm Schofield, 1980, p. 88-101.</ref>. Dans le fragment B12, il affirme : « Chaque chose est et était très manifestement (ἐμφανέστατα) constituée de celles des choses dont il y a le plus en elle »<ref>Anaxagore, fragment B12, cité par Simplicius, ''Commentaire sur la Physique'', 156, 1-157, 4, à 156, 10-11 (DK 59 B 12).</ref>. Autrement dit, une chose tire son identité et ses propriétés apparentes des substances qui prédominent en elle. Un morceau d'or nous apparaît comme de l'or parce que la substance or y est présente en proportion largement supérieure à toutes les autres substances. De même, notre chair nous semble être de la chair parce que la substance chair y domine quantitativement<ref>Simplicius, ''Commentaire sur la Physique'', 34, 29-35, 9 : « Chaque chose semble être cela dont elle a le plus, comme elle l'était auparavant » (δοκεῖν δ᾽ ἕκαστον εἶναι ταῦτα ὧν ἂν πλεῖστα ἐνῇ, ὥσπερ καὶ πρότερον).</ref>. Cette prédominance n'est pas absolue — puisque toutes les autres substances sont également présentes —, mais elle est suffisante pour conférer à l'objet ses caractéristiques perceptibles<ref>Anaxagore, B12 ; Aristote, ''Physique'', I, 4, 187b1-7.</ref>. Le principe de prédominance permet à Anaxagore de concilier son ontologie de mélange universel avec notre expérience quotidienne d'un monde composé d'objets distincts et identifiables. Il explique également le changement : lorsqu'un objet semble se transformer en un autre — par exemple, lorsque le pain que nous mangeons devient chair —, ce qui se produit en réalité est une modification des proportions relatives des substances présentes. La chair, déjà présente dans le pain mais en proportion imperceptible, vient s'ajouter à la chair de notre corps à mesure que les autres composants du pain se dispersent ou que leurs proportions relatives diminuent<ref>Anaxagore, B10 ; Aristote, ''Génération des animaux'', II, 4, 740b26-29 ; Simplicius, ''Commentaire sur la Physique'', 460, 4-465, 19 (DK 59 A 45).</ref>. Ainsi, contrairement aux apparences sensibles, il n'y a pas de transformation qualitative véritable, mais seulement des réarrangements quantitatifs<ref>Malcolm Schofield, 1980, p. 88-101 ; Daniel Graham, ''Explaining the Cosmos: The Ionian Tradition of Scientific Philosophy'', Princeton, Princeton University Press, 2006, p. 137-152.</ref>. Théophraste, dans son traité ''Sur les sensations'', rapporte qu'Anaxagore soutenait que « toute sensation s'accompagne de douleur » (πᾶσαν αἴσθησιν μετὰ λύπης εἶναι) précisément parce que percevoir implique un contact entre des qualités opposées<ref>Théophraste, ''De Sensibus'', 1, 27-29 (DK 59 A 92). Cf. également Aétius, IV, 9, 1 (DK 59 A 92).</ref>. Si le principe de prédominance explique pourquoi nous percevons des objets distincts, il explique aussi pourquoi cette perception n'est jamais parfaitement exacte : les sens ne peuvent discriminer que les différences marquées de proportion, mais les différences subtiles leur échappent<ref>Sextus Empiricus, ''Contre les mathématiciens'', VII, 90 (DK 59 B 21) : « Les apparences sont une vision des choses non-manifestes » (ὄψις τῶν ἀδήλων τὰ φαινόμενα).</ref>. === Synthèse : la réponse d'Anaxagore à Parménide === Les quatre principes métaphysiques que nous venons d'examiner — conservation, tout est dans tout, divisibilité infinie, prédominance — forment un système cohérent qui constitue la réponse d'Anaxagore au défi parménidien<ref>Patricia Curd, 1998, p. 123-165 ; Malcolm Schofield, 1980, p. 27-86.</ref>. Anaxagore accepte l'impossibilité du passage de l'être au non-être, mais rejette les conséquences que Parménide en tire concernant l'unicité, l'immobilité et l'homogénéité de l'être. Premièrement, en remplaçant la génération et la corruption par le mélange et la séparation (B17), Anaxagore sauve les phénomènes sans violer l'interdit éléatique : rien ne naît véritablement, rien ne périt véritablement, il n'y a que réarrangement de ce qui existe déjà de toute éternité. Deuxièmement, en posant que tout est dans tout (B11, B6), Anaxagore explique comment des substances apparemment nouvelles peuvent émerger sans être créées ex nihilo : elles étaient déjà présentes dans le mélange, simplement imperceptibles en raison de leur faible proportion. Troisièmement, en affirmant la divisibilité infinie (B3, B6), Anaxagore garantit que le mélange universel ne pourra jamais être entièrement défait. Quatrièmement, en introduisant le principe de prédominance (B12), Anaxagore rend compte de la diversité phénoménale et de la possibilité de la perception, tout en maintenant l'ontologie du mélange universel. Ainsi, Anaxagore parvient à préserver les intuitions fondamentales de Parménide — la permanence de l'être, l'impossibilité du non-être, l'immutabilité substantielle — tout en rendant compte de la pluralité, du changement et du devenir qui caractérisent notre expérience du monde<ref>Patricia Curd, 1998, p. 165 : « Anaxagoras accepts the fundamental Eleatic constraint that what-is-not cannot be, but offers an account of the world that preserves both plurality and change. » Cf. également Daniel Graham, 2006, p. 137-152 ; G. E. R. Lloyd, ''Early Greek Science: Thales to Aristotle'', Londres, Chatto & Windus, 1970, p. 47-52.</ref>. == Les ingrédients primordiaux == La question de savoir quels sont exactement les ingrédients élémentaires (τὰ χρήματα) qui composent l'univers d'Anaxagore a suscité d'intenses débats parmi les commentateurs, depuis Aristote jusqu'aux spécialistes contemporains<ref>Pour une vue d'ensemble des débats, voir Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 99-138 ; Patricia Curd, ''Anaxagoras of Clazomenae : Fragments and Testimonia'', Toronto, University of Toronto Press, 2007, essais 2-4 ; Daniel Graham, « Was Anaxagoras a Reductionist? », ''Ancient Philosophy'', vol. 24, 2004, p. 1-18.</ref>. Le problème est rendu particulièrement complexe par le fait qu'Anaxagore ne fournit pas de liste systématique et exhaustive de ces ingrédients, et que les fragments conservés mentionnent des entités de natures apparemment différentes. Comme le souligne Malcolm Schofield, « malgré le rôle cardinal de la doctrine du "tout est dans tout" dans sa philosophie, les fragments qui ont survécu indiquent que dans son exposition, Anaxagore assigna au moins autant d'importance à son récit du mélange primordial et à sa description du Noûs et de son activité cosmogonique »<ref>Malcolm Schofield, 1980, p. 99.</ref>. === Les opposés === Dans les fragments B1, B2, B4b, B8, B12 et B15, Anaxagore énonce explicitement plusieurs paires d'opposés (τὰ ἐναντία) : l'humide et le sec (τὸ ὑγρόν καὶ τὸ ξηρόν), le chaud et le froid (τὸ θερμόν καὶ τὸ ψυχρόν), le lumineux et l'obscur (τὸ λαμπρόν καὶ τὸ ζοφερόν), le dense et le rare (τὸ πυκνόν καὶ τὸ ἀραιόν)<ref>Anaxagore, B1, B2, B4b, B8, B12, B15.</ref>. Ces qualités opposées jouent un rôle fondamental dans la cosmologie d'Anaxagore. Ce sont elles qui, par leur séparation progressive (ἀποκρίνεσθαι) à partir du mélange originel et leur réagencement ultérieur, donnent naissance à la diversité des phénomènes naturels<ref>Anaxagore, B12, B13, B15, B16 ; Aristote, ''Physique'', I, 4, 187b1-7 (DK 59 A 52).</ref>. Les opposés ne doivent pas être compris comme de simples attributs qui qualifieraient une substance sous-jacente. Dans la pensée présocratique en général, et chez Anaxagore en particulier, la distinction ultérieure entre substance et qualité n'existe pas encore avec la netteté qu'elle acquerra chez Aristote<ref>G. E. L. Owen, « Tithenai ta Phainomena », dans ''Aristotle et les problèmes de méthode'', éd. Suzanne Mansion, Louvain, Publications Universitaires, 1961, p. 83-103 ; Jonathan Barnes, ''The Presocratic Philosophers'', Londres, Routledge, 1982, p. 316-321.</ref>. Le chaud (τὸ θερμόν) n'est pas simplement une propriété qui affecterait une matière indéterminée ; c'est une réalité substantielle en soi, qui peut être présente en plus ou moins grande proportion dans un mélange donné. Comme l'a remarqué F. M. Cornford, ces opposés doivent être compris comme des « choses-qualités » (quality-things)<ref>F. M. Cornford, « Anaxagoras' Theory of Matter », ''Classical Quarterly'', vol. 24, 1930, p. 14-30, aux pages 16-17.</ref>. Gregory Vlastos précise que ces opposés doivent plutôt être compris « comme des formes d'énergie ou de puissance (δύναμις) »<ref>Gregory Vlastos, « The Physical Theory of Anaxagoras », ''Philosophical Review'', vol. 59, 1950, p. 31-57.</ref>. === L'interprétation « austère » : les opposés seuls === Paul Tannery, à la fin du XIXe siècle, fut le premier à contester l'interprétation aristotélicienne selon laquelle Anaxagore aurait postulé l'existence de « particules homéomères » (particules de chair, d'os, etc.)<ref>Paul Tannery, « La théorie de la matière d'Anaxagore », ''Revue Philosophique'', vol. 22, 1886, p. 255-274 ; repris dans ''Pour l'histoire de la science hellène'', Paris, Alcan, 1887, p. 275-290.</ref>. Tannery soutenait qu'Anaxagore ne parlait que de qualités : l'humide, le sec, le chaud, le froid, etc. John Burnet adopta une position similaire dans son ouvrage influent ''Early Greek Philosophy'', reconnaissant que « même lorsque la notion de qualité (ποιότης) avait été définie, cette manière de penser survécut »<ref>John Burnet, ''Early Greek Philosophy'', 4{{e}} édition, Londres, Adam and Charles Black, 1930, p. 256-264, citation p. 263.</ref>. Burnet s'appuyait notamment sur Galien, qui affirme dans son commentaire sur Hippocrate que « ce sont les qualités qui sont éternelles » chez Anaxagore<ref>Galien, ''Commentaire sur le traité hippocratique Des humeurs'', XVI, 32 Kühn.</ref>. Cette interprétation présente plusieurs avantages. Premièrement, elle se fonde étroitement sur les fragments conservés d'Anaxagore lui-même, plutôt que sur les reconstructions d'Aristote, qui écrivait près de cent cinquante ans après Anaxagore et avait ses propres préoccupations philosophiques. Deuxièmement, elle rend compte du rôle cosmologique crucial des opposés dans les fragments B12, B13, B15 et B16, où ce sont explicitement les opposés qui se séparent lors de la cosmogonie<ref>Malcolm Schofield, 1980, p. 107-113 ; Colin Strang, « The Physical Theory of Anaxagoras », ''Archiv für Geschichte der Philosophie'', vol. 45, 1963, p. 101-118.</ref>. Cette interprétation a été défendue au XXe siècle par plusieurs spécialistes éminents, notamment F. M. Cornford, Gregory Vlastos (avec certaines réserves), Colin Strang, Malcolm Schofield (avec des nuances importantes), Brad Inwood, David Sedley, et plus récemment Anna Marmodoro<ref>F. M. Cornford, 1930 ; Gregory Vlastos, 1950, p. 329-333 ; Colin Strang, 1963 ; Malcolm Schofield, 1980, p. 99-138 ; Brad Inwood, « Anaxagoras and Infinite Divisibility », ''Illinois Classical Studies'', vol. 11, 1986, p. 17-33 ; David Sedley, ''Creationism and Its Critics in Antiquity'', Berkeley, University of California Press, 2007, p. 24-26 ; Anna Marmodoro, « Anaxagoras's Qualitative Gunk », ''British Journal for the History of Philosophy'', vol. 23, 2015, p. 402-422.</ref>. === L'interprétation aristotélicienne : les homéomères === Toutefois, la question devient plus complexe lorsqu'on prend en considération les témoignages indirects, notamment ceux d'Aristote. Dans plusieurs passages, Aristote attribue à Anaxagore une doctrine des « homéomères » (τὰ ὁμοιομερῆ), c'est-à-dire des substances dont chaque partie est semblable au tout<ref>Aristote, ''Métaphysique'', I, 3, 984a11-16 ; Aristote, ''Génération et corruption'', I, 1, 314a18-24 (DK 59 A 46).</ref>. Les exemples donnés par Aristote incluent la chair, les os, le sang, la moelle, l'or, le bois — autant de substances naturelles que nous rencontrons dans l'expérience quotidienne. Cependant, le terme même « homéomère » n'apparaît nulle part dans les fragments conservés d'Anaxagore. C'est Aristote qui l'a introduit pour caractériser ce qu'il comprenait être la position d'Anaxagore<ref>Le terme ὁμοιομερής apparaît d'abord chez Aristote, ''Parties des animaux'', II, 1, 646b10-20 ; ''Génération et corruption'', I, 1, 314a18 sq. Cf. Pierre Pellegrin, « La théorie aristotélicienne des homéomères », ''Revue de Métaphysique et de Morale'', vol. 86, 1981, p. 449-467.</ref>. Cette discordance entre les fragments authentiques — qui mentionnent principalement des opposés — et l'interprétation aristotélicienne — qui privilégie les substances naturelles comme la chair et les os — a conduit à une longue controverse parmi les spécialistes modernes. Comme le souligne W. K. C. Guthrie, « le fait le plus gênant pour ceux qui souhaitent suivre Aristote est que le terme homéomères n'apparaît jamais dans les fragments d'Anaxagore lui-même, et que les fragments ne mentionnent jamais explicitement la chair, les os ou le sang comme des ingrédients élémentaires »<ref>W. K. C. Guthrie, 1965, p. 284-285.</ref>. L'interprétation « expansive », qui accepte pleinement le témoignage aristotélicien, a néanmoins été défendue par plusieurs spécialistes, notamment Aristotle L. Peck dans les années 1920 et 1930, et plus récemment par George Kerferd<ref>Aristotle L. Peck, « Anaxagoras : Predication as a Problem in Physics », ''Classical Quarterly'', vol. 25, 1931, p. 27-37 et p. 112-120 ; George B. Kerferd, « Anaxagoras and the Concept of Matter before Aristotle », ''Bulletin of the John Rylands Library'', vol. 52, 1969, p. 129-143.</ref>. === L'interprétation médiane === Une troisième voie, une interprétation « médiane », a été proposée notamment par Malcolm Schofield et Patricia Curd<ref>Malcolm Schofield, 1980, p. 99-138 ; Patricia Curd, 1998, p. 123-165 ; Patricia Curd, 2007, essais 2-4.</ref>. Selon cette lecture, l'ontologie d'Anaxagore comprend plusieurs catégories d'entités : les opposés, les substances naturelles fondamentales comme les métaux, la terre, l'air et l'éther, et les ingrédients biologiques comme la chair, le sang et les os. En revanche, les plantes, les animaux et leurs parties organiques ne seraient pas des éléments primordiaux mais des constructions naturelles résultant de l'agencement des ingrédients plus fondamentaux<ref>Patricia Curd, 2007, essais 2-3, p. 157-191 ; Malcolm Schofield, 1980, p. 132-138.</ref>. Cette interprétation médiane présente l'avantage de tenir compte à la fois des fragments authentiques et des témoignages d'Aristote, sans les opposer frontalement. Elle reconnaît que les opposés jouent un rôle cosmologique crucial — c'est leur séparation qui déclenche la formation du cosmos (fragments B12, B13, B15) —, tout en admettant que les substances naturelles ont également une place dans l'ontologie d'Anaxagore. === Les semences (σπέρματα) === Un élément supplémentaire de complexité est introduit par la mention des « semences » (σπέρματα) dans les fragments B4a et B4b. Dans le fragment B4b, après avoir mentionné les opposés, Anaxagore ajoute : « et il y avait beaucoup de terre présente, et des semences infinies en nombre, ne se ressemblant en rien les unes aux autres » (πολλὴ δὲ γῆ ἐνῆν καὶ σπέρματα ἄπειρα πλῆθος οὐδὲν ἐοικότα ἀλλήλοις)<ref>Anaxagore, fragment B4b, cité par Simplicius, ''Commentaire sur la Physique'', 34, 29-35, 9 (DK 59 B 4b).</ref>. Le fragment B4a est encore plus explicite : « Il est juste de penser qu'il y avait dans toutes les choses qui étaient rassemblées beaucoup de choses de toutes sortes, et des semences de toutes choses, possédant des formes et des couleurs et des saveurs de toute espèce »<ref>Anaxagore, fragment B4a, cité par Simplicius, ''Commentaire sur la Physique'', 34, 18-29 (DK 59 B 4a).</ref>. La nature exacte de ces semences a fait l'objet de débats considérables parmi les commentateurs. Gregory Vlastos a proposé que σπέρμα soit un terme technique introduit par Anaxagore pour désigner un agrégat infinitésimal contenant tous les ingrédients, mais dans lequel un seul prédomine<ref>Gregory Vlastos, 1950, p. 338-342 ; cf. également J. E. Raven, « The Basis of Anaxagoras' Cosmology », ''Classical Quarterly'', vol. 4, 1954, p. 123-137.</ref>. David Lloyd a suggéré que les semences soient des portions pures (ou quasi-pures) d'opposés<ref>David Lloyd, « Anaxagoras on Life and Mind », ''Phronesis'', vol. 14, 1969, p. 246-251, note 1.</ref>. Une position intermédiaire, défendue par Malcolm Schofield, David Sedley, Patricia Curd et Daniel Gershenson, suggère que les semences doivent être comprises littéralement comme des semences biologiques ordinaires — graines de plantes et semences animales<ref>Malcolm Schofield, 1980, p. 119-132 ; David Sedley, 2007, p. 24-26 ; Patricia Curd, 2007, essai 2 ; Daniel E. Gershenson et Daniel A. Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 15-17.</ref>. Cette dernière interprétation présente plusieurs avantages. Elle ne fait pas violence au langage : lorsqu'Anaxagore parle de σπέρματα, il utilise le terme ordinaire pour « semences » sans lui donner un sens technique inhabituel. Elle explique la diversité infinie des semences mentionnée dans B4b. Elle est en outre corroborée par des témoignages anciens sur la biologie d'Anaxagore : Théophraste rapporte qu'« Anaxagore dit que l'air contient des semences de toutes choses, et que celles-ci, lorsqu'elles sont emportées avec l'eau, engendrent les plantes »<ref>Théophraste, ''Causes des plantes'', I, 5, 2 (DK 59 A 117).</ref>. De même, Hippolyte rapporte qu'« au commencement, les animaux naquirent de l'humide, du chaud et du terreux, puis plus tard les uns des autres »<ref>Hippolyte, ''Réfutation de toutes les hérésies'', I, 8, 12 (DK 59 A 42).</ref>. == L'état originel : tout ensemble == Le traité d'Anaxagore s'ouvrait par l'une des déclarations les plus célèbres et les plus discutées de la philosophie présocratique : « Toutes choses étaient ensemble » (ὁμοῦ πάντα χρήματα ἦν, fragment B1)<ref>Anaxagore, fragment B1, cité par Simplicius, ''Commentaire sur la Physique'', 155, 23-26 (DK 59 B 1). Diogène Laërce, II, 6, rapporte qu'Anaxagore « commença son traité d'une manière très attrayante ».</ref>. Cette formule programmatique, placée en position d'ouverture, exprime la thèse cosmogonique fondamentale d'Anaxagore concernant l'état primordial de l'univers. Plusieurs commentateurs, notamment G. E. L. Owen, ont souligné que la formulation d'Anaxagore constitue une réponse directe et délibérée à Parménide<ref>G. E. L. Owen, « The Place of the Timaeus in Plato's Dialogues », ''Classical Quarterly'', vol. 3, 1953, p. 79-95 ; repris dans ''Logic, Science and Dialectic'', Londres, Duckworth, 1986, p. 65-84.</ref>. Là où Parménide avait affirmé que l'être « est maintenant, tout ensemble, un, continu » (νῦν ἔστιν ὁμοῦ πᾶν, ἕν, συνεχές)<ref>Parménide, fragment B8, 5-6 (DK 28 B 8).</ref>, Anaxagore proclame que « toutes choses étaient ensemble ». Parménide affirmait l'unicité, la continuité, l'éternité présente et l'immobilité de l'être ; Anaxagore lui oppose la pluralité, la diversibilité infinie, l'existence passée et le devenir cosmogonique<ref>Malcolm Schofield, 1980, p. 64-65 ; Patricia Curd, 1998, p. 123-142.</ref>. === La description du mélange originel === Le fragment B1, dont Simplicius nous dit qu'il se trouvait près du début du traité d'Anaxagore, fournit une description plus complète de cet état primordial : <blockquote>Toutes choses étaient ensemble, illimitées et en multitude et en petitesse — car le petit aussi était illimité. Et toutes choses étant ensemble, rien n'était manifeste en raison de la petitesse. Car l'air et l'éther recouvraient toutes choses, tous deux étant illimités ; car ce sont eux qui sont les plus grands dans la totalité des choses, et en multitude et en grandeur.<ref>Anaxagore, fragment B1, cité par Simplicius, ''Commentaire sur la Physique'', 155, 23-157, 4 (DK 59 B 1).</ref></blockquote> Ce passage dense pose plusieurs problèmes interprétatifs considérables qui ont occupé les commentateurs depuis l'Antiquité. Quatre caractéristiques principales du mélange originel y sont énoncées : toutes choses étaient ensemble ; elles étaient illimitées en multitude et en petitesse ; rien n'était manifeste en raison de la petitesse ; l'air et l'éther recouvraient toutes choses. L'expression « illimitées en multitude et en petitesse » a suscité d'intenses débats interprétatifs. Deux lectures principales s'affrontent, que Malcolm Schofield a désignées respectivement comme l'interprétation « particulaire » et l'interprétation « proportionnelle »<ref>Malcolm Schofield, 1980, p. 70-99.</ref>. L'interprétation particulaire comprend « illimitées en multitude » comme signifiant qu'il existait un nombre infini de choses distinctes dans le mélange originel<ref>Gregory Vlastos, 1950, p. 31-57 ; W. K. C. Guthrie, 1965, p. 277-285 ; David Sider, 2005, p. 56-62.</ref>. L'interprétation proportionnelle, en revanche, refuse de comprendre le mélange originel comme une collection de particules discrètes : « illimitées en petitesse » signifierait plutôt que chaque ingrédient était présent dans le mélange en une proportion infiniment petite par rapport à la totalité<ref>Malcolm Schofield, 1980, p. 75-89 ; Colin Strang, 1963, p. 101-118 ; Jonathan Barnes, 1982, p. 39-53.</ref>. Le débat entre ces deux interprétations n'est pas résolu, et chacune présente des avantages et des difficultés<ref>Pour une discussion équilibrée des deux positions, voir Patricia Curd, 2007, essai 4, p. 192-213 ; Daniel Graham, 2006, p. 137-152.</ref>. === L'imperceptibilité du mélange === La troisième caractéristique du mélange originel est son indistinction : « rien n'était manifeste » (οὐδὲν ἔνδηλον ἦν). Anaxagore explique cette imperceptibilité par deux facteurs complémentaires : « en raison de la petitesse » (ὑπὸ σμικρότητος), et parce que « l'air et l'éther recouvraient toutes choses » (πάντα γὰρ ἀὴρ καὶ αἰθὴρ κατεῖχε). L'air (ἀήρ) désigne ici, conformément à l'usage ionien archaïque, une substance sombre, humide, froide et dense<ref>Anaximène, fragments DK 13 A 7, B 2 ; cf. Charles Kahn, ''Anaximander and the Origins of Greek Cosmology'', New York, Columbia University Press, 1960, p. 133-162 ; Daniel Graham, 2006, p. 93-102.</ref>. L'éther (αἰθήρ), en revanche, désigne la substance lumineuse, chaude, sèche et rare<ref>Anaxagore, B15. Cf. W. K. C. Guthrie, 1965, p. 301-304 ; Malcolm Schofield, 1980, p. 81-83.</ref>. Ces deux substances, affirme Anaxagore, « sont les plus grandes dans la totalité des choses, et en multitude et en grandeur ». Cela signifie qu'elles étaient présentes dans le mélange originel en proportions largement supérieures à tous les autres ingrédients. Le fragment B4b confirme cette description : <blockquote>Mais avant que ces choses ne fussent séparées, lorsque toutes choses étaient ensemble, aucune couleur n'était manifeste. Car le mélange de toutes choses l'empêchait — du sec et de l'humide, du chaud et du froid, du lumineux et de l'obscur, et de beaucoup de terre étant présente, et de semences illimitées en multitude, ne se ressemblant en rien les unes aux autres.<ref>Anaxagore, fragment B4b (DK 59 B 4b).</ref></blockquote> Ce passage confirme que l'indistinction du mélange originel n'était pas due à l'absence des ingrédients, mais à leur mélange si intime que leurs caractéristiques respectives se neutralisaient mutuellement. === L'immobilité originelle === Plusieurs témoignages anciens rapportent qu'avant l'intervention du Noûs, le mélange universel était au repos<ref>Aristote, ''Physique'', VIII, 1, 250b24-26 (DK 59 A 64).</ref>. Cette immobilité primordiale pose un problème philosophique considérable : si le mélange était éternellement au repos, qu'est-ce qui a pu le mettre en mouvement ? Anaxagore répond en postulant l'existence du Noûs (Νοῦς, Intellect ou Esprit), une entité distincte de toutes les substances matérielles, qui possède le pouvoir d'initier le mouvement<ref>Anaxagore, B12.</ref>. Mais cette réponse soulève elle-même de nouvelles difficultés, qui seront examinées plus loin. === L'étendue du mélange originel === Le fragment B1 affirme que l'air et l'éther « tous deux étaient illimités » (ἄμφω ἄπειρα ἐόντα), et le fragment B2 précise : « L'air et l'éther se séparent de la multitude environnante, et la multitude environnante est illimitée en quantité »<ref>Anaxagore, fragment B2 (DK 59 B 2).</ref>. Ces affirmations suggèrent que le mélange originel était spatialement infini<ref>Aristote, ''Physique'', III, 4, 203a19-23 (DK 59 A 43).</ref>. Le fragment B12 indique que « le Noûs commença à exercer son pouvoir à partir d'un petit commencement, puis la rotation s'étendit sur une région plus grande, et s'étendra sur une région plus grande encore »<ref>Anaxagore, B12 (DK 59 B 12).</ref>. Cela suggère que le processus cosmogonique, bien qu'il soit en cours depuis un temps considérable, n'a pas encore affecté la totalité du mélange infini. Cette conception d'un univers partiellement ordonné, où la cosmogonie est encore en cours dans les régions périphériques tandis que notre monde déjà structuré occupe une région centrale, est l'une des idées les plus originales d'Anaxagore<ref>W. K. C. Guthrie, 1965, p. 327-331 ; Patricia Curd, 2007, essai 5, p. 214-237 ; Daniel Graham, 2006, p. 148-152.</ref>. == Le Noûs : l'Intellect cosmique == Le fragment B12 d'Anaxagore, le plus long et le plus célèbre de tous les fragments conservés, est entièrement consacré au Noûs (Νοῦς, « Intellect » ou « Esprit »). Ce passage, d'une grandeur solennelle et d'une intensité remarquable, constitue l'un des textes les plus puissants de toute la prose grecque archaïque<ref>Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 3-32 ; Karl Deichgräber, « Anaxagoras Stil », ''Philologus'', vol. 87, 1933, p. 15-26.</ref>. Dans ce fragment, Anaxagore expose sa doctrine du Noûs comme principe du mouvement cosmogonique et source d'ordre dans l'univers. Le Noûs représente l'innovation philosophique la plus originale d'Anaxagore<ref>W. K. C. Guthrie, 1965, p. 272-279 ; Diogène Laërce, II, 6 (DK 59 A 1), rapporte qu'Anaxagore était surnommé « Monsieur Intellect » (ὁ Νοῦς) en raison de l'importance centrale qu'il donnait à cette notion.</ref>. Il convient d'emblée de préciser, afin d'éviter un contresens répandu, que le Noûs d'Anaxagore est d'abord une ''cause motrice'' du cosmos : il initie et contrôle le mouvement rotatoire qui produit la séparation des ingrédients. Anaxagore ne dit nulle part, dans les fragments conservés, que l'Intellect ordonne les choses ''parce qu'il serait meilleur qu'elles soient ainsi'' — c'est précisément ce que Socrate et Platon auraient voulu trouver chez lui, et dont ils lui reprocheront l'absence. La lecture proprement « téléologique » du Noûs est donc une projection rétrospective due à Platon et à la tradition postérieure, qu'il faut distinguer soigneusement de la doctrine d'Anaxagore elle-même. Appliquer sans précaution la distinction aristotélicienne des quatre causes (efficiente, finale, etc.) au Noûs anaxagoréen relève d'un anachronisme conceptuel qu'il vaut mieux éviter<ref>Sur ce point, voir Patricia Curd, « Anaxagoras », ''Stanford Encyclopedia of Philosophy'', 2007 (révision substantielle 2019), section sur le Noûs ; Malcolm Schofield, 1980, p. 55-70 ; James Lesher, « Mind's Knowledge and Powers of Control in Anaxagoras », ''Phronesis'', vol. 40, 1995, p. 125-142. Sur l'anachronisme que représente l'application des quatre causes aristotéliciennes aux physiciens présocratiques, voir l'article « Presocratic Philosophy », ''Stanford Encyclopedia of Philosophy''.</ref>. === La séparation du Noûs d'avec toutes choses === Le fragment B12 s'ouvre par une affirmation qui constitue la thèse fondamentale d'Anaxagore concernant le Noûs : <blockquote>Les autres choses ont une part de tout, mais le Noûs est illimité et autonome, et il n'a été mélangé à aucune chose, mais il existe seul, lui-même par lui-même.<ref>Anaxagore, fragment B12, cité par Simplicius, ''Commentaire sur la Physique'', 156, 13-15 (DK 59 B 12).</ref></blockquote> Cette phrase affirme que le Noûs constitue une exception au principe universel « tout est dans tout ». Tandis que toutes les substances matérielles contiennent en elles-mêmes des portions de toutes les autres substances, le Noûs, lui, demeure entièrement pur et séparé<ref>Anaxagore, B11 : « Dans toute chose il y a une part de toute chose, excepté le Noûs ; et il y a certaines choses dans lesquelles il y a aussi du Noûs ».</ref>. Trois attributs sont explicitement prédiqués du Noûs : il est « illimité » (ἄπειρον), « autonome » (αὐτοκρατές), et « non mélangé à aucune chose ». Anaxagore justifie cette séparation par un argument remarquable : <blockquote>Car s'il n'existait pas par lui-même, mais s'il avait été mélangé à quelque autre chose, il participerait de toutes les choses, s'il avait été mélangé à quoi que ce soit. Car dans toute chose il y a une part de toute chose, comme je l'ai dit précédemment. Et les choses mélangées avec lui l'empêcheraient, de sorte qu'il ne dominerait aucune chose de la même manière qu'il la domine en fait, étant seul par lui-même.<ref>Anaxagore, B12, lignes 156, 15-20.</ref></blockquote> L'argument procède ainsi : si le Noûs était mélangé à quoi que ce soit, il contiendrait une part de tout ; les substances mélangées l'empêcheraient d'exercer son pouvoir de contrôle ; or le Noûs exerce effectivement ce pouvoir ; donc le Noûs n'est mélangé à aucune chose<ref>Malcolm Schofield, 1980, p. 12-14 ; Jonathan Barnes, 1982, p. 375-377.</ref>. Plusieurs lectures de la prémisse cruciale — pourquoi le mélange empêcherait-il l'action du Noûs — ont été proposées et ne sont pas incompatibles<ref>Patricia Curd, 2007, essai 5, p. 220-225 ; Christian Vassallo, « Nous, Motion, and Teleology in Anaxagoras », ''Oxford Studies in Ancient Philosophy'', vol. 50, 2016, p. 1-32, aux pages 8-18.</ref>. === Les attributs du Noûs === Après avoir établi la séparation du Noûs, Anaxagore énonce une série d'attributs qui caractérisent sa nature. Le style employé ici est celui de la « prédication solennelle » (feierliche Prädikation), identifié par Karl Deichgräber comme caractéristique du style hymnico-religieux archaïque<ref>Karl Deichgräber, 1933, p. 16-22 ; Eduard Norden, ''Agnostos Theos'', Leipzig, Teubner, 1913, p. 3-29 ; Malcolm Schofield, 1980, p. 4-9.</ref> : <blockquote>Car il est le plus fin de toutes choses et le plus pur, et il possède toute connaissance à l'égard de toute chose et il a la plus grande force ; et toutes les choses qui ont une âme, les plus grandes comme les plus petites, toutes le Noûs les domine.<ref>Anaxagore, B12, lignes 156, 20-24.</ref></blockquote> Le premier attribut — « le plus fin » et « le plus pur » — a suscité des interprétations divergentes depuis l'Antiquité. Certains y voient l'affirmation, encore en gestation conceptuelle, de l'immatérialité du Noûs<ref>Aristote, ''De l'âme'', III, 4, 429a18-24 ; W. K. C. Guthrie, 1965, p. 278-279.</ref> ; d'autres, s'appuyant sur le fait qu'Anaxagore utilise λεπτός ailleurs pour décrire l'eau de mer, soutiennent que ces termes doivent être pris en un sens physique : le Noûs serait une substance matérielle extrêmement fine et pure<ref>Théophraste, dans Simplicius, ''Commentaire sur la Physique'', 27, 2-3 (DK 59 A 41) ; John Burnet, 1930, p. 268-269 ; Daniel Gershenson et Daniel Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 27-33.</ref>. Une position médiane suggère qu'Anaxagore n'avait pas encore développé le concept d'incorporéité dans sa pleine rigueur, mais cherchait néanmoins à exprimer que le Noûs est d'une nature fondamentalement différente des autres substances<ref>Malcolm Schofield, 1980, p. 18-22.</ref>. Le deuxième attribut — « il possède toute connaissance à l'égard de toute chose » — affirme une forme d'omniscience du Noûs. Le troisième — « il a la plus grande force » — exprime sa puissance, liée à sa connaissance<ref>Malcolm Schofield, 1980, p. 22-24.</ref>. Le quatrième — le contrôle du Noûs sur tous les êtres vivants — est cohérent avec le fragment B11, qui précise qu'il y a du Noûs dans certaines choses<ref>Anaxagore, B11. Cf. Malcolm Schofield, 1980, p. 25-27 ; Patricia Curd, 2007, essai 5, p. 225-230.</ref>. === Le rôle cosmogonique du Noûs === Après avoir décrit la nature du Noûs, Anaxagore expose son rôle dans la cosmogonie : <blockquote>Et le Noûs a dominé la révolution entière, de sorte qu'elle a commencé à tourner au commencement. Et d'abord elle a commencé à tourner à partir d'une petite région, mais elle tourne sur une région plus grande, et elle tournera sur une région plus grande encore. Et toutes les choses qui étaient mélangées ensemble et séparées et distinguées, toutes le Noûs les a connues. Et toutes les choses qui devaient être — celles qui étaient et celles qui sont maintenant et celles qui seront —, toutes le Noûs les a mises en ordre, et aussi cette révolution dans laquelle tournent maintenant les astres et le soleil et la lune et l'air et l'éther qui sont en train d'être séparés.<ref>Anaxagore, B12, lignes 156, 24-157, 3.</ref></blockquote> Ce passage affirme trois choses concernant l'activité cosmogonique du Noûs : il a initié un mouvement de révolution (περιχώρησις) dans le mélange originel ; ce mouvement, commencé dans une petite région, s'est étendu progressivement et continue de s'étendre ; c'est ce mouvement qui a produit la séparation (ἀπόκρισις) et la distinction (διάκρισις) des ingrédients, donnant naissance au cosmos ordonné que nous observons<ref>Simplicius, ''Commentaire sur la Physique'', 300, 31-301, 1 ; Aristote, ''Physique'', VIII, 1, 250b24-252a5 (DK 59 A 64).</ref>. Le mécanisme cosmogonique proprement dit est mécanique : c'est le mouvement rotatoire qui, par sa force centrifuge, sépare les substances denses et les substances rares<ref>Anaxagore, B12, B13, B15, B16.</ref>. Le Noûs n'intervient pas directement dans chaque détail de la cosmogonie ; il initie le mouvement rotatoire, et celui-ci produit ensuite mécaniquement la séparation et la réorganisation des substances<ref>Malcolm Schofield, 1980, p. 27-32 ; Daniel Graham, 2006, p. 148-152.</ref>. Le texte affirme cependant aussi que « le Noûs a mis en ordre » (διεκόσμησε νοῦς) toutes choses passées, présentes et futures. Le verbe διακοσμεῖν signifie « mettre en ordre », « arranger ». Il ne faut pas en conclure trop vite à une téléologie au sens fort (comme si les choses étaient disposées en vue du bien) : Anaxagore ne fournit pas, dans les fragments qui nous sont parvenus, un tel principe évaluatif. Le Noûs connaît et dispose — il est intelligent et ordonnateur — mais rien n'indique qu'il ordonne les choses parce qu'elles seraient ainsi « meilleures ». C'est précisément ce point qui sera au cœur de la critique platonicienne<ref>Christian Vassallo, 2016, a défendu l'idée qu'il y aurait malgré tout une dimension téléologique faible chez Anaxagore ; cette lecture reste minoritaire et contestée. Voir au contraire la synthèse de Patricia Curd, ''SEP'' 2007 (révisée 2019).</ref>. === La critique platonicienne et aristotélicienne === Dans le ''Phédon'', Platon fait raconter par Socrate sa déception à la lecture du livre d'Anaxagore : <blockquote>Un jour, j'entendis quelqu'un lire dans un livre d'Anaxagore, disant que c'est l'Intellect qui met tout en ordre et qui est la cause de toutes choses. Je fus ravi de cette cause, et il me sembla qu'il était en quelque sorte bon que l'Intellect fût la cause de tout ; et je pensai que, s'il en est ainsi, l'Intellect qui met tout en ordre doit tout ordonner et disposer chaque chose de la manière qui est la meilleure. [...] Mais cette merveilleuse espérance, mon ami, me fut enlevée lorsque, progressant dans ma lecture, je vis que cet homme ne fait aucun usage de l'Intellect, qu'il ne lui attribue aucune responsabilité dans la mise en ordre des choses, mais qu'il allègue comme causes l'air, l'éther, l'eau et beaucoup d'autres choses absurdes.<ref>Platon, ''Phédon'', 97b-98c (DK 59 A 47).</ref></blockquote> Ce passage est capital, et il faut en saisir exactement la portée. Socrate ne dit pas qu'Anaxagore avait effectivement proposé une explication par le « meilleur » et y aurait manqué dans le détail : il dit qu'il ''espérait'' trouver une telle explication, mais qu'il fut déçu. La formule « doit tout ordonner et disposer chaque chose de la manière qui est la meilleure » exprime donc l'attente socratique, non la doctrine anaxagoréenne elle-même. Anaxagore, dans les fragments, n'a jamais soutenu que le Noûs ordonne les choses parce qu'il serait meilleur qu'elles soient ainsi. La lecture qui fait du Noûs anaxagoréen une « cause téléologique » ou une « cause finale » est donc une reconstruction rétrospective, qui projette sur Anaxagore les exigences platoniciennes et aristotéliciennes<ref>Malcolm Schofield, 1980, p. 55-70 ; James Lesher, 1995, p. 125-142 ; Patricia Curd, ''SEP'', 2007 (révisée 2019).</ref>. Aristote reprend la critique dans la ''Métaphysique'' : <blockquote>Anaxagore utilise l'Intellect comme un ''deus ex machina'' pour la fabrication du monde ; et quand il est embarrassé pour expliquer pourquoi quelque chose est nécessairement ainsi, il le fait intervenir. Mais dans tous les autres cas, il allègue comme causes toutes sortes de choses plutôt que l'Intellect.<ref>Aristote, ''Métaphysique'', I, 4, 985a18-21 (DK 59 A 47).</ref></blockquote> Le reproche d'Aristote, comme celui de Socrate-Platon, présuppose un idéal explicatif téléologique qui n'est pas celui d'Anaxagore. Il ne faut donc pas conclure qu'Anaxagore « introduit le Noûs comme cause téléologique puis échoue à l'utiliser » : il introduit le Noûs comme cause motrice et ordonnatrice, et ses critiques l'accusent de ne pas avoir ''aussi'' développé une explication par le meilleur qu'eux attendent. La distinction est importante. === Synthèse : l'innovation du Noûs === L'introduction du Noûs par Anaxagore constitue une innovation philosophique à plusieurs titres. Premièrement, Anaxagore est, parmi les penseurs grecs dont nous avons conservé les doctrines, le premier à poser explicitement l'existence d'une entité qui, bien qu'elle agisse sur la matière, n'est mélangée à aucune substance matérielle<ref>W. K. C. Guthrie, 1965, p. 278-279 ; Daniel Gershenson et Daniel Greenberg, 1964, p. 27-33.</ref>. Cette conception préparera, sans y être pour autant équivalente, les doctrines ultérieures de l'âme immatérielle chez Platon et de l'intellect séparé chez Aristote<ref>Platon, ''Phédon'', 78b-84b ; Aristote, ''De l'âme'', III, 5, 430a10-25. Cf. Edward Hussey, ''The Presocratics'', Londres, Duckworth, 1972, p. 138-141.</ref>. Deuxièmement, Anaxagore identifie une cause unique pour le mouvement cosmique et l'ordre qui en résulte<ref>Aristote, ''Métaphysique'', I, 3, 984b15-20, qualifie cette innovation de « sobre ».</ref>. En désignant le principe cosmique par le terme Νοῦς — mot habituellement associé à l'intelligence et à la pensée —, Anaxagore suggère que l'ordre du monde n'est pas aveugle : il y a dans le cosmos quelque chose d'intelligent qui connaît et qui dispose. C'est cette suggestion qui sera reprise et radicalisée par Socrate, Platon et Aristote, au prix d'une transformation dont il faut reconnaître qu'elle va au-delà de ce qu'affirme Anaxagore lui-même<ref>Malcolm Schofield, 1980, p. 3-32 ; W. K. C. Guthrie, 1965, p. 327-331.</ref>. == La cosmogonie et la cosmologie == La cosmogonie et la cosmologie d'Anaxagore constituent l'application de ses principes métaphysiques. Dans ces domaines, Anaxagore se montre à la fois héritier de la tradition ionienne et novateur, proposant des explications naturalistes des phénomènes célestes et météorologiques<ref>W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 301-331 ; Daniel Gershenson et Daniel Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 34-55.</ref>. === Le mouvement rotatoire cosmogonique === Le processus cosmogonique commence lorsque le Noûs initie un mouvement de rotation (περιχώρησις) dans le mélange originel<ref>Anaxagore, fragment B12 (DK 59 B 12).</ref>. Ce mouvement rotatoire ne doit pas être conçu comme une simple rotation uniforme de l'ensemble du mélange, mais comme un tourbillon (δῖνος) ou un vortex dont la vitesse et l'étendue augmentent progressivement<ref>Aristote, ''Du Ciel'', II, 13, 295a9-14 (DK 59 A 88).</ref>. Anaxagore affirme que ce mouvement initial était d'une rapidité extraordinaire : « Rien de ce qui existe maintenant chez les hommes n'est aussi rapide, mais [il était] certainement plusieurs fois plus rapide »<ref>Anaxagore, fragment B9 (DK 59 B 9).</ref>. Le mécanisme de la séparation cosmogonique est mécanique. Le mouvement rotatoire produit une force centrifuge qui pousse les substances rares vers la périphérie et attire les substances denses vers le centre<ref>Anaxagore, B12, B15 ; Aristote, ''Du Ciel'', III, 2, 300b1-8 (DK 59 A 88).</ref>. Le fragment B15 décrit ce processus : <blockquote>Le dense et l'humide et le froid et l'obscur se rassemblèrent ici, là où maintenant est la terre, tandis que le rare et le chaud et le sec se retirèrent vers les régions lointaines de l'éther.<ref>Anaxagore, fragment B15 (DK 59 B 15).</ref></blockquote> Ce processus de séparation n'est jamais achevé. Conformément au principe que « rien n'est complètement séparé » (fragment B8), la rotation continue indéfiniment à produire des séparations et des mélanges partiels<ref>Anaxagore, B8, B12.</ref>. === La formation de la terre === Au centre du tourbillon cosmique, les substances denses, humides, froides et obscures se sont concentrées pour former la terre<ref>Anaxagore, B15 ; Aristote, ''Du Ciel'', III, 2, 300b8-16 (DK 59 A 88).</ref>. Selon Anaxagore, la terre a la forme d'un disque plat<ref>Hippolyte, ''Réfutation de toutes les hérésies'', I, 8, 3 (DK 59 A 42) ; Aristote, ''Du Ciel'', II, 13, 294b13-15 (DK 59 A 88).</ref>, conception traditionnelle dans la cosmologie ionienne<ref>Charles Kahn, ''Anaximander and the Origins of Greek Cosmology'', New York, Columbia University Press, 1960, p. 76-116 ; Daniel Graham, 2006, p. 93-102.</ref>. Anaxagore répond à la question de la stabilité de la terre en affirmant qu'elle demeure immobile parce qu'elle repose sur l'air (ἀήρ) qui la supporte<ref>Aristote, ''Du Ciel'', II, 13, 294b13-21 (DK 59 A 88) : « Anaxagore dit que la terre demeure immobile en raison de son égalité et de la grandeur, car elle ne coupe pas l'air mais le couvre comme un couvercle ».</ref>. Anaxagore aurait même effectué des démonstrations expérimentales avec des clepsydres pour montrer que l'air possède une résistance élastique capable de supporter des corps<ref>Aristote, ''Physique'', IV, 6, 213a22-27 (DK 59 A 68) ; Daniel Gershenson et Daniel Greenberg, 1964, p. 40-43.</ref>. === La formation des corps célestes === Les corps célestes — soleil, lune, étoiles — se sont formés à partir des substances rares, chaudes et sèches projetées vers la périphérie par la force du tourbillon cosmique<ref>Anaxagore, B15 ; Hippolyte, DK 59 A 42.</ref>. Cette théorie affirmait que les astres ne sont pas des êtres divins mais des masses de pierre ou de métal incandescent<ref>Platon, ''Apologie de Socrate'', 26d (DK 59 A 35).</ref>. Anaxagore soutenait que le soleil est une masse de pierre ou de métal incandescent<ref>Platon, ''Apologie'', 26d (DK 59 A 35) ; Hippolyte, DK 59 A 42 ; Diogène Laërce, II, 8 (DK 59 A 1) ; Aétius, II, 20, 6 (DK 59 A 72).</ref>. Quant à sa taille, Anaxagore estimait qu'« il est plus grand que le Péloponnèse »<ref>Aétius, II, 21, 3 (DK 59 A 72) ; Plutarque, ''Vie de Périclès'', 6, 2 (DK 59 A 1).</ref> — affirmation qui devait paraître extravagante aux Grecs de son époque. La lune, selon Anaxagore, est également un corps rocheux, semblable à la terre<ref>Platon, ''Apologie'', 26d ; Hippolyte, DK 59 A 42.</ref>. Mais contrairement au soleil, la lune ne produit pas sa propre lumière : elle brille par réflexion de la lumière solaire<ref>Plutarque, ''Contre Colotès'', 1116a-b (DK 59 A 77) ; Hippolyte, DK 59 A 42.</ref>. Cette découverte remarquable permit à Anaxagore d'expliquer correctement les phases de la lune et les éclipses<ref>Hippolyte, DK 59 A 42 ; Aétius, II, 29, 6 (DK 59 A 77).</ref>. Une éclipse de lune se produit lorsque la terre s'interpose entre le soleil et la lune, projetant son ombre sur celle-ci<ref>Hippolyte, DK 59 A 42. Cf. Dirk Couprie, « Anaxagoras on the Milky Way and Lunar Eclipses », ''Rhizomata'', vol. 5, 2017, p. 127-147.</ref>. Une éclipse de soleil se produit lorsque la lune s'interpose entre le soleil et la terre<ref>Hippolyte, DK 59 A 42 ; Plutarque, ''Vie de Périclès'', 35, 2 (DK 59 A 18).</ref>. Ces explications, fondées sur une compréhension correcte de la géométrie des positions relatives du soleil, de la terre et de la lune, représentent une avancée majeure dans l'histoire de l'astronomie<ref>W. K. C. Guthrie, 1965, p. 313-316 ; Daniel Graham, 2006, p. 149-150.</ref>. Selon la tradition, Anaxagore aurait prédit l'éclipse de soleil qui eut lieu le 3 mai 463 avant notre ère<ref>Plutarque, ''Vie de Périclès'', 35, 2 (DK 59 A 18). Certains historiens sont toutefois sceptiques quant à la possibilité qu'Anaxagore ait pu effectuer une prédiction aussi précise. Cf. W. K. C. Guthrie, 1965, p. 314, note 1 ; Daniel Graham, 2006, p. 150, note 25.</ref>. Anaxagore soutenait aussi que la surface de la lune présente des irrégularités — montagnes et vallées — semblables à celles de la terre<ref>Aétius, II, 30, 2 (DK 59 A 77).</ref>. La Voie lactée s'explique selon Anaxagore par l'ombre que projette la terre dans l'espace : dans les régions du ciel situées dans l'ombre de la terre, le soleil ne peut éclairer les étoiles ; celles-ci deviennent donc toutes visibles, même les plus faibles<ref>Aristote, ''Météorologiques'', I, 8, 345a25-31 (DK 59 A 80). Cf. Dirk Couprie, 2017.</ref>. === Le météorite d'Aigos Potamos === L'événement qui contribua à établir la réputation d'Anaxagore fut la chute d'un météorite de grande taille à Aigos Potamos, sur la rive européenne de l'Hellespont, vers 467 avant notre ère<ref>Pline l'Ancien, ''Histoire naturelle'', II, 149 (DK 59 A 11) ; Plutarque, ''Vie de Lysandre'', 12 (DK 59 A 12) ; Diogène Laërce, II, 10 (DK 59 A 1).</ref>. Selon les témoignages anciens, Anaxagore aurait prédit cette chute. Les historiens modernes sont divisés sur cette question : certains pensent qu'il s'agit d'une légende élaborée après coup, d'autres estiment qu'Anaxagore avait peut-être observé qu'un objet céleste se fragmentait<ref>Daniel W. Graham et Eric Hintz, « Anaxagoras and the Comet », ''Apeiron'', vol. 40, 2007, p. 1-20 ; Evangelos Th. Theodossiou et al., « The Fall of a Meteorite at Aegos Potami in 467/6 BC », ''Journal of Astronomical History and Heritage'', vol. 5, 2002, p. 135-140.</ref>. Quoi qu'il en soit, la chute du météorite fut associée au nom d'Anaxagore et parut confirmer sa théorie selon laquelle les corps célestes sont faits de pierre. === Météorologie === Anaxagore consacra une partie considérable de son traité à l'explication des phénomènes météorologiques. Les nuages se forment par évaporation de l'eau sous l'effet de la chaleur solaire<ref>Aétius, III, 4, 1 (DK 59 A 82).</ref>. La pluie provient de la condensation de la vapeur d'eau contenue dans les nuages<ref>Anaxagore, fragment B16 (DK 59 B 16).</ref>. La formation de la grêle posait un problème particulier : comment de la glace peut-elle se former en été ? Anaxagore proposa une explication ingénieuse : lors des journées très chaudes, des courants d'air ascendants peuvent pousser les nuages à des altitudes très élevées, où l'air est suffisamment froid pour que l'eau gèle<ref>Aétius, III, 4, 1 (DK 59 A 84) ; Aristote, ''Météorologiques'', I, 12, 348b23-349a11 (critique de la théorie d'Anaxagore).</ref>. Cette théorie, bien que partiellement erronée dans ses détails, témoigne d'une compréhension correcte du principe de convection. Le tonnerre et l'éclair sont causés, selon Anaxagore, par la chute de l'éther dans les nuages<ref>Aétius, III, 3, 3 (DK 59 A 84) ; Hippolyte, DK 59 A 42.</ref>. Cette explication reconnaît correctement que l'éclair précède le tonnerre, la vue étant plus rapide que l'ouïe<ref>Sénèque, ''Questions naturelles'', II, 22 (citant Anaxagore).</ref>. Les tremblements de terre sont causés, selon Anaxagore, par de l'éther chaud piégé sous la surface de la terre<ref>Aristote, ''Météorologiques'', II, 7, 365a19-21 (DK 59 A 89).</ref>. === Hydrologie et géologie === Anaxagore proposa une explication de la crue annuelle du Nil : les crues estivales sont causées par la fonte des neiges dans les régions montagneuses situées à la source du fleuve<ref>Diodore de Sicile, I, 38, 4 (attribution explicite à Anaxagore) ; Daniel Gershenson et Daniel Greenberg, 1964, p. 54.</ref>. Cette explication est essentiellement correcte. La mer, selon Anaxagore, existait dès le début, mais sa salinité actuelle provient de l'évaporation de l'eau douce sous l'effet du soleil<ref>Hippolyte, DK 59 A 42.</ref>. === Synthèse : la cosmologie naturaliste d'Anaxagore === La cosmologie d'Anaxagore se caractérise par trois traits fondamentaux. Premièrement, l'unité de la nature : Anaxagore affirme que les corps célestes sont constitués des mêmes substances que la terre et obéissent aux mêmes lois physiques<ref>Hippolyte, DK 59 A 42 ; Daniel Gershenson et Daniel Greenberg, 1964, p. 23-26, 47-48.</ref>. Il n'existe pas de différence ontologique entre le monde sublunaire et le monde supralunaire, contrairement à ce qu'affirmera plus tard Aristote<ref>Aristote, ''Du Ciel'', I, 2-3, 268b11-270b25.</ref>. Deuxièmement, l'explication mécanique : tous les phénomènes cosmologiques et météorologiques sont expliqués par des processus physiques — rotation, séparation par densité, évaporation, condensation —, sans recours à des agents divins<ref>Daniel Gershenson et Daniel Greenberg, 1964, p. 23-33.</ref>. Troisièmement, l'usage systématique de l'analogie : Anaxagore explique les phénomènes cosmiques par analogie avec des phénomènes terrestres observables<ref>Daniel Gershenson et Daniel Greenberg, 1964, p. 7-12.</ref>. Ces traits donnent à la cosmologie d'Anaxagore un caractère naturaliste marqué, qui représente une étape importante dans le développement d'une explication physique de la nature. Il convient toutefois de ne pas surestimer sa portée : parler d'Anaxagore comme d'un « fondateur de la méthode scientifique » (selon une expression qu'on trouve parfois dans la littérature) relève d'une certaine emphase rétrospective, qu'il vaut mieux tempérer en parlant plutôt d'un jalon dans une longue histoire où se combinent intuitions fécondes et erreurs caractéristiques de l'époque. == La physiologie et la biologie == Bien qu'Anaxagore soit surtout connu pour ses théories cosmologiques et météorologiques, les témoignages anciens indiquent qu'il consacra également une partie considérable de son traité à l'explication des phénomènes biologiques et physiologiques<ref>Daniel Gershenson et Daniel Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 55-57 ; W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 316-320.</ref>. === La théorie de la nutrition === L'une des questions biologiques les plus importantes qu'Anaxagore chercha à élucider est celle de la nutrition : comment l'alimentation se transforme-t-elle en chair, en os, en cheveux, et en toutes les autres parties du corps ? Selon les témoignages d'Aristote et de la tradition doxographique, Anaxagore observa que les êtres humains et les animaux se nourrissent d'aliments relativement simples — pain, eau — et que de ces aliments proviennent toutes les parties complexes et diversifiées de leur corps<ref>Simplicius, ''Commentaire sur la Physique'', 27, 2-11 (DK 59 A 45) : « Car comment, disait-il, des cheveux pourraient-ils provenir de ce qui n'est pas cheveu, et de la chair de ce qui n'est pas chair ? ». Cf. Aétius, I, 3, 5 (DK 59 A 46) ; Lucrèce, ''De la nature'', I, 834-838.</ref>. Cette observation posait un problème philosophique considérable au regard de l'interdit parménidien. La réponse d'Anaxagore était cohérente avec sa métaphysique générale : le pain doit déjà contenir de la chair, du sang, des os, des cheveux, et toutes les autres substances corporelles, bien que ces constituants y soient présents en quantités si infimes qu'ils demeurent imperceptibles<ref>Simplicius, ''Commentaire sur la Physique'', 460, 4-12 (DK 59 A 45).</ref>. Lorsque nous mangeons du pain, le corps extrait du pain les particules de chair qu'il contient déjà, et les ajoute à la chair existante<ref>Aristote, ''Génération des animaux'', I, 18, 723a6-11 (DK 59 A 45).</ref>. Cette théorie soulève évidemment d'autres difficultés : comment le corps sait-il extraire précisément les particules de chair du pain, et les diriger vers les muscles plutôt que vers les os ? Anaxagore attribuait cette fonction au Noûs présent dans chaque organisme vivant<ref>Cf. section « Le Noûs : l'Intellect cosmique ». Daniel Gershenson et Daniel Greenberg, 1964, p. 27-33, 55-56.</ref>. === La théorie de la perception sensorielle === Anaxagore élabora également une théorie originale de la perception sensorielle, fondée sur le principe que « le semblable n'est pas affecté par le semblable, mais les contraires sont affectés les uns par les autres »<ref>Théophraste, ''De Sensibus'', 27 (DK 59 A 92) ; Aristote, ''De l'âme'', III, 3, 427a21-26 (DK 59 A 94).</ref>. Ce principe constituait une réponse directe aux théories de ses prédécesseurs, notamment Empédocle, qui soutenait que la perception se produit par similitude<ref>Théophraste, ''De Sensibus'', 1-2 (DK 31 A 86).</ref>. Selon Anaxagore, pour qu'une perception ait lieu, il doit exister une différence entre l'organe sensoriel et l'objet perçu<ref>Théophraste, ''De Sensibus'', 27-28 (DK 59 A 92).</ref>. Nous ne sentons pas la température de l'air lorsqu'elle est exactement égale à celle de notre peau ; c'est seulement lorsqu'il existe une différence que nous percevons le chaud ou le froid<ref>Théophraste, ''De Sensibus'', 29 (DK 59 A 92).</ref>. Une conséquence remarquable de cette théorie est qu'Anaxagore considérait que toute perception s'accompagne nécessairement de douleur ou d'un certain désagrément (λύπη)<ref>Théophraste, ''De Sensibus'', 29 (DK 59 A 92). Cf. W. K. C. Guthrie, 1965, p. 319 ; Inna Kupreeva, « Sensing the World », dans ''Physis and Psyche in Plato and Aristotle'', Londres, Bloomsbury, 2024, p. 95-114.</ref>. === La reproduction et l'embryologie === Anaxagore proposa également des théories sur la reproduction et le développement embryonnaire. Selon les témoignages anciens, il soutenait que le sexe de l'enfant est déterminé par le père seul, et non par la mère<ref>Aétius, V, 7, 1 (DK 59 A 107) ; Hippolyte, DK 59 A 42.</ref>. Cette théorie n'était pas entièrement originale — des idées similaires avaient été proposées par Parménide et d'autres penseurs antérieurs<ref>Aétius, V, 7, 1 (DK 28 A 52-54). Cf. Ursula Mittwoch, « Sex Determination », ''EMBO Reports'', vol. 14, 2013, p. 588-592 ; Oliver Kember, « Anaxagoras' Theory of Sex Differentiation and Heredity », ''Phronesis'', vol. 18, 1973, p. 1-14.</ref>. === La génération des animaux et des plantes === Selon les témoignages doxographiques, Anaxagore distinguait entre la zoogonie originelle — la première génération des animaux — et la reproduction ultérieure par semences<ref>Hippolyte, DK 59 A 42 ; Aétius, V, 19, 4 (DK 59 A 42).</ref>. Dans la zoogonie originelle, les premiers animaux émergèrent de la terre encore chaude et humide, grâce aux semences (σπέρματα) contenues dans l'air et l'éther<ref>Théophraste, dans Simplicius, ''Commentaire sur la Physique'', 27, 2-4.</ref>. En ce qui concerne les plantes, Anaxagore affirmait qu'elles possèdent une âme (ψυχή) et un intellect (νοῦς)<ref>Aétius, V, 26, 4 (DK 59 A 117).</ref>. Cette conclusion découlait de ses observations du comportement des plantes : elles se tournent vers la lumière du soleil, étendent leurs racines vers l'eau<ref>Aristote, ''De l'âme'', I, 5, 410b27-411a7. Cf. Inna Kupreeva, 2024, p. 98-103.</ref>. === Observations biologiques diverses === Les sources anciennes conservent également quelques observations biologiques isolées d'Anaxagore. Il soutenait, par exemple, que les belettes sont les seuls animaux qui donnent naissance par la bouche<ref>Aristote, ''Histoire des animaux'', VI, 32, 580a15-17 (DK 59 A 114) ; Plutarque, ''Œuvres morales'', 975F-976A (DK 59 A 114).</ref>. Il était parvenu à cette conclusion erronée parce qu'il avait observé des belettes femelles transportant leurs petits dans leur gueule immédiatement après la mise bas<ref>Plutarque, ''Œuvres morales'', 975F (DK 59 A 114).</ref>. De même, il affirmait que les corbeaux et les ibis s'accouplent par le bec<ref>Aristote, ''Histoire des animaux'', V, 2, 539b31-540a1 (DK 59 A 114).</ref>. Ces erreurs montrent les limites de sa méthode empirique. === L'homme et les animaux === Selon Anaxagore, ce qui distingue l'homme du reste du règne animal est principalement l'une des distinctions physiologiques les plus évidentes : les mains de l'homme<ref>Aristote, ''Parties des animaux'', IV, 10, 687a7-12 (DK 59 A 102). Aristote critique cette position et affirme au contraire que l'homme a des mains parce qu'il est le plus intelligent.</ref>. Ce sont les mains, selon Anaxagore, qui permettent à l'homme de surpasser les animaux dans les compétences manipulatrices et les capacités techniques. == L'influence et la postérité == Anaxagore occupe une place importante dans l'histoire de la philosophie antique. Introducteur à Athènes de la tradition ionienne de recherche naturaliste, il constitue un jalon dans le développement de la pensée grecque du V{{e}} siècle<ref>Diogène Laërce, II, 6 (DK 59 A 1) ; W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 266-267.</ref>. Les lignes qui suivent s'efforcent de décrire son influence avec la prudence philologique nécessaire : il est facile, en traitant des présocratiques, de céder à la tentation des grandes filiations, alors que la documentation réelle invite souvent à plus de retenue. === La transmission immédiate : Archélaos et le cercle socratique === Le premier vecteur de l'influence d'Anaxagore fut son disciple direct Archélaos d'Athènes (ou de Milet), qui succéda à son maître à la tête de son école à Lampsaque<ref>Diogène Laërce, II, 16 (DK 60 A 1) ; Simplicius, ''Commentaire sur la Physique'', 27, 23 (DK 60 A 7).</ref>. Selon plusieurs témoignages anciens, Archélaos aurait été le maître de Socrate, établissant ainsi un lien entre Anaxagore et le fondateur de la philosophie morale<ref>Diogène Laërce, II, 16 (DK 60 A 1). Ion de Chios rapporte que le jeune Socrate voyagea avec Archélaos à Samos (DK 60 A 3). Cf. Gábor Betegh, « Archelaus on Cosmogony and the Origins of Social Institutions », ''Oxford Studies in Ancient Philosophy'', vol. 51, 2016, p. 1-40.</ref>. Archélaos semble avoir développé et modifié certaines doctrines de son maître, notamment en appliquant les principes anaxagoréens à l'éthique et aux institutions sociales<ref>Diogène Laërce, II, 16 (DK 60 A 1) ; Hippolyte, DK 60 A 4 ; Gábor Betegh, 2016.</ref>. === L'influence sur Socrate : espoirs et déceptions === La relation entre Anaxagore et Socrate a probablement été indirecte, médiée par la lecture du livre. Dans le ''Phédon'', Platon fait raconter par Socrate sa rencontre avec la pensée d'Anaxagore : rencontre qui suscita d'abord un enthousiasme, puis une déception<ref>Platon, ''Phédon'', 97b-98c (DK 59 A 47).</ref>. Comme on l'a souligné plus haut, cette déception porte sur une attente socratique — celle d'une explication par le « meilleur » — qu'Anaxagore n'avait pas, à proprement parler, formulée. Cette critique conduisit Socrate à se tourner vers la philosophie morale et la recherche de la cause finale — le Bien — qui rendrait compte de l'ordre du monde<ref>Platon, ''Phédon'', 99c-100a. Cf. David Sedley, « Teleology and Myth in the ''Phaedo'' », ''Proceedings of the Boston Area Colloquium in Ancient Philosophy'', vol. 5, 1989, p. 359-383.</ref>. L'idée qu'un principe rationnel puisse gouverner l'univers — que Socrate trouve chez Anaxagore et qu'il juge insuffisamment exploitée — influe sur le projet socratique sans qu'il faille pour autant présenter Anaxagore comme la cause nécessaire de la philosophie socratique<ref>David Sider, « Anaxagoras, Socrates, and the History of "Philosophy" », ''Research Bulletin of the CHS'', 2016 ; W. K. C. Guthrie, 1965, p. 327-331.</ref>. Dans l'''Apologie'', l'accusation portée contre Socrate incluait l'imputation de théories cosmologiques manifestement inspirées d'Anaxagore — ce qui montre l'association étroite entre les deux penseurs dans l'esprit des Athéniens<ref>Platon, ''Apologie de Socrate'', 26d (DK 59 A 35). Cf. Gregory Vlastos, ''Socrates: Ironist and Moral Philosopher'', Ithaca, Cornell University Press, 1991, p. 293-297.</ref>. Xénophon présente Socrate comme mettant en garde ses disciples contre l'étude des phénomènes célestes à la manière d'Anaxagore<ref>Xénophon, ''Mémorables'', IV, 7, 6 (DK 59 A 47).</ref>. === L'appropriation platonicienne : du Noûs au Démiurge === Platon s'empare de la doctrine anaxagoréenne du Noûs et la transforme — en la radicalisant — en une cosmologie téléologique pleinement articulée. Dans le ''Timée'', Platon présente le Démiurge — l'artisan divin qui façonne le monde sensible à l'image des Formes éternelles — comme une figure qu'on peut lire en dialogue avec le Noûs anaxagoréen<ref>Platon, ''Timée'', 29a-30c, 47e-48a. Cf. Glenn Morrow, « Necessity and Persuasion in Plato's ''Timaeus'' », ''Philosophical Review'', vol. 59, 1950, p. 147-163 ; Luc Brisson, ''Le Même et l'Autre dans la structure ontologique du Timée de Platon'', Paris, Klincksieck, 1974, p. 87-125.</ref>. Il faut cependant être prudent : la téléologie platonicienne va bien au-delà de ce qu'affirmait Anaxagore, et le Démiurge contemple les Formes éternelles, ce qui n'a pas d'équivalent chez le Clazoménien. Dans le ''Philèbe'', Platon affirme explicitement sa dette envers Anaxagore en déclarant que « l'Intellect est roi du ciel et de la terre »<ref>Platon, ''Philèbe'', 28c (DK 59 A 47). Cf. également ''Cratyle'', 413c.</ref>, reprenant ainsi la doctrine anaxagoréenne que « le Noûs domine tout »<ref>Anaxagore, B12 (DK 59 B 12).</ref>. Dans les ''Lois'', Platon fait l'éloge des « anciens » qui ont découvert que « l'Intellect gouverne toutes choses » — allusion possible à Anaxagore<ref>Platon, ''Lois'', X, 897b-c, XII, 967b-c. Cf. Malcolm Schofield, 1980, p. 55-70.</ref>. L'influence d'Anaxagore sur Platon est donc réelle, mais elle passe par une transformation considérable de la doctrine originelle. === La critique aristotélicienne === Aristote, tout en reconnaissant l'importance historique d'Anaxagore, fut l'un de ses critiques les plus sévères. Dans la ''Métaphysique'', il salue Anaxagore comme « un homme sobre parmi des bavards »<ref>Aristote, ''Métaphysique'', I, 3, 984b15-20 (DK 59 A 43).</ref>, louant son introduction du Noûs comme cause de l'ordre cosmique. Mais cette louange est immédiatement suivie d'une critique : Anaxagore « utilise l'Intellect comme un ''deus ex machina'' »<ref>Aristote, ''Métaphysique'', I, 4, 985a18-21 (DK 59 A 47).</ref>. Comme on l'a vu, cette critique présuppose un idéal téléologique qui n'est pas celui d'Anaxagore lui-même. Aristote adopte en revanche, en le reformulant, le terme technique homéomère (ὁμοιομερής) pour désigner les substances anaxagoréennes — bien qu'il soit probable qu'Anaxagore lui-même n'ait jamais utilisé ce terme<ref>Cf. Malcolm Schofield, 1980, p. 87-108.</ref>. La distinction anaxagoréenne entre substances homéomères (chair, os, sang) et substances anhoméomères (main, pied, visage) devient fondamentale dans la philosophie naturelle aristotélicienne<ref>Aristote, ''De la génération et de la corruption'', I, 1, 314a20-b1 ; ''Parties des animaux'', II, 1, 646a12-24.</ref>. === L'héritage dans la philosophie hellénistique et romaine === Après Aristote, la pensée d'Anaxagore continua d'exercer une influence diffuse. Les Stoïciens s'approprièrent certains aspects de sa doctrine du Noûs pour développer leur propre concept du Logos universel qui pénètre et gouverne toute la nature<ref>Diogène Laërce, VII, 134-139 (SVF II, 634) ; Cicéron, ''De la nature des dieux'', I, 11, 27 ; II, 8, 23. Cf. A. A. Long et D. N. Sedley, ''The Hellenistic Philosophers'', vol. I, Cambridge, Cambridge University Press, 1987, p. 268-274.</ref>. Le pneuma stoïcien présente des analogies avec le Noûs anaxagoréen, bien que les Stoïciens aient rejeté le dualisme matière/esprit d'Anaxagore en faveur d'un matérialisme intégral<ref>Michael J. White, « Stoic Natural Philosophy », dans Brad Inwood (éd.), ''The Cambridge Companion to the Stoics'', Cambridge, Cambridge University Press, 2003, p. 124-152.</ref>. Les Épicuriens, en revanche, rejetèrent la doctrine anaxagoréenne. Lucrèce critique explicitement Anaxagore dans le ''De natura rerum''<ref>Lucrèce, ''De la nature'', I, 830-920 (DK 59 A 44). Cf. David Sedley, ''Lucretius and the Transformation of Greek Wisdom'', Cambridge, Cambridge University Press, 1998, p. 24-33.</ref>. Dans la tradition néoplatonicienne, Anaxagore fut lu à travers le prisme de Platon. Simplicius, au VI{{e}} siècle de notre ère, consacra de longs passages de son commentaire sur la ''Physique'' d'Aristote à l'exégèse des fragments d'Anaxagore — et c'est grâce à ces commentaires que la majeure partie de notre connaissance d'Anaxagore nous est parvenue<ref>Simplicius, ''Commentaire sur la Physique'' (passim).</ref>. === Un héritage à ne pas surinterpréter === Il est tentant de présenter Anaxagore comme un précurseur de la science mécaniste moderne, de l'atomisme, voire de la physique contemporaine. Il faut résister à cette tentation, au moins dans sa forme la plus large. Au XVII{{e}} siècle, certains philosophes mécanistes ont effectivement cité les présocratiques, mais l'atomisme de Démocrite a exercé une influence beaucoup plus directe qu'Anaxagore sur la physique corpusculaire naissante ; l'influence propre d'Anaxagore reste, sur ce plan, limitée<ref>Alan Chalmers, « Atomism from the 17th to the 20th Century », ''Stanford Encyclopedia of Philosophy'', 2005 (révisé 2014) ; Andrew Pyle, ''Atomism and its Critics : From Democritus to Newton'', Bristol, Thoemmes Press, 1997.</ref>. On trouve parfois des rapprochements, au statut d'analogie suggestive plutôt que de filiation historique, entre la doctrine du « tout dans tout » et certaines idées de la physique moderne. De tels rapprochements — par exemple chez Heisenberg à propos des présocratiques en général — ont une valeur heuristique mais ne doivent pas être transformés en affirmations de continuité<ref>Werner Heisenberg, ''Physics and Philosophy'', New York, Harper, 1958, p. 62-63, évoque les présocratiques sans établir de filiation précise.</ref>. De même, il a été parfois suggéré que les théologiens médiévaux aient vu dans le Noûs un précurseur du Dieu créateur. Cette suggestion demande une prudence particulière : Thomas d'Aquin, Maïmonide ou Avicenne s'inscrivent surtout dans une tradition aristotélicienne et néoplatonicienne déjà très élaborée, où le Noûs anaxagoréen n'apparaît, quand il est mentionné, que de manière très indirecte. Il faut donc parler de continuités discrètes dans une histoire complexe plutôt que d'influence directe<ref>Sur la tradition aristotélicienne et néoplatonicienne médiévale, voir Étienne Gilson, ''L'esprit de la philosophie médiévale'', Paris, Vrin, 1932.</ref>. === Bilan === L'influence d'Anaxagore sur la postérité se caractérise par un contraste fondamental : d'un côté, son introduction du Noûs comme principe cosmique fut saluée comme une avancée majeure ; de l'autre, son absence de développement d'une explication par le « meilleur » fut jugée insuffisante par Socrate, Platon et Aristote. Cette dualité traverse toute l'histoire de sa réception<ref>W. K. C. Guthrie, 1965, p. 327-331 ; Malcolm Schofield, 1980, p. 55-70 ; Daniel Graham, ''Science before Socrates'', Oxford, Oxford University Press, 2006, p. 152-158.</ref>. Cette limitation même fut féconde : en ouvrant une voie sans la parcourir jusqu'au bout, Anaxagore a invité ses successeurs à poursuivre le chemin. Socrate se tourna vers la philosophie morale ; Platon développa une cosmologie téléologique ; Aristote élabora une doctrine systématique des quatre causes. Tous trois ont puisé dans Anaxagore — mais chacun à sa manière et au prix d'une transformation qui leur appartient en propre. On peut donc parler d'une empreinte d'Anaxagore sur les orientations ultérieures de la philosophie grecque, sans pour autant soutenir que Socrate, Platon ou Aristote n'auraient pas été ce qu'ils furent sans lui : leurs doctrines ont leurs propres fondements, et la cause d'Anaxagore n'est, dans leur cas, jamais que partielle. Sur le plan scientifique, l'héritage d'Anaxagore est surtout celui d'un modèle d'explication naturaliste : il propose des explications des phénomènes célestes et météorologiques en termes de substances et de processus physiques, sans recourir aux agents mythologiques. Cet héritage n'est pas négligeable ; il serait pourtant excessif de lui attribuer à titre principal la fondation d'une « méthode scientifique » au sens moderne, dont l'élaboration suppose beaucoup d'autres étapes<ref>Daniel Gershenson et Daniel Greenberg, 1964, défendent une version forte de cette thèse, qu'il convient de modérer. Cf. Geoffrey Lloyd, ''Early Greek Science : Thales to Aristotle'', Londres, Chatto & Windus, 1970, pour une perspective plus prudente.</ref>. En définitive, Anaxagore occupe une place singulière : celle d'un pionnier qui ouvre une voie sans l'explorer entièrement, et qui a légué à ses successeurs à la fois une doctrine — dans ses fragments conservés — et la tâche de combler ce qu'elle laisse ouvert. == Notes et références == {{references}} == Bibliographie == === Textes anciens : éditions et traductions === ; Diels, Hermann & Kranz, Walther (éds.) : ''Die Fragmente der Vorsokratiker'', 3 vol., Berlin, Weidmann, 1951-1952 (6{{e}} éd.) : [Édition standard de référence pour les fragments présocratiques, avec le système de numérotation DK (Diels-Kranz) utilisé dans la présente étude] ; Laks, André & Most, Glenn W. (éds.) : ''Early Greek Philosophy'', 9 vol., Loeb Classical Library, Cambridge (Mass.), Harvard University Press, 2016 : [Édition plus récente avec traduction anglaise et nouveau système de numérotation, utile en complément de Diels-Kranz] ; Kirk, G. S., Raven, J. E. & Schofield, M. (éds.) : ''The Presocratic Philosophers: A Critical History with a Selection of Texts'', 2{{e}} édition, Cambridge, Cambridge University Press, 1983 : [Traductions anglaises commentées des fragments] ; Curd, Patricia (éd.) : ''Anaxagoras of Clazomenae: Fragments and Testimonia'', Toronto, University of Toronto Press, 2007 : [Édition critique avec traductions anglaises commentées et analyses détaillées] ; Sider, David (éd.) : ''The Fragments of Anaxagoras: With a Commentary'', Sankt Augustin, Academia Verlag, 2005 : [Édition avec commentaire détaillé] ; Platon : ''Phédon'', trad. fr. Monique Dixsaut, Paris, GF-Flammarion, 1991 : [Dialogue contenant la critique socratique du Noûs d'Anaxagore] ; Platon : ''Apologie de Socrate'', trad. fr. Luc Brisson, Paris, GF-Flammarion, 1997 ; Platon : ''Timée'', trad. fr. Luc Brisson, Paris, GF-Flammarion, 1992 ; Platon : ''Philèbe'', trad. fr. Alfred Diès, Paris, Les Belles Lettres, 1941 ; Platon : ''Lois'', trad. fr. Édouard des Places, Paris, Les Belles Lettres, 1951-1956 ; Aristote : ''Métaphysique'', trad. fr. Jean Tricot, Paris, Vrin, 1933 ; Aristote : ''Physique'', trad. fr. Jean Tricot, Paris, Vrin, 1936 ; Aristote : ''Du Ciel'', trad. fr. Jean Tricot, Paris, Vrin, 1949 ; Aristote : ''Génération et corruption'', trad. fr. Jean Tricot, Paris, Vrin, 1950 ; Aristote : ''Génération des animaux'', trad. fr. Jean Tricot, Paris, Vrin, 1957 ; Aristote : ''Histoire des animaux'', trad. fr. Jean Tricot, Paris, Vrin, 1957 ; Aristote : ''Parties des animaux'', trad. fr. Jean Tricot, Paris, Vrin, 1957 ; Aristote : ''De l'âme'', trad. fr. Richard Bodéüs, Paris, GF-Flammarion, 1993 ; Diogène Laërce : ''Vies et doctrines des philosophes illustres'', sous la dir. de Marie-Odile Goulet-Cazé, Paris, Le Livre de Poche (La Pochothèque), 1999 ; Théophraste : ''De Sensibus'' (''On Sense Perception''), trad. angl. dans Kirk, Raven & Schofield, 1983 ; Simplicius : ''In Aristotelis Physicorum libros commentaria'', éd. Hermann Diels, Berlin, Reimer, 1882-1895 : [Source principale pour la transmission des fragments d'Anaxagore] ; Hippolyte : ''Réfutation de toutes les hérésies'', trad. angl. dans Curd, 2007 ; Lucrèce : ''De la nature'', trad. fr. Alfred Ernout, Paris, Les Belles Lettres, 1920 ; Cicéron : ''De la nature des dieux'', trad. fr. Clara Auvray-Assayas, Paris, Les Belles Lettres, 2002 ; Plutarque : ''Vies parallèles'' (''Vie de Périclès'', ''Vie de Lysandre''), trad. fr. Anne-Marie Ozanam, Paris, Gallimard, Bibliothèque de la Pléiade, 2001 ; Xénophon : ''Mémorables'', trad. fr. Louis-André Dorion et Michele Bandini, Paris, Les Belles Lettres, 2000-2011 === Études modernes : histoire et philosophie antiques === ; Schofield, Malcolm : ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980 : [Monographie majeure : étude exhaustive de la pensée anaxagoréenne avec analyse textuelle détaillée] ; Curd, Patricia : « Anaxagoras », ''Stanford Encyclopedia of Philosophy'', publié 2007, révision substantielle 2019 : [Synthèse de référence, avec bibliographie à jour] ; Guthrie, W. K. C. : ''A History of Greek Philosophy'', vol. II : ''The Presocratic Tradition from Parmenides to Democritus'', Cambridge, Cambridge University Press, 1965 ; Barnes, Jonathan : ''The Presocratic Philosophers'', 2 vol., Londres, Routledge, 1982 (révisé 2006) ; Kirk, G. S., Raven, J. E. & Schofield, M. : ''The Presocratic Philosophers: A Critical History with a Selection of Texts'', 2{{e}} édition, Cambridge, Cambridge University Press, 1983 ; Vlastos, Gregory : « The Physical Theory of Anaxagoras », ''Philosophical Review'', vol. 59, 1950, p. 31-57 ; Peck, Aristotle L. : « Anaxagoras: Predication as a Problem in Physics », ''Classical Quarterly'', vol. 25, 1931, p. 27-37 et p. 112-120 ; Strang, Colin : « The Physical Theory of Anaxagoras », ''Archiv für Geschichte der Philosophie'', vol. 45, 1963, p. 101-118 ; Cornford, F. M. : « Anaxagoras' Theory of Matter », ''Classical Quarterly'', vol. 24, 1930, p. 14-30 ; Graham, Daniel W. : « Was Anaxagoras a Reductionist? », ''Ancient Philosophy'', vol. 24, 2004, p. 1-18 ; Graham, Daniel W. : ''Science before Socrates: Parmenides, Anaxagoras, and the New Astronomy'', Oxford, Oxford University Press, 2006 ; Curd, Patricia : ''The Legacy of Parmenides: Eleatic Monism and Later Presocratic Thought'', Princeton, Princeton University Press, 1998 ; Owen, G. E. L. : « The Place of the Timaeus in Plato's Dialogues », ''Classical Quarterly'', vol. 3, 1953, p. 79-95 ; repris dans ''Logic, Science and Dialectic'', Londres, Duckworth, 1986 ; Raven, J. E. : « The Basis of Anaxagoras' Cosmology », ''Classical Quarterly'', vol. 4, 1954, p. 123-137 ; Kerferd, George B. : « Anaxagoras and the Concept of Matter before Aristotle », ''Bulletin of the John Rylands Library'', vol. 52, 1969, p. 129-143 ; Lloyd, David : « Anaxagoras on Life and Mind », ''Phronesis'', vol. 14, 1969, p. 246-251 ; Deichgräber, Karl : « Anaxagoras Stil », ''Philologus'', vol. 87, 1933, p. 15-26 ; Norden, Eduard : ''Agnostos Theos: Untersuchungen zur Formengeschichte religiöser Rede'', Leipzig, Teubner, 1913 ; Taylor, A. E. : « On the Date of the Trial of Anaxagoras », ''Classical Quarterly'', vol. 11, 1917, p. 81-87 ; Kahn, Charles H. : ''Anaximander and the Origins of Greek Cosmology'', New York, Columbia University Press, 1960 ; Inwood, Brad : « Anaxagoras and Infinite Divisibility », ''Illinois Classical Studies'', vol. 11, 1986, p. 17-33 ; Sedley, David : ''Creationism and Its Critics in Antiquity'', Berkeley, University of California Press, 2007 ; Sedley, David : « Teleology and Myth in the ''Phaedo'' », ''Proceedings of the Boston Area Colloquium in Ancient Philosophy'', vol. 5, 1989, p. 359-383 ; Betegh, Gábor : « Archelaus on Cosmogony and the Origins of Social Institutions », ''Oxford Studies in Ancient Philosophy'', vol. 51, 2016, p. 1-40 ; Sider, David : « Anaxagoras, Socrates, and the History of "Philosophy" », ''Research Bulletin of the Center for Hellenic Studies'', 31 octobre 2016 ; Furley, David L. : ''Two Studies in the Greek Atomists'', Princeton, Princeton University Press, 1967 ; Furley, David L. : « Anaxagoras, Plato and Naming of Parts », dans ''Presocratic Philosophy: Essays in Honour of Alexander Mourelatos'', éd. Victor Caston et Daniel Graham, Aldershot, Ashgate, 2002, p. 119-126 ; Furth, Montgomery : « A Philosophical Hero? Anaxagoras and the Eleatics », ''Oxford Studies in Ancient Philosophy'', vol. 9, 1991, p. 95-129 ; Lesher, James : « Mind's Knowledge and Powers of Control in Anaxagoras », ''Phronesis'', vol. 40, 1995, p. 125-142 ; Marmodoro, Anna : « Anaxagoras's Qualitative Gunk », ''British Journal for the History of Philosophy'', vol. 23, 2015, p. 402-422 ; Marmodoro, Anna & Morison, Benjamin (éds.) : ''Everything in Everything: Anaxagoras's Metaphysics'', Oxford, Oxford University Press, 2019 ; Vassallo, Christian : « Nous, Motion, and Teleology in Anaxagoras », ''Oxford Studies in Ancient Philosophy'', vol. 50, 2016, p. 1-32 === Études modernes : biologie, physiologie et philosophie naturelle === ; Gershenson, Daniel E. & Greenberg, Daniel A. : ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964 : [À utiliser avec précaution : la thèse centrale sur la « naissance de la méthode scientifique » doit être tempérée] ; Meyer, Arthur William : ''Essays on the History of Embryology'', Stanford, Stanford University Press, 1939 ; Preus, Anthony : « The Techne of Nutrition in Ancient Greek Philosophy », ''Apeiron'', vol. 53, 2020, p. 97-124 ; Kember, Oliver : « Anaxagoras' Theory of Sex Differentiation and Heredity », ''Phronesis'', vol. 18, 1973, p. 1-14 ; Mittwoch, Ursula : « Sex Determination: Science & Society Series on Sex and Science », ''EMBO Reports'', vol. 14, 2013, p. 588-592 ; Kupreeva, Inna : « Sensing the World: Humans, Plants, and the Physicality of Life in Early Greek Philosophy », dans ''Physis and Psyche in Plato and Aristotle'', éd. S. D. Kolstrup et T. L. Kind, Londres, Bloomsbury, 2024, p. 95-114 === Études modernes : astronomie, cosmologie et météorologie === ; Theodossiou, Evangelos Th., Dimitrijevic, Milcho S., Mantarakis, Nikos A. & Georgakarakos, Nikolaos I. : « The Fall of a Meteorite at Aegos Potami in 467/6 BC », ''Journal of Astronomical History and Heritage'', vol. 5, 2002, p. 135-140 ; Graham, Daniel W. & Hintz, Eric : « Anaxagoras and the Comet », ''Apeiron'', vol. 40, 2007, p. 1-20 ; Couprie, Dirk L. : « Anaxagoras on the Milky Way and Lunar Eclipses », ''Rhizomata'', vol. 5, 2017, p. 127-147 ; Lloyd, Geoffrey E. R. : ''Early Greek Science: Thales to Aristotle'', Londres, Chatto & Windus, 1970 === Études modernes : influence et réception === ; Vlastos, Gregory : ''Socrates: Ironist and Moral Philosopher'', Ithaca, Cornell University Press, 1991 ; Morrow, Glenn R. : « Necessity and Persuasion in Plato's ''Timaeus'' », ''Philosophical Review'', vol. 59, 1950, p. 147-163 ; Brisson, Luc : ''Le Même et l'Autre dans la structure ontologique du Timée de Platon'', Paris, Klincksieck, 1974 ; Johansen, Thomas Kjeller : ''Plato's Natural Philosophy'', Cambridge, Cambridge University Press, 2004 ; Johansen, Thomas Kjeller : « From Craft to Nature: The Emergence of Natural Teleology », dans ''Plato and Hesiod'', éd. G. R. Boys-Stones et J. H. Haubold, Oxford, Oxford University Press, 2020, p. 100-125 ; Long, A. A. & Sedley, D. N. : ''The Hellenistic Philosophers'', vol. I, Cambridge, Cambridge University Press, 1987 ; White, Michael J. : « Stoic Natural Philosophy », dans ''The Cambridge Companion to the Stoics'', éd. Brad Inwood, Cambridge, Cambridge University Press, 2003, p. 124-152 ; Sedley, David : ''Lucretius and the Transformation of Greek Wisdom'', Cambridge, Cambridge University Press, 1998 ; Chalmers, Alan : « Atomism from the 17th to the 20th Century », ''Stanford Encyclopedia of Philosophy'', 2005 (révisé 2014) ; Pyle, Andrew : ''Atomism and its Critics: From Democritus to Newton'', Bristol, Thoemmes Press, 1997 ; Gilson, Étienne : ''L'esprit de la philosophie médiévale'', Paris, Vrin, 1932 ; Heisenberg, Werner : ''Physics and Philosophy'', New York, Harper, 1958 : [À consulter pour les rapprochements prudents entre présocratiques et physique moderne, à titre d'analogie heuristique] === Instruments de recherche et ressources === ; Curd, Patricia : « Anaxagoras », ''Stanford Encyclopedia of Philosophy'', 2007 (révision substantielle 2019) ; Graham, Daniel W. : « Presocratic Philosophy », ''Stanford Encyclopedia of Philosophy'', 2019 ; ''Oxford Classical Dictionary'' : s.v. « Anaxagoras », diverses éditions : [À consulter pour la mise en garde prudente sur la tradition biographique] === Dictionnaires et encyclopédies === ; Goulet, Richard (éd.) : ''Dictionnaire des philosophes antiques'', vol. I, Paris, CNRS Éditions, 1989 (2{{e}} éd. 2003) : [Entrée détaillée sur Anaxagore avec bibliographie] {{autocat}} [[Catégorie:Philosophe]] k4tj2kv68tfg5mda3p5i0cctqh13pzv 763996 763995 2026-04-19T09:14:16Z PandaMystique 119061 763996 wikitext text/x-wiki {{DicoPhilo|Anaxagore de Clazomènes}} == Vie et contexte historique == Les données biographiques concernant Anaxagore sont, comme le souligne l'''Oxford Classical Dictionary'', en grande partie « confuses et déroutantes » (''confused and confusing''), et la plupart des anecdotes transmises par la tradition antique doivent être abordées avec une prudence critique. Les récits qui nous sont parvenus proviennent en effet de sources tardives, séparées d'Anaxagore par plusieurs siècles : Plutarque (I{{er}}-II{{e}} siècle ap. J.-C.) dans ses ''Vies parallèles'', Valère Maxime (I{{er}} siècle ap. J.-C.) dans ses ''Faits et dits mémorables'', puis Diogène Laërce (première moitié du III{{e}} siècle ap. J.-C.) dans ses ''Vies et doctrines des philosophes illustres''. Ces auteurs compilent eux-mêmes des traditions doxographiques antérieures, parfois stylisées à des fins rhétoriques ou moralisatrices, parfois clairement légendaires. Anaxagore (en grec ancien Ἀναξαγόρας, « maître de l'assemblée » ou « chef au forum ») naît vers 500 av. J.-C. à Clazomènes, cité grecque d'Ionie située sur la côte occidentale de l'actuelle Turquie, à une trentaine de kilomètres à l'ouest d'Izmir<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 6 (DK 59 A 1). Apollodore d'Athènes, cité par Diogène Laërce, place la naissance d'Anaxagore à la 70{{e}} Olympiade (500-496 av. J.-C.).</ref>. Fils d'Hégésibule (certaines sources mentionnent Eubule), il appartient, selon la tradition ancienne, à une famille aristocratique et aurait possédé un patrimoine important<ref>Diogène Laërce, II, 6-7 ; Valère Maxime, ''Faits et dits mémorables'', VIII, 7, ext. 5.</ref>. Selon Diogène Laërce, Anaxagore aurait abandonné ses biens à ses proches afin de se consacrer entièrement à la philosophie<ref>Diogène Laërce, II, 7 : « Il négligea ses biens par amour de la sagesse. »</ref>. Une autre tradition, rapportée tant par Valère Maxime que par Diogène Laërce, raconte qu'Anaxagore, revenant d'un long voyage, aurait trouvé ses propriétés en ruine et aurait déclaré : « Si cela n'avait pas péri, c'est moi qui aurais péri »<ref>Valère Maxime, ''Faits et dits mémorables'', VIII, 7, ext. 5 ; Diogène Laërce, II, 6-7. Valère Maxime commente la sentence comme exemple de la sagesse la plus parfaite.</ref>. Ces récits, qu'ils soient authentiques ou pour partie légendaires, illustrent l'image qu'Anaxagore a laissée dans la mémoire doxographique : celle du philosophe contemplatif, détaché des affaires domestiques et consacré à l'étude du cosmos. Une anecdote célèbre, rapportée par plusieurs sources anciennes, témoigne de ce détachement. Interrogé sur son attachement à sa patrie, Anaxagore aurait répondu en levant la main vers le ciel : « J'ai un soin extrême de ma patrie », signifiant par là que le véritable philosophe considère l'univers entier comme sa demeure<ref>Plutarque, ''Vie de Périclès'', 4, 3-5 ; Aristote, ''Éthique à Eudème'', 1215b6-8.</ref>. Lorsqu'on lui demanda quel était l'homme le plus heureux, il aurait répondu, sur le même mode paradoxal, qu'aucun de ceux auxquels on songe ordinairement ne saurait prétendre à ce titre, et que le véritable bonheur paraîtrait même étrange à son interlocuteur<ref>Aristote, ''Éthique à Eudème'', 1215b6-8.</ref>. À quelqu'un qui lui demandait encore pour quelle raison on devrait choisir de naître plutôt que de ne pas être, il aurait répondu : « Afin de contempler les cieux et l'ordre de l'univers entier »<ref>Aristote, ''Éthique à Eudème'', 1216a11-14. Cette phrase, qui condense toute une conception de la vie philosophique comme ''theoria'', sera reprise par les moralistes anciens et par la tradition philosophique jusqu'à l'époque romaine.</ref>. === L'arrivée à Athènes et l'activité philosophique === La chronologie exacte de la vie d'Anaxagore demeure l'objet de débats parmi les historiens modernes, en raison de divergences importantes entre les sources anciennes<ref>Leonard Woodbury, « Anaxagoras and Athens », ''Phoenix'', vol. 35, n{{o}} 4, 1981, p. 295-315 ; Jaap Mansfeld, « The Chronology of Anaxagoras' Athenian Period and the Date of His Trial », ''Mnemosyne'', vol. 32-33, 1979-1980, p. 39-69 et p. 85-95 ; Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 33-35.</ref>. Selon la chronologie établie par Apollodore d'Athènes et rapportée par Diogène Laërce, Anaxagore serait arrivé à Athènes vers 480 av. J.-C., à l'âge de vingt ans, au moment des guerres médiques<ref>Diogène Laërce, II, 7. Cette tradition est soutenue par certains chercheurs modernes : Patricia O'Brien, « Anaxagoras and the Diurnal Revolution », ''Phronesis'', vol. 13, 1968, p. 133-143 ; Leonard Woodbury, 1981 ; Daniel Graham, ''Science before Socrates'', Oxford, Oxford University Press, 2006.</ref>. D'autres savants, notamment Jaap Mansfeld, proposent une date plus tardive, vers 456 av. J.-C.<ref>Jaap Mansfeld, 1979-1980 ; cf. également David Sider, ''The Fragments of Anaxagoras'', Sankt Augustin, Academia Verlag, 2005, p. 1-18, pour une discussion approfondie des problèmes chronologiques.</ref>. La question n'est pas définitivement tranchée, mais la majorité des historiens s'accorde sur une période d'activité philosophique à Athènes s'étendant sur près de trente ans, probablement entre 480/456 et 450/437 av. J.-C.<ref>Diogène Laërce, II, 7 ; Malcolm Schofield, 1980, p. 33-35.</ref>. Quelle que soit la date précise de son arrivée, Anaxagore est présenté par la tradition antique comme le premier philosophe à introduire la spéculation cosmologique et la philosophie naturelle ionienne dans la cité athénienne<ref>Clément d'Alexandrie, ''Stromates'', I, 14, 63, 3 (DK 59 A 7) : « Anaxagore transporta d'Ionie à Athènes la philosophie. » Cf. également Plutarque, ''Vie de Périclès'', 4, 2 ; Isocrate, ''Sur l'échange'', 268.</ref>. Athènes, en pleine expansion culturelle et politique après sa victoire sur les Perses à Marathon (490 av. J.-C.) et à Salamine (480 av. J.-C.), deviendra rapidement le centre intellectuel du monde grec. C'est dans ce contexte que la tradition situe l'enseignement d'Anaxagore. La tradition biographique antique, dominée par le récit de Plutarque dans la ''Vie de Périclès'', rapporte qu'Anaxagore se serait lié à Périclès, l'homme d'État athénien qui dominera la vie politique de la cité de 454 à 431 av. J.-C.<ref>Plutarque, ''Vie de Périclès'', 4-5, 16, 32 (DK 59 A 15, A 17). Platon, ''Phèdre'', 269e-270a, évoque également l'influence d'Anaxagore sur l'éloquence de Périclès.</ref>. Selon cette tradition, Anaxagore aurait compté parmi les maîtres intellectuels de Périclès, et le surnom d'« instructeur de Périclès » lui aurait été attaché dès l'Antiquité<ref>Plutarque, ''Vie de Périclès'', 4, 5 ; Diogène Laërce, II, 10, 13. Les instruments de référence modernes (''Oxford Classical Dictionary'', s.v. « Anaxagoras ») restent toutefois prudents sur la portée exacte de cette relation, dont les témoignages sont tardifs.</ref>. Il convient cependant de ne pas transformer cette tradition en déclaration politique publique avérée : les sources dont nous disposons sont tardives, souvent biographiques ou rhétoriques, et la figure de l'« instructeur de Périclès » appartient en partie à la construction doxographique. L'idée que le grand orateur démocratique d'Athènes ait trouvé dans les leçons d'un philosophe naturaliste la source de son éloquence élevée et de son détachement aristocratique correspond à un topos biographique commode, qu'il convient de ne pas surinterpréter en termes de dette intellectuelle publique. Parmi les disciples ou auditeurs d'Anaxagore, les sources anciennes mentionnent aussi le poète tragique Euripide<ref>Diogène Laërce, II, 10 ; Héracléide du Pont, cité par Aulu-Gelle, ''Nuits attiques'', XV, 20 (DK 59 A 17). Certaines idées cosmologiques présentes dans les pièces d'Euripide semblent refléter l'enseignement d'Anaxagore.</ref>, le musicien et sophiste Damon<ref>Plutarque, ''Vie de Périclès'', 4.</ref>, et peut-être Archélaos, qui aurait ensuite été le maître de Socrate<ref>Diogène Laërce, II, 16, 23 (DK 60 A 1, A 4).</ref>. Concernant Socrate lui-même, la question de savoir s'il a personnellement rencontré Anaxagore demeure controversée. Dans le ''Phédon'', Platon fait dire à Socrate qu'il a entendu quelqu'un lire dans le livre d'Anaxagore (la tradition ancienne a identifié ce lecteur à Archélaos)<ref>Platon, ''Phédon'', 97b-99d. L'identité du lecteur n'est pas précisée par Platon, mais la tradition ancienne l'a identifié à Archélaos.</ref>, ce qui suggère que Socrate n'a probablement pas eu de contact direct avec le philosophe de Clazomènes. Dans l'''Apologie'', Platon fait par ailleurs référence au fait que les livres d'Anaxagore étaient en vente pour une drachme à l'orchestre du théâtre<ref>Platon, ''Apologie'', 26d. Cette remarque témoigne de la large diffusion de l'œuvre d'Anaxagore à Athènes à la fin du V{{e}} siècle av. J.-C.</ref>, ce qui indique que ses idées étaient largement connues à Athènes, même en l'absence d'enseignement oral direct. === L'œuvre écrite === Anaxagore a composé un traité intitulé ''Sur la nature'' (Περὶ φύσεως), rédigé en prose ionienne<ref>Diogène Laërce, II, 6 ; Simplicius, ''Commentaire sur la Physique d'Aristote'', 27, 2 (DK 59 A 41).</ref>. Selon les témoignages anciens, il n'aurait écrit qu'un seul livre<ref>Diogène Laërce, I, 16 ; II, 6-7.</ref>, bien que Platon emploie le pluriel τὰ Ἀναξαγόρου βιβλία dans l'''Apologie''<ref>Platon, ''Apologie'', 26d.</ref>. Cette apparente contradiction s'explique probablement par le fait que l'ouvrage, même relativement court, était copié sur plusieurs rouleaux de papyrus, ce qui était l'usage courant à l'époque pour les œuvres en prose. Diogène Laërce rapporte qu'Anaxagore fut le premier à publier un livre contenant des figures et des diagrammes<ref>Diogène Laërce, II, 11.</ref>, détail qui témoigne de la dimension scientifique et pédagogique qu'on lui prêtait dans l'Antiquité tardive. Le traité d'Anaxagore était rédigé dans un style sobre et dense, caractérisé par une prose ionienne archaïsante<ref>Karl Deichgräber, « Anaxagoras Stil », ''Philologus'', vol. 87, 1933, p. 15-26 ; Hermann Fränkel, ''Dichtung und Philosophie des frühen Griechentums'', Munich, C.H. Beck, 1962, p. 529-547.</ref>. Contrairement aux poèmes philosophiques de Parménide et d'Empédocle, rédigés en hexamètres dactyliques à la manière d'Homère ou d'Hésiode, Anaxagore choisit la prose comme véhicule de sa pensée, s'inscrivant ainsi dans la tradition ionienne des philosophes naturalistes inaugurée par Anaximandre et Anaximène. De ce traité, il ne subsiste aujourd'hui qu'une vingtaine de fragments authentiques, principalement conservés par Simplicius de Cilicie (VI{{e}} siècle ap. J.-C.) dans ses commentaires sur Aristote<ref>L'édition de référence des fragments et témoignages demeure celle de Hermann Diels et Walther Kranz, ''Die Fragmente der Vorsokratiker'', 6{{e}} édition, Berlin, Weidmann, 1951-1952, vol. II, p. 5-44 (59 A et B). Une édition plus récente est celle d'André Laks et Glenn Most, ''Early Greek Philosophy'', Loeb Classical Library, 9 vol., Cambridge (Mass.), Harvard University Press, 2016, qui a introduit un nouveau système de numérotation ; éditions et traductions anglaises : Patricia Curd, ''Anaxagoras of Clazomenae : Fragments and Testimonia'', Toronto, University of Toronto Press, 2007 ; David Sider, ''The Fragments of Anaxagoras'', Sankt Augustin, Academia Verlag, 2005.</ref>. Simplicius, conscient de la rareté de l'ouvrage à son époque, prit soin de citer longuement les premières parties du traité, qui exposaient les principes généraux du système<ref>Simplicius, ''Commentaire sur la Physique d'Aristote'', 155, 23-157, 4 (fragments B1-B4) ; 164, 24-166, 1 (fragment B12).</ref>. === Le procès et l'exil === Les circonstances du départ d'Anaxagore d'Athènes et de sa fin de vie sont particulièrement controversées dans les sources anciennes. Diogène Laërce rapporte pas moins de quatre versions contradictoires du procès d'Anaxagore<ref>Diogène Laërce, II, 12-15. Ces quatre récits divergent sur l'identité de l'accusateur, la nature précise des charges, le déroulement du procès et son issue.</ref>. Selon une première version, attribuée à Sotion, Anaxagore aurait été accusé d'impiété (ἀσέβεια) par Cléon pour avoir déclaré que le soleil était une masse de métal incandescent ; Périclès l'aurait défendu, et Anaxagore aurait été condamné à une amende de cinq talents et à l'exil<ref>Sotion, dans Diogène Laërce, II, 12.</ref>. Selon Satyre, l'accusateur aurait été Thucydide (l'adversaire politique de Périclès, et non l'historien), les charges auraient inclus non seulement l'impiété mais aussi la trahison (sympathies pro-perses ou « médisme »), et Anaxagore aurait été condamné à mort par contumace<ref>Satyre, dans Diogène Laërce, II, 12-13.</ref>. Deux autres versions, attribuées respectivement à Hermippe et à Hiéronymos de Rhodes, présentent des variantes sur le rôle de Périclès et l'issue du procès<ref>Hermippe et Hiéronymos de Rhodes, dans Diogène Laërce, II, 13-14.</ref>. Plutarque, dans la ''Vie de Périclès'', fournit une version différente. Selon lui, un devin et politicien athénien nommé Diopeithès aurait fait voter un décret (le « décret de Diopeithès ») autorisant les poursuites judiciaires contre ceux qui ne reconnaissaient pas les dieux ou enseignaient des doctrines sur les phénomènes célestes<ref>Plutarque, ''Vie de Périclès'', 32, 1-2. Ce décret, daté approximativement de 433-432 av. J.-C., aurait visé Anaxagore mais aussi, indirectement, Périclès.</ref>. Selon cette version, Anaxagore aurait été accusé d'impiété pour avoir soutenu que le soleil n'était pas une divinité mais une pierre incandescente, et que la lune était une terre<ref>Plutarque, ''Vie de Périclès'', 32, 2 ; Platon, ''Apologie'', 26d, où Mélètos accuse Socrate de professer les mêmes opinions qu'Anaxagore.</ref>. Grâce à l'intervention de Périclès, Anaxagore aurait échappé à la condamnation à mort mais aurait dû s'exiler d'Athènes<ref>Plutarque, ''Vie de Périclès'', 32, 5.</ref>. L'historicité même de ce procès a été mise en doute par certains historiens modernes, qui soulignent l'absence de témoignages contemporains et les ressemblances troublantes avec le procès de Socrate<ref>J. A. Davison, « Protagoras, Democritus, and Anaxagoras », ''Classical Quarterly'', vol. 3, 1953, p. 33-45, exprime un scepticisme extrême.</ref>. Néanmoins, la plupart des spécialistes admettent aujourd'hui qu'un procès a bien eu lieu, même si les détails précis nous échappent<ref>Leonard Woodbury, 1981 ; Daniel Graham, 2006, p. 311-318, défendent l'historicité du procès tout en reconnaissant les difficultés chronologiques.</ref>. La date du procès demeure elle aussi incertaine : certains savants la placent vers 450 av. J.-C.<ref>Cette date traditionnelle est défendue notamment dans l'''Oxford Classical Dictionary'', 2{{e}} édition, 1970, s.v. « Anaxagoras ».</ref>, d'autres vers 437-436 av. J.-C.<ref>Mansfeld, 1979-1980.</ref>, d'autres encore vers 433-432 av. J.-C.<ref>Woodbury, 1981.</ref>. Quelle que soit la date précise, Anaxagore se retire à Lampsaque, colonie grecque d'Asie Mineure située sur l'Hellespont (l'actuel détroit des Dardanelles), où, selon la tradition, il est accueilli avec honneur<ref>Diogène Laërce, II, 14-15 ; Valère Maxime, ''Faits et dits mémorables'', VIII, 7, ext. 5.</ref>. Il y meurt en 428 av. J.-C., à l'âge de soixante-douze ans selon Apollodore<ref>Apollodore, dans Diogène Laërce, II, 7.</ref>. Les habitants de Lampsaque lui vouent, selon les sources tardives, un culte et célèbrent sa mémoire pendant plus d'un siècle après sa mort. Ils auraient élevé un autel dédié à l'Esprit et à la Vérité (Νοῦς καὶ Ἀλήθεια) en son honneur, et observé l'anniversaire de sa mort comme un jour férié pour les enfants des écoles<ref>Diogène Laërce, II, 15 ; Élien, ''Histoire variée'', VIII, 19 ; Cicéron, ''Tusculanes'', V, 36, 104.</ref>. Une inscription fut placée sur sa tombe, dont le texte exact ne nous est malheureusement pas parvenu, mais qui témoignait, selon Diogène Laërce, de l'estime dans laquelle les citoyens de Lampsaque tenaient le philosophe<ref>Diogène Laërce, II, 15.</ref>. === Portrait et anecdotes === Les sources anciennes ont conservé de nombreuses anecdotes sur Anaxagore qui, même si leur authenticité historique ne peut être garantie, témoignent de l'image du philosophe dans la tradition biographique antique. Galien rapporte qu'Anaxagore, apprenant la mort de son fils, aurait déclaré avec le plus grand calme : « Je savais qu'il était mortel quand je l'ai engendré »<ref>Galien, ''Des opinions d'Hippocrate et de Platon'', IV, 7, 41 (DK 59 A 33).</ref>. Ces récits contribuèrent à faire d'Anaxagore le modèle, dans la tradition, du philosophe détaché des contingences matérielles et consacré à la contemplation de la nature. Dans le ''Phèdre'' de Platon, Socrate attribue l'éloquence de Périclès à l'influence d'Anaxagore, qui lui aurait appris à s'élever au-dessus des préoccupations quotidiennes par la spéculation sur la nature de l'intelligence (νοῦς) et de la folie<ref>Platon, ''Phèdre'', 269e-270a.</ref>. Cette remarque témoigne de la réputation d'Anaxagore dans l'Athènes classique : celle d'un penseur dont les spéculations abstraites et « météorologiques » (au sens ancien de l'étude des phénomènes célestes) pouvaient paraître étranges ou excessives aux yeux du commun, mais qui exerçaient une fascination certaine sur les esprits cultivés. Cette image se reflète jusque dans la comédie d'Aristophane, ''Les Nuées'', où les spéculations cosmologiques tournées en ridicule semblent en partie inspirées par les doctrines d'Anaxagore, bien que ce soit Socrate qui soit présenté sur scène<ref>Aristophane, ''Les Nuées'', passim. Cf. Kenneth Dover, ''Aristophanes Clouds'', Oxford, Clarendon Press, 1968, p. xxxii-xlvii, pour la discussion du rapport entre les théories cosmologiques ridiculisées dans la pièce et celles d'Anaxagore.</ref>. === L'école de Lampsaque === À Lampsaque, Anaxagore fonda ou du moins anima une école philosophique qui continua son enseignement après sa mort<ref>Malcolm Schofield, 1980, p. 1 ; W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 266-267.</ref>. Parmi ses disciples à Lampsaque, les sources mentionnent Métrodore de Lampsaque<ref>Diogène Laërce, II, 17 ; Plutarque, ''Des opinions des philosophes'', 888e (DK 61).</ref>, qui poursuivit et développa les interprétations allégoriques d'Homère dans un esprit anaxagoréen. L'influence de l'école de Lampsaque se prolongea au moins jusqu'à la fin du V{{e}} siècle av. J.-C. Cette présence d'Anaxagore à Lampsaque explique peut-être pourquoi ses idées ont continué à circuler et à être discutées dans le monde grec, malgré son départ d'Athènes et le caractère fragmentaire de la tradition de son œuvre écrite. == Les principes métaphysiques fondamentaux == La philosophie d'Anaxagore se construit en réponse directe aux exigences métaphysiques établies par Parménide d'Élée<ref>Patricia Curd, ''The Legacy of Parmenides: Eleatic Monism and Later Presocratic Thought'', Princeton, Princeton University Press, 1998, p. 123-142 ; Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 27-47.</ref>. Dans son poème philosophique, Parménide avait posé deux principes fondamentaux : « l'être est et ne peut pas ne pas être » (ἔστιν εἶναι) et « le non-être n'est pas et ne peut pas être » (οὐκ ἔστι μὴ εἶναι)<ref>Parménide, fragment B2, DK 28 B 2, 3-5 ; cf. également B6, 1-2 et B8, 1-2.</ref>. De ces prémisses, Parménide concluait que toute génération et toute corruption véritables sont impossibles, car elles impliqueraient un passage de l'être au non-être ou du non-être à l'être, transitions logiquement impossibles puisque le non-être ne peut en aucune manière être<ref>Parménide, B8, 6-21 ; Aristote, ''Physique'', I, 8, 191a23-31.</ref>. Plus encore, Parménide soutenait que l'être véritable devait être un, continu, homogène, immuable et éternel<ref>Parménide, B8, 22-25 : « Il n'est pas divisible, puisqu'il est tout entier semblable » (οὐδὲ διαιρετόν ἐστιν, ἐπεὶ πᾶν ἐστιν ὁμοῖον).</ref>. Anaxagore accepte les principes éléatiques fondamentaux, c'est-à-dire l'impossibilité du passage de l'être au non-être et inversement, mais refuse d'en tirer la conclusion que le monde sensible et le changement sont illusoires<ref>Aristote, ''Métaphysique'', I, 3, 984a11-16 ; Simplicius, ''Commentaire sur la Physique d'Aristote'', 25, 19-26 (DK 59 A 52).</ref>. Selon Simplicius, « Anaxagore de Clazomènes, qui fut un disciple de la philosophie d'Anaximène, fut le premier à s'opposer à Parménide »<ref>Simplicius, ''Commentaire sur la Physique'', 25, 19-21 (DK 59 A 52). L'affirmation qu'Anaxagore fut disciple d'Anaximène est chronologiquement problématique et doit être comprise au sens large d'une appartenance à la tradition ionienne.</ref>. Plutôt que de nier la réalité du changement et de la diversité, Anaxagore cherche à fonder une cosmologie conforme aux exigences parménidiennes tout en rendant compte de la multiplicité et du mouvement observables dans la nature<ref>Malcolm Schofield, 1980, p. 38-42 ; G. E. L. Owen, « Eleatic Questions », ''Classical Quarterly'', vol. 10, 1960, p. 84-102.</ref>. Comme l'a remarqué G. E. L. Owen, la phrase d'ouverture du traité d'Anaxagore, « Toutes choses étaient ensemble », est « manifestement formulée comme une contradiction plate de Parménide sur plusieurs questions majeures »<ref>G. E. L. Owen, « The Place of the Timaeus in Plato's Dialogues », ''Classical Quarterly'', vol. 3, 1953, p. 79-95, cité dans Malcolm Schofield, 1980, p. 64.</ref>. === Le principe de conservation === Le premier principe métaphysique fondamental qu'Anaxagore énonce se trouve dans le fragment B17 : « Les Grecs ne pensent pas correctement au sujet de la génération et de la corruption. Aucune chose ne naît ni ne périt, mais à partir des choses qui sont, il y a mélange et séparation. Ainsi, ils appelleraient correctement la génération "mélange" (σύγκρισις) et la corruption "séparation" (διάκρισις) »<ref>Anaxagore, fragment B17, cité par Simplicius, ''Commentaire sur la Physique'', 163, 20-24 (DK 59 B 17).</ref>. Ce principe de conservation constitue le fondement de toute sa physique et sa réponse directe à l'interdit parménidien. Ce qui apparaît comme génération n'est en réalité qu'un réarrangement d'éléments préexistants qui se mélangent (συμμίσγεται). Ce qui semble être corruption n'est que la dissociation (διακρίνεται) de ces mêmes éléments<ref>Anaxagore, B17 ; cf. également Aristote, ''Génération et corruption'', I, 1, 314a18-20 (DK 59 A 52) : « Anaxagore et d'autres disent que la génération et la corruption sont mélange et séparation. »</ref>. Rien ne naît du néant (ex nihilo nihil fit), rien ne retourne au néant : les composants élémentaires de la réalité existent de toute éternité et demeurent inaltérables dans leur nature propre. Aristote souligne qu'Anaxagore, comme d'autres philosophes naturalistes, fonde explicitement ce principe sur l'axiome parménidien<ref>Aristote, ''Physique'', I, 4, 187a26-31.</ref>. Le principe prend chez Anaxagore une forme plus spécifique encore dans le fragment B10, où il pose la question rhétorique : « Comment le cheveu pourrait-il provenir de ce qui n'est pas cheveu, et la chair de ce qui n'est pas chair ? » (πῶς γὰρ ἂν ἐκ μὴ τριχὸς γένοιτο θρὶξ καὶ σὰρξ ἐκ μὴ σαρκός;)<ref>Anaxagore, fragment B10, conservé dans une scholie à Grégoire de Nazianze (DK 59 B 10).</ref>. Cette formulation, qui deviendra proverbiale dans l'Antiquité, exprime ce que les commentateurs modernes appellent le « principe du semblable par le semblable » (similia similibus) : une substance ne peut provenir que de la même substance, déjà présente sous une forme latente<ref>Aristote, ''Physique'', I, 4, 187a26-29 ; cf. Malcolm Schofield, 1980, p. 51-58 ; Jonathan Barnes, ''The Presocratic Philosophers'', Londres, Routledge, 1982, p. 309-315.</ref>. Ce principe présente une analogie frappante avec celui qu'Antoine-Laurent de Lavoisier formulera en 1789 comme principe de conservation de la matière, et la sentence « Rien ne se perd, rien ne se crée, tout se transforme » en est parfois rapprochée. Il convient cependant de ne pas surinterpréter cette analogie : les mécanismes qu'Anaxagore propose pour expliquer les transformations (mélange et séparation d'ingrédients éternels) sont qualitatifs et ne relèvent pas d'une conception chimique quantitative au sens moderne<ref>Antoine-Laurent de Lavoisier, ''Traité élémentaire de chimie'', Paris, Cuchet, 1789, vol. I, p. 101 : « Rien ne se crée, ni dans les opérations de l'art, ni dans celles de la nature, et l'on peut poser en principe que, dans toute opération, il y a une égale quantité de matière avant et après l'opération. » Le rapprochement avec Anaxagore relève plus de l'analogie conceptuelle rétrospective que d'une filiation historique documentée.</ref>. L'intuition conservatrice d'Anaxagore partage avec le principe lavoisien l'idée que rien ne se perd ni ne se crée dans le devenir physique, mais il serait anachronique d'y voir l'un des principes cardinaux de la chimie moderne. === Tout est dans tout === Le deuxième principe métaphysique fondamental d'Anaxagore s'énonce ainsi : « Il y a une part de tout en toute chose » (ἐν παντὶ παντὸς μοῖρα ἔνεστι, fragment B11)<ref>Anaxagore, fragment B11, cité par Simplicius, ''Commentaire sur la Physique'', 164, 26 (DK 59 B 11).</ref>. Ce principe, souvent désigné par la formule latine ''omnia in omnibus'' (« toutes choses dans toutes choses »), constitue sans doute la thèse la plus caractéristique et la plus déroutante de la philosophie d'Anaxagore<ref>Cf. Gregory Vlastos, « The Physical Theory of Anaxagoras », ''Philosophical Review'', vol. 59, 1950, p. 31-57 ; Colin Strang, « The Physical Theory of Anaxagoras », ''Archiv für Geschichte der Philosophie'', vol. 45, 1963, p. 101-118.</ref>. Il signifie que chaque portion de matière, si infime soit-elle, contient en elle-même des portions de toutes les autres substances qui existent dans l'univers. Il n'existe aucune substance pure, aucun élément qui puisse être isolé de tous les autres<ref>Anaxagore, B6 : « Puisqu'il est impossible qu'il y ait un minimum, il ne serait pas possible que [quelque chose] soit séparé, ni ne vienne à l'être par soi-même » (ἐπεὶ δὲ οὐκ ἔστι τοῦ ὀλίγου τὸ ἐλάχιστον, οὐκ ἂν γένοιτο χωρίς, οὐδὲ γένοιτο καθ᾽ ἑαυτό).</ref>. Dans un morceau d'or, il y a non seulement de l'or en proportion dominante, mais aussi de la chair, des os, du chaud, du froid, du sec, de l'humide, et ainsi de suite pour toutes les qualités et substances possibles<ref>Aristote, ''Métaphysique'', I, 3, 984a13-16 ; Simplicius, ''Commentaire sur la Physique'', 34, 29-35, 9 (DK 59 A 45).</ref>. Cette thèse découle logiquement du principe de conservation et du principe du semblable par le semblable combinés. En effet, comment la chair pourrait-elle provenir du pain et du lait que nous consommons, si le pain et le lait ne contenaient pas déjà de la chair ? La question rhétorique du fragment B10 impose cette conclusion. La nourriture que nous ingérons doit nécessairement contenir en elle-même, quoique de manière imperceptible en raison de la petitesse des parties, toutes les substances qui composent notre corps : cheveux, ongles, veines, artères, nerfs, os<ref>Scholie anonyme à Grégoire de Nazianze, DK 59 B 10 : « Dans le même liquide séminal, il y a des cheveux, des ongles, des veines et des artères, des nerfs et des os, et ils sont imperceptibles en raison de la petitesse des parties (δι᾽ ὀλιγότητα), mais lorsqu'ils croissent, ils se séparent graduellement. »</ref>. C'est par un processus de séparation graduelle (ἀποκρίνεσθαι), au cours de la croissance, que ces éléments préexistants dans la nourriture viennent s'ajouter aux parties correspondantes de notre organisme. Le principe « tout est dans tout » s'applique non seulement aux substances naturelles comme la chair et les os, mais aussi aux qualités opposées. Anaxagore affirme que « le noir est dans le blanc et le blanc dans le noir » et qu'il en va de même pour le lourd et le léger, le chaud et le froid<ref>Anaxagore, B15, cité par Simplicius, ''Commentaire sur le Ciel'', 608, 26 (DK 59 B 15) ; Simplicius, ''Commentaire sur la Physique'', 175, 11-14.</ref>. Ces oppositions qualitatives ne sont pas absolues mais relatives : ce qui nous paraît blanc contient en réalité du noir, mais en proportion si faible que cette part de noir demeure invisible à nos sens. De même, ce qui est léger contient du lourd, et inversement<ref>Anaxagore, B1, B4b ; Aristote, ''Physique'', I, 4, 187b1-7 (DK 59 A 52).</ref>. Cette doctrine a suscité d'intenses débats interprétatifs depuis l'Antiquité. Aristote la présente comme une tentative de résoudre le problème de la nutrition tout en respectant les contraintes éléatiques<ref>Aristote, ''Physique'', III, 4, 203a19-b3 ; cf. W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 289-294.</ref>. Les interprètes modernes se divisent sur la question de savoir si Anaxagore conçoit ces « portions » (μοῖραι) comme des particules infinitésimales ou comme des qualités interpénétrées sans structure corpusculaire déterminée<ref>Pour la première interprétation « particulaire », voir Gregory Vlastos, 1950 ; Arthur L. Peck, « Anaxagoras : Predication as a Problem in Physics », ''Classical Quarterly'', vol. 25, 1931, p. 27-37 et p. 112-120. Pour la seconde interprétation « non-particulaire », voir Colin Strang, 1963 ; Daniel Graham, « Was Anaxagoras a Reductionist? », ''Ancient Philosophy'', vol. 24, 2004, p. 1-18 ; Anna Marmodoro, « Anaxagoras's Qualitative Gunk », ''British Journal for the History of Philosophy'', vol. 23, 2015, p. 402-422.</ref>. Quoi qu'il en soit, ce principe constitue la clé de voûte du système physique d'Anaxagore. === Pas de plus petit ni de plus grand === Le troisième principe métaphysique, exposé dans le fragment B3, stipule qu'« il n'y a pas de plus petit dans le petit, mais toujours un plus petit encore, car il est impossible que ce qui est cesse d'être par division (οὐ γάρ ἐστι τοῦ ἀποκεκρίσθαι τὸ ἐλάχιστον). Mais il y a aussi toujours un plus grand que le grand, et il est égal au petit en multitude (πλῆθος) ; mais par rapport à elle-même, chaque chose est à la fois grande et petite »<ref>Anaxagore, fragment B3, cité par Simplicius, ''Commentaire sur la Physique'', 164, 17-20 (DK 59 B 3).</ref>. Ce principe garantit la divisibilité infinie de la matière et s'oppose frontalement à toute conception atomiste de type démocritéen<ref>Leucippe et Démocrite, fragments DK 67-68 ; Aristote, ''Génération et corruption'', I, 2, 315b28-317a2 ; I, 8, 325a23-b5.</ref>. Contrairement à Leucippe et Démocrite, qui postulent l'existence d'atomes insécables (ἄτομα, littéralement « indivisibles »), Anaxagore maintient que toute portion de matière, aussi petite soit-elle, peut encore être divisée, et que cette division révélera toujours la présence de toutes les substances selon le principe « tout est dans tout »<ref>Démocrite, fragments B9, B125, B156, B164 ; cf. Aristote, ''De la génération et de la corruption'', I, 8, 325a23-b5 ; Simplicius, ''Commentaire sur le Ciel'', 294, 33-295, 22 (DK 68 A 37).</ref>. Pour Anaxagore, la divisibilité peut être infinie sans que la matière s'évanouisse dans le néant, précisément parce que chaque fragment, si petit soit-il, contient encore toutes les substances<ref>Anaxagore, B6 : « Puisque les parts du grand et du petit sont égales en nombre, ainsi également toutes choses seraient dans toute chose. Il n'est pas possible qu'elles soient séparées, mais toutes choses ont une part de toute chose » (ἴσαι γὰρ ἀριθμῷ μοῖραί εἰσι καὶ τοῦ μεγάλου καὶ τοῦ σμικροῦ· καὶ οὕτως ἂν εἴη πάντα ἐν παντί).</ref>. Cette infinité dans la petitesse se combine avec l'infinité dans la grandeur. Il n'existe pas de limite supérieure à l'extension d'une substance, pas plus qu'il n'existe de limite inférieure<ref>Anaxagore, B3 : « Mais il y a aussi toujours un plus grand que le grand, et il est égal au petit en multitude » (ἀλλὰ καὶ μεγάλου ἀεὶ ἔστι μεῖζον· καὶ ἴσον ἐστὶ τῷ σμικρῷ πλήθει).</ref>. Cette double infinité, à la fois de division et d'extension, constitue un trait distinctif de la métaphysique d'Anaxagore et pose des problèmes interprétatifs considérables aux commentateurs, tant anciens que modernes<ref>Margaret Furth, « A Philosophical Hero? Anaxagoras and the Eleatics », ''Oxford Studies in Ancient Philosophy'', vol. 9, 1991, p. 95-129 ; David Furley, « Anaxagoras, Plato and Naming of Parts », dans ''Presocratic Philosophy: Essays in Honour of Alexander Mourelatos'', éd. Victor Caston et Daniel Graham, Aldershot, Ashgate, 2002, p. 119-126.</ref>. Le principe de non-minimum a une conséquence métaphysique importante : il assure que le mélange universel « tout est dans tout » ne pourra jamais être défait. En effet, puisqu'il n'existe pas de plus petite quantité d'une substance, celle-ci ne pourra jamais être entièrement extraite d'un mélange. On pourra réduire sa proportion indéfiniment, mais elle demeurera toujours présente en quelque mesure<ref>Anaxagore, B6 ; Simplicius, ''Commentaire sur la Physique'', 164, 20-22 : « Car si tout est dans tout et si tout se sépare de tout, alors du prétendu minimum quelque chose de plus petit que lui sera séparé, et du prétendu maximum quelque chose de plus grand que lui a été séparé. »</ref>. Ainsi, le principe de mélange universel est garanti de manière structurelle par le principe de divisibilité infinie. Comme l'a remarqué Colin Strang, « la complexité structurelle n'est pas, dans la théorie d'Anaxagore, fonction de la taille »<ref>Colin Strang, 1963, p. 366 : « Structural complexity is not, on Anaxagoras' theory, a function of size. »</ref>. La justification qu'Anaxagore donne de ce principe est explicitement parménidienne : « car il est impossible que ce qui est cesse d'être » (οὐ γὰρ ἔστι τοῦ εἶναι τὸ μὴ εἶναι)<ref>Anaxagore, B3 ; cette formulation reprend directement l'axiome de Parménide, B2, 3.</ref>. Si l'on pouvait diviser la matière jusqu'à la faire disparaître complètement, cela impliquerait un passage de l'être au non-être, ce qui viole l'interdit fondamental. Toute division, aussi poussée soit-elle, doit donc laisser subsister quelque chose, et ce quelque chose, par application du principe « tout est dans tout », contiendra encore des portions de toutes les substances<ref>Simplicius, ''Commentaire sur la Physique'', 164, 23-165, 1 (commentaire sur B3).</ref>. === Le principe de prédominance === Du principe « tout est dans tout » découle une difficulté évidente : si chaque chose contient une part de toutes les autres, comment expliquer que nous percevions des objets distincts et identifiables ? Comment distinguer l'or de la chair, le noir du blanc, si l'or contient de la chair et la chair contient de l'or, si le noir contient du blanc et le blanc contient du noir ? Anaxagore résout cette difficulté par ce que les commentateurs modernes appellent le « principe de prédominance » (bien qu'Anaxagore lui-même n'emploie pas ce terme technique)<ref>Le terme « principe de prédominance » (predominance principle) a été introduit par les interprètes modernes, notamment Gregory Vlastos, 1950, p. 47-50 ; Malcolm Schofield, 1980, p. 88-101.</ref>. Dans le fragment B12, il affirme : « Chaque chose est et était très manifestement (ἐμφανέστατα) constituée de celles des choses dont il y a le plus en elle »<ref>Anaxagore, fragment B12, cité par Simplicius, ''Commentaire sur la Physique'', 156, 1-157, 4, à 156, 10-11 (DK 59 B 12).</ref>. Autrement dit, une chose tire son identité et ses propriétés apparentes des substances qui prédominent en elle. Un morceau d'or nous apparaît comme de l'or parce que la substance or y est présente en proportion largement supérieure à toutes les autres substances. De même, notre chair nous semble être de la chair parce que la substance chair y domine quantitativement<ref>Simplicius, ''Commentaire sur la Physique'', 34, 29-35, 9 : « Chaque chose semble être cela dont elle a le plus, comme elle l'était auparavant » (δοκεῖν δ᾽ ἕκαστον εἶναι ταῦτα ὧν ἂν πλεῖστα ἐνῇ, ὥσπερ καὶ πρότερον).</ref>. Cette prédominance n'est pas absolue, puisque toutes les autres substances demeurent présentes en quelque proportion, mais elle est suffisante pour conférer à l'objet ses caractéristiques perceptibles<ref>Anaxagore, B12 ; Aristote, ''Physique'', I, 4, 187b1-7.</ref>. Le principe de prédominance permet à Anaxagore de concilier son ontologie de mélange universel avec notre expérience quotidienne d'un monde composé d'objets distincts et identifiables. Il rend compte aussi du changement : lorsqu'un objet semble se transformer en un autre, par exemple lorsque le pain que nous mangeons devient chair, ce qui se produit en réalité est une modification des proportions relatives des substances présentes. La chair, déjà présente dans le pain mais en proportion imperceptible, vient s'ajouter à la chair de notre corps à mesure que les autres composants du pain se dispersent ou que leurs proportions relatives diminuent<ref>Anaxagore, B10 ; Aristote, ''Génération des animaux'', II, 4, 740b26-29 ; Simplicius, ''Commentaire sur la Physique'', 460, 4-465, 19 (DK 59 A 45).</ref>. Contrairement aux apparences sensibles, il n'y a pas de transformation qualitative véritable, mais seulement des réarrangements quantitatifs<ref>Malcolm Schofield, 1980, p. 88-101 ; Daniel Graham, ''Explaining the Cosmos: The Ionian Tradition of Scientific Philosophy'', Princeton, Princeton University Press, 2006, p. 137-152.</ref>. Théophraste, dans son traité ''Sur les sensations'', rapporte qu'Anaxagore soutenait que « toute sensation s'accompagne de douleur » (πᾶσαν αἴσθησιν μετὰ λύπης εἶναι) précisément parce que percevoir implique un contact entre des qualités opposées<ref>Théophraste, ''De Sensibus'', 1, 27-29 (DK 59 A 92). Cf. également Aétius, IV, 9, 1 (DK 59 A 92).</ref>. Si le principe de prédominance explique pourquoi nous percevons des objets distincts, il explique aussi pourquoi cette perception n'est jamais parfaitement exacte : les sens ne peuvent discriminer que les différences marquées de proportion, mais les différences subtiles leur échappent<ref>Sextus Empiricus, ''Contre les mathématiciens'', VII, 90 (DK 59 B 21) : « Les apparences sont une vision des choses non-manifestes » (ὄψις τῶν ἀδήλων τὰ φαινόμενα).</ref>. === Synthèse : la réponse d'Anaxagore à Parménide === Les quatre principes métaphysiques que nous venons d'examiner, soit la conservation, le « tout est dans tout », la divisibilité infinie et la prédominance, forment un système cohérent qui constitue la réponse d'Anaxagore au défi parménidien<ref>Patricia Curd, 1998, p. 123-165 ; Malcolm Schofield, 1980, p. 27-86.</ref>. Anaxagore accepte l'impossibilité du passage de l'être au non-être, mais rejette les conséquences que Parménide en tire concernant l'unicité, l'immobilité et l'homogénéité de l'être. Premièrement, en remplaçant la génération et la corruption par le mélange et la séparation (B17), Anaxagore sauve les phénomènes sans violer l'interdit éléatique : rien ne naît véritablement, rien ne périt véritablement, il n'y a que réarrangement de ce qui existe déjà de toute éternité. Deuxièmement, en posant que tout est dans tout (B11, B6), Anaxagore explique comment des substances apparemment nouvelles peuvent émerger sans être créées ex nihilo : elles étaient déjà présentes dans le mélange, simplement imperceptibles en raison de leur faible proportion. Troisièmement, en affirmant la divisibilité infinie (B3, B6), Anaxagore garantit que le mélange universel ne pourra jamais être entièrement défait. Quatrièmement, en introduisant le principe de prédominance (B12), Anaxagore rend compte de la diversité phénoménale et de la possibilité de la perception, tout en maintenant l'ontologie du mélange universel. Anaxagore parvient ainsi à préserver les intuitions fondamentales de Parménide, à savoir la permanence de l'être, l'impossibilité du non-être et l'immutabilité substantielle, tout en rendant compte de la pluralité, du changement et du devenir qui caractérisent notre expérience du monde<ref>Patricia Curd, 1998, p. 165 : « Anaxagoras accepts the fundamental Eleatic constraint that what-is-not cannot be, but offers an account of the world that preserves both plurality and change. » Cf. également Daniel Graham, 2006, p. 137-152 ; G. E. R. Lloyd, ''Early Greek Science: Thales to Aristotle'', Londres, Chatto & Windus, 1970, p. 47-52.</ref>. == Les ingrédients primordiaux == La question de savoir quels sont exactement les ingrédients élémentaires (τὰ χρήματα) qui composent l'univers d'Anaxagore a suscité d'intenses débats parmi les commentateurs, depuis Aristote jusqu'aux spécialistes contemporains<ref>Pour une vue d'ensemble des débats, voir Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 99-138 ; Patricia Curd, ''Anaxagoras of Clazomenae : Fragments and Testimonia'', Toronto, University of Toronto Press, 2007, essais 2-4 ; Daniel Graham, « Was Anaxagoras a Reductionist? », ''Ancient Philosophy'', vol. 24, 2004, p. 1-18.</ref>. Le problème est rendu particulièrement complexe par le fait qu'Anaxagore ne fournit pas de liste systématique et exhaustive de ces ingrédients, et que les fragments conservés mentionnent des entités de natures apparemment différentes. Comme le souligne Malcolm Schofield, « malgré le rôle cardinal de la doctrine du "tout est dans tout" dans sa philosophie, les fragments qui ont survécu indiquent que dans son exposition, Anaxagore assigna au moins autant d'importance à son récit du mélange primordial et à sa description du Noûs et de son activité cosmogonique »<ref>Malcolm Schofield, 1980, p. 99.</ref>. === Les opposés === Dans les fragments B1, B2, B4b, B8, B12 et B15, Anaxagore énonce explicitement plusieurs paires d'opposés (τὰ ἐναντία) : l'humide et le sec (τὸ ὑγρόν καὶ τὸ ξηρόν), le chaud et le froid (τὸ θερμόν καὶ τὸ ψυχρόν), le lumineux et l'obscur (τὸ λαμπρόν καὶ τὸ ζοφερόν), le dense et le rare (τὸ πυκνόν καὶ τὸ ἀραιόν)<ref>Anaxagore, B1, B2, B4b, B8, B12, B15.</ref>. Ces qualités opposées jouent un rôle fondamental dans la cosmologie d'Anaxagore. Ce sont elles qui, par leur séparation progressive (ἀποκρίνεσθαι) à partir du mélange originel et leur réagencement ultérieur, donnent naissance à la diversité des phénomènes naturels<ref>Anaxagore, B12, B13, B15, B16 ; Aristote, ''Physique'', I, 4, 187b1-7 (DK 59 A 52).</ref>. Les opposés ne doivent pas être compris comme de simples attributs qui qualifieraient une substance sous-jacente. Dans la pensée présocratique en général, et chez Anaxagore en particulier, la distinction ultérieure entre substance et qualité n'existe pas encore avec la netteté qu'elle acquerra chez Aristote<ref>G. E. L. Owen, « Tithenai ta Phainomena », dans ''Aristotle et les problèmes de méthode'', éd. Suzanne Mansion, Louvain, Publications Universitaires, 1961, p. 83-103 ; Jonathan Barnes, ''The Presocratic Philosophers'', Londres, Routledge, 1982, p. 316-321.</ref>. Le chaud (τὸ θερμόν) n'est pas simplement une propriété qui affecterait une matière indéterminée ; c'est une réalité substantielle en soi, qui peut être présente en plus ou moins grande proportion dans un mélange donné. Comme l'a remarqué F. M. Cornford, ces opposés doivent être compris comme des « choses-qualités » (quality-things)<ref>F. M. Cornford, « Anaxagoras' Theory of Matter », ''Classical Quarterly'', vol. 24, 1930, p. 14-30, aux pages 16-17.</ref>. Gregory Vlastos précise que ces opposés doivent plutôt être compris « comme des formes d'énergie ou de puissance (δύναμις) »<ref>Gregory Vlastos, « The Physical Theory of Anaxagoras », ''Philosophical Review'', vol. 59, 1950, p. 31-57.</ref>. === L'interprétation « austère » : les opposés seuls === Paul Tannery, à la fin du XIXe siècle, fut le premier à contester l'interprétation aristotélicienne selon laquelle Anaxagore aurait postulé l'existence de « particules homéomères » (particules de chair, d'os, etc.)<ref>Paul Tannery, « La théorie de la matière d'Anaxagore », ''Revue Philosophique'', vol. 22, 1886, p. 255-274 ; repris dans ''Pour l'histoire de la science hellène'', Paris, Alcan, 1887, p. 275-290.</ref>. Tannery soutenait qu'Anaxagore ne parlait que de qualités : l'humide, le sec, le chaud, le froid, etc. John Burnet adopta une position similaire dans son ouvrage influent ''Early Greek Philosophy'', reconnaissant que « même lorsque la notion de qualité (ποιότης) avait été définie, cette manière de penser survécut »<ref>John Burnet, ''Early Greek Philosophy'', 4{{e}} édition, Londres, Adam and Charles Black, 1930, p. 256-264, citation p. 263.</ref>. Burnet s'appuyait notamment sur Galien, qui affirme dans son commentaire sur Hippocrate que « ce sont les qualités qui sont éternelles » chez Anaxagore<ref>Galien, ''Commentaire sur le traité hippocratique Des humeurs'', XVI, 32 Kühn.</ref>. Cette interprétation présente plusieurs avantages. Premièrement, elle se fonde étroitement sur les fragments conservés d'Anaxagore lui-même, plutôt que sur les reconstructions d'Aristote, qui écrivait près de cent cinquante ans après Anaxagore et avait ses propres préoccupations philosophiques. Deuxièmement, elle rend compte du rôle cosmologique crucial des opposés dans les fragments B12, B13, B15 et B16, où ce sont explicitement les opposés qui se séparent lors de la cosmogonie<ref>Malcolm Schofield, 1980, p. 107-113 ; Colin Strang, « The Physical Theory of Anaxagoras », ''Archiv für Geschichte der Philosophie'', vol. 45, 1963, p. 101-118.</ref>. Cette interprétation a été défendue au XXe siècle par plusieurs spécialistes éminents, notamment F. M. Cornford, Gregory Vlastos (avec certaines réserves), Colin Strang, Malcolm Schofield (avec des nuances importantes), Brad Inwood, David Sedley, et plus récemment Anna Marmodoro<ref>F. M. Cornford, 1930 ; Gregory Vlastos, 1950, p. 329-333 ; Colin Strang, 1963 ; Malcolm Schofield, 1980, p. 99-138 ; Brad Inwood, « Anaxagoras and Infinite Divisibility », ''Illinois Classical Studies'', vol. 11, 1986, p. 17-33 ; David Sedley, ''Creationism and Its Critics in Antiquity'', Berkeley, University of California Press, 2007, p. 24-26 ; Anna Marmodoro, « Anaxagoras's Qualitative Gunk », ''British Journal for the History of Philosophy'', vol. 23, 2015, p. 402-422.</ref>. === L'interprétation aristotélicienne : les homéomères === Toutefois, la question devient plus complexe lorsqu'on prend en considération les témoignages indirects, notamment ceux d'Aristote. Dans plusieurs passages, Aristote attribue à Anaxagore une doctrine des « homéomères » (τὰ ὁμοιομερῆ), c'est-à-dire des substances dont chaque partie est semblable au tout<ref>Aristote, ''Métaphysique'', I, 3, 984a11-16 ; Aristote, ''Génération et corruption'', I, 1, 314a18-24 (DK 59 A 46).</ref>. Les exemples donnés par Aristote incluent la chair, les os, le sang, la moelle, l'or, le bois, autant de substances naturelles que nous rencontrons dans l'expérience quotidienne. Cependant, le terme même « homéomère » n'apparaît nulle part dans les fragments conservés d'Anaxagore. C'est Aristote qui l'a introduit pour caractériser ce qu'il comprenait être la position d'Anaxagore<ref>Le terme ὁμοιομερής apparaît d'abord chez Aristote, ''Parties des animaux'', II, 1, 646b10-20 ; ''Génération et corruption'', I, 1, 314a18 sq. Cf. Pierre Pellegrin, « La théorie aristotélicienne des homéomères », ''Revue de Métaphysique et de Morale'', vol. 86, 1981, p. 449-467.</ref>. Cette discordance entre les fragments authentiques, qui mentionnent principalement des opposés, et l'interprétation aristotélicienne, qui privilégie les substances naturelles comme la chair et les os, a conduit à une longue controverse parmi les spécialistes modernes. Comme le souligne W. K. C. Guthrie, « le fait le plus gênant pour ceux qui souhaitent suivre Aristote est que le terme homéomères n'apparaît jamais dans les fragments d'Anaxagore lui-même, et que les fragments ne mentionnent jamais explicitement la chair, les os ou le sang comme des ingrédients élémentaires »<ref>W. K. C. Guthrie, 1965, p. 284-285.</ref>. L'interprétation « expansive », qui accepte pleinement le témoignage aristotélicien, a néanmoins été défendue par plusieurs spécialistes, notamment Arthur L. Peck dans les années 1920 et 1930, et plus récemment par George Kerferd<ref>Arthur L. Peck, « Anaxagoras : Predication as a Problem in Physics », ''Classical Quarterly'', vol. 25, 1931, p. 27-37 et p. 112-120 ; George B. Kerferd, « Anaxagoras and the Concept of Matter before Aristotle », ''Bulletin of the John Rylands Library'', vol. 52, 1969, p. 129-143.</ref>. === L'interprétation médiane === Une troisième voie, une interprétation « médiane », a été proposée notamment par Malcolm Schofield et Patricia Curd<ref>Malcolm Schofield, 1980, p. 99-138 ; Patricia Curd, 1998, p. 123-165 ; Patricia Curd, 2007, essais 2-4.</ref>. Selon cette lecture, l'ontologie d'Anaxagore comprend plusieurs catégories d'entités : les opposés, les substances naturelles fondamentales comme les métaux, la terre, l'air et l'éther, et les ingrédients biologiques comme la chair, le sang et les os. En revanche, les plantes, les animaux et leurs parties organiques ne seraient pas des éléments primordiaux mais des constructions naturelles résultant de l'agencement des ingrédients plus fondamentaux<ref>Patricia Curd, 2007, essais 2-3, p. 157-191 ; Malcolm Schofield, 1980, p. 132-138.</ref>. Cette interprétation médiane présente l'avantage de tenir compte à la fois des fragments authentiques et des témoignages d'Aristote, sans les opposer frontalement. Elle reconnaît que les opposés jouent un rôle cosmologique crucial, puisque c'est leur séparation qui déclenche la formation du cosmos (fragments B12, B13, B15), tout en admettant que les substances naturelles ont également une place dans l'ontologie d'Anaxagore. === Les semences (σπέρματα) === Un élément supplémentaire de complexité est introduit par la mention des « semences » (σπέρματα) dans les fragments B4a et B4b. Dans le fragment B4b, après avoir mentionné les opposés, Anaxagore ajoute : « et il y avait beaucoup de terre présente, et des semences infinies en nombre, ne se ressemblant en rien les unes aux autres » (πολλὴ δὲ γῆ ἐνῆν καὶ σπέρματα ἄπειρα πλῆθος οὐδὲν ἐοικότα ἀλλήλοις)<ref>Anaxagore, fragment B4b, cité par Simplicius, ''Commentaire sur la Physique'', 34, 29-35, 9 (DK 59 B 4b).</ref>. Le fragment B4a est encore plus explicite : « Il est juste de penser qu'il y avait dans toutes les choses qui étaient rassemblées beaucoup de choses de toutes sortes, et des semences de toutes choses, possédant des formes et des couleurs et des saveurs de toute espèce »<ref>Anaxagore, fragment B4a, cité par Simplicius, ''Commentaire sur la Physique'', 34, 18-29 (DK 59 B 4a).</ref>. La nature exacte de ces semences a fait l'objet de débats considérables parmi les commentateurs. Gregory Vlastos a proposé que σπέρμα soit un terme technique introduit par Anaxagore pour désigner un agrégat infinitésimal contenant tous les ingrédients, mais dans lequel un seul prédomine<ref>Gregory Vlastos, 1950, p. 338-342 ; cf. également J. E. Raven, « The Basis of Anaxagoras' Cosmology », ''Classical Quarterly'', vol. 4, 1954, p. 123-137.</ref>. David Lloyd a suggéré que les semences soient des portions pures (ou quasi-pures) d'opposés<ref>David Lloyd, « Anaxagoras on Life and Mind », ''Phronesis'', vol. 14, 1969, p. 246-251, note 1.</ref>. Une position intermédiaire, défendue par Malcolm Schofield, David Sedley, Patricia Curd et Daniel Gershenson, suggère que les semences doivent être comprises littéralement comme des semences biologiques ordinaires, c'est-à-dire des graines de plantes et des semences animales<ref>Malcolm Schofield, 1980, p. 119-132 ; David Sedley, 2007, p. 24-26 ; Patricia Curd, 2007, essai 2 ; Daniel E. Gershenson et Daniel A. Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 15-17.</ref>. Cette dernière interprétation présente plusieurs avantages. Elle ne fait pas violence au langage : lorsqu'Anaxagore parle de σπέρματα, il utilise le terme ordinaire pour « semences » sans lui donner un sens technique inhabituel. Elle explique la diversité infinie des semences mentionnée dans B4b. Elle est en outre corroborée par des témoignages anciens sur la biologie d'Anaxagore : Théophraste rapporte qu'« Anaxagore dit que l'air contient des semences de toutes choses, et que celles-ci, lorsqu'elles sont emportées avec l'eau, engendrent les plantes »<ref>Théophraste, ''Causes des plantes'', I, 5, 2 (DK 59 A 117).</ref>. De même, Hippolyte rapporte qu'« au commencement, les animaux naquirent de l'humide, du chaud et du terreux, puis plus tard les uns des autres »<ref>Hippolyte, ''Réfutation de toutes les hérésies'', I, 8, 12 (DK 59 A 42).</ref>. == L'état originel : tout ensemble == Le traité d'Anaxagore s'ouvrait par l'une des déclarations les plus célèbres et les plus discutées de la philosophie présocratique : « Toutes choses étaient ensemble » (ὁμοῦ πάντα χρήματα ἦν, fragment B1)<ref>Anaxagore, fragment B1, cité par Simplicius, ''Commentaire sur la Physique'', 155, 23-26 (DK 59 B 1). Diogène Laërce, II, 6, rapporte qu'Anaxagore « commença son traité d'une manière très attrayante ».</ref>. Cette formule programmatique, placée en position d'ouverture, exprime la thèse cosmogonique fondamentale d'Anaxagore concernant l'état primordial de l'univers. Plusieurs commentateurs, notamment G. E. L. Owen, ont souligné que la formulation d'Anaxagore constitue une réponse directe et délibérée à Parménide<ref>G. E. L. Owen, « The Place of the Timaeus in Plato's Dialogues », ''Classical Quarterly'', vol. 3, 1953, p. 79-95 ; repris dans ''Logic, Science and Dialectic'', Londres, Duckworth, 1986, p. 65-84.</ref>. Là où Parménide avait affirmé que l'être « est maintenant, tout ensemble, un, continu » (νῦν ἔστιν ὁμοῦ πᾶν, ἕν, συνεχές)<ref>Parménide, fragment B8, 5-6 (DK 28 B 8).</ref>, Anaxagore proclame que « toutes choses étaient ensemble ». Parménide affirmait l'unicité, la continuité, l'éternité présente et l'immobilité de l'être ; Anaxagore lui oppose la pluralité, la diversibilité infinie, l'existence passée et le devenir cosmogonique<ref>Malcolm Schofield, 1980, p. 64-65 ; Patricia Curd, 1998, p. 123-142.</ref>. === La description du mélange originel === Le fragment B1, dont Simplicius nous dit qu'il se trouvait près du début du traité d'Anaxagore, fournit une description plus complète de cet état primordial : <blockquote>Toutes choses étaient ensemble, illimitées et en multitude et en petitesse, car le petit aussi était illimité. Et toutes choses étant ensemble, rien n'était manifeste en raison de la petitesse. Car l'air et l'éther recouvraient toutes choses, tous deux étant illimités ; car ce sont eux qui sont les plus grands dans la totalité des choses, et en multitude et en grandeur.<ref>Anaxagore, fragment B1, cité par Simplicius, ''Commentaire sur la Physique'', 155, 23-157, 4 (DK 59 B 1).</ref></blockquote> Ce passage dense pose plusieurs problèmes interprétatifs considérables qui ont occupé les commentateurs depuis l'Antiquité. Quatre caractéristiques principales du mélange originel y sont énoncées : toutes choses étaient ensemble ; elles étaient illimitées en multitude et en petitesse ; rien n'était manifeste en raison de la petitesse ; l'air et l'éther recouvraient toutes choses. L'expression « illimitées en multitude et en petitesse » a suscité d'intenses débats interprétatifs. Deux lectures principales s'affrontent, que Malcolm Schofield a désignées respectivement comme l'interprétation « particulaire » et l'interprétation « proportionnelle »<ref>Malcolm Schofield, 1980, p. 70-99.</ref>. L'interprétation particulaire comprend « illimitées en multitude » comme signifiant qu'il existait un nombre infini de choses distinctes dans le mélange originel<ref>Gregory Vlastos, 1950, p. 31-57 ; W. K. C. Guthrie, 1965, p. 277-285 ; David Sider, 2005, p. 56-62.</ref>. L'interprétation proportionnelle, en revanche, refuse de comprendre le mélange originel comme une collection de particules discrètes : « illimitées en petitesse » signifierait plutôt que chaque ingrédient était présent dans le mélange en une proportion infiniment petite par rapport à la totalité<ref>Malcolm Schofield, 1980, p. 75-89 ; Colin Strang, 1963, p. 101-118 ; Jonathan Barnes, 1982, p. 39-53.</ref>. Le débat entre ces deux interprétations n'est pas résolu, et chacune présente des avantages et des difficultés<ref>Pour une discussion équilibrée des deux positions, voir Patricia Curd, 2007, essai 4, p. 192-213 ; Daniel Graham, 2006, p. 137-152.</ref>. === L'imperceptibilité du mélange === La troisième caractéristique du mélange originel est son indistinction : « rien n'était manifeste » (οὐδὲν ἔνδηλον ἦν). Anaxagore explique cette imperceptibilité par deux facteurs complémentaires : « en raison de la petitesse » (ὑπὸ σμικρότητος), et parce que « l'air et l'éther recouvraient toutes choses » (πάντα γὰρ ἀὴρ καὶ αἰθὴρ κατεῖχε). L'air (ἀήρ) désigne ici, conformément à l'usage ionien archaïque, une substance sombre, humide, froide et dense<ref>Anaximène, fragments DK 13 A 7, B 2 ; cf. Charles Kahn, ''Anaximander and the Origins of Greek Cosmology'', New York, Columbia University Press, 1960, p. 133-162 ; Daniel Graham, 2006, p. 93-102.</ref>. L'éther (αἰθήρ), en revanche, désigne la substance lumineuse, chaude, sèche et rare<ref>Anaxagore, B15. Cf. W. K. C. Guthrie, 1965, p. 301-304 ; Malcolm Schofield, 1980, p. 81-83.</ref>. Ces deux substances, affirme Anaxagore, « sont les plus grandes dans la totalité des choses, et en multitude et en grandeur ». Cela signifie qu'elles étaient présentes dans le mélange originel en proportions largement supérieures à tous les autres ingrédients. Le fragment B4b confirme cette description : <blockquote>Mais avant que ces choses ne fussent séparées, lorsque toutes choses étaient ensemble, aucune couleur n'était manifeste. Car le mélange de toutes choses l'empêchait : celui du sec et de l'humide, du chaud et du froid, du lumineux et de l'obscur, ainsi que d'une grande quantité de terre présente et de semences illimitées en multitude, ne se ressemblant en rien les unes aux autres.<ref>Anaxagore, fragment B4b (DK 59 B 4b).</ref></blockquote> Ce passage confirme que l'indistinction du mélange originel n'était pas due à l'absence des ingrédients, mais à leur mélange si intime que leurs caractéristiques respectives se neutralisaient mutuellement. === L'immobilité originelle === Plusieurs témoignages anciens rapportent qu'avant l'intervention du Noûs, le mélange universel était au repos<ref>Aristote, ''Physique'', VIII, 1, 250b24-26 (DK 59 A 64).</ref>. Cette immobilité primordiale pose un problème philosophique considérable : si le mélange était éternellement au repos, qu'est-ce qui a pu le mettre en mouvement ? Anaxagore répond en postulant l'existence du Noûs (Νοῦς, Intellect ou Esprit), une entité distincte de toutes les substances matérielles, qui possède le pouvoir d'initier le mouvement<ref>Anaxagore, B12.</ref>. Mais cette réponse soulève elle-même de nouvelles difficultés, qui seront examinées plus loin. === L'étendue du mélange originel === Le fragment B1 affirme que l'air et l'éther « tous deux étaient illimités » (ἄμφω ἄπειρα ἐόντα), et le fragment B2 précise : « L'air et l'éther se séparent de la multitude environnante, et la multitude environnante est illimitée en quantité »<ref>Anaxagore, fragment B2 (DK 59 B 2).</ref>. Ces affirmations suggèrent que le mélange originel était spatialement infini<ref>Aristote, ''Physique'', III, 4, 203a19-23 (DK 59 A 43).</ref>. Le fragment B12 indique que « le Noûs commença à exercer son pouvoir à partir d'un petit commencement, puis la rotation s'étendit sur une région plus grande, et s'étendra sur une région plus grande encore »<ref>Anaxagore, B12 (DK 59 B 12).</ref>. Cela suggère que le processus cosmogonique, bien qu'il soit en cours depuis un temps considérable, n'a pas encore affecté la totalité du mélange infini. Cette conception d'un univers partiellement ordonné, où la cosmogonie est encore en cours dans les régions périphériques tandis que notre monde déjà structuré occupe une région centrale, est l'une des idées les plus originales d'Anaxagore<ref>W. K. C. Guthrie, 1965, p. 327-331 ; Patricia Curd, 2007, essai 5, p. 214-237 ; Daniel Graham, 2006, p. 148-152.</ref>. == Le Noûs : l'Intellect cosmique == Le fragment B12 d'Anaxagore, le plus long et le plus célèbre de tous les fragments conservés, est entièrement consacré au Noûs (Νοῦς, « Intellect » ou « Esprit »). Ce passage, d'une grandeur solennelle et d'une intensité remarquable, constitue l'un des textes les plus puissants de toute la prose grecque archaïque<ref>Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 3-32 ; Karl Deichgräber, « Anaxagoras Stil », ''Philologus'', vol. 87, 1933, p. 15-26.</ref>. Dans ce fragment, Anaxagore expose sa doctrine du Noûs comme principe du mouvement cosmogonique et source d'ordre dans l'univers. Le Noûs représente l'innovation philosophique la plus originale d'Anaxagore<ref>W. K. C. Guthrie, 1965, p. 272-279 ; Diogène Laërce, II, 6 (DK 59 A 1), rapporte qu'Anaxagore était surnommé « Monsieur Intellect » (ὁ Νοῦς) en raison de l'importance centrale qu'il donnait à cette notion.</ref>. Il convient d'emblée de préciser, afin d'éviter un contresens répandu dans la littérature secondaire ancienne comme moderne, que le Noûs d'Anaxagore est d'abord une ''cause motrice'' du cosmos : il initie et contrôle le mouvement rotatoire qui produit la séparation des ingrédients à partir du mélange originel. Anaxagore ne dit nulle part, dans les fragments conservés, que l'Intellect ordonne les choses ''parce qu'il serait meilleur qu'elles soient ainsi''. Or, c'est précisément cette affirmation que Socrate et Platon auraient voulu trouver chez le philosophe de Clazomènes, et dont ils lui reprocheront ensuite l'absence dans le passage célèbre du ''Phédon'' (97b-98c). La lecture proprement « téléologique » du Noûs, qui en fait une cause finale organisant le monde selon la Raison du Bien, est donc une reconstruction rétrospective, due à Platon puis à Aristote, et répercutée par toute une tradition doxographique postérieure. Elle projette sur Anaxagore les exigences explicatives du platonisme et de l'aristotélisme, exigences qu'Anaxagore ne partage pas sous cette forme. Plus fondamentalement encore, appliquer sans précaution aux présocratiques la distinction aristotélicienne des quatre causes (matérielle, formelle, efficiente, finale) relève d'un anachronisme conceptuel. Cette distinction est une construction d'Aristote, élaborée dans la ''Physique'' et la ''Métaphysique'' pour penser son propre rapport critique à ses prédécesseurs. La plupart des penseurs antérieurs à Aristote, Anaxagore compris, ne disposent pas d'un tel vocabulaire et n'organisent pas leur réflexion autour de cette quadripartition. Le Noûs d'Anaxagore ne peut être intégralement rangé dans aucune des quatre catégories aristotéliciennes : il est quelque chose comme une cause motrice doublée d'un principe ordonnateur, sans que cet ordre implique nécessairement une référence à un bien objectif. Il est donc plus juste de parler, comme le font les spécialistes contemporains, d'une fonction kinétique et cognitive du Noûs, plutôt que d'y voir la première formulation d'une téléologie rigoureuse<ref>Sur ce point, voir Patricia Curd, « Anaxagoras », ''Stanford Encyclopedia of Philosophy'', 2007 (révision substantielle 2019), section sur le Noûs ; Malcolm Schofield, 1980, p. 55-70 ; James Lesher, « Mind's Knowledge and Powers of Control in Anaxagoras », ''Phronesis'', vol. 40, 1995, p. 125-142. Sur l'anachronisme que représente l'application des quatre causes aristotéliciennes aux physiciens présocratiques, voir l'article « Presocratic Philosophy », ''Stanford Encyclopedia of Philosophy'', éd. Daniel Graham, 2019. Cf. également Christian Vassallo, « Nous, Motion, and Teleology in Anaxagoras », ''Oxford Studies in Ancient Philosophy'', vol. 50, 2016, p. 1-32, qui défend une lecture téléologique minimale et nuancée, restée minoritaire.</ref>. === La séparation du Noûs d'avec toutes choses === Le fragment B12 s'ouvre par une affirmation qui constitue la thèse fondamentale d'Anaxagore concernant le Noûs : <blockquote>Les autres choses ont une part de tout, mais le Noûs est illimité et autonome, et il n'a été mélangé à aucune chose, mais il existe seul, lui-même par lui-même.<ref>Anaxagore, fragment B12, cité par Simplicius, ''Commentaire sur la Physique'', 156, 13-15 (DK 59 B 12).</ref></blockquote> Cette phrase affirme que le Noûs constitue une exception au principe universel « tout est dans tout ». Tandis que toutes les substances matérielles contiennent en elles-mêmes des portions de toutes les autres substances, le Noûs, lui, demeure entièrement pur et séparé<ref>Anaxagore, B11 : « Dans toute chose il y a une part de toute chose, excepté le Noûs ; et il y a certaines choses dans lesquelles il y a aussi du Noûs ».</ref>. Trois attributs sont explicitement prédiqués du Noûs : il est « illimité » (ἄπειρον), « autonome » (αὐτοκρατές), et « non mélangé à aucune chose ». Anaxagore justifie cette séparation par un argument remarquable : <blockquote>Car s'il n'existait pas par lui-même, mais s'il avait été mélangé à quelque autre chose, il participerait de toutes les choses, s'il avait été mélangé à quoi que ce soit. Car dans toute chose il y a une part de toute chose, comme je l'ai dit précédemment. Et les choses mélangées avec lui l'empêcheraient, de sorte qu'il ne dominerait aucune chose de la même manière qu'il la domine en fait, étant seul par lui-même.<ref>Anaxagore, B12, lignes 156, 15-20.</ref></blockquote> L'argument procède ainsi : si le Noûs était mélangé à quoi que ce soit, il contiendrait une part de tout ; les substances mélangées l'empêcheraient d'exercer son pouvoir de contrôle ; or le Noûs exerce effectivement ce pouvoir ; donc le Noûs n'est mélangé à aucune chose<ref>Malcolm Schofield, 1980, p. 12-14 ; Jonathan Barnes, 1982, p. 375-377.</ref>. Plusieurs lectures de la prémisse cruciale, c'est-à-dire de la raison pour laquelle le mélange empêcherait l'action du Noûs, ont été proposées et ne sont pas incompatibles entre elles<ref>Patricia Curd, 2007, essai 5, p. 220-225 ; Christian Vassallo, « Nous, Motion, and Teleology in Anaxagoras », ''Oxford Studies in Ancient Philosophy'', vol. 50, 2016, p. 1-32, aux pages 8-18.</ref>. === Les attributs du Noûs === Après avoir établi la séparation du Noûs, Anaxagore énonce une série d'attributs qui caractérisent sa nature. Le style employé ici est celui de la « prédication solennelle » (feierliche Prädikation), identifié par Karl Deichgräber comme caractéristique du style hymnico-religieux archaïque<ref>Karl Deichgräber, 1933, p. 16-22 ; Eduard Norden, ''Agnostos Theos'', Leipzig, Teubner, 1913, p. 3-29 ; Malcolm Schofield, 1980, p. 4-9.</ref> : <blockquote>Car il est le plus fin de toutes choses et le plus pur, et il possède toute connaissance à l'égard de toute chose et il a la plus grande force ; et toutes les choses qui ont une âme, les plus grandes comme les plus petites, toutes le Noûs les domine.<ref>Anaxagore, B12, lignes 156, 20-24.</ref></blockquote> Le premier attribut, qui qualifie le Noûs de « plus fin » et « plus pur » de toutes choses, a suscité des interprétations divergentes depuis l'Antiquité. Certains y voient l'affirmation, encore en gestation conceptuelle, de l'immatérialité du Noûs<ref>Aristote, ''De l'âme'', III, 4, 429a18-24 ; W. K. C. Guthrie, 1965, p. 278-279.</ref> ; d'autres, en s'appuyant sur le fait qu'Anaxagore utilise l'adjectif λεπτός ailleurs pour décrire l'eau de mer, soutiennent que ces termes doivent être pris en un sens physique : le Noûs serait alors une substance matérielle d'une extrême finesse et d'une grande pureté, proche des réalités les plus ténues du monde sensible<ref>Théophraste, dans Simplicius, ''Commentaire sur la Physique'', 27, 2-3 (DK 59 A 41) ; John Burnet, 1930, p. 268-269 ; Daniel Gershenson et Daniel Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 27-33.</ref>. Une position médiane suggère qu'Anaxagore n'avait pas encore développé le concept d'incorporéité dans sa pleine rigueur conceptuelle, qui suppose une distinction nette du matériel et de l'immatériel élaborée plus tard par Platon, mais qu'il cherchait néanmoins à exprimer, avec les ressources linguistiques dont il disposait, l'idée que le Noûs est d'une nature fondamentalement différente de toutes les autres substances<ref>Malcolm Schofield, 1980, p. 18-22.</ref>. Le deuxième attribut, qui concerne la connaissance, affirme que le Noûs « possède toute connaissance à l'égard de toute chose » : il s'agit ici d'une forme d'omniscience cosmique. Le troisième, qui concerne la puissance, précise que le Noûs « a la plus grande force », cette force étant liée à sa connaissance selon un schéma que l'on retrouvera chez Aristote et qui fait de la puissance l'envers opératif du savoir<ref>Malcolm Schofield, 1980, p. 22-24.</ref>. Le quatrième attribut, qui concerne le contrôle exercé par le Noûs sur tous les êtres vivants, est cohérent avec le fragment B11, selon lequel il y a du Noûs dans certaines choses, c'est-à-dire dans les êtres animés<ref>Anaxagore, B11. Cf. Malcolm Schofield, 1980, p. 25-27 ; Patricia Curd, 2007, essai 5, p. 225-230.</ref>. === Le rôle cosmogonique du Noûs === Après avoir décrit la nature du Noûs, Anaxagore expose son rôle dans la cosmogonie : <blockquote>Et le Noûs a dominé la révolution entière, de sorte qu'elle a commencé à tourner au commencement. Et d'abord elle a commencé à tourner à partir d'une petite région, mais elle tourne sur une région plus grande, et elle tournera sur une région plus grande encore. Et toutes les choses qui étaient mélangées ensemble et séparées et distinguées, toutes le Noûs les a connues. Et toutes les choses qui devaient être, celles qui étaient, celles qui sont maintenant et celles qui seront, toutes le Noûs les a mises en ordre, et aussi cette révolution dans laquelle tournent maintenant les astres et le soleil et la lune et l'air et l'éther qui sont en train d'être séparés.<ref>Anaxagore, B12, lignes 156, 24-157, 3.</ref></blockquote> Ce passage affirme trois choses concernant l'activité cosmogonique du Noûs. D'abord, l'Intellect initie un mouvement de révolution (περιχώρησις) dans le mélange originel, jusqu'alors immobile. Ensuite, ce mouvement, commencé dans une petite région, s'est étendu progressivement et continue de s'étendre, ce qui implique que la cosmogonie n'est pas un événement ponctuel passé, mais un processus encore en cours dans les régions périphériques du mélange infini. Enfin, c'est ce mouvement qui a produit la séparation (ἀπόκρισις) et la distinction (διάκρισις) des ingrédients, donnant naissance au cosmos ordonné que nous observons<ref>Simplicius, ''Commentaire sur la Physique'', 300, 31-301, 1 ; Aristote, ''Physique'', VIII, 1, 250b24-252a5 (DK 59 A 64).</ref>. Le mécanisme cosmogonique proprement dit est d'ordre mécanique : c'est le mouvement rotatoire qui, par sa force centrifuge, sépare les substances denses des substances rares<ref>Anaxagore, B12, B13, B15, B16.</ref>. Le Noûs n'intervient donc pas directement dans chaque détail de la cosmogonie : il initie le mouvement rotatoire, et celui-ci produit ensuite mécaniquement la séparation et la réorganisation des substances. Cette répartition des rôles entre un principe moteur intelligent et un processus mécanique explique pourquoi Platon et Aristote pourront reprocher à Anaxagore de « laisser faire » la matière après avoir introduit l'Intellect : à leurs yeux, introduire une cause intelligente sans lui confier l'organisation détaillée du monde revient à lui refuser son rôle véritable<ref>Malcolm Schofield, 1980, p. 27-32 ; Daniel Graham, 2006, p. 148-152.</ref>. Le texte affirme cependant aussi que « le Noûs a mis en ordre » (διεκόσμησε νοῦς) toutes choses, passées, présentes et futures. Le verbe διακοσμεῖν signifie « mettre en ordre » ou « arranger ». Il ne faut pas en conclure trop vite à une téléologie au sens fort, comme si les choses étaient disposées en vue du bien : Anaxagore ne fournit pas, dans les fragments qui nous sont parvenus, un tel principe évaluatif. Le Noûs connaît et dispose, il est à la fois intelligent et ordonnateur, mais rien dans les textes n'indique qu'il ordonne les choses ''parce qu'elles seraient ainsi meilleures''. C'est précisément ce point qui sera au cœur de la critique platonicienne dans le ''Phédon'' et qu'il faut examiner avec soin<ref>Christian Vassallo, 2016, a défendu l'idée qu'il y aurait malgré tout une dimension téléologique faible chez Anaxagore ; cette lecture reste minoritaire et contestée. Voir au contraire la synthèse de Patricia Curd, ''SEP'' 2007 (révision substantielle 2019).</ref>. === La critique platonicienne et aristotélicienne === Dans le ''Phédon'', Platon fait raconter par Socrate sa déception à la lecture du livre d'Anaxagore : <blockquote>Un jour, j'entendis quelqu'un lire dans un livre d'Anaxagore, disant que c'est l'Intellect qui met tout en ordre et qui est la cause de toutes choses. Je fus ravi de cette cause, et il me sembla qu'il était en quelque sorte bon que l'Intellect fût la cause de tout ; et je pensai que, s'il en est ainsi, l'Intellect qui met tout en ordre doit tout ordonner et disposer chaque chose de la manière qui est la meilleure. [...] Mais cette merveilleuse espérance, mon ami, me fut enlevée lorsque, progressant dans ma lecture, je vis que cet homme ne fait aucun usage de l'Intellect, qu'il ne lui attribue aucune responsabilité dans la mise en ordre des choses, mais qu'il allègue comme causes l'air, l'éther, l'eau et beaucoup d'autres choses absurdes.<ref>Platon, ''Phédon'', 97b-98c (DK 59 A 47).</ref></blockquote> Ce passage est capital, et il faut en saisir exactement la portée pour éviter le contresens le plus tenace de la tradition exégétique. Socrate ne dit pas qu'Anaxagore avait effectivement proposé une explication par le « meilleur » et qu'il y aurait manqué dans le détail : il dit qu'il ''espérait'' trouver une telle explication, et qu'il fut déçu de ne pas la trouver. La formule « doit tout ordonner et disposer chaque chose de la manière qui est la meilleure » exprime donc l'attente socratique, c'est-à-dire ce que Socrate aurait voulu lire, non la doctrine effective d'Anaxagore. La nuance est décisive. Le texte d'Anaxagore, dans les fragments que nous possédons, affirme que le Noûs connaît toutes choses et qu'il les met en ordre par l'intermédiaire du tourbillon cosmogonique. Il n'affirme pas que cet ordre soit le meilleur possible, ni que le Noûs vise le bien en produisant cet ordre. C'est Socrate qui, lisant Anaxagore, projette ses propres attentes philosophiques : si le monde est ordonné par un Intellect, pense Socrate, alors il doit l'être en vue du bien, puisqu'un Intellect rationnel ne peut que vouloir le meilleur. Cette inférence paraît évidente à Socrate, mais elle n'est pas anaxagoréenne. La lecture qui fait du Noûs d'Anaxagore une « cause téléologique » ou une « cause finale » est, par conséquent, une reconstruction rétrospective qui projette sur le Clazoménien les exigences platoniciennes (le Bien comme cause) puis aristotéliciennes (la cause finale comme l'une des quatre causes)<ref>Malcolm Schofield, 1980, p. 55-70 ; James Lesher, 1995, p. 125-142 ; Patricia Curd, ''SEP'', 2007 (révision substantielle 2019).</ref>. Il faut ici distinguer deux choses qui sont souvent confondues : d'une part, l'attribution au Noûs d'un rôle moteur et cognitif, qui est effectivement anaxagoréenne ; d'autre part, l'attribution au Noûs d'un principe évaluatif selon lequel le monde serait disposé en vue du meilleur, qui ne l'est pas. Le reproche socratique ne consiste donc pas à accuser Anaxagore d'incohérence interne (avoir posé une téléologie puis l'avoir abandonnée), mais à regretter qu'il n'ait pas poussé sa pensée jusqu'à la téléologie que Socrate lui aurait souhaitée. La différence est subtile mais philosophiquement essentielle : elle marque le lieu précis où la pensée présocratique cède la place à la pensée classique. Aristote reprend et durcit la critique dans la ''Métaphysique'' : <blockquote>Anaxagore utilise l'Intellect comme un ''deus ex machina'' pour la fabrication du monde ; et quand il est embarrassé pour expliquer pourquoi quelque chose est nécessairement ainsi, il le fait intervenir. Mais dans tous les autres cas, il allègue comme causes toutes sortes de choses plutôt que l'Intellect.<ref>Aristote, ''Métaphysique'', I, 4, 985a18-21 (DK 59 A 47).</ref></blockquote> Le reproche d'Aristote, comme celui de Socrate-Platon, présuppose un idéal explicatif téléologique qui n'est pas celui d'Anaxagore. Il ne faut donc pas conclure qu'Anaxagore « introduit le Noûs comme cause téléologique puis échoue à l'utiliser » : il introduit le Noûs comme cause motrice et ordonnatrice, et ses critiques l'accusent de ne pas avoir ''aussi'' développé une explication par le meilleur qu'eux attendent. Cette distinction entre la doctrine effective d'Anaxagore et la doctrine que ses successeurs auraient voulu lire chez lui commande toute l'interprétation correcte de son héritage : l'histoire de la téléologie philosophique ne commence pas, en toute rigueur, avec Anaxagore, mais avec la déception philosophique de Socrate face à Anaxagore, puis avec la construction proprement platonicienne du Démiurge et aristotélicienne du Premier Moteur. === Synthèse : l'innovation du Noûs === L'introduction du Noûs par Anaxagore constitue une innovation philosophique à plusieurs titres. Premièrement, Anaxagore est, parmi les penseurs grecs dont nous avons conservé les doctrines, le premier à poser explicitement l'existence d'une entité qui, bien qu'elle agisse sur la matière, n'est mélangée à aucune substance matérielle<ref>W. K. C. Guthrie, 1965, p. 278-279 ; Daniel Gershenson et Daniel Greenberg, 1964, p. 27-33.</ref>. Cette conception préparera, sans y être pour autant équivalente, les doctrines ultérieures de l'âme immatérielle chez Platon et de l'intellect séparé chez Aristote<ref>Platon, ''Phédon'', 78b-84b ; Aristote, ''De l'âme'', III, 5, 430a10-25. Cf. Edward Hussey, ''The Presocratics'', Londres, Duckworth, 1972, p. 138-141.</ref>. Deuxièmement, Anaxagore identifie une cause unique pour le mouvement cosmique et l'ordre qui en résulte<ref>Aristote, ''Métaphysique'', I, 3, 984b15-20, qualifie cette innovation de « sobre ».</ref>. En désignant le principe cosmique par le terme Νοῦς, mot habituellement associé à l'intelligence et à la pensée, Anaxagore suggère que l'ordre du monde n'est pas aveugle : il y a dans le cosmos quelque chose d'intelligent qui connaît et qui dispose. C'est cette suggestion qui sera reprise et radicalisée par Socrate, Platon et Aristote, au prix d'une transformation dont il faut reconnaître qu'elle va au-delà de ce qu'affirme Anaxagore lui-même<ref>Malcolm Schofield, 1980, p. 3-32 ; W. K. C. Guthrie, 1965, p. 327-331.</ref>. == La cosmogonie et la cosmologie == La cosmogonie et la cosmologie d'Anaxagore constituent l'application de ses principes métaphysiques. Dans ces domaines, Anaxagore se montre à la fois héritier de la tradition ionienne et novateur, proposant des explications naturalistes des phénomènes célestes et météorologiques<ref>W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 301-331 ; Daniel Gershenson et Daniel Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 34-55.</ref>. === Le mouvement rotatoire cosmogonique === Le processus cosmogonique commence lorsque le Noûs initie un mouvement de rotation (περιχώρησις) dans le mélange originel<ref>Anaxagore, fragment B12 (DK 59 B 12).</ref>. Ce mouvement rotatoire ne doit pas être conçu comme une simple rotation uniforme de l'ensemble du mélange, mais comme un tourbillon (δῖνος) ou un vortex dont la vitesse et l'étendue augmentent progressivement<ref>Aristote, ''Du Ciel'', II, 13, 295a9-14 (DK 59 A 88).</ref>. Anaxagore affirme que ce mouvement initial était d'une rapidité extraordinaire : « Rien de ce qui existe maintenant chez les hommes n'est aussi rapide, mais [il était] certainement plusieurs fois plus rapide »<ref>Anaxagore, fragment B9 (DK 59 B 9).</ref>. Le mécanisme de la séparation cosmogonique est mécanique. Le mouvement rotatoire produit une force centrifuge qui pousse les substances rares vers la périphérie et attire les substances denses vers le centre<ref>Anaxagore, B12, B15 ; Aristote, ''Du Ciel'', III, 2, 300b1-8 (DK 59 A 88).</ref>. Le fragment B15 décrit ce processus : <blockquote>Le dense et l'humide et le froid et l'obscur se rassemblèrent ici, là où maintenant est la terre, tandis que le rare et le chaud et le sec se retirèrent vers les régions lointaines de l'éther.<ref>Anaxagore, fragment B15 (DK 59 B 15).</ref></blockquote> Ce processus de séparation n'est jamais achevé. Conformément au principe que « rien n'est complètement séparé » (fragment B8), la rotation continue indéfiniment à produire des séparations et des mélanges partiels<ref>Anaxagore, B8, B12.</ref>. === La formation de la terre === Au centre du tourbillon cosmique, les substances denses, humides, froides et obscures se sont concentrées pour former la terre<ref>Anaxagore, B15 ; Aristote, ''Du Ciel'', III, 2, 300b8-16 (DK 59 A 88).</ref>. Selon Anaxagore, la terre a la forme d'un disque plat<ref>Hippolyte, ''Réfutation de toutes les hérésies'', I, 8, 3 (DK 59 A 42) ; Aristote, ''Du Ciel'', II, 13, 294b13-15 (DK 59 A 88).</ref>, conception traditionnelle dans la cosmologie ionienne<ref>Charles Kahn, ''Anaximander and the Origins of Greek Cosmology'', New York, Columbia University Press, 1960, p. 76-116 ; Daniel Graham, 2006, p. 93-102.</ref>. Anaxagore répond à la question de la stabilité de la terre en affirmant qu'elle demeure immobile parce qu'elle repose sur l'air (ἀήρ) qui la supporte<ref>Aristote, ''Du Ciel'', II, 13, 294b13-21 (DK 59 A 88) : « Anaxagore dit que la terre demeure immobile en raison de son égalité et de la grandeur, car elle ne coupe pas l'air mais le couvre comme un couvercle ».</ref>. Anaxagore aurait même effectué des démonstrations expérimentales avec des clepsydres pour montrer que l'air possède une résistance élastique capable de supporter des corps<ref>Aristote, ''Physique'', IV, 6, 213a22-27 (DK 59 A 68) ; Daniel Gershenson et Daniel Greenberg, 1964, p. 40-43.</ref>. === La formation des corps célestes === Les corps célestes, soleil, lune et étoiles, se sont formés à partir des substances rares, chaudes et sèches projetées vers la périphérie par la force du tourbillon cosmique<ref>Anaxagore, B15 ; Hippolyte, DK 59 A 42.</ref>. Selon cette théorie, les astres ne sont pas des êtres divins mais des masses de pierre ou de métal incandescent, rougies par leur mouvement rapide à travers l'éther<ref>Platon, ''Apologie de Socrate'', 26d (DK 59 A 35) ; Hippolyte, DK 59 A 42. Cette thèse est l'une de celles qui fonderont, selon Plutarque, les accusations d'impiété contre Anaxagore.</ref>. Anaxagore soutenait que le soleil est une masse de pierre ou de métal incandescent<ref>Platon, ''Apologie'', 26d (DK 59 A 35) ; Hippolyte, DK 59 A 42 ; Diogène Laërce, II, 8 (DK 59 A 1) ; Aétius, II, 20, 6 (DK 59 A 72).</ref>. Quant à sa taille, Anaxagore estimait qu'« il est plus grand que le Péloponnèse »<ref>Aétius, II, 21, 3 (DK 59 A 72) ; Plutarque, ''Vie de Périclès'', 6, 2 (DK 59 A 1).</ref>, affirmation qui devait paraître extravagante aux Grecs de son époque, habitués à voir dans le soleil un astre de dimensions modestes. Anaxagore sous-estimait bien sûr très considérablement la taille réelle du soleil, mais l'audace consistait à oser le comparer à une portion de la terre grecque plutôt qu'à un simple disque lumineux. La lune, selon Anaxagore, est elle aussi un corps rocheux, semblable à la terre<ref>Platon, ''Apologie'', 26d ; Hippolyte, DK 59 A 42.</ref>. Contrairement au soleil, la lune ne produit pas sa propre lumière : elle brille par réflexion de la lumière solaire<ref>Plutarque, ''Contre Colotès'', 1116a-b (DK 59 A 77) ; Hippolyte, DK 59 A 42. L'attribution exclusive de cette découverte à Anaxagore doit être nuancée : certains témoignages anciens la rapportent également à Parménide (DK 28 B 14-15), et la question de la priorité reste discutée. Voir Daniel Graham, ''Science before Socrates'', 2006, p. 115-116.</ref>. Cette thèse permit à Anaxagore, qu'il en soit ou non le premier découvreur, d'expliquer correctement les phases lunaires et la mécanique des éclipses<ref>Hippolyte, DK 59 A 42 ; Aétius, II, 29, 6 (DK 59 A 77).</ref>. Une éclipse de lune se produit lorsque la terre s'interpose entre le soleil et la lune, projetant son ombre sur celle-ci<ref>Hippolyte, DK 59 A 42. Cf. Dirk Couprie, « Anaxagoras on the Milky Way and Lunar Eclipses », ''Rhizomata'', vol. 5, 2017, p. 127-147.</ref>. Une éclipse de soleil se produit inversement lorsque la lune s'interpose entre le soleil et la terre<ref>Hippolyte, DK 59 A 42 ; Plutarque, ''Vie de Périclès'', 35, 2 (DK 59 A 18).</ref>. Ces explications, fondées sur une compréhension correcte de la géométrie des positions relatives du soleil, de la terre et de la lune, représentent une avancée considérable dans l'histoire de l'astronomie grecque<ref>W. K. C. Guthrie, 1965, p. 313-316 ; Daniel Graham, 2006, p. 149-150.</ref>. Selon la tradition doxographique, Anaxagore aurait même prédit une éclipse solaire<ref>Plutarque, ''Vie de Périclès'', 35, 2 (DK 59 A 18). La tradition évoque plusieurs éclipses possibles au V{{e}} siècle av. J.-C., sans que l'on puisse identifier avec certitude celle dont il s'agit. Les historiens modernes sont pour la plupart sceptiques quant à la possibilité qu'Anaxagore ait pu effectuer une prédiction précise, faute d'un modèle astronomique suffisamment développé. Cf. W. K. C. Guthrie, 1965, p. 314, note 1 ; Daniel Graham, 2006, p. 150, note 25.</ref>. Anaxagore soutenait enfin que la surface de la lune présente des irrégularités analogues à celles de la terre, montagnes et vallées, intuition que les observations télescopiques confirmeront deux millénaires plus tard<ref>Aétius, II, 30, 2 (DK 59 A 77).</ref>. La Voie lactée s'explique selon Anaxagore par l'ombre que projette la terre dans l'espace : dans les régions du ciel situées dans l'ombre de la terre, le soleil ne peut éclairer les étoiles ; celles-ci deviennent donc toutes visibles, même les plus faibles<ref>Aristote, ''Météorologiques'', I, 8, 345a25-31 (DK 59 A 80). Cf. Dirk Couprie, 2017.</ref>. === Le météorite d'Aigos Potamos === L'événement qui contribua à établir la réputation d'Anaxagore fut la chute d'un météorite de grande taille à Aigos Potamos, sur la rive européenne de l'Hellespont, vers 467 avant notre ère<ref>Pline l'Ancien, ''Histoire naturelle'', II, 149 (DK 59 A 11) ; Plutarque, ''Vie de Lysandre'', 12 (DK 59 A 12) ; Diogène Laërce, II, 10 (DK 59 A 1).</ref>. Selon les témoignages anciens, Anaxagore aurait prédit cette chute. Les historiens modernes sont divisés sur cette question : certains pensent qu'il s'agit d'une légende élaborée après coup, d'autres estiment qu'Anaxagore avait peut-être observé qu'un objet céleste se fragmentait<ref>Daniel W. Graham et Eric Hintz, « Anaxagoras and the Comet », ''Apeiron'', vol. 40, 2007, p. 1-20 ; Evangelos Th. Theodossiou et al., « The Fall of a Meteorite at Aegos Potami in 467/6 BC », ''Journal of Astronomical History and Heritage'', vol. 5, 2002, p. 135-140.</ref>. Quoi qu'il en soit, la chute du météorite fut associée au nom d'Anaxagore et parut confirmer sa théorie selon laquelle les corps célestes sont faits de pierre. === Météorologie === Anaxagore consacra une partie considérable de son traité à l'explication des phénomènes météorologiques. Les nuages se forment par évaporation de l'eau sous l'effet de la chaleur solaire<ref>Aétius, III, 4, 1 (DK 59 A 82).</ref>. La pluie provient de la condensation de la vapeur d'eau contenue dans les nuages<ref>Anaxagore, fragment B16 (DK 59 B 16).</ref>. La formation de la grêle posait un problème particulier : comment de la glace peut-elle se former en été ? Anaxagore proposa une explication ingénieuse : lors des journées très chaudes, des courants d'air ascendants peuvent pousser les nuages à des altitudes très élevées, où l'air est suffisamment froid pour que l'eau gèle<ref>Aétius, III, 4, 1 (DK 59 A 84) ; Aristote, ''Météorologiques'', I, 12, 348b23-349a11 (critique de la théorie d'Anaxagore).</ref>. Cette théorie, bien que partiellement erronée dans ses détails, témoigne d'une compréhension correcte du principe de convection. Le tonnerre et l'éclair sont causés, selon Anaxagore, par la chute de l'éther dans les nuages<ref>Aétius, III, 3, 3 (DK 59 A 84) ; Hippolyte, DK 59 A 42.</ref>. Cette explication reconnaît correctement que l'éclair précède le tonnerre, la vue étant plus rapide que l'ouïe<ref>Sénèque, ''Questions naturelles'', II, 22 (citant Anaxagore).</ref>. Les tremblements de terre sont causés, selon Anaxagore, par de l'éther chaud piégé sous la surface de la terre<ref>Aristote, ''Météorologiques'', II, 7, 365a19-21 (DK 59 A 89).</ref>. === Hydrologie et géologie === Anaxagore proposa une explication de la crue annuelle du Nil : les crues estivales sont causées par la fonte des neiges dans les régions montagneuses situées à la source du fleuve<ref>Diodore de Sicile, I, 38, 4 (attribution explicite à Anaxagore) ; Daniel Gershenson et Daniel Greenberg, 1964, p. 54.</ref>. Cette explication est essentiellement correcte. La mer, selon Anaxagore, existait dès le début, mais sa salinité actuelle provient de l'évaporation de l'eau douce sous l'effet du soleil<ref>Hippolyte, DK 59 A 42.</ref>. === Synthèse : la cosmologie naturaliste d'Anaxagore === La cosmologie d'Anaxagore se caractérise par trois traits fondamentaux. Premièrement, l'unité de la nature : Anaxagore affirme que les corps célestes sont constitués des mêmes substances que la terre et obéissent aux mêmes lois physiques<ref>Hippolyte, DK 59 A 42 ; Daniel Gershenson et Daniel Greenberg, 1964, p. 23-26, 47-48.</ref>. Il n'existe pas de différence ontologique entre le monde sublunaire et le monde supralunaire, contrairement à ce qu'affirmera plus tard Aristote<ref>Aristote, ''Du Ciel'', I, 2-3, 268b11-270b25.</ref>. Deuxièmement, l'explication mécanique : tous les phénomènes cosmologiques et météorologiques sont expliqués par des processus physiques (rotation, séparation par densité, évaporation, condensation), sans recours à des agents divins<ref>Daniel Gershenson et Daniel Greenberg, 1964, p. 23-33.</ref>. Troisièmement, l'usage systématique de l'analogie : Anaxagore explique les phénomènes cosmiques par analogie avec des phénomènes terrestres observables<ref>Daniel Gershenson et Daniel Greenberg, 1964, p. 7-12.</ref>. Ces traits donnent à la cosmologie d'Anaxagore un caractère naturaliste marqué, qui représente une étape importante dans le développement d'une explication physique de la nature. Il convient toutefois de ne pas surestimer sa portée : parler d'Anaxagore comme d'un « fondateur de la méthode scientifique » (selon une expression qu'on trouve parfois dans la littérature) relève d'une certaine emphase rétrospective, qu'il vaut mieux tempérer en parlant plutôt d'un jalon dans une longue histoire où se combinent intuitions fécondes et erreurs caractéristiques de l'époque. == La physiologie et la biologie == Bien qu'Anaxagore soit surtout connu pour ses théories cosmologiques et météorologiques, les témoignages anciens indiquent qu'il consacra aussi une partie considérable de son traité à l'explication des phénomènes biologiques et physiologiques<ref>Daniel Gershenson et Daniel Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 55-57 ; W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 316-320.</ref>. === La théorie de la nutrition === L'une des questions biologiques les plus importantes qu'Anaxagore chercha à élucider est celle de la nutrition : comment l'alimentation se transforme-t-elle en chair, en os, en cheveux et en toutes les autres parties du corps ? Selon les témoignages d'Aristote et de la tradition doxographique, Anaxagore partait de l'observation que les êtres humains et les animaux se nourrissent d'aliments relativement simples (pain, eau) et que de ces aliments proviennent pourtant toutes les parties complexes et diversifiées de leur corps<ref>Simplicius, ''Commentaire sur la Physique'', 27, 2-11 (DK 59 A 45) : « Car comment, disait-il, des cheveux pourraient-ils provenir de ce qui n'est pas cheveu, et de la chair de ce qui n'est pas chair ? ». Cf. Aétius, I, 3, 5 (DK 59 A 46) ; Lucrèce, ''De la nature'', I, 834-838.</ref>. Cette observation posait un problème philosophique considérable au regard de l'interdit parménidien : il semble en effet qu'une substance nouvelle (la chair) naisse à partir d'une substance qui n'était pas chair (le pain), ce qui contredit le principe « rien ne naît de ce qui n'est pas ». La réponse d'Anaxagore était cohérente avec sa métaphysique générale : le pain doit déjà contenir de la chair, du sang, des os, des cheveux, et toutes les autres substances corporelles, bien que ces constituants y soient présents en quantités si infimes qu'ils demeurent imperceptibles<ref>Simplicius, ''Commentaire sur la Physique'', 460, 4-12 (DK 59 A 45).</ref>. Lorsque nous mangeons du pain, le corps extrait du pain les particules de chair qu'il contient déjà, et les ajoute à la chair existante<ref>Aristote, ''Génération des animaux'', I, 18, 723a6-11 (DK 59 A 45).</ref>. Cette théorie soulève évidemment d'autres difficultés : comment le corps sait-il extraire précisément les particules de chair du pain, et les diriger vers les muscles plutôt que vers les os ? Anaxagore attribuait cette fonction au Noûs présent dans chaque organisme vivant<ref>Cf. section « Le Noûs : l'Intellect cosmique ». Daniel Gershenson et Daniel Greenberg, 1964, p. 27-33, 55-56.</ref>. === La théorie de la perception sensorielle === Anaxagore élabora aussi une théorie originale de la perception sensorielle, fondée sur le principe que « le semblable n'est pas affecté par le semblable, mais les contraires sont affectés les uns par les autres »<ref>Théophraste, ''De Sensibus'', 27 (DK 59 A 92) ; Aristote, ''De l'âme'', III, 3, 427a21-26 (DK 59 A 94).</ref>. Ce principe constituait une réponse directe aux théories de ses prédécesseurs, notamment Empédocle, qui soutenait que la perception se produit par similitude<ref>Théophraste, ''De Sensibus'', 1-2 (DK 31 A 86).</ref>. Selon Anaxagore, pour qu'une perception ait lieu, il doit exister une différence entre l'organe sensoriel et l'objet perçu<ref>Théophraste, ''De Sensibus'', 27-28 (DK 59 A 92).</ref>. Nous ne sentons pas la température de l'air lorsqu'elle est exactement égale à celle de notre peau ; c'est seulement lorsqu'il existe une différence que nous percevons le chaud ou le froid<ref>Théophraste, ''De Sensibus'', 29 (DK 59 A 92).</ref>. Une conséquence remarquable de cette théorie est qu'Anaxagore considérait que toute perception s'accompagne nécessairement de douleur ou d'un certain désagrément (λύπη)<ref>Théophraste, ''De Sensibus'', 29 (DK 59 A 92). Cf. W. K. C. Guthrie, 1965, p. 319 ; Inna Kupreeva, « Sensing the World », dans ''Physis and Psyche in Plato and Aristotle'', Londres, Bloomsbury, 2024, p. 95-114.</ref>. === La reproduction et l'embryologie === Anaxagore proposa en outre des théories sur la reproduction et le développement embryonnaire. Selon les témoignages anciens, il soutenait que le sexe de l'enfant est déterminé par le père seul, et non par la mère<ref>Aétius, V, 7, 1 (DK 59 A 107) ; Hippolyte, DK 59 A 42.</ref>. Cette théorie n'était pas entièrement originale, puisque des idées similaires avaient été proposées par Parménide et d'autres penseurs antérieurs<ref>Aétius, V, 7, 1 (DK 28 A 52-54). Cf. Ursula Mittwoch, « Sex Determination », ''EMBO Reports'', vol. 14, 2013, p. 588-592 ; Oliver Kember, « Anaxagoras' Theory of Sex Differentiation and Heredity », ''Phronesis'', vol. 18, 1973, p. 1-14.</ref>. === La génération des animaux et des plantes === Selon les témoignages doxographiques, Anaxagore distinguait entre la zoogonie originelle, c'est-à-dire la première génération des animaux, et la reproduction ultérieure par semences<ref>Hippolyte, DK 59 A 42 ; Aétius, V, 19, 4 (DK 59 A 42).</ref>. Dans la zoogonie originelle, les premiers animaux émergèrent de la terre encore chaude et humide, grâce aux semences (σπέρματα) contenues dans l'air et l'éther<ref>Théophraste, dans Simplicius, ''Commentaire sur la Physique'', 27, 2-4.</ref>. En ce qui concerne les plantes, Anaxagore affirmait qu'elles possèdent une âme (ψυχή) et un intellect (νοῦς)<ref>Aétius, V, 26, 4 (DK 59 A 117).</ref>. Cette conclusion découlait de ses observations du comportement des plantes : elles se tournent vers la lumière du soleil, étendent leurs racines vers l'eau<ref>Aristote, ''De l'âme'', I, 5, 410b27-411a7. Cf. Inna Kupreeva, 2024, p. 98-103.</ref>. === Observations biologiques diverses === Les sources anciennes conservent enfin quelques observations biologiques isolées d'Anaxagore. Il soutenait, par exemple, que les belettes sont les seuls animaux qui donnent naissance par la bouche<ref>Aristote, ''Histoire des animaux'', VI, 32, 580a15-17 (DK 59 A 114) ; Plutarque, ''Œuvres morales'', 975F-976A (DK 59 A 114).</ref>. Il était parvenu à cette conclusion erronée parce qu'il avait observé des belettes femelles transportant leurs petits dans leur gueule immédiatement après la mise bas<ref>Plutarque, ''Œuvres morales'', 975F (DK 59 A 114).</ref>. De même, il affirmait que les corbeaux et les ibis s'accouplent par le bec<ref>Aristote, ''Histoire des animaux'', V, 2, 539b31-540a1 (DK 59 A 114).</ref>. Ces erreurs montrent les limites de sa méthode empirique. === L'homme et les animaux === Selon Anaxagore, ce qui distingue l'homme du reste du règne animal est principalement l'une des distinctions physiologiques les plus évidentes : les mains de l'homme<ref>Aristote, ''Parties des animaux'', IV, 10, 687a7-12 (DK 59 A 102). Aristote critique cette position et affirme au contraire que l'homme a des mains parce qu'il est le plus intelligent.</ref>. Ce sont les mains, selon Anaxagore, qui permettent à l'homme de surpasser les animaux dans les compétences manipulatrices et les capacités techniques. == L'influence et la postérité == Anaxagore occupe une place importante dans l'histoire de la philosophie antique. Introducteur à Athènes de la tradition ionienne de recherche naturaliste, il constitue un jalon dans le développement de la pensée grecque du V{{e}} siècle<ref>Diogène Laërce, II, 6 (DK 59 A 1) ; W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 266-267.</ref>. Les lignes qui suivent s'efforcent de décrire son influence avec la prudence philologique nécessaire : il est facile, en traitant des présocratiques, de céder à la tentation des grandes filiations, alors que la documentation réelle invite souvent à plus de retenue. === La transmission immédiate : Archélaos et le cercle socratique === Le premier vecteur de l'influence d'Anaxagore fut son disciple direct Archélaos d'Athènes (parfois dit aussi « de Milet »), qui enseigna à Athènes après le départ de son maître pour Lampsaque<ref>Diogène Laërce, II, 16 (DK 60 A 1) ; Simplicius, ''Commentaire sur la Physique'', 27, 23 (DK 60 A 7). La formule que l'on rencontre parfois, selon laquelle Archélaos aurait « succédé à Anaxagore à Lampsaque », n'est pas documentée par les sources : Archélaos est attesté comme actif à Athènes, où il aurait été le maître de Socrate. Sur ce point, voir Gábor Betegh, « Archelaus on Cosmogony and the Origins of Social Institutions », ''Oxford Studies in Ancient Philosophy'', vol. 51, 2016, p. 1-40.</ref>. Selon plusieurs témoignages anciens, Archélaos aurait été le maître de Socrate, ce qui établit un lien biographique, quoique indirect, entre Anaxagore et le fondateur de la philosophie morale<ref>Diogène Laërce, II, 16 (DK 60 A 1). Ion de Chios rapporte que le jeune Socrate voyagea avec Archélaos à Samos (DK 60 A 3).</ref>. Archélaos semble avoir développé et modifié certaines doctrines de son maître, en particulier en appliquant les principes anaxagoréens à l'éthique et aux institutions sociales, orientation qui anticipe peut-être le tournant moral pris ensuite par Socrate<ref>Diogène Laërce, II, 16 (DK 60 A 1) ; Hippolyte, DK 60 A 4 ; Gábor Betegh, 2016.</ref>. === L'influence sur Socrate : espoirs et déceptions === La relation entre Anaxagore et Socrate a probablement été indirecte, médiée par la lecture du livre. Dans le ''Phédon'', Platon fait raconter par Socrate sa rencontre avec la pensée d'Anaxagore, rencontre qui suscita d'abord un enthousiasme puis une déception<ref>Platon, ''Phédon'', 97b-98c (DK 59 A 47).</ref>. Comme on l'a souligné plus haut, cette déception porte sur une attente proprement socratique, celle d'une explication par le « meilleur », qu'Anaxagore n'avait pas, à proprement parler, formulée dans les termes que Socrate aurait voulus. Cette critique conduisit Socrate à se tourner vers la philosophie morale et la recherche de la cause finale, c'est-à-dire du Bien qui rendrait compte de l'ordre du monde<ref>Platon, ''Phédon'', 99c-100a. Cf. David Sedley, « Teleology and Myth in the ''Phaedo'' », ''Proceedings of the Boston Area Colloquium in Ancient Philosophy'', vol. 5, 1989, p. 359-383.</ref>. L'idée qu'un principe rationnel puisse gouverner l'univers, idée que Socrate trouve chez Anaxagore et qu'il juge insuffisamment exploitée, a pu orienter le projet socratique sans qu'il faille pour autant présenter Anaxagore comme sa cause nécessaire<ref>David Sider, « Anaxagoras, Socrates, and the History of "Philosophy" », ''Research Bulletin of the CHS'', 2016 ; W. K. C. Guthrie, 1965, p. 327-331.</ref>. Dans l'''Apologie'', l'accusation portée contre Socrate inclut l'imputation de théories cosmologiques manifestement inspirées d'Anaxagore, ce qui montre l'association étroite entre les deux penseurs dans l'esprit des Athéniens, indépendamment de la distance philosophique réelle qui les sépare<ref>Platon, ''Apologie de Socrate'', 26d (DK 59 A 35). Cf. Gregory Vlastos, ''Socrates: Ironist and Moral Philosopher'', Ithaca, Cornell University Press, 1991, p. 293-297.</ref>. Xénophon présente pour sa part Socrate comme mettant en garde ses disciples contre l'étude des phénomènes célestes à la manière d'Anaxagore, ce qui laisse entendre que Socrate lui-même s'était explicitement démarqué de la philosophie naturelle anaxagoréenne<ref>Xénophon, ''Mémorables'', IV, 7, 6 (DK 59 A 47).</ref>. === L'appropriation platonicienne : du Noûs au Démiurge === Platon s'empare de la doctrine anaxagoréenne du Noûs et la transforme, en la radicalisant, en une cosmologie téléologique pleinement articulée. Dans le ''Timée'', Platon présente le Démiurge, cet artisan divin qui façonne le monde sensible à l'image des Formes éternelles, comme une figure qu'on peut lire en dialogue avec le Noûs anaxagoréen<ref>Platon, ''Timée'', 29a-30c, 47e-48a. Cf. Glenn Morrow, « Necessity and Persuasion in Plato's ''Timaeus'' », ''Philosophical Review'', vol. 59, 1950, p. 147-163 ; Luc Brisson, ''Le Même et l'Autre dans la structure ontologique du Timée de Platon'', Paris, Klincksieck, 1974, p. 87-125.</ref>. Il convient toutefois d'être prudent. La téléologie platonicienne va en effet bien au-delà de ce qu'affirmait Anaxagore : le Démiurge contemple les Formes éternelles pour façonner le monde, et cet arrière-plan métaphysique d'essences intelligibles n'a pas d'équivalent chez le Clazoménien, dont le Noûs demeure un principe moteur et cognitif, mais non un principe qui « voit » des réalités intelligibles préalables à son action. Dans le ''Philèbe'', Platon affirme explicitement sa dette envers Anaxagore en déclarant que « l'Intellect est roi du ciel et de la terre »<ref>Platon, ''Philèbe'', 28c (DK 59 A 47). Cf. également ''Cratyle'', 413c.</ref>, reprenant ainsi la doctrine anaxagoréenne selon laquelle « le Noûs domine tout »<ref>Anaxagore, B12 (DK 59 B 12).</ref>. Dans les ''Lois'', Platon fait l'éloge des « anciens » qui ont découvert que « l'Intellect gouverne toutes choses », allusion probable à Anaxagore<ref>Platon, ''Lois'', X, 897b-c, XII, 967b-c. Cf. Malcolm Schofield, 1980, p. 55-70.</ref>. L'influence d'Anaxagore sur Platon est donc réelle, mais elle passe par une transformation considérable de la doctrine originelle : le Noûs anaxagoréen, simple cause motrice, devient chez Platon un Intellect qui contemple le Bien et façonne le monde selon le meilleur ordre possible. Le passage d'Anaxagore à Platon est celui qui fait naître, en toute rigueur, la tradition téléologique de la philosophie occidentale. === La critique aristotélicienne === Aristote, tout en reconnaissant l'importance historique d'Anaxagore, fut l'un de ses critiques les plus sévères. Dans la ''Métaphysique'', il salue Anaxagore comme « un homme sobre parmi des bavards »<ref>Aristote, ''Métaphysique'', I, 3, 984b15-20 (DK 59 A 43).</ref>, louant son introduction du Noûs comme cause de l'ordre cosmique. Mais cette louange est immédiatement suivie d'une critique : Anaxagore « utilise l'Intellect comme un ''deus ex machina'' »<ref>Aristote, ''Métaphysique'', I, 4, 985a18-21 (DK 59 A 47).</ref>. Comme on l'a vu, cette critique présuppose un idéal téléologique qui n'est pas celui d'Anaxagore lui-même. Aristote adopte en revanche, en le reformulant, le terme technique homéomère (ὁμοιομερής) pour désigner les substances anaxagoréennes, bien qu'il soit probable qu'Anaxagore lui-même n'ait jamais utilisé ce mot<ref>Cf. Malcolm Schofield, 1980, p. 87-108.</ref>. La distinction que la tradition anaxagoréenne a léguée, entre substances homéomères (chair, os, sang) et substances anhoméomères (main, pied, visage), devient ainsi fondamentale dans la philosophie naturelle aristotélicienne, où elle servira à articuler l'analyse des parties du vivant<ref>Aristote, ''De la génération et de la corruption'', I, 1, 314a20-b1 ; ''Parties des animaux'', II, 1, 646a12-24.</ref>. === L'héritage dans la philosophie hellénistique et romaine === Après Aristote, la pensée d'Anaxagore continua d'exercer une influence diffuse. Les Stoïciens s'approprièrent certains aspects de sa doctrine du Noûs pour développer leur propre concept du Logos universel qui pénètre et gouverne toute la nature<ref>Diogène Laërce, VII, 134-139 (SVF II, 634) ; Cicéron, ''De la nature des dieux'', I, 11, 27 ; II, 8, 23. Cf. A. A. Long et D. N. Sedley, ''The Hellenistic Philosophers'', vol. I, Cambridge, Cambridge University Press, 1987, p. 268-274.</ref>. Le pneuma stoïcien présente des analogies avec le Noûs anaxagoréen, bien que les Stoïciens aient rejeté le dualisme matière/esprit d'Anaxagore en faveur d'un matérialisme intégral<ref>Michael J. White, « Stoic Natural Philosophy », dans Brad Inwood (éd.), ''The Cambridge Companion to the Stoics'', Cambridge, Cambridge University Press, 2003, p. 124-152.</ref>. Les Épicuriens, en revanche, rejetèrent la doctrine anaxagoréenne. Lucrèce critique explicitement Anaxagore dans le ''De natura rerum''<ref>Lucrèce, ''De la nature'', I, 830-920 (DK 59 A 44). Cf. David Sedley, ''Lucretius and the Transformation of Greek Wisdom'', Cambridge, Cambridge University Press, 1998, p. 24-33.</ref>. Dans la tradition néoplatonicienne, Anaxagore fut lu à travers le prisme de Platon. Simplicius, au VI{{e}} siècle de notre ère, consacra de longs passages de son commentaire sur la ''Physique'' d'Aristote à l'exégèse des fragments d'Anaxagore ; c'est d'ailleurs grâce à ces commentaires que la majeure partie de notre connaissance d'Anaxagore nous est parvenue<ref>Simplicius, ''Commentaire sur la Physique'' (passim).</ref>. === Un héritage à ne pas surinterpréter === Il est tentant de présenter Anaxagore comme un précurseur de la science mécaniste moderne, de l'atomisme, voire de la physique contemporaine. Il faut résister à cette tentation, au moins dans sa forme la plus large. Au XVII{{e}} siècle, certains philosophes mécanistes ont effectivement cité les présocratiques pour légitimer leur programme, mais c'est l'atomisme de Démocrite et de Leucippe, médiatisé par Épicure et par Lucrèce, qui a exercé une influence directe sur la physique corpusculaire naissante de Gassendi, Boyle et Newton. L'influence propre d'Anaxagore sur cette tradition reste, pour l'essentiel, limitée et indirecte : sa doctrine de la divisibilité infinie et du mélange universel s'oppose d'ailleurs aux postulats fondamentaux de l'atomisme classique, et c'est précisément cette opposition qui la rendait peu exploitable pour les mécanistes modernes<ref>Alan Chalmers, « Atomism from the 17th to the 20th Century », ''Stanford Encyclopedia of Philosophy'', 2005 (révisé 2014) ; Andrew Pyle, ''Atomism and its Critics : From Democritus to Newton'', Bristol, Thoemmes Press, 1997.</ref>. On trouve aussi, ici ou là, des rapprochements entre la doctrine anaxagoréenne du « tout dans tout » et certaines idées de la physique moderne : théorie des champs, intrication quantique, non-séparabilité des systèmes physiques. De tels rapprochements, parfois suggérés par des physiciens eux-mêmes (Werner Heisenberg évoque à l'occasion les présocratiques dans ses réflexions philosophiques), peuvent avoir une valeur heuristique ou pédagogique. Ils ne doivent pas pour autant être transformés en affirmations de continuité doctrinale. La physique quantique procède d'un appareil mathématique et expérimental qui n'a aucun équivalent chez les Grecs anciens, et les analogies conceptuelles qu'on peut tracer avec Anaxagore relèvent de la métaphore rétrospective, non de la filiation historique<ref>Werner Heisenberg, ''Physics and Philosophy'', New York, Harper, 1958, p. 62-63, évoque les présocratiques sans établir de filiation précise avec la physique quantique.</ref>. Il a été parfois suggéré, de manière analogue, que les grands théologiens médiévaux aient vu dans le Noûs un précurseur du Dieu créateur et ordonnateur. Cette suggestion demande une prudence particulière. Thomas d'Aquin, Maïmonide et Avicenne s'inscrivent dans une tradition aristotélicienne et néoplatonicienne déjà très élaborée, dans laquelle le Noûs anaxagoréen n'apparaît, quand il est mentionné, qu'à travers la médiation d'Aristote, et le plus souvent pour être critiqué. La théologie médiévale développe sa conception du Dieu créateur à partir de ressources propres (exégèse biblique, coranique ou talmudique, théologie négative néoplatonicienne, métaphysique aristotélicienne de l'acte pur), et non par appropriation directe de la doctrine anaxagoréenne. Il faut donc parler de continuités discrètes dans une histoire complexe, où la figure d'Anaxagore est plus souvent un repoussoir ou une étape dépassée qu'une source vive<ref>Sur la tradition aristotélicienne et néoplatonicienne médiévale, voir Étienne Gilson, ''L'esprit de la philosophie médiévale'', Paris, Vrin, 1932 ; sur la transmission arabe des présocratiques, voir Cristina D'Ancona, « Greek into Arabic: Neoplatonism in Translation », dans P. Adamson et R. C. Taylor (éd.), ''The Cambridge Companion to Arabic Philosophy'', Cambridge, Cambridge University Press, 2005, p. 10-31.</ref>. Une dernière mise en garde concerne la prédiction de l'éclipse et la chute du météorite d'Aigos Potamos, qui ont parfois été présentées comme des triomphes de la « méthode scientifique » naissante. En réalité, les sources qui attribuent à Anaxagore ces prouesses prédictives sont tardives et souvent légendaires ; les historiens des sciences contemporains, comme Daniel Graham ou Geoffrey Lloyd, se montrent prudents, voire sceptiques, sur l'authenticité de ces anticipations. L'importance historique d'Anaxagore ne tient pas à la précision de ses prédictions, mais à la cohérence et à l'ambition explicative de son système naturaliste, qui propose de rendre compte des phénomènes célestes par des principes physiques accessibles à la raison, sans recourir aux figures mythologiques traditionnelles. === Bilan === L'influence d'Anaxagore sur la postérité se caractérise par un contraste fondamental : d'un côté, son introduction du Noûs comme principe cosmique fut saluée comme une avancée majeure par Aristote lui-même, qui loue son caractère « sobre » par contraste avec les rêveries des premiers Ioniens ; de l'autre, son refus (ou son incapacité) à développer une explication par le « meilleur » fut jugée insuffisante par Socrate, Platon et Aristote. Cette dualité traverse toute l'histoire de sa réception : Anaxagore est à la fois le penseur qui a rendu pensable une cause intellectuelle du cosmos, et celui qui a laissé cette cause inemployée au goût de ses successeurs<ref>W. K. C. Guthrie, 1965, p. 327-331 ; Malcolm Schofield, 1980, p. 55-70 ; Daniel Graham, ''Science before Socrates'', Oxford, Oxford University Press, 2006, p. 152-158.</ref>. Cette limitation même fut paradoxalement féconde. En ouvrant une voie sans la parcourir jusqu'au bout, Anaxagore a invité ses successeurs à poursuivre le chemin, chacun à sa manière. Socrate s'est tourné vers la philosophie morale, en cherchant dans le Bien la cause que le Noûs anaxagoréen ne fournissait pas. Platon a développé, dans le ''Timée'' et ailleurs, une cosmologie téléologique dans laquelle le Démiurge contemple les Formes éternelles pour façonner le monde. Aristote a élaboré, contre Platon autant que contre Anaxagore, une doctrine systématique des quatre causes qui intègre la cause finale dans un schéma explicatif unifié. Tous trois ont puisé dans Anaxagore, chacun à sa manière, mais au prix d'une transformation qui leur appartient en propre. On peut donc parler d'une empreinte durable d'Anaxagore sur les orientations ultérieures de la philosophie grecque, sans pour autant soutenir que Socrate, Platon ou Aristote n'auraient pas été ce qu'ils furent sans lui : leurs doctrines ont leurs propres fondements, souvent en réaction à Anaxagore autant qu'en prolongement de lui, et la cause anaxagoréenne n'est, dans leur cas, jamais que partielle. Sur le plan scientifique, l'héritage d'Anaxagore est surtout celui d'un modèle d'explication naturaliste. Il propose des explications des phénomènes célestes et météorologiques en termes de substances et de processus physiques, sans recourir aux agents mythologiques qui peuplaient encore l'imaginaire grec traditionnel. Le soleil n'est plus Hélios mais une pierre incandescente ; la lune n'est plus une déesse mais un corps rocheux qui reçoit la lumière du soleil ; la foudre n'est plus le trait de Zeus mais le produit d'un phénomène atmosphérique. Cet héritage naturaliste n'est pas négligeable, et il a nourri durablement la tradition philosophique et scientifique grecque. Il serait pourtant excessif de lui attribuer à titre principal la fondation d'une « méthode scientifique » au sens moderne : l'élaboration de cette méthode suppose l'invention de la preuve démonstrative, le développement de la mathématisation de la nature, et bien d'autres étapes que la pensée d'Anaxagore n'a pas accomplies<ref>Daniel Gershenson et Daniel Greenberg, 1964, défendent une version forte de la thèse d'Anaxagore comme fondateur de la méthode scientifique, thèse qu'il convient de modérer. Cf. Geoffrey Lloyd, ''Early Greek Science : Thales to Aristotle'', Londres, Chatto & Windus, 1970, pour une perspective plus prudente.</ref>. En définitive, Anaxagore occupe une place singulière dans l'histoire de la philosophie : celle d'un pionnier qui ouvre une voie sans l'explorer entièrement, et qui a légué à ses successeurs à la fois une doctrine (dans ses fragments conservés) et la tâche philosophique de combler ce que cette doctrine laisse ouvert. Cette position intermédiaire entre la cosmologie ionienne, dont il est le dernier grand représentant à Athènes, et la philosophie classique, dont il est l'un des principaux préparateurs, fait d'Anaxagore une figure pivot, dont l'intérêt philosophique propre ne doit pas être dilué dans l'histoire des influences qu'il a exercées. == Notes et références == {{references}} == Bibliographie == === Textes anciens : éditions et traductions === ; Diels, Hermann & Kranz, Walther (éds.) : ''Die Fragmente der Vorsokratiker'', 3 vol., Berlin, Weidmann, 1951-1952 (6{{e}} éd.) : [Édition standard de référence pour les fragments présocratiques, avec le système de numérotation DK (Diels-Kranz) utilisé dans la présente étude] ; Laks, André & Most, Glenn W. (éds.) : ''Early Greek Philosophy'', 9 vol., Loeb Classical Library, Cambridge (Mass.), Harvard University Press, 2016 : [Édition plus récente avec traduction anglaise et nouveau système de numérotation, utile en complément de Diels-Kranz] ; Kirk, G. S., Raven, J. E. & Schofield, M. (éds.) : ''The Presocratic Philosophers: A Critical History with a Selection of Texts'', 2{{e}} édition, Cambridge, Cambridge University Press, 1983 : [Traductions anglaises commentées des fragments] ; Curd, Patricia (éd.) : ''Anaxagoras of Clazomenae: Fragments and Testimonia'', Toronto, University of Toronto Press, 2007 : [Édition critique avec traductions anglaises commentées et analyses détaillées] ; Sider, David (éd.) : ''The Fragments of Anaxagoras: With a Commentary'', Sankt Augustin, Academia Verlag, 2005 : [Édition avec commentaire détaillé] ; Platon : ''Phédon'', trad. fr. Monique Dixsaut, Paris, GF-Flammarion, 1991 : [Dialogue contenant la critique socratique du Noûs d'Anaxagore] ; Platon : ''Apologie de Socrate'', trad. fr. Luc Brisson, Paris, GF-Flammarion, 1997 ; Platon : ''Timée'', trad. fr. Luc Brisson, Paris, GF-Flammarion, 1992 ; Platon : ''Philèbe'', trad. fr. Alfred Diès, Paris, Les Belles Lettres, 1941 ; Platon : ''Lois'', trad. fr. Édouard des Places, Paris, Les Belles Lettres, 1951-1956 ; Aristote : ''Métaphysique'', trad. fr. Jean Tricot, Paris, Vrin, 1933 ; Aristote : ''Physique'', trad. fr. Jean Tricot, Paris, Vrin, 1936 ; Aristote : ''Du Ciel'', trad. fr. Jean Tricot, Paris, Vrin, 1949 ; Aristote : ''Génération et corruption'', trad. fr. Jean Tricot, Paris, Vrin, 1950 ; Aristote : ''Génération des animaux'', trad. fr. Jean Tricot, Paris, Vrin, 1957 ; Aristote : ''Histoire des animaux'', trad. fr. Jean Tricot, Paris, Vrin, 1957 ; Aristote : ''Parties des animaux'', trad. fr. Jean Tricot, Paris, Vrin, 1957 ; Aristote : ''De l'âme'', trad. fr. Richard Bodéüs, Paris, GF-Flammarion, 1993 ; Diogène Laërce : ''Vies et doctrines des philosophes illustres'', sous la dir. de Marie-Odile Goulet-Cazé, Paris, Le Livre de Poche (La Pochothèque), 1999 ; Théophraste : ''De Sensibus'' (''On Sense Perception''), trad. angl. dans Kirk, Raven & Schofield, 1983 ; Simplicius : ''In Aristotelis Physicorum libros commentaria'', éd. Hermann Diels, Berlin, Reimer, 1882-1895 : [Source principale pour la transmission des fragments d'Anaxagore] ; Hippolyte : ''Réfutation de toutes les hérésies'', trad. angl. dans Curd, 2007 ; Lucrèce : ''De la nature'', trad. fr. Alfred Ernout, Paris, Les Belles Lettres, 1920 ; Cicéron : ''De la nature des dieux'', trad. fr. Clara Auvray-Assayas, Paris, Les Belles Lettres, 2002 ; Plutarque : ''Vies parallèles'' (''Vie de Périclès'', ''Vie de Lysandre''), trad. fr. Anne-Marie Ozanam, Paris, Gallimard, Bibliothèque de la Pléiade, 2001 ; Xénophon : ''Mémorables'', trad. fr. Louis-André Dorion et Michele Bandini, Paris, Les Belles Lettres, 2000-2011 === Études modernes : histoire et philosophie antiques === ; Schofield, Malcolm : ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980 : [Monographie majeure : étude exhaustive de la pensée anaxagoréenne avec analyse textuelle détaillée] ; Curd, Patricia : « Anaxagoras », ''Stanford Encyclopedia of Philosophy'', publié 2007, révision substantielle 2019 : [Synthèse de référence, avec bibliographie à jour] ; Guthrie, W. K. C. : ''A History of Greek Philosophy'', vol. II : ''The Presocratic Tradition from Parmenides to Democritus'', Cambridge, Cambridge University Press, 1965 ; Barnes, Jonathan : ''The Presocratic Philosophers'', 2 vol., Londres, Routledge, 1982 (révisé 2006) ; Kirk, G. S., Raven, J. E. & Schofield, M. : ''The Presocratic Philosophers: A Critical History with a Selection of Texts'', 2{{e}} édition, Cambridge, Cambridge University Press, 1983 ; Vlastos, Gregory : « The Physical Theory of Anaxagoras », ''Philosophical Review'', vol. 59, 1950, p. 31-57 ; Peck, Arthur L. : « Anaxagoras: Predication as a Problem in Physics », ''Classical Quarterly'', vol. 25, 1931, p. 27-37 et p. 112-120 ; Strang, Colin : « The Physical Theory of Anaxagoras », ''Archiv für Geschichte der Philosophie'', vol. 45, 1963, p. 101-118 ; Cornford, F. M. : « Anaxagoras' Theory of Matter », ''Classical Quarterly'', vol. 24, 1930, p. 14-30 ; Graham, Daniel W. : « Was Anaxagoras a Reductionist? », ''Ancient Philosophy'', vol. 24, 2004, p. 1-18 ; Graham, Daniel W. : ''Science before Socrates: Parmenides, Anaxagoras, and the New Astronomy'', Oxford, Oxford University Press, 2006 ; Curd, Patricia : ''The Legacy of Parmenides: Eleatic Monism and Later Presocratic Thought'', Princeton, Princeton University Press, 1998 ; Owen, G. E. L. : « The Place of the Timaeus in Plato's Dialogues », ''Classical Quarterly'', vol. 3, 1953, p. 79-95 ; repris dans ''Logic, Science and Dialectic'', Londres, Duckworth, 1986 ; Raven, J. E. : « The Basis of Anaxagoras' Cosmology », ''Classical Quarterly'', vol. 4, 1954, p. 123-137 ; Kerferd, George B. : « Anaxagoras and the Concept of Matter before Aristotle », ''Bulletin of the John Rylands Library'', vol. 52, 1969, p. 129-143 ; Lloyd, David : « Anaxagoras on Life and Mind », ''Phronesis'', vol. 14, 1969, p. 246-251 ; Deichgräber, Karl : « Anaxagoras Stil », ''Philologus'', vol. 87, 1933, p. 15-26 ; Norden, Eduard : ''Agnostos Theos: Untersuchungen zur Formengeschichte religiöser Rede'', Leipzig, Teubner, 1913 ; Taylor, A. E. : « On the Date of the Trial of Anaxagoras », ''Classical Quarterly'', vol. 11, 1917, p. 81-87 ; Kahn, Charles H. : ''Anaximander and the Origins of Greek Cosmology'', New York, Columbia University Press, 1960 ; Inwood, Brad : « Anaxagoras and Infinite Divisibility », ''Illinois Classical Studies'', vol. 11, 1986, p. 17-33 ; Sedley, David : ''Creationism and Its Critics in Antiquity'', Berkeley, University of California Press, 2007 ; Sedley, David : « Teleology and Myth in the ''Phaedo'' », ''Proceedings of the Boston Area Colloquium in Ancient Philosophy'', vol. 5, 1989, p. 359-383 ; Betegh, Gábor : « Archelaus on Cosmogony and the Origins of Social Institutions », ''Oxford Studies in Ancient Philosophy'', vol. 51, 2016, p. 1-40 ; Sider, David : « Anaxagoras, Socrates, and the History of "Philosophy" », ''Research Bulletin of the Center for Hellenic Studies'', 31 octobre 2016 ; Furley, David L. : ''Two Studies in the Greek Atomists'', Princeton, Princeton University Press, 1967 ; Furley, David L. : « Anaxagoras, Plato and Naming of Parts », dans ''Presocratic Philosophy: Essays in Honour of Alexander Mourelatos'', éd. Victor Caston et Daniel Graham, Aldershot, Ashgate, 2002, p. 119-126 ; Furth, Montgomery : « A Philosophical Hero? Anaxagoras and the Eleatics », ''Oxford Studies in Ancient Philosophy'', vol. 9, 1991, p. 95-129 ; Lesher, James : « Mind's Knowledge and Powers of Control in Anaxagoras », ''Phronesis'', vol. 40, 1995, p. 125-142 ; Marmodoro, Anna : « Anaxagoras's Qualitative Gunk », ''British Journal for the History of Philosophy'', vol. 23, 2015, p. 402-422 ; Marmodoro, Anna & Morison, Benjamin (éds.) : ''Everything in Everything: Anaxagoras's Metaphysics'', Oxford, Oxford University Press, 2019 ; Vassallo, Christian : « Nous, Motion, and Teleology in Anaxagoras », ''Oxford Studies in Ancient Philosophy'', vol. 50, 2016, p. 1-32 === Études modernes : biologie, physiologie et philosophie naturelle === ; Gershenson, Daniel E. & Greenberg, Daniel A. : ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964 : [À utiliser avec précaution : la thèse centrale sur la « naissance de la méthode scientifique » doit être tempérée] ; Meyer, Arthur William : ''Essays on the History of Embryology'', Stanford, Stanford University Press, 1939 ; Preus, Anthony : « The Techne of Nutrition in Ancient Greek Philosophy », ''Apeiron'', vol. 53, 2020, p. 97-124 ; Kember, Oliver : « Anaxagoras' Theory of Sex Differentiation and Heredity », ''Phronesis'', vol. 18, 1973, p. 1-14 ; Mittwoch, Ursula : « Sex Determination: Science & Society Series on Sex and Science », ''EMBO Reports'', vol. 14, 2013, p. 588-592 ; Kupreeva, Inna : « Sensing the World: Humans, Plants, and the Physicality of Life in Early Greek Philosophy », dans ''Physis and Psyche in Plato and Aristotle'', éd. S. D. Kolstrup et T. L. Kind, Londres, Bloomsbury, 2024, p. 95-114 === Études modernes : astronomie, cosmologie et météorologie === ; Theodossiou, Evangelos Th., Dimitrijevic, Milcho S., Mantarakis, Nikos A. & Georgakarakos, Nikolaos I. : « The Fall of a Meteorite at Aegos Potami in 467/6 BC », ''Journal of Astronomical History and Heritage'', vol. 5, 2002, p. 135-140 ; Graham, Daniel W. & Hintz, Eric : « Anaxagoras and the Comet », ''Apeiron'', vol. 40, 2007, p. 1-20 ; Couprie, Dirk L. : « Anaxagoras on the Milky Way and Lunar Eclipses », ''Rhizomata'', vol. 5, 2017, p. 127-147 ; Lloyd, Geoffrey E. R. : ''Early Greek Science: Thales to Aristotle'', Londres, Chatto & Windus, 1970 === Études modernes : influence et réception === ; Vlastos, Gregory : ''Socrates: Ironist and Moral Philosopher'', Ithaca, Cornell University Press, 1991 ; Morrow, Glenn R. : « Necessity and Persuasion in Plato's ''Timaeus'' », ''Philosophical Review'', vol. 59, 1950, p. 147-163 ; Brisson, Luc : ''Le Même et l'Autre dans la structure ontologique du Timée de Platon'', Paris, Klincksieck, 1974 ; Johansen, Thomas Kjeller : ''Plato's Natural Philosophy'', Cambridge, Cambridge University Press, 2004 ; Johansen, Thomas Kjeller : « From Craft to Nature: The Emergence of Natural Teleology », dans ''Plato and Hesiod'', éd. G. R. Boys-Stones et J. H. Haubold, Oxford, Oxford University Press, 2020, p. 100-125 ; Long, A. A. & Sedley, D. N. : ''The Hellenistic Philosophers'', vol. I, Cambridge, Cambridge University Press, 1987 ; White, Michael J. : « Stoic Natural Philosophy », dans ''The Cambridge Companion to the Stoics'', éd. Brad Inwood, Cambridge, Cambridge University Press, 2003, p. 124-152 ; Sedley, David : ''Lucretius and the Transformation of Greek Wisdom'', Cambridge, Cambridge University Press, 1998 ; Chalmers, Alan : « Atomism from the 17th to the 20th Century », ''Stanford Encyclopedia of Philosophy'', 2005 (révisé 2014) ; Pyle, Andrew : ''Atomism and its Critics: From Democritus to Newton'', Bristol, Thoemmes Press, 1997 ; Gilson, Étienne : ''L'esprit de la philosophie médiévale'', Paris, Vrin, 1932 ; Heisenberg, Werner : ''Physics and Philosophy'', New York, Harper, 1958 : [À consulter pour les rapprochements prudents entre présocratiques et physique moderne, à titre d'analogie heuristique] === Instruments de recherche et ressources === ; Curd, Patricia : « Anaxagoras », ''Stanford Encyclopedia of Philosophy'', 2007 (révision substantielle 2019) ; Graham, Daniel W. : « Presocratic Philosophy », ''Stanford Encyclopedia of Philosophy'', 2019 ; ''Oxford Classical Dictionary'' : s.v. « Anaxagoras », diverses éditions : [À consulter pour la mise en garde prudente sur la tradition biographique] === Dictionnaires et encyclopédies === ; Goulet, Richard (éd.) : ''Dictionnaire des philosophes antiques'', vol. I, Paris, CNRS Éditions, 1989 (2{{e}} éd. 2003) : [Entrée détaillée sur Anaxagore avec bibliographie] {{autocat}} [[Catégorie:Philosophe]] fneafnukf6inmushxlhzs757g0scdec 763997 763996 2026-04-19T09:15:27Z PandaMystique 119061 763997 wikitext text/x-wiki {{DicoPhilo|Anaxagore de Clazomènes}} == Vie et contexte historique == {{wikisource|Auteur:Anaxagore_de_Clazomènes|Anaxagore de Clazomènes}} Les données biographiques concernant Anaxagore sont, comme le souligne l'''Oxford Classical Dictionary'', en grande partie « confuses et déroutantes » (''confused and confusing''), et la plupart des anecdotes transmises par la tradition antique doivent être abordées avec une prudence critique. Les récits qui nous sont parvenus proviennent en effet de sources tardives, séparées d'Anaxagore par plusieurs siècles : Plutarque (I{{er}}-II{{e}} siècle ap. J.-C.) dans ses ''Vies parallèles'', Valère Maxime (I{{er}} siècle ap. J.-C.) dans ses ''Faits et dits mémorables'', puis Diogène Laërce (première moitié du III{{e}} siècle ap. J.-C.) dans ses ''Vies et doctrines des philosophes illustres''. Ces auteurs compilent eux-mêmes des traditions doxographiques antérieures, parfois stylisées à des fins rhétoriques ou moralisatrices, parfois clairement légendaires. Anaxagore (en grec ancien Ἀναξαγόρας, « maître de l'assemblée » ou « chef au forum ») naît vers 500 av. J.-C. à Clazomènes, cité grecque d'Ionie située sur la côte occidentale de l'actuelle Turquie, à une trentaine de kilomètres à l'ouest d'Izmir<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 6 (DK 59 A 1). Apollodore d'Athènes, cité par Diogène Laërce, place la naissance d'Anaxagore à la 70{{e}} Olympiade (500-496 av. J.-C.).</ref>. Fils d'Hégésibule (certaines sources mentionnent Eubule), il appartient, selon la tradition ancienne, à une famille aristocratique et aurait possédé un patrimoine important<ref>Diogène Laërce, II, 6-7 ; Valère Maxime, ''Faits et dits mémorables'', VIII, 7, ext. 5.</ref>. Selon Diogène Laërce, Anaxagore aurait abandonné ses biens à ses proches afin de se consacrer entièrement à la philosophie<ref>Diogène Laërce, II, 7 : « Il négligea ses biens par amour de la sagesse. »</ref>. Une autre tradition, rapportée tant par Valère Maxime que par Diogène Laërce, raconte qu'Anaxagore, revenant d'un long voyage, aurait trouvé ses propriétés en ruine et aurait déclaré : « Si cela n'avait pas péri, c'est moi qui aurais péri »<ref>Valère Maxime, ''Faits et dits mémorables'', VIII, 7, ext. 5 ; Diogène Laërce, II, 6-7. Valère Maxime commente la sentence comme exemple de la sagesse la plus parfaite.</ref>. Ces récits, qu'ils soient authentiques ou pour partie légendaires, illustrent l'image qu'Anaxagore a laissée dans la mémoire doxographique : celle du philosophe contemplatif, détaché des affaires domestiques et consacré à l'étude du cosmos. Une anecdote célèbre, rapportée par plusieurs sources anciennes, témoigne de ce détachement. Interrogé sur son attachement à sa patrie, Anaxagore aurait répondu en levant la main vers le ciel : « J'ai un soin extrême de ma patrie », signifiant par là que le véritable philosophe considère l'univers entier comme sa demeure<ref>Plutarque, ''Vie de Périclès'', 4, 3-5 ; Aristote, ''Éthique à Eudème'', 1215b6-8.</ref>. Lorsqu'on lui demanda quel était l'homme le plus heureux, il aurait répondu, sur le même mode paradoxal, qu'aucun de ceux auxquels on songe ordinairement ne saurait prétendre à ce titre, et que le véritable bonheur paraîtrait même étrange à son interlocuteur<ref>Aristote, ''Éthique à Eudème'', 1215b6-8.</ref>. À quelqu'un qui lui demandait encore pour quelle raison on devrait choisir de naître plutôt que de ne pas être, il aurait répondu : « Afin de contempler les cieux et l'ordre de l'univers entier »<ref>Aristote, ''Éthique à Eudème'', 1216a11-14. Cette phrase, qui condense toute une conception de la vie philosophique comme ''theoria'', sera reprise par les moralistes anciens et par la tradition philosophique jusqu'à l'époque romaine.</ref>. === L'arrivée à Athènes et l'activité philosophique === La chronologie exacte de la vie d'Anaxagore demeure l'objet de débats parmi les historiens modernes, en raison de divergences importantes entre les sources anciennes<ref>Leonard Woodbury, « Anaxagoras and Athens », ''Phoenix'', vol. 35, n{{o}} 4, 1981, p. 295-315 ; Jaap Mansfeld, « The Chronology of Anaxagoras' Athenian Period and the Date of His Trial », ''Mnemosyne'', vol. 32-33, 1979-1980, p. 39-69 et p. 85-95 ; Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 33-35.</ref>. Selon la chronologie établie par Apollodore d'Athènes et rapportée par Diogène Laërce, Anaxagore serait arrivé à Athènes vers 480 av. J.-C., à l'âge de vingt ans, au moment des guerres médiques<ref>Diogène Laërce, II, 7. Cette tradition est soutenue par certains chercheurs modernes : Patricia O'Brien, « Anaxagoras and the Diurnal Revolution », ''Phronesis'', vol. 13, 1968, p. 133-143 ; Leonard Woodbury, 1981 ; Daniel Graham, ''Science before Socrates'', Oxford, Oxford University Press, 2006.</ref>. D'autres savants, notamment Jaap Mansfeld, proposent une date plus tardive, vers 456 av. J.-C.<ref>Jaap Mansfeld, 1979-1980 ; cf. également David Sider, ''The Fragments of Anaxagoras'', Sankt Augustin, Academia Verlag, 2005, p. 1-18, pour une discussion approfondie des problèmes chronologiques.</ref>. La question n'est pas définitivement tranchée, mais la majorité des historiens s'accorde sur une période d'activité philosophique à Athènes s'étendant sur près de trente ans, probablement entre 480/456 et 450/437 av. J.-C.<ref>Diogène Laërce, II, 7 ; Malcolm Schofield, 1980, p. 33-35.</ref>. Quelle que soit la date précise de son arrivée, Anaxagore est présenté par la tradition antique comme le premier philosophe à introduire la spéculation cosmologique et la philosophie naturelle ionienne dans la cité athénienne<ref>Clément d'Alexandrie, ''Stromates'', I, 14, 63, 3 (DK 59 A 7) : « Anaxagore transporta d'Ionie à Athènes la philosophie. » Cf. également Plutarque, ''Vie de Périclès'', 4, 2 ; Isocrate, ''Sur l'échange'', 268.</ref>. Athènes, en pleine expansion culturelle et politique après sa victoire sur les Perses à Marathon (490 av. J.-C.) et à Salamine (480 av. J.-C.), deviendra rapidement le centre intellectuel du monde grec. C'est dans ce contexte que la tradition situe l'enseignement d'Anaxagore. La tradition biographique antique, dominée par le récit de Plutarque dans la ''Vie de Périclès'', rapporte qu'Anaxagore se serait lié à Périclès, l'homme d'État athénien qui dominera la vie politique de la cité de 454 à 431 av. J.-C.<ref>Plutarque, ''Vie de Périclès'', 4-5, 16, 32 (DK 59 A 15, A 17). Platon, ''Phèdre'', 269e-270a, évoque également l'influence d'Anaxagore sur l'éloquence de Périclès.</ref>. Selon cette tradition, Anaxagore aurait compté parmi les maîtres intellectuels de Périclès, et le surnom d'« instructeur de Périclès » lui aurait été attaché dès l'Antiquité<ref>Plutarque, ''Vie de Périclès'', 4, 5 ; Diogène Laërce, II, 10, 13. Les instruments de référence modernes (''Oxford Classical Dictionary'', s.v. « Anaxagoras ») restent toutefois prudents sur la portée exacte de cette relation, dont les témoignages sont tardifs.</ref>. Il convient cependant de ne pas transformer cette tradition en déclaration politique publique avérée : les sources dont nous disposons sont tardives, souvent biographiques ou rhétoriques, et la figure de l'« instructeur de Périclès » appartient en partie à la construction doxographique. L'idée que le grand orateur démocratique d'Athènes ait trouvé dans les leçons d'un philosophe naturaliste la source de son éloquence élevée et de son détachement aristocratique correspond à un topos biographique commode, qu'il convient de ne pas surinterpréter en termes de dette intellectuelle publique. Parmi les disciples ou auditeurs d'Anaxagore, les sources anciennes mentionnent aussi le poète tragique Euripide<ref>Diogène Laërce, II, 10 ; Héracléide du Pont, cité par Aulu-Gelle, ''Nuits attiques'', XV, 20 (DK 59 A 17). Certaines idées cosmologiques présentes dans les pièces d'Euripide semblent refléter l'enseignement d'Anaxagore.</ref>, le musicien et sophiste Damon<ref>Plutarque, ''Vie de Périclès'', 4.</ref>, et peut-être Archélaos, qui aurait ensuite été le maître de Socrate<ref>Diogène Laërce, II, 16, 23 (DK 60 A 1, A 4).</ref>. Concernant Socrate lui-même, la question de savoir s'il a personnellement rencontré Anaxagore demeure controversée. Dans le ''Phédon'', Platon fait dire à Socrate qu'il a entendu quelqu'un lire dans le livre d'Anaxagore (la tradition ancienne a identifié ce lecteur à Archélaos)<ref>Platon, ''Phédon'', 97b-99d. L'identité du lecteur n'est pas précisée par Platon, mais la tradition ancienne l'a identifié à Archélaos.</ref>, ce qui suggère que Socrate n'a probablement pas eu de contact direct avec le philosophe de Clazomènes. Dans l'''Apologie'', Platon fait par ailleurs référence au fait que les livres d'Anaxagore étaient en vente pour une drachme à l'orchestre du théâtre<ref>Platon, ''Apologie'', 26d. Cette remarque témoigne de la large diffusion de l'œuvre d'Anaxagore à Athènes à la fin du V{{e}} siècle av. J.-C.</ref>, ce qui indique que ses idées étaient largement connues à Athènes, même en l'absence d'enseignement oral direct. === L'œuvre écrite === Anaxagore a composé un traité intitulé ''Sur la nature'' (Περὶ φύσεως), rédigé en prose ionienne<ref>Diogène Laërce, II, 6 ; Simplicius, ''Commentaire sur la Physique d'Aristote'', 27, 2 (DK 59 A 41).</ref>. Selon les témoignages anciens, il n'aurait écrit qu'un seul livre<ref>Diogène Laërce, I, 16 ; II, 6-7.</ref>, bien que Platon emploie le pluriel τὰ Ἀναξαγόρου βιβλία dans l'''Apologie''<ref>Platon, ''Apologie'', 26d.</ref>. Cette apparente contradiction s'explique probablement par le fait que l'ouvrage, même relativement court, était copié sur plusieurs rouleaux de papyrus, ce qui était l'usage courant à l'époque pour les œuvres en prose. Diogène Laërce rapporte qu'Anaxagore fut le premier à publier un livre contenant des figures et des diagrammes<ref>Diogène Laërce, II, 11.</ref>, détail qui témoigne de la dimension scientifique et pédagogique qu'on lui prêtait dans l'Antiquité tardive. Le traité d'Anaxagore était rédigé dans un style sobre et dense, caractérisé par une prose ionienne archaïsante<ref>Karl Deichgräber, « Anaxagoras Stil », ''Philologus'', vol. 87, 1933, p. 15-26 ; Hermann Fränkel, ''Dichtung und Philosophie des frühen Griechentums'', Munich, C.H. Beck, 1962, p. 529-547.</ref>. Contrairement aux poèmes philosophiques de Parménide et d'Empédocle, rédigés en hexamètres dactyliques à la manière d'Homère ou d'Hésiode, Anaxagore choisit la prose comme véhicule de sa pensée, s'inscrivant ainsi dans la tradition ionienne des philosophes naturalistes inaugurée par Anaximandre et Anaximène. De ce traité, il ne subsiste aujourd'hui qu'une vingtaine de fragments authentiques, principalement conservés par Simplicius de Cilicie (VI{{e}} siècle ap. J.-C.) dans ses commentaires sur Aristote<ref>L'édition de référence des fragments et témoignages demeure celle de Hermann Diels et Walther Kranz, ''Die Fragmente der Vorsokratiker'', 6{{e}} édition, Berlin, Weidmann, 1951-1952, vol. II, p. 5-44 (59 A et B). Une édition plus récente est celle d'André Laks et Glenn Most, ''Early Greek Philosophy'', Loeb Classical Library, 9 vol., Cambridge (Mass.), Harvard University Press, 2016, qui a introduit un nouveau système de numérotation ; éditions et traductions anglaises : Patricia Curd, ''Anaxagoras of Clazomenae : Fragments and Testimonia'', Toronto, University of Toronto Press, 2007 ; David Sider, ''The Fragments of Anaxagoras'', Sankt Augustin, Academia Verlag, 2005.</ref>. Simplicius, conscient de la rareté de l'ouvrage à son époque, prit soin de citer longuement les premières parties du traité, qui exposaient les principes généraux du système<ref>Simplicius, ''Commentaire sur la Physique d'Aristote'', 155, 23-157, 4 (fragments B1-B4) ; 164, 24-166, 1 (fragment B12).</ref>. === Le procès et l'exil === Les circonstances du départ d'Anaxagore d'Athènes et de sa fin de vie sont particulièrement controversées dans les sources anciennes. Diogène Laërce rapporte pas moins de quatre versions contradictoires du procès d'Anaxagore<ref>Diogène Laërce, II, 12-15. Ces quatre récits divergent sur l'identité de l'accusateur, la nature précise des charges, le déroulement du procès et son issue.</ref>. Selon une première version, attribuée à Sotion, Anaxagore aurait été accusé d'impiété (ἀσέβεια) par Cléon pour avoir déclaré que le soleil était une masse de métal incandescent ; Périclès l'aurait défendu, et Anaxagore aurait été condamné à une amende de cinq talents et à l'exil<ref>Sotion, dans Diogène Laërce, II, 12.</ref>. Selon Satyre, l'accusateur aurait été Thucydide (l'adversaire politique de Périclès, et non l'historien), les charges auraient inclus non seulement l'impiété mais aussi la trahison (sympathies pro-perses ou « médisme »), et Anaxagore aurait été condamné à mort par contumace<ref>Satyre, dans Diogène Laërce, II, 12-13.</ref>. Deux autres versions, attribuées respectivement à Hermippe et à Hiéronymos de Rhodes, présentent des variantes sur le rôle de Périclès et l'issue du procès<ref>Hermippe et Hiéronymos de Rhodes, dans Diogène Laërce, II, 13-14.</ref>. Plutarque, dans la ''Vie de Périclès'', fournit une version différente. Selon lui, un devin et politicien athénien nommé Diopeithès aurait fait voter un décret (le « décret de Diopeithès ») autorisant les poursuites judiciaires contre ceux qui ne reconnaissaient pas les dieux ou enseignaient des doctrines sur les phénomènes célestes<ref>Plutarque, ''Vie de Périclès'', 32, 1-2. Ce décret, daté approximativement de 433-432 av. J.-C., aurait visé Anaxagore mais aussi, indirectement, Périclès.</ref>. Selon cette version, Anaxagore aurait été accusé d'impiété pour avoir soutenu que le soleil n'était pas une divinité mais une pierre incandescente, et que la lune était une terre<ref>Plutarque, ''Vie de Périclès'', 32, 2 ; Platon, ''Apologie'', 26d, où Mélètos accuse Socrate de professer les mêmes opinions qu'Anaxagore.</ref>. Grâce à l'intervention de Périclès, Anaxagore aurait échappé à la condamnation à mort mais aurait dû s'exiler d'Athènes<ref>Plutarque, ''Vie de Périclès'', 32, 5.</ref>. L'historicité même de ce procès a été mise en doute par certains historiens modernes, qui soulignent l'absence de témoignages contemporains et les ressemblances troublantes avec le procès de Socrate<ref>J. A. Davison, « Protagoras, Democritus, and Anaxagoras », ''Classical Quarterly'', vol. 3, 1953, p. 33-45, exprime un scepticisme extrême.</ref>. Néanmoins, la plupart des spécialistes admettent aujourd'hui qu'un procès a bien eu lieu, même si les détails précis nous échappent<ref>Leonard Woodbury, 1981 ; Daniel Graham, 2006, p. 311-318, défendent l'historicité du procès tout en reconnaissant les difficultés chronologiques.</ref>. La date du procès demeure elle aussi incertaine : certains savants la placent vers 450 av. J.-C.<ref>Cette date traditionnelle est défendue notamment dans l'''Oxford Classical Dictionary'', 2{{e}} édition, 1970, s.v. « Anaxagoras ».</ref>, d'autres vers 437-436 av. J.-C.<ref>Mansfeld, 1979-1980.</ref>, d'autres encore vers 433-432 av. J.-C.<ref>Woodbury, 1981.</ref>. Quelle que soit la date précise, Anaxagore se retire à Lampsaque, colonie grecque d'Asie Mineure située sur l'Hellespont (l'actuel détroit des Dardanelles), où, selon la tradition, il est accueilli avec honneur<ref>Diogène Laërce, II, 14-15 ; Valère Maxime, ''Faits et dits mémorables'', VIII, 7, ext. 5.</ref>. Il y meurt en 428 av. J.-C., à l'âge de soixante-douze ans selon Apollodore<ref>Apollodore, dans Diogène Laërce, II, 7.</ref>. Les habitants de Lampsaque lui vouent, selon les sources tardives, un culte et célèbrent sa mémoire pendant plus d'un siècle après sa mort. Ils auraient élevé un autel dédié à l'Esprit et à la Vérité (Νοῦς καὶ Ἀλήθεια) en son honneur, et observé l'anniversaire de sa mort comme un jour férié pour les enfants des écoles<ref>Diogène Laërce, II, 15 ; Élien, ''Histoire variée'', VIII, 19 ; Cicéron, ''Tusculanes'', V, 36, 104.</ref>. Une inscription fut placée sur sa tombe, dont le texte exact ne nous est malheureusement pas parvenu, mais qui témoignait, selon Diogène Laërce, de l'estime dans laquelle les citoyens de Lampsaque tenaient le philosophe<ref>Diogène Laërce, II, 15.</ref>. === Portrait et anecdotes === Les sources anciennes ont conservé de nombreuses anecdotes sur Anaxagore qui, même si leur authenticité historique ne peut être garantie, témoignent de l'image du philosophe dans la tradition biographique antique. Galien rapporte qu'Anaxagore, apprenant la mort de son fils, aurait déclaré avec le plus grand calme : « Je savais qu'il était mortel quand je l'ai engendré »<ref>Galien, ''Des opinions d'Hippocrate et de Platon'', IV, 7, 41 (DK 59 A 33).</ref>. Ces récits contribuèrent à faire d'Anaxagore le modèle, dans la tradition, du philosophe détaché des contingences matérielles et consacré à la contemplation de la nature. Dans le ''Phèdre'' de Platon, Socrate attribue l'éloquence de Périclès à l'influence d'Anaxagore, qui lui aurait appris à s'élever au-dessus des préoccupations quotidiennes par la spéculation sur la nature de l'intelligence (νοῦς) et de la folie<ref>Platon, ''Phèdre'', 269e-270a.</ref>. Cette remarque témoigne de la réputation d'Anaxagore dans l'Athènes classique : celle d'un penseur dont les spéculations abstraites et « météorologiques » (au sens ancien de l'étude des phénomènes célestes) pouvaient paraître étranges ou excessives aux yeux du commun, mais qui exerçaient une fascination certaine sur les esprits cultivés. Cette image se reflète jusque dans la comédie d'Aristophane, ''Les Nuées'', où les spéculations cosmologiques tournées en ridicule semblent en partie inspirées par les doctrines d'Anaxagore, bien que ce soit Socrate qui soit présenté sur scène<ref>Aristophane, ''Les Nuées'', passim. Cf. Kenneth Dover, ''Aristophanes Clouds'', Oxford, Clarendon Press, 1968, p. xxxii-xlvii, pour la discussion du rapport entre les théories cosmologiques ridiculisées dans la pièce et celles d'Anaxagore.</ref>. === L'école de Lampsaque === À Lampsaque, Anaxagore fonda ou du moins anima une école philosophique qui continua son enseignement après sa mort<ref>Malcolm Schofield, 1980, p. 1 ; W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 266-267.</ref>. Parmi ses disciples à Lampsaque, les sources mentionnent Métrodore de Lampsaque<ref>Diogène Laërce, II, 17 ; Plutarque, ''Des opinions des philosophes'', 888e (DK 61).</ref>, qui poursuivit et développa les interprétations allégoriques d'Homère dans un esprit anaxagoréen. L'influence de l'école de Lampsaque se prolongea au moins jusqu'à la fin du V{{e}} siècle av. J.-C. Cette présence d'Anaxagore à Lampsaque explique peut-être pourquoi ses idées ont continué à circuler et à être discutées dans le monde grec, malgré son départ d'Athènes et le caractère fragmentaire de la tradition de son œuvre écrite. == Les principes métaphysiques fondamentaux == La philosophie d'Anaxagore se construit en réponse directe aux exigences métaphysiques établies par Parménide d'Élée<ref>Patricia Curd, ''The Legacy of Parmenides: Eleatic Monism and Later Presocratic Thought'', Princeton, Princeton University Press, 1998, p. 123-142 ; Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 27-47.</ref>. Dans son poème philosophique, Parménide avait posé deux principes fondamentaux : « l'être est et ne peut pas ne pas être » (ἔστιν εἶναι) et « le non-être n'est pas et ne peut pas être » (οὐκ ἔστι μὴ εἶναι)<ref>Parménide, fragment B2, DK 28 B 2, 3-5 ; cf. également B6, 1-2 et B8, 1-2.</ref>. De ces prémisses, Parménide concluait que toute génération et toute corruption véritables sont impossibles, car elles impliqueraient un passage de l'être au non-être ou du non-être à l'être, transitions logiquement impossibles puisque le non-être ne peut en aucune manière être<ref>Parménide, B8, 6-21 ; Aristote, ''Physique'', I, 8, 191a23-31.</ref>. Plus encore, Parménide soutenait que l'être véritable devait être un, continu, homogène, immuable et éternel<ref>Parménide, B8, 22-25 : « Il n'est pas divisible, puisqu'il est tout entier semblable » (οὐδὲ διαιρετόν ἐστιν, ἐπεὶ πᾶν ἐστιν ὁμοῖον).</ref>. Anaxagore accepte les principes éléatiques fondamentaux, c'est-à-dire l'impossibilité du passage de l'être au non-être et inversement, mais refuse d'en tirer la conclusion que le monde sensible et le changement sont illusoires<ref>Aristote, ''Métaphysique'', I, 3, 984a11-16 ; Simplicius, ''Commentaire sur la Physique d'Aristote'', 25, 19-26 (DK 59 A 52).</ref>. Selon Simplicius, « Anaxagore de Clazomènes, qui fut un disciple de la philosophie d'Anaximène, fut le premier à s'opposer à Parménide »<ref>Simplicius, ''Commentaire sur la Physique'', 25, 19-21 (DK 59 A 52). L'affirmation qu'Anaxagore fut disciple d'Anaximène est chronologiquement problématique et doit être comprise au sens large d'une appartenance à la tradition ionienne.</ref>. Plutôt que de nier la réalité du changement et de la diversité, Anaxagore cherche à fonder une cosmologie conforme aux exigences parménidiennes tout en rendant compte de la multiplicité et du mouvement observables dans la nature<ref>Malcolm Schofield, 1980, p. 38-42 ; G. E. L. Owen, « Eleatic Questions », ''Classical Quarterly'', vol. 10, 1960, p. 84-102.</ref>. Comme l'a remarqué G. E. L. Owen, la phrase d'ouverture du traité d'Anaxagore, « Toutes choses étaient ensemble », est « manifestement formulée comme une contradiction plate de Parménide sur plusieurs questions majeures »<ref>G. E. L. Owen, « The Place of the Timaeus in Plato's Dialogues », ''Classical Quarterly'', vol. 3, 1953, p. 79-95, cité dans Malcolm Schofield, 1980, p. 64.</ref>. === Le principe de conservation === Le premier principe métaphysique fondamental qu'Anaxagore énonce se trouve dans le fragment B17 : « Les Grecs ne pensent pas correctement au sujet de la génération et de la corruption. Aucune chose ne naît ni ne périt, mais à partir des choses qui sont, il y a mélange et séparation. Ainsi, ils appelleraient correctement la génération "mélange" (σύγκρισις) et la corruption "séparation" (διάκρισις) »<ref>Anaxagore, fragment B17, cité par Simplicius, ''Commentaire sur la Physique'', 163, 20-24 (DK 59 B 17).</ref>. Ce principe de conservation constitue le fondement de toute sa physique et sa réponse directe à l'interdit parménidien. Ce qui apparaît comme génération n'est en réalité qu'un réarrangement d'éléments préexistants qui se mélangent (συμμίσγεται). Ce qui semble être corruption n'est que la dissociation (διακρίνεται) de ces mêmes éléments<ref>Anaxagore, B17 ; cf. également Aristote, ''Génération et corruption'', I, 1, 314a18-20 (DK 59 A 52) : « Anaxagore et d'autres disent que la génération et la corruption sont mélange et séparation. »</ref>. Rien ne naît du néant (ex nihilo nihil fit), rien ne retourne au néant : les composants élémentaires de la réalité existent de toute éternité et demeurent inaltérables dans leur nature propre. Aristote souligne qu'Anaxagore, comme d'autres philosophes naturalistes, fonde explicitement ce principe sur l'axiome parménidien<ref>Aristote, ''Physique'', I, 4, 187a26-31.</ref>. Le principe prend chez Anaxagore une forme plus spécifique encore dans le fragment B10, où il pose la question rhétorique : « Comment le cheveu pourrait-il provenir de ce qui n'est pas cheveu, et la chair de ce qui n'est pas chair ? » (πῶς γὰρ ἂν ἐκ μὴ τριχὸς γένοιτο θρὶξ καὶ σὰρξ ἐκ μὴ σαρκός;)<ref>Anaxagore, fragment B10, conservé dans une scholie à Grégoire de Nazianze (DK 59 B 10).</ref>. Cette formulation, qui deviendra proverbiale dans l'Antiquité, exprime ce que les commentateurs modernes appellent le « principe du semblable par le semblable » (similia similibus) : une substance ne peut provenir que de la même substance, déjà présente sous une forme latente<ref>Aristote, ''Physique'', I, 4, 187a26-29 ; cf. Malcolm Schofield, 1980, p. 51-58 ; Jonathan Barnes, ''The Presocratic Philosophers'', Londres, Routledge, 1982, p. 309-315.</ref>. Ce principe présente une analogie frappante avec celui qu'Antoine-Laurent de Lavoisier formulera en 1789 comme principe de conservation de la matière, et la sentence « Rien ne se perd, rien ne se crée, tout se transforme » en est parfois rapprochée. Il convient cependant de ne pas surinterpréter cette analogie : les mécanismes qu'Anaxagore propose pour expliquer les transformations (mélange et séparation d'ingrédients éternels) sont qualitatifs et ne relèvent pas d'une conception chimique quantitative au sens moderne<ref>Antoine-Laurent de Lavoisier, ''Traité élémentaire de chimie'', Paris, Cuchet, 1789, vol. I, p. 101 : « Rien ne se crée, ni dans les opérations de l'art, ni dans celles de la nature, et l'on peut poser en principe que, dans toute opération, il y a une égale quantité de matière avant et après l'opération. » Le rapprochement avec Anaxagore relève plus de l'analogie conceptuelle rétrospective que d'une filiation historique documentée.</ref>. L'intuition conservatrice d'Anaxagore partage avec le principe lavoisien l'idée que rien ne se perd ni ne se crée dans le devenir physique, mais il serait anachronique d'y voir l'un des principes cardinaux de la chimie moderne. === Tout est dans tout === Le deuxième principe métaphysique fondamental d'Anaxagore s'énonce ainsi : « Il y a une part de tout en toute chose » (ἐν παντὶ παντὸς μοῖρα ἔνεστι, fragment B11)<ref>Anaxagore, fragment B11, cité par Simplicius, ''Commentaire sur la Physique'', 164, 26 (DK 59 B 11).</ref>. Ce principe, souvent désigné par la formule latine ''omnia in omnibus'' (« toutes choses dans toutes choses »), constitue sans doute la thèse la plus caractéristique et la plus déroutante de la philosophie d'Anaxagore<ref>Cf. Gregory Vlastos, « The Physical Theory of Anaxagoras », ''Philosophical Review'', vol. 59, 1950, p. 31-57 ; Colin Strang, « The Physical Theory of Anaxagoras », ''Archiv für Geschichte der Philosophie'', vol. 45, 1963, p. 101-118.</ref>. Il signifie que chaque portion de matière, si infime soit-elle, contient en elle-même des portions de toutes les autres substances qui existent dans l'univers. Il n'existe aucune substance pure, aucun élément qui puisse être isolé de tous les autres<ref>Anaxagore, B6 : « Puisqu'il est impossible qu'il y ait un minimum, il ne serait pas possible que [quelque chose] soit séparé, ni ne vienne à l'être par soi-même » (ἐπεὶ δὲ οὐκ ἔστι τοῦ ὀλίγου τὸ ἐλάχιστον, οὐκ ἂν γένοιτο χωρίς, οὐδὲ γένοιτο καθ᾽ ἑαυτό).</ref>. Dans un morceau d'or, il y a non seulement de l'or en proportion dominante, mais aussi de la chair, des os, du chaud, du froid, du sec, de l'humide, et ainsi de suite pour toutes les qualités et substances possibles<ref>Aristote, ''Métaphysique'', I, 3, 984a13-16 ; Simplicius, ''Commentaire sur la Physique'', 34, 29-35, 9 (DK 59 A 45).</ref>. Cette thèse découle logiquement du principe de conservation et du principe du semblable par le semblable combinés. En effet, comment la chair pourrait-elle provenir du pain et du lait que nous consommons, si le pain et le lait ne contenaient pas déjà de la chair ? La question rhétorique du fragment B10 impose cette conclusion. La nourriture que nous ingérons doit nécessairement contenir en elle-même, quoique de manière imperceptible en raison de la petitesse des parties, toutes les substances qui composent notre corps : cheveux, ongles, veines, artères, nerfs, os<ref>Scholie anonyme à Grégoire de Nazianze, DK 59 B 10 : « Dans le même liquide séminal, il y a des cheveux, des ongles, des veines et des artères, des nerfs et des os, et ils sont imperceptibles en raison de la petitesse des parties (δι᾽ ὀλιγότητα), mais lorsqu'ils croissent, ils se séparent graduellement. »</ref>. C'est par un processus de séparation graduelle (ἀποκρίνεσθαι), au cours de la croissance, que ces éléments préexistants dans la nourriture viennent s'ajouter aux parties correspondantes de notre organisme. Le principe « tout est dans tout » s'applique non seulement aux substances naturelles comme la chair et les os, mais aussi aux qualités opposées. Anaxagore affirme que « le noir est dans le blanc et le blanc dans le noir » et qu'il en va de même pour le lourd et le léger, le chaud et le froid<ref>Anaxagore, B15, cité par Simplicius, ''Commentaire sur le Ciel'', 608, 26 (DK 59 B 15) ; Simplicius, ''Commentaire sur la Physique'', 175, 11-14.</ref>. Ces oppositions qualitatives ne sont pas absolues mais relatives : ce qui nous paraît blanc contient en réalité du noir, mais en proportion si faible que cette part de noir demeure invisible à nos sens. De même, ce qui est léger contient du lourd, et inversement<ref>Anaxagore, B1, B4b ; Aristote, ''Physique'', I, 4, 187b1-7 (DK 59 A 52).</ref>. Cette doctrine a suscité d'intenses débats interprétatifs depuis l'Antiquité. Aristote la présente comme une tentative de résoudre le problème de la nutrition tout en respectant les contraintes éléatiques<ref>Aristote, ''Physique'', III, 4, 203a19-b3 ; cf. W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 289-294.</ref>. Les interprètes modernes se divisent sur la question de savoir si Anaxagore conçoit ces « portions » (μοῖραι) comme des particules infinitésimales ou comme des qualités interpénétrées sans structure corpusculaire déterminée<ref>Pour la première interprétation « particulaire », voir Gregory Vlastos, 1950 ; Arthur L. Peck, « Anaxagoras : Predication as a Problem in Physics », ''Classical Quarterly'', vol. 25, 1931, p. 27-37 et p. 112-120. Pour la seconde interprétation « non-particulaire », voir Colin Strang, 1963 ; Daniel Graham, « Was Anaxagoras a Reductionist? », ''Ancient Philosophy'', vol. 24, 2004, p. 1-18 ; Anna Marmodoro, « Anaxagoras's Qualitative Gunk », ''British Journal for the History of Philosophy'', vol. 23, 2015, p. 402-422.</ref>. Quoi qu'il en soit, ce principe constitue la clé de voûte du système physique d'Anaxagore. === Pas de plus petit ni de plus grand === Le troisième principe métaphysique, exposé dans le fragment B3, stipule qu'« il n'y a pas de plus petit dans le petit, mais toujours un plus petit encore, car il est impossible que ce qui est cesse d'être par division (οὐ γάρ ἐστι τοῦ ἀποκεκρίσθαι τὸ ἐλάχιστον). Mais il y a aussi toujours un plus grand que le grand, et il est égal au petit en multitude (πλῆθος) ; mais par rapport à elle-même, chaque chose est à la fois grande et petite »<ref>Anaxagore, fragment B3, cité par Simplicius, ''Commentaire sur la Physique'', 164, 17-20 (DK 59 B 3).</ref>. Ce principe garantit la divisibilité infinie de la matière et s'oppose frontalement à toute conception atomiste de type démocritéen<ref>Leucippe et Démocrite, fragments DK 67-68 ; Aristote, ''Génération et corruption'', I, 2, 315b28-317a2 ; I, 8, 325a23-b5.</ref>. Contrairement à Leucippe et Démocrite, qui postulent l'existence d'atomes insécables (ἄτομα, littéralement « indivisibles »), Anaxagore maintient que toute portion de matière, aussi petite soit-elle, peut encore être divisée, et que cette division révélera toujours la présence de toutes les substances selon le principe « tout est dans tout »<ref>Démocrite, fragments B9, B125, B156, B164 ; cf. Aristote, ''De la génération et de la corruption'', I, 8, 325a23-b5 ; Simplicius, ''Commentaire sur le Ciel'', 294, 33-295, 22 (DK 68 A 37).</ref>. Pour Anaxagore, la divisibilité peut être infinie sans que la matière s'évanouisse dans le néant, précisément parce que chaque fragment, si petit soit-il, contient encore toutes les substances<ref>Anaxagore, B6 : « Puisque les parts du grand et du petit sont égales en nombre, ainsi également toutes choses seraient dans toute chose. Il n'est pas possible qu'elles soient séparées, mais toutes choses ont une part de toute chose » (ἴσαι γὰρ ἀριθμῷ μοῖραί εἰσι καὶ τοῦ μεγάλου καὶ τοῦ σμικροῦ· καὶ οὕτως ἂν εἴη πάντα ἐν παντί).</ref>. Cette infinité dans la petitesse se combine avec l'infinité dans la grandeur. Il n'existe pas de limite supérieure à l'extension d'une substance, pas plus qu'il n'existe de limite inférieure<ref>Anaxagore, B3 : « Mais il y a aussi toujours un plus grand que le grand, et il est égal au petit en multitude » (ἀλλὰ καὶ μεγάλου ἀεὶ ἔστι μεῖζον· καὶ ἴσον ἐστὶ τῷ σμικρῷ πλήθει).</ref>. Cette double infinité, à la fois de division et d'extension, constitue un trait distinctif de la métaphysique d'Anaxagore et pose des problèmes interprétatifs considérables aux commentateurs, tant anciens que modernes<ref>Margaret Furth, « A Philosophical Hero? Anaxagoras and the Eleatics », ''Oxford Studies in Ancient Philosophy'', vol. 9, 1991, p. 95-129 ; David Furley, « Anaxagoras, Plato and Naming of Parts », dans ''Presocratic Philosophy: Essays in Honour of Alexander Mourelatos'', éd. Victor Caston et Daniel Graham, Aldershot, Ashgate, 2002, p. 119-126.</ref>. Le principe de non-minimum a une conséquence métaphysique importante : il assure que le mélange universel « tout est dans tout » ne pourra jamais être défait. En effet, puisqu'il n'existe pas de plus petite quantité d'une substance, celle-ci ne pourra jamais être entièrement extraite d'un mélange. On pourra réduire sa proportion indéfiniment, mais elle demeurera toujours présente en quelque mesure<ref>Anaxagore, B6 ; Simplicius, ''Commentaire sur la Physique'', 164, 20-22 : « Car si tout est dans tout et si tout se sépare de tout, alors du prétendu minimum quelque chose de plus petit que lui sera séparé, et du prétendu maximum quelque chose de plus grand que lui a été séparé. »</ref>. Ainsi, le principe de mélange universel est garanti de manière structurelle par le principe de divisibilité infinie. Comme l'a remarqué Colin Strang, « la complexité structurelle n'est pas, dans la théorie d'Anaxagore, fonction de la taille »<ref>Colin Strang, 1963, p. 366 : « Structural complexity is not, on Anaxagoras' theory, a function of size. »</ref>. La justification qu'Anaxagore donne de ce principe est explicitement parménidienne : « car il est impossible que ce qui est cesse d'être » (οὐ γὰρ ἔστι τοῦ εἶναι τὸ μὴ εἶναι)<ref>Anaxagore, B3 ; cette formulation reprend directement l'axiome de Parménide, B2, 3.</ref>. Si l'on pouvait diviser la matière jusqu'à la faire disparaître complètement, cela impliquerait un passage de l'être au non-être, ce qui viole l'interdit fondamental. Toute division, aussi poussée soit-elle, doit donc laisser subsister quelque chose, et ce quelque chose, par application du principe « tout est dans tout », contiendra encore des portions de toutes les substances<ref>Simplicius, ''Commentaire sur la Physique'', 164, 23-165, 1 (commentaire sur B3).</ref>. === Le principe de prédominance === Du principe « tout est dans tout » découle une difficulté évidente : si chaque chose contient une part de toutes les autres, comment expliquer que nous percevions des objets distincts et identifiables ? Comment distinguer l'or de la chair, le noir du blanc, si l'or contient de la chair et la chair contient de l'or, si le noir contient du blanc et le blanc contient du noir ? Anaxagore résout cette difficulté par ce que les commentateurs modernes appellent le « principe de prédominance » (bien qu'Anaxagore lui-même n'emploie pas ce terme technique)<ref>Le terme « principe de prédominance » (predominance principle) a été introduit par les interprètes modernes, notamment Gregory Vlastos, 1950, p. 47-50 ; Malcolm Schofield, 1980, p. 88-101.</ref>. Dans le fragment B12, il affirme : « Chaque chose est et était très manifestement (ἐμφανέστατα) constituée de celles des choses dont il y a le plus en elle »<ref>Anaxagore, fragment B12, cité par Simplicius, ''Commentaire sur la Physique'', 156, 1-157, 4, à 156, 10-11 (DK 59 B 12).</ref>. Autrement dit, une chose tire son identité et ses propriétés apparentes des substances qui prédominent en elle. Un morceau d'or nous apparaît comme de l'or parce que la substance or y est présente en proportion largement supérieure à toutes les autres substances. De même, notre chair nous semble être de la chair parce que la substance chair y domine quantitativement<ref>Simplicius, ''Commentaire sur la Physique'', 34, 29-35, 9 : « Chaque chose semble être cela dont elle a le plus, comme elle l'était auparavant » (δοκεῖν δ᾽ ἕκαστον εἶναι ταῦτα ὧν ἂν πλεῖστα ἐνῇ, ὥσπερ καὶ πρότερον).</ref>. Cette prédominance n'est pas absolue, puisque toutes les autres substances demeurent présentes en quelque proportion, mais elle est suffisante pour conférer à l'objet ses caractéristiques perceptibles<ref>Anaxagore, B12 ; Aristote, ''Physique'', I, 4, 187b1-7.</ref>. Le principe de prédominance permet à Anaxagore de concilier son ontologie de mélange universel avec notre expérience quotidienne d'un monde composé d'objets distincts et identifiables. Il rend compte aussi du changement : lorsqu'un objet semble se transformer en un autre, par exemple lorsque le pain que nous mangeons devient chair, ce qui se produit en réalité est une modification des proportions relatives des substances présentes. La chair, déjà présente dans le pain mais en proportion imperceptible, vient s'ajouter à la chair de notre corps à mesure que les autres composants du pain se dispersent ou que leurs proportions relatives diminuent<ref>Anaxagore, B10 ; Aristote, ''Génération des animaux'', II, 4, 740b26-29 ; Simplicius, ''Commentaire sur la Physique'', 460, 4-465, 19 (DK 59 A 45).</ref>. Contrairement aux apparences sensibles, il n'y a pas de transformation qualitative véritable, mais seulement des réarrangements quantitatifs<ref>Malcolm Schofield, 1980, p. 88-101 ; Daniel Graham, ''Explaining the Cosmos: The Ionian Tradition of Scientific Philosophy'', Princeton, Princeton University Press, 2006, p. 137-152.</ref>. Théophraste, dans son traité ''Sur les sensations'', rapporte qu'Anaxagore soutenait que « toute sensation s'accompagne de douleur » (πᾶσαν αἴσθησιν μετὰ λύπης εἶναι) précisément parce que percevoir implique un contact entre des qualités opposées<ref>Théophraste, ''De Sensibus'', 1, 27-29 (DK 59 A 92). Cf. également Aétius, IV, 9, 1 (DK 59 A 92).</ref>. Si le principe de prédominance explique pourquoi nous percevons des objets distincts, il explique aussi pourquoi cette perception n'est jamais parfaitement exacte : les sens ne peuvent discriminer que les différences marquées de proportion, mais les différences subtiles leur échappent<ref>Sextus Empiricus, ''Contre les mathématiciens'', VII, 90 (DK 59 B 21) : « Les apparences sont une vision des choses non-manifestes » (ὄψις τῶν ἀδήλων τὰ φαινόμενα).</ref>. === Synthèse : la réponse d'Anaxagore à Parménide === Les quatre principes métaphysiques que nous venons d'examiner, soit la conservation, le « tout est dans tout », la divisibilité infinie et la prédominance, forment un système cohérent qui constitue la réponse d'Anaxagore au défi parménidien<ref>Patricia Curd, 1998, p. 123-165 ; Malcolm Schofield, 1980, p. 27-86.</ref>. Anaxagore accepte l'impossibilité du passage de l'être au non-être, mais rejette les conséquences que Parménide en tire concernant l'unicité, l'immobilité et l'homogénéité de l'être. Premièrement, en remplaçant la génération et la corruption par le mélange et la séparation (B17), Anaxagore sauve les phénomènes sans violer l'interdit éléatique : rien ne naît véritablement, rien ne périt véritablement, il n'y a que réarrangement de ce qui existe déjà de toute éternité. Deuxièmement, en posant que tout est dans tout (B11, B6), Anaxagore explique comment des substances apparemment nouvelles peuvent émerger sans être créées ex nihilo : elles étaient déjà présentes dans le mélange, simplement imperceptibles en raison de leur faible proportion. Troisièmement, en affirmant la divisibilité infinie (B3, B6), Anaxagore garantit que le mélange universel ne pourra jamais être entièrement défait. Quatrièmement, en introduisant le principe de prédominance (B12), Anaxagore rend compte de la diversité phénoménale et de la possibilité de la perception, tout en maintenant l'ontologie du mélange universel. Anaxagore parvient ainsi à préserver les intuitions fondamentales de Parménide, à savoir la permanence de l'être, l'impossibilité du non-être et l'immutabilité substantielle, tout en rendant compte de la pluralité, du changement et du devenir qui caractérisent notre expérience du monde<ref>Patricia Curd, 1998, p. 165 : « Anaxagoras accepts the fundamental Eleatic constraint that what-is-not cannot be, but offers an account of the world that preserves both plurality and change. » Cf. également Daniel Graham, 2006, p. 137-152 ; G. E. R. Lloyd, ''Early Greek Science: Thales to Aristotle'', Londres, Chatto & Windus, 1970, p. 47-52.</ref>. == Les ingrédients primordiaux == La question de savoir quels sont exactement les ingrédients élémentaires (τὰ χρήματα) qui composent l'univers d'Anaxagore a suscité d'intenses débats parmi les commentateurs, depuis Aristote jusqu'aux spécialistes contemporains<ref>Pour une vue d'ensemble des débats, voir Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 99-138 ; Patricia Curd, ''Anaxagoras of Clazomenae : Fragments and Testimonia'', Toronto, University of Toronto Press, 2007, essais 2-4 ; Daniel Graham, « Was Anaxagoras a Reductionist? », ''Ancient Philosophy'', vol. 24, 2004, p. 1-18.</ref>. Le problème est rendu particulièrement complexe par le fait qu'Anaxagore ne fournit pas de liste systématique et exhaustive de ces ingrédients, et que les fragments conservés mentionnent des entités de natures apparemment différentes. Comme le souligne Malcolm Schofield, « malgré le rôle cardinal de la doctrine du "tout est dans tout" dans sa philosophie, les fragments qui ont survécu indiquent que dans son exposition, Anaxagore assigna au moins autant d'importance à son récit du mélange primordial et à sa description du Noûs et de son activité cosmogonique »<ref>Malcolm Schofield, 1980, p. 99.</ref>. === Les opposés === Dans les fragments B1, B2, B4b, B8, B12 et B15, Anaxagore énonce explicitement plusieurs paires d'opposés (τὰ ἐναντία) : l'humide et le sec (τὸ ὑγρόν καὶ τὸ ξηρόν), le chaud et le froid (τὸ θερμόν καὶ τὸ ψυχρόν), le lumineux et l'obscur (τὸ λαμπρόν καὶ τὸ ζοφερόν), le dense et le rare (τὸ πυκνόν καὶ τὸ ἀραιόν)<ref>Anaxagore, B1, B2, B4b, B8, B12, B15.</ref>. Ces qualités opposées jouent un rôle fondamental dans la cosmologie d'Anaxagore. Ce sont elles qui, par leur séparation progressive (ἀποκρίνεσθαι) à partir du mélange originel et leur réagencement ultérieur, donnent naissance à la diversité des phénomènes naturels<ref>Anaxagore, B12, B13, B15, B16 ; Aristote, ''Physique'', I, 4, 187b1-7 (DK 59 A 52).</ref>. Les opposés ne doivent pas être compris comme de simples attributs qui qualifieraient une substance sous-jacente. Dans la pensée présocratique en général, et chez Anaxagore en particulier, la distinction ultérieure entre substance et qualité n'existe pas encore avec la netteté qu'elle acquerra chez Aristote<ref>G. E. L. Owen, « Tithenai ta Phainomena », dans ''Aristotle et les problèmes de méthode'', éd. Suzanne Mansion, Louvain, Publications Universitaires, 1961, p. 83-103 ; Jonathan Barnes, ''The Presocratic Philosophers'', Londres, Routledge, 1982, p. 316-321.</ref>. Le chaud (τὸ θερμόν) n'est pas simplement une propriété qui affecterait une matière indéterminée ; c'est une réalité substantielle en soi, qui peut être présente en plus ou moins grande proportion dans un mélange donné. Comme l'a remarqué F. M. Cornford, ces opposés doivent être compris comme des « choses-qualités » (quality-things)<ref>F. M. Cornford, « Anaxagoras' Theory of Matter », ''Classical Quarterly'', vol. 24, 1930, p. 14-30, aux pages 16-17.</ref>. Gregory Vlastos précise que ces opposés doivent plutôt être compris « comme des formes d'énergie ou de puissance (δύναμις) »<ref>Gregory Vlastos, « The Physical Theory of Anaxagoras », ''Philosophical Review'', vol. 59, 1950, p. 31-57.</ref>. === L'interprétation « austère » : les opposés seuls === Paul Tannery, à la fin du XIXe siècle, fut le premier à contester l'interprétation aristotélicienne selon laquelle Anaxagore aurait postulé l'existence de « particules homéomères » (particules de chair, d'os, etc.)<ref>Paul Tannery, « La théorie de la matière d'Anaxagore », ''Revue Philosophique'', vol. 22, 1886, p. 255-274 ; repris dans ''Pour l'histoire de la science hellène'', Paris, Alcan, 1887, p. 275-290.</ref>. Tannery soutenait qu'Anaxagore ne parlait que de qualités : l'humide, le sec, le chaud, le froid, etc. John Burnet adopta une position similaire dans son ouvrage influent ''Early Greek Philosophy'', reconnaissant que « même lorsque la notion de qualité (ποιότης) avait été définie, cette manière de penser survécut »<ref>John Burnet, ''Early Greek Philosophy'', 4{{e}} édition, Londres, Adam and Charles Black, 1930, p. 256-264, citation p. 263.</ref>. Burnet s'appuyait notamment sur Galien, qui affirme dans son commentaire sur Hippocrate que « ce sont les qualités qui sont éternelles » chez Anaxagore<ref>Galien, ''Commentaire sur le traité hippocratique Des humeurs'', XVI, 32 Kühn.</ref>. Cette interprétation présente plusieurs avantages. Premièrement, elle se fonde étroitement sur les fragments conservés d'Anaxagore lui-même, plutôt que sur les reconstructions d'Aristote, qui écrivait près de cent cinquante ans après Anaxagore et avait ses propres préoccupations philosophiques. Deuxièmement, elle rend compte du rôle cosmologique crucial des opposés dans les fragments B12, B13, B15 et B16, où ce sont explicitement les opposés qui se séparent lors de la cosmogonie<ref>Malcolm Schofield, 1980, p. 107-113 ; Colin Strang, « The Physical Theory of Anaxagoras », ''Archiv für Geschichte der Philosophie'', vol. 45, 1963, p. 101-118.</ref>. Cette interprétation a été défendue au XXe siècle par plusieurs spécialistes éminents, notamment F. M. Cornford, Gregory Vlastos (avec certaines réserves), Colin Strang, Malcolm Schofield (avec des nuances importantes), Brad Inwood, David Sedley, et plus récemment Anna Marmodoro<ref>F. M. Cornford, 1930 ; Gregory Vlastos, 1950, p. 329-333 ; Colin Strang, 1963 ; Malcolm Schofield, 1980, p. 99-138 ; Brad Inwood, « Anaxagoras and Infinite Divisibility », ''Illinois Classical Studies'', vol. 11, 1986, p. 17-33 ; David Sedley, ''Creationism and Its Critics in Antiquity'', Berkeley, University of California Press, 2007, p. 24-26 ; Anna Marmodoro, « Anaxagoras's Qualitative Gunk », ''British Journal for the History of Philosophy'', vol. 23, 2015, p. 402-422.</ref>. === L'interprétation aristotélicienne : les homéomères === Toutefois, la question devient plus complexe lorsqu'on prend en considération les témoignages indirects, notamment ceux d'Aristote. Dans plusieurs passages, Aristote attribue à Anaxagore une doctrine des « homéomères » (τὰ ὁμοιομερῆ), c'est-à-dire des substances dont chaque partie est semblable au tout<ref>Aristote, ''Métaphysique'', I, 3, 984a11-16 ; Aristote, ''Génération et corruption'', I, 1, 314a18-24 (DK 59 A 46).</ref>. Les exemples donnés par Aristote incluent la chair, les os, le sang, la moelle, l'or, le bois, autant de substances naturelles que nous rencontrons dans l'expérience quotidienne. Cependant, le terme même « homéomère » n'apparaît nulle part dans les fragments conservés d'Anaxagore. C'est Aristote qui l'a introduit pour caractériser ce qu'il comprenait être la position d'Anaxagore<ref>Le terme ὁμοιομερής apparaît d'abord chez Aristote, ''Parties des animaux'', II, 1, 646b10-20 ; ''Génération et corruption'', I, 1, 314a18 sq. Cf. Pierre Pellegrin, « La théorie aristotélicienne des homéomères », ''Revue de Métaphysique et de Morale'', vol. 86, 1981, p. 449-467.</ref>. Cette discordance entre les fragments authentiques, qui mentionnent principalement des opposés, et l'interprétation aristotélicienne, qui privilégie les substances naturelles comme la chair et les os, a conduit à une longue controverse parmi les spécialistes modernes. Comme le souligne W. K. C. Guthrie, « le fait le plus gênant pour ceux qui souhaitent suivre Aristote est que le terme homéomères n'apparaît jamais dans les fragments d'Anaxagore lui-même, et que les fragments ne mentionnent jamais explicitement la chair, les os ou le sang comme des ingrédients élémentaires »<ref>W. K. C. Guthrie, 1965, p. 284-285.</ref>. L'interprétation « expansive », qui accepte pleinement le témoignage aristotélicien, a néanmoins été défendue par plusieurs spécialistes, notamment Arthur L. Peck dans les années 1920 et 1930, et plus récemment par George Kerferd<ref>Arthur L. Peck, « Anaxagoras : Predication as a Problem in Physics », ''Classical Quarterly'', vol. 25, 1931, p. 27-37 et p. 112-120 ; George B. Kerferd, « Anaxagoras and the Concept of Matter before Aristotle », ''Bulletin of the John Rylands Library'', vol. 52, 1969, p. 129-143.</ref>. === L'interprétation médiane === Une troisième voie, une interprétation « médiane », a été proposée notamment par Malcolm Schofield et Patricia Curd<ref>Malcolm Schofield, 1980, p. 99-138 ; Patricia Curd, 1998, p. 123-165 ; Patricia Curd, 2007, essais 2-4.</ref>. Selon cette lecture, l'ontologie d'Anaxagore comprend plusieurs catégories d'entités : les opposés, les substances naturelles fondamentales comme les métaux, la terre, l'air et l'éther, et les ingrédients biologiques comme la chair, le sang et les os. En revanche, les plantes, les animaux et leurs parties organiques ne seraient pas des éléments primordiaux mais des constructions naturelles résultant de l'agencement des ingrédients plus fondamentaux<ref>Patricia Curd, 2007, essais 2-3, p. 157-191 ; Malcolm Schofield, 1980, p. 132-138.</ref>. Cette interprétation médiane présente l'avantage de tenir compte à la fois des fragments authentiques et des témoignages d'Aristote, sans les opposer frontalement. Elle reconnaît que les opposés jouent un rôle cosmologique crucial, puisque c'est leur séparation qui déclenche la formation du cosmos (fragments B12, B13, B15), tout en admettant que les substances naturelles ont également une place dans l'ontologie d'Anaxagore. === Les semences (σπέρματα) === Un élément supplémentaire de complexité est introduit par la mention des « semences » (σπέρματα) dans les fragments B4a et B4b. Dans le fragment B4b, après avoir mentionné les opposés, Anaxagore ajoute : « et il y avait beaucoup de terre présente, et des semences infinies en nombre, ne se ressemblant en rien les unes aux autres » (πολλὴ δὲ γῆ ἐνῆν καὶ σπέρματα ἄπειρα πλῆθος οὐδὲν ἐοικότα ἀλλήλοις)<ref>Anaxagore, fragment B4b, cité par Simplicius, ''Commentaire sur la Physique'', 34, 29-35, 9 (DK 59 B 4b).</ref>. Le fragment B4a est encore plus explicite : « Il est juste de penser qu'il y avait dans toutes les choses qui étaient rassemblées beaucoup de choses de toutes sortes, et des semences de toutes choses, possédant des formes et des couleurs et des saveurs de toute espèce »<ref>Anaxagore, fragment B4a, cité par Simplicius, ''Commentaire sur la Physique'', 34, 18-29 (DK 59 B 4a).</ref>. La nature exacte de ces semences a fait l'objet de débats considérables parmi les commentateurs. Gregory Vlastos a proposé que σπέρμα soit un terme technique introduit par Anaxagore pour désigner un agrégat infinitésimal contenant tous les ingrédients, mais dans lequel un seul prédomine<ref>Gregory Vlastos, 1950, p. 338-342 ; cf. également J. E. Raven, « The Basis of Anaxagoras' Cosmology », ''Classical Quarterly'', vol. 4, 1954, p. 123-137.</ref>. David Lloyd a suggéré que les semences soient des portions pures (ou quasi-pures) d'opposés<ref>David Lloyd, « Anaxagoras on Life and Mind », ''Phronesis'', vol. 14, 1969, p. 246-251, note 1.</ref>. Une position intermédiaire, défendue par Malcolm Schofield, David Sedley, Patricia Curd et Daniel Gershenson, suggère que les semences doivent être comprises littéralement comme des semences biologiques ordinaires, c'est-à-dire des graines de plantes et des semences animales<ref>Malcolm Schofield, 1980, p. 119-132 ; David Sedley, 2007, p. 24-26 ; Patricia Curd, 2007, essai 2 ; Daniel E. Gershenson et Daniel A. Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 15-17.</ref>. Cette dernière interprétation présente plusieurs avantages. Elle ne fait pas violence au langage : lorsqu'Anaxagore parle de σπέρματα, il utilise le terme ordinaire pour « semences » sans lui donner un sens technique inhabituel. Elle explique la diversité infinie des semences mentionnée dans B4b. Elle est en outre corroborée par des témoignages anciens sur la biologie d'Anaxagore : Théophraste rapporte qu'« Anaxagore dit que l'air contient des semences de toutes choses, et que celles-ci, lorsqu'elles sont emportées avec l'eau, engendrent les plantes »<ref>Théophraste, ''Causes des plantes'', I, 5, 2 (DK 59 A 117).</ref>. De même, Hippolyte rapporte qu'« au commencement, les animaux naquirent de l'humide, du chaud et du terreux, puis plus tard les uns des autres »<ref>Hippolyte, ''Réfutation de toutes les hérésies'', I, 8, 12 (DK 59 A 42).</ref>. == L'état originel : tout ensemble == Le traité d'Anaxagore s'ouvrait par l'une des déclarations les plus célèbres et les plus discutées de la philosophie présocratique : « Toutes choses étaient ensemble » (ὁμοῦ πάντα χρήματα ἦν, fragment B1)<ref>Anaxagore, fragment B1, cité par Simplicius, ''Commentaire sur la Physique'', 155, 23-26 (DK 59 B 1). Diogène Laërce, II, 6, rapporte qu'Anaxagore « commença son traité d'une manière très attrayante ».</ref>. Cette formule programmatique, placée en position d'ouverture, exprime la thèse cosmogonique fondamentale d'Anaxagore concernant l'état primordial de l'univers. Plusieurs commentateurs, notamment G. E. L. Owen, ont souligné que la formulation d'Anaxagore constitue une réponse directe et délibérée à Parménide<ref>G. E. L. Owen, « The Place of the Timaeus in Plato's Dialogues », ''Classical Quarterly'', vol. 3, 1953, p. 79-95 ; repris dans ''Logic, Science and Dialectic'', Londres, Duckworth, 1986, p. 65-84.</ref>. Là où Parménide avait affirmé que l'être « est maintenant, tout ensemble, un, continu » (νῦν ἔστιν ὁμοῦ πᾶν, ἕν, συνεχές)<ref>Parménide, fragment B8, 5-6 (DK 28 B 8).</ref>, Anaxagore proclame que « toutes choses étaient ensemble ». Parménide affirmait l'unicité, la continuité, l'éternité présente et l'immobilité de l'être ; Anaxagore lui oppose la pluralité, la diversibilité infinie, l'existence passée et le devenir cosmogonique<ref>Malcolm Schofield, 1980, p. 64-65 ; Patricia Curd, 1998, p. 123-142.</ref>. === La description du mélange originel === Le fragment B1, dont Simplicius nous dit qu'il se trouvait près du début du traité d'Anaxagore, fournit une description plus complète de cet état primordial : <blockquote>Toutes choses étaient ensemble, illimitées et en multitude et en petitesse, car le petit aussi était illimité. Et toutes choses étant ensemble, rien n'était manifeste en raison de la petitesse. Car l'air et l'éther recouvraient toutes choses, tous deux étant illimités ; car ce sont eux qui sont les plus grands dans la totalité des choses, et en multitude et en grandeur.<ref>Anaxagore, fragment B1, cité par Simplicius, ''Commentaire sur la Physique'', 155, 23-157, 4 (DK 59 B 1).</ref></blockquote> Ce passage dense pose plusieurs problèmes interprétatifs considérables qui ont occupé les commentateurs depuis l'Antiquité. Quatre caractéristiques principales du mélange originel y sont énoncées : toutes choses étaient ensemble ; elles étaient illimitées en multitude et en petitesse ; rien n'était manifeste en raison de la petitesse ; l'air et l'éther recouvraient toutes choses. L'expression « illimitées en multitude et en petitesse » a suscité d'intenses débats interprétatifs. Deux lectures principales s'affrontent, que Malcolm Schofield a désignées respectivement comme l'interprétation « particulaire » et l'interprétation « proportionnelle »<ref>Malcolm Schofield, 1980, p. 70-99.</ref>. L'interprétation particulaire comprend « illimitées en multitude » comme signifiant qu'il existait un nombre infini de choses distinctes dans le mélange originel<ref>Gregory Vlastos, 1950, p. 31-57 ; W. K. C. Guthrie, 1965, p. 277-285 ; David Sider, 2005, p. 56-62.</ref>. L'interprétation proportionnelle, en revanche, refuse de comprendre le mélange originel comme une collection de particules discrètes : « illimitées en petitesse » signifierait plutôt que chaque ingrédient était présent dans le mélange en une proportion infiniment petite par rapport à la totalité<ref>Malcolm Schofield, 1980, p. 75-89 ; Colin Strang, 1963, p. 101-118 ; Jonathan Barnes, 1982, p. 39-53.</ref>. Le débat entre ces deux interprétations n'est pas résolu, et chacune présente des avantages et des difficultés<ref>Pour une discussion équilibrée des deux positions, voir Patricia Curd, 2007, essai 4, p. 192-213 ; Daniel Graham, 2006, p. 137-152.</ref>. === L'imperceptibilité du mélange === La troisième caractéristique du mélange originel est son indistinction : « rien n'était manifeste » (οὐδὲν ἔνδηλον ἦν). Anaxagore explique cette imperceptibilité par deux facteurs complémentaires : « en raison de la petitesse » (ὑπὸ σμικρότητος), et parce que « l'air et l'éther recouvraient toutes choses » (πάντα γὰρ ἀὴρ καὶ αἰθὴρ κατεῖχε). L'air (ἀήρ) désigne ici, conformément à l'usage ionien archaïque, une substance sombre, humide, froide et dense<ref>Anaximène, fragments DK 13 A 7, B 2 ; cf. Charles Kahn, ''Anaximander and the Origins of Greek Cosmology'', New York, Columbia University Press, 1960, p. 133-162 ; Daniel Graham, 2006, p. 93-102.</ref>. L'éther (αἰθήρ), en revanche, désigne la substance lumineuse, chaude, sèche et rare<ref>Anaxagore, B15. Cf. W. K. C. Guthrie, 1965, p. 301-304 ; Malcolm Schofield, 1980, p. 81-83.</ref>. Ces deux substances, affirme Anaxagore, « sont les plus grandes dans la totalité des choses, et en multitude et en grandeur ». Cela signifie qu'elles étaient présentes dans le mélange originel en proportions largement supérieures à tous les autres ingrédients. Le fragment B4b confirme cette description : <blockquote>Mais avant que ces choses ne fussent séparées, lorsque toutes choses étaient ensemble, aucune couleur n'était manifeste. Car le mélange de toutes choses l'empêchait : celui du sec et de l'humide, du chaud et du froid, du lumineux et de l'obscur, ainsi que d'une grande quantité de terre présente et de semences illimitées en multitude, ne se ressemblant en rien les unes aux autres.<ref>Anaxagore, fragment B4b (DK 59 B 4b).</ref></blockquote> Ce passage confirme que l'indistinction du mélange originel n'était pas due à l'absence des ingrédients, mais à leur mélange si intime que leurs caractéristiques respectives se neutralisaient mutuellement. === L'immobilité originelle === Plusieurs témoignages anciens rapportent qu'avant l'intervention du Noûs, le mélange universel était au repos<ref>Aristote, ''Physique'', VIII, 1, 250b24-26 (DK 59 A 64).</ref>. Cette immobilité primordiale pose un problème philosophique considérable : si le mélange était éternellement au repos, qu'est-ce qui a pu le mettre en mouvement ? Anaxagore répond en postulant l'existence du Noûs (Νοῦς, Intellect ou Esprit), une entité distincte de toutes les substances matérielles, qui possède le pouvoir d'initier le mouvement<ref>Anaxagore, B12.</ref>. Mais cette réponse soulève elle-même de nouvelles difficultés, qui seront examinées plus loin. === L'étendue du mélange originel === Le fragment B1 affirme que l'air et l'éther « tous deux étaient illimités » (ἄμφω ἄπειρα ἐόντα), et le fragment B2 précise : « L'air et l'éther se séparent de la multitude environnante, et la multitude environnante est illimitée en quantité »<ref>Anaxagore, fragment B2 (DK 59 B 2).</ref>. Ces affirmations suggèrent que le mélange originel était spatialement infini<ref>Aristote, ''Physique'', III, 4, 203a19-23 (DK 59 A 43).</ref>. Le fragment B12 indique que « le Noûs commença à exercer son pouvoir à partir d'un petit commencement, puis la rotation s'étendit sur une région plus grande, et s'étendra sur une région plus grande encore »<ref>Anaxagore, B12 (DK 59 B 12).</ref>. Cela suggère que le processus cosmogonique, bien qu'il soit en cours depuis un temps considérable, n'a pas encore affecté la totalité du mélange infini. Cette conception d'un univers partiellement ordonné, où la cosmogonie est encore en cours dans les régions périphériques tandis que notre monde déjà structuré occupe une région centrale, est l'une des idées les plus originales d'Anaxagore<ref>W. K. C. Guthrie, 1965, p. 327-331 ; Patricia Curd, 2007, essai 5, p. 214-237 ; Daniel Graham, 2006, p. 148-152.</ref>. == Le Noûs : l'Intellect cosmique == Le fragment B12 d'Anaxagore, le plus long et le plus célèbre de tous les fragments conservés, est entièrement consacré au Noûs (Νοῦς, « Intellect » ou « Esprit »). Ce passage, d'une grandeur solennelle et d'une intensité remarquable, constitue l'un des textes les plus puissants de toute la prose grecque archaïque<ref>Malcolm Schofield, ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980, p. 3-32 ; Karl Deichgräber, « Anaxagoras Stil », ''Philologus'', vol. 87, 1933, p. 15-26.</ref>. Dans ce fragment, Anaxagore expose sa doctrine du Noûs comme principe du mouvement cosmogonique et source d'ordre dans l'univers. Le Noûs représente l'innovation philosophique la plus originale d'Anaxagore<ref>W. K. C. Guthrie, 1965, p. 272-279 ; Diogène Laërce, II, 6 (DK 59 A 1), rapporte qu'Anaxagore était surnommé « Monsieur Intellect » (ὁ Νοῦς) en raison de l'importance centrale qu'il donnait à cette notion.</ref>. Il convient d'emblée de préciser, afin d'éviter un contresens répandu dans la littérature secondaire ancienne comme moderne, que le Noûs d'Anaxagore est d'abord une ''cause motrice'' du cosmos : il initie et contrôle le mouvement rotatoire qui produit la séparation des ingrédients à partir du mélange originel. Anaxagore ne dit nulle part, dans les fragments conservés, que l'Intellect ordonne les choses ''parce qu'il serait meilleur qu'elles soient ainsi''. Or, c'est précisément cette affirmation que Socrate et Platon auraient voulu trouver chez le philosophe de Clazomènes, et dont ils lui reprocheront ensuite l'absence dans le passage célèbre du ''Phédon'' (97b-98c). La lecture proprement « téléologique » du Noûs, qui en fait une cause finale organisant le monde selon la Raison du Bien, est donc une reconstruction rétrospective, due à Platon puis à Aristote, et répercutée par toute une tradition doxographique postérieure. Elle projette sur Anaxagore les exigences explicatives du platonisme et de l'aristotélisme, exigences qu'Anaxagore ne partage pas sous cette forme. Plus fondamentalement encore, appliquer sans précaution aux présocratiques la distinction aristotélicienne des quatre causes (matérielle, formelle, efficiente, finale) relève d'un anachronisme conceptuel. Cette distinction est une construction d'Aristote, élaborée dans la ''Physique'' et la ''Métaphysique'' pour penser son propre rapport critique à ses prédécesseurs. La plupart des penseurs antérieurs à Aristote, Anaxagore compris, ne disposent pas d'un tel vocabulaire et n'organisent pas leur réflexion autour de cette quadripartition. Le Noûs d'Anaxagore ne peut être intégralement rangé dans aucune des quatre catégories aristotéliciennes : il est quelque chose comme une cause motrice doublée d'un principe ordonnateur, sans que cet ordre implique nécessairement une référence à un bien objectif. Il est donc plus juste de parler, comme le font les spécialistes contemporains, d'une fonction kinétique et cognitive du Noûs, plutôt que d'y voir la première formulation d'une téléologie rigoureuse<ref>Sur ce point, voir Patricia Curd, « Anaxagoras », ''Stanford Encyclopedia of Philosophy'', 2007 (révision substantielle 2019), section sur le Noûs ; Malcolm Schofield, 1980, p. 55-70 ; James Lesher, « Mind's Knowledge and Powers of Control in Anaxagoras », ''Phronesis'', vol. 40, 1995, p. 125-142. Sur l'anachronisme que représente l'application des quatre causes aristotéliciennes aux physiciens présocratiques, voir l'article « Presocratic Philosophy », ''Stanford Encyclopedia of Philosophy'', éd. Daniel Graham, 2019. Cf. également Christian Vassallo, « Nous, Motion, and Teleology in Anaxagoras », ''Oxford Studies in Ancient Philosophy'', vol. 50, 2016, p. 1-32, qui défend une lecture téléologique minimale et nuancée, restée minoritaire.</ref>. === La séparation du Noûs d'avec toutes choses === Le fragment B12 s'ouvre par une affirmation qui constitue la thèse fondamentale d'Anaxagore concernant le Noûs : <blockquote>Les autres choses ont une part de tout, mais le Noûs est illimité et autonome, et il n'a été mélangé à aucune chose, mais il existe seul, lui-même par lui-même.<ref>Anaxagore, fragment B12, cité par Simplicius, ''Commentaire sur la Physique'', 156, 13-15 (DK 59 B 12).</ref></blockquote> Cette phrase affirme que le Noûs constitue une exception au principe universel « tout est dans tout ». Tandis que toutes les substances matérielles contiennent en elles-mêmes des portions de toutes les autres substances, le Noûs, lui, demeure entièrement pur et séparé<ref>Anaxagore, B11 : « Dans toute chose il y a une part de toute chose, excepté le Noûs ; et il y a certaines choses dans lesquelles il y a aussi du Noûs ».</ref>. Trois attributs sont explicitement prédiqués du Noûs : il est « illimité » (ἄπειρον), « autonome » (αὐτοκρατές), et « non mélangé à aucune chose ». Anaxagore justifie cette séparation par un argument remarquable : <blockquote>Car s'il n'existait pas par lui-même, mais s'il avait été mélangé à quelque autre chose, il participerait de toutes les choses, s'il avait été mélangé à quoi que ce soit. Car dans toute chose il y a une part de toute chose, comme je l'ai dit précédemment. Et les choses mélangées avec lui l'empêcheraient, de sorte qu'il ne dominerait aucune chose de la même manière qu'il la domine en fait, étant seul par lui-même.<ref>Anaxagore, B12, lignes 156, 15-20.</ref></blockquote> L'argument procède ainsi : si le Noûs était mélangé à quoi que ce soit, il contiendrait une part de tout ; les substances mélangées l'empêcheraient d'exercer son pouvoir de contrôle ; or le Noûs exerce effectivement ce pouvoir ; donc le Noûs n'est mélangé à aucune chose<ref>Malcolm Schofield, 1980, p. 12-14 ; Jonathan Barnes, 1982, p. 375-377.</ref>. Plusieurs lectures de la prémisse cruciale, c'est-à-dire de la raison pour laquelle le mélange empêcherait l'action du Noûs, ont été proposées et ne sont pas incompatibles entre elles<ref>Patricia Curd, 2007, essai 5, p. 220-225 ; Christian Vassallo, « Nous, Motion, and Teleology in Anaxagoras », ''Oxford Studies in Ancient Philosophy'', vol. 50, 2016, p. 1-32, aux pages 8-18.</ref>. === Les attributs du Noûs === Après avoir établi la séparation du Noûs, Anaxagore énonce une série d'attributs qui caractérisent sa nature. Le style employé ici est celui de la « prédication solennelle » (feierliche Prädikation), identifié par Karl Deichgräber comme caractéristique du style hymnico-religieux archaïque<ref>Karl Deichgräber, 1933, p. 16-22 ; Eduard Norden, ''Agnostos Theos'', Leipzig, Teubner, 1913, p. 3-29 ; Malcolm Schofield, 1980, p. 4-9.</ref> : <blockquote>Car il est le plus fin de toutes choses et le plus pur, et il possède toute connaissance à l'égard de toute chose et il a la plus grande force ; et toutes les choses qui ont une âme, les plus grandes comme les plus petites, toutes le Noûs les domine.<ref>Anaxagore, B12, lignes 156, 20-24.</ref></blockquote> Le premier attribut, qui qualifie le Noûs de « plus fin » et « plus pur » de toutes choses, a suscité des interprétations divergentes depuis l'Antiquité. Certains y voient l'affirmation, encore en gestation conceptuelle, de l'immatérialité du Noûs<ref>Aristote, ''De l'âme'', III, 4, 429a18-24 ; W. K. C. Guthrie, 1965, p. 278-279.</ref> ; d'autres, en s'appuyant sur le fait qu'Anaxagore utilise l'adjectif λεπτός ailleurs pour décrire l'eau de mer, soutiennent que ces termes doivent être pris en un sens physique : le Noûs serait alors une substance matérielle d'une extrême finesse et d'une grande pureté, proche des réalités les plus ténues du monde sensible<ref>Théophraste, dans Simplicius, ''Commentaire sur la Physique'', 27, 2-3 (DK 59 A 41) ; John Burnet, 1930, p. 268-269 ; Daniel Gershenson et Daniel Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 27-33.</ref>. Une position médiane suggère qu'Anaxagore n'avait pas encore développé le concept d'incorporéité dans sa pleine rigueur conceptuelle, qui suppose une distinction nette du matériel et de l'immatériel élaborée plus tard par Platon, mais qu'il cherchait néanmoins à exprimer, avec les ressources linguistiques dont il disposait, l'idée que le Noûs est d'une nature fondamentalement différente de toutes les autres substances<ref>Malcolm Schofield, 1980, p. 18-22.</ref>. Le deuxième attribut, qui concerne la connaissance, affirme que le Noûs « possède toute connaissance à l'égard de toute chose » : il s'agit ici d'une forme d'omniscience cosmique. Le troisième, qui concerne la puissance, précise que le Noûs « a la plus grande force », cette force étant liée à sa connaissance selon un schéma que l'on retrouvera chez Aristote et qui fait de la puissance l'envers opératif du savoir<ref>Malcolm Schofield, 1980, p. 22-24.</ref>. Le quatrième attribut, qui concerne le contrôle exercé par le Noûs sur tous les êtres vivants, est cohérent avec le fragment B11, selon lequel il y a du Noûs dans certaines choses, c'est-à-dire dans les êtres animés<ref>Anaxagore, B11. Cf. Malcolm Schofield, 1980, p. 25-27 ; Patricia Curd, 2007, essai 5, p. 225-230.</ref>. === Le rôle cosmogonique du Noûs === Après avoir décrit la nature du Noûs, Anaxagore expose son rôle dans la cosmogonie : <blockquote>Et le Noûs a dominé la révolution entière, de sorte qu'elle a commencé à tourner au commencement. Et d'abord elle a commencé à tourner à partir d'une petite région, mais elle tourne sur une région plus grande, et elle tournera sur une région plus grande encore. Et toutes les choses qui étaient mélangées ensemble et séparées et distinguées, toutes le Noûs les a connues. Et toutes les choses qui devaient être, celles qui étaient, celles qui sont maintenant et celles qui seront, toutes le Noûs les a mises en ordre, et aussi cette révolution dans laquelle tournent maintenant les astres et le soleil et la lune et l'air et l'éther qui sont en train d'être séparés.<ref>Anaxagore, B12, lignes 156, 24-157, 3.</ref></blockquote> Ce passage affirme trois choses concernant l'activité cosmogonique du Noûs. D'abord, l'Intellect initie un mouvement de révolution (περιχώρησις) dans le mélange originel, jusqu'alors immobile. Ensuite, ce mouvement, commencé dans une petite région, s'est étendu progressivement et continue de s'étendre, ce qui implique que la cosmogonie n'est pas un événement ponctuel passé, mais un processus encore en cours dans les régions périphériques du mélange infini. Enfin, c'est ce mouvement qui a produit la séparation (ἀπόκρισις) et la distinction (διάκρισις) des ingrédients, donnant naissance au cosmos ordonné que nous observons<ref>Simplicius, ''Commentaire sur la Physique'', 300, 31-301, 1 ; Aristote, ''Physique'', VIII, 1, 250b24-252a5 (DK 59 A 64).</ref>. Le mécanisme cosmogonique proprement dit est d'ordre mécanique : c'est le mouvement rotatoire qui, par sa force centrifuge, sépare les substances denses des substances rares<ref>Anaxagore, B12, B13, B15, B16.</ref>. Le Noûs n'intervient donc pas directement dans chaque détail de la cosmogonie : il initie le mouvement rotatoire, et celui-ci produit ensuite mécaniquement la séparation et la réorganisation des substances. Cette répartition des rôles entre un principe moteur intelligent et un processus mécanique explique pourquoi Platon et Aristote pourront reprocher à Anaxagore de « laisser faire » la matière après avoir introduit l'Intellect : à leurs yeux, introduire une cause intelligente sans lui confier l'organisation détaillée du monde revient à lui refuser son rôle véritable<ref>Malcolm Schofield, 1980, p. 27-32 ; Daniel Graham, 2006, p. 148-152.</ref>. Le texte affirme cependant aussi que « le Noûs a mis en ordre » (διεκόσμησε νοῦς) toutes choses, passées, présentes et futures. Le verbe διακοσμεῖν signifie « mettre en ordre » ou « arranger ». Il ne faut pas en conclure trop vite à une téléologie au sens fort, comme si les choses étaient disposées en vue du bien : Anaxagore ne fournit pas, dans les fragments qui nous sont parvenus, un tel principe évaluatif. Le Noûs connaît et dispose, il est à la fois intelligent et ordonnateur, mais rien dans les textes n'indique qu'il ordonne les choses ''parce qu'elles seraient ainsi meilleures''. C'est précisément ce point qui sera au cœur de la critique platonicienne dans le ''Phédon'' et qu'il faut examiner avec soin<ref>Christian Vassallo, 2016, a défendu l'idée qu'il y aurait malgré tout une dimension téléologique faible chez Anaxagore ; cette lecture reste minoritaire et contestée. Voir au contraire la synthèse de Patricia Curd, ''SEP'' 2007 (révision substantielle 2019).</ref>. === La critique platonicienne et aristotélicienne === Dans le ''Phédon'', Platon fait raconter par Socrate sa déception à la lecture du livre d'Anaxagore : <blockquote>Un jour, j'entendis quelqu'un lire dans un livre d'Anaxagore, disant que c'est l'Intellect qui met tout en ordre et qui est la cause de toutes choses. Je fus ravi de cette cause, et il me sembla qu'il était en quelque sorte bon que l'Intellect fût la cause de tout ; et je pensai que, s'il en est ainsi, l'Intellect qui met tout en ordre doit tout ordonner et disposer chaque chose de la manière qui est la meilleure. [...] Mais cette merveilleuse espérance, mon ami, me fut enlevée lorsque, progressant dans ma lecture, je vis que cet homme ne fait aucun usage de l'Intellect, qu'il ne lui attribue aucune responsabilité dans la mise en ordre des choses, mais qu'il allègue comme causes l'air, l'éther, l'eau et beaucoup d'autres choses absurdes.<ref>Platon, ''Phédon'', 97b-98c (DK 59 A 47).</ref></blockquote> Ce passage est capital, et il faut en saisir exactement la portée pour éviter le contresens le plus tenace de la tradition exégétique. Socrate ne dit pas qu'Anaxagore avait effectivement proposé une explication par le « meilleur » et qu'il y aurait manqué dans le détail : il dit qu'il ''espérait'' trouver une telle explication, et qu'il fut déçu de ne pas la trouver. La formule « doit tout ordonner et disposer chaque chose de la manière qui est la meilleure » exprime donc l'attente socratique, c'est-à-dire ce que Socrate aurait voulu lire, non la doctrine effective d'Anaxagore. La nuance est décisive. Le texte d'Anaxagore, dans les fragments que nous possédons, affirme que le Noûs connaît toutes choses et qu'il les met en ordre par l'intermédiaire du tourbillon cosmogonique. Il n'affirme pas que cet ordre soit le meilleur possible, ni que le Noûs vise le bien en produisant cet ordre. C'est Socrate qui, lisant Anaxagore, projette ses propres attentes philosophiques : si le monde est ordonné par un Intellect, pense Socrate, alors il doit l'être en vue du bien, puisqu'un Intellect rationnel ne peut que vouloir le meilleur. Cette inférence paraît évidente à Socrate, mais elle n'est pas anaxagoréenne. La lecture qui fait du Noûs d'Anaxagore une « cause téléologique » ou une « cause finale » est, par conséquent, une reconstruction rétrospective qui projette sur le Clazoménien les exigences platoniciennes (le Bien comme cause) puis aristotéliciennes (la cause finale comme l'une des quatre causes)<ref>Malcolm Schofield, 1980, p. 55-70 ; James Lesher, 1995, p. 125-142 ; Patricia Curd, ''SEP'', 2007 (révision substantielle 2019).</ref>. Il faut ici distinguer deux choses qui sont souvent confondues : d'une part, l'attribution au Noûs d'un rôle moteur et cognitif, qui est effectivement anaxagoréenne ; d'autre part, l'attribution au Noûs d'un principe évaluatif selon lequel le monde serait disposé en vue du meilleur, qui ne l'est pas. Le reproche socratique ne consiste donc pas à accuser Anaxagore d'incohérence interne (avoir posé une téléologie puis l'avoir abandonnée), mais à regretter qu'il n'ait pas poussé sa pensée jusqu'à la téléologie que Socrate lui aurait souhaitée. La différence est subtile mais philosophiquement essentielle : elle marque le lieu précis où la pensée présocratique cède la place à la pensée classique. Aristote reprend et durcit la critique dans la ''Métaphysique'' : <blockquote>Anaxagore utilise l'Intellect comme un ''deus ex machina'' pour la fabrication du monde ; et quand il est embarrassé pour expliquer pourquoi quelque chose est nécessairement ainsi, il le fait intervenir. Mais dans tous les autres cas, il allègue comme causes toutes sortes de choses plutôt que l'Intellect.<ref>Aristote, ''Métaphysique'', I, 4, 985a18-21 (DK 59 A 47).</ref></blockquote> Le reproche d'Aristote, comme celui de Socrate-Platon, présuppose un idéal explicatif téléologique qui n'est pas celui d'Anaxagore. Il ne faut donc pas conclure qu'Anaxagore « introduit le Noûs comme cause téléologique puis échoue à l'utiliser » : il introduit le Noûs comme cause motrice et ordonnatrice, et ses critiques l'accusent de ne pas avoir ''aussi'' développé une explication par le meilleur qu'eux attendent. Cette distinction entre la doctrine effective d'Anaxagore et la doctrine que ses successeurs auraient voulu lire chez lui commande toute l'interprétation correcte de son héritage : l'histoire de la téléologie philosophique ne commence pas, en toute rigueur, avec Anaxagore, mais avec la déception philosophique de Socrate face à Anaxagore, puis avec la construction proprement platonicienne du Démiurge et aristotélicienne du Premier Moteur. === Synthèse : l'innovation du Noûs === L'introduction du Noûs par Anaxagore constitue une innovation philosophique à plusieurs titres. Premièrement, Anaxagore est, parmi les penseurs grecs dont nous avons conservé les doctrines, le premier à poser explicitement l'existence d'une entité qui, bien qu'elle agisse sur la matière, n'est mélangée à aucune substance matérielle<ref>W. K. C. Guthrie, 1965, p. 278-279 ; Daniel Gershenson et Daniel Greenberg, 1964, p. 27-33.</ref>. Cette conception préparera, sans y être pour autant équivalente, les doctrines ultérieures de l'âme immatérielle chez Platon et de l'intellect séparé chez Aristote<ref>Platon, ''Phédon'', 78b-84b ; Aristote, ''De l'âme'', III, 5, 430a10-25. Cf. Edward Hussey, ''The Presocratics'', Londres, Duckworth, 1972, p. 138-141.</ref>. Deuxièmement, Anaxagore identifie une cause unique pour le mouvement cosmique et l'ordre qui en résulte<ref>Aristote, ''Métaphysique'', I, 3, 984b15-20, qualifie cette innovation de « sobre ».</ref>. En désignant le principe cosmique par le terme Νοῦς, mot habituellement associé à l'intelligence et à la pensée, Anaxagore suggère que l'ordre du monde n'est pas aveugle : il y a dans le cosmos quelque chose d'intelligent qui connaît et qui dispose. C'est cette suggestion qui sera reprise et radicalisée par Socrate, Platon et Aristote, au prix d'une transformation dont il faut reconnaître qu'elle va au-delà de ce qu'affirme Anaxagore lui-même<ref>Malcolm Schofield, 1980, p. 3-32 ; W. K. C. Guthrie, 1965, p. 327-331.</ref>. == La cosmogonie et la cosmologie == La cosmogonie et la cosmologie d'Anaxagore constituent l'application de ses principes métaphysiques. Dans ces domaines, Anaxagore se montre à la fois héritier de la tradition ionienne et novateur, proposant des explications naturalistes des phénomènes célestes et météorologiques<ref>W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 301-331 ; Daniel Gershenson et Daniel Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 34-55.</ref>. === Le mouvement rotatoire cosmogonique === Le processus cosmogonique commence lorsque le Noûs initie un mouvement de rotation (περιχώρησις) dans le mélange originel<ref>Anaxagore, fragment B12 (DK 59 B 12).</ref>. Ce mouvement rotatoire ne doit pas être conçu comme une simple rotation uniforme de l'ensemble du mélange, mais comme un tourbillon (δῖνος) ou un vortex dont la vitesse et l'étendue augmentent progressivement<ref>Aristote, ''Du Ciel'', II, 13, 295a9-14 (DK 59 A 88).</ref>. Anaxagore affirme que ce mouvement initial était d'une rapidité extraordinaire : « Rien de ce qui existe maintenant chez les hommes n'est aussi rapide, mais [il était] certainement plusieurs fois plus rapide »<ref>Anaxagore, fragment B9 (DK 59 B 9).</ref>. Le mécanisme de la séparation cosmogonique est mécanique. Le mouvement rotatoire produit une force centrifuge qui pousse les substances rares vers la périphérie et attire les substances denses vers le centre<ref>Anaxagore, B12, B15 ; Aristote, ''Du Ciel'', III, 2, 300b1-8 (DK 59 A 88).</ref>. Le fragment B15 décrit ce processus : <blockquote>Le dense et l'humide et le froid et l'obscur se rassemblèrent ici, là où maintenant est la terre, tandis que le rare et le chaud et le sec se retirèrent vers les régions lointaines de l'éther.<ref>Anaxagore, fragment B15 (DK 59 B 15).</ref></blockquote> Ce processus de séparation n'est jamais achevé. Conformément au principe que « rien n'est complètement séparé » (fragment B8), la rotation continue indéfiniment à produire des séparations et des mélanges partiels<ref>Anaxagore, B8, B12.</ref>. === La formation de la terre === Au centre du tourbillon cosmique, les substances denses, humides, froides et obscures se sont concentrées pour former la terre<ref>Anaxagore, B15 ; Aristote, ''Du Ciel'', III, 2, 300b8-16 (DK 59 A 88).</ref>. Selon Anaxagore, la terre a la forme d'un disque plat<ref>Hippolyte, ''Réfutation de toutes les hérésies'', I, 8, 3 (DK 59 A 42) ; Aristote, ''Du Ciel'', II, 13, 294b13-15 (DK 59 A 88).</ref>, conception traditionnelle dans la cosmologie ionienne<ref>Charles Kahn, ''Anaximander and the Origins of Greek Cosmology'', New York, Columbia University Press, 1960, p. 76-116 ; Daniel Graham, 2006, p. 93-102.</ref>. Anaxagore répond à la question de la stabilité de la terre en affirmant qu'elle demeure immobile parce qu'elle repose sur l'air (ἀήρ) qui la supporte<ref>Aristote, ''Du Ciel'', II, 13, 294b13-21 (DK 59 A 88) : « Anaxagore dit que la terre demeure immobile en raison de son égalité et de la grandeur, car elle ne coupe pas l'air mais le couvre comme un couvercle ».</ref>. Anaxagore aurait même effectué des démonstrations expérimentales avec des clepsydres pour montrer que l'air possède une résistance élastique capable de supporter des corps<ref>Aristote, ''Physique'', IV, 6, 213a22-27 (DK 59 A 68) ; Daniel Gershenson et Daniel Greenberg, 1964, p. 40-43.</ref>. === La formation des corps célestes === Les corps célestes, soleil, lune et étoiles, se sont formés à partir des substances rares, chaudes et sèches projetées vers la périphérie par la force du tourbillon cosmique<ref>Anaxagore, B15 ; Hippolyte, DK 59 A 42.</ref>. Selon cette théorie, les astres ne sont pas des êtres divins mais des masses de pierre ou de métal incandescent, rougies par leur mouvement rapide à travers l'éther<ref>Platon, ''Apologie de Socrate'', 26d (DK 59 A 35) ; Hippolyte, DK 59 A 42. Cette thèse est l'une de celles qui fonderont, selon Plutarque, les accusations d'impiété contre Anaxagore.</ref>. Anaxagore soutenait que le soleil est une masse de pierre ou de métal incandescent<ref>Platon, ''Apologie'', 26d (DK 59 A 35) ; Hippolyte, DK 59 A 42 ; Diogène Laërce, II, 8 (DK 59 A 1) ; Aétius, II, 20, 6 (DK 59 A 72).</ref>. Quant à sa taille, Anaxagore estimait qu'« il est plus grand que le Péloponnèse »<ref>Aétius, II, 21, 3 (DK 59 A 72) ; Plutarque, ''Vie de Périclès'', 6, 2 (DK 59 A 1).</ref>, affirmation qui devait paraître extravagante aux Grecs de son époque, habitués à voir dans le soleil un astre de dimensions modestes. Anaxagore sous-estimait bien sûr très considérablement la taille réelle du soleil, mais l'audace consistait à oser le comparer à une portion de la terre grecque plutôt qu'à un simple disque lumineux. La lune, selon Anaxagore, est elle aussi un corps rocheux, semblable à la terre<ref>Platon, ''Apologie'', 26d ; Hippolyte, DK 59 A 42.</ref>. Contrairement au soleil, la lune ne produit pas sa propre lumière : elle brille par réflexion de la lumière solaire<ref>Plutarque, ''Contre Colotès'', 1116a-b (DK 59 A 77) ; Hippolyte, DK 59 A 42. L'attribution exclusive de cette découverte à Anaxagore doit être nuancée : certains témoignages anciens la rapportent également à Parménide (DK 28 B 14-15), et la question de la priorité reste discutée. Voir Daniel Graham, ''Science before Socrates'', 2006, p. 115-116.</ref>. Cette thèse permit à Anaxagore, qu'il en soit ou non le premier découvreur, d'expliquer correctement les phases lunaires et la mécanique des éclipses<ref>Hippolyte, DK 59 A 42 ; Aétius, II, 29, 6 (DK 59 A 77).</ref>. Une éclipse de lune se produit lorsque la terre s'interpose entre le soleil et la lune, projetant son ombre sur celle-ci<ref>Hippolyte, DK 59 A 42. Cf. Dirk Couprie, « Anaxagoras on the Milky Way and Lunar Eclipses », ''Rhizomata'', vol. 5, 2017, p. 127-147.</ref>. Une éclipse de soleil se produit inversement lorsque la lune s'interpose entre le soleil et la terre<ref>Hippolyte, DK 59 A 42 ; Plutarque, ''Vie de Périclès'', 35, 2 (DK 59 A 18).</ref>. Ces explications, fondées sur une compréhension correcte de la géométrie des positions relatives du soleil, de la terre et de la lune, représentent une avancée considérable dans l'histoire de l'astronomie grecque<ref>W. K. C. Guthrie, 1965, p. 313-316 ; Daniel Graham, 2006, p. 149-150.</ref>. Selon la tradition doxographique, Anaxagore aurait même prédit une éclipse solaire<ref>Plutarque, ''Vie de Périclès'', 35, 2 (DK 59 A 18). La tradition évoque plusieurs éclipses possibles au V{{e}} siècle av. J.-C., sans que l'on puisse identifier avec certitude celle dont il s'agit. Les historiens modernes sont pour la plupart sceptiques quant à la possibilité qu'Anaxagore ait pu effectuer une prédiction précise, faute d'un modèle astronomique suffisamment développé. Cf. W. K. C. Guthrie, 1965, p. 314, note 1 ; Daniel Graham, 2006, p. 150, note 25.</ref>. Anaxagore soutenait enfin que la surface de la lune présente des irrégularités analogues à celles de la terre, montagnes et vallées, intuition que les observations télescopiques confirmeront deux millénaires plus tard<ref>Aétius, II, 30, 2 (DK 59 A 77).</ref>. La Voie lactée s'explique selon Anaxagore par l'ombre que projette la terre dans l'espace : dans les régions du ciel situées dans l'ombre de la terre, le soleil ne peut éclairer les étoiles ; celles-ci deviennent donc toutes visibles, même les plus faibles<ref>Aristote, ''Météorologiques'', I, 8, 345a25-31 (DK 59 A 80). Cf. Dirk Couprie, 2017.</ref>. === Le météorite d'Aigos Potamos === L'événement qui contribua à établir la réputation d'Anaxagore fut la chute d'un météorite de grande taille à Aigos Potamos, sur la rive européenne de l'Hellespont, vers 467 avant notre ère<ref>Pline l'Ancien, ''Histoire naturelle'', II, 149 (DK 59 A 11) ; Plutarque, ''Vie de Lysandre'', 12 (DK 59 A 12) ; Diogène Laërce, II, 10 (DK 59 A 1).</ref>. Selon les témoignages anciens, Anaxagore aurait prédit cette chute. Les historiens modernes sont divisés sur cette question : certains pensent qu'il s'agit d'une légende élaborée après coup, d'autres estiment qu'Anaxagore avait peut-être observé qu'un objet céleste se fragmentait<ref>Daniel W. Graham et Eric Hintz, « Anaxagoras and the Comet », ''Apeiron'', vol. 40, 2007, p. 1-20 ; Evangelos Th. Theodossiou et al., « The Fall of a Meteorite at Aegos Potami in 467/6 BC », ''Journal of Astronomical History and Heritage'', vol. 5, 2002, p. 135-140.</ref>. Quoi qu'il en soit, la chute du météorite fut associée au nom d'Anaxagore et parut confirmer sa théorie selon laquelle les corps célestes sont faits de pierre. === Météorologie === Anaxagore consacra une partie considérable de son traité à l'explication des phénomènes météorologiques. Les nuages se forment par évaporation de l'eau sous l'effet de la chaleur solaire<ref>Aétius, III, 4, 1 (DK 59 A 82).</ref>. La pluie provient de la condensation de la vapeur d'eau contenue dans les nuages<ref>Anaxagore, fragment B16 (DK 59 B 16).</ref>. La formation de la grêle posait un problème particulier : comment de la glace peut-elle se former en été ? Anaxagore proposa une explication ingénieuse : lors des journées très chaudes, des courants d'air ascendants peuvent pousser les nuages à des altitudes très élevées, où l'air est suffisamment froid pour que l'eau gèle<ref>Aétius, III, 4, 1 (DK 59 A 84) ; Aristote, ''Météorologiques'', I, 12, 348b23-349a11 (critique de la théorie d'Anaxagore).</ref>. Cette théorie, bien que partiellement erronée dans ses détails, témoigne d'une compréhension correcte du principe de convection. Le tonnerre et l'éclair sont causés, selon Anaxagore, par la chute de l'éther dans les nuages<ref>Aétius, III, 3, 3 (DK 59 A 84) ; Hippolyte, DK 59 A 42.</ref>. Cette explication reconnaît correctement que l'éclair précède le tonnerre, la vue étant plus rapide que l'ouïe<ref>Sénèque, ''Questions naturelles'', II, 22 (citant Anaxagore).</ref>. Les tremblements de terre sont causés, selon Anaxagore, par de l'éther chaud piégé sous la surface de la terre<ref>Aristote, ''Météorologiques'', II, 7, 365a19-21 (DK 59 A 89).</ref>. === Hydrologie et géologie === Anaxagore proposa une explication de la crue annuelle du Nil : les crues estivales sont causées par la fonte des neiges dans les régions montagneuses situées à la source du fleuve<ref>Diodore de Sicile, I, 38, 4 (attribution explicite à Anaxagore) ; Daniel Gershenson et Daniel Greenberg, 1964, p. 54.</ref>. Cette explication est essentiellement correcte. La mer, selon Anaxagore, existait dès le début, mais sa salinité actuelle provient de l'évaporation de l'eau douce sous l'effet du soleil<ref>Hippolyte, DK 59 A 42.</ref>. === Synthèse : la cosmologie naturaliste d'Anaxagore === La cosmologie d'Anaxagore se caractérise par trois traits fondamentaux. Premièrement, l'unité de la nature : Anaxagore affirme que les corps célestes sont constitués des mêmes substances que la terre et obéissent aux mêmes lois physiques<ref>Hippolyte, DK 59 A 42 ; Daniel Gershenson et Daniel Greenberg, 1964, p. 23-26, 47-48.</ref>. Il n'existe pas de différence ontologique entre le monde sublunaire et le monde supralunaire, contrairement à ce qu'affirmera plus tard Aristote<ref>Aristote, ''Du Ciel'', I, 2-3, 268b11-270b25.</ref>. Deuxièmement, l'explication mécanique : tous les phénomènes cosmologiques et météorologiques sont expliqués par des processus physiques (rotation, séparation par densité, évaporation, condensation), sans recours à des agents divins<ref>Daniel Gershenson et Daniel Greenberg, 1964, p. 23-33.</ref>. Troisièmement, l'usage systématique de l'analogie : Anaxagore explique les phénomènes cosmiques par analogie avec des phénomènes terrestres observables<ref>Daniel Gershenson et Daniel Greenberg, 1964, p. 7-12.</ref>. Ces traits donnent à la cosmologie d'Anaxagore un caractère naturaliste marqué, qui représente une étape importante dans le développement d'une explication physique de la nature. Il convient toutefois de ne pas surestimer sa portée : parler d'Anaxagore comme d'un « fondateur de la méthode scientifique » (selon une expression qu'on trouve parfois dans la littérature) relève d'une certaine emphase rétrospective, qu'il vaut mieux tempérer en parlant plutôt d'un jalon dans une longue histoire où se combinent intuitions fécondes et erreurs caractéristiques de l'époque. == La physiologie et la biologie == Bien qu'Anaxagore soit surtout connu pour ses théories cosmologiques et météorologiques, les témoignages anciens indiquent qu'il consacra aussi une partie considérable de son traité à l'explication des phénomènes biologiques et physiologiques<ref>Daniel Gershenson et Daniel Greenberg, ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964, p. 55-57 ; W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 316-320.</ref>. === La théorie de la nutrition === L'une des questions biologiques les plus importantes qu'Anaxagore chercha à élucider est celle de la nutrition : comment l'alimentation se transforme-t-elle en chair, en os, en cheveux et en toutes les autres parties du corps ? Selon les témoignages d'Aristote et de la tradition doxographique, Anaxagore partait de l'observation que les êtres humains et les animaux se nourrissent d'aliments relativement simples (pain, eau) et que de ces aliments proviennent pourtant toutes les parties complexes et diversifiées de leur corps<ref>Simplicius, ''Commentaire sur la Physique'', 27, 2-11 (DK 59 A 45) : « Car comment, disait-il, des cheveux pourraient-ils provenir de ce qui n'est pas cheveu, et de la chair de ce qui n'est pas chair ? ». Cf. Aétius, I, 3, 5 (DK 59 A 46) ; Lucrèce, ''De la nature'', I, 834-838.</ref>. Cette observation posait un problème philosophique considérable au regard de l'interdit parménidien : il semble en effet qu'une substance nouvelle (la chair) naisse à partir d'une substance qui n'était pas chair (le pain), ce qui contredit le principe « rien ne naît de ce qui n'est pas ». La réponse d'Anaxagore était cohérente avec sa métaphysique générale : le pain doit déjà contenir de la chair, du sang, des os, des cheveux, et toutes les autres substances corporelles, bien que ces constituants y soient présents en quantités si infimes qu'ils demeurent imperceptibles<ref>Simplicius, ''Commentaire sur la Physique'', 460, 4-12 (DK 59 A 45).</ref>. Lorsque nous mangeons du pain, le corps extrait du pain les particules de chair qu'il contient déjà, et les ajoute à la chair existante<ref>Aristote, ''Génération des animaux'', I, 18, 723a6-11 (DK 59 A 45).</ref>. Cette théorie soulève évidemment d'autres difficultés : comment le corps sait-il extraire précisément les particules de chair du pain, et les diriger vers les muscles plutôt que vers les os ? Anaxagore attribuait cette fonction au Noûs présent dans chaque organisme vivant<ref>Cf. section « Le Noûs : l'Intellect cosmique ». Daniel Gershenson et Daniel Greenberg, 1964, p. 27-33, 55-56.</ref>. === La théorie de la perception sensorielle === Anaxagore élabora aussi une théorie originale de la perception sensorielle, fondée sur le principe que « le semblable n'est pas affecté par le semblable, mais les contraires sont affectés les uns par les autres »<ref>Théophraste, ''De Sensibus'', 27 (DK 59 A 92) ; Aristote, ''De l'âme'', III, 3, 427a21-26 (DK 59 A 94).</ref>. Ce principe constituait une réponse directe aux théories de ses prédécesseurs, notamment Empédocle, qui soutenait que la perception se produit par similitude<ref>Théophraste, ''De Sensibus'', 1-2 (DK 31 A 86).</ref>. Selon Anaxagore, pour qu'une perception ait lieu, il doit exister une différence entre l'organe sensoriel et l'objet perçu<ref>Théophraste, ''De Sensibus'', 27-28 (DK 59 A 92).</ref>. Nous ne sentons pas la température de l'air lorsqu'elle est exactement égale à celle de notre peau ; c'est seulement lorsqu'il existe une différence que nous percevons le chaud ou le froid<ref>Théophraste, ''De Sensibus'', 29 (DK 59 A 92).</ref>. Une conséquence remarquable de cette théorie est qu'Anaxagore considérait que toute perception s'accompagne nécessairement de douleur ou d'un certain désagrément (λύπη)<ref>Théophraste, ''De Sensibus'', 29 (DK 59 A 92). Cf. W. K. C. Guthrie, 1965, p. 319 ; Inna Kupreeva, « Sensing the World », dans ''Physis and Psyche in Plato and Aristotle'', Londres, Bloomsbury, 2024, p. 95-114.</ref>. === La reproduction et l'embryologie === Anaxagore proposa en outre des théories sur la reproduction et le développement embryonnaire. Selon les témoignages anciens, il soutenait que le sexe de l'enfant est déterminé par le père seul, et non par la mère<ref>Aétius, V, 7, 1 (DK 59 A 107) ; Hippolyte, DK 59 A 42.</ref>. Cette théorie n'était pas entièrement originale, puisque des idées similaires avaient été proposées par Parménide et d'autres penseurs antérieurs<ref>Aétius, V, 7, 1 (DK 28 A 52-54). Cf. Ursula Mittwoch, « Sex Determination », ''EMBO Reports'', vol. 14, 2013, p. 588-592 ; Oliver Kember, « Anaxagoras' Theory of Sex Differentiation and Heredity », ''Phronesis'', vol. 18, 1973, p. 1-14.</ref>. === La génération des animaux et des plantes === Selon les témoignages doxographiques, Anaxagore distinguait entre la zoogonie originelle, c'est-à-dire la première génération des animaux, et la reproduction ultérieure par semences<ref>Hippolyte, DK 59 A 42 ; Aétius, V, 19, 4 (DK 59 A 42).</ref>. Dans la zoogonie originelle, les premiers animaux émergèrent de la terre encore chaude et humide, grâce aux semences (σπέρματα) contenues dans l'air et l'éther<ref>Théophraste, dans Simplicius, ''Commentaire sur la Physique'', 27, 2-4.</ref>. En ce qui concerne les plantes, Anaxagore affirmait qu'elles possèdent une âme (ψυχή) et un intellect (νοῦς)<ref>Aétius, V, 26, 4 (DK 59 A 117).</ref>. Cette conclusion découlait de ses observations du comportement des plantes : elles se tournent vers la lumière du soleil, étendent leurs racines vers l'eau<ref>Aristote, ''De l'âme'', I, 5, 410b27-411a7. Cf. Inna Kupreeva, 2024, p. 98-103.</ref>. === Observations biologiques diverses === Les sources anciennes conservent enfin quelques observations biologiques isolées d'Anaxagore. Il soutenait, par exemple, que les belettes sont les seuls animaux qui donnent naissance par la bouche<ref>Aristote, ''Histoire des animaux'', VI, 32, 580a15-17 (DK 59 A 114) ; Plutarque, ''Œuvres morales'', 975F-976A (DK 59 A 114).</ref>. Il était parvenu à cette conclusion erronée parce qu'il avait observé des belettes femelles transportant leurs petits dans leur gueule immédiatement après la mise bas<ref>Plutarque, ''Œuvres morales'', 975F (DK 59 A 114).</ref>. De même, il affirmait que les corbeaux et les ibis s'accouplent par le bec<ref>Aristote, ''Histoire des animaux'', V, 2, 539b31-540a1 (DK 59 A 114).</ref>. Ces erreurs montrent les limites de sa méthode empirique. === L'homme et les animaux === Selon Anaxagore, ce qui distingue l'homme du reste du règne animal est principalement l'une des distinctions physiologiques les plus évidentes : les mains de l'homme<ref>Aristote, ''Parties des animaux'', IV, 10, 687a7-12 (DK 59 A 102). Aristote critique cette position et affirme au contraire que l'homme a des mains parce qu'il est le plus intelligent.</ref>. Ce sont les mains, selon Anaxagore, qui permettent à l'homme de surpasser les animaux dans les compétences manipulatrices et les capacités techniques. == L'influence et la postérité == Anaxagore occupe une place importante dans l'histoire de la philosophie antique. Introducteur à Athènes de la tradition ionienne de recherche naturaliste, il constitue un jalon dans le développement de la pensée grecque du V{{e}} siècle<ref>Diogène Laërce, II, 6 (DK 59 A 1) ; W. K. C. Guthrie, ''A History of Greek Philosophy'', vol. II, Cambridge, Cambridge University Press, 1965, p. 266-267.</ref>. Les lignes qui suivent s'efforcent de décrire son influence avec la prudence philologique nécessaire : il est facile, en traitant des présocratiques, de céder à la tentation des grandes filiations, alors que la documentation réelle invite souvent à plus de retenue. === La transmission immédiate : Archélaos et le cercle socratique === Le premier vecteur de l'influence d'Anaxagore fut son disciple direct Archélaos d'Athènes (parfois dit aussi « de Milet »), qui enseigna à Athènes après le départ de son maître pour Lampsaque<ref>Diogène Laërce, II, 16 (DK 60 A 1) ; Simplicius, ''Commentaire sur la Physique'', 27, 23 (DK 60 A 7). La formule que l'on rencontre parfois, selon laquelle Archélaos aurait « succédé à Anaxagore à Lampsaque », n'est pas documentée par les sources : Archélaos est attesté comme actif à Athènes, où il aurait été le maître de Socrate. Sur ce point, voir Gábor Betegh, « Archelaus on Cosmogony and the Origins of Social Institutions », ''Oxford Studies in Ancient Philosophy'', vol. 51, 2016, p. 1-40.</ref>. Selon plusieurs témoignages anciens, Archélaos aurait été le maître de Socrate, ce qui établit un lien biographique, quoique indirect, entre Anaxagore et le fondateur de la philosophie morale<ref>Diogène Laërce, II, 16 (DK 60 A 1). Ion de Chios rapporte que le jeune Socrate voyagea avec Archélaos à Samos (DK 60 A 3).</ref>. Archélaos semble avoir développé et modifié certaines doctrines de son maître, en particulier en appliquant les principes anaxagoréens à l'éthique et aux institutions sociales, orientation qui anticipe peut-être le tournant moral pris ensuite par Socrate<ref>Diogène Laërce, II, 16 (DK 60 A 1) ; Hippolyte, DK 60 A 4 ; Gábor Betegh, 2016.</ref>. === L'influence sur Socrate : espoirs et déceptions === La relation entre Anaxagore et Socrate a probablement été indirecte, médiée par la lecture du livre. Dans le ''Phédon'', Platon fait raconter par Socrate sa rencontre avec la pensée d'Anaxagore, rencontre qui suscita d'abord un enthousiasme puis une déception<ref>Platon, ''Phédon'', 97b-98c (DK 59 A 47).</ref>. Comme on l'a souligné plus haut, cette déception porte sur une attente proprement socratique, celle d'une explication par le « meilleur », qu'Anaxagore n'avait pas, à proprement parler, formulée dans les termes que Socrate aurait voulus. Cette critique conduisit Socrate à se tourner vers la philosophie morale et la recherche de la cause finale, c'est-à-dire du Bien qui rendrait compte de l'ordre du monde<ref>Platon, ''Phédon'', 99c-100a. Cf. David Sedley, « Teleology and Myth in the ''Phaedo'' », ''Proceedings of the Boston Area Colloquium in Ancient Philosophy'', vol. 5, 1989, p. 359-383.</ref>. L'idée qu'un principe rationnel puisse gouverner l'univers, idée que Socrate trouve chez Anaxagore et qu'il juge insuffisamment exploitée, a pu orienter le projet socratique sans qu'il faille pour autant présenter Anaxagore comme sa cause nécessaire<ref>David Sider, « Anaxagoras, Socrates, and the History of "Philosophy" », ''Research Bulletin of the CHS'', 2016 ; W. K. C. Guthrie, 1965, p. 327-331.</ref>. Dans l'''Apologie'', l'accusation portée contre Socrate inclut l'imputation de théories cosmologiques manifestement inspirées d'Anaxagore, ce qui montre l'association étroite entre les deux penseurs dans l'esprit des Athéniens, indépendamment de la distance philosophique réelle qui les sépare<ref>Platon, ''Apologie de Socrate'', 26d (DK 59 A 35). Cf. Gregory Vlastos, ''Socrates: Ironist and Moral Philosopher'', Ithaca, Cornell University Press, 1991, p. 293-297.</ref>. Xénophon présente pour sa part Socrate comme mettant en garde ses disciples contre l'étude des phénomènes célestes à la manière d'Anaxagore, ce qui laisse entendre que Socrate lui-même s'était explicitement démarqué de la philosophie naturelle anaxagoréenne<ref>Xénophon, ''Mémorables'', IV, 7, 6 (DK 59 A 47).</ref>. === L'appropriation platonicienne : du Noûs au Démiurge === Platon s'empare de la doctrine anaxagoréenne du Noûs et la transforme, en la radicalisant, en une cosmologie téléologique pleinement articulée. Dans le ''Timée'', Platon présente le Démiurge, cet artisan divin qui façonne le monde sensible à l'image des Formes éternelles, comme une figure qu'on peut lire en dialogue avec le Noûs anaxagoréen<ref>Platon, ''Timée'', 29a-30c, 47e-48a. Cf. Glenn Morrow, « Necessity and Persuasion in Plato's ''Timaeus'' », ''Philosophical Review'', vol. 59, 1950, p. 147-163 ; Luc Brisson, ''Le Même et l'Autre dans la structure ontologique du Timée de Platon'', Paris, Klincksieck, 1974, p. 87-125.</ref>. Il convient toutefois d'être prudent. La téléologie platonicienne va en effet bien au-delà de ce qu'affirmait Anaxagore : le Démiurge contemple les Formes éternelles pour façonner le monde, et cet arrière-plan métaphysique d'essences intelligibles n'a pas d'équivalent chez le Clazoménien, dont le Noûs demeure un principe moteur et cognitif, mais non un principe qui « voit » des réalités intelligibles préalables à son action. Dans le ''Philèbe'', Platon affirme explicitement sa dette envers Anaxagore en déclarant que « l'Intellect est roi du ciel et de la terre »<ref>Platon, ''Philèbe'', 28c (DK 59 A 47). Cf. également ''Cratyle'', 413c.</ref>, reprenant ainsi la doctrine anaxagoréenne selon laquelle « le Noûs domine tout »<ref>Anaxagore, B12 (DK 59 B 12).</ref>. Dans les ''Lois'', Platon fait l'éloge des « anciens » qui ont découvert que « l'Intellect gouverne toutes choses », allusion probable à Anaxagore<ref>Platon, ''Lois'', X, 897b-c, XII, 967b-c. Cf. Malcolm Schofield, 1980, p. 55-70.</ref>. L'influence d'Anaxagore sur Platon est donc réelle, mais elle passe par une transformation considérable de la doctrine originelle : le Noûs anaxagoréen, simple cause motrice, devient chez Platon un Intellect qui contemple le Bien et façonne le monde selon le meilleur ordre possible. Le passage d'Anaxagore à Platon est celui qui fait naître, en toute rigueur, la tradition téléologique de la philosophie occidentale. === La critique aristotélicienne === Aristote, tout en reconnaissant l'importance historique d'Anaxagore, fut l'un de ses critiques les plus sévères. Dans la ''Métaphysique'', il salue Anaxagore comme « un homme sobre parmi des bavards »<ref>Aristote, ''Métaphysique'', I, 3, 984b15-20 (DK 59 A 43).</ref>, louant son introduction du Noûs comme cause de l'ordre cosmique. Mais cette louange est immédiatement suivie d'une critique : Anaxagore « utilise l'Intellect comme un ''deus ex machina'' »<ref>Aristote, ''Métaphysique'', I, 4, 985a18-21 (DK 59 A 47).</ref>. Comme on l'a vu, cette critique présuppose un idéal téléologique qui n'est pas celui d'Anaxagore lui-même. Aristote adopte en revanche, en le reformulant, le terme technique homéomère (ὁμοιομερής) pour désigner les substances anaxagoréennes, bien qu'il soit probable qu'Anaxagore lui-même n'ait jamais utilisé ce mot<ref>Cf. Malcolm Schofield, 1980, p. 87-108.</ref>. La distinction que la tradition anaxagoréenne a léguée, entre substances homéomères (chair, os, sang) et substances anhoméomères (main, pied, visage), devient ainsi fondamentale dans la philosophie naturelle aristotélicienne, où elle servira à articuler l'analyse des parties du vivant<ref>Aristote, ''De la génération et de la corruption'', I, 1, 314a20-b1 ; ''Parties des animaux'', II, 1, 646a12-24.</ref>. === L'héritage dans la philosophie hellénistique et romaine === Après Aristote, la pensée d'Anaxagore continua d'exercer une influence diffuse. Les Stoïciens s'approprièrent certains aspects de sa doctrine du Noûs pour développer leur propre concept du Logos universel qui pénètre et gouverne toute la nature<ref>Diogène Laërce, VII, 134-139 (SVF II, 634) ; Cicéron, ''De la nature des dieux'', I, 11, 27 ; II, 8, 23. Cf. A. A. Long et D. N. Sedley, ''The Hellenistic Philosophers'', vol. I, Cambridge, Cambridge University Press, 1987, p. 268-274.</ref>. Le pneuma stoïcien présente des analogies avec le Noûs anaxagoréen, bien que les Stoïciens aient rejeté le dualisme matière/esprit d'Anaxagore en faveur d'un matérialisme intégral<ref>Michael J. White, « Stoic Natural Philosophy », dans Brad Inwood (éd.), ''The Cambridge Companion to the Stoics'', Cambridge, Cambridge University Press, 2003, p. 124-152.</ref>. Les Épicuriens, en revanche, rejetèrent la doctrine anaxagoréenne. Lucrèce critique explicitement Anaxagore dans le ''De natura rerum''<ref>Lucrèce, ''De la nature'', I, 830-920 (DK 59 A 44). Cf. David Sedley, ''Lucretius and the Transformation of Greek Wisdom'', Cambridge, Cambridge University Press, 1998, p. 24-33.</ref>. Dans la tradition néoplatonicienne, Anaxagore fut lu à travers le prisme de Platon. Simplicius, au VI{{e}} siècle de notre ère, consacra de longs passages de son commentaire sur la ''Physique'' d'Aristote à l'exégèse des fragments d'Anaxagore ; c'est d'ailleurs grâce à ces commentaires que la majeure partie de notre connaissance d'Anaxagore nous est parvenue<ref>Simplicius, ''Commentaire sur la Physique'' (passim).</ref>. === Un héritage à ne pas surinterpréter === Il est tentant de présenter Anaxagore comme un précurseur de la science mécaniste moderne, de l'atomisme, voire de la physique contemporaine. Il faut résister à cette tentation, au moins dans sa forme la plus large. Au XVII{{e}} siècle, certains philosophes mécanistes ont effectivement cité les présocratiques pour légitimer leur programme, mais c'est l'atomisme de Démocrite et de Leucippe, médiatisé par Épicure et par Lucrèce, qui a exercé une influence directe sur la physique corpusculaire naissante de Gassendi, Boyle et Newton. L'influence propre d'Anaxagore sur cette tradition reste, pour l'essentiel, limitée et indirecte : sa doctrine de la divisibilité infinie et du mélange universel s'oppose d'ailleurs aux postulats fondamentaux de l'atomisme classique, et c'est précisément cette opposition qui la rendait peu exploitable pour les mécanistes modernes<ref>Alan Chalmers, « Atomism from the 17th to the 20th Century », ''Stanford Encyclopedia of Philosophy'', 2005 (révisé 2014) ; Andrew Pyle, ''Atomism and its Critics : From Democritus to Newton'', Bristol, Thoemmes Press, 1997.</ref>. On trouve aussi, ici ou là, des rapprochements entre la doctrine anaxagoréenne du « tout dans tout » et certaines idées de la physique moderne : théorie des champs, intrication quantique, non-séparabilité des systèmes physiques. De tels rapprochements, parfois suggérés par des physiciens eux-mêmes (Werner Heisenberg évoque à l'occasion les présocratiques dans ses réflexions philosophiques), peuvent avoir une valeur heuristique ou pédagogique. Ils ne doivent pas pour autant être transformés en affirmations de continuité doctrinale. La physique quantique procède d'un appareil mathématique et expérimental qui n'a aucun équivalent chez les Grecs anciens, et les analogies conceptuelles qu'on peut tracer avec Anaxagore relèvent de la métaphore rétrospective, non de la filiation historique<ref>Werner Heisenberg, ''Physics and Philosophy'', New York, Harper, 1958, p. 62-63, évoque les présocratiques sans établir de filiation précise avec la physique quantique.</ref>. Il a été parfois suggéré, de manière analogue, que les grands théologiens médiévaux aient vu dans le Noûs un précurseur du Dieu créateur et ordonnateur. Cette suggestion demande une prudence particulière. Thomas d'Aquin, Maïmonide et Avicenne s'inscrivent dans une tradition aristotélicienne et néoplatonicienne déjà très élaborée, dans laquelle le Noûs anaxagoréen n'apparaît, quand il est mentionné, qu'à travers la médiation d'Aristote, et le plus souvent pour être critiqué. La théologie médiévale développe sa conception du Dieu créateur à partir de ressources propres (exégèse biblique, coranique ou talmudique, théologie négative néoplatonicienne, métaphysique aristotélicienne de l'acte pur), et non par appropriation directe de la doctrine anaxagoréenne. Il faut donc parler de continuités discrètes dans une histoire complexe, où la figure d'Anaxagore est plus souvent un repoussoir ou une étape dépassée qu'une source vive<ref>Sur la tradition aristotélicienne et néoplatonicienne médiévale, voir Étienne Gilson, ''L'esprit de la philosophie médiévale'', Paris, Vrin, 1932 ; sur la transmission arabe des présocratiques, voir Cristina D'Ancona, « Greek into Arabic: Neoplatonism in Translation », dans P. Adamson et R. C. Taylor (éd.), ''The Cambridge Companion to Arabic Philosophy'', Cambridge, Cambridge University Press, 2005, p. 10-31.</ref>. Une dernière mise en garde concerne la prédiction de l'éclipse et la chute du météorite d'Aigos Potamos, qui ont parfois été présentées comme des triomphes de la « méthode scientifique » naissante. En réalité, les sources qui attribuent à Anaxagore ces prouesses prédictives sont tardives et souvent légendaires ; les historiens des sciences contemporains, comme Daniel Graham ou Geoffrey Lloyd, se montrent prudents, voire sceptiques, sur l'authenticité de ces anticipations. L'importance historique d'Anaxagore ne tient pas à la précision de ses prédictions, mais à la cohérence et à l'ambition explicative de son système naturaliste, qui propose de rendre compte des phénomènes célestes par des principes physiques accessibles à la raison, sans recourir aux figures mythologiques traditionnelles. === Bilan === L'influence d'Anaxagore sur la postérité se caractérise par un contraste fondamental : d'un côté, son introduction du Noûs comme principe cosmique fut saluée comme une avancée majeure par Aristote lui-même, qui loue son caractère « sobre » par contraste avec les rêveries des premiers Ioniens ; de l'autre, son refus (ou son incapacité) à développer une explication par le « meilleur » fut jugée insuffisante par Socrate, Platon et Aristote. Cette dualité traverse toute l'histoire de sa réception : Anaxagore est à la fois le penseur qui a rendu pensable une cause intellectuelle du cosmos, et celui qui a laissé cette cause inemployée au goût de ses successeurs<ref>W. K. C. Guthrie, 1965, p. 327-331 ; Malcolm Schofield, 1980, p. 55-70 ; Daniel Graham, ''Science before Socrates'', Oxford, Oxford University Press, 2006, p. 152-158.</ref>. Cette limitation même fut paradoxalement féconde. En ouvrant une voie sans la parcourir jusqu'au bout, Anaxagore a invité ses successeurs à poursuivre le chemin, chacun à sa manière. Socrate s'est tourné vers la philosophie morale, en cherchant dans le Bien la cause que le Noûs anaxagoréen ne fournissait pas. Platon a développé, dans le ''Timée'' et ailleurs, une cosmologie téléologique dans laquelle le Démiurge contemple les Formes éternelles pour façonner le monde. Aristote a élaboré, contre Platon autant que contre Anaxagore, une doctrine systématique des quatre causes qui intègre la cause finale dans un schéma explicatif unifié. Tous trois ont puisé dans Anaxagore, chacun à sa manière, mais au prix d'une transformation qui leur appartient en propre. On peut donc parler d'une empreinte durable d'Anaxagore sur les orientations ultérieures de la philosophie grecque, sans pour autant soutenir que Socrate, Platon ou Aristote n'auraient pas été ce qu'ils furent sans lui : leurs doctrines ont leurs propres fondements, souvent en réaction à Anaxagore autant qu'en prolongement de lui, et la cause anaxagoréenne n'est, dans leur cas, jamais que partielle. Sur le plan scientifique, l'héritage d'Anaxagore est surtout celui d'un modèle d'explication naturaliste. Il propose des explications des phénomènes célestes et météorologiques en termes de substances et de processus physiques, sans recourir aux agents mythologiques qui peuplaient encore l'imaginaire grec traditionnel. Le soleil n'est plus Hélios mais une pierre incandescente ; la lune n'est plus une déesse mais un corps rocheux qui reçoit la lumière du soleil ; la foudre n'est plus le trait de Zeus mais le produit d'un phénomène atmosphérique. Cet héritage naturaliste n'est pas négligeable, et il a nourri durablement la tradition philosophique et scientifique grecque. Il serait pourtant excessif de lui attribuer à titre principal la fondation d'une « méthode scientifique » au sens moderne : l'élaboration de cette méthode suppose l'invention de la preuve démonstrative, le développement de la mathématisation de la nature, et bien d'autres étapes que la pensée d'Anaxagore n'a pas accomplies<ref>Daniel Gershenson et Daniel Greenberg, 1964, défendent une version forte de la thèse d'Anaxagore comme fondateur de la méthode scientifique, thèse qu'il convient de modérer. Cf. Geoffrey Lloyd, ''Early Greek Science : Thales to Aristotle'', Londres, Chatto & Windus, 1970, pour une perspective plus prudente.</ref>. En définitive, Anaxagore occupe une place singulière dans l'histoire de la philosophie : celle d'un pionnier qui ouvre une voie sans l'explorer entièrement, et qui a légué à ses successeurs à la fois une doctrine (dans ses fragments conservés) et la tâche philosophique de combler ce que cette doctrine laisse ouvert. Cette position intermédiaire entre la cosmologie ionienne, dont il est le dernier grand représentant à Athènes, et la philosophie classique, dont il est l'un des principaux préparateurs, fait d'Anaxagore une figure pivot, dont l'intérêt philosophique propre ne doit pas être dilué dans l'histoire des influences qu'il a exercées. == Notes et références == {{references}} == Bibliographie == === Textes anciens : éditions et traductions === ; Diels, Hermann & Kranz, Walther (éds.) : ''Die Fragmente der Vorsokratiker'', 3 vol., Berlin, Weidmann, 1951-1952 (6{{e}} éd.) : [Édition standard de référence pour les fragments présocratiques, avec le système de numérotation DK (Diels-Kranz) utilisé dans la présente étude] ; Laks, André & Most, Glenn W. (éds.) : ''Early Greek Philosophy'', 9 vol., Loeb Classical Library, Cambridge (Mass.), Harvard University Press, 2016 : [Édition plus récente avec traduction anglaise et nouveau système de numérotation, utile en complément de Diels-Kranz] ; Kirk, G. S., Raven, J. E. & Schofield, M. (éds.) : ''The Presocratic Philosophers: A Critical History with a Selection of Texts'', 2{{e}} édition, Cambridge, Cambridge University Press, 1983 : [Traductions anglaises commentées des fragments] ; Curd, Patricia (éd.) : ''Anaxagoras of Clazomenae: Fragments and Testimonia'', Toronto, University of Toronto Press, 2007 : [Édition critique avec traductions anglaises commentées et analyses détaillées] ; Sider, David (éd.) : ''The Fragments of Anaxagoras: With a Commentary'', Sankt Augustin, Academia Verlag, 2005 : [Édition avec commentaire détaillé] ; Platon : ''Phédon'', trad. fr. Monique Dixsaut, Paris, GF-Flammarion, 1991 : [Dialogue contenant la critique socratique du Noûs d'Anaxagore] ; Platon : ''Apologie de Socrate'', trad. fr. Luc Brisson, Paris, GF-Flammarion, 1997 ; Platon : ''Timée'', trad. fr. Luc Brisson, Paris, GF-Flammarion, 1992 ; Platon : ''Philèbe'', trad. fr. Alfred Diès, Paris, Les Belles Lettres, 1941 ; Platon : ''Lois'', trad. fr. Édouard des Places, Paris, Les Belles Lettres, 1951-1956 ; Aristote : ''Métaphysique'', trad. fr. Jean Tricot, Paris, Vrin, 1933 ; Aristote : ''Physique'', trad. fr. Jean Tricot, Paris, Vrin, 1936 ; Aristote : ''Du Ciel'', trad. fr. Jean Tricot, Paris, Vrin, 1949 ; Aristote : ''Génération et corruption'', trad. fr. Jean Tricot, Paris, Vrin, 1950 ; Aristote : ''Génération des animaux'', trad. fr. Jean Tricot, Paris, Vrin, 1957 ; Aristote : ''Histoire des animaux'', trad. fr. Jean Tricot, Paris, Vrin, 1957 ; Aristote : ''Parties des animaux'', trad. fr. Jean Tricot, Paris, Vrin, 1957 ; Aristote : ''De l'âme'', trad. fr. Richard Bodéüs, Paris, GF-Flammarion, 1993 ; Diogène Laërce : ''Vies et doctrines des philosophes illustres'', sous la dir. de Marie-Odile Goulet-Cazé, Paris, Le Livre de Poche (La Pochothèque), 1999 ; Théophraste : ''De Sensibus'' (''On Sense Perception''), trad. angl. dans Kirk, Raven & Schofield, 1983 ; Simplicius : ''In Aristotelis Physicorum libros commentaria'', éd. Hermann Diels, Berlin, Reimer, 1882-1895 : [Source principale pour la transmission des fragments d'Anaxagore] ; Hippolyte : ''Réfutation de toutes les hérésies'', trad. angl. dans Curd, 2007 ; Lucrèce : ''De la nature'', trad. fr. Alfred Ernout, Paris, Les Belles Lettres, 1920 ; Cicéron : ''De la nature des dieux'', trad. fr. Clara Auvray-Assayas, Paris, Les Belles Lettres, 2002 ; Plutarque : ''Vies parallèles'' (''Vie de Périclès'', ''Vie de Lysandre''), trad. fr. Anne-Marie Ozanam, Paris, Gallimard, Bibliothèque de la Pléiade, 2001 ; Xénophon : ''Mémorables'', trad. fr. Louis-André Dorion et Michele Bandini, Paris, Les Belles Lettres, 2000-2011 === Études modernes : histoire et philosophie antiques === ; Schofield, Malcolm : ''An Essay on Anaxagoras'', Cambridge, Cambridge University Press, 1980 : [Monographie majeure : étude exhaustive de la pensée anaxagoréenne avec analyse textuelle détaillée] ; Curd, Patricia : « Anaxagoras », ''Stanford Encyclopedia of Philosophy'', publié 2007, révision substantielle 2019 : [Synthèse de référence, avec bibliographie à jour] ; Guthrie, W. K. C. : ''A History of Greek Philosophy'', vol. II : ''The Presocratic Tradition from Parmenides to Democritus'', Cambridge, Cambridge University Press, 1965 ; Barnes, Jonathan : ''The Presocratic Philosophers'', 2 vol., Londres, Routledge, 1982 (révisé 2006) ; Kirk, G. S., Raven, J. E. & Schofield, M. : ''The Presocratic Philosophers: A Critical History with a Selection of Texts'', 2{{e}} édition, Cambridge, Cambridge University Press, 1983 ; Vlastos, Gregory : « The Physical Theory of Anaxagoras », ''Philosophical Review'', vol. 59, 1950, p. 31-57 ; Peck, Arthur L. : « Anaxagoras: Predication as a Problem in Physics », ''Classical Quarterly'', vol. 25, 1931, p. 27-37 et p. 112-120 ; Strang, Colin : « The Physical Theory of Anaxagoras », ''Archiv für Geschichte der Philosophie'', vol. 45, 1963, p. 101-118 ; Cornford, F. M. : « Anaxagoras' Theory of Matter », ''Classical Quarterly'', vol. 24, 1930, p. 14-30 ; Graham, Daniel W. : « Was Anaxagoras a Reductionist? », ''Ancient Philosophy'', vol. 24, 2004, p. 1-18 ; Graham, Daniel W. : ''Science before Socrates: Parmenides, Anaxagoras, and the New Astronomy'', Oxford, Oxford University Press, 2006 ; Curd, Patricia : ''The Legacy of Parmenides: Eleatic Monism and Later Presocratic Thought'', Princeton, Princeton University Press, 1998 ; Owen, G. E. L. : « The Place of the Timaeus in Plato's Dialogues », ''Classical Quarterly'', vol. 3, 1953, p. 79-95 ; repris dans ''Logic, Science and Dialectic'', Londres, Duckworth, 1986 ; Raven, J. E. : « The Basis of Anaxagoras' Cosmology », ''Classical Quarterly'', vol. 4, 1954, p. 123-137 ; Kerferd, George B. : « Anaxagoras and the Concept of Matter before Aristotle », ''Bulletin of the John Rylands Library'', vol. 52, 1969, p. 129-143 ; Lloyd, David : « Anaxagoras on Life and Mind », ''Phronesis'', vol. 14, 1969, p. 246-251 ; Deichgräber, Karl : « Anaxagoras Stil », ''Philologus'', vol. 87, 1933, p. 15-26 ; Norden, Eduard : ''Agnostos Theos: Untersuchungen zur Formengeschichte religiöser Rede'', Leipzig, Teubner, 1913 ; Taylor, A. E. : « On the Date of the Trial of Anaxagoras », ''Classical Quarterly'', vol. 11, 1917, p. 81-87 ; Kahn, Charles H. : ''Anaximander and the Origins of Greek Cosmology'', New York, Columbia University Press, 1960 ; Inwood, Brad : « Anaxagoras and Infinite Divisibility », ''Illinois Classical Studies'', vol. 11, 1986, p. 17-33 ; Sedley, David : ''Creationism and Its Critics in Antiquity'', Berkeley, University of California Press, 2007 ; Sedley, David : « Teleology and Myth in the ''Phaedo'' », ''Proceedings of the Boston Area Colloquium in Ancient Philosophy'', vol. 5, 1989, p. 359-383 ; Betegh, Gábor : « Archelaus on Cosmogony and the Origins of Social Institutions », ''Oxford Studies in Ancient Philosophy'', vol. 51, 2016, p. 1-40 ; Sider, David : « Anaxagoras, Socrates, and the History of "Philosophy" », ''Research Bulletin of the Center for Hellenic Studies'', 31 octobre 2016 ; Furley, David L. : ''Two Studies in the Greek Atomists'', Princeton, Princeton University Press, 1967 ; Furley, David L. : « Anaxagoras, Plato and Naming of Parts », dans ''Presocratic Philosophy: Essays in Honour of Alexander Mourelatos'', éd. Victor Caston et Daniel Graham, Aldershot, Ashgate, 2002, p. 119-126 ; Furth, Montgomery : « A Philosophical Hero? Anaxagoras and the Eleatics », ''Oxford Studies in Ancient Philosophy'', vol. 9, 1991, p. 95-129 ; Lesher, James : « Mind's Knowledge and Powers of Control in Anaxagoras », ''Phronesis'', vol. 40, 1995, p. 125-142 ; Marmodoro, Anna : « Anaxagoras's Qualitative Gunk », ''British Journal for the History of Philosophy'', vol. 23, 2015, p. 402-422 ; Marmodoro, Anna & Morison, Benjamin (éds.) : ''Everything in Everything: Anaxagoras's Metaphysics'', Oxford, Oxford University Press, 2019 ; Vassallo, Christian : « Nous, Motion, and Teleology in Anaxagoras », ''Oxford Studies in Ancient Philosophy'', vol. 50, 2016, p. 1-32 === Études modernes : biologie, physiologie et philosophie naturelle === ; Gershenson, Daniel E. & Greenberg, Daniel A. : ''Anaxagoras and the Birth of Scientific Method'', New York, Blaisdell, 1964 : [À utiliser avec précaution : la thèse centrale sur la « naissance de la méthode scientifique » doit être tempérée] ; Meyer, Arthur William : ''Essays on the History of Embryology'', Stanford, Stanford University Press, 1939 ; Preus, Anthony : « The Techne of Nutrition in Ancient Greek Philosophy », ''Apeiron'', vol. 53, 2020, p. 97-124 ; Kember, Oliver : « Anaxagoras' Theory of Sex Differentiation and Heredity », ''Phronesis'', vol. 18, 1973, p. 1-14 ; Mittwoch, Ursula : « Sex Determination: Science & Society Series on Sex and Science », ''EMBO Reports'', vol. 14, 2013, p. 588-592 ; Kupreeva, Inna : « Sensing the World: Humans, Plants, and the Physicality of Life in Early Greek Philosophy », dans ''Physis and Psyche in Plato and Aristotle'', éd. S. D. Kolstrup et T. L. Kind, Londres, Bloomsbury, 2024, p. 95-114 === Études modernes : astronomie, cosmologie et météorologie === ; Theodossiou, Evangelos Th., Dimitrijevic, Milcho S., Mantarakis, Nikos A. & Georgakarakos, Nikolaos I. : « The Fall of a Meteorite at Aegos Potami in 467/6 BC », ''Journal of Astronomical History and Heritage'', vol. 5, 2002, p. 135-140 ; Graham, Daniel W. & Hintz, Eric : « Anaxagoras and the Comet », ''Apeiron'', vol. 40, 2007, p. 1-20 ; Couprie, Dirk L. : « Anaxagoras on the Milky Way and Lunar Eclipses », ''Rhizomata'', vol. 5, 2017, p. 127-147 ; Lloyd, Geoffrey E. R. : ''Early Greek Science: Thales to Aristotle'', Londres, Chatto & Windus, 1970 === Études modernes : influence et réception === ; Vlastos, Gregory : ''Socrates: Ironist and Moral Philosopher'', Ithaca, Cornell University Press, 1991 ; Morrow, Glenn R. : « Necessity and Persuasion in Plato's ''Timaeus'' », ''Philosophical Review'', vol. 59, 1950, p. 147-163 ; Brisson, Luc : ''Le Même et l'Autre dans la structure ontologique du Timée de Platon'', Paris, Klincksieck, 1974 ; Johansen, Thomas Kjeller : ''Plato's Natural Philosophy'', Cambridge, Cambridge University Press, 2004 ; Johansen, Thomas Kjeller : « From Craft to Nature: The Emergence of Natural Teleology », dans ''Plato and Hesiod'', éd. G. R. Boys-Stones et J. H. Haubold, Oxford, Oxford University Press, 2020, p. 100-125 ; Long, A. A. & Sedley, D. N. : ''The Hellenistic Philosophers'', vol. I, Cambridge, Cambridge University Press, 1987 ; White, Michael J. : « Stoic Natural Philosophy », dans ''The Cambridge Companion to the Stoics'', éd. Brad Inwood, Cambridge, Cambridge University Press, 2003, p. 124-152 ; Sedley, David : ''Lucretius and the Transformation of Greek Wisdom'', Cambridge, Cambridge University Press, 1998 ; Chalmers, Alan : « Atomism from the 17th to the 20th Century », ''Stanford Encyclopedia of Philosophy'', 2005 (révisé 2014) ; Pyle, Andrew : ''Atomism and its Critics: From Democritus to Newton'', Bristol, Thoemmes Press, 1997 ; Gilson, Étienne : ''L'esprit de la philosophie médiévale'', Paris, Vrin, 1932 ; Heisenberg, Werner : ''Physics and Philosophy'', New York, Harper, 1958 : [À consulter pour les rapprochements prudents entre présocratiques et physique moderne, à titre d'analogie heuristique] === Instruments de recherche et ressources === ; Curd, Patricia : « Anaxagoras », ''Stanford Encyclopedia of Philosophy'', 2007 (révision substantielle 2019) ; Graham, Daniel W. : « Presocratic Philosophy », ''Stanford Encyclopedia of Philosophy'', 2019 ; ''Oxford Classical Dictionary'' : s.v. « Anaxagoras », diverses éditions : [À consulter pour la mise en garde prudente sur la tradition biographique] === Dictionnaires et encyclopédies === ; Goulet, Richard (éd.) : ''Dictionnaire des philosophes antiques'', vol. I, Paris, CNRS Éditions, 1989 (2{{e}} éd. 2003) : [Entrée détaillée sur Anaxagore avec bibliographie] {{autocat}} [[Catégorie:Philosophe]] dtqds7o30e8tsa4x5sc624nedesied8 Les cartes graphiques/Le rendu d'une scène 3D : l'API graphique 0 83408 763942 763813 2026-04-18T13:41:46Z Mewtow 31375 /* Les attributs de sommets et variables uniformes */ 763942 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ===Les optimisations liées aux ''draw calls''=== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. Une première optimisation regroupe les objets avec le même ''render state'' ensemble. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, cette optimisation débloque d'autres optimisations très importantes, qui permettent de réduire le nombre de ''draw calls''. Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Diverses optimisations permettent de faciliter le ''batching''. L'idée est de rendre les différents ''render state'' plus similaires que la normale. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures en une seule texture. Deux objets avec les mêmes shaders et les mêmes options de configuration, peuvent ainsi partager le même ''render state'' quand ils adressent le même atlas de texture et non exactement les mêmes textures. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} slp3nz83x7u4xtxzdnle4m6mmxrqqrd 763943 763942 2026-04-18T13:41:55Z Mewtow 31375 /* Les textures */ 763943 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ===Les optimisations liées aux ''draw calls''=== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. Une première optimisation regroupe les objets avec le même ''render state'' ensemble. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, cette optimisation débloque d'autres optimisations très importantes, qui permettent de réduire le nombre de ''draw calls''. Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Diverses optimisations permettent de faciliter le ''batching''. L'idée est de rendre les différents ''render state'' plus similaires que la normale. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures en une seule texture. Deux objets avec les mêmes shaders et les mêmes options de configuration, peuvent ainsi partager le même ''render state'' quand ils adressent le même atlas de texture et non exactement les mêmes textures. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} 62w2vubw279keun6oezaccux5q2br0z 763945 763943 2026-04-18T13:47:34Z Mewtow 31375 /* Les optimisations liées aux draw calls */ 763945 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ===Les optimisations liées aux ''draw calls''=== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. Une première optimisation regroupe les objets avec le même ''render state'' ensemble. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, cette optimisation débloque d'autres optimisations très importantes, qui permettent de réduire le nombre de ''draw calls''. Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Diverses optimisations permettent de faciliter le ''batching'', en rendant les différents ''render state'' plus similaires que la normale. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures dans une super-texture. La technique évite des changements d'état dans l'API 3D. Pas besoin de changer de texture à chaque objet, on peut partager une super-texture capable de texturer plusieurs objets différents, rendus l'un après l'autre. La création d'un atlas de texture n'est pas de tout repos, il ne suffit pas de regrouper plusieurs textures. Le filtrage de texture ne doit pas mélanger des texels provenant de deux textures différents, même si elles sont dans la même super-texture. Et il y a d'autres problèmes liés aux techniques dites de ''mip-mapping'', ainsi qu'aux modes d'adressage de texture. Pour éviter ces problèmes, les API modernes ont ajouté une fonctionnalité : les '''''texture arrays'''''. Le terme ''array'' est assez clair : il s'agit de tableaux, qui regroupent plusieurs textures. Précisons ce qu'on veut dire pas regrouper. Les textures ne sont pas placées dans une super-texture, elles ne sont pas consécutives en mémoire. Par contre, le ''texture array'' mémorise l'adresse de chaque texture. Pour accéder à une texture, il suffit de fournir son indice, sa place dans le tableau : on récupére l'adresse de la texture, qui est ensuite envoyée à l'unité de texture. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} e56mux20wyn7j6gahhak2oiaw49uala 763946 763945 2026-04-18T13:51:42Z Mewtow 31375 /* Les optimisations liées aux draw calls */ 763946 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ==Les optimisations liées aux ''draw calls''== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. ===Les optimisations du ''render state''=== Une première optimisation vise à réduire les changements de ''render state''. Elle rend les objets avec le même ''render state'' ensemble, les uns à la suite des autres. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, d'autres optimisations permettent d'en profiter au mieux. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures dans une super-texture. La technique évite des changements d'état dans l'API 3D. Pas besoin de changer de texture à chaque objet, on peut partager une super-texture capable de texturer plusieurs objets différents. La création d'un atlas de texture n'est pas de tout repos, il ne suffit pas de regrouper plusieurs textures. Le filtrage de texture ne doit pas mélanger des texels provenant de deux textures différents, même si elles sont dans la même super-texture. Et il y a d'autres problèmes liés aux techniques dites de ''mip-mapping'', ainsi qu'aux modes d'adressage de texture. Pour éviter ces problèmes, les API modernes ont ajouté une fonctionnalité : les '''''texture arrays'''''. Le terme ''array'' est assez clair : il s'agit de tableaux, qui regroupent plusieurs textures. Précisons ce qu'on veut dire pas regrouper. Les textures ne sont pas placées dans une super-texture, elles ne sont pas consécutives en mémoire. Par contre, le ''texture array'' mémorise l'adresse de chaque texture. Pour accéder à une texture, il suffit de fournir son indice, sa place dans le tableau : on récupère l'adresse de la texture, qui est ensuite envoyée à l'unité de texture. ===Le ''batching'' et l'''instancing''=== Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} 4m8y0qn83cvio5opqbjh1ch2d2id413 763947 763946 2026-04-18T13:52:01Z Mewtow 31375 /* Le batching et l'instancing */ 763947 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ==Les optimisations liées aux ''draw calls''== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. ===Les optimisations du ''render state''=== Une première optimisation vise à réduire les changements de ''render state''. Elle rend les objets avec le même ''render state'' ensemble, les uns à la suite des autres. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, d'autres optimisations permettent d'en profiter au mieux. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures dans une super-texture. La technique évite des changements d'état dans l'API 3D. Pas besoin de changer de texture à chaque objet, on peut partager une super-texture capable de texturer plusieurs objets différents. La création d'un atlas de texture n'est pas de tout repos, il ne suffit pas de regrouper plusieurs textures. Le filtrage de texture ne doit pas mélanger des texels provenant de deux textures différents, même si elles sont dans la même super-texture. Et il y a d'autres problèmes liés aux techniques dites de ''mip-mapping'', ainsi qu'aux modes d'adressage de texture. Pour éviter ces problèmes, les API modernes ont ajouté une fonctionnalité : les '''''texture arrays'''''. Le terme ''array'' est assez clair : il s'agit de tableaux, qui regroupent plusieurs textures. Précisons ce qu'on veut dire pas regrouper. Les textures ne sont pas placées dans une super-texture, elles ne sont pas consécutives en mémoire. Par contre, le ''texture array'' mémorise l'adresse de chaque texture. Pour accéder à une texture, il suffit de fournir son indice, sa place dans le tableau : on récupère l'adresse de la texture, qui est ensuite envoyée à l'unité de texture. ===Les optimisations des ''draw calls'' : ''batching'' et '''instancing''=== Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} bim3h7o1h4rat1tvmwnyjqjtqanw0bv 763948 763947 2026-04-18T13:52:07Z Mewtow 31375 /* Les optimisations des draw calls : batching et 'instancing */ 763948 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ==Les optimisations liées aux ''draw calls''== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. ===Les optimisations du ''render state''=== Une première optimisation vise à réduire les changements de ''render state''. Elle rend les objets avec le même ''render state'' ensemble, les uns à la suite des autres. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, d'autres optimisations permettent d'en profiter au mieux. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures dans une super-texture. La technique évite des changements d'état dans l'API 3D. Pas besoin de changer de texture à chaque objet, on peut partager une super-texture capable de texturer plusieurs objets différents. La création d'un atlas de texture n'est pas de tout repos, il ne suffit pas de regrouper plusieurs textures. Le filtrage de texture ne doit pas mélanger des texels provenant de deux textures différents, même si elles sont dans la même super-texture. Et il y a d'autres problèmes liés aux techniques dites de ''mip-mapping'', ainsi qu'aux modes d'adressage de texture. Pour éviter ces problèmes, les API modernes ont ajouté une fonctionnalité : les '''''texture arrays'''''. Le terme ''array'' est assez clair : il s'agit de tableaux, qui regroupent plusieurs textures. Précisons ce qu'on veut dire pas regrouper. Les textures ne sont pas placées dans une super-texture, elles ne sont pas consécutives en mémoire. Par contre, le ''texture array'' mémorise l'adresse de chaque texture. Pour accéder à une texture, il suffit de fournir son indice, sa place dans le tableau : on récupère l'adresse de la texture, qui est ensuite envoyée à l'unité de texture. ===Les optimisations des ''draw calls'' : ''batching'' et ''instancing''=== Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} 3817gv6rcpht973n7yedk5sntn13fcv 763949 763948 2026-04-18T13:55:12Z Mewtow 31375 /* Les textures et unités de texture */ 763949 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. Les API actuelles tendent de plus en plus vers les ''''textures ''bindless''''', qui ne doivent pas être associées à une unité de texture. A la place, leur adresse est simplement fournie au ''pixel shader'' via des ''uniforms'', ou un autre mécanisme. L'adresse se retrouve simplement dans les registres du ''pixel shader'', ce qui fait qu'on n'est plus limité par le nombre d'unités de texture. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ==Les optimisations liées aux ''draw calls''== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. ===Les optimisations du ''render state''=== Une première optimisation vise à réduire les changements de ''render state''. Elle rend les objets avec le même ''render state'' ensemble, les uns à la suite des autres. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, d'autres optimisations permettent d'en profiter au mieux. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures dans une super-texture. La technique évite des changements d'état dans l'API 3D. Pas besoin de changer de texture à chaque objet, on peut partager une super-texture capable de texturer plusieurs objets différents. La création d'un atlas de texture n'est pas de tout repos, il ne suffit pas de regrouper plusieurs textures. Le filtrage de texture ne doit pas mélanger des texels provenant de deux textures différents, même si elles sont dans la même super-texture. Et il y a d'autres problèmes liés aux techniques dites de ''mip-mapping'', ainsi qu'aux modes d'adressage de texture. Pour éviter ces problèmes, les API modernes ont ajouté une fonctionnalité : les '''''texture arrays'''''. Le terme ''array'' est assez clair : il s'agit de tableaux, qui regroupent plusieurs textures. Précisons ce qu'on veut dire pas regrouper. Les textures ne sont pas placées dans une super-texture, elles ne sont pas consécutives en mémoire. Par contre, le ''texture array'' mémorise l'adresse de chaque texture. Pour accéder à une texture, il suffit de fournir son indice, sa place dans le tableau : on récupère l'adresse de la texture, qui est ensuite envoyée à l'unité de texture. ===Les optimisations des ''draw calls'' : ''batching'' et ''instancing''=== Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} nc9z5c2ibns381149qxeueee1hj2nrg 763950 763949 2026-04-18T13:58:01Z Mewtow 31375 /* Les textures et unités de texture */ 763950 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. Les API actuelles tendent de plus en plus vers les '''textures ''bindless''''', qui ne doivent pas être associées à une unité de texture. A la place, leur adresse est simplement fournie au ''pixel shader'' via des ''uniforms'', ou un autre mécanisme. L'adresse se retrouve simplement dans les registres du ''pixel shader'', ce qui fait qu'on n'est plus limité par le nombre d'unités de texture. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ==Les optimisations liées aux ''draw calls''== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. ===Les optimisations du ''render state''=== Une première optimisation vise à réduire les changements de ''render state''. Elle rend les objets avec le même ''render state'' ensemble, les uns à la suite des autres. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, d'autres optimisations permettent d'en profiter au mieux. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures dans une super-texture. La technique évite des changements d'état dans l'API 3D. Pas besoin de changer de texture à chaque objet, on peut partager une super-texture capable de texturer plusieurs objets différents. La création d'un atlas de texture n'est pas de tout repos, il ne suffit pas de regrouper plusieurs textures. Le filtrage de texture ne doit pas mélanger des texels provenant de deux textures différents, même si elles sont dans la même super-texture. Et il y a d'autres problèmes liés aux techniques dites de ''mip-mapping'', ainsi qu'aux modes d'adressage de texture. Pour éviter ces problèmes, les API modernes ont ajouté une fonctionnalité : les '''''texture arrays'''''. Le terme ''array'' est assez clair : il s'agit de tableaux, qui regroupent plusieurs textures. Précisons ce qu'on veut dire pas regrouper. Les textures ne sont pas placées dans une super-texture, elles ne sont pas consécutives en mémoire. Par contre, le ''texture array'' mémorise l'adresse de chaque texture. Pour accéder à une texture, il suffit de fournir son indice, sa place dans le tableau : on récupère l'adresse de la texture, qui est ensuite envoyée à l'unité de texture. ===Les optimisations des ''draw calls'' : ''batching'' et ''instancing''=== Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} qic01p6w3bmyggptshneh1n3imxr01n 763951 763950 2026-04-18T13:58:21Z Mewtow 31375 /* Les render states et les Pipeline State Object */ 763951 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. Les API actuelles tendent de plus en plus vers les '''textures ''bindless''''', qui ne doivent pas être associées à une unité de texture. A la place, leur adresse est simplement fournie au ''pixel shader'' via des ''uniforms'', ou un autre mécanisme. L'adresse se retrouve simplement dans les registres du ''pixel shader'', ce qui fait qu'on n'est plus limité par le nombre d'unités de texture. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction ''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ==Les optimisations liées aux ''draw calls''== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. ===Les optimisations du ''render state''=== Une première optimisation vise à réduire les changements de ''render state''. Elle rend les objets avec le même ''render state'' ensemble, les uns à la suite des autres. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, d'autres optimisations permettent d'en profiter au mieux. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures dans une super-texture. La technique évite des changements d'état dans l'API 3D. Pas besoin de changer de texture à chaque objet, on peut partager une super-texture capable de texturer plusieurs objets différents. La création d'un atlas de texture n'est pas de tout repos, il ne suffit pas de regrouper plusieurs textures. Le filtrage de texture ne doit pas mélanger des texels provenant de deux textures différents, même si elles sont dans la même super-texture. Et il y a d'autres problèmes liés aux techniques dites de ''mip-mapping'', ainsi qu'aux modes d'adressage de texture. Pour éviter ces problèmes, les API modernes ont ajouté une fonctionnalité : les '''''texture arrays'''''. Le terme ''array'' est assez clair : il s'agit de tableaux, qui regroupent plusieurs textures. Précisons ce qu'on veut dire pas regrouper. Les textures ne sont pas placées dans une super-texture, elles ne sont pas consécutives en mémoire. Par contre, le ''texture array'' mémorise l'adresse de chaque texture. Pour accéder à une texture, il suffit de fournir son indice, sa place dans le tableau : on récupère l'adresse de la texture, qui est ensuite envoyée à l'unité de texture. ===Les optimisations des ''draw calls'' : ''batching'' et ''instancing''=== Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} bd1an86eb9u7wktrrlw50n6kolgxc8b 763955 763951 2026-04-18T16:02:34Z Mewtow 31375 /* Les optimisations du render state */ 763955 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. Les API actuelles tendent de plus en plus vers les '''textures ''bindless''''', qui ne doivent pas être associées à une unité de texture. A la place, leur adresse est simplement fournie au ''pixel shader'' via des ''uniforms'', ou un autre mécanisme. L'adresse se retrouve simplement dans les registres du ''pixel shader'', ce qui fait qu'on n'est plus limité par le nombre d'unités de texture. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction ''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ==Les optimisations liées aux ''draw calls''== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. ===Les optimisations du ''render state''=== Les changements de ''render state'' sont souvent assez gourmands. Pas forcément pour le GPU, mais il y a toujours un certain cout soit au niveau de l'API, soit pour le pilote du GPU. Le cout est donc partagé entre CPU et GPU, avec un cout certain pour le CPU, très variable pour le GPU. Les changements d'état en question ne sont pas égaux non plus, certains sont plus couteux que d'autres. Pour donner quelques chiffres, je vais me baser les données obtenues par Cass Everitt et John McDonald en 2014, pour un ''driver'' OpenGL. Ils ont été présentés dans la conférence ''Beyond Porting: How Modern OpenGL Can Radically Reduce Driver Overhead'', aux ''Steam Dev Days 2014'', trouvable sur Youtube. Les données sont anciennes, les choses ont certainement évoluées. Les couts vont, du plus grand au plus petit : * changer de ''render target'' ; * changer de shader ; * reconfigurer les ROPs ; * changer de texture ; * changer la géométrie ou les variables ''uniforms''. Une première optimisation vise à réduire les changements de ''render state''. Elle rend les objets avec le même ''render state'' ensemble, les uns à la suite des autres. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, d'autres optimisations permettent d'en profiter au mieux. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures dans une super-texture. La technique évite des changements d'état dans l'API 3D. Pas besoin de changer de texture à chaque objet, on peut partager une super-texture capable de texturer plusieurs objets différents. La création d'un atlas de texture n'est pas de tout repos, il ne suffit pas de regrouper plusieurs textures. Le filtrage de texture ne doit pas mélanger des texels provenant de deux textures différents, même si elles sont dans la même super-texture. Et il y a d'autres problèmes liés aux techniques dites de ''mip-mapping'', ainsi qu'aux modes d'adressage de texture. Pour éviter ces problèmes, les API modernes ont ajouté une fonctionnalité : les '''''texture arrays'''''. Le terme ''array'' est assez clair : il s'agit de tableaux, qui regroupent plusieurs textures. Précisons ce qu'on veut dire pas regrouper. Les textures ne sont pas placées dans une super-texture, elles ne sont pas consécutives en mémoire. Par contre, le ''texture array'' mémorise l'adresse de chaque texture. Pour accéder à une texture, il suffit de fournir son indice, sa place dans le tableau : on récupère l'adresse de la texture, qui est ensuite envoyée à l'unité de texture. Pour ce qui est des ''shaders'', on peut regrouper plusieurs ''shaders'' en un seul. Par exemple, au lieu d'avoir un shader pour les sources de lumière ponctuelles et un autre pour les sources directionnelles, on peut utiliser un shader qui traite les deux. Pour cela, il suffit d'avoir une partie du shader spécialisée pour les sources ponctuelles, une autre pour les sources directionnelles. Le shader a juste à utiliser un branchement pour exécuter le bon morceau de code. Le branchement a juste à tester une variable uniforme pour faire son choix. Poussée à soin paraoxysme, le résultat est un ou plusieurs ''über shaders'', qui supporte tous les types de sources de lumières et tous les materials. Un problème des ''über shaders'' est qu'ils tendent à être moins bien optimisés, car il utilise plus de registres. Pour donner un exemple, prenons un shader qui gère à la fois les sources de lumières ponctuelles et directionnelles, avec un morceau de code pour chaque. L'un des deux morceau de code utilisera plus de registres que les autres, et le shader réservera assez de shader pour ce dernier. Ainsi, au lieu d'avoir un shader pour les sources ponctuelles qui utilise 20 registres, et un autre shader pour les sources directionnelles qui en utilise 50, on a un seul shader qui en utilise 50. Le problème est que vu que le shader utilise plus de registres, cela limite le nombre d'instances du shader lancées en simultané, et donc le masquage de la latence. ===Les optimisations des ''draw calls'' : ''batching'' et ''instancing''=== Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} e2p1s0ka9do3k1na2zv1xif3ya7cwao 763956 763955 2026-04-18T16:05:09Z Mewtow 31375 /* Les optimisations du render state */ 763956 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. Les API actuelles tendent de plus en plus vers les '''textures ''bindless''''', qui ne doivent pas être associées à une unité de texture. A la place, leur adresse est simplement fournie au ''pixel shader'' via des ''uniforms'', ou un autre mécanisme. L'adresse se retrouve simplement dans les registres du ''pixel shader'', ce qui fait qu'on n'est plus limité par le nombre d'unités de texture. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction ''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ==Les optimisations liées aux ''draw calls''== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. ===Les optimisations du ''render state''=== Les changements de ''render state'' sont souvent assez gourmands. Pas forcément pour le GPU, mais il y a toujours un certain cout soit au niveau de l'API, soit pour le pilote du GPU. Le cout est donc partagé entre CPU et GPU, avec un cout certain pour le CPU, très variable pour le GPU. Les changements d'état en question ne sont pas égaux non plus, certains sont plus couteux que d'autres. Pour donner quelques chiffres, je vais me baser les données obtenues par Cass Everitt et John McDonald en 2014, pour un ''driver'' OpenGL. Ils ont été présentés dans la conférence ''Beyond Porting: How Modern OpenGL Can Radically Reduce Driver Overhead'', aux ''Steam Dev Days 2014'', trouvable sur Youtube. Les données sont anciennes, les choses ont certainement évoluées. Les couts vont, du plus grand au plus petit : * changer de ''render target'' ; * changer de shader ; * reconfigurer les ROPs ; * changer de texture ; * changer la géométrie ou les variables ''uniforms''. Une première optimisation vise à réduire les changements de ''render state''. Elle rend les objets avec le même ''render state'' ensemble, les uns à la suite des autres. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, d'autres optimisations permettent d'en profiter au mieux. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures dans une super-texture. La technique évite des changements d'état dans l'API 3D. Pas besoin de changer de texture à chaque objet, on peut partager une super-texture capable de texturer plusieurs objets différents. La création d'un atlas de texture n'est pas de tout repos, il ne suffit pas de regrouper plusieurs textures. Le filtrage de texture ne doit pas mélanger des texels provenant de deux textures différents, même si elles sont dans la même super-texture. Et il y a d'autres problèmes liés aux techniques dites de ''mip-mapping'', ainsi qu'aux modes d'adressage de texture. Pour éviter ces problèmes, les API modernes ont ajouté une fonctionnalité : les '''''texture arrays'''''. Le terme ''array'' est assez clair : il s'agit de tableaux, qui regroupent plusieurs textures. Précisons ce qu'on veut dire pas regrouper. Les textures ne sont pas placées dans une super-texture, elles ne sont pas consécutives en mémoire. Par contre, le ''texture array'' mémorise l'adresse de chaque texture. Pour accéder à une texture, il suffit de fournir son indice, sa place dans le tableau : on récupère l'adresse de la texture, qui est ensuite envoyée à l'unité de texture. Pour ce qui est des ''shaders'', on peut regrouper plusieurs ''shaders'' en un seul. Par exemple, au lieu d'avoir un shader pour les sources de lumière ponctuelles et un autre pour les sources directionnelles, on peut utiliser un shader qui traite les deux. Pour cela, il suffit d'avoir une partie du shader spécialisée pour les sources ponctuelles, une autre pour les sources directionnelles. Le shader a juste à utiliser un branchement pour exécuter le bon morceau de code. Le branchement a juste à tester une variable uniforme pour faire son choix. Poussée à soin paroxysme, le résultat est un ou plusieurs '''''über shaders''''', qui supporte tous les types de sources de lumières et tous les ''materials''. Un problème des ''über shaders'' est qu'ils tendent à être moins bien optimisés, car il utilise plus de registres. Pour donner un exemple, prenons un shader qui gère à la fois les sources de lumières ponctuelles et directionnelles, avec un morceau de code pour chaque. L'un des deux morceau de code utilisera plus de registres que les autres, et le shader réservera assez de shader pour ce dernier. Ainsi, au lieu d'avoir un shader pour les sources ponctuelles qui utilise 20 registres, et un autre shader pour les sources directionnelles qui en utilise 50, on a un seul shader qui en utilise 50. Le problème est que vu que le shader utilise plus de registres, cela limite le nombre d'instances du shader lancées en simultané, et donc le masquage de la latence. ===Les optimisations des ''draw calls'' : ''batching'' et ''instancing''=== Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} ego79hz3qoa2xut6oiyiceia42swidm 763957 763956 2026-04-18T16:08:55Z Mewtow 31375 /* Les optimisations du render state */ 763957 wikitext text/x-wiki De nos jours, le développement de jeux vidéo, ou tout simplement de tout rendu 3D, utilise des API 3D. Les API 3D les plus connues sont DirectX, OpenGL, et Vulkan. L'enjeu des API est de ne pas avoir à recoder un moteur de jeu différent pour chaque carte graphique ou ordinateur existant. Elles fournissent des fonctions qui effectuent des calculs bien spécifiques de rendu 3D, mais pas que. L'application de rendu 3D utilise des fonctionnalités de ces API 3D, qui elles-mêmes utilisent les autres intermédiaires, les autres maillons de la chaîne. Typiquement, ces API communiquent avec le pilote de la carte graphique et le système d'exploitation. ==La description des API 3D les plus communes== Dans ce chapitre, nous n'allons pas faire de cours du DirextX, ulkan ou toute API précise. Toutes le API graphiques fonctionnent globalement sur les mêmes principes, que nous allons expliquer dans les grandes lignes. Les explications seront conçues pour que les personnes sans bagage de la programmation graphique puissent comprendre, seuls desbases très mineures en programmation seront nécessaires dans le pire des cas. ===Les ''draw calls''=== Une API 3D fournit un certain nombre de fonctions qu'un programmeur peut exécuter à loisir. La principale est la fonction qui dessine quelque chose dans le ''framebuffer''. Elle est appelée ''draw()'' dans la terminologie DirectX, gldraw pour OpenGL, vkcmddraw pour Vulkan. Une exécution de cette fonction est appelée un '''''draw call'''''. Un ''draw call''envoie des informations à la carte graphique, afin qu'elle affiche ce qui est demandé. Instinctivement, on pourrait croire que la fonction ''draw'' calcule tout l'image à afficher d'un seul coup, mais ce n'est pas le cas. En réalité, le moteur graphique d'un jeu effectue le rendu objet par objet, avec un ''draw call'' par objet. Plus il y a d'objets, plus le processeur exécutera de ''draw calls''. Diverses optimisations permettent d'économiser des ''draw calls'', mais cela ne change pas le fait que dessiner l'image finale demande plusieurs ''draw calls'', entre une centaine et plusieurs centaines de milliers suivant la complexité de la scène à rendre. Le fait de rendre une image objet par objet permet de nombreuses optimisations. Par exemple, il peut utiliser une première passe pour dessiner les objets opaques, puis une seconde pour les objets transparents. Tous les moteurs 3D font ainsi, car gérer la transparence est toujours compliqué, surtout avec un tampon de profondeur. Un autre avantage est que le moteur de jeu peut faciliter le travail de l'élimination des surfaces cachées. Par exemple, le moteur de jeu peut trier les objets selon leur profondeur, afin de les rendre du plus proche au plus lointain. Pour les objets opaques, cela permet d'éliminer les surfaces cachées à la perfection : aucun triangle/pixel caché par un autre ne sera rendu. Pour la transparence, cela permet un rendu idéal. Mais trier les objets selon leur profondeur prend alors du temps CPU, qu'il faut comparer à ce qui est gagné sur le GPU. Avant les années 2010 environ, le processeur faisait une bonne partie de l'élimination des surfaces cachées, dans le sens où il déterminait quels objets étaient cachés par d'autres. Il n'émettait pas de ''draw calls'' pour les objets complétement cachés par un autre objet opaque. Par contre, il travaillait au niveau des objets, alors que le GPU travaillait au niveau des triangles. Les objets partiellement cachés étaient gérés par le GPU, avec une élimination des surface cachées triangle par triangle. De nos jours, l'élimination des surfaces cachées est réalisée sur le GPU, dans sa totalité. L'idée est d'utiliser un ''shader'' séparé, un ''compute shader'', qui s'exécute avant toute autre opération de rendu. La scène 3D et tous les modèles sont dans la mémoire vidéo, et non en mémoire RAM. Le ''compute shader'' lit l'ensemble de la géométrie et élimine les surface cachées. On parle de '''''GPU driven rendering''''' pour désigner cette élimination des surfaces cachées réalisée sur le GPU (il faudrait aussi rajouter le choix du ''Level Of Detail'', mais passons. ===Les ''render target''=== Plus haut, j'ai dit qu'un ''draw call'' dessine une image dans le ''framebuffer''. Et il s'agit là du cas le plus important, mais certaines techniques de rendu demandent de dessiner des images intermédiaires, qui sont utilisées pour calculer l'image finale. Les images intermédiaires doivent alors être enregistrées ailleurs, par exemple dans une texture. L'idée générale d'enregistrer des images intermédiaires dans une texture, qui sont alors lues par un ''pixel shader'' pour des calculs d'éclairage, des filtres de post-traitement, ou autre. Autoriser d'enregistrer l'image finale dans une texture s'appelle du '''''render-to-texture'''''. Les techniques d'éclairage basées sur des ''shadowmap'' sont dans ce cas. Elles demandent de rendre la scène 3D deux fois : une fois du point de vue de la source de lumière, puis une seconde fois pour obtenir l'image finale. L'idée est que les pixels invisibles depuis la source de lumière, mais visibles depuis la caméra, sont dans l'ombre. La scène rendue depuis la caméra doit donc être mémorisée quelque part, de préférence dans une texture appelée une ''shadowmap''. Une autre utilisation est l'application de filtres de post-traitement, comme du bloom, de la profondeur de champ, etc. L'idée est de mémoriser l'image initiale, sans post-traitement, dans une texture. Puis, un ''shader'' lit cette texture, applique un filtre dessus, et mémorise le résultat dans une autre texture ou dans le ''framebuffer'' s'il calcule l'image finale. Pour cela, les API 3D modernes permettent de préciser où enregistrer l'image finale : dans le ''framebuffer'', dans une texture, dans une simple portion de mémoire, etc. Les endroits où l'image finale peut être rendue s'appellent des '''''render target'''''. Les API modernes supportent de nombreux ''render target'', avec au minimum un ''framebuffer''. Initialement, les API anciennes ne supportaient que le ''framebuffer''. Puis le ''render-to-texture'' est apparu, puis d'autres formes de ''render target''. ===Les attributs de sommets, variables uniformes et unités de texture=== Lors d'un ''draw call'', certains paramètres vont rester constants, alors que d'autres vont varier d'un sommet à l'autre. Les paramètres qui varient d'un sommet à l'autre sont des '''attributs de sommet'''. Par exemple, prenons un sommet : sa position, sa couleur et ses coordonnées de texture sont des attributs du sommet. Les paramètres constants sont appelées des '''variables uniformes''', ou encore des ''uniforms''. Elles restent les mêmes pour un objet, mais varient d'un objet à l'autre. Un exemple est les matrices utilisées par les étapes de transformation et de projection. : Il y a la même chose avec les pixels, avec des attributs de pixels et des ''pixel uniforms'', la différence étant que les attributs de pixels sont calculés par la rastérisation. Les deux sont stockés différemment : les variables uniformes sont simplement intégrées dans les shaders, alors que les attributs sont placés dans le tampon de sommets. Il faut noter que les processeurs de shaders avaient autrefois des registres séparés pour les deux, et c'est toujours un peu le cas à l'heure actuelle. ===Les textures et unités de texture=== Les API OpenGL et DirectX gèrent les textures d'une manière assez différente de ce qui se fait en matériel. Les API partent du principe que le GPU a plusieurs unités de textures, qui sont numérotées de 0 à N. Les textures doivent être associées à une unité de texture avant du ''draw call''. En clair, on doit dire que telle texture doit aller dans l'unité de texture numéro 0, telle autre texture dans l'unité de texture numéro 1, etc. Les anciennes API ne géraient qu'une seule texture, mais l'arrivée du multitexturing a imposé d'ajouter des unités de texture en plus. Une texture est placée en mémoire vidéo et on connait son adresse, sa position en mémoire. En théorie, l'unité de texture a juste besoin de connaitre l'adresse de la texture, ainsi que sa résolution, sa taille en mémoire VRAM, et quelques autres détails. L'ensemble de ces informations est appelé le ''texture state''. L'unité de texture mémorise le ''texture state'' dans des registres internes, et tout accès à une texture utilise le ''texture state'' pour lire le bon texel. En associant une texture a une unité de texture, on copie ces informations dans les registres de l'unité de texture. En changeant de texture, on modifie le contenu de ces registres internes. Les anciennes cartes graphiques fonctionnaient sur ce principe, elles avaient bien des unités de texture avec des registres pour mémoriser un ''texture state''. Le problème est que les GPU ne fonctionnent pas exactement sur ce principe. L'unité de texture des GPU modernes est intégrée dans les processeurs de shaders, ce qui fait qu'attribuer une texture à une unité de texture n'est pas l'idéal. De plus, les unités de textures actuelles sont ''bindless'' : elles n'utilisent pas de registres internes, elles ne mémorisent pas de ''texture state''. A la place, le ''texture state'' est mémorisé dans les registres du processeur de shader. A chaque accès, le ''texture state'' est envoyé à l'unité de texture. Il faut dire que les unités mémoire des processeurs fonctionnent sur ce principe : on leur envoie une adresse, un indice et d'autres informations ; et elles effectuent l'accès mémoire. Les API actuelles tendent de plus en plus vers les '''textures ''bindless''''', qui ne doivent pas être associées à une unité de texture. A la place, leur adresse est simplement fournie au ''pixel shader'' via des ''uniforms'', ou un autre mécanisme. L'adresse se retrouve simplement dans les registres du ''pixel shader'', ce qui fait qu'on n'est plus limité par le nombre d'unités de texture. ===Les ''render states'' et les ''Pipeline State Object''=== Pour rendre un objet avec un ''draw call'', il faut préciser toutes informations nécessaires pour son rendu : la géométrie de l'objet représentée par une liste de triangles, les textures de l'objet, les ''shaders'' à exécuter (vertex ou pixel shaders), etc. Pour simplifier, nous allons regrouper ces informations en deux : un ''mesh'' qui représente la géométrie de l'objet, et le reste. La géométrie de l'objet est juste une liste de triangles. Le reste est regroupé dans un '''''render state''''', qui liste les textures, les shaders, quel ''render starget'' utiliser, et surtout : diverses options de configuration. Il n'y a qu'un seul ''render state'' actif, qui est mémorisé dans une portion de la RAM qui est toujours fixe. Pour les programmeurs, le ''render state'' est dans une variable globale, qui est lue directement par la fonction ''draw''. Si on veut rendre un objet, on doit mettre à jour le ''render state'' avant de lancer un ''draw call''. Un moteur graphique fait donc le travail suivant : * Pour chaque image : ** Mettre à jour la position de la caméra et autres ** Pour chaque objet, scène 3D inclue : *** 1 - Mettre à jour le ''render state'' *** 2 - Exécuter le ''draw call'' L'API 3D fournit des fonctions pour modifier le ''render state'', en plus de la fonction ''draw''. A ce niveau, les anciennes API fonctionne différemment des API plus récentes comme DirectX 12, Vulkan et consort. Les anciennes API fournissaient plusieurs fonctions très spécialisées : certaines pour modifier les textures, d'autres pour changer les shaders, et un paquet d’autres pour modifier telle ou telle option de configuration. Par exemple, il y a probablement une fonction pour changer l'antialiasing. Les API modernes, comme DirectX 12 et Vulkan, permettent de mettre à jour le ''render state'' assez simplement. L'idée est de pré-calculer un ''render state'', qui est alors appelé un ''Pipeline State Object'' (PSO). Mettre à jour le ''render state'' demande alors juste de copier un PSO dans le render state, au lieu d'exécuter une dizaine ou centaine de fonctions pour obtenir le ''render state'' voulu. ===Les commandes graphiques=== L'API 3D traduit chaque ''draw call'' en une ou plusieurs '''commandes graphiques''', qui sont envoyées au ''driver'' du GPU. Les commandes en question sont assez diverses, mais elles sont spécifiques à chaque API graphique. Intuitivement, un ''draw call'' correspond à une commande graphique. Mais il peut y avoir d'autres types de commandes. Par exemple, copier une texture dans la mémoire vidéo demande d'exécuter une commande decopie, idem pour ce qui est de copier un objet/''mesh''. Pour comprendre en quoi un ''draw call'' peut se traduire en plusieurs commandes, prenons l'exemple suivant. On souhaite rendre un objet avec une texture bien précise, mais celle-ci n'a pas encore été chargée en mémoire vidéo. Dans ce cas, le ''draw call'' utilisera une commande pour copier la texture en mémoire vidéo, puis une seconde commande pour rendre l'objet dans le ''framebuffer''. Par contre, si la texture est déjà en mémoire vidéo, le ''draw call'' se traduira en une unique commande de rendu 3D. Il en est de même si le ''mesh'' n'est pas encore en mémoire vidéo : il faut exécuter une commande pour copier le ''mesh'' dans la mémoire vidéo. Il faut préciser que c'est la même chose si le ''draw call'' exécute un shader pour la première fois . Le ''driver'' doit compiler le shader pour la première fois, puis utiliser une commande pour mettre le résultat en mémoire vidéo, puis enfin effectuer le rendu. Cela explique le ''shader stuttering'' présent dans certains jeux récents, à savoir le petit ralentissement très énervant qui survient quand un ''shader'' est compilé en plein milieu d'une partie de jeu. Il est possible de limiter ce problème en compilant des shaders à l'avance, histoire de préparer le terrain pour les futurs ''draw calls'', dans une certaine mesure, mais cela demande du travail, qui n'est possible que le nombre de shaders à compiler reste faible. Les commandes graphiques sont envoyées au ''driver'' de la carte graphique. Il transforme alors ces commandes graphiques en '''commandes matérielles''', compréhensibles par le matériel, en quelque chose que le GPU peut exécuter. Le format des commandes matérielles est spécifique à chaque marquer de GPU, les GPU NVIDIA, Intel et AMD n'utilisent pas le même format de commande. Il est même possible que chaque GPU ait son propre format pour les commandes matérielles. Aussi, nous allons nous arrêter là pour le moment et laissons cela au chapitre sur le processeur de commande. ==Les optimisations liées aux ''draw calls''== Il faut noter qu'un ''draw call'' demande d'utiliser un peu de puissance CPU : il faut traduire le ''draw call'' en commandes, les envoyer au ''driver'', qui fait du travail dessus, avant de les envoyer au GPU. Dans les premières versions d'OpenGL et DirectX, chaque ''draw call'' effectuait une commutation de contexte pour passer en espace noyau, afin de communiquer avec le ''driver''. Mais cette contrainte a depuis été relâchée, bien qu'elle marche dans les grandes lignes. Faire plein de ''draw calls'' aura donc un cout en CPU conséquent. ===Les optimisations du ''render state''=== Les changements de ''render state'' sont souvent assez gourmands. Pas forcément pour le GPU, mais il y a toujours un certain cout soit au niveau de l'API, soit pour le pilote du GPU. Le cout est donc partagé entre CPU et GPU, avec un cout certain pour le CPU, très variable pour le GPU. Les changements d'état en question ne sont pas égaux non plus, certains sont plus couteux que d'autres. Pour donner quelques chiffres, je vais me baser les données obtenues par Cass Everitt et John McDonald en 2014, pour un ''driver'' OpenGL. Ils ont été présentés dans la conférence ''Beyond Porting: How Modern OpenGL Can Radically Reduce Driver Overhead'', aux ''Steam Dev Days 2014'', trouvable sur Youtube. Les données sont anciennes, les choses ont certainement évoluées. Les couts vont, du plus grand au plus petit : * changer de ''render target'' ; * changer de shader ; * reconfigurer les ROPs ; * changer de texture ; * changer la géométrie ou les variables ''uniforms''. Une première optimisation vise à réduire les changements de ''render state''. Elle rend les objets avec le même ''render state'' ensemble, les uns à la suite des autres. Sans cette optimisation, le moteur graphique met à jour le ''render state'' à chaque fois qu'il rend un objet. Avec cette optimisation, il met à jour le ''render state'' plus rarement. Par contre, le moteur graphique dépense du temps et de la puissance de calcul pour faire le tri. Il y a donc un compromis pas évident, qui ne vaudrait pas souvent le coup. Cependant, d'autres optimisations permettent d'en profiter au mieux. Une optimisation de ce type est l'usage d''''atlas de textures'''. Un atlas de texture regroupe plusieurs textures dans une super-texture. La technique évite des changements d'état dans l'API 3D. Pas besoin de changer de texture à chaque objet, on peut partager une super-texture capable de texturer plusieurs objets différents. La création d'un atlas de texture n'est pas de tout repos, il ne suffit pas de regrouper plusieurs textures. Le filtrage de texture ne doit pas mélanger des texels provenant de deux textures différents, même si elles sont dans la même super-texture. Et il y a d'autres problèmes liés aux techniques dites de ''mip-mapping'', ainsi qu'aux modes d'adressage de texture. Pour éviter ces problèmes, les API modernes ont ajouté une fonctionnalité : les '''''texture arrays'''''. Le terme ''array'' est assez clair : il s'agit de tableaux, qui regroupent plusieurs textures. Précisons ce qu'on veut dire pas regrouper. Les textures ne sont pas placées dans une super-texture, elles ne sont pas consécutives en mémoire. Par contre, le ''texture array'' mémorise l'adresse de chaque texture. Pour accéder à une texture, il suffit de fournir son indice, sa place dans le tableau : on récupère l'adresse de la texture, qui est ensuite envoyée à l'unité de texture. Pour ce qui est des ''shaders'', on peut regrouper plusieurs ''shaders'' en un seul. Par exemple, au lieu d'avoir un shader pour les sources de lumière ponctuelles et un autre pour les sources directionnelles, on peut utiliser un shader qui traite les deux. Pour cela, il suffit d'avoir une partie du shader spécialisée pour les sources ponctuelles, une autre pour les sources directionnelles. Le shader a juste à utiliser un branchement pour exécuter le bon morceau de code. Le branchement a juste à tester une variable uniforme pour faire son choix. Poussée à soin paroxysme, le résultat est un ou plusieurs '''''über shaders''''', qui supporte tous les types de sources de lumières et tous les ''materials''. Un problème est que les ''über shaders'' ont une grande taille, ce qui fait qu'ils peuvent déborder du cache d'instruction. Un autre problème des ''über shaders'' est qu'ils tendent à être moins bien optimisés, car il utilise plus de registres. Pour donner un exemple, prenons un shader qui gère à la fois les sources de lumières ponctuelles et directionnelles, avec un morceau de code pour chaque. L'un des deux morceau de code utilisera plus de registres que les autres, et le shader réservera assez de shader pour ce dernier. Ainsi, au lieu d'avoir un shader pour les sources ponctuelles qui utilise 20 registres, et un autre shader pour les sources directionnelles qui en utilise 50, on a un seul shader qui en utilise 50. Le problème est que vu que le shader utilise plus de registres, cela limite le nombre d'instances du shader lancées en simultané, et donc le masquage de la latence. ===Les optimisations des ''draw calls'' : ''batching'' et ''instancing''=== Plus haut, j'ai dit que le rendu se fait objet par objet, ''mesh'' par ''mesh''. Mais il s'agit là d'une simplification. En réalité, tout moteur graphique digne de ce nom incorpore des optimisations qui cassent cette règle. L'idée est d'éviter de faire plein de petits ''draw call'' : le GPU sera alors peu utilisé alors que le CPU fera beaucoup de travail. A l'inverse, faire peu de gros ''draw call'' entrainera une forte occupation du GPU au prix d'un cout CPU mineur. La première optimisation, appelée le '''''batching''''', regroupe plusieurs objets/''meshs'' en un seul ''draw call''. Par contre, cette optimisation ne marche que pour des objets ayant le même ''render state'', à l'exception de la géométrie. Les deux objets rendus ensemble doivent utiliser les mêmes shaders, les mêmes textures, etc. De plus, la fusion de deux objets doit se faire en mémoire RAM et est le fait du CPU, le GPU et la mémoire vidéo ne sont pas concernés. L'optimisation marche bien pour des objets statiques, ce qui permet de faire la fusion une fois pour toute, là où les objets dynamiques demandent de faire la fusion à chaque image. Une seconde optimisation,appelée l''''''instancing''''', marche dans le cas où un objet dynamique est présent en plusieurs exemplaires à l'écran. L'idée est qu'au lieu d'utiliser un ''draw call'' par exemplaire, on utilise un seul ''draw call'' pour tous les exemplaires. L'avantage est que la carte n'a besoin de mémoriser qu'un seul exemplaire en mémoire vidéo, au lieu de mémoriser plusieurs copies du même ''mesh''. Il faut préciser que les différents exemplaires peuvent être placés à des endroits éloignés, être tournés différemment par rapport à la caméra, être dans des états d'animation différents, etc. Pour cela, le ''draw call'' précise, pour chaque exemplaire, comment l'orienter, le tourner et l'animer. Le ''render state'' contient pour cela une '''liste d'instances''' pour mémoriser ces informations pur chaque exemplaire. Le GPU peut consulter cette liste et la copier en mémoire vidéo. Une seule commande permet ainsi de rendre plusieurs exemplaires : le GPU lit la liste d'instance, le ''mesh'' et dessine automatiquement chaque exemplaire voulu de l'objet. Réduire le nombre de ''draw calls'' peut aussi se faire en évitant les objets peu détaillés, qui utilisent peu de polygones. Pour des objets trop peu détaillés, le GPU exécutera le ''draw call'' très vite et devra attendre que le CPU envoie le suivant. Le cout du ''draw call'' dominera le temps de calcul sur le GPU. Du temps de DirectX 9, l'idéal était d'avoir des objets d'au moins une centaine de triangles. De nos jours, les GPU les CPU sont plus puissant,ce qui fait que ce chiffre est à revoir, mais je n'en connais pas la valeur, même approximative. ==Le pipeline graphique== En plus de fournir des fonctions que les programmeurs peuvent utiliser, les API graphiques décrivent comment s'effectue le rendu d'une image. Elles spécifient comment doit être traité la géométrie, comment doit se faire la rastérisation, le filtrage de texture et bien d'autres choses. Pour le dire autrement, elles décrivent le pipeline graphique à utiliser. Pour rappel, le pipeline graphique comprend plusieurs étapes : plusieurs étapes de traitement de la géométrie, une phase de rastérisation, puis plusieurs étapes de traitement des pixels. Une API 3D comme DirectX ou OpenGl décrète quelles sont les étapes à faire, ce qu'elles font, et l'ordre dans lesquelles il faut les exécuter. Il n'existe pas un pipeline graphique unique et chaque API 3D fait à sa sauce, mais la plupart des API modernes ont des pipelines graphiques très similaires. Les seules différences majeures concernent la présence d'étapes facultatives, comme l'étape de tesselation, qui sont absentes des API anciennes. Pour donner un exemple, je vais prendre l'exemple d'OpenGL 1.0, une des premières version d'OpenGL, aujourd'hui totalement obsolète. Le pipeline d'OpenGL 1.0 est illustré ci-dessous. Il implémente le pipeline graphique de base, avec une phase de traitement de la géométrie (''per vertex operations'' et ''primitive assembly''), la rastérisation, et les traitements sur les pixels (''per fragment operations''). On y voit la présence du ''framebuffer'' et de la mémoire dédiée aux textures, les deux étant soit séparées, soit placée dans la même mémoire vidéo. La ''display list'' est une liste de commandes, de ''draw calls'', que la carte graphique doit traiter d'un seul bloc, chaque ''display list'' correspond au rendu d'une image, pour simplifier. Les étapes ''evaluator'' et ''pixel operations'' sont des étapes facultatives, qui ne sont pas dans le pipeline graphique de base, mais qui sont utiles pour implémenter certains effets graphiques. [[File:Pipeline OpenGL.svg|centre|vignette|upright=2|Pipeline d'OpenGL 1.0]] Le pipeline d'OpenGL 1.0 vu plus haut est très simple, comparé aux pipelines des API modernes. Pour comparaison, voici des schémas qui décrivent le pipeline de DirextX 10 et 11. Vous voyez que le nombre d'étapes n'est pas le même, que les étapes elles-mêmes sont légèrement différentes, etc. Toutes les API 3D modernes sont organisées plus ou moins de la même manière, ce qui fait que le pipeline des schémas ci-dessous colle assez bien avec les logiciels 3D anciens et modernes, ainsi qu'avec l'organisation des cartes graphiques (anciennes ou modernes). {| | style="vertical-align:top;" | [[File:D3D Pipeline.svg|vignette|D3D Pipeline]] |[[File:D3D11 Pipeline.svg|vignette|Pipeline de D3D 11]] |} ===L'implémentation peut être logicielle ou matérielle=== Une API graphique est avant tout quelque chose qui aide le programmeur. Il est d'ailleurs possible de les utiliser sans GPU, avec une simple carte d'affichage. Le rendu 3D se fait alors sur le processeur, et la carte d'affichage ne fait que recevoir l'image calculée et l'afficher. Et c'était le cas dans les années 90, avant l'invention des premières cartes accélératrices 3D. Le rôle des API 3D était de fournir des morceaux de code et un pipeline graphique, afin de simplifier le travail des développeurs, pas de déporter des calculs sur une carte accélératrice 3D. D'ailleurs, OpenGl et Direct X sont apparues avant que les premières cartes graphiques grand public soient inventées. Les premiers accélérateurs 3D sont arrivés sur le marché quelques mois après la toute première version de Direct X et Microsoft n'avait pas prévu le coup. OpenGL était lui encore plus ancien et ne servait pas initialement pour les jeux vidéos, mais pour la production d'images de synthèses et dans des applications industrielles (conception assistée par ordinateur, imagerie médicale, autres). OpenGL était l'API plébiscitée à l'époque, car elle était déjà bien implantée dans le domaine industriel, la compatibilité avec les différents OS de l'époque était très bonne, mais aussi car elle était assez simple à programmer. De nos jours, la grosse majorité du rendu 3D se fait sur le GPU. Les ''draw calls'' sont intégralement traités par le GPU, à quelques détails près. Mais les premières cartes accélératrices 3D ne le gérait que partiellement. Concrétement, les premières cartes de 3Dfx déléguaient le traitement de la géométrie au processeur, et ne s'occupaient que des étapes de rastérisation, de placage de texture et les étapes suivantes. Autant prévenir maintenant, nous verrons de nombreuses cartes graphiques de de genre dans le chapitre sur l'historique de l'accélération 3D. ===Les API imposent des contraintes sur le matériel=== Les API graphiques décrivent un pipeline, mais fournissent aussi d'autres contraintes. Par exemple, elles fournissent des régles sur la manière dont doit être faite la rastérisation. Elle disent plus ou moins quel doit être le résultat attendu par le programmeur. Et les GPU doivent respecter ces règles, ils doivent effectuer le rendu de manière à avoir un résultat identique à celui spécifié par l'API. Notez ma formulation quelque peu alambiquée, qui cache un point important : les GPU font comme si ! Je dis faire comme si, car il se peut que le matériel fasse autrement, mais pour un résultat identique. Tant que l'image finale est celle attendue par l'API 3D, le GPU a le droit de prendre des raccourcis, d'éliminer des calculs inutiles, d'utiliser un algorithme de rastérisation différent, etc. Par exemple, il arrive que la carte graphique fasse certaines opérations en avance, comparé au pipeline imposé par l'API, pour des raisons de performance. Typiquement, effectuer du ''culling'' ou les tests de profondeur plus tôt permet d'annuler de nombreux pixels invisibles à l'écran, et donc d'éliminer beaucoup de calculs inutiles. Mais la carte graphique doit cependant corriger le tout de manière à ce que pour le programmeur, tout se passe comme l'API 3D l'ordonne. De manière générale, sans même se limiter à l'ordonnancement des étapes du pipeline graphique, les règles imposées par les API 3D sont des contraintes fortes, qui contraignent les cartes graphiques dans ce qu'elles peuvent faire. De nombreuses optimisations sont rendues impossibles à cause des contraintes des API 3D. ==Le pilote de carte graphique== Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre les API 3D et la carte graphique. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D très basique, juste assez pour afficher l’interface de base du système d'exploitation. Par exemple, certaines résolutions ne sont pas disponibles et les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent. Le pilote de la carte graphique gère beaucoup de choses. Comme tout pilote de périphérique, il gère la communication entre procersseur et GPU, via des techniques communes comme les interruptions, le ''pooling'' ou le ''DMA''. Plus évident, il s'occupe de la gestion de la mémoire vidéo, à savoir que c'est lui qui place les textures ou les modèles 3D dedans, il place le ''framebuffer'', les ''render target'' et tout ce qui réside en mémoire vidéo. Il s'occupe aussi des fonctionnalités liées à l'affichage : initialiser la carte graphique, fixer la résolution, le taux de rafraichissement, gérer le curseur de souris matériel, etc. Mais surtout, le pilote de périphérique s'occupe de l'exécution des ''draw call'' et des changements de ''render state''. Dans ce qui suit, nous allons nous intéresser aux fonctionnalités spécifiques au rendu 3D. ===Les commandes matérielles, compréhensibles par le GPU=== Pour rappel, les API 3Denvoient des '''commandes graphiques''' au pilote de périphérique. Les commandes graphiques sont standardisées, spécifiques à chaque API, et surtout : indépendantes du matériel. Le matériel ne comprend pas ces commandes graphiques ! A la place, le GPU comprend des '''commandes matérielles''', spécifiques à chaque marque de GPU, si ce n'est à chaque GPU. Lors du passage à une nouvelle génération de GPU, des commandes matérielles peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc. Le pilote de la carte graphique doit convertir les commandes graphiques de l'API 3D, en commandes matérielles que le GPU peut comprendre. : La traduction des commandes se fait dans le pilote en espace utilisateur, alors que leur envoi au GPU est le fait du pilote en espace noyau. L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données dans une '''file de commandes''', où les commandes matérielles attendent leur tour, dans l'ordre d'arrivée. Elle est placée soit dans une portion de la mémoire vidéo, soit est dans la mémoire RAM. Si la file de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Une file de commandes pleine est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que la file de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment. ===La compilation des ''shaders''=== Le pilote de carte graphique traduit les ''shaders'' en code machine que le GPU peut exécuter. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, avant d'être pré-compilés vers un langage dit intermédiaire. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', alors que la seconde est le fait du pilote du GPU. L'avantage est que la compilation prend moins de temps, comparé à compiler directement du code HLSL/GLSL. Le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution. Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire. Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Les anciennes cartes graphiques avaient des circuits de T&L pour traiter la géométrie, mais elles ont disparues sur les machines récentes. Par souci de compatibilité, les circuits de T&L doivent être émulés sur les GPU récents. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'' fournit par le pilote de carte graphique. {{NavChapitre | book=Les cartes graphiques | prev=La mémoire unifiée et la mémoire vidéo dédiée | prevText=La mémoire unifiée et la mémoire vidéo dédiée | next=Le processeur de commandes | nextText=Le processeur de commandes }}{{autocat}} 349ogw8aaigbx0obgb1h4koolk3vuj7 Les cartes graphiques/Les écritures en VRAM hors ROPs 0 83807 763958 763931 2026-04-18T16:16:36Z Mewtow 31375 /* L'éclairage différé */ 763958 wikitext text/x-wiki Le chapitre précédent nous a expliqué comment les ROPs mettaient à jour le ''framebuffer'' et le tampon de profondeur. Cependant, les ROPs ne sont pas les seuls à écrire en mémoire vidéo. Les ''compute shaders'', ''vertex shaders'' et ''pixel shaders'' peuvent écrire des données en mémoire vidéo, dans des tableaux dédiés. Et n'allez pas croire que c'est un petit sujet. La majeure partie des jeux vidéos actuels utilisent des techniques de rendu différé qui ne fonctionneraient pas sans cela ! Et ne parlons pas des ''compute shaders'', qui ne serviraient à rien sans cela. Dans ce chapitre, nous allons détailler comment se font ces écritures en mémoire. : Notons que nous mettons de côté les architectures ''sort-middle'' et les GPU à rendu en ''tile'' dans ce chapitre. ==Les fonctionnalités de ''Multiple Render Targets''== Il faut noter que les API modernes permettent à un ''pixel shader'' d'écrire dans plusieurs ''render-target''. On parle alors de '''''Multiple Render Targets''''', abrévié en MRT. L'implémentation classique est que le ''pixel shader'' peut écrire son résultat dans une ou plusieurs textures. Précisons qu'il s'agit bien du ''pixel shader'' qui écrit dans une texture. Suivant le GPU, l'écriture dans les ''render target'' peut ou non passer par les ROPs. Sur les anciennes versions de DirectX, un seul ''render target'' passait par les ROPs, mais pas les autres. De nos jours, tous les ''render target'' passent par les ROPs. Il est même possible de configurer le mélange ''alpha'' différemment suivant le ''render target''. [[File:MultiRenderTarget.svg|centre|vignette|upright=3|''Multiple Render Target''.]] ===L'éclairage différé=== Le MRT accélère fortement les techniques d''''éclairage différé''', qui séparent les calculs d'éclairage par pixel dans une passe séparée du reste. Ils fonctionnent en deux passes, au minimum. La première calcule toutes les informations nécessaires pour les calculs d'éclairage par pixel. Elle calcule les normales, la couleur diffuse, de quoi calculer les réflexions spéculaires, et d'autres informations du genre. Le tout est mémorisé dans plusieurs "textures" séparées, qui sont lues par un '''pixel shader''' pour obtenir l'image finale. On parle aussi de rendu différé au lieu d'éclairage différé, mais c'est un détail. La première passe calcule la couleur de chaque pixel, la profondeur de chaque pixel, et la normale de la surface pour chaque pixel. Dans l'implémentation la plus simple, ces informations sont écrites dans trois "textures" : une qui contient la couleur diffuse, une autre pour la profondeur, et une autre pour les normales. Il faut alors supporter des pseudo-''framebuffer'' pour chaque "texture". Ils sont appelés des '''''G-buffer''''', ce qui est l'abréviation de tampon géométrique. {| |[[File:Deferred rendering pass col.jpg|thumb|''G-buffer'' pour la couleur.]] |[[File:Deferred rendering pass dep.jpg|thumb|''G-buffer'' pour la profondeur.]] |[[File:Deferred rendering pass nor.jpg|thumb|''G-buffer'' pour les normales.]] |[[File:Deferred rendering pass res.jpg|thumb|Image finale]] |} L'intérêt de l'éclairage différé est d'exécuter le ''pixel shader'' une fois par source de lumière et par pixel. Il y a donc un ''draw call'' par source de lumière, qui lit les ''g-buffer''. La conséquence est que le rendu différé utilise généralement un shader par type de sources de lumières, qu'elles soient ponctuelles, directionnelles, etc. Pour comparer, sans éclairage différé, l'éclairage est réalisé autrement. L'implémentation la plus simple fait les calculs d'éclairage une fois par source et lumière et par objet, avec un ''draw call'' par objet et par source de lumière. On parle alors de ''''rendu direct multi-passe''''. La géométrie d'un objet est donc recalculée plusieurs fois, une fois par source de lumière. Par contre, le ''pixel shader'' est généralement assez simple, car on peut utiliser un ''shader'' différent pour les lumières ponctuelles, directionnelles, etc. Mieux que ça : on a tendance à avoir un ''shader'' par ''material'', vu qu'on a généralement un seul ''material'' par objet, les exceptions étant rares. Le cout en termes de performances est alors le suivant : des ''pixels shaders'' très spécialisés et optimisés, mais des calculs géométriques redondants. Une implémentation plus optimisée utilise un seul ''draw call'' par objet. Le ''draw call'' reçoit une liste des sources de lumière. Le ''pixel shader'' lit les sources de lumière une par une dans cette liste, et calcule l'éclairage une source de lumière à la fois, jusqu'à obtenir la couleur finale du pixel. La différence est qu'on a un ''draw call'' par objet, et non un par objet et source de lumière. On parle alors de '''rendu direct simple passe'''. Les calculs géométriques redondants sont éliminés, mais le ''pixel shader'' perd sa spécialisation. Au lieu d'utiliser plusieurs ''pixel shaders'' spécialisés, on utilise un ''über shader'' qui supporte tous les types de sources de lumières et tous les ''materials''. La conséquence est qu'un ''über shader'' tend à être moins bien optimisé, car il utilise plus de registres. Cela est cependant compensé par le fait que le ''pixel shader'' fait peu d'accès mémoire, comparé au rendu différé. Il a beau utiliser plus de registres, ce n'est pas tellement un problème vu qu'il y a moins d'accès mémoire à masquer. Le rendu différé a donc de nombreux avantages et désavantages. Un avantage est que le rendu différé se marie bien avec les effets graphiques de type ''screen space'', comme certaines formes d'occlusion ambiante. Ces effets ont besoin de connaitre la profondeur de chaque pixel, qui est fournie sur un plateau dans un ''g-buffer''. Par contre, la transparence n'est pas prise en charge, de même que l'antialiasing de type MSAA. Il y a aussi des conséquences en termes de performances, car lire les ''g-buffer'' n'est pas gratuit. Le cout en bande passante mémoire, est totalement absent dans le rendu direct, qui se contente de lire les textures des objets. Au final, le rendu différé fait un compromis différent du rendu direct : il échange du temps de calcul contre de la bande passante mémoire et contre la VRAM occupée par les ''g-buffer''. Un autre désavantage est que tous les objets doivent faire leurs calculs d'éclairage avec le peu d'informations présentes dans le ''g-buffer''. Ils doivent donc se débrouiller avec une couleur diffuse, spéculaire, les normales et quelques informations comme l'émissivité. En clair, plusieurs ''materials'' partagent des BRDFs très similaires, si ce n'est identiques. Le rendu direct n'a absolument pas ce problème et peut utiliser un ''material'' différent par objet, voire plusieurs ''materials'' différents sur un même objet. Ils seront simplement rendu dans des ''draw call'' séparés. ===Une prépasse z implicite=== Les avantages et désavantages de l'éclairage différé sont très nombreux. L'un d'entre eux est que l'on n'a pas d'''overdraw'' : seuls les pixels visibles sont éclairés. Ce n'est pas forcément le cas avec le rendu direct (''forward rendenring''), où des pixels sont éclairés par un ''pixel shader'' et enregistrés dans le ''framebuffer'', avant d'être écrasé par la suite. On dit qu'il y a un certain ''overdraw'', d'autant plus important que les pixels invisibles éclairés sont nombreux. Les techniques d'élimination des pixels cachés limitent la casse, mais elles ne fonctionnent à la perfection que si l'on trie les triangles du plus proche au plus lointain, ce qui n'est jamais fait en pratique. Il est cependant possible d'éliminer tout ''overdraw'' avec le rendu direct, en utilisant une pré-passe z (abordé dans le chapitre sur le ROP). D'ailleurs, le rendu différé effectue une pré-passe z implicitement. En réalité, l'éclairage différé est une sorte de pré-passe z sous stéroïde, qui ne fait pas que calculer le tampon de profondeur final, mais calcule aussi les normales et d'autres informations fournies par la rastérisation. De plus, le rendu différé ne recalcule pas la géométrie dans la seconde passe, alors que le rendu direct avec pré-passe z va le faire. Par contre, cette pré-passe z implicite est à l'origine de plusieurs défauts de cette technique. Déjà, l'usage d'une prépasse z n'est pas compatible avec l'usage de la transparence. Le rendu différé ne fonctionne que pour les objets opaques. La seule solution est de traiter les objets opaques à part des objets transparents. Les objets opaques sont rendus dans une première passe utilisant le rendu différé, alors que les objets transparents sont rendus dans une seconde passe qui utilise le rendu direct.La prépasse z désactive aussi l'antialiasing, même si je ne peux pas expliquer pourquoi ici. ===L'implémentation sur les GPU modernes=== Sans MRT, le rendu différé est très compliqué à implémenter. Chaque ''g-buffer'' demande une passe de rendu, chacune re-calculant la géométrie. Par contre, avec MRT, les différents ''g-buffer'' sont tous calculés en une seule passe, sans recalculer la géométrie plusieurs fois. Il est possible d'ajouter d'autres textures pour d'autres informations, comme des textures pour les paramètres de la lumière spéculaire (couleur spéculaire, illumination). Par contre, on est rapidement limité en termes de nombres de textures. En effet, les GPU modernes supportent entre 4 à 8 textures maximum en sortie pour le MRT. Un ''pixel shader'' a donc accès à 4 ''g-buffer'', rarement plus. Pour une compatibilité maximale, les programmeurs se limitent à 4 textures. De plus, utiliser plus de textures aurait un impact en matière de consommation mémoire, et de bande passante. Pour limiter la casse, il est possible de regrouper des informations séparées dans un seul ''g-buffer'', pour gagner de la place. Par exemple, la couleur diffuse est une couleur RGB codée sur 24 bits, là où une texture utilise souvent des pixels RGBA codés sur 32 bits. Il est alors d'utiliser l'octet pour la composante A pour stocker une autre information, comme une information de métallicité (utilisée pour calculer la couleur spéculaire). Un autre défaut du MRT est que son usage désactive de nombreuses optimisations intégrées dans les ROPs, comme le ''z-fast pass''. De telles optimisations demandent en effet que les ROPs n'écrivent que dans un seul ''render target'' : le tampon de profondeur. LA vitesse des ROPs est alors doublée, sans compter que la bande passante mémoire est mieux utilisée. Mais dès qu'on active le MRT, ces optimisations sont désactivées. En comparaison, elles sont activées lors d'une prépasse z normale. Enfin, le MRT désactive automatiquement l'antialiasing matériel. Précisément, c'est l'antialiasing de type ''multisampling'' (MSAA) qui est désactivé, même s'il est possible d'utiliser du ''supersampling'' ou un antialiasing par post-traitement. En conséquence, l'usage de l'éclairage différé désactive totalement le MSAA. ==La fonctionnalité de ''stream output''== Une fonctionnalité des ''geometry shaders'' est la possibilité d'enregistrer leurs résultats en mémoire. Il s'agit de la fonctionnalité du '''''stream output'''''. On peut ainsi remplir une texture ou le ''vertex buffer'' dans la mémoire vidéo, avec le résultat d'un ''geometry shader''. Notons que celle-ci mémorise un ensemble de primitives, pas autre chose. Cette fonctionnalité est utilisée pour certains effets ou rendu bien précis, mais il faut avouer qu'elle n'est pas très souvent utilisée. Aussi, les concepteurs de cartes graphiques n'ont pas optimisé cette fonctionnalité au maximum. Le ''stream output'' n'a généralement pas accès prioritaire à la mémoire, comparé aux ROP, et n'a souvent accès qu'à une partie limitée de la bande passante mémoire. Notons qu'il existe deux formes de ''stream output'' : une qui permet aux ''vertex shader'' d'écrire dans une texture, l'autre qui permet aux ''geometry shaders'' de le faire. Notons que le ''stream output'' fournit un flux de primitives, pas de sommets, même pour le flux sortant d'un ''vertex shader''. En clair, beaucoup de sommets sont dupliqués et ont n'a pas d{{'}}''index buffer''. Les résultats du ''stream output'' sont donc assez lourds et prennent beaucoup de mémoire. [[File:Stream output.png|centre|vignette|upright=2.5|Stream output]] ==Les instructions atomiques des ''compute shaders''== Dans le chapitre précédent, nous avions parlé des ROPs, et notamment de pourquoi on ne peut pas les émuler dans un ''shader''. Le problème est que plusieurs instances de shaders peuvent lire une même donnée, faire un traitement, et la remplacer par leur résultat. Et l'ordre entre ces instances de shaders n'est pas garanti. Le même problème peut survenir dans un ''compute shader'', à savoir un shader utilisé pour du GPGPU. Un ''compute shader'' peut accéder à un ou plusieurs tableaux en mémoire, voire des structures de données plus complexes. Mais restons sur des tableaux. Les tableaux en question sont appelés des ''Unordered Access Views'', leur nom indiquant qu'aucune garantie n'est donnée sur l'ordre des écritures. Il n'y a pas de tri entre instances de shaders pour que les shaders écrivent leurs données dans leur ordre de lancement. À la place, les écritures dans un ''compute shader'' sont immédiates. Les shaders peuvent lire et écrire dans la mémoire vidéo dans le désordre. Le problème est qu'il y a des données qui ne doivent pas être traitées par plusieurs instances d'un ''compute shader'' à la fois. Heureusement, les ''compute shaders'' disposent d'un mécanisme pour éliminer ce problème. La solution en question est similaire à ce qu'on trouve sur les architectures multicœurs, qui sont confrontées au même problème. Il y a des données qui ne doivent pas être traitées par plusieurs ''threads'' à la fois. La solution est d'ajouter au processeur des '''instructions atomiques''', qui lisent, modifient et écrivent une donnée, sans qu'aucune instruction ne puisse les déranger. De telles instructions empêchent tout accès mémoire à la donnée manipulée, tant qu'elles ne sont pas terminées. Le terme atomique veut dire qu'elles donnent l'impression de s'exécuter en une seule fois, elles ne donnent pas l'impression d'associer une lecture, une opération et une écriture séparées. Les GPU modernes ont des instructions atomiques, utilisables uniquement dans les ''pixel shaders''. Par contre, leur implémentation matérielle est différente. Les CPU multicœurs rendent leurs instructions atomiques par divers mécanismes. Le plus simple bloque totalement le bus mémoire, pour empêcher aux autres cœurs d'accéder à la mémoire RAM. Le cout en performance est alors énorme. Une solution alternative utilise les mécanismes de cohérence des caches. L'idée est de bloquer certaines lignes de cache, afin qu'aucun autre cœur ne puisse lire ou écrire la ligne de cache réservée. Mais les GPU ne peuvent pas faire cela. Les méthodes précédentes fonctionnent quand on a un bus mémoire partagé entre 2-4 cœurs, mais ont un cout en performance trop important au-delà. Aussi, les GPUs font autrement. Ils exécutent les instructions atomiques en dehors du processeur de shader ! À la place, chaque cache est associé à une '''unité atomique''' (''atomic unit''), qui exécute les opérations atomiques directement dans le cache, en dehors des processeurs de shader. Pour exécuter une instruction atomique, les processeurs de shaders envoient l'instruction aux unités atomiques, qui les exécutent directement dans le cache. Les unités atomiques peuvent recevoir plusieurs instructions atomiques en même temps, mais elles forcent l'exécution des instructions atomiques à se faire une par une. {{NavChapitre | book=Les cartes graphiques | prev=Les Render Output Target | prevText=Les Render Output Target | next=Le support matériel du lancer de rayons | nextText=Le support matériel du lancer de rayons }}{{autocat}} pc41va49vjh5t5ppxlo1yo91ydybre4 763959 763958 2026-04-18T16:25:18Z Mewtow 31375 /* L'éclairage différé */ 763959 wikitext text/x-wiki Le chapitre précédent nous a expliqué comment les ROPs mettaient à jour le ''framebuffer'' et le tampon de profondeur. Cependant, les ROPs ne sont pas les seuls à écrire en mémoire vidéo. Les ''compute shaders'', ''vertex shaders'' et ''pixel shaders'' peuvent écrire des données en mémoire vidéo, dans des tableaux dédiés. Et n'allez pas croire que c'est un petit sujet. La majeure partie des jeux vidéos actuels utilisent des techniques de rendu différé qui ne fonctionneraient pas sans cela ! Et ne parlons pas des ''compute shaders'', qui ne serviraient à rien sans cela. Dans ce chapitre, nous allons détailler comment se font ces écritures en mémoire. : Notons que nous mettons de côté les architectures ''sort-middle'' et les GPU à rendu en ''tile'' dans ce chapitre. ==Les fonctionnalités de ''Multiple Render Targets''== Il faut noter que les API modernes permettent à un ''pixel shader'' d'écrire dans plusieurs ''render-target''. On parle alors de '''''Multiple Render Targets''''', abrévié en MRT. L'implémentation classique est que le ''pixel shader'' peut écrire son résultat dans une ou plusieurs textures. Précisons qu'il s'agit bien du ''pixel shader'' qui écrit dans une texture. Suivant le GPU, l'écriture dans les ''render target'' peut ou non passer par les ROPs. Sur les anciennes versions de DirectX, un seul ''render target'' passait par les ROPs, mais pas les autres. De nos jours, tous les ''render target'' passent par les ROPs. Il est même possible de configurer le mélange ''alpha'' différemment suivant le ''render target''. [[File:MultiRenderTarget.svg|centre|vignette|upright=3|''Multiple Render Target''.]] ===L'éclairage différé=== Le MRT accélère fortement les techniques d''''éclairage différé''', qui séparent les calculs d'éclairage par pixel dans une passe séparée du reste. Ils fonctionnent en deux passes, au minimum. La première calcule toutes les informations nécessaires pour les calculs d'éclairage par pixel. Elle calcule les normales, la couleur diffuse, de quoi calculer les réflexions spéculaires, et d'autres informations du genre. Le tout est mémorisé dans plusieurs "textures" séparées, qui sont lues par un '''pixel shader''' pour obtenir l'image finale. On parle aussi de rendu différé au lieu d'éclairage différé, mais c'est un détail. La première passe calcule la couleur de chaque pixel, la profondeur de chaque pixel, et la normale de la surface pour chaque pixel. Dans l'implémentation la plus simple, ces informations sont écrites dans trois "textures" : une qui contient la couleur diffuse, une autre pour la profondeur, et une autre pour les normales. Il faut alors supporter des pseudo-''framebuffer'' pour chaque "texture". Ils sont appelés des '''''G-buffer''''', ce qui est l'abréviation de tampon géométrique. {| |[[File:Deferred rendering pass col.jpg|thumb|''G-buffer'' pour la couleur.]] |[[File:Deferred rendering pass dep.jpg|thumb|''G-buffer'' pour la profondeur.]] |[[File:Deferred rendering pass nor.jpg|thumb|''G-buffer'' pour les normales.]] |[[File:Deferred rendering pass res.jpg|thumb|Image finale]] |} L'intérêt de l'éclairage différé est d'exécuter le ''pixel shader'' une fois par source de lumière et par pixel. Il y a donc un ''draw call'' par source de lumière, qui lit les ''g-buffer''. La conséquence est que le rendu différé utilise généralement un shader par type de sources de lumières, qu'elles soient ponctuelles, directionnelles, etc. Pour comparer, sans éclairage différé, l'éclairage est réalisé autrement. L'implémentation la plus simple fait les calculs d'éclairage une fois par source et lumière et par objet, avec un ''draw call'' par objet et par source de lumière. On parle alors de '''rendu direct multi-passe'''. La géométrie d'un objet est donc recalculée plusieurs fois, une fois par source de lumière. Par contre, le ''pixel shader'' est généralement assez simple, car on peut utiliser un ''shader'' différent pour les lumières ponctuelles, directionnelles, etc. Mieux que ça : on a tendance à avoir un ''shader'' par ''material'', vu qu'on a généralement un seul ''material'' par objet, les exceptions étant rares. Le cout en termes de performances est alors le suivant : des ''pixels shaders'' très spécialisés et optimisés, mais des calculs géométriques redondants. Une implémentation plus optimisée utilise un seul ''draw call'' par objet. Le ''draw call'' reçoit une liste des sources de lumière. Le ''pixel shader'' lit les sources de lumière une par une dans cette liste, et calcule l'éclairage une source de lumière à la fois, jusqu'à obtenir la couleur finale du pixel. La différence est qu'on a un ''draw call'' par objet, et non un par objet et source de lumière. On parle alors de '''rendu direct simple passe'''. Les calculs géométriques redondants sont éliminés, mais le ''pixel shader'' perd sa spécialisation. Au lieu d'utiliser plusieurs ''pixel shaders'' spécialisés, on utilise un ''über shader'' qui supporte tous les types de sources de lumières et tous les ''materials''. La conséquence est qu'un ''über shader'' tend à être moins bien optimisé, car il utilise plus de registres. Cela est cependant compensé par le fait que le ''pixel shader'' fait peu d'accès mémoire, comparé au rendu différé. Il a beau utiliser plus de registres, ce n'est pas tellement un problème vu qu'il y a moins d'accès mémoire à masquer. Le rendu différé a donc de nombreux avantages et désavantages. Un avantage est que le rendu différé se marie bien avec les effets graphiques de type ''screen space'', comme certaines formes d'occlusion ambiante. Ces effets ont besoin de connaitre la profondeur de chaque pixel, qui est fournie sur un plateau dans un ''g-buffer''. Par contre, la transparence n'est pas prise en charge, de même que l'antialiasing de type MSAA. Il y a aussi des conséquences en termes de performances, car lire les ''g-buffer'' n'est pas gratuit. Le cout en bande passante mémoire, est totalement absent dans le rendu direct, qui se contente de lire les textures des objets. Au final, le rendu différé fait un compromis différent du rendu direct : il échange du temps de calcul contre de la bande passante mémoire et contre la VRAM occupée par les ''g-buffer''. Un autre désavantage est que tous les objets doivent faire leurs calculs d'éclairage avec le peu d'informations présentes dans le ''g-buffer''. Ils doivent donc se débrouiller avec une couleur diffuse, spéculaire, les normales et quelques informations comme l'émissivité. En clair, plusieurs ''materials'' partagent des BRDFs très similaires, voire identique. Le rendu direct n'a absolument pas ce problème et peut utiliser un ''material'' différent par objet, voire plusieurs ''materials'' différents sur un même objet. Ils seront simplement rendus dans des ''draw call'' séparés. ===Une prépasse z implicite=== Les avantages et désavantages de l'éclairage différé sont très nombreux. L'un d'entre eux est que l'on n'a pas d'''overdraw'' : seuls les pixels visibles sont éclairés. Ce n'est pas forcément le cas avec le rendu direct (''forward rendenring''), où des pixels sont éclairés par un ''pixel shader'' et enregistrés dans le ''framebuffer'', avant d'être écrasé par la suite. On dit qu'il y a un certain ''overdraw'', d'autant plus important que les pixels invisibles éclairés sont nombreux. Les techniques d'élimination des pixels cachés limitent la casse, mais elles ne fonctionnent à la perfection que si l'on trie les triangles du plus proche au plus lointain, ce qui n'est jamais fait en pratique. Il est cependant possible d'éliminer tout ''overdraw'' avec le rendu direct, en utilisant une pré-passe z (abordé dans le chapitre sur le ROP). D'ailleurs, le rendu différé effectue une pré-passe z implicitement. En réalité, l'éclairage différé est une sorte de pré-passe z sous stéroïde, qui ne fait pas que calculer le tampon de profondeur final, mais calcule aussi les normales et d'autres informations fournies par la rastérisation. De plus, le rendu différé ne recalcule pas la géométrie dans la seconde passe, alors que le rendu direct avec pré-passe z va le faire. Par contre, cette pré-passe z implicite est à l'origine de plusieurs défauts de cette technique. Déjà, l'usage d'une prépasse z n'est pas compatible avec l'usage de la transparence. Le rendu différé ne fonctionne que pour les objets opaques. La seule solution est de traiter les objets opaques à part des objets transparents. Les objets opaques sont rendus dans une première passe utilisant le rendu différé, alors que les objets transparents sont rendus dans une seconde passe qui utilise le rendu direct.La prépasse z désactive aussi l'antialiasing, même si je ne peux pas expliquer pourquoi ici. ===L'implémentation sur les GPU modernes=== Sans MRT, le rendu différé est très compliqué à implémenter. Chaque ''g-buffer'' demande une passe de rendu, chacune re-calculant la géométrie. Par contre, avec MRT, les différents ''g-buffer'' sont tous calculés en une seule passe, sans recalculer la géométrie plusieurs fois. Il est possible d'ajouter d'autres textures pour d'autres informations, comme des textures pour les paramètres de la lumière spéculaire (couleur spéculaire, illumination). Par contre, on est rapidement limité en termes de nombres de textures. En effet, les GPU modernes supportent entre 4 à 8 textures maximum en sortie pour le MRT. Un ''pixel shader'' a donc accès à 4 ''g-buffer'', rarement plus. Pour une compatibilité maximale, les programmeurs se limitent à 4 textures. De plus, utiliser plus de textures aurait un impact en matière de consommation mémoire, et de bande passante. Pour limiter la casse, il est possible de regrouper des informations séparées dans un seul ''g-buffer'', pour gagner de la place. Par exemple, la couleur diffuse est une couleur RGB codée sur 24 bits, là où une texture utilise souvent des pixels RGBA codés sur 32 bits. Il est alors d'utiliser l'octet pour la composante A pour stocker une autre information, comme une information de métallicité (utilisée pour calculer la couleur spéculaire). Un autre défaut du MRT est que son usage désactive de nombreuses optimisations intégrées dans les ROPs, comme le ''z-fast pass''. De telles optimisations demandent en effet que les ROPs n'écrivent que dans un seul ''render target'' : le tampon de profondeur. LA vitesse des ROPs est alors doublée, sans compter que la bande passante mémoire est mieux utilisée. Mais dès qu'on active le MRT, ces optimisations sont désactivées. En comparaison, elles sont activées lors d'une prépasse z normale. Enfin, le MRT désactive automatiquement l'antialiasing matériel. Précisément, c'est l'antialiasing de type ''multisampling'' (MSAA) qui est désactivé, même s'il est possible d'utiliser du ''supersampling'' ou un antialiasing par post-traitement. En conséquence, l'usage de l'éclairage différé désactive totalement le MSAA. ==La fonctionnalité de ''stream output''== Une fonctionnalité des ''geometry shaders'' est la possibilité d'enregistrer leurs résultats en mémoire. Il s'agit de la fonctionnalité du '''''stream output'''''. On peut ainsi remplir une texture ou le ''vertex buffer'' dans la mémoire vidéo, avec le résultat d'un ''geometry shader''. Notons que celle-ci mémorise un ensemble de primitives, pas autre chose. Cette fonctionnalité est utilisée pour certains effets ou rendu bien précis, mais il faut avouer qu'elle n'est pas très souvent utilisée. Aussi, les concepteurs de cartes graphiques n'ont pas optimisé cette fonctionnalité au maximum. Le ''stream output'' n'a généralement pas accès prioritaire à la mémoire, comparé aux ROP, et n'a souvent accès qu'à une partie limitée de la bande passante mémoire. Notons qu'il existe deux formes de ''stream output'' : une qui permet aux ''vertex shader'' d'écrire dans une texture, l'autre qui permet aux ''geometry shaders'' de le faire. Notons que le ''stream output'' fournit un flux de primitives, pas de sommets, même pour le flux sortant d'un ''vertex shader''. En clair, beaucoup de sommets sont dupliqués et ont n'a pas d{{'}}''index buffer''. Les résultats du ''stream output'' sont donc assez lourds et prennent beaucoup de mémoire. [[File:Stream output.png|centre|vignette|upright=2.5|Stream output]] ==Les instructions atomiques des ''compute shaders''== Dans le chapitre précédent, nous avions parlé des ROPs, et notamment de pourquoi on ne peut pas les émuler dans un ''shader''. Le problème est que plusieurs instances de shaders peuvent lire une même donnée, faire un traitement, et la remplacer par leur résultat. Et l'ordre entre ces instances de shaders n'est pas garanti. Le même problème peut survenir dans un ''compute shader'', à savoir un shader utilisé pour du GPGPU. Un ''compute shader'' peut accéder à un ou plusieurs tableaux en mémoire, voire des structures de données plus complexes. Mais restons sur des tableaux. Les tableaux en question sont appelés des ''Unordered Access Views'', leur nom indiquant qu'aucune garantie n'est donnée sur l'ordre des écritures. Il n'y a pas de tri entre instances de shaders pour que les shaders écrivent leurs données dans leur ordre de lancement. À la place, les écritures dans un ''compute shader'' sont immédiates. Les shaders peuvent lire et écrire dans la mémoire vidéo dans le désordre. Le problème est qu'il y a des données qui ne doivent pas être traitées par plusieurs instances d'un ''compute shader'' à la fois. Heureusement, les ''compute shaders'' disposent d'un mécanisme pour éliminer ce problème. La solution en question est similaire à ce qu'on trouve sur les architectures multicœurs, qui sont confrontées au même problème. Il y a des données qui ne doivent pas être traitées par plusieurs ''threads'' à la fois. La solution est d'ajouter au processeur des '''instructions atomiques''', qui lisent, modifient et écrivent une donnée, sans qu'aucune instruction ne puisse les déranger. De telles instructions empêchent tout accès mémoire à la donnée manipulée, tant qu'elles ne sont pas terminées. Le terme atomique veut dire qu'elles donnent l'impression de s'exécuter en une seule fois, elles ne donnent pas l'impression d'associer une lecture, une opération et une écriture séparées. Les GPU modernes ont des instructions atomiques, utilisables uniquement dans les ''pixel shaders''. Par contre, leur implémentation matérielle est différente. Les CPU multicœurs rendent leurs instructions atomiques par divers mécanismes. Le plus simple bloque totalement le bus mémoire, pour empêcher aux autres cœurs d'accéder à la mémoire RAM. Le cout en performance est alors énorme. Une solution alternative utilise les mécanismes de cohérence des caches. L'idée est de bloquer certaines lignes de cache, afin qu'aucun autre cœur ne puisse lire ou écrire la ligne de cache réservée. Mais les GPU ne peuvent pas faire cela. Les méthodes précédentes fonctionnent quand on a un bus mémoire partagé entre 2-4 cœurs, mais ont un cout en performance trop important au-delà. Aussi, les GPUs font autrement. Ils exécutent les instructions atomiques en dehors du processeur de shader ! À la place, chaque cache est associé à une '''unité atomique''' (''atomic unit''), qui exécute les opérations atomiques directement dans le cache, en dehors des processeurs de shader. Pour exécuter une instruction atomique, les processeurs de shaders envoient l'instruction aux unités atomiques, qui les exécutent directement dans le cache. Les unités atomiques peuvent recevoir plusieurs instructions atomiques en même temps, mais elles forcent l'exécution des instructions atomiques à se faire une par une. {{NavChapitre | book=Les cartes graphiques | prev=Les Render Output Target | prevText=Les Render Output Target | next=Le support matériel du lancer de rayons | nextText=Le support matériel du lancer de rayons }}{{autocat}} oz9xvqsuvshqh6yzhb0w8s4xjwkdkfz Mathc initiation/0069 0 83814 763953 763872 2026-04-18T14:26:09Z Xhungab 23827 763953 wikitext text/x-wiki __NOTOC__ [[Catégorie:Mathc initiation (livre)]] [[Mathc initiation/Fichiers h : c44a4| Sommaire]] {{Partie{{{type|}}}|Calcul de R_s_x_R_t au point p}} Se sera utilisé dans le calcul de [[Mathc initiation/a458|l''''intégrale de flux de surface définie paramétriquement''']]. : . : Copier la bibliothèque dans votre répertoire de travail : * [[Mathc initiation/006d|x_afile.h ............. Déclaration des fichiers h]] * [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]] * [[Mathc initiation/Fichiers c : c47ca|x_strcp.h ........... Déclaration des structures (points, vecteurs)]] * [[Mathc initiation/Fichiers h : c25a4|x_fxy.h .............. Calculer les dérivées partielles]] * [[Mathc initiation/a452|x_rsxrt.h ............ Calcul de R_s_x_R_t au point p]] : . : Les fonctions pour les différents exemples : * [[Mathc initiation/006c|f.h]] : . : '''Calculer R_s_x_R_t au point p :''' * [[Mathc initiation/006b|c00a.c ]] '''Calculer R_s_x_R_t au point p :''' * [[Mathc initiation/006a|c00b.c ]] {{AutoCat}} 8r5t4205r624czinmrq27hbctxg8qo1 Flamusse bourguignonne 0 83821 763962 2026-04-18T18:02:32Z Arnaud 25 3667 Page créée avec « {{livre de cuisine}} [[Fichier:Flamusse bourguignonne 04.jpg|vignette|Flamusse bourguignonne]] [[Fichier:Flamusse bourguignonne 02.jpg|vignette|]] La '''flamusse bourguignonne''' est une pâtisserie traditionnelle de la cuisine bourguignonne, généralement aux pommes. == Recette == === Ingrédients === Pour 6 personnes : * 3 pommes * 3 œufs * 70 g de farine * 30 cl de lait * 30 g de beurre * 80 g de sucre * Une pincée de sel === Préparation === # Dans un s... » 763962 wikitext text/x-wiki {{livre de cuisine}} [[Fichier:Flamusse bourguignonne 04.jpg|vignette|Flamusse bourguignonne]] [[Fichier:Flamusse bourguignonne 02.jpg|vignette|]] La '''flamusse bourguignonne''' est une pâtisserie traditionnelle de la cuisine bourguignonne, généralement aux pommes. == Recette == === Ingrédients === Pour 6 personnes : * 3 pommes * 3 œufs * 70 g de farine * 30 cl de lait * 30 g de beurre * 80 g de sucre * Une pincée de sel === Préparation === # Dans un saladier, mélangez la farine avec le sucre en poudre, et une pincée de sel. # Cassez les œufs dans un puit au centre, et mélangez au fouet en incorporant petit à petit le mélange précédant. # Ajoutez le lait et la crème tout en mélangeant jusqu’à l’obtention d’une pâte homogène. # Pelez les pommes, et les couper en quartiers. # Faites revenir puis dorer les quartiers de pommes au beurre, dans une poêle pendant 5 minutes. # Beurrez un moule, disposez les quartiers de pommes au fond du moule. # Versez la pâte. # Faire cuire au four à 180 °C pendant 50 minutes. Vérifiez la cuisson. # Laissez tiédir avant de démouler. # Saupoudrer de sucre ou de cannelle avant dégustation. {{Sur Wikipédia|Flamusse bourguignonne}} [[Catégorie:Cuisine française|{{SUBPAGENAME}}]] [[Catégorie:Desserts|Flamusse bourguignonne]] [[Catégorie:Cuisine bourguignone]] nlodmf8klm9j9arvmdeer5y6loqi068 Livre de cuisine/Flamusse bourguignonne 0 83822 763963 2026-04-18T18:05:23Z Arnaud 25 3667 Page créée avec « {{livre de cuisine}} [[Fichier:Flamusse bourguignonne 04.jpg|vignette|Flamusse bourguignonne]] [[Fichier:Flamusse bourguignonne 02.jpg|vignette|]] La '''flamusse bourguignonne''' est une pâtisserie traditionnelle de la cuisine bourguignonne, généralement aux pommes. == Recette == === Ingrédients === Pour 6 personnes : * 3 pommes * 3 œufs * 70 g de farine * 30 cl de lait * 30 g de beurre * 80 g de sucre * Une pincée de sel === Préparation === # Dans un s... » 763963 wikitext text/x-wiki {{livre de cuisine}} [[Fichier:Flamusse bourguignonne 04.jpg|vignette|Flamusse bourguignonne]] [[Fichier:Flamusse bourguignonne 02.jpg|vignette|]] La '''flamusse bourguignonne''' est une pâtisserie traditionnelle de la cuisine bourguignonne, généralement aux pommes. == Recette == === Ingrédients === Pour 6 personnes : * 3 pommes * 3 œufs * 70 g de farine * 30 cl de lait * 30 g de beurre * 80 g de sucre * Une pincée de sel === Préparation === # Dans un saladier, mélangez la farine avec le sucre en poudre, et une pincée de sel. # Cassez les œufs dans un puit au centre, et mélangez au fouet en incorporant petit à petit le mélange précédant. # Ajoutez le lait et la crème tout en mélangeant jusqu’à l’obtention d’une pâte homogène. # Pelez les pommes, et les couper en quartiers. # Faites revenir puis dorer les quartiers de pommes au beurre, dans une poêle pendant 5 minutes. # Beurrez un moule, disposez les quartiers de pommes au fond du moule. # Versez la pâte. # Faire cuire au four à 180 °C pendant 50 minutes. Vérifiez la cuisson. # Laissez tiédir avant de démouler. # Saupoudrer de sucre ou de cannelle avant dégustation. {{Sur Wikipédia|Flamusse bourguignonne}} [[Catégorie:Cuisine française|{{SUBPAGENAME}}]] [[Catégorie:Desserts|Flamusse bourguignonne]] [[Catégorie:Cuisine bourguignonne]] axwxnjfef1tzct4k8injftd95s7uihn Pour lire Platon/Introduction par les mythes 0 83823 763976 2026-04-19T06:13:04Z PandaMystique 119061 PandaMystique a déplacé la page [[Pour lire Platon/Introduction par les mythes]] vers [[Pour lire Platon/Introduction par les dialogues]] par-dessus une redirection 763976 wikitext text/x-wiki #REDIRECTION [[Pour lire Platon/Introduction par les dialogues]] kx3yy02vidka1n9qnw9xxibj3tv4px5 763978 763976 2026-04-19T06:14:37Z PandaMystique 119061 Redirection supprimée vers [[Pour lire Platon/Introduction par les dialogues]] 763978 wikitext text/x-wiki <noinclude>{{Pour lire Platon}}</noinclude> {|class=wikitable |<strong>Lire Platon à l'aide des mythes et métaphores</strong> Invitons-nous à la table philosophique de Platon. Il clarifie certaines discussions par le recours aux mythes. C'est une façon de présenter ses questionnements. La liste des questions proposées n'est pas close. |[[Image:Plato_Pio-Clemetino_Inv305.jpg|290px]] |} == La philosophie == === Fille de l'étonnement et de l'arc-en-ciel === [[Fichier:'Paris à l'Arc-en-ciel' by Robert Delaunay, 1914.jpg|vignette|'Paris à l'Arc-en-ciel' by Robert Delaunay, 1914]] <blockquote>''Théêtète [155d] SOCRATE'' ''« Je vois, mon ami, que Théodore n’a pas mal deviné le caractère de ton esprit ; car c’est la vraie marque d’un philosophe que le sentiment d’étonnement que tu éprouves. La philosophie, en effet, n’a pas d’autre origine, et celui qui a fait d’Iris la fille de Thaumas n’est pas, il me semble, un mauvais généalogiste. »''</blockquote>Fille de l'étonnement et de l'arc-en-ciel telle est la philosophie. Qu'est-ce que cela signifie? Le philosophe s'étonne sans être béat ou stupéfait. Il découvre l'illusion des évidences, sources de préjugés. L'arc-en-ciel réunit les opposés par un lien qui semblable au fil du discours cherche à rassembler, réconcilier. La philosophie rassemble ce qui se contredit et produit à ce propos de beaux discours, à l'image de l'arc-en-ciel. === Désirer le vrai ou la question du manque === [[Fichier:Chouette Puy dy Fou.jpg|gauche|vignette|La chouette est le symbole de la philosophie]] ==== La définition du Banquet : l'opinion, Aristophane et Diotime. ==== L'étymologie du mot ''philosophie'' est l'amour de la sagesse ou du savoir. Mais, quoique connue, cette signification n'étonne peut-être pas autant qu'elle le devrait. L'étonnement (au sens du coup de tonnerre, ce qui réveille par l'effet inattendu qu'il produit) est la vertu première de la philosophie. La lecture de Platon va nous montrer de quelle manière cet amour peut nous paraître déroutant. Tout d'abord, être sage, ou connaître, semble se rapporter à un état intellectuel. Or, il ne va pas de soi que l'on puisse dire qu'une réalité qui a à voir avec nos facultés intellectuelles puisse susciter une affection, et encore moins une affection telle qu'un désir. On peut ressentir de la joie à trouver une vérité, mais ce sentiment n'est pas ce qui fait la vérité. À plus forte raison, il est étrange que le désir amoureux soit non pas seulement dirigé vers le savoir, mais que ce savoir ne soit pas possible sans lui. Dans '''le Banquet''', le désir se définit dans un premier temps par sa relation au plaisir sexuel, et on perçoit mal le rapport qu'il peut entretenir avec la sagesse. C'est le poète comique Aristophane<ref>Théâtre complet d'Aristophane sur [[s:Auteur:Aristophane|Wikisource]]</ref> qui le présente dans un mythe, le mythe de l'androgyne. Un mythe est un discours qui ''semble'' échapper au discours de la raison puisqu'il est fondamentalement produit par l'imagination. Au moment où il le présente, Aristophane, ivre, s'est enfin remis d'un hoquet insistant. Les interlocuteurs du Banquet sont d'ailleurs assez ivres dans l'ensemble. Le contexte est ainsi apparemment fort éloigné de la sagesse. Pour être tranquilles les hommes présents ont demandé à la servante de les laisser entre eux. C'est dans ces conditions qu'Aristophane prend la parole. [[Fichier:Nature dévoilée par Philosophie.jpg|vignette]] ==== Discours d'Aristophane ou le désir du corps. ==== Le désir se donne à comprendre comme une punition divine. Au début, raconte-t-il dans un récit mythique qu'il crée pour l'occasion, les hommes avaient des formes sphériques et ne manquaient de rien.Puis leur vint cette tentation d'escalader le ciel et de se mesurer aux Dieux. Zeus, pour les punir, les coupa en deux et c'est ainsi que naquit le désir et le manque à combler. Le nombril est la trace de l'antique châtiment. Ceux qui jusqu'à présent, n'avaient pas la moindre idée du manque (quoique s'ils ont voulu escalader le ciel de l'Olympe, c'est peut-être parce qu'il leur manquait quelque chose!) sont dans un tel état de souffrance qu'ils ne cessent de chercher leur moitié perdue. On désire ainsi ce que l'on a perdu. Pour Aristophane le désir se nourrit de la perte. ==== Diotime ou le désir philosophique. ==== [[Fichier:Eros Farnese MAN Napoli 6353.jpg|gauche|vignette]] C'est un discours assez énigmatique que tient cette prêtresse dans '''le Banquet.''' Socrate évoque sa rencontre avec elle et la fait parler du désir. Lisons :<blockquote>''D: C’est une assez longue histoire, [203b] Je vais pourtant te la raconter. Il faut savoir que, le jour où naquit Aphrodite [2], les dieux festoyaient ; parmi eux, se trouvait le fils de Mètis [3], Poros[4] . Or, quand le banquet fut terminé, arriva Pénia [5], qui était venue mendier comme cela est naturel un jour de bombance, et elle se tenait sur le pas de la porte. Or Poros, qui s’était enivré de nectar, car le vin n’exis­tait pas encore à cette époque, se traîna dans le jar­din de Zeus et, appesanti par l’ivresse, s’y endormit. Alors, Pénia, dans sa pénurie, eut le projet de se faire faire un enfant par Poros ; [203c] elle s’étendit près de lui et devint grosse d’Éros. Si Éros est devenu le suivant d’Aphrodite et son servant, c’est bien parce qu’il a été engendré lors des fêtes données en l’hon­neur de la naissance de la déesse ; et si en même temps il est par nature amoureux du beau, c’est parce qu’Aphrodite est belle.''</blockquote><blockquote>''Puis donc qu’il est le fils de Poros et de Pénia, Éros se trouve dans la condition que voici. D’abord, il est toujours pauvre, et il s’en faut de beaucoup qu’il soit délicat et beau, comme le croient la plupart des gens. Au contraire, il est rude, malpropre, va­nu-pieds et [203d] il n’a pas de gîte, couchant toujours par terre et à la dure, dormant à la belle étoile sur le pas des portes et sur le bord des chemins, car, puis­qu’il tient de sa mère, c’est l’indigence qu’il a en par­tage. À l’exemple de son père en revanche, il est à l’affût de ce qui est beau et de ce qui est bon, il est viril, résolu, ardent, c’est un chasseur redoutable ; il ne cesse de tramer des ruses, il est passionné de savoir et fertile en expédients, il passe tout son temps à philo­sopher, c’est un sorcier redoutable, un magicien et un expert [6]. Il faut ajouter que par nature il n’est ni immortel [203e] ni mortel. En l’espace d’une même journée, tantôt il est en fleur, plein de vie, tantôt il est mourant ; puis il revient à la vie quand ses expédients réussissent en vertu de la nature qu’il tient de son père ; mais ce que lui procurent ses expédients sans cesse lui échappe ; aussi Eros n’est-il jamais ni dans l’indigence ni dans l’opulence.''</blockquote><blockquote>''<nowiki>Par ailleurs, il se trouve à mi-chemin entre le savoir et l’ignorance. Voici en effet ce qui en est. Aucun dieu ne tend vers le savoir ni ne [204a] désire devenir savant, car il l’est ; or, si l’on est savant, on n’a pas besoin de tendre vers le savoir. Les ignorants ne tendent pas davantage vers le savoir ni ne désirent devenir savants. Mais c’est justement ce qu’il y a de fâcheux dans l’ignorance : alors que l’on n’est ni beau ni bon ni savant, on croit l’être suffisamment. Non, celui qui ne s’imagine pas en être dépourvu ne désire pas ce dont il ne croit pas devoir être pourvu. […] Il va de soi, en effet, que le savoir compte parmi les choses qui sont les plus belles ; or Eros est amour du beau. Par suite, Éros doit nécessairement tendre vers le savoir, et, puisqu’il tend vers le savoir, il doit tenir le milieu entre celui qui sait et l’ignorant. Et ce qui en lui explique ces traits, c’est son origine car il est né d’un père doté de savoir et plein de res­sources, et d’une mère dépourvue de savoir et de res­sources. Telle est bien, mon cher Socrate, la nature de ce démon. »</nowiki>''</blockquote><blockquote>''[Platon, Le Banquet, 203b-204b trad Brisson GF pp. 142-145]''</blockquote><blockquote></blockquote> ----<blockquote>''<nowiki>[1] C’est un problème de logique : sont contraires deux classes exclusives, dont les éléments ne peuvent qe trouver dans l’une et l’autre, ex repos/mouvement. Mais il existe des êtres « intermédiaires » [metaxu = mélangé # pureté de l’idée]</nowiki>''</blockquote><blockquote>''[2] Fille de Zeus et de Dionè.''</blockquote><blockquote>''[3] Première épouse de Zeus.''</blockquote><blockquote>''[4] Platon personnifie Poros qui signifie le « passage » au sens d’une voie maritime, donc au sens figuré « ressource ».''</blockquote><blockquote>''[5] La « Pauvreté » [cf. pénurie]''</blockquote><blockquote>''[6] Dont les discours « enchantent » comme ceux des sophistes.''</blockquote>Cette introduction vise surtout à présenter une démarche. Nous ne rentrons pas pour le moment dans le détail analytique. == Socrate parlait, Platon écrit. == [[Fichier:P1050775 Louvre statue Gudea statue A détail ecriture rwk.JPG|vignette|P1050775 Louvre statue Gudea statue A détail écriture rwk]] Socrate n'est pas dans les dialogues de Platon un personnage historique. Il permet de problématiser des questions et de libérer en fin de parcours Platon de Socrate. Socrate n'a rien écrit. Platon ne cesse de critiquer l'écriture, tout en la pratiquant excellemment. Les premiers dialogues, jusqu'au Ménon inclus, sont des dialogues dits "aporétiques" car on ne trouve pas de solution ou la violence s'installe entre Socrate et certains de ses interlocuteurs.<ref> Le plus violent d'entre eux sera Calliclès dans Gorgias. </ref>Il y a une impuissance philosophique de la parole à convaincre. Platon souligne l'échec de la maïeutique, art d'accouchement des âmes. Mais dans le même temps il y a une critique virulente de l'écriture. On la trouve par exemple dans le mythe de Theuth dans Phèdre (274b-275b). Platon y voit le déploiement d'une mauvaise mémoire attachée à un apprentissage passif et par conséquent une absence de réelle connaissance. == La philosophie et le mythe : puissance de l'imagination pour suppléer à la raison? == Une des réponses à cette situation où le silence remplace la réflexion, est le recours au mythe qui ne se réduit pas à une simple illustration. Il permet d'introduire autrement le questionnement et de réconcilier les interlocuteurs. === Prenons un exemple à propos de la justice si difficile à élaborer : le Mythe de l'anneau de Gygès === <blockquote>''Les hommes prétendent que, par nature, il est bon de commettre l'injustice et mauvais de la souffrir, mais qu'il y a plus de mal à la souffrir que de bien à la commettre. Aussi, lorsque mutuellement ils la commettent et la subissent, et qu'ils goûtent des deux états, ceux qui ne peuvent point éviter l'un ni choisir l'autre estiment utile de s'entendre pour ne plus commettre ni subir l'injustice. De là prirent naissance les lois et les conventions, et l'on appela ce que prescrivait la loi légitime et juste. Voilà l'origine et l'essence de la justice : elle tient le milieu entre le plus grand bien — commettre impunément l'injustice — et le plus grand mal — la subir quand on est incapable de se venger. Entre ces deux extrêmes, la justice est aimée non comme un bien en soi, mais parce que l'impuissance de commettre l'injustice lui donne du prix. En effet, celui qui peut pratiquer cette dernière ne s'entendra jamais avec personne pour s'abstenir de la commettre ou de la subir, car il serait fou. Telle est donc, Socrate, la nature de la justice et telle son origine, selon l'opinion commune.''</blockquote><blockquote> </blockquote><blockquote> ''Maintenant, que ceux qui la pratiquent agissent par impuissance de commettre l'injustice, c'est ce que nous sentirons particulièrement bien si nous faisons la supposition suivante. Donnons licence au juste et à l'injuste de faire ce qu'ils veulent ; suivons-les et regardons où, l'un et l'autre, les mène le désir. Nous prendrons le juste en flagrant délit de poursuivre le même but que l'injuste, poussé par le besoin de l'emporter sur les autres : c'est ce que recherche toute nature comme un bien, mais que, par loi et par force, on ramène au respect de l'égalité. La licence dont je parle serait surtout significative s'ils recevaient le pouvoir qu'eut jadis, dit-on, l'ancêtre de Gygès le Lydien. Cet homme était berger au service du roi qui gouvernait alors la Lydie. Un jour, au cours d'un violent orage accompagné d'un séisme, le sol se fendit et il se forma une ouverture béante près de l'endroit où il faisait paître son troupeau. Plein d'étonnement, il y descendit, et, entre autres merveilles que la fable énumère, il vit un cheval d'airain creux, percé de petites portes ; s'étant penché vers l'intérieur, il y aperçut un cadavre de taille plus grande, semblait-il, que celle d'un homme, et qui avait à la main un anneau d'or, dont il s'empara ; puis il partit sans prendre autre chose. Or, à l'assemblée habituelle des bergers qui se tenait chaque mois pour informer le roi de l'état de ses troupeaux, il se rendit portant au doigt cet anneau. Ayant pris place au milieu des autres, il tourna par hasard le chaton de la bague vers l'intérieur de sa main ; aussitôt il devint invisible à ses voisins qui parlèrent de lui comme s'il était parti. Etonné, il mania de nouveau la bague en tâtonnant, tourna le chaton en dehors et, ce faisant, redevint visible. S'étant rendu compte de cela, il répéta l'expérience pour voir si l'anneau avait bien ce pouvoir ; le même prodige se reproduisit : en tournant le chaton en dedans il devenait invisible, en dehors visible. Dès qu'il fut sûr de son fait, il fit en sorte d'être au nombre des messagers qui se rendaient auprès du roi. Arrivé au palais, il séduisit la reine, complota avec elle la mort du roi, le tua, et obtint ainsi le pouvoir. Si donc il existait deux anneaux de cette sorte, et que le juste reçût l'un, l'injuste l'autre, aucun, pense-t-on, ne serait de nature assez adamantine pour persévérer dans la justice et pour avoir le courage de ne pas toucher au bien d'autrui, alors qu'il pourrait prendre sans crainte ce qu'il voudrait sur l'agora, s'introduire dans les maisons pour s'unir à qui lui plairait, tuer les uns, briser les fers des autres et faire tout à son gré, devenu l'égal d'un dieu parmi les hommes. En agissant ainsi, rien ne le distinguerait du méchant : ils tendraient tous les deux vers le même but. Et l'on citerait cela comme une grande preuve que personne n'est juste volontairement, mais par contrainte, la justice n'étant pas un bien individuel, puisque celui qui se croit capable de commettre l'injustice la commet. Tout homme, en effet, pense que l'injustice est individuellement plus profitable que la justice, et le pense avec raison d'après le partisan de cette doctrine. Car si quelqu'un recevait cette licence dont j'ai parlé, et ne consentait jamais à commettre l'injustice, ni à toucher au bien d'autrui, il paraîtrait le plus malheureux des hommes, et le plus insensé, à ceux qui auraient connaissance de sa conduite ; se trouvant mutuellement en présence ils le loueraient, mais pour se tromper les uns les autres, et à cause de leur crainte d'être eux-mêmes victimes de l'injustice. Voilà ce que j'avais à dire sur ce point.''</blockquote><blockquote>''Platon, République''</blockquote> [[Fichier:Paphos Haus des Theseus - Mosaik Achilles 1.jpg|vignette]] Si on lit de près ce texte, il en ressort que l'homme n'est pas spontanément quelqu'un de tourné vers la justice. En effet, le berger trouve une bague et son premier mouvement est de se l'approprier. Cependant, il avait la réputation d'être juste; ainsi la nature de l'homme est-elle présentée comme injuste et la justice comme une convention nécessaire à la vie commune. Il va donc dérober une bague qui a le pouvoir de lui permettre de se dérober aux regards d'autrui. Cette insistance sur le verbe dérober manifeste que l'homme est spontanément porté à prendre les biens d'autrui, à lui en usurper la propriété, et que sa raison est fondamentalement une raison calculatrice, rusée et dissimulatrice. Cette raison déraisonnable, que l'on retrouvera dans les analyses de Rousseau, est bien loin de la raison valorisée par les philosophes. Au contraire, elle est au service des affects et des intérêts particuliers. Voler les biens d'autrui est un penchant naturel. Ce texte donne à penser que la justice est d'abord là pour protéger la propriété privée. ( Même définition de la justice chez Marx, qui y voit l'origine d'un droit formel, au service d'une liberté et d'une égalité tout autant formelles). La justice malgré les limites que nous venons de poser apparaît nécessaire, comme nécessaire à des hommes seulement guidés par leur intérêt personnel. On retrouvera la même analyse chez Hobbes, à propos de l'homme qu'il qualifie de "loup pour l'homme". Gygès d'ailleurs ne fait pas que voler, il dépouille un cadavre. Acte sacrilège. Aucune morale, aucun respect, sans justice. C'est le portrait d'un barbare. == Quelle est la place du philosophe dans la cité ? == '''Lecture conseillée''' : ''Apologie de Socrate et'' [[Pour lire Platon/Études de quelques passages des dialogues]] : étude de la Lettre VII <br/> Socrate prononce plusieurs discours devant les juges d'Athènes. Il est en effet accusé de corrompre la jeunesse et d'introduire de nouvelles divinités. Pourtant le philosophe passait son temps à interroger les gens et n'enseignait aucune doctrine en particulier, mais pensait avoir une activité politique de première importance en soumettant ces concitoyens à l'épreuve de ses objections. Qu'est-ce qui fait que le philosophe est malvenu dans la cité et y a-t-il une place s'il risque la mort en exerçant son activité ? == Une philosophie politique en réponse au Procès == === Pourquoi obéir aux lois ? === ==== '''Lecture conseillée''' : ''Criton ([[s:Criton|sur Wikisource]])'' ==== Quelques jours avant son exécution, Socrate reçoit la visite en prison d'un vieil ami, Criton<sub>.</sub> Celui-ci lui propose de s'évader. À son grand désarroi, Socrate refuse, et lui explique pourquoi il se doit de se soumettre à sa condamnation. Pourquoi Socrate choisit-il d'obéir à la loi, alors que cette obéissance le conduit à une mort injuste ? Il fait parler les lois dans ce qu'on appelle une prosopopée<ref> Une prosopopée est une figure de rhétorique qui consiste à faire parler une personne morte ou absente, un animal, une chose personnifiée ou encore une abstraction. </ref>.<blockquote>Le texte commence ainsi : </blockquote><blockquote>''Vois si tu l’entendras de cette autre manière : Au moment de nous enfuir ou de sortir d’ici, quel que soit le mot qu’il te plaira de choisir, si les Lois et la République venaient se présenter devant nous, et nous disaient : « Réponds-moi, Socrate, que vas-tu faire ? L’action que tu entreprends a-t-elle d’autre but que de nous détruire, nous qui sommes les Lois, et avec nous la République tout entière, autant qu’il dépend de toi ? Ou te semble-t-il possible que l’État subsiste et ne soit pas renversé, lorsque les arrêts rendus restent sans force et que de simples particuliers leur enlèvent l’effet et la sanction qu’ils doivent avoir ? » Que répondrons-nous, Criton, à ce reproche et à d’autres semblables ? Car on aurait beaucoup à dire, surtout un orateur, sur cette infraction de la loi qui ordonne que les jugements rendus aient tout leur effet. Ou bien dirons-nous aux Lois que la République a été injuste envers nous et qu’elle n’a pas bien jugé ? Est-ce là ce que nous leur dirons ? ou que pourrons-nous leur dire ?''</blockquote><blockquote>''Criton''</blockquote><blockquote>''Rien de plus, Socrate, absolument rien.''</blockquote><blockquote>''Socrate''</blockquote><blockquote>''« Eh quoi ! Socrate, diraient les Lois, est-ce là ce dont nous étions convenues avec toi ? Ou plutôt n’étions-nous pas convenues avec toi que les jugements rendus par la République seraient exécutés ? » Et si nous paraissions surpris de les entendre parler ainsi, elles nous diraient peut-être : « Socrate, ne t’étonne pas de ce langage, mais réponds-nous, puisque tu as coutume de procéder par questions et par réponses. Voyons : quel sujet de plainte as-tu contre nous et contre la République pour entreprendre ainsi de nous renverser ? Et d’abord, n’est-ce pas nous qui t’avons donné la vie ? N’est-ce pas nous qui avons présidé à l’union de ton père et de ta mère, ainsi qu’à ta naissance ? Déclare-le hautement : as-tu à te plaindre de celles d’entre-nous qui règlent les mariages et les trouves-tu mauvaises ? »''</blockquote><blockquote>''Criton''</blockquote><blockquote>''Je ne m’en plains pas, dirais-je.''</blockquote><blockquote>''Socrate''</blockquote><blockquote>''« Est-ce de celles qui déterminent la nourriture de l’enfant et l’éducation selon laquelle tu as été élevé toi-même ? Celles qui ont été instituées pour cet objet n’ont-elles pas bien fait d’ordonner à ton père de t’élever dans les exercices de la musique et de la gymnastique ? »''</blockquote><blockquote>''Criton''</blockquote><blockquote>''Très bien, répondrais-je.''</blockquote><blockquote>''Socrate''</blockquote><blockquote>''« À la bonne heure. Mais, puisque c’est à nous que tu dois ta naissance, ta nourriture et ton éducation, peux-tu nier que tu sois notre enfant, notre esclave même, toi et tes ancêtres ? Et s’il en est ainsi, crois-tu que tu aies contre nous les mêmes droits que nous avons contre toi, et que tout ce que nous pourrions entreprendre contre toi, tu puisses à ton tour l’entreprendre justement contre nous ? Eh quoi ! tandis qu’à l’égard d’un père ou d’un maître, si tu en avais un, tu n’aurais pas des droits égaux, comme de leur rendre injures pour injures, coups pour coups, ni rien de semblable, tu aurais tous ces droits envers les lois et la patrie, en sorte que si nous avons prononcé ta mort, croyant qu’elle est juste, tu entreprendras à ton tour de nous détruire, nous qui sommes les Lois, et la patrie avec nous, autant qu’il est en toi, et tu diras que tu es en droit d’agir ainsi, toi qui te consacres en réalité au culte de la vertu ? Ta sagesse va-t-elle jusqu’à ignorer que la patrie est, aux yeux des dieux et des hommes sensés, quelque chose de plus cher, plus respectable, plus auguste et plus saint qu’une mère, un père et tous les aïeux ? qu’il faut avoir pour la patrie, même irritée, plus de respect, de soumission et d’égard, que pour un père ? qu’il faut l’adoucir par la persuasion ou faire tout ce qu’elle ordonne, et souffrir sans murmure ce qu’elle commande, soit qu’elle nous condamne aux verges ou aux fers, soit qu’elle nous envoie à la guerre pour être blessés et tués ? que notre devoir est de lui obéir, que la justice le veut ainsi, qu’il ne faut jamais ni reculer, ni lâcher pied, ni quitter son poste ? que dans les combats, devant les tribunaux et partout, il faut faire ce qu’ordonnent l’État et la patrie, ou employer les moyens de persuasion que la justice avoue ? qu’enfin, si c’est une impiété de faire violence à son père ou à sa mère, c’est une impiété bien plus grande encore de faire violence à sa patrie ? » Que répondrons-nous à cela, Criton ? Reconnaîtrons-nous que les lois disent la vérité, ou non ?''</blockquote><blockquote>''Criton''</blockquote><blockquote>''Il me semble qu’elles disent la vérité.''</blockquote><blockquote>''Socrate''</blockquote><blockquote>''« Considère donc, Socrate, ajouteraient les Lois, que, si nous disons la vérité, ce que tu entreprends contre nous n’est pas juste. En effet, ce n’est pas assez pour nous de t’avoir donné la vie, de t’avoir nourri et élevé, de t’avoir admis au partage de tous les biens qui étaient en notre pouvoir, toi et tous les autres citoyens, nous déclarons encore, et c’est un droit que nous reconnaissons à tout Athénien qui veut en user, qu’aussitôt qu'il a été reçu dans la classe des éphèbes, qu’il a vu ce qui se passe dans la République, et qu'il nous a vues aussi, nous qui sommes les Lois, il est libre, si nous ne lui plaisons pas, d’emporter ce qu'il possède et de se retirer ou il voudra. Et si quelqu'un d’entre-vous veut aller dans une colonie, parce que nous lui déplaisons, nous et la République, si même il veut aller s’établir quelque part à l’étranger, aucune de nous ne s’y oppose et ne le défend : il peut aller partout où il voudra avec tous ses biens.''</blockquote>Dans cette première partie du texte, les lois rappellent leur but : elles ont donné la vie à Socrate et ont fait de lui un être humain possesseur de plusieurs biens. Elles ne nous forcent pas à rester contre notre gré..nous ne sommes tenus qu'à un devoir de respect si nous vivons sous leur tutelle, sinon nous pouvons partir en choisissant par exemple l'exil. C'est la proposition qui sera faite à Socrate au cours de son procès.. Elles se présentent immédiatement comme protectrices de l'homme et surtout de ses biens. Comme le soulignera plusieurs siècles plus tard, Marx, les lois sont au service de la propriété et à ce titre elles visent non pas la justice mais l'intérêt d'une certaine classe sociale qu'il qualifiera de bourgeoise. Il y a un contrat juridique qui est passé entre les citoyens et la Cité. Ce contrat est à tout moment rétractable. Rien ne peut forcer à rester dans la Cité. Mais rejeter le contrat c'est alors devenir étranger, extérieur. Les lois se présentent dès lors comme ce qui rassemble, autour d'un intérêt commun. Si elles introduisent cet intérêt commun c'est justement parce que les hommes sont plutôt portés spontanément à l'inverse..Animés d'un naturel égoïste ils ont une tendance à oublier les autres.On ne peut forcer les hommes à rester contre leur gré. Aristote considérera que l'homme qui vit hors la Cité ne peut être qu'un Dieu ou une bête..Platon au contraire laisse la porte ouverte...On choisit d'être citoyen, ce qui suppose une liberté de choix. Retour au texte : <blockquote>''Mais quant à celui de vous qui persiste à demeurer ici, en voyant de quelle manière nous rendons la justice et nous administrons toutes les affaires de la république, nous déclarons dès lors que par le fait il s’est engagé envers nous à faire tout ce que nous lui ordonnerions, et s’il n’obéit pas, nous le déclarons trois fois coupable : d’abord, parce qu’il nous désobéit à nous qui lui avons donné la vie, ensuite parce que c’est nous qui l’avons élevé, enfin parce qu’ayant pris l’engagement d’être soumis, il ne veut ni obéir ni employer la persuasion à notre égard, si nous faisons quelque chose qui ne soit pas bien ; et tandis que nous lui proposons à titre de simple proposition, et non comme un ordre tyrannique, de faire ce que nous lui commandons, lui laissant même le choix d’en appeler à la persuasion ou d’obéir, il ne fait ni l’un ni l’autre. Voilà, Socrate, les accusations que tu vas encourir, si tu accomplis ton projet, et tu les encourras plus que tout autre Athénien. »''</blockquote>Que faire alors si on est resté dans la Cité? Obéir répondent les lois. Mais obéir n'est nullement un acte de soumission. Comme le rappelle le texte, on peut toujours discuter la loi. Seul le discours persuasif peut faire face à la loi..Il faut donc argumenter c'est à dire donner les raisons de son désaccord. La loi n'est pas parfaite et peut donner lieu à un conflit  qu'on peut qualifier comme le dira Ricoeur de conflit d'interprétation. La loi est soumise à discussion avant son adoption. Une fois décidée, elle doit être suivie.<blockquote>''Si je leur demandais pour quelle raison elles me traiteraient comme je le mérite, en me disant que je me suis engagé avec elles plus formellement que tout autre Athénien, elles me diraient : « Socrate, tu nous as donné de grandes preuves que nous te plaisions, Nous et la République. Tu n’aurais pas habité Athènes avec plus de constance que tout autre, si elle n’avait pas eu pour toi un attrait particulier. Jamais aucune des solennités de la Grèce n’a pu te faire quitter cette ville, si ce n’est une seule fois, lorsque tu es allé à l’Isthme de Corinthe ; tu n’es sorti d’ici que pour aller à la guerre ; jamais tu n’as entrepris aucun de ces voyages que font tous les hommes ; jamais tu n’as eu le désir de connaître une autre ville ni d’autres lois ; mais toujours nous t’avons suffi, nous et notre ville ; telle était ta prédilection pour nous, tu consentais si bien à vivre sous notre gouvernement, que c’est dans notre ville que tu as voulu entre autres choses devenir père de famille, témoignage assuré qu’elle te plaisait. Enfin, pendant ton procès, tu aurais pu te condamner à l’exil, si tu l’avais voulu, et faire avec notre consentement ce que tu entreprends aujourd’hui malgré nous. Alors tu affectais de ne pas craindre la nécessité de mourir, mais, comme tu disais, tu préférais la mort à l’exil. Et maintenant, sans égard pour ces belles paroles, sans respect pour nous, qui sommes les Lois, tu médites notre ruine, tu fais ce que ferait l’esclave le plus vil, tu vas t’enfuir au mépris des traités et des engagements que tu as pris de te laisser gouverner par nous. D’abord réponds-nous sur cette question : avons-nous raison de dire que tu as pris l’engagement, de fait, et non de parole, de te soumettre à notre empire ? Est-ce vrai, ou non ? » Que dirons-nous à cela, Criton ? Y a-t-il autre chose à faire que d’en convenir ?''</blockquote>Dans ce passage les lois rappellent à Socrate qu'il préfère la mort à l'exil. Fidélité à Athènes ou à soi-même? Trahir la loi c'est finalement se trahir soi-même, ne pas être fiable et sombrer dans un immoralisme qui n'est pas sans rappeler le personnage de Calliclès dans le Gorgias, personnage violent qui refuse la loi au nom d'un droit à la violence et du droit du plus fort.<blockquote>''Criton''</blockquote><blockquote>''Il le faut de toute nécessité, Socrate.''</blockquote><blockquote>''Socrate''</blockquote><blockquote>''« Eh bien, diraient-elles encore, ne violes-tu pas les conventions et les engagements qui te lient à nous ? Et pourtant tu ne les as contractés ni par force, ni par surprise, ni sans avoir le temps de prendre un parti, mais tu as eu, pour y penser, soixante-dix années, pendant lesquelles tu avais la faculté de te retirer, si tu n’étais pas satisfait de nous, et si nos conventions ne te paraissaient pas justes.''</blockquote>Les lois certes sont des conventions..cependant l'homme ne peut faire ce qu'il veut des conventions. La convention ne se confond pas avec l'arbitraire. Il ne s'agit pas pour lui de subir ces conventions mais bien plutôt de les réfléchir afin de ne pas s'y soumettre aveuglément. Ainsi, obéir n'empêche pas l'exercice de la réflexion, ce qu'Alain appelait le droit de résistance:<blockquote>'''''Résistance et obéissance, voilà les deux vertus du citoyen. Par l'obéissance, il assure l'ordre ; par la résistance il assure la liberté. Et il est bien clair que l'ordre et la liberté ne sont point séparables, car le jeu des forces, c'est-à-dire la guerre privée, à toute minute, n'enferme aucune liberté ; c'est une vie animale, livrée à tous les hasards. Donc les deux termes, ordre et liberté, sont bien loin d'être opposés ; j'aime mieux dire qu'ils sont corrélatifs. La liberté ne va pas sans l'ordre ; l'ordre ne vaut rien sans la liberté.'''''</blockquote><blockquote>'''''Obéir en résistant, c'est tout le secret. Ce qui détruit l'obéissance est anarchie ; ce qui détruit la résistance est tyrannie. Ces deux maux s'appellent, car la tyrannie employant la force contre les opinions, les opinions, en retour, emploient la force contre la tyrannie ; et inversement, quand la résistance devient désobéissance, les pouvoirs ont beau jeu pour écraser la résistance, et ainsi deviennent tyranniques. Dès qu'un pouvoir use de force pour tuer la critique, il est tyrannique.'''''</blockquote><blockquote>'''ALAIN''' Propos d'un Normand, 4 septembre 1912</blockquote>Ce texte d'Alain a le mérite de mettre à jour les enjeux de ce texte de Platon: les deux risques quand on ne comprend pas le sens de la loi, c'est d'abord de la réduire à la tyrannie (confusion entre obéir et se soumettre) ou de tomber dans l'anarchie, par le refus d'obéissance (on  confond désobéir et être vigilant, c'est à dire réfléchir) '<blockquote>''Or, tu n’as préféré le séjour ni de Lacédémone, ni de la Crète, dont tu vantes sans cesse le gouvernement, ni d’aucune ville grecque ou barbare, mais tu es sorti d’Athènes moins souvent que les boiteux, les aveugles et les autres infirmes : preuve évidente que tu avais plus d’amour que les autres Athéniens pour cette ville et pour nous-mêmes qui sommes les Lois de cette ville : car peut-on aimer une cité sans en aimer les lois ? Et maintenant seras-tu infidèle à tes engagements ? Non, Socrate, si du moins tu t’en rapportes à nous, et tu ne t’exposeras pas au ridicule en sortant de la ville. »''</blockquote><blockquote>''« Considère, si tu es infidèle à tes engagements et que tu viennes à en violer un seul, quel bien tu te feras à toi-même et à tes amis. Il est à peu près certain que tes amis seront bannis et privés de leur patrie, ou dépouillés de leurs biens ; et toi, si tu vas te retirer dans quelle ville voisine, à Thèbes ou à Mégare, qui sont régies par de bonnes lois, tu y seras reçu, Socrate, comme un ennemi de leur constitution ; tous ceux qui sont attachés à leur pays verront en toi un homme suspect, un corrupteur des lois, et tu confirmeras toi-même l’opinion que tes juges t’ont justement condamné ; car tout corrupteur des lois passera aussi pour corrupteur des jeunes gens et des hommes faibles. Fuirais-tu les villes les plus policées et la société des hommes les plus honnêtes ? Mais, à cette condition, sera-ce la peine de vivre ? Ou bien, si tu les approches, quels discours leur tiendras-tu, Socrate ? Auras-tu le front de leur répéter ce que tu disais ici, que l’homme doit préférer à tout la vertu, la justice, les lois et l’obéissance aux lois ? Ne penses-tu pas qu’ils trouveront bien honteuse la conduite de Socrate ? Il faut bien que tu le penses. Tu t’éloigneras donc de ces villes bien policées, et tu iras en Thessalie chez les amis de Criton ; car le désordre et la licence règnent dans ce pays, et peut-être prendrait-on plaisir à t’entendre raconter la manière plaisante dont tu te serais échappé de prison, enveloppé d’un manteau, affublé d’une peau de bête ou de tout autre déguisement comme font tous les fugitifs, et tout à fait méconnaissable. N’y aura-t-il personne pour s’étonner que dans un âge avancé, lorsque tu n’avais plus, selon toutes les apparences, que peu de jours à vivre, tu aies eu le courage de transgresser les lois les plus saintes pour conserver une existence si misérable ? Non, peut-être, si tu n’offenses personne : autrement, Socrate, tu entendras bien des propos humiliants et indignes de toi. Tu passeras ta vie à t’insinuer auprès de tout le monde par des flatteries et des bassesses serviles ; et que feras-tu en Thessalie que de quêter des festins, comme si tu n’étais allé en Thessalie que pour un souper ? Et tous ces discours sur la justice et les autres parties de la justice, où seront-ils pour nous ? Mais c’est pour tes enfants que tu veux vivre, afin de les nourrir et de les élever ? Quoi donc ! Faut-il les emmener en Thessalie pour les nourrir et les élever ? Faut-il en faire des étrangers, afin qu’ils aient encore cette obligation à leur père ? Supposons que tu ne le fasses pas : s’ils restent ici loin de toi, seront-ils mieux nourris et mieux élevés quand tu ne seras pas avec eux ? Tes amis sans doute en prendront soin pour toi. Mais est-il nécessaire que tu t’exiles en Thessalie, pour qu’ils en prennent soin ? Et si tu vas chez Pluton, les abandonneront-ils ? Non, Socrate, si du moins ceux qui se disent tes amis valent quelque chose ; et il faut le croire. »''</blockquote>La liberté qu'accordent les lois est une liberté contrôlée certes, mais une liberté réelle n'est pensable que si elle s'associe à la contrainte, contrainte que la réflexion personnelle est seule en mesure de donner. La raison est contrainte, mais une contrainte qui libère des passions et impulsions.<blockquote>Notons aussi que l'amitié n'est pensable que si les lois sont suivies. L'engagement n'a de raison d'être que si la loi nous engage..ainsi le respect de la loi est-il le premier de tous les engagements. Ainsi en va-t-il de l'amitié de Criton et Socrate.</blockquote><blockquote>''« Rends-toi donc, Socrate, aux conseils de celles qui t’ont nourri : ne mets ni tes enfants, ni ta vie, ni quoi que ce soit, au-dessus de la justice, afin qu’en arrivant dans les enfers tu puisses alléguer toutes ces raisons pour ta défense devant ceux qui y commandent ; car ici-bas, si tu fais ce qu’on te propose, tu ne rends pas ta cause meilleure, plus juste, plus sainte, ni pour toi, ni pour aucun des tiens, et, quand tu seras arrivé dans l’autre monde, tu ne pourras pas non plus la rendre meilleure. Maintenant, au contraire, si tu meurs, tu meurs victime de l’injustice, non des lois, mais des hommes, au lieu que, si tu sors de la ville, après avoir si honteusement commis l’injustice à ton tour, rendu le mal pour le mal, violé toutes les conventions, tous les engagements que tu as contractés envers nous, maltraité ceux que tu devrais le plus ménager, toi-même, tes amis, ta patrie et nous, alors nous te poursuivrons de notre inimitié pendant ta vie, et après ta mort nos surs, les lois des enfers, ne te feront pas un accueil favorable, sachant que tu as fait tous les efforts qui dépendaient de toi pour nous renverser. Ne suis donc pas les conseils de Criton, mais les nôtres. »''</blockquote><blockquote>''Voilà, sache-le bien, mon cher Criton, les discours que je crois entendre, comme les Corybantes croient entendre les flûtes sacrées ; le son de ces paroles retentit dans mon âme et me rend insensible à tout autre langage. Sois donc certain, telle est du moins ma conviction présente, que tout ce que tu dirais pour les combattre serait inutile. Cependant, si tu crois avoir quelque chose de plus à faire, dis-le.''</blockquote><blockquote>''Criton''</blockquote><blockquote>''Non, je n’ai rien à dire, Socrate.''</blockquote><blockquote>''Socrate''</blockquote><blockquote>''Laisse donc là cette discussion, Criton, et suivons la route où Dieu nous conduit''.</blockquote> == Liste des mythes == ER LE PAMPHYLIEN ATLANTIDE LA CAVERNE PROMÉTHÉE (texte grec et traduction) ENFERS (Texte grec et traduction) 9u60f5qq1k5r0pstjfwu3exih5lpdzz Pour lire Platon/Guide des dialogues/Apologie de Socrate 0 83824 763987 2026-04-19T07:34:07Z PandaMystique 119061 Page créée avec « == Introduction générale == === L’événement et sa portée === En l’an 399 avant notre ère, à Athènes, un vieil homme de soixante-dix ans comparaît devant un tribunal populaire composé de cinq cent un jurés citoyens. Il s’appelle Socrate, fils de Sophronisque ; il est accusé d’impiété et de corruption de la jeunesse. Au terme d’une longue journée d’audience, à une faible majorité d’abord (telle que, selon ses propres mots en 36a, un... » 763987 wikitext text/x-wiki == Introduction générale == === L’événement et sa portée === En l’an 399 avant notre ère, à Athènes, un vieil homme de soixante-dix ans comparaît devant un tribunal populaire composé de cinq cent un jurés citoyens. Il s’appelle Socrate, fils de Sophronisque ; il est accusé d’impiété et de corruption de la jeunesse. Au terme d’une longue journée d’audience, à une faible majorité d’abord (telle que, selon ses propres mots en 36a, un basculement de trente voix aurait suffi à l’acquittement), puis à une majorité plus large pour la peine capitale, il sera condamné à mort. Quelques semaines plus tard, il boira la ciguë dans sa cellule, devant ses disciples, après le retour du bateau sacré de Délos. Cet événement, déjà traumatisant en son temps, est devenu le mythe fondateur de la philosophie occidentale : le moment où la cité met à mort celui qui prétendait y exercer, par la parole et l’examen, un magistère moral. L’''Apologie de Socrate'' est l’œuvre par laquelle Platon, alors âgé d’une trentaine d’années, rend compte de ce procès. On situe sa composition probablement entre 390 et 385, soit une dizaine d’années après les faits. Le texte se présente sans cadre fictionnel : nul narrateur, nul personnage introductif, nulle scène préalable comme il en existe dans la plupart des autres dialogues. Nous sommes plongés d’emblée dans la parole de Socrate, au moment où il se lève pour prendre la parole. Contrairement à la plupart des dialogues platoniciens, Platon s’efface presque entièrement : comme le remarque B. Piettre dans son commentaire, dans aucune autre œuvre on n’a l’impression d’entendre avec une telle proximité la parole de Socrate, « comme s’il nous était donné d’assister au procès »<ref name="piettre">Bernard Piettre, ''Platon, Apologie de Socrate'', traduction, présentation et notes de Bernard et Renée Piettre, Paris, Le Livre de Poche (Librairie générale française), coll. « Libretti », 1997, p. 21.</ref>. Cela ne signifie pas que l’''Apologie'' soit une transcription sténographique du plaidoyer : Platon recompose, réorganise, stylise. Mais il s’efforce, probablement plus que dans tout autre dialogue, de restituer la voix, le ton, l’argumentation du maître. Il convient de rappeler que l’''Apologie'' de Platon n’est pas la seule défense posthume de Socrate. Xénophon a également rédigé une ''Apologie'' qui nous est parvenue, brève et d’un ton psychologique différent, ainsi que des ''Mémorables'' qui reprennent certains motifs<ref>Xénophon, ''Apologie de Socrate'' et ''Mémorables'', trad. P. Chambry, Paris, Garnier-Flammarion, 1967.</ref>. D’autres disciples, comme Eschine de Sphettos, ont composé des écrits socratiques aujourd’hui perdus<ref>Voir Gabriele Giannantoni, ''Socratis et Socraticorum Reliquiae'', 4 vol., Naples, Bibliopolis, 1990.</ref>. Un pamphlet hostile, l’''Accusation de Socrate'' du sophiste Polycrate (probablement composé vers 393-392), circulait et aurait donné à Platon l’occasion de répondre indirectement. La concurrence entre ces « socratiques » explique vraisemblablement en partie le soin que Platon met à camper son propre Socrate, qui finira par s’imposer comme la figure canonique dans la postérité. L’enjeu de l’''Apologie'' dépasse de loin la réhabilitation posthume d’un homme. À travers le procès de Socrate, Platon met en accusation la cité qui l’a condamné ; il ouvre l’espace d’une autre politique, où la philosophie, cet « amour du savoir » (''philosophía''), apparaît comme la véritable vocation civique. Condamner le philosophe, c’est pour Athènes se condamner elle-même, préférer l’ignorance au savoir, la facilité au courage, la flatterie à la vérité. L’''Apologie'' constitue ainsi, selon une formule souvent reprise, l’un des textes fondateurs de la philosophie comme discours distinct, à la fois opposé à la sophistique et à la rhétorique, et radicalement engagé dans l’existence concrète. Elle offre en outre une cristallisation exemplaire de ce qu’on appellera le « dialogue socratique », genre dont Platon fera sa forme propre de pensée. === Contexte historique du procès === Pour comprendre la portée du texte, il faut garder à l’esprit la situation d’Athènes en 399. La cité sort épuisée de la longue guerre du Péloponnèse (431-404), qui l’a opposée à Sparte et s’est soldée par la défaite totale d’Athènes. Après la capitulation, Lysandre, le général spartiate, a imposé un gouvernement oligarchique (les Trente) qui a régné par la terreur pendant environ huit mois (404-403) : exécutions sommaires, confiscations, exil des démocrates. La démocratie a été rétablie par un soulèvement armé parti de Phylè en 403, et, dans le cadre des accords de réconciliation conclus sous l’archontat d’Euclide, une amnistie générale a été proclamée pour ne pas ajouter la guerre civile aux malheurs déjà subis : il était désormais interdit, sous peine de sanctions, de se référer aux événements antérieurs à la restauration<ref>Sur cette amnistie, qu’il ne faut pas confondre avec le décret de Patroclide de 405 (qui portait sur la réhabilitation des ''atimoi''), voir Aristote, ''Constitution d’Athènes'', 39-40 ; et Xénophon, ''Helléniques'', II, 4, 38-43.</ref>. Mais les plaies étaient béantes, et le ressentiment courait souterrainement. Or Socrate avait été, dans les années antérieures, un familier de personnages qui incarnaient précisément ces désastres. Alcibiade, son disciple brillant et fantasque, avait entraîné Athènes dans la désastreuse expédition de Sicile (415-413), scandalisé la cité par l’affaire de la mutilation des hermès, puis fini par trahir sa patrie en passant chez Sparte. Critias, autre de ses proches, avait été l’un des chefs, peut-être le principal, du gouvernement des Trente, artisan de la terreur oligarchique. Charmide, oncle de Platon et lui aussi de l’entourage socratique, avait également appartenu à ce régime. Même si Socrate lui-même avait refusé de collaborer avec les Trente (comme il le rappellera dans son plaidoyer à propos de l’arrestation de Léon de Salamine), sa réputation se trouvait entachée. Le procès de 399, bien que portant officiellement sur des griefs religieux, fonctionne donc aussi comme un règlement de comptes politique, que l’amnistie interdisait pourtant de mener ouvertement. Eschine le rhéteur, quelques décennies plus tard, dira sans détour que les Athéniens avaient condamné Socrate « parce qu’il avait été le maître de Critias »<ref>Eschine, ''Contre Timarque'', I, 173.</ref>. L’accusation est portée par trois hommes. Mélétos, poète tragique obscur, plutôt jeune, est le plaignant officiel, celui qui a déposé la plainte auprès de l’archonte-roi, magistrat compétent pour les affaires religieuses. Mais Socrate sait parfaitement que le véritable moteur de la procédure est Anytos, riche tanneur, homme politique influent de la démocratie restaurée, qui avait participé à la chute des Trente. Son fils, raconte Xénophon, fréquentait Socrate et préférait ses entretiens à la tannerie paternelle : hostilité d’autant plus vive<ref>Xénophon, ''Apologie de Socrate'', 29-31.</ref>. Dans le ''Ménon'' de Platon (90b-95a), Anytos apparaît comme le type même du démocrate borné, ennemi viscéral des sophistes et plus largement des intellectuels<ref>Platon, ''Ménon'', 89e-95a.</ref>. Le troisième accusateur, Lycon, est un orateur dont la fonction semble avoir été de soutenir la plaidoirie par une péroraison énergique. La plainte, dont Diogène Laërce nous a conservé le texte<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 40.</ref>, s’énonce approximativement ainsi : <blockquote>Socrate est coupable de ne pas reconnaître les dieux que reconnaît la cité et d’introduire des divinités nouvelles ; il est aussi coupable de corrompre la jeunesse. Peine requise : la mort.</blockquote> Trois griefs, donc : la négation des dieux traditionnels, l’introduction de nouvelles divinités, la corruption de la jeunesse. Ces trois chefs d’accusation sont intimement liés dans la logique de l’accusation : en introduisant de nouvelles divinités et en niant les anciennes, Socrate aurait, par son enseignement, détourné les jeunes gens du respect dû à la religion civique, donc corrompu la cité dans ses fondements. L’accusation d’impiété (''asébeia'') était une accusation politique au sens fort, puisque la religion à Athènes n’était pas une affaire privée mais la substance même du lien civique. === Le cadre procédural === Le procès se tient à l’Héliée, le tribunal populaire, probablement dans un local situé sur l’agora. Les jurés (501 ce jour-là, nombre impair pour éviter les égalités) ont été tirés au sort le matin même parmi les six mille citoyens qui s’étaient inscrits comme héliastes pour l’année. Une journée entière est consacrée à la procédure, dont le temps est rigoureusement partagé entre l’accusation et la défense au moyen de la clepsydre, horloge à eau. Chaque partie parle pour son propre compte : ni procureur ni avocat. Il est permis de faire corroborer son discours par un orateur plus habile (ce dont Mélétos semble avoir bénéficié avec Lycon), ou de lire un discours écrit par un logographe. La tradition rapporte que Lysias, le plus grand logographe de l’époque, aurait composé pour Socrate un plaidoyer de défense ; Socrate en aurait reconnu la beauté avant de le rejeter comme ne lui convenant pas<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 40 ; Cicéron, ''De l’orateur'', I, 231.</ref>. La procédure, pour un procès où la loi n’a pas fixé la peine à l’avance (on parle alors d’''agôn timētós'', « procès à peine à estimer »), comporte deux votes. Après les plaidoiries, un premier vote tranche sur la culpabilité. En cas de condamnation, l’accusateur propose une peine (ici, la mort), l’accusé doit proposer une contre-peine, et un second vote choisit entre les deux sans possibilité de moyen terme. Cette contrainte procédurale pèse lourd sur la suite : les jurés, contraints de choisir entre deux propositions extrêmes, se trouvent ainsi, par la stratégie de Socrate refusant de proposer une peine crédible, poussés à voter la mort. C’est la procédure en deux temps qui explique la structure tripartite de l’''Apologie'' telle que Platon nous l’a transmise : d’abord le grand plaidoyer de défense (17a-35d), puis le discours de contre-proposition pénale après le verdict de culpabilité (35e-38b), enfin une dernière allocution prononcée après la condamnation à mort, adressée tour à tour aux jurés qui ont voté contre lui et à ceux qui l’ont soutenu (38c-42a). Il faut ajouter que les procès athéniens étaient des spectacles autant que des procédures. Le public y assiste ; les plaideurs usent de toute la rhétorique, des larmes de la famille éplorée, des supplications ostentatoires, des témoignages de moralité. Aristophane, dans ''Les Guêpes'' (422), ridiculise la passion que les Athéniens portaient à ces mises en scène judiciaires et le plaisir qu’ils prenaient à voir les accusés se répandre en lamentations. Socrate, comme on le verra, refuse délibérément toute cette théâtralité, ce qui nourrira, après la condamnation, son reproche aux juges : ils l’ont condamné non pas parce qu’il était coupable, mais parce qu’il n’a pas consenti à s’abaisser. === Structure et dispositif du texte === Lu rétrospectivement à travers la grille qu’Aristote constituera dans sa ''Rhétorique'', le plaidoyer de Socrate laisse reconnaître, dans son grand plan, les parties canoniques du discours judiciaire : on y distingue successivement l’exorde (17a-18a), la diabolè ou dénigrement des accusations antérieures (18a-19d), la narration ou ''diḗgēsis'' qui expose les faits (19d-24b), la réfutation proprement dite (24b-28a), l’amplification et la péroraison (28a-35d). Il ne s’agit pas de prétendre que Platon a composé son texte en suivant consciemment un schéma déjà codifié (la ''Rhétorique'' aristotélicienne est postérieure), mais de remarquer que le plaidoyer épouse, dans ses grandes articulations, les usages rhétoriques de son temps. Cette conformité apparente est constamment subvertie par l’ironie socratique : Socrate adopte la forme du discours judiciaire tout en la démentant dans son contenu, en refusant ses procédés et en retournant ses attentes. Le texte fonctionne ainsi comme une parodie philosophique de la rhétorique : il emprunte à l’art oratoire sa structure, mais seulement pour en exhiber la vanité quand il s’agit de la vérité. == Lecture suivie du texte == === Le premier discours : la défense (17a-35d) === ==== L’exorde (17a-18a) : la vérité contre l’éloquence ==== Dès les premiers mots, Socrate pose le ton. La formule d’ouverture, <blockquote>Quel effet, Athéniens, ont produit sur vous mes accusateurs, je l’ignore,</blockquote> est un exorde classique (en grec ''prooímion'') dont la fonction rhétorique, comme le rappelle Aristote dans la ''Rhétorique'' (III, 14), est de capter la bienveillance et l’attention de l’auditoire<ref>Aristote, ''Rhétorique'', III, 14, 1415a22-b30.</ref>. Socrate s’en sert pour établir immédiatement l’antithèse qui structurera tout son plaidoyer : ses accusateurs ont parlé avec persuasion, mais n’ont dit « rien de vrai ou presque » ; lui, en revanche, dira « toute la vérité ». Cette opposition entre l’éloquence ornée des adversaires et la parole nue de Socrate est un retournement ironique savamment préparé. Les accusateurs ont mis les juges en garde contre le « redoutable discoureur » qu’est Socrate. Mais, rétorque-t-il, s’ils entendent par là celui qui dit la vérité, il concède qu’il est un orateur, bien que pas à leur manière. Ses discours ne seront pas, dit-il, « des discours élégamment tournés, comme les leurs, ni même des discours qu’embellissent des expressions et des termes choisis », mais « des choses dites à l’improviste dans les termes qui [lui] viendront à l’esprit ». L’opposition technique est nette : d’un côté le discours apprêté, fondé sur la sélection du vocabulaire (''onómata''), la tournure des phrases (''rhḗmata'') et les arrangements (''kósmos'') ; de l’autre une parole qui se veut « au hasard », sans préparation. Socrate demande aux juges de lui pardonner cette façon de parler : il a soixante-dix ans, comparaît pour la première fois de sa vie devant un tribunal, et il est donc « étranger à la langue en usage ici ». Comme un véritable étranger qui parlerait dans son dialecte, il entend s’exprimer comme il le fait ordinairement, sur l’agora ou devant les comptoirs des changeurs. Le juge, conclut-il, doit juger sur le fond : <blockquote>si mes allégations sont justes ou non. Telle est en effet la vertu du juge, tandis que celle de l’orateur est de dire la vérité. (18a)</blockquote> Cette prise de position initiale est philosophiquement lourde de conséquences. Elle reformule, dès l’ouverture, l’opposition fondamentale entre la rhétorique (l’art de persuader, tel que le pratiquent sophistes et logographes) et la philosophie (recherche de la vérité par l’examen rationnel). Les sophistes, comme Gorgias ou Protagoras, avaient fait de la rhétorique un instrument neutre, capable de « faire de l’argument le plus faible l’argument le plus fort » (formule qui reviendra explicitement contre Socrate au chef d’accusation). Socrate, à l’inverse, affirme que la parole a pour fonction première de manifester le vrai, et que celui qui se défend en justice ne doit pas jouer des passions ou des artifices, mais soumettre les arguments à l’examen rationnel des jurés. On notera pourtant que cette protestation de simplicité est elle-même une figure rhétorique sophistiquée : celui qui clame qu’il ne sait pas parler parle déjà, et fort bien. Il s’agit de la ''dissimulatio artis'' que Cicéron théorisera plus tard : l’art supérieur consiste à cacher l’art. L’ironie socratique commence dès l’exorde. Socrate se présente comme un ''idiṓtēs'' (un simple particulier, un « idiot » au sens grec), étranger aux codes du tribunal, mais cette posture est elle-même un dispositif stratégique qui retourne contre l’accusation la suspicion de sophistique : ce n’est pas Socrate qui manipule les mots, ce sont les accusateurs. L’inversion est complète. ==== Les « anciens accusateurs » : la calomnie de longue durée (18a-19d) ==== Socrate introduit ensuite une distinction cruciale : il doit répondre à deux catégories d’accusations, celles de ses « accusateurs récents » (Mélétos, Anytos, Lycon), mais aussi, et d’abord, celles de ses « anciens accusateurs », qui ont « depuis de nombreuses années » répandu une image fausse de lui. Ceux-là, il les redoute davantage que les nouveaux, pour trois raisons convergentes. Ils sont d’abord nombreux : il ne s’agit pas de trois personnes identifiables, mais d’une rumeur collective. Leurs accusations sont ensuite anciennes : elles ont eu le temps de s’enraciner dans les esprits. Enfin, ils ont agi auprès des juges « dès l’enfance », à un âge où « vous aviez le moins de défiance », de sorte que les jurés en ont reçu l’empreinte avant même d’avoir l’âge de l’examiner. Quelle est la teneur de cette calomnie ancienne ? Socrate la résume en une formule qui reviendra plusieurs fois dans le texte comme un chef d’accusation fantasmatique : <blockquote>Il existe un certain Socrate, un savant, un « penseur » qui s’intéresse aux choses qui se trouvent en l’air, qui mène des recherches sur tout ce qui se trouve sous la terre et qui de l’argument le plus faible fait l’argument le plus fort. (18b)</blockquote> Cette caricature hétéroclite condense en réalité trois traits initialement distincts. Il y a d’abord la figure du physicien à la manière des présocratiques (Anaxagore, Diogène d’Apollonie), qui spéculait sur les phénomènes célestes (''tà metéōra'') et souterrains. Il y a ensuite celle de l’athée, puisque interroger la nature par la raison revenait, dans la perception populaire, à nier les dieux traditionnels (Anaxagore avait été poursuivi pour cette raison vers 433, Protagoras également, et tous deux avaient été contraints à l’exil). Il y a enfin celle du sophiste, expert en retournements dialectiques, capable de faire triompher n’importe quelle cause par le seul art des mots. L’incohérence de ce portrait (un philosophe de la nature qui serait en même temps un manipulateur rhétorique) ne l’empêche pas d’être efficace dans l’opinion. C’est le propre de la rumeur (''diabolḗ'') : elle n’a pas à être cohérente pour être puissante. Socrate le reconnaît avec lucidité : la force de cette calomnie vient précisément de ce qu’elle ne repose sur aucune source identifiable, qu’on ne peut donc ni l’interroger, ni la réfuter. Combattre ces accusateurs anonymes, dit Socrate, « c’est comme se battre contre des ombres » (18d). C’est une difficulté propre au philosophe dans la cité : face à l’opinion établie, il n’a pas d’adversaire identifiable, donc pas de prise dialectique. Socrate désigne cependant une source probable : les comédies, et en particulier ''Les Nuées'' d’Aristophane, représentées en 423 (vingt-quatre ans avant le procès)<ref>Aristophane, ''Les Nuées'', représentées pour la première fois aux Grandes Dionysies de 423 av. J.-C.</ref>. Il la mentionne d’abord anonymement, parlant d’un <blockquote>Socrate qui se balançait, en prétendant qu’il se déplaçait dans les airs et en débitant plein d’autres bêtises concernant des sujets sur lesquels je ne suis un expert ni peu ni prou. (19c)</blockquote> Le nom d’Aristophane sera explicitement prononcé peu après. Dans cette pièce, le poète comique met en scène un Socrate juché dans une corbeille suspendue, pour mieux « mêler sa pensée subtile à l’air », invoquant les Nuées comme divinités substitutives à celles de la religion populaire, enseignant à un paysan, Strepsiade, puis à son fils Phidippide, comment faire triompher le « Raisonnement injuste » et ruiner par cet apprentissage la piété filiale. La pièce s’achève d’ailleurs sur l’incendie du « Pensoir » socratique. L’enjeu pour Socrate n’est pas seulement de corriger une image fausse : c’est de montrer que les accusations de Mélétos se rabattent exactement sur cette caricature (négation des dieux, introduction de nouveautés religieuses, corruption de la jeunesse), de sorte que contre-attaquer la comédie, c’est déjà dissoudre la plainte. Il y a là un geste tranché : Socrate refuse explicitement d’être confondu, d’une part avec les physiciens qui spéculent sur la nature, d’autre part avec les sophistes qui enseignent la rhétorique contre rémunération. Il énumère d’ailleurs plusieurs sophistes célèbres (Gorgias de Léontinoi, Prodicos de Céos, Hippias d’Élis) et rappelle l’anecdote d’Événos de Paros, qui faisait payer cinq mines pour son enseignement (20b) : somme considérable, correspondant à environ un an et demi de salaire d’un ouvrier qualifié<ref name="brisson">Platon, ''Apologie de Socrate. Criton'', trad. et notes de Luc Brisson, Paris, GF-Flammarion, 2016, p. 131, note 54.</ref>. Socrate, lui, n’a rien à vendre, et c’est précisément cela, comme on le verra, qui témoigne de la pureté de sa démarche. Notons enfin une dimension cruciale de ce moment : Socrate récuse par avance toute identification à la figure du philosophe-penseur retiré du monde, que Platon décrit dans le ''Théétète'' (174a) par l’anecdote célèbre de Thalès tombant dans le puits en contemplant les étoiles<ref>Platon, ''Théétète'', 174a.</ref>. La défense de Socrate sera celle d’un homme de l’agora, immergé dans la cité, engagé dans l’examen de ses concitoyens. Le philosophe socratique n’est pas un contemplatif éloigné des affaires humaines : c’est au contraire le plus urbain des hommes, celui qui ne quitte jamais la ville (comme il le dit dans le ''Phèdre''), celui dont l’activité est fondamentalement politique, même s’il n’exerce aucune charge politique. ==== La narration : l’oracle de Delphes et la naissance de la mission (19d-24b) ==== Pour expliquer l’origine de la calomnie, Socrate engage ce qui correspond à la narration (''diḗgēsis'') du discours rhétorique. Il entreprend de raconter comment il est devenu ce personnage que certains considèrent comme un savant. Cette narration, qui occupe une part importante du discours, contient deux éléments structurants : le récit de l’oracle de Delphes et l’exposé de l’enquête qui s’ensuivit. ===== Le savoir humain et le savoir plus qu’humain (20d-21a) ===== Avant de raconter l’oracle, Socrate pose une distinction fondamentale qui constitue peut-être la pièce conceptuelle centrale de tout le plaidoyer. On dit qu’il est « savant » (''sophós'') : soit. Mais savant en quoi ? Il existe, dit-il, un savoir qui excède la mesure humaine, celui auquel prétendent, moyennant rétribution, les sophistes. Ce savoir-là, Socrate ne le possède pas ; et il aurait « des chances d’être un savant » seulement dans un sens plus modeste, celui d’un savoir qui « se rapporte à l’être humain », une sagesse humaine (''anthrōpínē sophía''). La distinction est capitale. Elle sépare deux ordres de connaissance : celui qui porte sur les choses divines (la nature, le cosmos, les causes premières), que Socrate refuse de revendiquer ; et celui qui porte sur l’humain, sur ce qu’il convient de faire pour vivre bien. Cette distinction préfigure le partage que toute l’histoire de la philosophie reprendra entre philosophie théorique et philosophie pratique, et elle annonce également la réorientation socratique qu’évoque Cicéron dans un passage célèbre : <blockquote>Socrate a fait descendre la philosophie du ciel sur la terre, l’a introduite dans les villes et même dans les maisons, et l’a obligée à s’enquérir de la vie, des mœurs, des choses bonnes et mauvaises. (''Tusculanes'', V, 10-11)<ref>Cicéron, ''Tusculanes'', V, 4, 10-11 : « Socrates autem primus philosophiam devocavit e caelo et in urbibus conlocavit et in domus etiam introduxit... »</ref></blockquote> La philosophie cesse d’être cosmologie pour devenir éthique. Pour prouver l’existence et la nature de ce savoir proprement humain, Socrate invoque un témoin insolite mais sans appel : « le dieu de Delphes », c’est-à-dire Apollon pythien. Le recours à la parole oraculaire, dans un tribunal populaire attaché à la religion civique, est rhétoriquement habile : il retourne l’accusation d’impiété en présentant Socrate comme un serviteur du dieu. ===== L’oracle et l’enquête (21a-23c) ===== C’est le moment où surgit, dans le texte, le récit de l’oracle. Chéréphon, ami d’enfance de Socrate (un homme à la passion impétueuse, démocrate, exilé sous les Trente et revenu avec la démocratie, que la comédie moquait pour sa maigreur ascétique et sa « mine d’endive »<ref>Aristophane, ''Les Guêpes'', v. 1408 ; ''Les Oiseaux'', v. 1296 ; ''Les Nuées'', v. 104.</ref>), était allé un jour à Delphes et avait eu l’audace de demander à la Pythie s’il existait quelqu’un de plus sage que Socrate. La Pythie, prêtresse d’Apollon, parle au nom du dieu et rend des oracles aux consultants : elle répondit que « personne n’était plus sage ». Chéréphon est mort entre-temps (probablement vers 403), mais son frère, présent à l’audience, pourra en témoigner. Cette réponse divine met Socrate dans un profond embarras. Il a « conscience de n’être savant ni peu ni prou ». Mais le dieu, par définition, ne peut mentir : « la loi divine l’interdit » (21b). Comment résoudre l’énigme (''aínigma'') ? Socrate décide alors d’entreprendre une vérification. Il va chercher quelqu’un de plus sage que lui, afin de pouvoir revenir à Delphes et dire : <blockquote>ce dieu m’avait désigné comme le plus sage, mais voici qui l’est davantage.</blockquote> Cette démarche, loin d’être un geste d’orgueil, est présentée comme un service rendu au dieu : c’est pour vérifier l’oracle, non pour le contredire, que Socrate entreprend son enquête. Il accorde à l’oracle une autorité suffisante pour l’interroger méthodiquement, selon un principe qui rappelle la maxime exégétique attribuée à Héraclite : « le maître dont l’oracle est à Delphes ne dit ni ne cache rien : il fait signe »<ref>Héraclite d’Éphèse, fragment DK 22 B 93.</ref>. L’enquête est méthodiquement menée dans trois directions, que Socrate parcourt successivement. Il va d’abord trouver un homme politique (dont il tait le nom, conformément à la pudeur judiciaire) qui passait pour sage. Après l’avoir interrogé, il constate que cet homme se croit sage mais ne l’est pas. Socrate tire alors la leçon capitale qui donnera son contenu à la sagesse humaine : <blockquote>Il y a des chances que je sois moi-même plus sage que cet homme. Car aucun de nous, il est vraisemblable, ne sait rien qui en vaille la peine ; mais lui pense savoir alors qu’il ne sait pas, tandis que moi, tout comme je ne sais pas, je ne pense pas non plus savoir. (21d)</blockquote> Tel est le célèbre savoir du non-savoir socratique : non pas une ignorance totale, mais la conscience lucide et réfléchie de sa propre ignorance, qui vaut davantage que l’illusion de la science. Socrate répète l’opération avec d’autres hommes politiques, et chaque fois la déception est la même, mais pire : plus l’homme est réputé, plus son ignorance est grande ; plus il est humble, plus il est proche du vrai. Socrate passe ensuite aux poètes : auteurs de tragédies, poètes dithyrambiques et autres. Il leur demande ce que signifient leurs propres œuvres, espérant d’eux un savoir, puisqu’ils produisent de la beauté. Il découvre alors qu’ils composent « non par savoir, mais par une sorte de disposition naturelle et par inspiration, comme les devins et les oracles » (22c). Les poètes disent beaucoup de belles choses sans savoir ce qu’elles veulent dire ; et, comme les hommes politiques, ils se croient, à cause de leur art, savants dans d’autres domaines où ils ne le sont pas. L’inspiration poétique est ainsi reconnue comme réelle, mais dissociée du savoir : le poète est possédé par une muse, non par une compétence. Cette analyse, qui reviendra dans le ''Ion'', est une pièce importante de la pensée platonicienne sur l’art<ref>Platon, ''Ion'', 533d-535a.</ref>. Socrate examine enfin les artisans (''cheirotéchnai''). Ici, la situation est plus nuancée. Les artisans possèdent une compétence réelle, une ''tékhnē'', que Socrate ne songe pas à leur dénier. Mais cette compétence les induit à se croire savants « dans les choses les plus importantes », c’est-à-dire dans les questions morales et politiques, alors qu’ils ne le sont pas. L’artisan, parce qu’il sait faire une paire de chaussures, se croit en droit d’avoir un avis éclairé sur la justice. C’est là un point crucial : Socrate reconnaît la légitimité de la ''tékhnē'' dans son domaine propre, mais refuse l’extrapolation de la compétence technique à la sagesse pratique. La conclusion de l’enquête est double. D’une part, Socrate conclut à la véracité de l’oracle : sa sagesse consiste en ce qu’il ne croit pas savoir ce qu’il ne sait pas. D’autre part, il comprend que l’oracle ne le désignait pas ''lui'' spécifiquement comme sage, mais se servait de son nom à titre d’exemple : <blockquote>il y a des chances, Messieurs, pour qu’en réalité le sage, ce soit le dieu, et que dans ce fameux oracle il veuille dire que la sagesse humaine a bien peu de valeur, et même aucune ; et il est clair qu’en désignant Socrate il s’est servi de mon nom pour me prendre en exemple, comme s’il disait : « Le plus sage d’entre vous, hommes, est celui qui, comme Socrate, a reconnu qu’en réalité sa sagesse ne vaut rien. » (23a-b)</blockquote> Socrate devient ainsi, non pas un savant, mais un exemple (''parádeigma'') par lequel le dieu invite tous les hommes à reconnaître la misère de leur prétention au savoir. La sagesse n’est pas une propriété de l’individu, mais une relation à l’ignorance ; elle est, on pourrait dire, éminemment ''maïeutique'' : elle fait accoucher les autres de la conscience de leur propre ignorance. ===== La mission et la naissance de la haine (23b-24b) ===== De cette interprétation de l’oracle naît la mission socratique. Socrate continue, « en service pour le dieu » (''hypēresía toû theoû''), à enquêter sur quiconque prétend être sage, pour manifester chaque fois qu’il ne l’est pas. Cette activité l’a réduit à « une grande pauvreté », car elle lui a occupé toute sa vie au détriment de ses affaires. Mais elle a aussi suscité contre lui des haines innombrables, chaque fois qu’il a démasqué l’ignorance d’un puissant ou d’un réputé. On touche ici à un mécanisme psychologique qu’il faut bien mesurer : nul n’aime être convaincu d’ignorance, surtout publiquement ; celui qui le fait, même par amour du vrai, se constitue des ennemis à proportion de sa rigueur. Les jeunes gens de bonne famille, ceux qui disposent de loisir (''scholḗ''), prennent plaisir à le voir faire ; ils tentent à leur tour d’imiter sa démarche, et ainsi « se font eux-mêmes haïr par ceux qu’ils examinent » (23c), qui ne s’en prennent pas à ces jeunes, mais à Socrate. Ce dernier devient ainsi le responsable imaginaire d’une activité critique qui déborde largement sa personne. C’est le ressort profond de l’accusation de « corruption de la jeunesse » : ce n’est pas que Socrate ait enseigné le mal, c’est que ses méthodes, imitées par ses disciples, font éclater un scandale qu’on veut lui imputer. C’est ainsi que s’est formée la réputation selon laquelle Socrate serait un « corrupteur de la jeunesse », et qu’on a repris contre lui la vieille caricature : un homme qui fait des recherches sur le ciel et la terre et qui fait triompher la mauvaise cause. Socrate tire la conclusion rhétorique : « c’est en disant la vérité que je me fais des ennemis », ce qui est, dit-il, la preuve qu’il dit vrai. Les causes de l’accusation sont là : non dans une faute réelle, mais dans le ressentiment de ceux qu’il a démasqués. Cette section du plaidoyer est philosophiquement fondamentale. Elle met en place plusieurs concepts clefs du socratisme tel que Platon l’entend. D’abord, la philosophie comme examen (''exétasis'') et non comme doctrine : on ne peut enseigner la philosophie de Socrate parce qu’elle n’est pas un corps de propositions à transmettre, mais une pratique à exercer. Ensuite, le savoir de l’ignorance comme seule forme accessible du savoir humain. Enfin, la philosophie comme mission divine, ce qui lui confère une légitimité supérieure à celle des institutions politiques. Cette dimension religieuse de la philosophie est essentielle à l’économie du texte : si la mission est divine, elle ne peut être interrompue par un décret humain, fût-il celui d’un tribunal souverain. On notera également que cette démarche, en démasquant l’ignorance des prétendus savants, introduit une différence entre savoir et non-savoir sur des sujets où la démocratie athénienne supposait, pour délibérer, qu’il n’y en avait pas. Comme l’explique un passage du ''Protagoras'' (319c-d), rédigé par Platon peu après l’''Apologie'', les assemblées démocratiques distinguent les sujets techniques (sur lesquels seuls les compétents s’expriment) et les sujets politiques (sur lesquels tous les citoyens, cordonniers, potiers, tanneurs ou menuisiers, peuvent donner leur avis)<ref>Platon, ''Protagoras'', 319b-d.</ref>. La délibération collective suppose que sur les sujets politiques, il n’existe pas de différence de compétence entre les citoyens. Or l’enquête socratique a précisément pour effet de réintroduire cette différence sur les objets où l’institution démocratique la refoulait. Se prétendre ignorant soi-même et révéler l’ignorance d’autrui sur les questions de justice ou de vertu, c’est mettre en cause le principe majoritaire là où il s’applique. On comprend en quoi, malgré les apparences, la posture socratique est subversive pour la démocratie athénienne : elle frappe à sa racine épistémologique. ==== La réfutation de Mélétos (24b-28a) ==== Socrate en vient maintenant aux accusations officielles. Il relit la plainte, <blockquote>Socrate est coupable de corrompre la jeunesse et de reconnaître non pas les dieux que la cité reconnaît, mais, au lieu de ceux-là, des divinités nouvelles,</blockquote> et entreprend de l’examiner point par point. Il utilise alors la prérogative que la loi athénienne accorde à l’accusé : interroger directement son accusateur, qui est tenu de répondre. Ce qui suit est l’un des grands morceaux dialectiques de l’''Apologie''. Socrate, en un véritable elenchos (cette réfutation par interrogation qui est sa marque de fabrique), démonte successivement les trois volets de l’accusation. On notera d’emblée un jeu de mots grec qui court tout l’interrogatoire : le nom de Mélétos (''Mélētos'') évoque le verbe ''mélei'' (« se soucier de »). Socrate va reprocher à Mélétos, précisément, de ne pas se soucier (mélein) des choses dont il prétend se soucier. L’ironie est ciselée jusque dans l’onomastique. La méthode que Socrate utilise ici est l’''elenchos'' : il part des thèses de l’interlocuteur, l’amène par des questions à en reconnaître les conséquences, puis lui fait constater la contradiction entre ces conséquences et d’autres thèses qu’il tient également pour vraies. Cette procédure, qui ne démontre rien positivement mais montre qu’une thèse est intenable, est le moteur de la dialectique socratique dans les dialogues de jeunesse de Platon<ref>Sur la méthode de l'elenchus, voir Gregory Vlastos, « The Socratic Elenchus », ''Oxford Studies in Ancient Philosophy'', I, 1983, p. 27-58.</ref>. ===== Qui rend les jeunes meilleurs ? (24b-25c) ===== Premier volet : la corruption de la jeunesse. Socrate commence par un piège dialectique. Si Mélétos accuse quelqu’un de corrompre les jeunes, c’est qu’il a à l’esprit ce qui les rend meilleurs. Qu’il le dise donc. Mélétos, pris au dépourvu, balbutie : « Les lois ». Mais ce n’est pas une personne, objecte Socrate. Il insiste : quel ''homme'' rend les jeunes meilleurs ? Mélétos répond alors, au gré d’une improvisation visiblement embarrassée : les juges, puis les membres du Conseil, puis ceux de l’Assemblée, bref tous les Athéniens ; tous, sauf Socrate. Socrate retourne alors l’argument par une analogie mémorable, celle des chevaux. Suppose-t-on que tous les hommes rendent les chevaux meilleurs, et qu’un seul les corromprait ? Non, c’est évidemment le contraire : pour les chevaux comme pour tous les animaux, seuls quelques spécialistes savent les rendre meilleurs, et la plupart des gens, s’ils s’en occupent, les nuisent. Il en va de même pour les jeunes gens. L’éducation est un art, et l’art suppose la compétence, qui n’est pas le partage du plus grand nombre. En prétendant que tous éduquent bien sauf Socrate, Mélétos révèle qu’il n’a jamais sérieusement réfléchi à ce dont il parle : il s’est désintéressé de la question. Ce qui est précisément ce que le jeu de mots sur son nom entendait suggérer. Ce premier argument est intéressant par ce qu’il laisse apercevoir de la position de Socrate à l’égard de la démocratie. L’éducation, comme le souligne le commentaire de C. Chrétien, est « une affaire politique tant la formation de l’homme paraît indissociable de celle du citoyen »<ref name="chretien">Claude Chrétien, ''Platon, Apologie de Socrate'', Paris, Hatier, coll. « Profil philosophie », 1993, p. 44.</ref>. Or, pour Mélétos et pour les Athéniens en général, la cité est à elle-même sa propre pédagogie : chaque citoyen forme, par son exemple et sa participation à la vie publique, les futurs citoyens. Socrate, à l’inverse, soutient que l’éducation, comme les autres arts, relève d’une compétence particulière, ce qui va à l’encontre du présupposé démocratique d’une compétence civique également partagée. En paraissant ne se battre que sur un détail logique, Socrate attaque en réalité le principe même de la démocratie athénienne : celui selon lequel chaque citoyen est naturellement compétent pour la délibération politique. ===== Corrompre volontairement ? (25c-26a) ===== Deuxième pièce du dispositif : Socrate demande à Mélétos s’il le pense corrompre les jeunes volontairement ou involontairement. Mélétos, rageusement, répond : volontairement. Mais Socrate montre alors que cette réponse est intenable. Car si les méchants font du mal à leur entourage, et les bons du bien, alors corrompre volontairement les gens qui nous entourent (avec lesquels on vit) c’est s’exposer soi-même à en pâtir. Personne, pas même le plus ignorant, ne choisit délibérément de se nuire à soi-même. Donc, ou bien Socrate ne corrompt pas, ou bien, s’il corrompt, c’est involontairement. Et dans ce cas, la loi ne prévoit pas un procès mais une remontrance privée : si on m’avait averti, j’aurais cessé. En me traînant devant le tribunal, Mélétos prouve que son but n’est pas de me corriger mais de me punir ; ce qui est contradictoire avec l’idée d’une faute involontaire. Cet argument repose sur l’un des principes les plus fermes du socratisme, le célèbre paradoxe socratique selon lequel nul ne fait le mal volontairement (''oudeís hekṓn hamartánei''). Socrate y croit sincèrement : si l’on savait vraiment ce qu’est le bien, on ne pourrait pas ne pas le vouloir. Le vice n’est pas une perversion de la volonté, c’est une forme d’ignorance. Cette thèse, qui paraîtra contre-intuitive à toute la tradition postérieure (notamment chrétienne, qui mettra l’accent sur la malice du mal), sera développée dans plusieurs dialogues platoniciens, notamment le ''Gorgias'' et le ''Protagoras''<ref>Platon, ''Gorgias'', 466a-468e, 509c-e ; ''Protagoras'', 352b-358d.</ref>. Aristote, dans l’''Éthique à Nicomaque'', la critiquera comme négligeant la réalité de la ''akrasía'' (faiblesse de la volonté)<ref>Aristote, ''Éthique à Nicomaque'', VII, 2-3, 1145b21-1147b19.</ref>. Mais l’argument sert ici surtout à piéger Mélétos dans une contradiction : soit tu m’accuses d’une faute involontaire, et le procès est illégitime (car la procédure judiciaire vise des fautes intentionnelles) ; soit tu m’accuses d’une faute volontaire, mais celle-ci est psychologiquement impossible (personne ne choisit de se nuire à soi-même). Dans les deux cas, la plainte s’effondre. Remarquons la précision juridique : Socrate joue habilement sur la distinction athénienne entre fautes volontaires (justiciables) et involontaires (pour lesquelles la remontrance privée, la ''nouthesía'', était la procédure appropriée). ===== L’athéisme et les divinités démoniques (26a-28a) ===== Troisième volet : la question religieuse, qui est le cœur même de l’accusation. Socrate demande à Mélétos de préciser son propos : l’accuse-t-il de reconnaître des dieux différents de ceux de la cité, ou de ne reconnaître aucun dieu du tout ? La distinction est cruciale, car la plainte elle-même est ambiguë (elle évoque des « divinités nouvelles », ce qui présuppose que Socrate croit à des divinités, mais lui reproche aussi de ne pas reconnaître celles de la cité). Mélétos, avec une maladresse que Socrate exploite pleinement, s’emporte et répond : « aucun dieu du tout ». Il ajoute même, piégé par sa propre fureur, que Socrate prétend, à la manière d’Anaxagore, que « le soleil est une pierre et la lune une terre ». Socrate saisit l’occasion avec une précision chirurgicale. D’abord, il ridiculise la confusion : Anaxagore, en effet, a soutenu cette thèse physicienne, mais les livres d’Anaxagore, qui se trouvent au marché (à l’orchestre, endroit de l’agora où l’on vendait les livres), coûtent « tout au plus une drachme » ; pourquoi donc accuser Socrate d’avoir inventé ce qu’on peut lire partout et qui n’est pas de lui ? Le trait est doublement dévastateur : il innocente Socrate et il prouve l’incompétence de Mélétos, qui ne distingue pas Socrate d’Anaxagore. Ensuite, il tend son piège principal. La plainte officielle dit que Socrate introduit de nouvelles divinités (''daimónia''). Or Mélétos vient d’affirmer que Socrate n’admet aucun dieu du tout. Ces deux affirmations sont contradictoires : on ne peut pas à la fois reconnaître des divinités et ne reconnaître aucun dieu. Mélétos se contredit lui-même, et sous serment, car la plainte avait été déposée sous serment réciproque (''antōmosía''). Socrate poursuit par un syllogisme subtil. Peut-on reconnaître des « phénomènes démoniques » (''daimónia prágmata'') sans reconnaître l’existence de démons ? De même que l’on ne peut reconnaître des phénomènes hippiques sans reconnaître les chevaux, ni des phénomènes musicaux sans reconnaître les musiciens, on ne peut reconnaître des phénomènes démoniques sans reconnaître les démons. Or les démons, selon la religion grecque traditionnelle, sont soit des dieux soit des enfants de dieux. Donc, si Socrate reconnaît des démons, il reconnaît aussi des dieux, ou à tout le moins des êtres divins. La plainte se contredit elle-même : Mélétos affirme à la fois que Socrate ne reconnaît aucun dieu et qu’il reconnaît des démons, donc des dieux. Ce raisonnement est brillant sur le plan dialectique, mais il a suscité la perplexité des commentateurs. Il repose sur une définition traditionnelle des démons comme « enfants des dieux », qui n’est pas toujours stabilisée dans la culture grecque (chez Hésiode, les démons sont plutôt des hommes de l’âge d’or devenus esprits), et laisse entière la question de savoir ce que sont les « nouvelles divinités » dont Socrate était réellement accusé. La plupart des commentateurs modernes estiment que l’accusation visait précisément le fameux ''daimónion'' socratique, la voix intérieure divine dont Socrate parlera plus loin, qui serait apparue aux Athéniens comme une divinité privée, nouvelle, donc impie car non reconnue par la cité. Socrate, en déplaçant le débat sur la question abstraite de l’existence ou non des démons, élude habilement cette difficulté. C’est un procédé dialectique, non un argument de fond. Il faut aussi percevoir le geste de fond. Comme le note Claude Chrétien, Socrate, par ce raisonnement, rattache sa croyance en des « phénomènes démoniques » à une croyance minimale mais ferme en la divinité, sur un mode qui relève d’une théologie négative : il ne dit rien de positif sur les dieux, mais affirme seulement que quelque chose, dans l’expérience humaine, manifeste leur existence<ref name="chretien-30">Claude Chrétien, ''Platon, Apologie de Socrate'', ''op. cit.'', p. 30-31.</ref>. Cela est cohérent avec son agnosticisme sur les mythes (dans le ''Phèdre'', Socrate refuse de spéculer sur les aventures de Borée enlevant Orithye<ref>Platon, ''Phèdre'', 229b-230a.</ref>) et avec sa dévotion pratique à la religion de la cité (on le voit obéir aux rites dans plusieurs dialogues). Socrate est pieux en acte, agnostique en théorie : il reconnaît une transcendance divine, sans prétendre en décrire la nature. Cette position, fine et paradoxale, est à l’origine d’une tension féconde dans toute la théologie philosophique ultérieure. À la fin de cette section, Socrate conclut qu’il a suffisamment montré que l’accusation de Mélétos est sans consistance. Mais, ajoute-t-il avec lucidité, il sait bien que ce n’est pas elle qui le fera condamner : c’est la vieille calomnie, la haine accumulée au fil des années. D’autres hommes justes, avant lui, ont subi le même sort, et beaucoup en subiront après. ==== La mission divine et le modèle héroïque (28a-30c) ==== Ayant réfuté l’accusation formelle, Socrate entreprend alors ce que Piettre appelle une « amplification » : il justifie tout son mode de vie. L’''Apologie'' bascule ici d’un plaidoyer juridique vers une proclamation philosophique. La question n’est plus « suis-je coupable de ces griefs précis ? » mais : « comment justifier un mode d’existence qui expose à la mort ? » C’est à partir de ce moment que l’''Apologie'' cesse d’être un simple plaidoyer pour devenir un manifeste. ===== Le modèle d’Achille (28b-d) ===== Socrate anticipe une objection qu’un auditoire athénien pouvait sincèrement formuler : n’as-tu pas honte, Socrate, d’avoir mené une existence qui t’expose à mourir ? Il y répond par l’évocation des héros homériques, et notamment d’Achille, la figure de référence de la vertu guerrière grecque. Quand Thétis, sa mère, lui annonça qu’il mourrait s’il vengeait Patrocle en tuant Hector, Achille répondit, selon l’''Iliade'' : <blockquote>que je meure immédiatement, pour peu que je punisse le coupable, plutôt que de rester ici, à être la risée de tous, assis sur mes vaisseaux, poids inutile de la terre.<ref>Homère, ''Iliade'', XVIII, v. 96-104 ; cité par Socrate en ''Apologie'', 28c-d.</ref></blockquote> Achille a donc méprisé la mort pour préserver l’honneur. Celui qui occupe une place, explique Socrate, doit y rester, au péril de sa vie, « sans tenir compte d’autre chose que du déshonneur ». Cet argument est une adaptation du modèle homérique, et non une simple reprise. Socrate transforme l’héroïsme aristocratique d’Achille (celui d’un demi-dieu, fils d’une déesse) en une vertu démocratique accessible à tout soldat de la phalange hoplitique : il s’agit de tenir son poste, quelle que soit sa place, qu’on l’ait choisie ou qu’on y ait été affecté par son chef (28d). La vertu n’est plus aristocratique, elle est civique ; elle n’est plus la propriété d’une élite, elle est accessible à quiconque occupe sa place avec fermeté. Cette démocratisation de la vertu héroïque prépare la transposition qui va suivre. Socrate rappelle d’ailleurs son propre passé militaire. Il a combattu à Potidée (432-429), à Amphipolis (424), et surtout à Délion (424), où son courage fut loué par Alcibiade dans le ''Banquet'' (219e et suivants<ref>Platon, ''Banquet'', 219e-221c.</ref>) et par le général Lachès dans le dialogue du même nom (''Lachès'', 181a-b<ref>Platon, ''Lachès'', 181a-b.</ref>). Comme ces soldats qui ne quittent pas leur poste, Socrate ne peut quitter celui qui lui a été assigné, par le dieu. ===== Le poste assigné par le dieu (28d-29a) ===== Socrate pose alors la transposition capitale : <blockquote>le poste qu’on m’a assigné, moi, est celui du philosophe, qui doit vivre en philosophant, en s’examinant soi-même et en examinant les autres. Je ne peux le quitter par crainte de la mort, pas plus qu’un soldat ne peut quitter le sien.</blockquote> La philosophie est ainsi présentée comme une assignation divine, équivalente à l’ordre d’un chef de guerre, et plus impérieuse encore, puisque l’ordre vient du dieu. Cette analogie entre vie philosophique et vie militaire, qui fera carrière dans toute la tradition stoïcienne (Sénèque, Épictète, Marc Aurèle en useront abondamment<ref>Voir notamment Épictète, ''Entretiens'', I, 9, 24 ; III, 24, 31-36 ; Marc Aurèle, ''Pensées'', III, 5 ; VII, 45.</ref>), fonde la philosophie comme service, comme ''officium'', comme devoir qu’on ne peut déserter sans se déshonorer. ===== L’ignorance de la mort (29a-b) ===== Vient alors l’un des passages les plus célèbres du texte, où Socrate renverse la psychologie commune du courage : <blockquote>Craindre la mort, Athéniens, ce n’est rien d’autre que se donner pour savant sans l’être ; c’est donner l’impression qu’on sait ce qu’on ne sait pas. (29a)</blockquote> Car personne ne sait ce qu’est la mort, ni si elle n’est pas pour l’homme le plus grand des biens ; mais on la redoute comme si l’on savait qu’elle est le plus grand des maux. C’est là, dit Socrate, la forme la plus répréhensible d’ignorance : croire savoir ce qu’on ne sait pas, donc la même erreur que celle des faux sages qu’il a démasqués dans son enquête. Le raisonnement est d’une rigueur remarquable. Il articule le savoir du non-savoir à l’éthique du courage. Socrate, lui, sait qu’il ne sait rien de la mort ; il ne la craint donc pas. Mais il sait en revanche, et c’est la seule « exception » à son ignorance proclamée, que <blockquote>commettre une injustice et désobéir à un meilleur que soi, dieu ou homme, cela je sais que c’est mauvais et honteux. (29b)</blockquote> On voit ici le dispositif éthique qui va commander toute la suite : entre un mal certain (l’injustice et la lâcheté) et un mal supposé mais incertain (la mort), le sage choisit sans hésiter d’éviter le premier. Le courage philosophique n’est donc pas un mépris enthousiaste de la mort, comme celui d’Achille, c’est une lucidité sur ce qui est réellement à craindre. Ce renversement de la psychologie héroïque en lucidité rationnelle est l’un des gestes fondateurs de la philosophie morale. ===== L’hypothèse de l’acquittement conditionnel (29c-30c) ===== Socrate imagine alors une situation extrême, une sorte d’expérience de pensée. Supposons que les juges lui offrent de l’acquitter à la condition qu’il cesse de philosopher. Alors Socrate répondrait : <blockquote>Athéniens, je vous suis reconnaissant et je vous aime, mais j’obéirai au dieu plutôt qu’à vous ; et tant qu’il me restera un souffle de vie, tant que j’en serai capable, je ne cesserai, soyez-en sûrs, de philosopher, de vous exhorter et de m’expliquer avec tel ou tel d’entre vous. (29d)</blockquote> Il continuerait à dire à chacun la formule qui résume toute sa mission : <blockquote>Ô excellent homme, toi qui es d’Athènes, la cité la plus grande et la plus réputée pour son savoir et sa puissance, tu n’as pas honte de t’occuper de ta fortune et des moyens de t’enrichir le plus possible, de ta réputation, des honneurs, alors que de ton intelligence, de la vérité, de ton âme et des moyens de la perfectionner, tu ne t’en occupes et ne t’en soucies aucunement ? (29d-e)</blockquote> On est ici au cœur du message socratique, tel que Platon l’a consigné. Le renversement qu’opère ce passage est philosophiquement majeur. D’une part, Socrate affirme que son obéissance au dieu l’emporte sur son obéissance à la cité : c’est, en puissance, toute la doctrine de la désobéissance civile au nom d’une norme transcendante. D’autre part, il renverse la hiérarchie des biens : la vertu ne vient pas de l’argent, mais l’argent et tous les autres biens viennent de la vertu ; il faut donc se soucier prioritairement de son âme (''psuchḗ''), et non de ses biens matériels ou de sa réputation. C’est la naissance, dans la philosophie occidentale, du souci de soi (''epiméleia heautoû'') compris comme soin de l’âme et examen permanent de soi-même, thème qui parcourra toute la tradition ultérieure, des écoles hellénistiques aux spirituels chrétiens, jusqu’aux lectures contemporaines de Michel Foucault qui en fera un objet majeur de ses derniers cours au Collège de France<ref name="foucault-hs">Michel Foucault, ''L’Herméneutique du sujet. Cours au Collège de France, 1981-1982'', éd. F. Gros, Paris, Gallimard/Seuil, coll. « Hautes Études », 2001.</ref>. Socrate ajoute alors l’une de ses déclarations les plus provocatrices : <blockquote>Là-dessus, Athéniens, croyez-en ou non Anytos, acquittez-moi ou ne m’acquittez pas, toujours est-il que je ne changerai pas de conduite, même si je devais souffrir mille morts. (30c)</blockquote> La défense ne demande plus un acquittement ; elle proclame la pérennité de la mission, quel que soit le verdict. Socrate, à ce moment précis du plaidoyer, cesse d’être un accusé pour devenir un apôtre. Cette attitude explique, rétrospectivement, l’incompréhension et l’irritation des jurés : il ne se défend pas, il les défie. ==== Socrate, « taon de la cité » (30c-31c) ==== Socrate affirme alors, avec une audace étonnante, <blockquote>si vous me condamnez à mort, ce n’est pas à moi, mais à vous-mêmes, que vous ferez le plus de tort. (30c)</blockquote> Car ni Mélétos ni Anytos ne peuvent le léser véritablement : ils peuvent le tuer, l’exiler, le priver de ses droits civiques (''atimía''), choses que certains tiendraient pour de grands malheurs, mais lui ne les tient pas pour tels. Le vrai mal est celui que font à leur âme ceux qui entreprennent de tuer injustement. Cette thèse, que Platon développera dans le ''Gorgias'' (469c) sous la forme « il vaut mieux subir l’injustice que la commettre »<ref>Platon, ''Gorgias'', 469c : « [...] je choisirais de subir plutôt que de commettre l’injustice. » Voir aussi 474b-479e.</ref>, est probablement la plus radicale de toutes les thèses morales de l’antiquité. Surgit alors la célèbre image du taon. Socrate est <blockquote>un homme attaché à la cité par le dieu, comme le serait un taon au flanc d’un cheval de grande taille et de bonne race, mais qui se montrerait un peu mou en raison même de sa taille et qui aurait besoin d’être réveillé par l’insecte. (30e)</blockquote> Athènes est ce cheval noble mais assoupi ; Socrate est l’insecte qui le pique, le réveille, le harcèle. Le dieu lui a donné cette mission, qui explique qu’il passe son temps à aborder chacun, « comme un père ou un frère plus âgé », pour le persuader d’avoir souci de la vertu. Cette image mérite qu’on s’y arrête, tant elle est dense. Elle articule trois éléments. D’abord, la noblesse du cheval : Athènes n’est pas critiquée absolument, mais reconnue pour ce qu’elle est, la plus belle cité du monde grec, de « grande taille et de bonne race ». Ensuite, son engourdissement : cette grandeur même la rend molle, somnolente, incapable de s’ébrouer spontanément. Enfin, la nécessité du taon : seule une figure dérangeante, insupportable, inutile en apparence, peut réveiller la cité. Le taon n’est pas à sa place dans le cheval ; il est un corps étranger, irritant ; mais précisément, c’est de cette position dérangeante que vient son utilité. La philosophie est pensée ici comme critique nécessaire, comme dissidence féconde, comme décalage qui maintient la cité vivante. On voit se dessiner une dialectique subtile. Socrate est indissociablement dedans et dehors : citoyen d’Athènes, engagé dans sa cité, respectueux de ses lois au point d’accepter la mort plutôt que de fuir (comme l’exprimera le ''Criton''<ref>Platon, ''Criton'', 50a-54d.</ref>), et en même temps étranger à ses conformismes, à ses illusions, à ses complaisances. Sans le taon, le cheval dormirait ; mais le cheval peut aussi, d’un mouvement irrité, écraser le taon. C’est exactement ce qui se passe au procès. L’image contient en elle-même une prophétie : tuer le taon, c’est se priver de la piqûre bienfaisante, condamner la cité au sommeil. D’où la prédiction de Socrate : <blockquote>en suite de quoi, vous passeriez votre vie à dormir, à moins que le dieu, ayant souci de vous, ne vous envoie quelqu’un d’autre. (31a)</blockquote> La suite de l’histoire, à tout le moins celle de la pensée, dira que cet autre sera Platon lui-même, puis Aristote, et la longue lignée des philosophes que l’''Apologie'' aura rendus possibles. Socrate apporte ensuite une preuve empirique de son désintéressement : sa pauvreté. Si son activité avait un but intéressé, si elle rapportait un salaire, on pourrait douter de la pureté de ses motivations. Mais ses accusateurs, malgré leur acharnement, n’ont pu produire aucun témoin attestant qu’il ait jamais exigé ou reçu un salaire. Sa misère est la meilleure preuve qu’il dit vrai. Cette insistance sur la gratuité de son enseignement est une pique adressée aux sophistes, qui se faisaient richement rémunérer, et un trait supplémentaire qui distingue la philosophie socratique de la ''téchnē'' marchande des sophistes. Socrate n’est pas un prestataire de services ; il est un serviteur du dieu. ==== Le démon et la prudence politique (31c-32e) ==== Une objection se présente naturellement : si Socrate est ce grand conseiller des particuliers, pourquoi ne monte-t-il pas à la tribune pour conseiller la cité elle-même dans ses assemblées ? La réponse est le fameux passage sur le ''daimónion'' socratique. ===== Qu’est-ce que le démon de Socrate ? (31c-d) ===== Socrate confie aux juges ce qu’il a déjà dit « maintes fois en maints endroits » : <blockquote>il m’advient quelque chose de divin et de démonique (''theîón ti kai daimónion''), une voix intérieure qui, depuis [mon] enfance, [...] chaque fois qu’elle m’advient, me détourne toujours de ce que je me propose de faire, mais jamais ne m’y encourage. (31c-d)</blockquote> Cette voix a trois caractéristiques remarquables. Elle est toujours dissuasive : « jamais elle ne m’y encourage ». Elle est personnelle : elle ne s’adresse qu’à Socrate. Elle est présente depuis l’enfance, donc constitutive de son rapport au monde. Elle s’est précisément opposée à son entrée en politique. Il s’agit donc, littéralement, de cette « divinité nouvelle » que l’accusation lui impute, ce que Mélétos, ironise Socrate, a d’ailleurs « consigné dans son acte d’accusation » (31d). Cette ironie est cinglante : l’accusation a pris pour un crime ce que Socrate revendique comme une grâce. La nature du ''daimónion'' a fait l’objet de multiples interprétations, dès l’Antiquité et jusqu’aux temps modernes. Plutarque a consacré un traité entier à la question (''Du démon de Socrate'') dans lequel il discute plusieurs hypothèses<ref>Plutarque, ''Du démon de Socrate'' (''De genio Socratis''), dans ''Œuvres morales'', t. VIII, trad. J. Hani, Paris, Les Belles Lettres, 1980.</ref>. À l’époque moderne, on l’a interprété tour à tour comme une hallucination d’un névrosé<ref>F. Lélut, ''Du démon de Socrate, spécimen d’une application de la science psychologique à celle de l’histoire'', Paris, Trinquart, 1836.</ref>, comme une manifestation de l’inconscient<ref>Arthur Koestler, ''Le Démon de Socrate'', Paris, Calmann-Lévy, 1970.</ref>, comme la voix de la conscience morale<ref>G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', tome II (sur Socrate), trad. G. Marmasse, Paris, Vrin, 2007, p. 316-321.</ref>, comme une inspiration divine authentique<ref>Henri Bergson, ''Les Deux sources de la morale et de la religion'' (1932), dans ''Œuvres'', éd. du Centenaire, Paris, PUF, 1959, p. 1027.</ref>, comme une intuition irrationnelle<ref>E. R. Dodds, ''Les Grecs et l’irrationnel'' (1951), trad. M. Gibson, Paris, Flammarion, coll. « Champs », 1977, chap. VII.</ref>. Nietzsche y voyait, de manière originale, la monstruosité d’un homme chez qui l’instinct, contrairement à l’ordinaire, ne crée pas mais critique, et chez qui la conscience rationnelle est au contraire créatrice : Socrate comme « homme théorique », rupture dans l’histoire de l’esprit grec dionysiaque<ref name="nietzsche-nt">Friedrich Nietzsche, ''La Naissance de la tragédie'' (1872), § 13-15, trad. P. Lacoue-Labarthe, dans ''Œuvres philosophiques complètes'', t. I, Paris, Gallimard, 1977.</ref>. Il faut probablement admettre, avec Claude Chrétien, que le démon est irréductible à une simple astuce défensive ou à un symbole : Socrate y croyait réellement, au point de risquer sa vie en le suivant<ref>Chrétien, ''op. cit.'', p. 28-29.</ref>. Son caractère purement négatif (il inhibe, ne prescrit jamais) en fait un signe du divin dans la vie humaine, mais un signe essentiellement limitatif : la divinité indique seulement ce qu’il ne faut pas faire, et laisse à l’homme la responsabilité de chercher, par l’examen rationnel, ce qu’il doit faire. Cette structure, où le divin ne donne pas la vérité mais seulement la limite, est cohérente avec la théologie négative que Socrate manifeste dans tout le plaidoyer : nous ne savons pas positivement ce que sont les dieux, mais nous recevons d’eux des signes qui nous empêchent de nous égarer. ===== Pourquoi pas la politique ? (31d-32a) ===== Socrate explique que si le démon l’a détourné de la politique, c’est pour préserver sa vie : « s’il y avait longtemps que j’avais entrepris de faire de la politique, il y a longtemps que je serais mort ». Et il formule alors une sentence vertigineuse, qui est peut-être la plus critique de l’''Apologie'' à l’égard de la démocratie athénienne : <blockquote>il n’y a personne au monde qui puisse garder la vie sauve s’il s’oppose loyalement à vous ou à toute autre collectivité, et s’il cherche à empêcher qu’il ne se produise dans la cité de nombreuses injustices et illégalités. Mais nécessairement tout vrai champion de la justice, s’il veut garder la vie sauve ne serait-ce qu’un peu de temps, doit vivre en simple particulier (''idiōteúein'') mais non en homme public. (32a)</blockquote> Cette sentence est d’une portée immense. Elle signifie que la politique telle qu’elle se pratique à Athènes est incompatible avec la justice. Le juste, s’il veut vivre, doit rester à l’écart des affaires publiques ; et s’il y entre, il doit s’attendre à mourir. C’est la cassure socratique avec la tradition civique grecque, qui voyait dans la participation politique (''politeía'') la plus haute réalisation de l’homme libre. L’homme proprement libre, pour les Grecs classiques, c’est le citoyen qui prend part aux assemblées ; l’''idiṓtēs'' qui se retire dans la sphère privée est, sinon méprisé, du moins considéré comme incomplet. Socrate renverse cette hiérarchie : la vraie vie politique, pour le juste, passe par le retrait de la politique officielle et par une politique privée, celle de la discussion personne à personne, de l’enseignement moral qui opère non par les discours publics mais par l’examen intime de chacun. C’est, comme le suggère la présentation de la collection Flammarion, « l’espace d’une autre politique »<ref name="brisson-mace-presentation">Arnaud Macé, « Présentation », dans Platon, ''Apologie de Socrate'', traduction par Luc Brisson, Paris, GF-Flammarion, 2017.</ref>. Cette thèse, qu’on peut lire comme une désertion civique, est aussi une critique profonde des conditions de la délibération démocratique. Elle rejoint ce que Platon développera dans la ''République'' : la cité idéale est celle où la philosophie serait au pouvoir, non celle où elle est écrasée par le plus grand nombre. Mais l’''Apologie'' n’est pas encore la ''République'' : Socrate n’y propose pas une contre-cité, il y constate seulement qu’aucune cité existante ne permet au juste de participer à ses affaires sans se renier. ===== Les deux épisodes probants (32a-e) ===== Socrate prouve cette thèse par deux épisodes biographiques, choisis avec soin. Le premier se situe sous la démocratie, en 406, l’année du procès des généraux des Arginuses. Les Athéniens avaient remporté une victoire navale importante contre Sparte près des îles Arginuses (au large de Lesbos), mais les généraux victorieux avaient été empêchés par une tempête de ramasser les cadavres et les naufragés athéniens, violation grave des usages religieux. Rentrés à Athènes, ils furent mis en cause ; mais au lieu de leur garantir un procès individuel comme l’exigeait le droit athénien, l’Assemblée, emportée par la colère populaire, voulut les juger en bloc. Socrate siégeait ce jour-là au Conseil, comme prytane pour sa tribu, l’Antiochide. Il fut le seul des cinquante prytanes à refuser de mettre aux voix cette motion collective, malgré les menaces et les cris de la foule<ref>Le récit détaillé du procès des généraux des Arginuses se trouve chez Xénophon, ''Helléniques'', I, 7, 1-35.</ref>. Les orateurs voulaient le faire arrêter sur-le-champ, les citoyens eux-mêmes l’y encourageaient ; il tint bon. Les généraux furent néanmoins jugés et six d’entre eux exécutés. Peu après, Athènes regretta sa décision, mais Socrate avait risqué sa vie pour la légalité, sans succès immédiat. Le second épisode se situe sous l’oligarchie, en 404. Les Trente l’avaient convoqué avec quatre autres citoyens à la Tholos (la rotonde, siège des prytanes occupée par le régime) et lui avaient ordonné d’aller chercher à Salamine un riche citoyen démocrate, Léon, pour qu’il soit exécuté et que ses biens soient confisqués. C’était une manœuvre classique des Trente : compromettre un maximum de citoyens dans leurs crimes pour les rendre solidaires du régime<ref>Xénophon, ''Helléniques'', II, 3, 39 ; Platon, ''Lettre VII'', 324d-325a.</ref>. Socrate, lui, refusa l’ordre. Il rentra simplement chez lui pendant que les quatre autres allaient chercher Léon, qui fut assassiné. Socrate, rappelle-t-il, aurait probablement payé cela de sa vie si les Trente n’avaient pas été renversés peu après (c’était le cas : le régime tomba en 403). Ces deux épisodes sont politiquement remarquables, et Platon les a sans doute choisis avec une intention nette. Ils montrent Socrate s’opposant également aux excès de la démocratie (procès des Arginuses) et à ceux de l’oligarchie (affaire de Léon), par fidélité à une justice supérieure au régime en place. Il n’est ni un démocrate de conviction ni un oligarque : il est un homme qui, dans l’un et l’autre cas, risque sa vie pour ne pas commettre d’injustice. Cette double symétrie est cruciale : elle répond par avance à tous ceux qui, dans la démocratie restaurée de 399, voudraient voir en Socrate un sympathisant des Trente, en raison de ses liens avec Critias et Charmide. Platon montre au contraire que Socrate a résisté aux Trente au péril de sa vie. Mais il montre également qu’il a résisté à la démocratie elle-même, quand celle-ci violait le droit. La neutralité politique de Socrate, ou plutôt cet au-delà du partisanisme, constitue une part de sa radicalité, qui déconcerte tous ceux qui voudraient l’enrôler dans un camp. ==== Socrate et ses « disciples » (33a-34b) ==== Socrate en vient alors au dernier volet du premier discours : la question des jeunes gens qu’on l’accuse d’avoir corrompus. Il récuse d’abord le terme même de « disciple » : <blockquote>je n’ai jamais, moi, été le maître (''didáskalos'') de personne. (33a)</blockquote> Cette affirmation est importante. Elle distingue radicalement Socrate des sophistes, qui se présentaient comme maîtres (''didáskaloi'') et vendaient un enseignement structuré ; Socrate n’a jamais promis un enseignement, jamais fait payer, jamais suivi un programme ; il a parlé à quiconque voulait l’écouter, jeunes et vieux, riches et pauvres, sans distinction, et n’est pas responsable de ce que chacun devient au sortir de la conversation. Cette posture est philosophiquement significative : elle implique que la philosophie n’est pas transmissible comme une technique, mais seulement comme une pratique qu’on ne peut qu’imiter. Socrate produit alors un argument a contrario d’une grande force. Si réellement il avait corrompu les jeunes, pourquoi n’est-ce pas ''eux-mêmes'', devenus adultes, qui viendraient témoigner contre lui ? Ou du moins leurs proches, parents, frères, qui auraient à se plaindre de cette corruption et en seraient les premiers concernés ? Or, bien au contraire, nombre de ses familiers, ou de leurs proches, sont présents à l’audience pour le soutenir. Socrate en énumère plusieurs, nommément, dans un passage qui vaut témoignage historique : Criton et son fils Critobule, Lysanias de Sphettos père d’Eschine (l’auteur de dialogues socratiques), Antiphon père d’Épigène, Nicostratos frère de Théodotos, ainsi qu’Adimante (le frère aîné de Platon) et plusieurs autres<ref>''Apologie'', 33d-34a. Sur ces personnages, voir Debra Nails, ''The People of Plato: A Prosopography of Plato and Other Socratics'', Indianapolis, Hackett, 2002.</ref>. <blockquote>Je pourrais citer pour vous beaucoup d’autres hommes, parmi lesquels il aurait fallu que Mélétos produise, au cours de son discours, quelque témoin. (34a)</blockquote> Cette preuve par les témoins absents est d’une grande force logique : l’accusateur a été incapable de trouver, parmi tous ceux qui auraient dû être les premières victimes, un seul pour le blâmer. C’est ce qu’on appelle un argument ''a silentio'' : le silence des supposées victimes prouve l’innocence de l’accusé. On notera que Platon se fait ici historien. En nommant les disciples présents, il fixe un moment dans le temps et offre à la postérité un témoignage vérifiable. Il se nomme lui-même plus loin (34a, puis 38b), parmi les amis prêts à se porter caution pour l’amende. Cette présence documentaire est rare dans les dialogues platoniciens, où Platon s’efface généralement : ici, il est témoin du procès de son maître. ==== Le refus du pathos (34b-35d) ==== Socrate aborde alors la péroraison de son premier discours. Il sait parfaitement ce qu’on attend d’un accusé athénien au moment crucial : larmes, supplications, présentation de la femme et des enfants éplorés, appel à la pitié, théâtralité de la détresse. Cette mise en scène, connue de tous par les comédies d’Aristophane et par l’habitude des tribunaux, était devenue quasi rituelle<ref>Sur la caricature du tribunal populaire, voir Aristophane, ''Les Guêpes'', v. 548-630.</ref>. Socrate a trois fils, dont l’un est déjà adolescent (''meirákion'') et les deux autres encore petits ; il pourrait les faire paraître, lui qui ne refuse pas d’être un homme. Mais il ne le fera pas. Pourquoi ? Deux motifs convergent. D’abord, par souci de l’honneur : un homme de sa réputation, réelle ou supposée, ne peut s’abaisser à ces scènes sans se ridiculiser et sans « ridiculiser la cité ». Les citoyens étrangers qui l’observent et qui connaissent la réputation d’Athènes s’étonneraient de voir les plus éminents de ses hommes se comporter de manière indigne. Socrate invoque la ''dóxa'' (l’opinion) d’Athènes aux yeux du monde grec pour justifier son refus de jouer le jeu. Mais surtout, et c’est le second motif, plus profond : il ne serait pas juste de supplier le juge. Et ici, Socrate formule une analyse capitale de la fonction judiciaire : <blockquote>le juge ne siège pas pour réduire la justice en faveur (''charízesthai''), mais pour décider de ce qui est juste ; et il a fait serment non de favoriser qui lui plaît, mais de rendre la justice selon les lois. (35c)</blockquote> Le juge athénien prêtait en effet un serment (l’''héliastikós hórkos'') par lequel il s’engageait à juger selon les lois<ref>Sur le serment héliastique, voir Démosthène, ''Contre Timocrate'', 149-151 ; Adriaan Lanni, ''Law and Justice in the Courts of Classical Athens'', Cambridge, Cambridge University Press, 2006, p. 75-76.</ref>. Supplier les juges, c’est leur demander de se parjurer, donc d’introduire le parjure dans la cité, donc de commettre une impiété effective contre les dieux garants du serment. Le geste de Socrate est ici d’une cohérence parfaite et quasi mathématique. Lui qui est accusé d’impiété ne peut, à l’instant critique, demander aux juges de commettre une vraie impiété (le parjure) pour le sauver. Ce serait confirmer dans les faits, activement, l’accusation qu’il réfute en paroles. Il préfère la mort à cet abaissement, qui serait en outre, non plus imaginairement mais réellement, une atteinte aux dieux de la cité. Par ce refus, Socrate démontre par les actes ce qu’il prétendait par les mots : il est le véritable pieux, et ce sont les accusateurs qui, en voulant la mort d’un juste, pratiquent la vraie impiété. Le premier discours s’achève là. Les juges votent. Socrate est déclaré coupable. La majorité est étroite : si trente voix s’étaient portées sur l’autre bord, Socrate aurait été acquitté. Pour un jury de 501, cela suggère un vote d’environ 280 contre 220 (chiffres que Diogène Laërce confirme dans son récit<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 41.</ref>, mais qui ne sont pas dans Platon). Ce qui est frappant, c’est la relative faiblesse de la condamnation : l’acquittement était à portée. === Le second discours : la contre-peine (35e-38b) === La procédure athénienne exige maintenant que Socrate propose une peine alternative à celle réclamée par l’accusation. Mélétos a proposé la mort. L’usage voulait qu’on proposât une peine sensiblement moins sévère (un lourd exil, une amende importante) pour donner au jury une alternative crédible. Le calcul tacite, dans les procès à peine à estimer, consistait à offrir une sanction à peine en deçà de celle demandée par l’accusateur, de manière à ne pas trop décevoir les attentes du jury tout en se ménageant un sort moins dur. Socrate va adopter une tout autre stratégie. ==== La contingence du vote (35e-36b) ==== Socrate remarque d’abord, avec une ironie qui frise la provocation, qu’il est étonné non pas d’avoir été condamné, mais de l’avoir été à une si faible majorité. Il s’attendait à une condamnation bien plus nette. Si trente voix de plus avaient basculé, il aurait été acquitté. Il observe aussi que, ce qui le perd véritablement, ce n’est pas Mélétos : car sans l’appui d’Anytos et de Lycon, le seul Mélétos, compte tenu des voix obtenues, aurait dû payer mille drachmes d’amende pour n’avoir pas recueilli le cinquième des suffrages (règle destinée à décourager les plaintes frivoles). En divisant malicieusement les voix reçues entre ses trois accusateurs, Socrate montre que Mélétos seul n’aurait pas obtenu sa condamnation. Cette remarque, en apparence technique, est profondément déstabilisante : elle montre que la procédure qui vient de condamner Socrate est elle-même contingente, dépendante du nombre d’accusateurs autant que du fond du dossier. Elle suggère, au passage, que la véritable force du parti de l’accusation réside dans Anytos, l’homme politique, non dans Mélétos, le plaignant nominal. Le procès apparaît donc comme un montage politique, sous un habillage religieux. ==== La proposition du Prytanée (36b-37a) ==== Quelle contre-peine proposer ? Socrate prend la question au sérieux, mais dans un sens retourné : quelle peine mérité-je ? Il rappelle toute sa vie : avoir négligé les affaires, l’argent, les magistratures, les assemblées et les honneurs, pour se consacrer au service privé de la vertu, <blockquote>en essayant de convaincre chacun d’entre vous de ne pas se préoccuper de ses affaires personnelles avant de se préoccuper, pour lui-même, de la façon de devenir le meilleur et le plus sensé possible. (36c)</blockquote> Que mérite un homme ainsi ? Un bon traitement, dit-il, et non une peine. Il propose donc non pas un châtiment, mais une récompense : être nourri aux frais de l’État au Prytanée. Le Prytanée était à Athènes l’édifice public où la cité nourrissait, aux frais de l’État, les prytanes en exercice, les hôtes officiels et les citoyens illustres, notamment les vainqueurs des jeux Olympiques et les bienfaiteurs de la patrie. C’était la plus haute distinction civique, l’équivalent d’une reconnaissance par l’État comme héros ou sauveur de la cité. Socrate explique qu’il la mérite plus que quiconque : <blockquote>si celui-ci [le vainqueur olympique] vous procure l’apparence du bonheur, je vous en offre, moi, la réalité ; lui n’a aucun besoin d’être nourri, mais moi, j’en ai besoin. (36d-e)</blockquote> L’argument est double : Socrate est un bienfaiteur réel (plus que le champion olympique, dont la gloire n’est que sportive) et il est pauvre (donc il a besoin de ce soutien alimentaire, alors que le champion en a moins besoin). Cette proposition est manifestement provocatrice, et aucun commentateur n’en doute. Socrate ne joue plus le jeu de la procédure ; il retourne le tribunal. Pour un jury qui vient de le condamner, proposer d’être traité en héros civique est une humiliation délibérée. Comment comprendre cette audace ? Plusieurs explications se combinent. D’abord, une cohérence logique : Socrate est convaincu de n’avoir commis aucune injustice, il refuse donc de s’infliger une peine comme s’il était coupable. Ensuite, une fidélité à sa parole : s’il proposait une peine qu’il estime injuste, il trahirait son principe d’agir toujours selon le juste. Enfin, peut-être, une acceptation anticipée de la mort : il est âgé (soixante-dix ans, espérance de vie largement dépassée dans l’Antiquité), il a accompli sa mission, il ne craint pas la sentence, il n’a donc aucune raison de ruser. À quoi s’ajoute, plus subtilement, un calcul dramatique : proposer une vraie contre-peine reviendrait à reconnaître la compétence du tribunal ; en proposant une récompense, Socrate refuse symboliquement la sentence avant même qu’elle ne tombe. ==== L’examen des autres peines et la proposition d’amende (37a-38b) ==== Socrate continue son raisonnement avec une rigueur didactique : puisque je sais que je ne me cause volontairement de tort à personne, je ne vais pas, loin de là, m’en causer à moi-même en proposant une peine qui serait un tort. Il passe alors en revue, méthodiquement, les peines possibles. La prison ? Ce serait passer sa vie soumis au pouvoir des Onze, les magistrats qui administraient les prisons et les exécutions, donc à une sujétion indigne. Une amende lourde assortie de contrainte par corps jusqu’au paiement ? Cela revient au même : il n’a pas d’argent. L’exil ? C’est ici que Socrate développe sa réponse la plus fine. Il serait absurde, dit-il, de choisir l’exil : si ses propres concitoyens ne supportent plus ses entretiens, au point d’en vouloir « se débarrasser », pourquoi les étrangers les supporteraient-ils davantage ? Il serait chassé de ville en ville. Et ne pourrait-il vivre en se taisant ? Non, et ici vient l’un des sommets du texte : cesser de philosopher équivaudrait à désobéir au dieu. <blockquote>Il n’y a pas pour un homme de plus grand bien que de s’entretenir chaque jour de la vertu et des autres sujets dont vous m’entendez discuter, en examinant moi-même les autres ; car une vie sans examen n’est pas digne d’être vécue par un homme. (38a)</blockquote> Cette dernière formule, en grec ''ho anéxetastos bíos ou biōtós anthrṓpōi'', est probablement la plus célèbre de toute l’''Apologie'' et peut-être de toute la philosophie antique. Elle condense le programme de la philosophie socratique : la vie doit être soumise à un examen (''exétasis'') constant, à une interrogation rationnelle sur ses buts et ses valeurs. Sans cet examen, on ne vit pas une vie proprement humaine. Formule vertigineuse, qui fait de la philosophie non pas un luxe intellectuel mais la condition même d’une existence digne de ce nom. Elle suggère qu’il existe un seuil d’humanité : en deçà de l’examen, l’homme n’est pas pleinement homme. La philosophie cesse alors d’être une option ajoutée à la vie pour devenir l’élément qui en fait une vie humaine. Finalement, Socrate concède une mine d’argent (somme modique, correspondant à peu près à trois mois de salaire d’un ouvrier qualifié), mais accepte, sur la pression de ses amis (Platon, Criton, Critobule, Apollodore), de proposer trente mines, avec leur caution personnelle. C’est une somme importante, équivalent à plusieurs années de salaire, mais manifestement insuffisante face à la proposition de mort, et la manière dont elle est introduite (sous la pression des amis, comme en dernier recours) ne masque pas que Socrate lui-même n’y adhère pas vraiment. Le jury vote une seconde fois. Cette fois, la majorité est beaucoup plus nette : selon les chiffres traditionnels rapportés par Diogène Laërce, quatre-vingts jurés supplémentaires ont voté contre Socrate par rapport au premier vote, manifestement indignés par son attitude. Socrate est condamné à mort. === Le troisième discours : après la condamnation (38c-42a) === Ce troisième discours n’est pas prévu par la procédure. Une fois la sentence prononcée, les magistrats passent aux formalités d’enregistrement, notamment la notification aux Onze, chefs des geôliers et du bourreau. Socrate prend pourtant la parole une dernière fois, sans doute pendant que les Onze procèdent à ces écritures, pour s’adresser à la partie de l’assistance qui est restée sur place. Il s’agit d’une péroraison spontanée, hors procédure, probablement élargie et composée par Platon pour les besoins du texte, même si l’événement lui-même est plausible. Ce discours se divise en deux moments : d’abord aux jurés qui ont voté sa mort, puis à ceux qui ont voté son acquittement. ==== Aux jurés de la condamnation (38c-39d) ==== Socrate commence par un constat ironique : les Athéniens ont gagné peu de temps, il est âgé, il serait mort bientôt de toute façon. Mais en échange, ils vont gagner <blockquote>le renom, auprès des gens avides de diffamer notre cité, d’avoir fait mourir un sage en la personne de Socrate. (38c)</blockquote> La réputation d’Athènes souffrira de ce procès bien au-delà de ce qu’elle a cru gagner. La prédiction est parfaitement exacte : la condamnation de Socrate a, dans la mémoire collective de l’Occident, sérieusement terni l’image de la démocratie athénienne. Surtout, Socrate retourne contre les juges l’argument central de son plaidoyer : s’il n’a pas réussi à les persuader, ce n’est pas faute d’arguments, mais parce qu’il n’a pas voulu employer les tactiques indignes (larmes, supplications) qu’ils attendaient. <blockquote>Je préfère de beaucoup mourir après m’être défendu comme je l’ai fait plutôt que vivre après un plaidoyer à leur façon. (38e)</blockquote> Et il prononce cette antithèse fameuse : <blockquote>la difficulté n’est pas d’échapper à la mort, elle est bien plus d’échapper à la lâcheté (''ponēría''), car elle court plus vite que la mort. En l’occurrence, moi qui suis lent et vieux, j’ai été rattrapé par la plus lente des deux [la mort], cependant que mes accusateurs, qui sont lestes et rapides, ont été rattrapés par la plus rapide, qui est la méchanceté. (39a-b)</blockquote> L’image est saisissante : chaque homme est poursuivi par son destin, mais les uns sont rattrapés par la mort physique et les autres par la mort morale, infiniment plus grave. Socrate se permet alors une prophétie (''manteúomai''). Il est, dit-il, <blockquote>au point où les hommes prophétisent le mieux, quand ils sont à la veille de mourir, (39c)</blockquote> allusion à la croyance grecque selon laquelle les mourants acquièrent un don de divination (voir le motif du chant du cygne dans le ''Phédon'' 84e<ref>Platon, ''Phédon'', 84e-85b : « les cygnes [...], lorsqu’ils sentent qu’ils vont mourir, chantent ce jour-là plus fort et plus beau qu’ils n’ont jamais chanté, dans la joie d’aller trouver le dieu dont ils sont les serviteurs ».</ref>). Sa prophétie est terrible : <blockquote>un châtiment vous viendra aussitôt après ma mort, bien plus pénible que celui par lequel vous m’aurez tué. [...] Le nombre croîtra de ceux qui vous demanderont des comptes, que je retenais jusqu’ici, sans que vous vous en aperceviez ; et ils seront d’autant plus pénibles qu’ils sont plus jeunes. (39c-d)</blockquote> Tuer n’est pas la façon de se délivrer du blâme ; la seule manière honorable est « de se préparer soi-même à être le meilleur possible ». Cette prophétie s’accomplira de manière surprenante par la suite : les « petits socratiques » (Antisthène le cynique, Aristippe, Euclide de Mégare), puis Platon et son Académie, puis Aristote et le Lycée, poursuivront inlassablement l’interrogation commencée par Socrate. La philosophie comme vocation publique est née du procès. ==== Aux jurés de l’acquittement (39e-42a) ==== Socrate se tourne alors vers ceux qui ont voté pour lui, qu’il appelle désormais, seuls, ''juges'' véritables. Cette distinction terminologique est capitale : avant le verdict, tous étaient indistinctement ''andres'' (« messieurs ») ou ''Athēnaîoi'' (« Athéniens ») ; maintenant, seuls ceux qui ont voté juste méritent, selon Socrate, le titre de juges (''dikastaí''). Le tribunal vient d’être scindé en deux catégories asymétriques. Il leur confie deux pensées, l’une sur le signe divin, l’autre sur la mort. ===== Le silence du démon (39e-40c) ===== Son démon, dit-il, avait coutume de l’arrêter chaque fois qu’il s’apprêtait à faire quelque chose de mauvais, même dans des circonstances mineures. Or, aujourd’hui, depuis le matin, tout au long de cette journée qui l’a mené à la condamnation à mort, le démon ne s’est pas manifesté une seule fois. Il ne l’a pas arrêté au moment où il quittait son domicile, ni lorsqu’il montait au tribunal, ni à aucun moment de son plaidoyer. Cette absence est elle-même un signe : <blockquote>il y a des chances pour que ce qui m’est arrivé soit un bien ; et c’est nous qui faisons des suppositions incorrectes quand nous considérons la mort comme un mal. (40b-c)</blockquote> Le silence du démon est la preuve, pour Socrate, que la voie qu’il suit est la bonne. L’argument est philosophiquement subtil. Socrate ne dit pas qu’il sait que la mort est un bien ; il dit qu’il a une raison de penser que ce qui lui arrive est un bien, puisque le dieu (par la voix du démon) ne l’a pas arrêté. C’est un argument ''e silentio'' transposé sur le plan religieux : le silence divin, pour qui est habitué à être averti, vaut approbation. Cette structure de raisonnement est fragile, mais elle est cohérente avec la théologie socratique : la divinité se manifeste par ses interventions plutôt que par ses paroles positives ; son silence, quand il y a habitude d’intervention, est significatif. ===== Les deux hypothèses sur la mort (40c-41d) ===== Socrate propose alors une méditation sur la mort, l’une des plus belles pages de la philosophie antique, construite comme une alternative raisonnée. De deux choses l’une : ou bien la mort est l’absence de toute sensation, ou bien elle est une migration (''metoíkēsis'') de l’âme de ce lieu vers un autre lieu. Dans la première hypothèse (la mort comme absence de sensation), la mort ressemble au sommeil sans rêve. Or qui n’aimerait pas, si on lui demandait de choisir entre une nuit de sommeil profond sans rêves et toutes les autres nuits et jours de sa vie, reconnaître que cette nuit est plus précieuse que la plupart ? Même le Grand Roi de Perse (homme réputé le plus heureux du monde aux yeux des Grecs) trouverait peu de jours comparables à une telle nuit. Si la mort est cela, alors la totalité du temps après la mort se réduit à « une seule nuit », et cette nuit est un gain. L’argument est intéressant par sa structure : il part d’une expérience commune (le sommeil sans rêve) pour désamorcer la peur métaphysique de la mort. Si l’on aime le sommeil quand il nous prend, pourquoi craindre la mort si elle lui ressemble ? La stratégie argumentative, qui rappelle celle d’Épicure et de Lucrèce plus tard (« la mort n’est rien pour nous »<ref>Épicure, ''Lettre à Ménécée'', § 125 : « la mort n’est rien pour nous, puisque tant que nous sommes, la mort n’est pas, et quand la mort est là, nous ne sommes plus ». Lucrèce, ''De la nature des choses'', III, v. 830 et suiv.</ref>), désarticule la crainte en la confrontant à ce que nous expérimentons quotidiennement. Dans la seconde hypothèse (la mort comme migration), la mort est un voyage vers l’au-delà, où l’on retrouve tous les morts. Que pourrait-on imaginer de plus heureux ? On serait délivré des juges qui prétendent juger ici-bas, pour rencontrer les vrais juges dont on dit qu’ils y rendent la justice : Minos, Rhadamante, Éaque (les trois juges infernaux traditionnels), plus Triptolème (qui les remplace parfois dans l’iconographie attique). On rencontrerait Orphée, Musée, Hésiode, Homère, les grandes figures poétiques et religieuses du passé. On pourrait y continuer, sans cette fois risquer la mort, l’activité d’examen qui fut la sienne sur terre : interroger Palamède, Ajax (tous deux morts par jugements injustes, comme lui-même : la comparaison est évidemment à son avantage), le chef de l’armée grecque à Troie (Agamemnon), Ulysse, Sisyphe, « et tant d’autres hommes et femmes qu’on pourrait nommer ». <blockquote>Discuter avec ceux de là-bas, vivre en leur société, les soumettre à examen, ne serait-ce pas le comble du bonheur ? Aussi bien, les gens de là-bas ne mettent à mort personne pour ce motif. (41c)</blockquote> Ce passage est riche de tonalités. Il y a une évidente dimension humoristique : Socrate imagine l’au-delà comme la continuation indéfinie de son activité terrestre, l’examen dialectique, mais cette fois sans risque, puisqu’on n’y meurt plus. L’Hadès devient une agora élargie à tous les temps. Il y a également une dimension consolatoire : la comparaison avec Palamède et Ajax, héros victimes de procès injustes, ennoblit le sort de Socrate. Il y a enfin une dimension ironique vis-à-vis des jurés athéniens : les vrais juges ne sont pas à Athènes, mais dans l’Hadès, et Socrate se réjouit d’aller les retrouver. Ce passage a suscité des interprétations contrastées. Certains commentateurs y voient une réelle espérance platonicienne en l’immortalité de l’âme, telle qu’elle sera développée dans le ''Phédon''<ref>Platon, ''Phédon'', 80a-84b, 105c-107a.</ref>. D’autres, comme Chrétien, soulignent que le Socrate de l’''Apologie'' reste fondamentalement agnostique : il présente deux hypothèses, il ne tranche pas entre elles, et l’imagination y a au moins autant de part que la raison<ref>Chrétien, ''op. cit.'', p. 32-36.</ref>. Socrate lui-même conclut prudemment : <blockquote>aucun mal ne peut toucher un homme de bien ni pendant sa vie ni après sa mort, et les dieux ne se désintéressent pas de son sort. (41d)</blockquote> Cette conclusion n’affirme pas dogmatiquement une survie, mais énonce une foi pratique : quoi qu’il arrive, le juste n’a rien à craindre. L’''Apologie'', contrairement au ''Phédon'', ne construit pas de doctrine positive sur l’immortalité ; elle se tient au seuil d’une telle doctrine, dans un agnosticisme serein. ===== Le testament (41e-42a) ===== Socrate clôt son discours par un testament pour ses fils. Il ne demande qu’une chose aux Athéniens : <blockquote>Quand mes fils seront grands, punissez-les, citoyens, en les tourmentant comme je vous tourmentais, pour peu qu’ils vous paraissent se soucier d’argent ou de n’importe quoi d’autre plus que de la vertu. Et, s’ils croient être quelque chose, alors qu’ils ne sont rien, adressez-leur le reproche que je vous adressais. (41e)</blockquote> La mission philosophique se transmet ainsi, comme un héritage inversé : Socrate demande à ses bourreaux de devenir eux-mêmes, envers ses enfants, ce qu’il était pour eux, des tourmenteurs par la vertu. C’est le pardon actif d’un homme qui refuse de laisser sa mort briser la chaîne de l’examen. Puis vient la dernière phrase, l’une des plus célèbres de la littérature philosophique : <blockquote>Mais voici déjà l’heure de partir, moi pour mourir et vous pour vivre. De mon sort ou du vôtre lequel est le meilleur ? La réponse reste incertaine pour tout le monde, sauf pour la divinité. (''plḕn hē tôi theôi'', 42a)</blockquote> Cette clôture ouvre, sur l’indécidable, l’agnosticisme ultime. Socrate ne sait pas, nul ne sait, lequel est le plus heureux, de lui qui va mourir ou de ses juges qui vont continuer à vivre. Seule la divinité le sait. L’''Apologie'' se ferme ainsi sur le mot même de la sagesse socratique : la reconnaissance de l’ignorance humaine, couplée à la confiance paisible en un ordre divin qui excède notre mesure. Rien n’est affirmé dogmatiquement ; tout se termine sur une interrogation qui n’attend pas de réponse humaine. C’est une fin d’une sobriété admirable, qui évite à la fois la plainte et la consolation artificielle. Elle est, par son rythme et par son contenu, digne d’une page d’évangile ou d’un chapitre des ''Pensées'' de Marc-Aurèle. == Concepts et thèmes majeurs == Une lecture suivie ne serait pas complète sans reprendre quelques-uns des grands thèmes qui courent à travers le texte et en font la portée philosophique durable. === La sagesse humaine : le savoir du non-savoir === Le concept central de l’''Apologie'' est celui de la sagesse humaine (''anthrōpínē sophía'', 20d). Socrate n’est pas savant au sens fort du terme, c’est-à-dire à la manière des sophistes qui prétendaient posséder un savoir sur les choses divines, sur la nature, sur la politique, sur la vertu. Mais il possède une sagesse proprement humaine, qui consiste à reconnaître que l’on ne sait pas. Ce savoir du non-savoir n’est pas un scepticisme, encore moins un renoncement. C’est une position éthique : celui qui sait qu’il ne sait pas est disposé à chercher, à interroger, à examiner ; celui qui croit savoir est fermé à toute remise en question et, par là même, incapable de tout progrès. Cette sagesse n’est pourtant pas purement négative. Socrate affirme savoir au moins deux choses : qu’il est mauvais et laid de commettre l’injustice et de désobéir à un meilleur que soi (29b), et que la vie non examinée n’est pas digne d’être vécue (38a). De ces deux « savoirs » découle tout le comportement de Socrate au procès : il ne peut trahir la justice pour sauver sa vie, et il ne peut cesser d’examiner les autres sans trahir sa vocation propre. On voit donc que le savoir du non-savoir n’est pas une position d’ignorance totale, mais une structure articulée : une ignorance avouée sur les grandes questions métaphysiques, et une certitude pratique sur les exigences éthiques. Cette combinaison fait du socratisme une forme de philosophie pratique : elle rend possible l’action juste sans requérir une science achevée. Il faut enfin noter que le savoir du non-savoir est peut-être la thèse la plus féconde du socratisme pour l’histoire de la pensée. Toute la philosophie postérieure, chaque fois qu’elle commence par un doute méthodique (Descartes), par une critique des prétentions de la raison (Kant), par une déconstruction des évidences (phénoménologie), s’inscrit dans le sillage de l’interrogation socratique. La philosophie moderne est, en ce sens, socratique par son geste inaugural, même quand elle ne l’est plus par ses conclusions. === La méthode de l’elenchus === L’''Apologie'' donne à voir, en acte, la méthode philosophique propre de Socrate : l’elenchus ou réfutation par interrogation. Elle apparaît à plusieurs reprises, mais surtout dans l’interrogatoire de Mélétos (24b-28a). Son fonctionnement est simple dans son principe : Socrate ne pose pas directement ses propres thèses ; il prend pour point de départ celles de son interlocuteur, puis, par une série de questions dont chacune requiert une réponse qui semble évidente, il conduit l’interlocuteur à reconnaître des conséquences incompatibles avec d’autres thèses qu’il tient également pour vraies. La contradiction ainsi mise au jour n’est pas celle de Socrate, mais celle de l’interlocuteur avec lui-même. Cette méthode a plusieurs vertus philosophiques. D’abord, elle respecte la liberté de l’interlocuteur : Socrate ne lui impose rien, il l’amène seulement à voir ce qu’il pensait déjà. Ensuite, elle produit un savoir négatif sûr : la réfutation, à défaut de démontrer le vrai, prouve au moins que la thèse examinée est fausse. Enfin, elle a un effet moral : elle introduit chez l’interlocuteur l’expérience de l’''aporía'' (la perplexité), qui est le point de départ possible d’une recherche véritable. L’interlocuteur, déchargé de son illusion de savoir, peut commencer à apprendre. L’elenchus est ainsi, au sens propre, maïeutique : il fait accoucher les esprits. Mais l’elenchus a aussi ses limites, que l’''Apologie'' laisse apercevoir. Il peut blesser l’amour-propre ; il peut créer des haines durables ; il peut donner à ceux qui en sont la cible l’impression d’être humiliés publiquement. Socrate lui-même en témoigne : son enquête a suscité contre lui des rancœurs innombrables. La philosophie a un coût social, que l’elenchus rend visible avec une particulière acuité. === Le souci de l’âme === Le message moral central de l’''Apologie'' tient dans une exhortation : il faut se soucier prioritairement de son âme (''psuchḗ'') et de son amélioration, non de son corps, de sa richesse ou de sa réputation (29d-30b). <blockquote>La vertu ne naît pas de l’argent, mais c’est de la vertu que naissent et l’argent et tout le reste des biens utiles aux hommes, aussi bien privés que publics. (30b)</blockquote> Ce renversement est l’une des sources principales de la morale occidentale : la hiérarchie des biens est réordonnée autour du bien intérieur, et la richesse extérieure n’a de valeur qu’en tant qu’elle découle d’une vertu préalable. Ce souci de soi (''epiméleia heautoû'') n’est pas égoïste. Il est au contraire la condition du souci des autres : on ne peut aider autrui à améliorer son âme sans avoir travaillé à la sienne. Socrate harcèle ses concitoyens parce qu’il veut qu’ils prennent soin de leur âme, non parce qu’il veut sauver la sienne à ses dépens. Et c’est précisément parce qu’il se soucie de la cité qu’il la secoue : le taon pique le cheval pour le réveiller, non pour le faire souffrir. Cette thèse a eu une postérité immense. Elle est reprise, transformée, intériorisée par les écoles hellénistiques (stoïciens, épicuriens), qui en font le cœur de la sagesse pratique. Elle passe ensuite dans le christianisme, où le « soin de l’âme » devient le salut personnel. À l’époque moderne, Michel Foucault lui a consacré une part importante de ses derniers cours, voyant dans le souci de soi une alternative à l’éthique cartésienne fondée sur la seule connaissance de soi<ref>Michel Foucault, ''Le Souci de soi'' (''Histoire de la sexualité'', t. III), Paris, Gallimard, 1984 ; et ''L’Herméneutique du sujet'', ''op. cit.''</ref>. L’''Apologie'' est à l’origine de cette longue tradition, même si le souci de soi socratique reste, par certains aspects, très différent des élaborations postérieures : il est moins un travail sur soi qu’un examen de soi par la discussion avec autrui. === La philosophie comme mission divine === L’''Apologie'' fonde la légitimité de la philosophie sur une mission divine. Socrate ne philosophe pas par goût ou par choix : il le fait parce que le dieu lui en a fait l’ordre, à travers l’oracle de Delphes et à travers le démon. Cette dimension religieuse est essentielle pour comprendre la posture socratique. S’il pouvait cesser d’interroger, il le ferait peut-être (c’est une activité ingrate et dangereuse) ; mais il ne le peut pas, car ce serait désobéir au dieu. La philosophie est ainsi un service sacré, équivalent à celui des prêtres ou des devins, mais accompli par d’autres moyens : non par le rite, mais par l’examen rationnel. Cette dimension place Socrate dans une position paradoxale par rapport aux accusations d’impiété. L’homme que l’on accuse de ne pas croire aux dieux est en réalité celui qui les sert le plus fidèlement, au point de mourir pour leur obéir. Platon retourne ainsi l’accusation : les véritables impies sont ceux qui, en condamnant Socrate, refusent le présent que le dieu leur a fait. Le procès apparaît alors sous un jour inversé : non plus un acte de piété de la cité contre un impie, mais un acte d’impiété de la cité contre un serviteur du dieu. Ce thème a eu un écho particulier dans la tradition chrétienne, qui a parfois vu en Socrate une figure prophétique du Christ : un juste mis à mort par une communauté religieuse qui croyait servir ses dieux en le tuant. Justin martyr, au IIᵉ siècle, comparera explicitement Socrate et Jésus, voyant dans le philosophe athénien une préfiguration providentielle de la Passion<ref>Justin de Naplouse, ''Première Apologie'', 46, 1-4 : les chrétiens considèrent comme chrétiens avant la lettre « ceux qui ont vécu avec le Logos [...] parmi les Grecs, Socrate, Héraclite et ceux qui leur furent semblables ».</ref>. La philosophie chrétienne primitive a ainsi trouvé dans l’''Apologie'' une matrice pour penser la martyrologie. === La justice supérieure === Le thème de la justice traverse tout le texte. Socrate se présente comme un homme profondément respectueux des lois : il a risqué sa vie pour le respect de la procédure sous la démocratie (affaire des Arginuses) ; il mourra par fidélité aux lois dans le ''Criton'' plutôt que de s’évader. Mais son obéissance aux lois n’est pas inconditionnelle : il y a une justice supérieure, fondée sur des valeurs, qui commande dans certains cas la désobéissance civile, comme lorsqu’il refuse d’exécuter les ordres des Trente concernant Léon de Salamine. Cette justice supérieure n’est pas un simple idéal abstrait ; elle est, pour Socrate, ce qui fait le prix (''axía'') de la vie humaine. Elle a une origine divine et s’impose à la conscience au-delà des conventions sociales. On voit déjà poindre ici, avant les développements platoniciens de la ''République'', l’idée d’une justice en soi, distincte de la justice légale, et qui fonde celle-ci sans s’y réduire. Antigone, chez Sophocle, invoquait déjà les « lois non écrites » des dieux contre les décrets humains<ref>Sophocle, ''Antigone'', v. 450-460.</ref> ; Socrate, à sa manière, s’inscrit dans cette tradition. Mais il la rationalise : ce n’est plus le simple respect d’une tradition familiale ou religieuse, c’est la fidélité à un ordre supérieur accessible par l’examen rationnel. La philosophie devient ainsi le lieu où se manifeste cette justice transcendante, dont les lois humaines ne sont qu’une approximation imparfaite. Cette double fidélité (aux lois de la cité et à une justice supérieure) est source d’une tension qui traversera toute la tradition philosophique et juridique occidentale. Elle est au cœur des doctrines du droit naturel<ref>Voir notamment Cicéron, ''De republica'', III, 33 sur la ''lex vera'' ; Thomas d’Aquin, ''Somme théologique'', I-II, q. 94 sur la loi naturelle.</ref>, des théories modernes de la désobéissance civile (Thoreau, Gandhi, Martin Luther King), et des débats contemporains sur la légitimité du droit positif. === La mort et le courage === Le rapport de Socrate à la mort est une pièce maîtresse du texte. Sa thèse est à double face. D’un côté, la mort en elle-même est inconnue : nul ne sait si elle est un mal ou un bien, et la craindre comme un mal certain est la plus répréhensible des ignorances. De l’autre, il y a pire que la mort : la lâcheté, l’injustice, l’abandon de son poste. Le courage socratique n’est donc pas, comme celui des héros homériques, la volonté exaltée de mourir pour l’honneur ; c’est la lucidité sur ce qui est réellement à craindre, non la mort mais le vice. Ce renversement a nourri toute la morale stoïcienne (qui en fait un de ses principes cardinaux : ne rien craindre de ce qui ne dépend pas de nous, donc pas la mort) puis, par des voies détournées, la pensée chrétienne du martyre. Il faut insister sur la finesse de l’analyse socratique. Elle ne dit pas : « la mort est un bien » (affirmation dogmatique contraire à son savoir du non-savoir). Elle ne dit pas non plus : « la mort est indifférente » (comme le diront plus tard les stoïciens). Elle dit : « la mort est inconnue, donc je ne peux la craindre comme un mal certain ; mais l’injustice est un mal certain, donc je peux la craindre ». Le courage n’est pas fondé sur une espérance métaphysique, mais sur une hiérarchie des savoirs : le connu prime sur l’inconnu, et j’organise ma conduite en fonction de ce que je sais. Cette analyse aura un long destin. Épicure la reprendra pour dire : « la mort n’est rien pour nous »<ref>Épicure, ''Lettre à Ménécée'', § 125.</ref>. Les stoïciens la transformeront en doctrine de l’indifférence aux choses externes. Montaigne en fera un objet central de ses ''Essais''<ref>Montaigne, ''Essais'', I, 20 « Que philosopher, c’est apprendre à mourir » ; III, 12 « De la physionomie ».</ref>. Heidegger, au XXᵉ siècle, retournera au Socrate de l’''Apologie'' en interrogeant le rapport authentique à la mort comme condition d’une existence propre<ref>Martin Heidegger, ''Sein und Zeit'' (1927), § 46-53, sur l’''être-pour-la-mort''.</ref>. Chaque fois, c’est le même geste inaugural qui est repris, celui d’une mort désarmée par la pensée. === Philosophie et cité : l’autre politique === L’''Apologie'' esquisse une conception originale de la politique. Socrate se déclare non-politique au sens courant du terme : il n’a pas fréquenté l’assemblée, il n’a pas cherché les magistratures, il n’a pas fait carrière publique. Mais il se présente comme le plus politique des Athéniens au sens profond : il s’est occupé de la cité elle-même (plus que de ses affaires), en se souciant du perfectionnement de ses concitoyens. C’est une autre politique, qui ne passe pas par les institutions officielles (corrompues selon lui par la démagogie et l’ignorance) mais par la conversation privée, par l’interpellation personnelle, par la formation morale. Cette vision a un versant critique radical vis-à-vis de la démocratie athénienne, qui émerge des épisodes des Arginuses et de Léon de Salamine : la démocratie, livrée à ses passions, peut se conduire de manière aussi injuste qu’une tyrannie. Il serait naïf de voir en Socrate un démocrate simple ou un anti-démocrate simple ; il est un critique des deux régimes en tant qu’ils s’éloignent de la justice. Mais sa critique vise, au-delà des régimes, la capacité même des collectivités humaines à délibérer justement : « il n’y a personne au monde qui puisse garder la vie sauve s’il s’oppose loyalement à vous ou à toute autre collectivité » (32a). Ce diagnostic, désabusé, n’est pas tant politique qu’anthropologique : les foules, quelles qu’elles soient, résistent mal à l’examen rationnel. Mais cette critique a aussi un versant constructif. La philosophie, par l’examen qu’elle exerce sur les esprits, prépare la possibilité d’une politique véritablement juste. Platon développera cette intuition dans la ''République'', en allant jusqu’à imaginer une cité où les philosophes seraient rois, mais ce développement excède le cadre de l’''Apologie''. Au moment où Platon écrit ce texte, la cité idéale n’est pas encore pensée ; il y a seulement la dénonciation d’une cité qui a tué son meilleur citoyen, et la promesse implicite d’une pensée qui prolongera la mission interrompue. === L’ironie socratique === Un mot enfin sur l’ironie, qui est omniprésente dans le texte, et qu’il faut distinguer en plusieurs registres. Il y a d’abord l’ironie au sens étroit : dire le contraire de ce que l’on pense, comme lorsque Socrate qualifie Mélétos de « bon citoyen » et « patriote ». Il y a ensuite l’ironie dialectique : amener l’interlocuteur à se contredire lui-même par des questions prétendument naïves, alors que Socrate sait parfaitement où il le mène. Il y a enfin l’ironie existentielle : vivre de telle façon que toute son existence est un démenti de ce qu’on attendrait d’un homme dans sa situation. La proposition d’être nourri au Prytanée relève de cette dernière : Socrate, condamné, se présente comme un bienfaiteur ; il retourne les rôles, fait du tribunal une scène tragicomique. Cette ironie n’est pas seulement un trait de style. Elle est l’expression d’une distance philosophique à l’égard des conventions et des évidences. Celui qui a compris que le savoir commun est illusoire, que la rhétorique est trompeuse, que les hiérarchies sociales reposent sur des malentendus, ne peut plus prendre au sérieux les rituels qui ordonnent la vie ordinaire. L’ironie socratique est le signe visible d’une conscience libre, qui refuse de se soumettre aux attentes. C’est pourquoi Søren Kierkegaard, au XIXᵉ siècle, en a fait dans sa thèse sur ''Le Concept d’ironie'' le trait caractéristique de la subjectivité philosophique naissante : avec Socrate, la conscience se sépare du monde, s’intériorise, devient sujet<ref name="kierkegaard">Søren Kierkegaard, ''Le Concept d’ironie constamment rapporté à Socrate'' (''Om Begrebet Ironi'', 1841), trad. P.-H. Tisseau et E.-M. Jacquet-Tisseau, dans ''Œuvres complètes'', t. II, Paris, Éditions de l’Orante, 1975.</ref>. L’ironie est ce mode de la subjectivité qui se pose en se distinguant de ce qui est. == Questions de lecture et postérité == === Le Socrate de l’''Apologie'' et le Socrate historique === Une question classique est de savoir dans quelle mesure l’''Apologie'' restitue fidèlement le plaidoyer effectivement prononcé par Socrate en 399. Les éléments de réponse sont partiels. D’une part, Platon, jeune témoin du procès (il avait environ vingt-huit ans), avait de puissantes raisons d’être fidèle : la mémoire des jurés qui liraient le texte était fraîche, et toute infidélité flagrante aurait nui à la thèse défensive. D’autre part, Xénophon, dans sa propre ''Apologie'', confirme certains points centraux (l’oracle de Delphes, le refus de préparer sa défense, le rôle du démon, l’attitude provocante devant le tribunal). Les convergences entre Platon et Xénophon, qui écrivent séparément, garantissent la réalité d’un noyau historique. Pour autant, l’''Apologie'' de Platon est une œuvre écrite, composée probablement plusieurs années après le procès, et qui obéit aux lois de la composition littéraire. La structure en trois discours parfaitement articulée, la densité dialectique de l’interrogatoire de Mélétos, la beauté rythmique de certaines périodes (la finale : « moi pour mourir et vous pour vivre… ») relèvent de l’art platonicien. Le témoignage historique et la recréation littéraire ne sont pas dissociables. La question du « Socrate historique » a été débattue à l’infini. On distingue traditionnellement plusieurs Socrate : celui d’Aristophane (caricatural), celui de Xénophon (pragmatique, moraliste de bon sens), celui de Platon (dialectique et idéaliste), celui d’Aristote (prédécesseur des idées, attribuant à Socrate la recherche des définitions universelles et l’induction<ref>Aristote, ''Métaphysique'', XIII, 4, 1078b17-30 : « deux choses peuvent à bon droit être attribuées à Socrate : les raisonnements inductifs et la définition universelle ».</ref>). Lequel est le vrai ? La réponse la plus raisonnable est qu’aucun ne l’est entièrement, mais qu’ils donnent, collectivement, une image composite d’un homme dont la puissance personnelle a dépassé de beaucoup ce que les documents peuvent restituer. L’''Apologie'' est probablement, de tous les textes, celui qui se tient le plus près de la voix réellement entendue, parce que Platon y a choisi, exceptionnellement, de s’effacer devant son maître. Il est usuel, chez les commentateurs, de distinguer dans l’''Apologie'' des couches. Certains éléments semblent historiques au plus haut degré : le cadre procédural, les noms des accusateurs, l’attitude refusant la supplication, la référence à Chéréphon (mort avant le procès, mais dont les héritiers étaient vivants pour confirmer ou démentir), la condamnation et son déroulement. D’autres éléments sont vraisemblablement platoniciens : l’articulation en trois discours parfaitement équilibrés, la méditation finale sur la mort, peut-être la prophétie adressée aux juges condamnateurs. Mais tout cela relève d’appréciations délicates, et Platon lui-même dans la ''Lettre VII'' revendique le droit de penser philosophiquement à partir du Socrate qu’il a connu<ref>Platon, ''Lettre VII'', 324d-326b. Sur la question socratique, voir Louis-André Dorion, ''Socrate'', Paris, PUF, coll. « Que sais-je ? », 2004.</ref>. === La postérité de l’''Apologie'' === L’''Apologie de Socrate'' est l’un des textes les plus lus, les plus imités, les plus commentés de la philosophie occidentale. Sa postérité ne se laisse pas résumer en quelques lignes, mais on peut en indiquer quelques étapes majeures. Dans l’Antiquité, l’''Apologie'' est immédiatement imitée : Xénophon en donne sa version ; des apologies perdues, d’Eschine de Sphettos ou de Lysias, avaient également circulé. Au IIᵉ siècle, Apulée compose une ''Apologie'' (sur son propre procès en magie) qui imite la structure platonicienne<ref>Apulée, ''Apologie ou De la magie'', texte établi et traduit par Paul Vallette, Paris, Les Belles Lettres, 1924.</ref>. Les écoles hellénistiques (stoïciens, cyniques) prennent Socrate comme figure tutélaire du sage qui meurt pour sa vérité : Épictète et Marc Aurèle se réfèrent constamment à lui<ref>Voir notamment Épictète, ''Entretiens'', II, 1, 32 ; II, 2, 8-20 ; et Marc Aurèle, ''Pensées'', VII, 19 ; XI, 25, 28.</ref>. Les cyniques voient en lui le philosophe de la pauvreté et de la provocation, exemple d’une existence libre des conventions. La tradition chrétienne primitive trouve dans Socrate une figure prophétique. Justin martyr, au IIᵉ siècle, fait de Socrate un « chrétien avant le Christ », guidé par le Logos divin<ref>Justin, ''Première Apologie'', 46 ; ''Seconde Apologie'', 10.</ref>. Les Pères de l’Église le mentionnent souvent, tantôt pour s’en réclamer (Clément d’Alexandrie, Origène), tantôt pour le mettre à distance (Tertullien)<ref>Clément d’Alexandrie, ''Stromates'', I, 14 ; Tertullien, ''Apologétique'', 46.</ref>. La mort de Socrate, rapprochée du martyre, fournit un modèle de témoignage pour la vérité. Toute la hagiographie martyrologique se nourrit, en partie, de l’''Apologie''. À la Renaissance, la redécouverte de Platon par Marsile Ficin et l’Académie florentine met l’''Apologie'' au centre du canon philosophique<ref>Marsile Ficin traduit les œuvres complètes de Platon en latin (''Platonis Opera Omnia'', Florence, 1484), rendant l’''Apologie'' accessible à l’Europe savante.</ref>. Montaigne, dans les ''Essais'', lui consacre plusieurs chapitres et voit en Socrate la figure même de la sagesse sans science, du bon sens humain qui vaut mieux que toutes les spéculations. <blockquote>Socrate fait mouvoir son âme d’un mouvement naturel et commun. Ainsi dit un paysan, ainsi dit une femme. (Montaigne, ''Essais'', III, 12)<ref>Montaigne, ''Essais'', III, 12 « De la physionomie », édition Villey-Saulnier, PUF, 1965, p. 1037-1038.</ref></blockquote> À l’époque des Lumières, l’''Apologie'' devient un manifeste anticlérical. Voltaire y voit le procès de l’intolérance religieuse, Diderot une défense de la libre pensée, Rousseau un modèle d’éthique civile. David peint ''La Mort de Socrate'' (1787)<ref>Jacques-Louis David, ''La Mort de Socrate'', 1787, huile sur toile, 129,5 × 196,2 cm, New York, Metropolitan Museum of Art.</ref>, tableau emblématique où le philosophe saisit la coupe avec sérénité tandis que ses disciples se lamentent : image iconique qui influencera durablement l’imaginaire philosophique. Au XIXᵉ siècle, Hegel, Kierkegaard et Nietzsche proposent trois lectures marquantes. Hegel, dans ses ''Leçons sur l’histoire de la philosophie'', voit Socrate comme le moment où la conscience morale s’intériorise, et dans sa condamnation le conflit tragique entre l’ancienne cité et la subjectivité naissante<ref>G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', t. II, trad. P. Garniron, Paris, Vrin, 1971, p. 263-328.</ref>. Kierkegaard fait de l’ironie socratique, analysée dans ''Le Concept d’ironie'' (1841), le modèle de la subjectivité existentielle<ref name="kierkegaard" />. Nietzsche, dans ''La Naissance de la tragédie'' (1872), voit au contraire en Socrate le destructeur du tragique grec, l’homme théorique qui substitue la raison critique à la sagesse instinctive, et accomplit ainsi une « monstruosité par défaut »<ref name="nietzsche-nt" />. Les trois lectures sont opposées, mais elles attestent toutes la centralité de Socrate dans la pensée moderne. Au XXᵉ siècle, l’''Apologie'' continue d’être un lieu de lecture privilégié. Hannah Arendt, dans ''La Vie de l’esprit'', en fait le modèle de la pensée responsable<ref>Hannah Arendt, ''La Vie de l’esprit'', trad. L. Lotringer, Paris, PUF, 1981 ; voir aussi « Philosophie et politique », ''Les Cahiers de philosophie'', n° 4, 1987.</ref>. Michel Foucault, dans ses derniers cours au Collège de France (''Le Courage de la vérité'', 1984), y voit le texte fondateur de la ''parrhēsía'', c’est-à-dire du « franc-parler » philosophique qui engage la vie de celui qui parle<ref>Michel Foucault, ''Le Courage de la vérité. Le Gouvernement de soi et des autres II. Cours au Collège de France, 1983-1984'', éd. F. Gros, Paris, Gallimard/Seuil, 2009.</ref>. Leo Strauss et son école ont proposé une relecture « ésotérique » du texte, attentive à ce que Socrate ne dit pas et à ce qu’il suggère entre les lignes<ref>Leo Strauss, ''Studies in Platonic Political Philosophy'', Chicago, University of Chicago Press, 1983 ; ''The City and Man'', Chicago, Rand McNally, 1964.</ref>. Lire l’''Apologie'' aujourd’hui, c’est donc entrer dans un texte sédimenté par vingt-quatre siècles de commentaires, et qui n’a pas encore épuisé sa charge philosophique. Chaque époque y trouve un Socrate à sa mesure, et c’est peut-être la marque des grandes œuvres de pouvoir soutenir cette pluralité indéfinie d’interprétations. == Conclusion == L’''Apologie de Socrate'' n’est pas un plaidoyer ordinaire. Ce n’est même pas, à proprement parler, un plaidoyer au sens technique, puisque Socrate y refuse presque toutes les tactiques qui auraient pu le faire acquitter : il ne se fait pas écrire par un logographe, il ne supplie pas, il ne fait pas paraître ses enfants, il provoque le jury quand il lui faudrait le ménager. C’est une profession de foi philosophique, prononcée au moment où la vie de l’auteur est en jeu, et qui tire précisément de cet enjeu sa gravité unique. L’''Apologie'' est la preuve par la mort d’une thèse que Socrate n’a cessé de soutenir par les mots : la vertu vaut plus que la vie. Trois traits en font un texte fondateur. D’abord, il inaugure la figure du philosophe comme témoin : celui dont la cohérence entre la parole et la vie va jusqu’au sacrifice. Socrate meurt parce qu’il ne veut pas trahir ce qu’il a dit ; et Platon, en écrivant l’''Apologie'', fait de cette mort le sceau de la vérité philosophique. La philosophie, à partir de ce texte, n’est plus seulement une activité intellectuelle : elle est un engagement existentiel, et le philosophe n’est plus seulement celui qui sait, mais celui qui vit ce qu’il dit. Ensuite, le texte définit la philosophie elle-même, en l’opposant à la sophistique, à la rhétorique et à la religion populaire. Non un savoir constitué, mais un examen ; non une éloquence, mais une recherche du vrai ; non un rite, mais un service intérieur du divin. Cette triple opposition structure toute la pensée platonicienne ultérieure et, par voie de conséquence, la pensée occidentale. La philosophie, depuis Socrate, se définit comme ce qui n’est pas sophistique : un savoir désintéressé, inséparable d’une pratique de vérité. Enfin, le texte pose la question politique dans ses termes platoniciens : la cité peut-elle accueillir le philosophe ? La condamnation de Socrate se laisse lire comme une réponse négative qu’Athènes aurait donnée en acte à cette question, et c’est ainsi que Platon semble l’interpréter. L’œuvre de Platon tout entière tentera de penser une cité qui répondrait autrement, depuis les esquisses du ''Gorgias'' et de la ''République'' jusqu’à la construction ultime des ''Lois''. Le procès de Socrate est ainsi, indirectement, à l’origine de la philosophie politique occidentale. Lire l’''Apologie'' aujourd’hui, c’est s’exposer à une exigence intacte. La vie non examinée n’est pas digne d’être vécue ; la vertu vaut plus que l’argent, que la réputation, que la vie même ; la lâcheté est pire que la mort ; le juste préfère subir l’injustice plutôt que la commettre. Ces formules, qui paraissent extrêmes, sont le cœur d’une éthique dont la philosophie occidentale n’a cessé de se réclamer, même quand elle croyait s’en affranchir. Le taon, vingt-quatre siècles plus tard, pique toujours. == Notes et références == <references /> == Bibliographie sélective == === Éditions et traductions de l’''Apologie'' === * Platon, ''Apologie de Socrate. Criton'', traduction, introduction et notes par Luc Brisson, Paris, GF-Flammarion, 2016. * Platon, ''Apologie de Socrate'', traduction par Luc Brisson, présentation, notes, dossier, répertoire et glossaire par Arnaud Macé, Paris, GF-Flammarion, 2017. * Platon, ''Apologie de Socrate'', traduction, présentation et notes de Bernard et Renée Piettre, Paris, Le Livre de Poche (Librairie générale française), coll. « Libretti », 1997. * Platon, ''Apologie de Socrate'', texte établi et traduit par Maurice Croiset, Paris, Les Belles Lettres, coll. des Universités de France, 1920 (nombreuses rééditions). * Platon, ''Œuvres complètes'', sous la direction de Luc Brisson, Paris, Flammarion, 2008 (contient l’''Apologie''). === Commentaires === * Claude Chrétien, ''Platon, Apologie de Socrate'', Paris, Hatier, coll. « Profil philosophie », 1993. * Paul Allen Miller, Charles Platter, ''Plato’s Apology of Socrates: A Commentary'', Norman, University of Oklahoma Press, 2010. * Émile de Strycker, S. R. Slings, ''Plato’s Apology of Socrates: A Literary and Philosophical Study with a Running Commentary'', édité et complété par S. R. Slings, Leyde, E. J. Brill, coll. « Mnemosyne Supplements » 137, 1994. * Thomas G. West, ''Plato’s Apology of Socrates: An Interpretation with a New Translation'', Ithaca, Cornell University Press, 1979. === Études sur Socrate et l’''Apologie'' === * Louis-André Dorion, ''Socrate'', Paris, PUF, coll. « Que sais-je ? », 2004. * Gregory Vlastos, ''Socrate : ironie et philosophie morale'', trad. C. Dalimier, Paris, Aubier, 1994 (''Socrates: Ironist and Moral Philosopher'', 1991). * Gregory Vlastos, « The Socratic Elenchus », ''Oxford Studies in Ancient Philosophy'', I, 1983, p. 27-58. * Debra Nails, ''The People of Plato: A Prosopography of Plato and Other Socratics'', Indianapolis, Hackett, 2002. * Gabriele Giannantoni, ''Socratis et Socraticorum Reliquiae'', 4 vol., Naples, Bibliopolis, 1990. * Monique Canto-Sperber (dir.), ''Les Paradoxes de la connaissance. Essais sur le Ménon de Platon'', Paris, Odile Jacob, 1991. === Réceptions modernes === * G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', t. II, trad. P. Garniron, Paris, Vrin, 1971. * Søren Kierkegaard, ''Le Concept d’ironie constamment rapporté à Socrate'' (1841), trad. P.-H. Tisseau et E.-M. Jacquet-Tisseau, Paris, Éditions de l’Orante, 1975. * Friedrich Nietzsche, ''La Naissance de la tragédie'' (1872), dans ''Œuvres philosophiques complètes'', t. I, Paris, Gallimard, 1977. * E. R. Dodds, ''Les Grecs et l’irrationnel'' (1951), trad. M. Gibson, Paris, Flammarion, coll. « Champs », 1977. * Hannah Arendt, ''La Vie de l’esprit'', trad. L. Lotringer, Paris, PUF, 1981. * Michel Foucault, ''L’Herméneutique du sujet. Cours au Collège de France, 1981-1982'', éd. F. Gros, Paris, Gallimard/Seuil, 2001. * Michel Foucault, ''Le Courage de la vérité. Cours au Collège de France, 1983-1984'', éd. F. Gros, Paris, Gallimard/Seuil, 2009. * Leo Strauss, ''Studies in Platonic Political Philosophy'', Chicago, University of Chicago Press, 1983. === Sources antiques === * Aristophane, ''Les Nuées'', dans ''Théâtre complet'', t. I, trad. V.-H. Debidour, Paris, Gallimard, coll. « Folio », 1965. * Xénophon, ''Apologie de Socrate'' et ''Mémorables'', trad. P. Chambry, Paris, Garnier-Flammarion, 1967. * Diogène Laërce, ''Vies et doctrines des philosophes illustres'', trad. sous la direction de M.-O. Goulet-Cazé, Paris, Le Livre de Poche, coll. « La Pochothèque », 1999. * Plutarque, ''Du démon de Socrate'', dans ''Œuvres morales'', t. VIII, trad. J. Hani, Paris, Les Belles Lettres, 1980. * Aristote, ''Rhétorique'', trad. M. Dufour et A. Wartelle, Paris, Les Belles Lettres, 1938-1973. * Cicéron, ''Tusculanes'', trad. J. Humbert, Paris, Les Belles Lettres, 1931. t8hldeouuloimftsly1b8h773qp0cen 763990 763987 2026-04-19T07:46:10Z PandaMystique 119061 763990 wikitext text/x-wiki == Introduction générale == {{wikisource|Apologie de Socrate (Platon)|Apologie de Socrate}} === L’événement et sa portée === En l’an 399 avant notre ère, à Athènes, un vieil homme de soixante-dix ans comparaît devant un tribunal populaire composé de cinq cent un jurés citoyens. Il s’appelle Socrate, fils de Sophronisque ; il est accusé d’impiété et de corruption de la jeunesse. Au terme d’une longue journée d’audience, à une faible majorité d’abord (telle que, selon ses propres mots en 36a, un basculement de trente voix aurait suffi à l’acquittement), puis à une majorité plus large pour la peine capitale, il sera condamné à mort. Quelques semaines plus tard, il boira la ciguë dans sa cellule, devant ses disciples, après le retour du bateau sacré de Délos. Cet événement, déjà traumatisant en son temps, est devenu le mythe fondateur de la philosophie occidentale : le moment où la cité met à mort celui qui prétendait y exercer, par la parole et l’examen, un magistère moral. L’''Apologie de Socrate'' est l’œuvre par laquelle Platon, alors âgé d’une trentaine d’années, rend compte de ce procès. On situe sa composition probablement entre 390 et 385, soit une dizaine d’années après les faits. Le texte se présente sans cadre fictionnel : nul narrateur, nul personnage introductif, nulle scène préalable comme il en existe dans la plupart des autres dialogues. Nous sommes plongés d’emblée dans la parole de Socrate, au moment où il se lève pour prendre la parole. Contrairement à la plupart des dialogues platoniciens, Platon s’efface presque entièrement : comme le remarque B. Piettre dans son commentaire, dans aucune autre œuvre on n’a l’impression d’entendre avec une telle proximité la parole de Socrate, « comme s’il nous était donné d’assister au procès »<ref name="piettre">Bernard Piettre, ''Platon, Apologie de Socrate'', traduction, présentation et notes de Bernard et Renée Piettre, Paris, Le Livre de Poche (Librairie générale française), coll. « Libretti », 1997, p. 21.</ref>. Cela ne signifie pas que l’''Apologie'' soit une transcription sténographique du plaidoyer : Platon recompose, réorganise, stylise. Mais il s’efforce, probablement plus que dans tout autre dialogue, de restituer la voix, le ton, l’argumentation du maître. Il convient de rappeler que l’''Apologie'' de Platon n’est pas la seule défense posthume de Socrate. Xénophon a également rédigé une ''Apologie'' qui nous est parvenue, brève et d’un ton psychologique différent, ainsi que des ''Mémorables'' qui reprennent certains motifs<ref>Xénophon, ''Apologie de Socrate'' et ''Mémorables'', trad. P. Chambry, Paris, Garnier-Flammarion, 1967.</ref>. D’autres disciples, comme Eschine de Sphettos, ont composé des écrits socratiques aujourd’hui perdus<ref>Voir Gabriele Giannantoni, ''Socratis et Socraticorum Reliquiae'', 4 vol., Naples, Bibliopolis, 1990.</ref>. Un pamphlet hostile, l’''Accusation de Socrate'' du sophiste Polycrate (probablement composé vers 393-392), circulait et aurait donné à Platon l’occasion de répondre indirectement. La concurrence entre ces « socratiques » explique vraisemblablement en partie le soin que Platon met à camper son propre Socrate, qui finira par s’imposer comme la figure canonique dans la postérité. L’enjeu de l’''Apologie'' dépasse de loin la réhabilitation posthume d’un homme. À travers le procès de Socrate, Platon met en accusation la cité qui l’a condamné ; il ouvre l’espace d’une autre politique, où la philosophie, cet « amour du savoir » (''philosophía''), apparaît comme la véritable vocation civique. Condamner le philosophe, c’est pour Athènes se condamner elle-même, préférer l’ignorance au savoir, la facilité au courage, la flatterie à la vérité. L’''Apologie'' constitue ainsi, selon une formule souvent reprise, l’un des textes fondateurs de la philosophie comme discours distinct, à la fois opposé à la sophistique et à la rhétorique, et radicalement engagé dans l’existence concrète. Elle offre en outre une cristallisation exemplaire de ce qu’on appellera le « dialogue socratique », genre dont Platon fera sa forme propre de pensée. === Contexte historique du procès === Pour comprendre la portée du texte, il faut garder à l’esprit la situation d’Athènes en 399. La cité sort épuisée de la longue guerre du Péloponnèse (431-404), qui l’a opposée à Sparte et s’est soldée par la défaite totale d’Athènes. Après la capitulation, Lysandre, le général spartiate, a imposé un gouvernement oligarchique (les Trente) qui a régné par la terreur pendant environ huit mois (404-403) : exécutions sommaires, confiscations, exil des démocrates. La démocratie a été rétablie par un soulèvement armé parti de Phylè en 403, et, dans le cadre des accords de réconciliation conclus sous l’archontat d’Euclide, une amnistie générale a été proclamée pour ne pas ajouter la guerre civile aux malheurs déjà subis : il était désormais interdit, sous peine de sanctions, de se référer aux événements antérieurs à la restauration<ref>Sur cette amnistie, qu’il ne faut pas confondre avec le décret de Patroclide de 405 (qui portait sur la réhabilitation des ''atimoi''), voir Aristote, ''Constitution d’Athènes'', 39-40 ; et Xénophon, ''Helléniques'', II, 4, 38-43.</ref>. Mais les plaies étaient béantes, et le ressentiment courait souterrainement. Or Socrate avait été, dans les années antérieures, un familier de personnages qui incarnaient précisément ces désastres. Alcibiade, son disciple brillant et fantasque, avait entraîné Athènes dans la désastreuse expédition de Sicile (415-413), scandalisé la cité par l’affaire de la mutilation des hermès, puis fini par trahir sa patrie en passant chez Sparte. Critias, autre de ses proches, avait été l’un des chefs, peut-être le principal, du gouvernement des Trente, artisan de la terreur oligarchique. Charmide, oncle de Platon et lui aussi de l’entourage socratique, avait également appartenu à ce régime. Même si Socrate lui-même avait refusé de collaborer avec les Trente (comme il le rappellera dans son plaidoyer à propos de l’arrestation de Léon de Salamine), sa réputation se trouvait entachée. Le procès de 399, bien que portant officiellement sur des griefs religieux, fonctionne donc aussi comme un règlement de comptes politique, que l’amnistie interdisait pourtant de mener ouvertement. Eschine le rhéteur, quelques décennies plus tard, dira sans détour que les Athéniens avaient condamné Socrate « parce qu’il avait été le maître de Critias »<ref>Eschine, ''Contre Timarque'', I, 173.</ref>. L’accusation est portée par trois hommes. Mélétos, poète tragique obscur, plutôt jeune, est le plaignant officiel, celui qui a déposé la plainte auprès de l’archonte-roi, magistrat compétent pour les affaires religieuses. Mais Socrate sait parfaitement que le véritable moteur de la procédure est Anytos, riche tanneur, homme politique influent de la démocratie restaurée, qui avait participé à la chute des Trente. Son fils, raconte Xénophon, fréquentait Socrate et préférait ses entretiens à la tannerie paternelle : hostilité d’autant plus vive<ref>Xénophon, ''Apologie de Socrate'', 29-31.</ref>. Dans le ''Ménon'' de Platon (90b-95a), Anytos apparaît comme le type même du démocrate borné, ennemi viscéral des sophistes et plus largement des intellectuels<ref>Platon, ''Ménon'', 89e-95a.</ref>. Le troisième accusateur, Lycon, est un orateur dont la fonction semble avoir été de soutenir la plaidoirie par une péroraison énergique. La plainte, dont Diogène Laërce nous a conservé le texte<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 40.</ref>, s’énonce approximativement ainsi : <blockquote>Socrate est coupable de ne pas reconnaître les dieux que reconnaît la cité et d’introduire des divinités nouvelles ; il est aussi coupable de corrompre la jeunesse. Peine requise : la mort.</blockquote> Trois griefs, donc : la négation des dieux traditionnels, l’introduction de nouvelles divinités, la corruption de la jeunesse. Ces trois chefs d’accusation sont intimement liés dans la logique de l’accusation : en introduisant de nouvelles divinités et en niant les anciennes, Socrate aurait, par son enseignement, détourné les jeunes gens du respect dû à la religion civique, donc corrompu la cité dans ses fondements. L’accusation d’impiété (''asébeia'') était une accusation politique au sens fort, puisque la religion à Athènes n’était pas une affaire privée mais la substance même du lien civique. === Le cadre procédural === Le procès se tient à l’Héliée, le tribunal populaire, probablement dans un local situé sur l’agora. Les jurés (501 ce jour-là, nombre impair pour éviter les égalités) ont été tirés au sort le matin même parmi les six mille citoyens qui s’étaient inscrits comme héliastes pour l’année. Une journée entière est consacrée à la procédure, dont le temps est rigoureusement partagé entre l’accusation et la défense au moyen de la clepsydre, horloge à eau. Chaque partie parle pour son propre compte : ni procureur ni avocat. Il est permis de faire corroborer son discours par un orateur plus habile (ce dont Mélétos semble avoir bénéficié avec Lycon), ou de lire un discours écrit par un logographe. La tradition rapporte que Lysias, le plus grand logographe de l’époque, aurait composé pour Socrate un plaidoyer de défense ; Socrate en aurait reconnu la beauté avant de le rejeter comme ne lui convenant pas<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 40 ; Cicéron, ''De l’orateur'', I, 231.</ref>. La procédure, pour un procès où la loi n’a pas fixé la peine à l’avance (on parle alors d’''agôn timētós'', « procès à peine à estimer »), comporte deux votes. Après les plaidoiries, un premier vote tranche sur la culpabilité. En cas de condamnation, l’accusateur propose une peine (ici, la mort), l’accusé doit proposer une contre-peine, et un second vote choisit entre les deux sans possibilité de moyen terme. Cette contrainte procédurale pèse lourd sur la suite : les jurés, contraints de choisir entre deux propositions extrêmes, se trouvent ainsi, par la stratégie de Socrate refusant de proposer une peine crédible, poussés à voter la mort. C’est la procédure en deux temps qui explique la structure tripartite de l’''Apologie'' telle que Platon nous l’a transmise : d’abord le grand plaidoyer de défense (17a-35d), puis le discours de contre-proposition pénale après le verdict de culpabilité (35e-38b), enfin une dernière allocution prononcée après la condamnation à mort, adressée tour à tour aux jurés qui ont voté contre lui et à ceux qui l’ont soutenu (38c-42a). Il faut ajouter que les procès athéniens étaient des spectacles autant que des procédures. Le public y assiste ; les plaideurs usent de toute la rhétorique, des larmes de la famille éplorée, des supplications ostentatoires, des témoignages de moralité. Aristophane, dans ''Les Guêpes'' (422), ridiculise la passion que les Athéniens portaient à ces mises en scène judiciaires et le plaisir qu’ils prenaient à voir les accusés se répandre en lamentations. Socrate, comme on le verra, refuse délibérément toute cette théâtralité, ce qui nourrira, après la condamnation, son reproche aux juges : ils l’ont condamné non pas parce qu’il était coupable, mais parce qu’il n’a pas consenti à s’abaisser. === Structure et dispositif du texte === Lu rétrospectivement à travers la grille qu’Aristote constituera dans sa ''Rhétorique'', le plaidoyer de Socrate laisse reconnaître, dans son grand plan, les parties canoniques du discours judiciaire : on y distingue successivement l’exorde (17a-18a), la diabolè ou dénigrement des accusations antérieures (18a-19d), la narration ou ''diḗgēsis'' qui expose les faits (19d-24b), la réfutation proprement dite (24b-28a), l’amplification et la péroraison (28a-35d). Il ne s’agit pas de prétendre que Platon a composé son texte en suivant consciemment un schéma déjà codifié (la ''Rhétorique'' aristotélicienne est postérieure), mais de remarquer que le plaidoyer épouse, dans ses grandes articulations, les usages rhétoriques de son temps. Cette conformité apparente est constamment subvertie par l’ironie socratique : Socrate adopte la forme du discours judiciaire tout en la démentant dans son contenu, en refusant ses procédés et en retournant ses attentes. Le texte fonctionne ainsi comme une parodie philosophique de la rhétorique : il emprunte à l’art oratoire sa structure, mais seulement pour en exhiber la vanité quand il s’agit de la vérité. == Lecture suivie du texte == === Le premier discours : la défense (17a-35d) === ==== L’exorde (17a-18a) : la vérité contre l’éloquence ==== Dès les premiers mots, Socrate pose le ton. La formule d’ouverture, <blockquote>Quel effet, Athéniens, ont produit sur vous mes accusateurs, je l’ignore,</blockquote> est un exorde classique (en grec ''prooímion'') dont la fonction rhétorique, comme le rappelle Aristote dans la ''Rhétorique'' (III, 14), est de capter la bienveillance et l’attention de l’auditoire<ref>Aristote, ''Rhétorique'', III, 14, 1415a22-b30.</ref>. Socrate s’en sert pour établir immédiatement l’antithèse qui structurera tout son plaidoyer : ses accusateurs ont parlé avec persuasion, mais n’ont dit « rien de vrai ou presque » ; lui, en revanche, dira « toute la vérité ». Cette opposition entre l’éloquence ornée des adversaires et la parole nue de Socrate est un retournement ironique savamment préparé. Les accusateurs ont mis les juges en garde contre le « redoutable discoureur » qu’est Socrate. Mais, rétorque-t-il, s’ils entendent par là celui qui dit la vérité, il concède qu’il est un orateur, bien que pas à leur manière. Ses discours ne seront pas, dit-il, « des discours élégamment tournés, comme les leurs, ni même des discours qu’embellissent des expressions et des termes choisis », mais « des choses dites à l’improviste dans les termes qui [lui] viendront à l’esprit ». L’opposition technique est nette : d’un côté le discours apprêté, fondé sur la sélection du vocabulaire (''onómata''), la tournure des phrases (''rhḗmata'') et les arrangements (''kósmos'') ; de l’autre une parole qui se veut « au hasard », sans préparation. Socrate demande aux juges de lui pardonner cette façon de parler : il a soixante-dix ans, comparaît pour la première fois de sa vie devant un tribunal, et il est donc « étranger à la langue en usage ici ». Comme un véritable étranger qui parlerait dans son dialecte, il entend s’exprimer comme il le fait ordinairement, sur l’agora ou devant les comptoirs des changeurs. Le juge, conclut-il, doit juger sur le fond : <blockquote>si mes allégations sont justes ou non. Telle est en effet la vertu du juge, tandis que celle de l’orateur est de dire la vérité. (18a)</blockquote> Cette prise de position initiale est philosophiquement lourde de conséquences. Elle reformule, dès l’ouverture, l’opposition fondamentale entre la rhétorique (l’art de persuader, tel que le pratiquent sophistes et logographes) et la philosophie (recherche de la vérité par l’examen rationnel). Les sophistes, comme Gorgias ou Protagoras, avaient fait de la rhétorique un instrument neutre, capable de « faire de l’argument le plus faible l’argument le plus fort » (formule qui reviendra explicitement contre Socrate au chef d’accusation). Socrate, à l’inverse, affirme que la parole a pour fonction première de manifester le vrai, et que celui qui se défend en justice ne doit pas jouer des passions ou des artifices, mais soumettre les arguments à l’examen rationnel des jurés. On notera pourtant que cette protestation de simplicité est elle-même une figure rhétorique sophistiquée : celui qui clame qu’il ne sait pas parler parle déjà, et fort bien. Il s’agit de la ''dissimulatio artis'' que Cicéron théorisera plus tard : l’art supérieur consiste à cacher l’art. L’ironie socratique commence dès l’exorde. Socrate se présente comme un ''idiṓtēs'' (un simple particulier, un « idiot » au sens grec), étranger aux codes du tribunal, mais cette posture est elle-même un dispositif stratégique qui retourne contre l’accusation la suspicion de sophistique : ce n’est pas Socrate qui manipule les mots, ce sont les accusateurs. L’inversion est complète. ==== Les « anciens accusateurs » : la calomnie de longue durée (18a-19d) ==== Socrate introduit ensuite une distinction cruciale : il doit répondre à deux catégories d’accusations, celles de ses « accusateurs récents » (Mélétos, Anytos, Lycon), mais aussi, et d’abord, celles de ses « anciens accusateurs », qui ont « depuis de nombreuses années » répandu une image fausse de lui. Ceux-là, il les redoute davantage que les nouveaux, pour trois raisons convergentes. Ils sont d’abord nombreux : il ne s’agit pas de trois personnes identifiables, mais d’une rumeur collective. Leurs accusations sont ensuite anciennes : elles ont eu le temps de s’enraciner dans les esprits. Enfin, ils ont agi auprès des juges « dès l’enfance », à un âge où « vous aviez le moins de défiance », de sorte que les jurés en ont reçu l’empreinte avant même d’avoir l’âge de l’examiner. Quelle est la teneur de cette calomnie ancienne ? Socrate la résume en une formule qui reviendra plusieurs fois dans le texte comme un chef d’accusation fantasmatique : <blockquote>Il existe un certain Socrate, un savant, un « penseur » qui s’intéresse aux choses qui se trouvent en l’air, qui mène des recherches sur tout ce qui se trouve sous la terre et qui de l’argument le plus faible fait l’argument le plus fort. (18b)</blockquote> Cette caricature hétéroclite condense en réalité trois traits initialement distincts. Il y a d’abord la figure du physicien à la manière des présocratiques (Anaxagore, Diogène d’Apollonie), qui spéculait sur les phénomènes célestes (''tà metéōra'') et souterrains. Il y a ensuite celle de l’athée, puisque interroger la nature par la raison revenait, dans la perception populaire, à nier les dieux traditionnels (Anaxagore avait été poursuivi pour cette raison vers 433, Protagoras également, et tous deux avaient été contraints à l’exil). Il y a enfin celle du sophiste, expert en retournements dialectiques, capable de faire triompher n’importe quelle cause par le seul art des mots. L’incohérence de ce portrait (un philosophe de la nature qui serait en même temps un manipulateur rhétorique) ne l’empêche pas d’être efficace dans l’opinion. C’est le propre de la rumeur (''diabolḗ'') : elle n’a pas à être cohérente pour être puissante. Socrate le reconnaît avec lucidité : la force de cette calomnie vient précisément de ce qu’elle ne repose sur aucune source identifiable, qu’on ne peut donc ni l’interroger, ni la réfuter. Combattre ces accusateurs anonymes, dit Socrate, « c’est comme se battre contre des ombres » (18d). C’est une difficulté propre au philosophe dans la cité : face à l’opinion établie, il n’a pas d’adversaire identifiable, donc pas de prise dialectique. Socrate désigne cependant une source probable : les comédies, et en particulier ''Les Nuées'' d’Aristophane, représentées en 423 (vingt-quatre ans avant le procès)<ref>Aristophane, ''Les Nuées'', représentées pour la première fois aux Grandes Dionysies de 423 av. J.-C.</ref>. Il la mentionne d’abord anonymement, parlant d’un <blockquote>Socrate qui se balançait, en prétendant qu’il se déplaçait dans les airs et en débitant plein d’autres bêtises concernant des sujets sur lesquels je ne suis un expert ni peu ni prou. (19c)</blockquote> Le nom d’Aristophane sera explicitement prononcé peu après. Dans cette pièce, le poète comique met en scène un Socrate juché dans une corbeille suspendue, pour mieux « mêler sa pensée subtile à l’air », invoquant les Nuées comme divinités substitutives à celles de la religion populaire, enseignant à un paysan, Strepsiade, puis à son fils Phidippide, comment faire triompher le « Raisonnement injuste » et ruiner par cet apprentissage la piété filiale. La pièce s’achève d’ailleurs sur l’incendie du « Pensoir » socratique. L’enjeu pour Socrate n’est pas seulement de corriger une image fausse : c’est de montrer que les accusations de Mélétos se rabattent exactement sur cette caricature (négation des dieux, introduction de nouveautés religieuses, corruption de la jeunesse), de sorte que contre-attaquer la comédie, c’est déjà dissoudre la plainte. Il y a là un geste tranché : Socrate refuse explicitement d’être confondu, d’une part avec les physiciens qui spéculent sur la nature, d’autre part avec les sophistes qui enseignent la rhétorique contre rémunération. Il énumère d’ailleurs plusieurs sophistes célèbres (Gorgias de Léontinoi, Prodicos de Céos, Hippias d’Élis) et rappelle l’anecdote d’Événos de Paros, qui faisait payer cinq mines pour son enseignement (20b) : somme considérable, correspondant à environ un an et demi de salaire d’un ouvrier qualifié<ref name="brisson">Platon, ''Apologie de Socrate. Criton'', trad. et notes de Luc Brisson, Paris, GF-Flammarion, 2016, p. 131, note 54.</ref>. Socrate, lui, n’a rien à vendre, et c’est précisément cela, comme on le verra, qui témoigne de la pureté de sa démarche. Notons enfin une dimension cruciale de ce moment : Socrate récuse par avance toute identification à la figure du philosophe-penseur retiré du monde, que Platon décrit dans le ''Théétète'' (174a) par l’anecdote célèbre de Thalès tombant dans le puits en contemplant les étoiles<ref>Platon, ''Théétète'', 174a.</ref>. La défense de Socrate sera celle d’un homme de l’agora, immergé dans la cité, engagé dans l’examen de ses concitoyens. Le philosophe socratique n’est pas un contemplatif éloigné des affaires humaines : c’est au contraire le plus urbain des hommes, celui qui ne quitte jamais la ville (comme il le dit dans le ''Phèdre''), celui dont l’activité est fondamentalement politique, même s’il n’exerce aucune charge politique. ==== La narration : l’oracle de Delphes et la naissance de la mission (19d-24b) ==== Pour expliquer l’origine de la calomnie, Socrate engage ce qui correspond à la narration (''diḗgēsis'') du discours rhétorique. Il entreprend de raconter comment il est devenu ce personnage que certains considèrent comme un savant. Cette narration, qui occupe une part importante du discours, contient deux éléments structurants : le récit de l’oracle de Delphes et l’exposé de l’enquête qui s’ensuivit. ===== Le savoir humain et le savoir plus qu’humain (20d-21a) ===== Avant de raconter l’oracle, Socrate pose une distinction fondamentale qui constitue peut-être la pièce conceptuelle centrale de tout le plaidoyer. On dit qu’il est « savant » (''sophós'') : soit. Mais savant en quoi ? Il existe, dit-il, un savoir qui excède la mesure humaine, celui auquel prétendent, moyennant rétribution, les sophistes. Ce savoir-là, Socrate ne le possède pas ; et il aurait « des chances d’être un savant » seulement dans un sens plus modeste, celui d’un savoir qui « se rapporte à l’être humain », une sagesse humaine (''anthrōpínē sophía''). La distinction est capitale. Elle sépare deux ordres de connaissance : celui qui porte sur les choses divines (la nature, le cosmos, les causes premières), que Socrate refuse de revendiquer ; et celui qui porte sur l’humain, sur ce qu’il convient de faire pour vivre bien. Cette distinction préfigure le partage que toute l’histoire de la philosophie reprendra entre philosophie théorique et philosophie pratique, et elle annonce également la réorientation socratique qu’évoque Cicéron dans un passage célèbre : <blockquote>Socrate a fait descendre la philosophie du ciel sur la terre, l’a introduite dans les villes et même dans les maisons, et l’a obligée à s’enquérir de la vie, des mœurs, des choses bonnes et mauvaises. (''Tusculanes'', V, 10-11)<ref>Cicéron, ''Tusculanes'', V, 4, 10-11 : « Socrates autem primus philosophiam devocavit e caelo et in urbibus conlocavit et in domus etiam introduxit... »</ref></blockquote> La philosophie cesse d’être cosmologie pour devenir éthique. Pour prouver l’existence et la nature de ce savoir proprement humain, Socrate invoque un témoin insolite mais sans appel : « le dieu de Delphes », c’est-à-dire Apollon pythien. Le recours à la parole oraculaire, dans un tribunal populaire attaché à la religion civique, est rhétoriquement habile : il retourne l’accusation d’impiété en présentant Socrate comme un serviteur du dieu. ===== L’oracle et l’enquête (21a-23c) ===== C’est le moment où surgit, dans le texte, le récit de l’oracle. Chéréphon, ami d’enfance de Socrate (un homme à la passion impétueuse, démocrate, exilé sous les Trente et revenu avec la démocratie, que la comédie moquait pour sa maigreur ascétique et sa « mine d’endive »<ref>Aristophane, ''Les Guêpes'', v. 1408 ; ''Les Oiseaux'', v. 1296 ; ''Les Nuées'', v. 104.</ref>), était allé un jour à Delphes et avait eu l’audace de demander à la Pythie s’il existait quelqu’un de plus sage que Socrate. La Pythie, prêtresse d’Apollon, parle au nom du dieu et rend des oracles aux consultants : elle répondit que « personne n’était plus sage ». Chéréphon est mort entre-temps (probablement vers 403), mais son frère, présent à l’audience, pourra en témoigner. Cette réponse divine met Socrate dans un profond embarras. Il a « conscience de n’être savant ni peu ni prou ». Mais le dieu, par définition, ne peut mentir : « la loi divine l’interdit » (21b). Comment résoudre l’énigme (''aínigma'') ? Socrate décide alors d’entreprendre une vérification. Il va chercher quelqu’un de plus sage que lui, afin de pouvoir revenir à Delphes et dire : <blockquote>ce dieu m’avait désigné comme le plus sage, mais voici qui l’est davantage.</blockquote> Cette démarche, loin d’être un geste d’orgueil, est présentée comme un service rendu au dieu : c’est pour vérifier l’oracle, non pour le contredire, que Socrate entreprend son enquête. Il accorde à l’oracle une autorité suffisante pour l’interroger méthodiquement, selon un principe qui rappelle la maxime exégétique attribuée à Héraclite : « le maître dont l’oracle est à Delphes ne dit ni ne cache rien : il fait signe »<ref>Héraclite d’Éphèse, fragment DK 22 B 93.</ref>. L’enquête est méthodiquement menée dans trois directions, que Socrate parcourt successivement. Il va d’abord trouver un homme politique (dont il tait le nom, conformément à la pudeur judiciaire) qui passait pour sage. Après l’avoir interrogé, il constate que cet homme se croit sage mais ne l’est pas. Socrate tire alors la leçon capitale qui donnera son contenu à la sagesse humaine : <blockquote>Il y a des chances que je sois moi-même plus sage que cet homme. Car aucun de nous, il est vraisemblable, ne sait rien qui en vaille la peine ; mais lui pense savoir alors qu’il ne sait pas, tandis que moi, tout comme je ne sais pas, je ne pense pas non plus savoir. (21d)</blockquote> Tel est le célèbre savoir du non-savoir socratique : non pas une ignorance totale, mais la conscience lucide et réfléchie de sa propre ignorance, qui vaut davantage que l’illusion de la science. Socrate répète l’opération avec d’autres hommes politiques, et chaque fois la déception est la même, mais pire : plus l’homme est réputé, plus son ignorance est grande ; plus il est humble, plus il est proche du vrai. Socrate passe ensuite aux poètes : auteurs de tragédies, poètes dithyrambiques et autres. Il leur demande ce que signifient leurs propres œuvres, espérant d’eux un savoir, puisqu’ils produisent de la beauté. Il découvre alors qu’ils composent « non par savoir, mais par une sorte de disposition naturelle et par inspiration, comme les devins et les oracles » (22c). Les poètes disent beaucoup de belles choses sans savoir ce qu’elles veulent dire ; et, comme les hommes politiques, ils se croient, à cause de leur art, savants dans d’autres domaines où ils ne le sont pas. L’inspiration poétique est ainsi reconnue comme réelle, mais dissociée du savoir : le poète est possédé par une muse, non par une compétence. Cette analyse, qui reviendra dans le ''Ion'', est une pièce importante de la pensée platonicienne sur l’art<ref>Platon, ''Ion'', 533d-535a.</ref>. Socrate examine enfin les artisans (''cheirotéchnai''). Ici, la situation est plus nuancée. Les artisans possèdent une compétence réelle, une ''tékhnē'', que Socrate ne songe pas à leur dénier. Mais cette compétence les induit à se croire savants « dans les choses les plus importantes », c’est-à-dire dans les questions morales et politiques, alors qu’ils ne le sont pas. L’artisan, parce qu’il sait faire une paire de chaussures, se croit en droit d’avoir un avis éclairé sur la justice. C’est là un point crucial : Socrate reconnaît la légitimité de la ''tékhnē'' dans son domaine propre, mais refuse l’extrapolation de la compétence technique à la sagesse pratique. La conclusion de l’enquête est double. D’une part, Socrate conclut à la véracité de l’oracle : sa sagesse consiste en ce qu’il ne croit pas savoir ce qu’il ne sait pas. D’autre part, il comprend que l’oracle ne le désignait pas ''lui'' spécifiquement comme sage, mais se servait de son nom à titre d’exemple : <blockquote>il y a des chances, Messieurs, pour qu’en réalité le sage, ce soit le dieu, et que dans ce fameux oracle il veuille dire que la sagesse humaine a bien peu de valeur, et même aucune ; et il est clair qu’en désignant Socrate il s’est servi de mon nom pour me prendre en exemple, comme s’il disait : « Le plus sage d’entre vous, hommes, est celui qui, comme Socrate, a reconnu qu’en réalité sa sagesse ne vaut rien. » (23a-b)</blockquote> Socrate devient ainsi, non pas un savant, mais un exemple (''parádeigma'') par lequel le dieu invite tous les hommes à reconnaître la misère de leur prétention au savoir. La sagesse n’est pas une propriété de l’individu, mais une relation à l’ignorance ; elle est, on pourrait dire, éminemment ''maïeutique'' : elle fait accoucher les autres de la conscience de leur propre ignorance. ===== La mission et la naissance de la haine (23b-24b) ===== De cette interprétation de l’oracle naît la mission socratique. Socrate continue, « en service pour le dieu » (''hypēresía toû theoû''), à enquêter sur quiconque prétend être sage, pour manifester chaque fois qu’il ne l’est pas. Cette activité l’a réduit à « une grande pauvreté », car elle lui a occupé toute sa vie au détriment de ses affaires. Mais elle a aussi suscité contre lui des haines innombrables, chaque fois qu’il a démasqué l’ignorance d’un puissant ou d’un réputé. On touche ici à un mécanisme psychologique qu’il faut bien mesurer : nul n’aime être convaincu d’ignorance, surtout publiquement ; celui qui le fait, même par amour du vrai, se constitue des ennemis à proportion de sa rigueur. Les jeunes gens de bonne famille, ceux qui disposent de loisir (''scholḗ''), prennent plaisir à le voir faire ; ils tentent à leur tour d’imiter sa démarche, et ainsi « se font eux-mêmes haïr par ceux qu’ils examinent » (23c), qui ne s’en prennent pas à ces jeunes, mais à Socrate. Ce dernier devient ainsi le responsable imaginaire d’une activité critique qui déborde largement sa personne. C’est le ressort profond de l’accusation de « corruption de la jeunesse » : ce n’est pas que Socrate ait enseigné le mal, c’est que ses méthodes, imitées par ses disciples, font éclater un scandale qu’on veut lui imputer. C’est ainsi que s’est formée la réputation selon laquelle Socrate serait un « corrupteur de la jeunesse », et qu’on a repris contre lui la vieille caricature : un homme qui fait des recherches sur le ciel et la terre et qui fait triompher la mauvaise cause. Socrate tire la conclusion rhétorique : « c’est en disant la vérité que je me fais des ennemis », ce qui est, dit-il, la preuve qu’il dit vrai. Les causes de l’accusation sont là : non dans une faute réelle, mais dans le ressentiment de ceux qu’il a démasqués. Cette section du plaidoyer est philosophiquement fondamentale. Elle met en place plusieurs concepts clefs du socratisme tel que Platon l’entend. D’abord, la philosophie comme examen (''exétasis'') et non comme doctrine : on ne peut enseigner la philosophie de Socrate parce qu’elle n’est pas un corps de propositions à transmettre, mais une pratique à exercer. Ensuite, le savoir de l’ignorance comme seule forme accessible du savoir humain. Enfin, la philosophie comme mission divine, ce qui lui confère une légitimité supérieure à celle des institutions politiques. Cette dimension religieuse de la philosophie est essentielle à l’économie du texte : si la mission est divine, elle ne peut être interrompue par un décret humain, fût-il celui d’un tribunal souverain. On notera également que cette démarche, en démasquant l’ignorance des prétendus savants, introduit une différence entre savoir et non-savoir sur des sujets où la démocratie athénienne supposait, pour délibérer, qu’il n’y en avait pas. Comme l’explique un passage du ''Protagoras'' (319c-d), rédigé par Platon peu après l’''Apologie'', les assemblées démocratiques distinguent les sujets techniques (sur lesquels seuls les compétents s’expriment) et les sujets politiques (sur lesquels tous les citoyens, cordonniers, potiers, tanneurs ou menuisiers, peuvent donner leur avis)<ref>Platon, ''Protagoras'', 319b-d.</ref>. La délibération collective suppose que sur les sujets politiques, il n’existe pas de différence de compétence entre les citoyens. Or l’enquête socratique a précisément pour effet de réintroduire cette différence sur les objets où l’institution démocratique la refoulait. Se prétendre ignorant soi-même et révéler l’ignorance d’autrui sur les questions de justice ou de vertu, c’est mettre en cause le principe majoritaire là où il s’applique. On comprend en quoi, malgré les apparences, la posture socratique est subversive pour la démocratie athénienne : elle frappe à sa racine épistémologique. ==== La réfutation de Mélétos (24b-28a) ==== Socrate en vient maintenant aux accusations officielles. Il relit la plainte, <blockquote>Socrate est coupable de corrompre la jeunesse et de reconnaître non pas les dieux que la cité reconnaît, mais, au lieu de ceux-là, des divinités nouvelles,</blockquote> et entreprend de l’examiner point par point. Il utilise alors la prérogative que la loi athénienne accorde à l’accusé : interroger directement son accusateur, qui est tenu de répondre. Ce qui suit est l’un des grands morceaux dialectiques de l’''Apologie''. Socrate, en un véritable elenchos (cette réfutation par interrogation qui est sa marque de fabrique), démonte successivement les trois volets de l’accusation. On notera d’emblée un jeu de mots grec qui court tout l’interrogatoire : le nom de Mélétos (''Mélētos'') évoque le verbe ''mélei'' (« se soucier de »). Socrate va reprocher à Mélétos, précisément, de ne pas se soucier (mélein) des choses dont il prétend se soucier. L’ironie est ciselée jusque dans l’onomastique. La méthode que Socrate utilise ici est l’''elenchos'' : il part des thèses de l’interlocuteur, l’amène par des questions à en reconnaître les conséquences, puis lui fait constater la contradiction entre ces conséquences et d’autres thèses qu’il tient également pour vraies. Cette procédure, qui ne démontre rien positivement mais montre qu’une thèse est intenable, est le moteur de la dialectique socratique dans les dialogues de jeunesse de Platon<ref>Sur la méthode de l'elenchus, voir Gregory Vlastos, « The Socratic Elenchus », ''Oxford Studies in Ancient Philosophy'', I, 1983, p. 27-58.</ref>. ===== Qui rend les jeunes meilleurs ? (24b-25c) ===== Premier volet : la corruption de la jeunesse. Socrate commence par un piège dialectique. Si Mélétos accuse quelqu’un de corrompre les jeunes, c’est qu’il a à l’esprit ce qui les rend meilleurs. Qu’il le dise donc. Mélétos, pris au dépourvu, balbutie : « Les lois ». Mais ce n’est pas une personne, objecte Socrate. Il insiste : quel ''homme'' rend les jeunes meilleurs ? Mélétos répond alors, au gré d’une improvisation visiblement embarrassée : les juges, puis les membres du Conseil, puis ceux de l’Assemblée, bref tous les Athéniens ; tous, sauf Socrate. Socrate retourne alors l’argument par une analogie mémorable, celle des chevaux. Suppose-t-on que tous les hommes rendent les chevaux meilleurs, et qu’un seul les corromprait ? Non, c’est évidemment le contraire : pour les chevaux comme pour tous les animaux, seuls quelques spécialistes savent les rendre meilleurs, et la plupart des gens, s’ils s’en occupent, les nuisent. Il en va de même pour les jeunes gens. L’éducation est un art, et l’art suppose la compétence, qui n’est pas le partage du plus grand nombre. En prétendant que tous éduquent bien sauf Socrate, Mélétos révèle qu’il n’a jamais sérieusement réfléchi à ce dont il parle : il s’est désintéressé de la question. Ce qui est précisément ce que le jeu de mots sur son nom entendait suggérer. Ce premier argument est intéressant par ce qu’il laisse apercevoir de la position de Socrate à l’égard de la démocratie. L’éducation, comme le souligne le commentaire de C. Chrétien, est « une affaire politique tant la formation de l’homme paraît indissociable de celle du citoyen »<ref name="chretien">Claude Chrétien, ''Platon, Apologie de Socrate'', Paris, Hatier, coll. « Profil philosophie », 1993, p. 44.</ref>. Or, pour Mélétos et pour les Athéniens en général, la cité est à elle-même sa propre pédagogie : chaque citoyen forme, par son exemple et sa participation à la vie publique, les futurs citoyens. Socrate, à l’inverse, soutient que l’éducation, comme les autres arts, relève d’une compétence particulière, ce qui va à l’encontre du présupposé démocratique d’une compétence civique également partagée. En paraissant ne se battre que sur un détail logique, Socrate attaque en réalité le principe même de la démocratie athénienne : celui selon lequel chaque citoyen est naturellement compétent pour la délibération politique. ===== Corrompre volontairement ? (25c-26a) ===== Deuxième pièce du dispositif : Socrate demande à Mélétos s’il le pense corrompre les jeunes volontairement ou involontairement. Mélétos, rageusement, répond : volontairement. Mais Socrate montre alors que cette réponse est intenable. Car si les méchants font du mal à leur entourage, et les bons du bien, alors corrompre volontairement les gens qui nous entourent (avec lesquels on vit) c’est s’exposer soi-même à en pâtir. Personne, pas même le plus ignorant, ne choisit délibérément de se nuire à soi-même. Donc, ou bien Socrate ne corrompt pas, ou bien, s’il corrompt, c’est involontairement. Et dans ce cas, la loi ne prévoit pas un procès mais une remontrance privée : si on m’avait averti, j’aurais cessé. En me traînant devant le tribunal, Mélétos prouve que son but n’est pas de me corriger mais de me punir ; ce qui est contradictoire avec l’idée d’une faute involontaire. Cet argument repose sur l’un des principes les plus fermes du socratisme, le célèbre paradoxe socratique selon lequel nul ne fait le mal volontairement (''oudeís hekṓn hamartánei''). Socrate y croit sincèrement : si l’on savait vraiment ce qu’est le bien, on ne pourrait pas ne pas le vouloir. Le vice n’est pas une perversion de la volonté, c’est une forme d’ignorance. Cette thèse, qui paraîtra contre-intuitive à toute la tradition postérieure (notamment chrétienne, qui mettra l’accent sur la malice du mal), sera développée dans plusieurs dialogues platoniciens, notamment le ''Gorgias'' et le ''Protagoras''<ref>Platon, ''Gorgias'', 466a-468e, 509c-e ; ''Protagoras'', 352b-358d.</ref>. Aristote, dans l’''Éthique à Nicomaque'', la critiquera comme négligeant la réalité de la ''akrasía'' (faiblesse de la volonté)<ref>Aristote, ''Éthique à Nicomaque'', VII, 2-3, 1145b21-1147b19.</ref>. Mais l’argument sert ici surtout à piéger Mélétos dans une contradiction : soit tu m’accuses d’une faute involontaire, et le procès est illégitime (car la procédure judiciaire vise des fautes intentionnelles) ; soit tu m’accuses d’une faute volontaire, mais celle-ci est psychologiquement impossible (personne ne choisit de se nuire à soi-même). Dans les deux cas, la plainte s’effondre. Remarquons la précision juridique : Socrate joue habilement sur la distinction athénienne entre fautes volontaires (justiciables) et involontaires (pour lesquelles la remontrance privée, la ''nouthesía'', était la procédure appropriée). ===== L’athéisme et les divinités démoniques (26a-28a) ===== Troisième volet : la question religieuse, qui est le cœur même de l’accusation. Socrate demande à Mélétos de préciser son propos : l’accuse-t-il de reconnaître des dieux différents de ceux de la cité, ou de ne reconnaître aucun dieu du tout ? La distinction est cruciale, car la plainte elle-même est ambiguë (elle évoque des « divinités nouvelles », ce qui présuppose que Socrate croit à des divinités, mais lui reproche aussi de ne pas reconnaître celles de la cité). Mélétos, avec une maladresse que Socrate exploite pleinement, s’emporte et répond : « aucun dieu du tout ». Il ajoute même, piégé par sa propre fureur, que Socrate prétend, à la manière d’Anaxagore, que « le soleil est une pierre et la lune une terre ». Socrate saisit l’occasion avec une précision chirurgicale. D’abord, il ridiculise la confusion : Anaxagore, en effet, a soutenu cette thèse physicienne, mais les livres d’Anaxagore, qui se trouvent au marché (à l’orchestre, endroit de l’agora où l’on vendait les livres), coûtent « tout au plus une drachme » ; pourquoi donc accuser Socrate d’avoir inventé ce qu’on peut lire partout et qui n’est pas de lui ? Le trait est doublement dévastateur : il innocente Socrate et il prouve l’incompétence de Mélétos, qui ne distingue pas Socrate d’Anaxagore. Ensuite, il tend son piège principal. La plainte officielle dit que Socrate introduit de nouvelles divinités (''daimónia''). Or Mélétos vient d’affirmer que Socrate n’admet aucun dieu du tout. Ces deux affirmations sont contradictoires : on ne peut pas à la fois reconnaître des divinités et ne reconnaître aucun dieu. Mélétos se contredit lui-même, et sous serment, car la plainte avait été déposée sous serment réciproque (''antōmosía''). Socrate poursuit par un syllogisme subtil. Peut-on reconnaître des « phénomènes démoniques » (''daimónia prágmata'') sans reconnaître l’existence de démons ? De même que l’on ne peut reconnaître des phénomènes hippiques sans reconnaître les chevaux, ni des phénomènes musicaux sans reconnaître les musiciens, on ne peut reconnaître des phénomènes démoniques sans reconnaître les démons. Or les démons, selon la religion grecque traditionnelle, sont soit des dieux soit des enfants de dieux. Donc, si Socrate reconnaît des démons, il reconnaît aussi des dieux, ou à tout le moins des êtres divins. La plainte se contredit elle-même : Mélétos affirme à la fois que Socrate ne reconnaît aucun dieu et qu’il reconnaît des démons, donc des dieux. Ce raisonnement est brillant sur le plan dialectique, mais il a suscité la perplexité des commentateurs. Il repose sur une définition traditionnelle des démons comme « enfants des dieux », qui n’est pas toujours stabilisée dans la culture grecque (chez Hésiode, les démons sont plutôt des hommes de l’âge d’or devenus esprits), et laisse entière la question de savoir ce que sont les « nouvelles divinités » dont Socrate était réellement accusé. La plupart des commentateurs modernes estiment que l’accusation visait précisément le fameux ''daimónion'' socratique, la voix intérieure divine dont Socrate parlera plus loin, qui serait apparue aux Athéniens comme une divinité privée, nouvelle, donc impie car non reconnue par la cité. Socrate, en déplaçant le débat sur la question abstraite de l’existence ou non des démons, élude habilement cette difficulté. C’est un procédé dialectique, non un argument de fond. Il faut aussi percevoir le geste de fond. Comme le note Claude Chrétien, Socrate, par ce raisonnement, rattache sa croyance en des « phénomènes démoniques » à une croyance minimale mais ferme en la divinité, sur un mode qui relève d’une théologie négative : il ne dit rien de positif sur les dieux, mais affirme seulement que quelque chose, dans l’expérience humaine, manifeste leur existence<ref name="chretien-30">Claude Chrétien, ''Platon, Apologie de Socrate'', ''op. cit.'', p. 30-31.</ref>. Cela est cohérent avec son agnosticisme sur les mythes (dans le ''Phèdre'', Socrate refuse de spéculer sur les aventures de Borée enlevant Orithye<ref>Platon, ''Phèdre'', 229b-230a.</ref>) et avec sa dévotion pratique à la religion de la cité (on le voit obéir aux rites dans plusieurs dialogues). Socrate est pieux en acte, agnostique en théorie : il reconnaît une transcendance divine, sans prétendre en décrire la nature. Cette position, fine et paradoxale, est à l’origine d’une tension féconde dans toute la théologie philosophique ultérieure. À la fin de cette section, Socrate conclut qu’il a suffisamment montré que l’accusation de Mélétos est sans consistance. Mais, ajoute-t-il avec lucidité, il sait bien que ce n’est pas elle qui le fera condamner : c’est la vieille calomnie, la haine accumulée au fil des années. D’autres hommes justes, avant lui, ont subi le même sort, et beaucoup en subiront après. ==== La mission divine et le modèle héroïque (28a-30c) ==== Ayant réfuté l’accusation formelle, Socrate entreprend alors ce que Piettre appelle une « amplification » : il justifie tout son mode de vie. L’''Apologie'' bascule ici d’un plaidoyer juridique vers une proclamation philosophique. La question n’est plus « suis-je coupable de ces griefs précis ? » mais : « comment justifier un mode d’existence qui expose à la mort ? » C’est à partir de ce moment que l’''Apologie'' cesse d’être un simple plaidoyer pour devenir un manifeste. ===== Le modèle d’Achille (28b-d) ===== Socrate anticipe une objection qu’un auditoire athénien pouvait sincèrement formuler : n’as-tu pas honte, Socrate, d’avoir mené une existence qui t’expose à mourir ? Il y répond par l’évocation des héros homériques, et notamment d’Achille, la figure de référence de la vertu guerrière grecque. Quand Thétis, sa mère, lui annonça qu’il mourrait s’il vengeait Patrocle en tuant Hector, Achille répondit, selon l’''Iliade'' : <blockquote>que je meure immédiatement, pour peu que je punisse le coupable, plutôt que de rester ici, à être la risée de tous, assis sur mes vaisseaux, poids inutile de la terre.<ref>Homère, ''Iliade'', XVIII, v. 96-104 ; cité par Socrate en ''Apologie'', 28c-d.</ref></blockquote> Achille a donc méprisé la mort pour préserver l’honneur. Celui qui occupe une place, explique Socrate, doit y rester, au péril de sa vie, « sans tenir compte d’autre chose que du déshonneur ». Cet argument est une adaptation du modèle homérique, et non une simple reprise. Socrate transforme l’héroïsme aristocratique d’Achille (celui d’un demi-dieu, fils d’une déesse) en une vertu démocratique accessible à tout soldat de la phalange hoplitique : il s’agit de tenir son poste, quelle que soit sa place, qu’on l’ait choisie ou qu’on y ait été affecté par son chef (28d). La vertu n’est plus aristocratique, elle est civique ; elle n’est plus la propriété d’une élite, elle est accessible à quiconque occupe sa place avec fermeté. Cette démocratisation de la vertu héroïque prépare la transposition qui va suivre. Socrate rappelle d’ailleurs son propre passé militaire. Il a combattu à Potidée (432-429), à Amphipolis (424), et surtout à Délion (424), où son courage fut loué par Alcibiade dans le ''Banquet'' (219e et suivants<ref>Platon, ''Banquet'', 219e-221c.</ref>) et par le général Lachès dans le dialogue du même nom (''Lachès'', 181a-b<ref>Platon, ''Lachès'', 181a-b.</ref>). Comme ces soldats qui ne quittent pas leur poste, Socrate ne peut quitter celui qui lui a été assigné, par le dieu. ===== Le poste assigné par le dieu (28d-29a) ===== Socrate pose alors la transposition capitale : <blockquote>le poste qu’on m’a assigné, moi, est celui du philosophe, qui doit vivre en philosophant, en s’examinant soi-même et en examinant les autres. Je ne peux le quitter par crainte de la mort, pas plus qu’un soldat ne peut quitter le sien.</blockquote> La philosophie est ainsi présentée comme une assignation divine, équivalente à l’ordre d’un chef de guerre, et plus impérieuse encore, puisque l’ordre vient du dieu. Cette analogie entre vie philosophique et vie militaire, qui fera carrière dans toute la tradition stoïcienne (Sénèque, Épictète, Marc Aurèle en useront abondamment<ref>Voir notamment Épictète, ''Entretiens'', I, 9, 24 ; III, 24, 31-36 ; Marc Aurèle, ''Pensées'', III, 5 ; VII, 45.</ref>), fonde la philosophie comme service, comme ''officium'', comme devoir qu’on ne peut déserter sans se déshonorer. ===== L’ignorance de la mort (29a-b) ===== Vient alors l’un des passages les plus célèbres du texte, où Socrate renverse la psychologie commune du courage : <blockquote>Craindre la mort, Athéniens, ce n’est rien d’autre que se donner pour savant sans l’être ; c’est donner l’impression qu’on sait ce qu’on ne sait pas. (29a)</blockquote> Car personne ne sait ce qu’est la mort, ni si elle n’est pas pour l’homme le plus grand des biens ; mais on la redoute comme si l’on savait qu’elle est le plus grand des maux. C’est là, dit Socrate, la forme la plus répréhensible d’ignorance : croire savoir ce qu’on ne sait pas, donc la même erreur que celle des faux sages qu’il a démasqués dans son enquête. Le raisonnement est d’une rigueur remarquable. Il articule le savoir du non-savoir à l’éthique du courage. Socrate, lui, sait qu’il ne sait rien de la mort ; il ne la craint donc pas. Mais il sait en revanche, et c’est la seule « exception » à son ignorance proclamée, que <blockquote>commettre une injustice et désobéir à un meilleur que soi, dieu ou homme, cela je sais que c’est mauvais et honteux. (29b)</blockquote> On voit ici le dispositif éthique qui va commander toute la suite : entre un mal certain (l’injustice et la lâcheté) et un mal supposé mais incertain (la mort), le sage choisit sans hésiter d’éviter le premier. Le courage philosophique n’est donc pas un mépris enthousiaste de la mort, comme celui d’Achille, c’est une lucidité sur ce qui est réellement à craindre. Ce renversement de la psychologie héroïque en lucidité rationnelle est l’un des gestes fondateurs de la philosophie morale. ===== L’hypothèse de l’acquittement conditionnel (29c-30c) ===== Socrate imagine alors une situation extrême, une sorte d’expérience de pensée. Supposons que les juges lui offrent de l’acquitter à la condition qu’il cesse de philosopher. Alors Socrate répondrait : <blockquote>Athéniens, je vous suis reconnaissant et je vous aime, mais j’obéirai au dieu plutôt qu’à vous ; et tant qu’il me restera un souffle de vie, tant que j’en serai capable, je ne cesserai, soyez-en sûrs, de philosopher, de vous exhorter et de m’expliquer avec tel ou tel d’entre vous. (29d)</blockquote> Il continuerait à dire à chacun la formule qui résume toute sa mission : <blockquote>Ô excellent homme, toi qui es d’Athènes, la cité la plus grande et la plus réputée pour son savoir et sa puissance, tu n’as pas honte de t’occuper de ta fortune et des moyens de t’enrichir le plus possible, de ta réputation, des honneurs, alors que de ton intelligence, de la vérité, de ton âme et des moyens de la perfectionner, tu ne t’en occupes et ne t’en soucies aucunement ? (29d-e)</blockquote> On est ici au cœur du message socratique, tel que Platon l’a consigné. Le renversement qu’opère ce passage est philosophiquement majeur. D’une part, Socrate affirme que son obéissance au dieu l’emporte sur son obéissance à la cité : c’est, en puissance, toute la doctrine de la désobéissance civile au nom d’une norme transcendante. D’autre part, il renverse la hiérarchie des biens : la vertu ne vient pas de l’argent, mais l’argent et tous les autres biens viennent de la vertu ; il faut donc se soucier prioritairement de son âme (''psuchḗ''), et non de ses biens matériels ou de sa réputation. C’est la naissance, dans la philosophie occidentale, du souci de soi (''epiméleia heautoû'') compris comme soin de l’âme et examen permanent de soi-même, thème qui parcourra toute la tradition ultérieure, des écoles hellénistiques aux spirituels chrétiens, jusqu’aux lectures contemporaines de Michel Foucault qui en fera un objet majeur de ses derniers cours au Collège de France<ref name="foucault-hs">Michel Foucault, ''L’Herméneutique du sujet. Cours au Collège de France, 1981-1982'', éd. F. Gros, Paris, Gallimard/Seuil, coll. « Hautes Études », 2001.</ref>. Socrate ajoute alors l’une de ses déclarations les plus provocatrices : <blockquote>Là-dessus, Athéniens, croyez-en ou non Anytos, acquittez-moi ou ne m’acquittez pas, toujours est-il que je ne changerai pas de conduite, même si je devais souffrir mille morts. (30c)</blockquote> La défense ne demande plus un acquittement ; elle proclame la pérennité de la mission, quel que soit le verdict. Socrate, à ce moment précis du plaidoyer, cesse d’être un accusé pour devenir un apôtre. Cette attitude explique, rétrospectivement, l’incompréhension et l’irritation des jurés : il ne se défend pas, il les défie. ==== Socrate, « taon de la cité » (30c-31c) ==== Socrate affirme alors, avec une audace étonnante, <blockquote>si vous me condamnez à mort, ce n’est pas à moi, mais à vous-mêmes, que vous ferez le plus de tort. (30c)</blockquote> Car ni Mélétos ni Anytos ne peuvent le léser véritablement : ils peuvent le tuer, l’exiler, le priver de ses droits civiques (''atimía''), choses que certains tiendraient pour de grands malheurs, mais lui ne les tient pas pour tels. Le vrai mal est celui que font à leur âme ceux qui entreprennent de tuer injustement. Cette thèse, que Platon développera dans le ''Gorgias'' (469c) sous la forme « il vaut mieux subir l’injustice que la commettre »<ref>Platon, ''Gorgias'', 469c : « [...] je choisirais de subir plutôt que de commettre l’injustice. » Voir aussi 474b-479e.</ref>, est probablement la plus radicale de toutes les thèses morales de l’antiquité. Surgit alors la célèbre image du taon. Socrate est <blockquote>un homme attaché à la cité par le dieu, comme le serait un taon au flanc d’un cheval de grande taille et de bonne race, mais qui se montrerait un peu mou en raison même de sa taille et qui aurait besoin d’être réveillé par l’insecte. (30e)</blockquote> Athènes est ce cheval noble mais assoupi ; Socrate est l’insecte qui le pique, le réveille, le harcèle. Le dieu lui a donné cette mission, qui explique qu’il passe son temps à aborder chacun, « comme un père ou un frère plus âgé », pour le persuader d’avoir souci de la vertu. Cette image mérite qu’on s’y arrête, tant elle est dense. Elle articule trois éléments. D’abord, la noblesse du cheval : Athènes n’est pas critiquée absolument, mais reconnue pour ce qu’elle est, la plus belle cité du monde grec, de « grande taille et de bonne race ». Ensuite, son engourdissement : cette grandeur même la rend molle, somnolente, incapable de s’ébrouer spontanément. Enfin, la nécessité du taon : seule une figure dérangeante, insupportable, inutile en apparence, peut réveiller la cité. Le taon n’est pas à sa place dans le cheval ; il est un corps étranger, irritant ; mais précisément, c’est de cette position dérangeante que vient son utilité. La philosophie est pensée ici comme critique nécessaire, comme dissidence féconde, comme décalage qui maintient la cité vivante. On voit se dessiner une dialectique subtile. Socrate est indissociablement dedans et dehors : citoyen d’Athènes, engagé dans sa cité, respectueux de ses lois au point d’accepter la mort plutôt que de fuir (comme l’exprimera le ''Criton''<ref>Platon, ''Criton'', 50a-54d.</ref>), et en même temps étranger à ses conformismes, à ses illusions, à ses complaisances. Sans le taon, le cheval dormirait ; mais le cheval peut aussi, d’un mouvement irrité, écraser le taon. C’est exactement ce qui se passe au procès. L’image contient en elle-même une prophétie : tuer le taon, c’est se priver de la piqûre bienfaisante, condamner la cité au sommeil. D’où la prédiction de Socrate : <blockquote>en suite de quoi, vous passeriez votre vie à dormir, à moins que le dieu, ayant souci de vous, ne vous envoie quelqu’un d’autre. (31a)</blockquote> La suite de l’histoire, à tout le moins celle de la pensée, dira que cet autre sera Platon lui-même, puis Aristote, et la longue lignée des philosophes que l’''Apologie'' aura rendus possibles. Socrate apporte ensuite une preuve empirique de son désintéressement : sa pauvreté. Si son activité avait un but intéressé, si elle rapportait un salaire, on pourrait douter de la pureté de ses motivations. Mais ses accusateurs, malgré leur acharnement, n’ont pu produire aucun témoin attestant qu’il ait jamais exigé ou reçu un salaire. Sa misère est la meilleure preuve qu’il dit vrai. Cette insistance sur la gratuité de son enseignement est une pique adressée aux sophistes, qui se faisaient richement rémunérer, et un trait supplémentaire qui distingue la philosophie socratique de la ''téchnē'' marchande des sophistes. Socrate n’est pas un prestataire de services ; il est un serviteur du dieu. ==== Le démon et la prudence politique (31c-32e) ==== Une objection se présente naturellement : si Socrate est ce grand conseiller des particuliers, pourquoi ne monte-t-il pas à la tribune pour conseiller la cité elle-même dans ses assemblées ? La réponse est le fameux passage sur le ''daimónion'' socratique. ===== Qu’est-ce que le démon de Socrate ? (31c-d) ===== Socrate confie aux juges ce qu’il a déjà dit « maintes fois en maints endroits » : <blockquote>il m’advient quelque chose de divin et de démonique (''theîón ti kai daimónion''), une voix intérieure qui, depuis [mon] enfance, [...] chaque fois qu’elle m’advient, me détourne toujours de ce que je me propose de faire, mais jamais ne m’y encourage. (31c-d)</blockquote> Cette voix a trois caractéristiques remarquables. Elle est toujours dissuasive : « jamais elle ne m’y encourage ». Elle est personnelle : elle ne s’adresse qu’à Socrate. Elle est présente depuis l’enfance, donc constitutive de son rapport au monde. Elle s’est précisément opposée à son entrée en politique. Il s’agit donc, littéralement, de cette « divinité nouvelle » que l’accusation lui impute, ce que Mélétos, ironise Socrate, a d’ailleurs « consigné dans son acte d’accusation » (31d). Cette ironie est cinglante : l’accusation a pris pour un crime ce que Socrate revendique comme une grâce. La nature du ''daimónion'' a fait l’objet de multiples interprétations, dès l’Antiquité et jusqu’aux temps modernes. Plutarque a consacré un traité entier à la question (''Du démon de Socrate'') dans lequel il discute plusieurs hypothèses<ref>Plutarque, ''Du démon de Socrate'' (''De genio Socratis''), dans ''Œuvres morales'', t. VIII, trad. J. Hani, Paris, Les Belles Lettres, 1980.</ref>. À l’époque moderne, on l’a interprété tour à tour comme une hallucination d’un névrosé<ref>F. Lélut, ''Du démon de Socrate, spécimen d’une application de la science psychologique à celle de l’histoire'', Paris, Trinquart, 1836.</ref>, comme une manifestation de l’inconscient<ref>Arthur Koestler, ''Le Démon de Socrate'', Paris, Calmann-Lévy, 1970.</ref>, comme la voix de la conscience morale<ref>G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', tome II (sur Socrate), trad. G. Marmasse, Paris, Vrin, 2007, p. 316-321.</ref>, comme une inspiration divine authentique<ref>Henri Bergson, ''Les Deux sources de la morale et de la religion'' (1932), dans ''Œuvres'', éd. du Centenaire, Paris, PUF, 1959, p. 1027.</ref>, comme une intuition irrationnelle<ref>E. R. Dodds, ''Les Grecs et l’irrationnel'' (1951), trad. M. Gibson, Paris, Flammarion, coll. « Champs », 1977, chap. VII.</ref>. Nietzsche y voyait, de manière originale, la monstruosité d’un homme chez qui l’instinct, contrairement à l’ordinaire, ne crée pas mais critique, et chez qui la conscience rationnelle est au contraire créatrice : Socrate comme « homme théorique », rupture dans l’histoire de l’esprit grec dionysiaque<ref name="nietzsche-nt">Friedrich Nietzsche, ''La Naissance de la tragédie'' (1872), § 13-15, trad. P. Lacoue-Labarthe, dans ''Œuvres philosophiques complètes'', t. I, Paris, Gallimard, 1977.</ref>. Il faut probablement admettre, avec Claude Chrétien, que le démon est irréductible à une simple astuce défensive ou à un symbole : Socrate y croyait réellement, au point de risquer sa vie en le suivant<ref>Chrétien, ''op. cit.'', p. 28-29.</ref>. Son caractère purement négatif (il inhibe, ne prescrit jamais) en fait un signe du divin dans la vie humaine, mais un signe essentiellement limitatif : la divinité indique seulement ce qu’il ne faut pas faire, et laisse à l’homme la responsabilité de chercher, par l’examen rationnel, ce qu’il doit faire. Cette structure, où le divin ne donne pas la vérité mais seulement la limite, est cohérente avec la théologie négative que Socrate manifeste dans tout le plaidoyer : nous ne savons pas positivement ce que sont les dieux, mais nous recevons d’eux des signes qui nous empêchent de nous égarer. ===== Pourquoi pas la politique ? (31d-32a) ===== Socrate explique que si le démon l’a détourné de la politique, c’est pour préserver sa vie : « s’il y avait longtemps que j’avais entrepris de faire de la politique, il y a longtemps que je serais mort ». Et il formule alors une sentence vertigineuse, qui est peut-être la plus critique de l’''Apologie'' à l’égard de la démocratie athénienne : <blockquote>il n’y a personne au monde qui puisse garder la vie sauve s’il s’oppose loyalement à vous ou à toute autre collectivité, et s’il cherche à empêcher qu’il ne se produise dans la cité de nombreuses injustices et illégalités. Mais nécessairement tout vrai champion de la justice, s’il veut garder la vie sauve ne serait-ce qu’un peu de temps, doit vivre en simple particulier (''idiōteúein'') mais non en homme public. (32a)</blockquote> Cette sentence est d’une portée immense. Elle signifie que la politique telle qu’elle se pratique à Athènes est incompatible avec la justice. Le juste, s’il veut vivre, doit rester à l’écart des affaires publiques ; et s’il y entre, il doit s’attendre à mourir. C’est la cassure socratique avec la tradition civique grecque, qui voyait dans la participation politique (''politeía'') la plus haute réalisation de l’homme libre. L’homme proprement libre, pour les Grecs classiques, c’est le citoyen qui prend part aux assemblées ; l’''idiṓtēs'' qui se retire dans la sphère privée est, sinon méprisé, du moins considéré comme incomplet. Socrate renverse cette hiérarchie : la vraie vie politique, pour le juste, passe par le retrait de la politique officielle et par une politique privée, celle de la discussion personne à personne, de l’enseignement moral qui opère non par les discours publics mais par l’examen intime de chacun. C’est, comme le suggère la présentation de la collection Flammarion, « l’espace d’une autre politique »<ref name="brisson-mace-presentation">Arnaud Macé, « Présentation », dans Platon, ''Apologie de Socrate'', traduction par Luc Brisson, Paris, GF-Flammarion, 2017.</ref>. Cette thèse, qu’on peut lire comme une désertion civique, est aussi une critique profonde des conditions de la délibération démocratique. Elle rejoint ce que Platon développera dans la ''République'' : la cité idéale est celle où la philosophie serait au pouvoir, non celle où elle est écrasée par le plus grand nombre. Mais l’''Apologie'' n’est pas encore la ''République'' : Socrate n’y propose pas une contre-cité, il y constate seulement qu’aucune cité existante ne permet au juste de participer à ses affaires sans se renier. ===== Les deux épisodes probants (32a-e) ===== Socrate prouve cette thèse par deux épisodes biographiques, choisis avec soin. Le premier se situe sous la démocratie, en 406, l’année du procès des généraux des Arginuses. Les Athéniens avaient remporté une victoire navale importante contre Sparte près des îles Arginuses (au large de Lesbos), mais les généraux victorieux avaient été empêchés par une tempête de ramasser les cadavres et les naufragés athéniens, violation grave des usages religieux. Rentrés à Athènes, ils furent mis en cause ; mais au lieu de leur garantir un procès individuel comme l’exigeait le droit athénien, l’Assemblée, emportée par la colère populaire, voulut les juger en bloc. Socrate siégeait ce jour-là au Conseil, comme prytane pour sa tribu, l’Antiochide. Il fut le seul des cinquante prytanes à refuser de mettre aux voix cette motion collective, malgré les menaces et les cris de la foule<ref>Le récit détaillé du procès des généraux des Arginuses se trouve chez Xénophon, ''Helléniques'', I, 7, 1-35.</ref>. Les orateurs voulaient le faire arrêter sur-le-champ, les citoyens eux-mêmes l’y encourageaient ; il tint bon. Les généraux furent néanmoins jugés et six d’entre eux exécutés. Peu après, Athènes regretta sa décision, mais Socrate avait risqué sa vie pour la légalité, sans succès immédiat. Le second épisode se situe sous l’oligarchie, en 404. Les Trente l’avaient convoqué avec quatre autres citoyens à la Tholos (la rotonde, siège des prytanes occupée par le régime) et lui avaient ordonné d’aller chercher à Salamine un riche citoyen démocrate, Léon, pour qu’il soit exécuté et que ses biens soient confisqués. C’était une manœuvre classique des Trente : compromettre un maximum de citoyens dans leurs crimes pour les rendre solidaires du régime<ref>Xénophon, ''Helléniques'', II, 3, 39 ; Platon, ''Lettre VII'', 324d-325a.</ref>. Socrate, lui, refusa l’ordre. Il rentra simplement chez lui pendant que les quatre autres allaient chercher Léon, qui fut assassiné. Socrate, rappelle-t-il, aurait probablement payé cela de sa vie si les Trente n’avaient pas été renversés peu après (c’était le cas : le régime tomba en 403). Ces deux épisodes sont politiquement remarquables, et Platon les a sans doute choisis avec une intention nette. Ils montrent Socrate s’opposant également aux excès de la démocratie (procès des Arginuses) et à ceux de l’oligarchie (affaire de Léon), par fidélité à une justice supérieure au régime en place. Il n’est ni un démocrate de conviction ni un oligarque : il est un homme qui, dans l’un et l’autre cas, risque sa vie pour ne pas commettre d’injustice. Cette double symétrie est cruciale : elle répond par avance à tous ceux qui, dans la démocratie restaurée de 399, voudraient voir en Socrate un sympathisant des Trente, en raison de ses liens avec Critias et Charmide. Platon montre au contraire que Socrate a résisté aux Trente au péril de sa vie. Mais il montre également qu’il a résisté à la démocratie elle-même, quand celle-ci violait le droit. La neutralité politique de Socrate, ou plutôt cet au-delà du partisanisme, constitue une part de sa radicalité, qui déconcerte tous ceux qui voudraient l’enrôler dans un camp. ==== Socrate et ses « disciples » (33a-34b) ==== Socrate en vient alors au dernier volet du premier discours : la question des jeunes gens qu’on l’accuse d’avoir corrompus. Il récuse d’abord le terme même de « disciple » : <blockquote>je n’ai jamais, moi, été le maître (''didáskalos'') de personne. (33a)</blockquote> Cette affirmation est importante. Elle distingue radicalement Socrate des sophistes, qui se présentaient comme maîtres (''didáskaloi'') et vendaient un enseignement structuré ; Socrate n’a jamais promis un enseignement, jamais fait payer, jamais suivi un programme ; il a parlé à quiconque voulait l’écouter, jeunes et vieux, riches et pauvres, sans distinction, et n’est pas responsable de ce que chacun devient au sortir de la conversation. Cette posture est philosophiquement significative : elle implique que la philosophie n’est pas transmissible comme une technique, mais seulement comme une pratique qu’on ne peut qu’imiter. Socrate produit alors un argument a contrario d’une grande force. Si réellement il avait corrompu les jeunes, pourquoi n’est-ce pas ''eux-mêmes'', devenus adultes, qui viendraient témoigner contre lui ? Ou du moins leurs proches, parents, frères, qui auraient à se plaindre de cette corruption et en seraient les premiers concernés ? Or, bien au contraire, nombre de ses familiers, ou de leurs proches, sont présents à l’audience pour le soutenir. Socrate en énumère plusieurs, nommément, dans un passage qui vaut témoignage historique : Criton et son fils Critobule, Lysanias de Sphettos père d’Eschine (l’auteur de dialogues socratiques), Antiphon père d’Épigène, Nicostratos frère de Théodotos, ainsi qu’Adimante (le frère aîné de Platon) et plusieurs autres<ref>''Apologie'', 33d-34a. Sur ces personnages, voir Debra Nails, ''The People of Plato: A Prosopography of Plato and Other Socratics'', Indianapolis, Hackett, 2002.</ref>. <blockquote>Je pourrais citer pour vous beaucoup d’autres hommes, parmi lesquels il aurait fallu que Mélétos produise, au cours de son discours, quelque témoin. (34a)</blockquote> Cette preuve par les témoins absents est d’une grande force logique : l’accusateur a été incapable de trouver, parmi tous ceux qui auraient dû être les premières victimes, un seul pour le blâmer. C’est ce qu’on appelle un argument ''a silentio'' : le silence des supposées victimes prouve l’innocence de l’accusé. On notera que Platon se fait ici historien. En nommant les disciples présents, il fixe un moment dans le temps et offre à la postérité un témoignage vérifiable. Il se nomme lui-même plus loin (34a, puis 38b), parmi les amis prêts à se porter caution pour l’amende. Cette présence documentaire est rare dans les dialogues platoniciens, où Platon s’efface généralement : ici, il est témoin du procès de son maître. ==== Le refus du pathos (34b-35d) ==== Socrate aborde alors la péroraison de son premier discours. Il sait parfaitement ce qu’on attend d’un accusé athénien au moment crucial : larmes, supplications, présentation de la femme et des enfants éplorés, appel à la pitié, théâtralité de la détresse. Cette mise en scène, connue de tous par les comédies d’Aristophane et par l’habitude des tribunaux, était devenue quasi rituelle<ref>Sur la caricature du tribunal populaire, voir Aristophane, ''Les Guêpes'', v. 548-630.</ref>. Socrate a trois fils, dont l’un est déjà adolescent (''meirákion'') et les deux autres encore petits ; il pourrait les faire paraître, lui qui ne refuse pas d’être un homme. Mais il ne le fera pas. Pourquoi ? Deux motifs convergent. D’abord, par souci de l’honneur : un homme de sa réputation, réelle ou supposée, ne peut s’abaisser à ces scènes sans se ridiculiser et sans « ridiculiser la cité ». Les citoyens étrangers qui l’observent et qui connaissent la réputation d’Athènes s’étonneraient de voir les plus éminents de ses hommes se comporter de manière indigne. Socrate invoque la ''dóxa'' (l’opinion) d’Athènes aux yeux du monde grec pour justifier son refus de jouer le jeu. Mais surtout, et c’est le second motif, plus profond : il ne serait pas juste de supplier le juge. Et ici, Socrate formule une analyse capitale de la fonction judiciaire : <blockquote>le juge ne siège pas pour réduire la justice en faveur (''charízesthai''), mais pour décider de ce qui est juste ; et il a fait serment non de favoriser qui lui plaît, mais de rendre la justice selon les lois. (35c)</blockquote> Le juge athénien prêtait en effet un serment (l’''héliastikós hórkos'') par lequel il s’engageait à juger selon les lois<ref>Sur le serment héliastique, voir Démosthène, ''Contre Timocrate'', 149-151 ; Adriaan Lanni, ''Law and Justice in the Courts of Classical Athens'', Cambridge, Cambridge University Press, 2006, p. 75-76.</ref>. Supplier les juges, c’est leur demander de se parjurer, donc d’introduire le parjure dans la cité, donc de commettre une impiété effective contre les dieux garants du serment. Le geste de Socrate est ici d’une cohérence parfaite et quasi mathématique. Lui qui est accusé d’impiété ne peut, à l’instant critique, demander aux juges de commettre une vraie impiété (le parjure) pour le sauver. Ce serait confirmer dans les faits, activement, l’accusation qu’il réfute en paroles. Il préfère la mort à cet abaissement, qui serait en outre, non plus imaginairement mais réellement, une atteinte aux dieux de la cité. Par ce refus, Socrate démontre par les actes ce qu’il prétendait par les mots : il est le véritable pieux, et ce sont les accusateurs qui, en voulant la mort d’un juste, pratiquent la vraie impiété. Le premier discours s’achève là. Les juges votent. Socrate est déclaré coupable. La majorité est étroite : si trente voix s’étaient portées sur l’autre bord, Socrate aurait été acquitté. Pour un jury de 501, cela suggère un vote d’environ 280 contre 220 (chiffres que Diogène Laërce confirme dans son récit<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 41.</ref>, mais qui ne sont pas dans Platon). Ce qui est frappant, c’est la relative faiblesse de la condamnation : l’acquittement était à portée. === Le second discours : la contre-peine (35e-38b) === La procédure athénienne exige maintenant que Socrate propose une peine alternative à celle réclamée par l’accusation. Mélétos a proposé la mort. L’usage voulait qu’on proposât une peine sensiblement moins sévère (un lourd exil, une amende importante) pour donner au jury une alternative crédible. Le calcul tacite, dans les procès à peine à estimer, consistait à offrir une sanction à peine en deçà de celle demandée par l’accusateur, de manière à ne pas trop décevoir les attentes du jury tout en se ménageant un sort moins dur. Socrate va adopter une tout autre stratégie. ==== La contingence du vote (35e-36b) ==== Socrate remarque d’abord, avec une ironie qui frise la provocation, qu’il est étonné non pas d’avoir été condamné, mais de l’avoir été à une si faible majorité. Il s’attendait à une condamnation bien plus nette. Si trente voix de plus avaient basculé, il aurait été acquitté. Il observe aussi que, ce qui le perd véritablement, ce n’est pas Mélétos : car sans l’appui d’Anytos et de Lycon, le seul Mélétos, compte tenu des voix obtenues, aurait dû payer mille drachmes d’amende pour n’avoir pas recueilli le cinquième des suffrages (règle destinée à décourager les plaintes frivoles). En divisant malicieusement les voix reçues entre ses trois accusateurs, Socrate montre que Mélétos seul n’aurait pas obtenu sa condamnation. Cette remarque, en apparence technique, est profondément déstabilisante : elle montre que la procédure qui vient de condamner Socrate est elle-même contingente, dépendante du nombre d’accusateurs autant que du fond du dossier. Elle suggère, au passage, que la véritable force du parti de l’accusation réside dans Anytos, l’homme politique, non dans Mélétos, le plaignant nominal. Le procès apparaît donc comme un montage politique, sous un habillage religieux. ==== La proposition du Prytanée (36b-37a) ==== Quelle contre-peine proposer ? Socrate prend la question au sérieux, mais dans un sens retourné : quelle peine mérité-je ? Il rappelle toute sa vie : avoir négligé les affaires, l’argent, les magistratures, les assemblées et les honneurs, pour se consacrer au service privé de la vertu, <blockquote>en essayant de convaincre chacun d’entre vous de ne pas se préoccuper de ses affaires personnelles avant de se préoccuper, pour lui-même, de la façon de devenir le meilleur et le plus sensé possible. (36c)</blockquote> Que mérite un homme ainsi ? Un bon traitement, dit-il, et non une peine. Il propose donc non pas un châtiment, mais une récompense : être nourri aux frais de l’État au Prytanée. Le Prytanée était à Athènes l’édifice public où la cité nourrissait, aux frais de l’État, les prytanes en exercice, les hôtes officiels et les citoyens illustres, notamment les vainqueurs des jeux Olympiques et les bienfaiteurs de la patrie. C’était la plus haute distinction civique, l’équivalent d’une reconnaissance par l’État comme héros ou sauveur de la cité. Socrate explique qu’il la mérite plus que quiconque : <blockquote>si celui-ci [le vainqueur olympique] vous procure l’apparence du bonheur, je vous en offre, moi, la réalité ; lui n’a aucun besoin d’être nourri, mais moi, j’en ai besoin. (36d-e)</blockquote> L’argument est double : Socrate est un bienfaiteur réel (plus que le champion olympique, dont la gloire n’est que sportive) et il est pauvre (donc il a besoin de ce soutien alimentaire, alors que le champion en a moins besoin). Cette proposition est manifestement provocatrice, et aucun commentateur n’en doute. Socrate ne joue plus le jeu de la procédure ; il retourne le tribunal. Pour un jury qui vient de le condamner, proposer d’être traité en héros civique est une humiliation délibérée. Comment comprendre cette audace ? Plusieurs explications se combinent. D’abord, une cohérence logique : Socrate est convaincu de n’avoir commis aucune injustice, il refuse donc de s’infliger une peine comme s’il était coupable. Ensuite, une fidélité à sa parole : s’il proposait une peine qu’il estime injuste, il trahirait son principe d’agir toujours selon le juste. Enfin, peut-être, une acceptation anticipée de la mort : il est âgé (soixante-dix ans, espérance de vie largement dépassée dans l’Antiquité), il a accompli sa mission, il ne craint pas la sentence, il n’a donc aucune raison de ruser. À quoi s’ajoute, plus subtilement, un calcul dramatique : proposer une vraie contre-peine reviendrait à reconnaître la compétence du tribunal ; en proposant une récompense, Socrate refuse symboliquement la sentence avant même qu’elle ne tombe. ==== L’examen des autres peines et la proposition d’amende (37a-38b) ==== Socrate continue son raisonnement avec une rigueur didactique : puisque je sais que je ne me cause volontairement de tort à personne, je ne vais pas, loin de là, m’en causer à moi-même en proposant une peine qui serait un tort. Il passe alors en revue, méthodiquement, les peines possibles. La prison ? Ce serait passer sa vie soumis au pouvoir des Onze, les magistrats qui administraient les prisons et les exécutions, donc à une sujétion indigne. Une amende lourde assortie de contrainte par corps jusqu’au paiement ? Cela revient au même : il n’a pas d’argent. L’exil ? C’est ici que Socrate développe sa réponse la plus fine. Il serait absurde, dit-il, de choisir l’exil : si ses propres concitoyens ne supportent plus ses entretiens, au point d’en vouloir « se débarrasser », pourquoi les étrangers les supporteraient-ils davantage ? Il serait chassé de ville en ville. Et ne pourrait-il vivre en se taisant ? Non, et ici vient l’un des sommets du texte : cesser de philosopher équivaudrait à désobéir au dieu. <blockquote>Il n’y a pas pour un homme de plus grand bien que de s’entretenir chaque jour de la vertu et des autres sujets dont vous m’entendez discuter, en examinant moi-même les autres ; car une vie sans examen n’est pas digne d’être vécue par un homme. (38a)</blockquote> Cette dernière formule, en grec ''ho anéxetastos bíos ou biōtós anthrṓpōi'', est probablement la plus célèbre de toute l’''Apologie'' et peut-être de toute la philosophie antique. Elle condense le programme de la philosophie socratique : la vie doit être soumise à un examen (''exétasis'') constant, à une interrogation rationnelle sur ses buts et ses valeurs. Sans cet examen, on ne vit pas une vie proprement humaine. Formule vertigineuse, qui fait de la philosophie non pas un luxe intellectuel mais la condition même d’une existence digne de ce nom. Elle suggère qu’il existe un seuil d’humanité : en deçà de l’examen, l’homme n’est pas pleinement homme. La philosophie cesse alors d’être une option ajoutée à la vie pour devenir l’élément qui en fait une vie humaine. Finalement, Socrate concède une mine d’argent (somme modique, correspondant à peu près à trois mois de salaire d’un ouvrier qualifié), mais accepte, sur la pression de ses amis (Platon, Criton, Critobule, Apollodore), de proposer trente mines, avec leur caution personnelle. C’est une somme importante, équivalent à plusieurs années de salaire, mais manifestement insuffisante face à la proposition de mort, et la manière dont elle est introduite (sous la pression des amis, comme en dernier recours) ne masque pas que Socrate lui-même n’y adhère pas vraiment. Le jury vote une seconde fois. Cette fois, la majorité est beaucoup plus nette : selon les chiffres traditionnels rapportés par Diogène Laërce, quatre-vingts jurés supplémentaires ont voté contre Socrate par rapport au premier vote, manifestement indignés par son attitude. Socrate est condamné à mort. === Le troisième discours : après la condamnation (38c-42a) === Ce troisième discours n’est pas prévu par la procédure. Une fois la sentence prononcée, les magistrats passent aux formalités d’enregistrement, notamment la notification aux Onze, chefs des geôliers et du bourreau. Socrate prend pourtant la parole une dernière fois, sans doute pendant que les Onze procèdent à ces écritures, pour s’adresser à la partie de l’assistance qui est restée sur place. Il s’agit d’une péroraison spontanée, hors procédure, probablement élargie et composée par Platon pour les besoins du texte, même si l’événement lui-même est plausible. Ce discours se divise en deux moments : d’abord aux jurés qui ont voté sa mort, puis à ceux qui ont voté son acquittement. ==== Aux jurés de la condamnation (38c-39d) ==== Socrate commence par un constat ironique : les Athéniens ont gagné peu de temps, il est âgé, il serait mort bientôt de toute façon. Mais en échange, ils vont gagner <blockquote>le renom, auprès des gens avides de diffamer notre cité, d’avoir fait mourir un sage en la personne de Socrate. (38c)</blockquote> La réputation d’Athènes souffrira de ce procès bien au-delà de ce qu’elle a cru gagner. La prédiction est parfaitement exacte : la condamnation de Socrate a, dans la mémoire collective de l’Occident, sérieusement terni l’image de la démocratie athénienne. Surtout, Socrate retourne contre les juges l’argument central de son plaidoyer : s’il n’a pas réussi à les persuader, ce n’est pas faute d’arguments, mais parce qu’il n’a pas voulu employer les tactiques indignes (larmes, supplications) qu’ils attendaient. <blockquote>Je préfère de beaucoup mourir après m’être défendu comme je l’ai fait plutôt que vivre après un plaidoyer à leur façon. (38e)</blockquote> Et il prononce cette antithèse fameuse : <blockquote>la difficulté n’est pas d’échapper à la mort, elle est bien plus d’échapper à la lâcheté (''ponēría''), car elle court plus vite que la mort. En l’occurrence, moi qui suis lent et vieux, j’ai été rattrapé par la plus lente des deux [la mort], cependant que mes accusateurs, qui sont lestes et rapides, ont été rattrapés par la plus rapide, qui est la méchanceté. (39a-b)</blockquote> L’image est saisissante : chaque homme est poursuivi par son destin, mais les uns sont rattrapés par la mort physique et les autres par la mort morale, infiniment plus grave. Socrate se permet alors une prophétie (''manteúomai''). Il est, dit-il, <blockquote>au point où les hommes prophétisent le mieux, quand ils sont à la veille de mourir, (39c)</blockquote> allusion à la croyance grecque selon laquelle les mourants acquièrent un don de divination (voir le motif du chant du cygne dans le ''Phédon'' 84e<ref>Platon, ''Phédon'', 84e-85b : « les cygnes [...], lorsqu’ils sentent qu’ils vont mourir, chantent ce jour-là plus fort et plus beau qu’ils n’ont jamais chanté, dans la joie d’aller trouver le dieu dont ils sont les serviteurs ».</ref>). Sa prophétie est terrible : <blockquote>un châtiment vous viendra aussitôt après ma mort, bien plus pénible que celui par lequel vous m’aurez tué. [...] Le nombre croîtra de ceux qui vous demanderont des comptes, que je retenais jusqu’ici, sans que vous vous en aperceviez ; et ils seront d’autant plus pénibles qu’ils sont plus jeunes. (39c-d)</blockquote> Tuer n’est pas la façon de se délivrer du blâme ; la seule manière honorable est « de se préparer soi-même à être le meilleur possible ». Cette prophétie s’accomplira de manière surprenante par la suite : les « petits socratiques » (Antisthène le cynique, Aristippe, Euclide de Mégare), puis Platon et son Académie, puis Aristote et le Lycée, poursuivront inlassablement l’interrogation commencée par Socrate. La philosophie comme vocation publique est née du procès. ==== Aux jurés de l’acquittement (39e-42a) ==== Socrate se tourne alors vers ceux qui ont voté pour lui, qu’il appelle désormais, seuls, ''juges'' véritables. Cette distinction terminologique est capitale : avant le verdict, tous étaient indistinctement ''andres'' (« messieurs ») ou ''Athēnaîoi'' (« Athéniens ») ; maintenant, seuls ceux qui ont voté juste méritent, selon Socrate, le titre de juges (''dikastaí''). Le tribunal vient d’être scindé en deux catégories asymétriques. Il leur confie deux pensées, l’une sur le signe divin, l’autre sur la mort. ===== Le silence du démon (39e-40c) ===== Son démon, dit-il, avait coutume de l’arrêter chaque fois qu’il s’apprêtait à faire quelque chose de mauvais, même dans des circonstances mineures. Or, aujourd’hui, depuis le matin, tout au long de cette journée qui l’a mené à la condamnation à mort, le démon ne s’est pas manifesté une seule fois. Il ne l’a pas arrêté au moment où il quittait son domicile, ni lorsqu’il montait au tribunal, ni à aucun moment de son plaidoyer. Cette absence est elle-même un signe : <blockquote>il y a des chances pour que ce qui m’est arrivé soit un bien ; et c’est nous qui faisons des suppositions incorrectes quand nous considérons la mort comme un mal. (40b-c)</blockquote> Le silence du démon est la preuve, pour Socrate, que la voie qu’il suit est la bonne. L’argument est philosophiquement subtil. Socrate ne dit pas qu’il sait que la mort est un bien ; il dit qu’il a une raison de penser que ce qui lui arrive est un bien, puisque le dieu (par la voix du démon) ne l’a pas arrêté. C’est un argument ''e silentio'' transposé sur le plan religieux : le silence divin, pour qui est habitué à être averti, vaut approbation. Cette structure de raisonnement est fragile, mais elle est cohérente avec la théologie socratique : la divinité se manifeste par ses interventions plutôt que par ses paroles positives ; son silence, quand il y a habitude d’intervention, est significatif. ===== Les deux hypothèses sur la mort (40c-41d) ===== Socrate propose alors une méditation sur la mort, l’une des plus belles pages de la philosophie antique, construite comme une alternative raisonnée. De deux choses l’une : ou bien la mort est l’absence de toute sensation, ou bien elle est une migration (''metoíkēsis'') de l’âme de ce lieu vers un autre lieu. Dans la première hypothèse (la mort comme absence de sensation), la mort ressemble au sommeil sans rêve. Or qui n’aimerait pas, si on lui demandait de choisir entre une nuit de sommeil profond sans rêves et toutes les autres nuits et jours de sa vie, reconnaître que cette nuit est plus précieuse que la plupart ? Même le Grand Roi de Perse (homme réputé le plus heureux du monde aux yeux des Grecs) trouverait peu de jours comparables à une telle nuit. Si la mort est cela, alors la totalité du temps après la mort se réduit à « une seule nuit », et cette nuit est un gain. L’argument est intéressant par sa structure : il part d’une expérience commune (le sommeil sans rêve) pour désamorcer la peur métaphysique de la mort. Si l’on aime le sommeil quand il nous prend, pourquoi craindre la mort si elle lui ressemble ? La stratégie argumentative, qui rappelle celle d’Épicure et de Lucrèce plus tard (« la mort n’est rien pour nous »<ref>Épicure, ''Lettre à Ménécée'', § 125 : « la mort n’est rien pour nous, puisque tant que nous sommes, la mort n’est pas, et quand la mort est là, nous ne sommes plus ». Lucrèce, ''De la nature des choses'', III, v. 830 et suiv.</ref>), désarticule la crainte en la confrontant à ce que nous expérimentons quotidiennement. Dans la seconde hypothèse (la mort comme migration), la mort est un voyage vers l’au-delà, où l’on retrouve tous les morts. Que pourrait-on imaginer de plus heureux ? On serait délivré des juges qui prétendent juger ici-bas, pour rencontrer les vrais juges dont on dit qu’ils y rendent la justice : Minos, Rhadamante, Éaque (les trois juges infernaux traditionnels), plus Triptolème (qui les remplace parfois dans l’iconographie attique). On rencontrerait Orphée, Musée, Hésiode, Homère, les grandes figures poétiques et religieuses du passé. On pourrait y continuer, sans cette fois risquer la mort, l’activité d’examen qui fut la sienne sur terre : interroger Palamède, Ajax (tous deux morts par jugements injustes, comme lui-même : la comparaison est évidemment à son avantage), le chef de l’armée grecque à Troie (Agamemnon), Ulysse, Sisyphe, « et tant d’autres hommes et femmes qu’on pourrait nommer ». <blockquote>Discuter avec ceux de là-bas, vivre en leur société, les soumettre à examen, ne serait-ce pas le comble du bonheur ? Aussi bien, les gens de là-bas ne mettent à mort personne pour ce motif. (41c)</blockquote> Ce passage est riche de tonalités. Il y a une évidente dimension humoristique : Socrate imagine l’au-delà comme la continuation indéfinie de son activité terrestre, l’examen dialectique, mais cette fois sans risque, puisqu’on n’y meurt plus. L’Hadès devient une agora élargie à tous les temps. Il y a également une dimension consolatoire : la comparaison avec Palamède et Ajax, héros victimes de procès injustes, ennoblit le sort de Socrate. Il y a enfin une dimension ironique vis-à-vis des jurés athéniens : les vrais juges ne sont pas à Athènes, mais dans l’Hadès, et Socrate se réjouit d’aller les retrouver. Ce passage a suscité des interprétations contrastées. Certains commentateurs y voient une réelle espérance platonicienne en l’immortalité de l’âme, telle qu’elle sera développée dans le ''Phédon''<ref>Platon, ''Phédon'', 80a-84b, 105c-107a.</ref>. D’autres, comme Chrétien, soulignent que le Socrate de l’''Apologie'' reste fondamentalement agnostique : il présente deux hypothèses, il ne tranche pas entre elles, et l’imagination y a au moins autant de part que la raison<ref>Chrétien, ''op. cit.'', p. 32-36.</ref>. Socrate lui-même conclut prudemment : <blockquote>aucun mal ne peut toucher un homme de bien ni pendant sa vie ni après sa mort, et les dieux ne se désintéressent pas de son sort. (41d)</blockquote> Cette conclusion n’affirme pas dogmatiquement une survie, mais énonce une foi pratique : quoi qu’il arrive, le juste n’a rien à craindre. L’''Apologie'', contrairement au ''Phédon'', ne construit pas de doctrine positive sur l’immortalité ; elle se tient au seuil d’une telle doctrine, dans un agnosticisme serein. ===== Le testament (41e-42a) ===== Socrate clôt son discours par un testament pour ses fils. Il ne demande qu’une chose aux Athéniens : <blockquote>Quand mes fils seront grands, punissez-les, citoyens, en les tourmentant comme je vous tourmentais, pour peu qu’ils vous paraissent se soucier d’argent ou de n’importe quoi d’autre plus que de la vertu. Et, s’ils croient être quelque chose, alors qu’ils ne sont rien, adressez-leur le reproche que je vous adressais. (41e)</blockquote> La mission philosophique se transmet ainsi, comme un héritage inversé : Socrate demande à ses bourreaux de devenir eux-mêmes, envers ses enfants, ce qu’il était pour eux, des tourmenteurs par la vertu. C’est le pardon actif d’un homme qui refuse de laisser sa mort briser la chaîne de l’examen. Puis vient la dernière phrase, l’une des plus célèbres de la littérature philosophique : <blockquote>Mais voici déjà l’heure de partir, moi pour mourir et vous pour vivre. De mon sort ou du vôtre lequel est le meilleur ? La réponse reste incertaine pour tout le monde, sauf pour la divinité. (''plḕn hē tôi theôi'', 42a)</blockquote> Cette clôture ouvre, sur l’indécidable, l’agnosticisme ultime. Socrate ne sait pas, nul ne sait, lequel est le plus heureux, de lui qui va mourir ou de ses juges qui vont continuer à vivre. Seule la divinité le sait. L’''Apologie'' se ferme ainsi sur le mot même de la sagesse socratique : la reconnaissance de l’ignorance humaine, couplée à la confiance paisible en un ordre divin qui excède notre mesure. Rien n’est affirmé dogmatiquement ; tout se termine sur une interrogation qui n’attend pas de réponse humaine. C’est une fin d’une sobriété admirable, qui évite à la fois la plainte et la consolation artificielle. Elle est, par son rythme et par son contenu, digne d’une page d’évangile ou d’un chapitre des ''Pensées'' de Marc-Aurèle. == Concepts et thèmes majeurs == Une lecture suivie ne serait pas complète sans reprendre quelques-uns des grands thèmes qui courent à travers le texte et en font la portée philosophique durable. === La sagesse humaine : le savoir du non-savoir === Le concept central de l’''Apologie'' est celui de la sagesse humaine (''anthrōpínē sophía'', 20d). Socrate n’est pas savant au sens fort du terme, c’est-à-dire à la manière des sophistes qui prétendaient posséder un savoir sur les choses divines, sur la nature, sur la politique, sur la vertu. Mais il possède une sagesse proprement humaine, qui consiste à reconnaître que l’on ne sait pas. Ce savoir du non-savoir n’est pas un scepticisme, encore moins un renoncement. C’est une position éthique : celui qui sait qu’il ne sait pas est disposé à chercher, à interroger, à examiner ; celui qui croit savoir est fermé à toute remise en question et, par là même, incapable de tout progrès. Cette sagesse n’est pourtant pas purement négative. Socrate affirme savoir au moins deux choses : qu’il est mauvais et laid de commettre l’injustice et de désobéir à un meilleur que soi (29b), et que la vie non examinée n’est pas digne d’être vécue (38a). De ces deux « savoirs » découle tout le comportement de Socrate au procès : il ne peut trahir la justice pour sauver sa vie, et il ne peut cesser d’examiner les autres sans trahir sa vocation propre. On voit donc que le savoir du non-savoir n’est pas une position d’ignorance totale, mais une structure articulée : une ignorance avouée sur les grandes questions métaphysiques, et une certitude pratique sur les exigences éthiques. Cette combinaison fait du socratisme une forme de philosophie pratique : elle rend possible l’action juste sans requérir une science achevée. Il faut enfin noter que le savoir du non-savoir est peut-être la thèse la plus féconde du socratisme pour l’histoire de la pensée. Toute la philosophie postérieure, chaque fois qu’elle commence par un doute méthodique (Descartes), par une critique des prétentions de la raison (Kant), par une déconstruction des évidences (phénoménologie), s’inscrit dans le sillage de l’interrogation socratique. La philosophie moderne est, en ce sens, socratique par son geste inaugural, même quand elle ne l’est plus par ses conclusions. === La méthode de l’elenchus === L’''Apologie'' donne à voir, en acte, la méthode philosophique propre de Socrate : l’elenchus ou réfutation par interrogation. Elle apparaît à plusieurs reprises, mais surtout dans l’interrogatoire de Mélétos (24b-28a). Son fonctionnement est simple dans son principe : Socrate ne pose pas directement ses propres thèses ; il prend pour point de départ celles de son interlocuteur, puis, par une série de questions dont chacune requiert une réponse qui semble évidente, il conduit l’interlocuteur à reconnaître des conséquences incompatibles avec d’autres thèses qu’il tient également pour vraies. La contradiction ainsi mise au jour n’est pas celle de Socrate, mais celle de l’interlocuteur avec lui-même. Cette méthode a plusieurs vertus philosophiques. D’abord, elle respecte la liberté de l’interlocuteur : Socrate ne lui impose rien, il l’amène seulement à voir ce qu’il pensait déjà. Ensuite, elle produit un savoir négatif sûr : la réfutation, à défaut de démontrer le vrai, prouve au moins que la thèse examinée est fausse. Enfin, elle a un effet moral : elle introduit chez l’interlocuteur l’expérience de l’''aporía'' (la perplexité), qui est le point de départ possible d’une recherche véritable. L’interlocuteur, déchargé de son illusion de savoir, peut commencer à apprendre. L’elenchus est ainsi, au sens propre, maïeutique : il fait accoucher les esprits. Mais l’elenchus a aussi ses limites, que l’''Apologie'' laisse apercevoir. Il peut blesser l’amour-propre ; il peut créer des haines durables ; il peut donner à ceux qui en sont la cible l’impression d’être humiliés publiquement. Socrate lui-même en témoigne : son enquête a suscité contre lui des rancœurs innombrables. La philosophie a un coût social, que l’elenchus rend visible avec une particulière acuité. === Le souci de l’âme === Le message moral central de l’''Apologie'' tient dans une exhortation : il faut se soucier prioritairement de son âme (''psuchḗ'') et de son amélioration, non de son corps, de sa richesse ou de sa réputation (29d-30b). <blockquote>La vertu ne naît pas de l’argent, mais c’est de la vertu que naissent et l’argent et tout le reste des biens utiles aux hommes, aussi bien privés que publics. (30b)</blockquote> Ce renversement est l’une des sources principales de la morale occidentale : la hiérarchie des biens est réordonnée autour du bien intérieur, et la richesse extérieure n’a de valeur qu’en tant qu’elle découle d’une vertu préalable. Ce souci de soi (''epiméleia heautoû'') n’est pas égoïste. Il est au contraire la condition du souci des autres : on ne peut aider autrui à améliorer son âme sans avoir travaillé à la sienne. Socrate harcèle ses concitoyens parce qu’il veut qu’ils prennent soin de leur âme, non parce qu’il veut sauver la sienne à ses dépens. Et c’est précisément parce qu’il se soucie de la cité qu’il la secoue : le taon pique le cheval pour le réveiller, non pour le faire souffrir. Cette thèse a eu une postérité immense. Elle est reprise, transformée, intériorisée par les écoles hellénistiques (stoïciens, épicuriens), qui en font le cœur de la sagesse pratique. Elle passe ensuite dans le christianisme, où le « soin de l’âme » devient le salut personnel. À l’époque moderne, Michel Foucault lui a consacré une part importante de ses derniers cours, voyant dans le souci de soi une alternative à l’éthique cartésienne fondée sur la seule connaissance de soi<ref>Michel Foucault, ''Le Souci de soi'' (''Histoire de la sexualité'', t. III), Paris, Gallimard, 1984 ; et ''L’Herméneutique du sujet'', ''op. cit.''</ref>. L’''Apologie'' est à l’origine de cette longue tradition, même si le souci de soi socratique reste, par certains aspects, très différent des élaborations postérieures : il est moins un travail sur soi qu’un examen de soi par la discussion avec autrui. === La philosophie comme mission divine === L’''Apologie'' fonde la légitimité de la philosophie sur une mission divine. Socrate ne philosophe pas par goût ou par choix : il le fait parce que le dieu lui en a fait l’ordre, à travers l’oracle de Delphes et à travers le démon. Cette dimension religieuse est essentielle pour comprendre la posture socratique. S’il pouvait cesser d’interroger, il le ferait peut-être (c’est une activité ingrate et dangereuse) ; mais il ne le peut pas, car ce serait désobéir au dieu. La philosophie est ainsi un service sacré, équivalent à celui des prêtres ou des devins, mais accompli par d’autres moyens : non par le rite, mais par l’examen rationnel. Cette dimension place Socrate dans une position paradoxale par rapport aux accusations d’impiété. L’homme que l’on accuse de ne pas croire aux dieux est en réalité celui qui les sert le plus fidèlement, au point de mourir pour leur obéir. Platon retourne ainsi l’accusation : les véritables impies sont ceux qui, en condamnant Socrate, refusent le présent que le dieu leur a fait. Le procès apparaît alors sous un jour inversé : non plus un acte de piété de la cité contre un impie, mais un acte d’impiété de la cité contre un serviteur du dieu. Ce thème a eu un écho particulier dans la tradition chrétienne, qui a parfois vu en Socrate une figure prophétique du Christ : un juste mis à mort par une communauté religieuse qui croyait servir ses dieux en le tuant. Justin martyr, au IIᵉ siècle, comparera explicitement Socrate et Jésus, voyant dans le philosophe athénien une préfiguration providentielle de la Passion<ref>Justin de Naplouse, ''Première Apologie'', 46, 1-4 : les chrétiens considèrent comme chrétiens avant la lettre « ceux qui ont vécu avec le Logos [...] parmi les Grecs, Socrate, Héraclite et ceux qui leur furent semblables ».</ref>. La philosophie chrétienne primitive a ainsi trouvé dans l’''Apologie'' une matrice pour penser la martyrologie. === La justice supérieure === Le thème de la justice traverse tout le texte. Socrate se présente comme un homme profondément respectueux des lois : il a risqué sa vie pour le respect de la procédure sous la démocratie (affaire des Arginuses) ; il mourra par fidélité aux lois dans le ''Criton'' plutôt que de s’évader. Mais son obéissance aux lois n’est pas inconditionnelle : il y a une justice supérieure, fondée sur des valeurs, qui commande dans certains cas la désobéissance civile, comme lorsqu’il refuse d’exécuter les ordres des Trente concernant Léon de Salamine. Cette justice supérieure n’est pas un simple idéal abstrait ; elle est, pour Socrate, ce qui fait le prix (''axía'') de la vie humaine. Elle a une origine divine et s’impose à la conscience au-delà des conventions sociales. On voit déjà poindre ici, avant les développements platoniciens de la ''République'', l’idée d’une justice en soi, distincte de la justice légale, et qui fonde celle-ci sans s’y réduire. Antigone, chez Sophocle, invoquait déjà les « lois non écrites » des dieux contre les décrets humains<ref>Sophocle, ''Antigone'', v. 450-460.</ref> ; Socrate, à sa manière, s’inscrit dans cette tradition. Mais il la rationalise : ce n’est plus le simple respect d’une tradition familiale ou religieuse, c’est la fidélité à un ordre supérieur accessible par l’examen rationnel. La philosophie devient ainsi le lieu où se manifeste cette justice transcendante, dont les lois humaines ne sont qu’une approximation imparfaite. Cette double fidélité (aux lois de la cité et à une justice supérieure) est source d’une tension qui traversera toute la tradition philosophique et juridique occidentale. Elle est au cœur des doctrines du droit naturel<ref>Voir notamment Cicéron, ''De republica'', III, 33 sur la ''lex vera'' ; Thomas d’Aquin, ''Somme théologique'', I-II, q. 94 sur la loi naturelle.</ref>, des théories modernes de la désobéissance civile (Thoreau, Gandhi, Martin Luther King), et des débats contemporains sur la légitimité du droit positif. === La mort et le courage === Le rapport de Socrate à la mort est une pièce maîtresse du texte. Sa thèse est à double face. D’un côté, la mort en elle-même est inconnue : nul ne sait si elle est un mal ou un bien, et la craindre comme un mal certain est la plus répréhensible des ignorances. De l’autre, il y a pire que la mort : la lâcheté, l’injustice, l’abandon de son poste. Le courage socratique n’est donc pas, comme celui des héros homériques, la volonté exaltée de mourir pour l’honneur ; c’est la lucidité sur ce qui est réellement à craindre, non la mort mais le vice. Ce renversement a nourri toute la morale stoïcienne (qui en fait un de ses principes cardinaux : ne rien craindre de ce qui ne dépend pas de nous, donc pas la mort) puis, par des voies détournées, la pensée chrétienne du martyre. Il faut insister sur la finesse de l’analyse socratique. Elle ne dit pas : « la mort est un bien » (affirmation dogmatique contraire à son savoir du non-savoir). Elle ne dit pas non plus : « la mort est indifférente » (comme le diront plus tard les stoïciens). Elle dit : « la mort est inconnue, donc je ne peux la craindre comme un mal certain ; mais l’injustice est un mal certain, donc je peux la craindre ». Le courage n’est pas fondé sur une espérance métaphysique, mais sur une hiérarchie des savoirs : le connu prime sur l’inconnu, et j’organise ma conduite en fonction de ce que je sais. Cette analyse aura un long destin. Épicure la reprendra pour dire : « la mort n’est rien pour nous »<ref>Épicure, ''Lettre à Ménécée'', § 125.</ref>. Les stoïciens la transformeront en doctrine de l’indifférence aux choses externes. Montaigne en fera un objet central de ses ''Essais''<ref>Montaigne, ''Essais'', I, 20 « Que philosopher, c’est apprendre à mourir » ; III, 12 « De la physionomie ».</ref>. Heidegger, au XXᵉ siècle, retournera au Socrate de l’''Apologie'' en interrogeant le rapport authentique à la mort comme condition d’une existence propre<ref>Martin Heidegger, ''Sein und Zeit'' (1927), § 46-53, sur l’''être-pour-la-mort''.</ref>. Chaque fois, c’est le même geste inaugural qui est repris, celui d’une mort désarmée par la pensée. === Philosophie et cité : l’autre politique === L’''Apologie'' esquisse une conception originale de la politique. Socrate se déclare non-politique au sens courant du terme : il n’a pas fréquenté l’assemblée, il n’a pas cherché les magistratures, il n’a pas fait carrière publique. Mais il se présente comme le plus politique des Athéniens au sens profond : il s’est occupé de la cité elle-même (plus que de ses affaires), en se souciant du perfectionnement de ses concitoyens. C’est une autre politique, qui ne passe pas par les institutions officielles (corrompues selon lui par la démagogie et l’ignorance) mais par la conversation privée, par l’interpellation personnelle, par la formation morale. Cette vision a un versant critique radical vis-à-vis de la démocratie athénienne, qui émerge des épisodes des Arginuses et de Léon de Salamine : la démocratie, livrée à ses passions, peut se conduire de manière aussi injuste qu’une tyrannie. Il serait naïf de voir en Socrate un démocrate simple ou un anti-démocrate simple ; il est un critique des deux régimes en tant qu’ils s’éloignent de la justice. Mais sa critique vise, au-delà des régimes, la capacité même des collectivités humaines à délibérer justement : « il n’y a personne au monde qui puisse garder la vie sauve s’il s’oppose loyalement à vous ou à toute autre collectivité » (32a). Ce diagnostic, désabusé, n’est pas tant politique qu’anthropologique : les foules, quelles qu’elles soient, résistent mal à l’examen rationnel. Mais cette critique a aussi un versant constructif. La philosophie, par l’examen qu’elle exerce sur les esprits, prépare la possibilité d’une politique véritablement juste. Platon développera cette intuition dans la ''République'', en allant jusqu’à imaginer une cité où les philosophes seraient rois, mais ce développement excède le cadre de l’''Apologie''. Au moment où Platon écrit ce texte, la cité idéale n’est pas encore pensée ; il y a seulement la dénonciation d’une cité qui a tué son meilleur citoyen, et la promesse implicite d’une pensée qui prolongera la mission interrompue. === L’ironie socratique === Un mot enfin sur l’ironie, qui est omniprésente dans le texte, et qu’il faut distinguer en plusieurs registres. Il y a d’abord l’ironie au sens étroit : dire le contraire de ce que l’on pense, comme lorsque Socrate qualifie Mélétos de « bon citoyen » et « patriote ». Il y a ensuite l’ironie dialectique : amener l’interlocuteur à se contredire lui-même par des questions prétendument naïves, alors que Socrate sait parfaitement où il le mène. Il y a enfin l’ironie existentielle : vivre de telle façon que toute son existence est un démenti de ce qu’on attendrait d’un homme dans sa situation. La proposition d’être nourri au Prytanée relève de cette dernière : Socrate, condamné, se présente comme un bienfaiteur ; il retourne les rôles, fait du tribunal une scène tragicomique. Cette ironie n’est pas seulement un trait de style. Elle est l’expression d’une distance philosophique à l’égard des conventions et des évidences. Celui qui a compris que le savoir commun est illusoire, que la rhétorique est trompeuse, que les hiérarchies sociales reposent sur des malentendus, ne peut plus prendre au sérieux les rituels qui ordonnent la vie ordinaire. L’ironie socratique est le signe visible d’une conscience libre, qui refuse de se soumettre aux attentes. C’est pourquoi Søren Kierkegaard, au XIXᵉ siècle, en a fait dans sa thèse sur ''Le Concept d’ironie'' le trait caractéristique de la subjectivité philosophique naissante : avec Socrate, la conscience se sépare du monde, s’intériorise, devient sujet<ref name="kierkegaard">Søren Kierkegaard, ''Le Concept d’ironie constamment rapporté à Socrate'' (''Om Begrebet Ironi'', 1841), trad. P.-H. Tisseau et E.-M. Jacquet-Tisseau, dans ''Œuvres complètes'', t. II, Paris, Éditions de l’Orante, 1975.</ref>. L’ironie est ce mode de la subjectivité qui se pose en se distinguant de ce qui est. == Questions de lecture et postérité == === Le Socrate de l’''Apologie'' et le Socrate historique === Une question classique est de savoir dans quelle mesure l’''Apologie'' restitue fidèlement le plaidoyer effectivement prononcé par Socrate en 399. Les éléments de réponse sont partiels. D’une part, Platon, jeune témoin du procès (il avait environ vingt-huit ans), avait de puissantes raisons d’être fidèle : la mémoire des jurés qui liraient le texte était fraîche, et toute infidélité flagrante aurait nui à la thèse défensive. D’autre part, Xénophon, dans sa propre ''Apologie'', confirme certains points centraux (l’oracle de Delphes, le refus de préparer sa défense, le rôle du démon, l’attitude provocante devant le tribunal). Les convergences entre Platon et Xénophon, qui écrivent séparément, garantissent la réalité d’un noyau historique. Pour autant, l’''Apologie'' de Platon est une œuvre écrite, composée probablement plusieurs années après le procès, et qui obéit aux lois de la composition littéraire. La structure en trois discours parfaitement articulée, la densité dialectique de l’interrogatoire de Mélétos, la beauté rythmique de certaines périodes (la finale : « moi pour mourir et vous pour vivre… ») relèvent de l’art platonicien. Le témoignage historique et la recréation littéraire ne sont pas dissociables. La question du « Socrate historique » a été débattue à l’infini. On distingue traditionnellement plusieurs Socrate : celui d’Aristophane (caricatural), celui de Xénophon (pragmatique, moraliste de bon sens), celui de Platon (dialectique et idéaliste), celui d’Aristote (prédécesseur des idées, attribuant à Socrate la recherche des définitions universelles et l’induction<ref>Aristote, ''Métaphysique'', XIII, 4, 1078b17-30 : « deux choses peuvent à bon droit être attribuées à Socrate : les raisonnements inductifs et la définition universelle ».</ref>). Lequel est le vrai ? La réponse la plus raisonnable est qu’aucun ne l’est entièrement, mais qu’ils donnent, collectivement, une image composite d’un homme dont la puissance personnelle a dépassé de beaucoup ce que les documents peuvent restituer. L’''Apologie'' est probablement, de tous les textes, celui qui se tient le plus près de la voix réellement entendue, parce que Platon y a choisi, exceptionnellement, de s’effacer devant son maître. Il est usuel, chez les commentateurs, de distinguer dans l’''Apologie'' des couches. Certains éléments semblent historiques au plus haut degré : le cadre procédural, les noms des accusateurs, l’attitude refusant la supplication, la référence à Chéréphon (mort avant le procès, mais dont les héritiers étaient vivants pour confirmer ou démentir), la condamnation et son déroulement. D’autres éléments sont vraisemblablement platoniciens : l’articulation en trois discours parfaitement équilibrés, la méditation finale sur la mort, peut-être la prophétie adressée aux juges condamnateurs. Mais tout cela relève d’appréciations délicates, et Platon lui-même dans la ''Lettre VII'' revendique le droit de penser philosophiquement à partir du Socrate qu’il a connu<ref>Platon, ''Lettre VII'', 324d-326b. Sur la question socratique, voir Louis-André Dorion, ''Socrate'', Paris, PUF, coll. « Que sais-je ? », 2004.</ref>. === La postérité de l’''Apologie'' === L’''Apologie de Socrate'' est l’un des textes les plus lus, les plus imités, les plus commentés de la philosophie occidentale. Sa postérité ne se laisse pas résumer en quelques lignes, mais on peut en indiquer quelques étapes majeures. Dans l’Antiquité, l’''Apologie'' est immédiatement imitée : Xénophon en donne sa version ; des apologies perdues, d’Eschine de Sphettos ou de Lysias, avaient également circulé. Au IIᵉ siècle, Apulée compose une ''Apologie'' (sur son propre procès en magie) qui imite la structure platonicienne<ref>Apulée, ''Apologie ou De la magie'', texte établi et traduit par Paul Vallette, Paris, Les Belles Lettres, 1924.</ref>. Les écoles hellénistiques (stoïciens, cyniques) prennent Socrate comme figure tutélaire du sage qui meurt pour sa vérité : Épictète et Marc Aurèle se réfèrent constamment à lui<ref>Voir notamment Épictète, ''Entretiens'', II, 1, 32 ; II, 2, 8-20 ; et Marc Aurèle, ''Pensées'', VII, 19 ; XI, 25, 28.</ref>. Les cyniques voient en lui le philosophe de la pauvreté et de la provocation, exemple d’une existence libre des conventions. La tradition chrétienne primitive trouve dans Socrate une figure prophétique. Justin martyr, au IIᵉ siècle, fait de Socrate un « chrétien avant le Christ », guidé par le Logos divin<ref>Justin, ''Première Apologie'', 46 ; ''Seconde Apologie'', 10.</ref>. Les Pères de l’Église le mentionnent souvent, tantôt pour s’en réclamer (Clément d’Alexandrie, Origène), tantôt pour le mettre à distance (Tertullien)<ref>Clément d’Alexandrie, ''Stromates'', I, 14 ; Tertullien, ''Apologétique'', 46.</ref>. La mort de Socrate, rapprochée du martyre, fournit un modèle de témoignage pour la vérité. Toute la hagiographie martyrologique se nourrit, en partie, de l’''Apologie''. À la Renaissance, la redécouverte de Platon par Marsile Ficin et l’Académie florentine met l’''Apologie'' au centre du canon philosophique<ref>Marsile Ficin traduit les œuvres complètes de Platon en latin (''Platonis Opera Omnia'', Florence, 1484), rendant l’''Apologie'' accessible à l’Europe savante.</ref>. Montaigne, dans les ''Essais'', lui consacre plusieurs chapitres et voit en Socrate la figure même de la sagesse sans science, du bon sens humain qui vaut mieux que toutes les spéculations. <blockquote>Socrate fait mouvoir son âme d’un mouvement naturel et commun. Ainsi dit un paysan, ainsi dit une femme. (Montaigne, ''Essais'', III, 12)<ref>Montaigne, ''Essais'', III, 12 « De la physionomie », édition Villey-Saulnier, PUF, 1965, p. 1037-1038.</ref></blockquote> À l’époque des Lumières, l’''Apologie'' devient un manifeste anticlérical. Voltaire y voit le procès de l’intolérance religieuse, Diderot une défense de la libre pensée, Rousseau un modèle d’éthique civile. David peint ''La Mort de Socrate'' (1787)<ref>Jacques-Louis David, ''La Mort de Socrate'', 1787, huile sur toile, 129,5 × 196,2 cm, New York, Metropolitan Museum of Art.</ref>, tableau emblématique où le philosophe saisit la coupe avec sérénité tandis que ses disciples se lamentent : image iconique qui influencera durablement l’imaginaire philosophique. Au XIXᵉ siècle, Hegel, Kierkegaard et Nietzsche proposent trois lectures marquantes. Hegel, dans ses ''Leçons sur l’histoire de la philosophie'', voit Socrate comme le moment où la conscience morale s’intériorise, et dans sa condamnation le conflit tragique entre l’ancienne cité et la subjectivité naissante<ref>G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', t. II, trad. P. Garniron, Paris, Vrin, 1971, p. 263-328.</ref>. Kierkegaard fait de l’ironie socratique, analysée dans ''Le Concept d’ironie'' (1841), le modèle de la subjectivité existentielle<ref name="kierkegaard" />. Nietzsche, dans ''La Naissance de la tragédie'' (1872), voit au contraire en Socrate le destructeur du tragique grec, l’homme théorique qui substitue la raison critique à la sagesse instinctive, et accomplit ainsi une « monstruosité par défaut »<ref name="nietzsche-nt" />. Les trois lectures sont opposées, mais elles attestent toutes la centralité de Socrate dans la pensée moderne. Au XXᵉ siècle, l’''Apologie'' continue d’être un lieu de lecture privilégié. Hannah Arendt, dans ''La Vie de l’esprit'', en fait le modèle de la pensée responsable<ref>Hannah Arendt, ''La Vie de l’esprit'', trad. L. Lotringer, Paris, PUF, 1981 ; voir aussi « Philosophie et politique », ''Les Cahiers de philosophie'', n° 4, 1987.</ref>. Michel Foucault, dans ses derniers cours au Collège de France (''Le Courage de la vérité'', 1984), y voit le texte fondateur de la ''parrhēsía'', c’est-à-dire du « franc-parler » philosophique qui engage la vie de celui qui parle<ref>Michel Foucault, ''Le Courage de la vérité. Le Gouvernement de soi et des autres II. Cours au Collège de France, 1983-1984'', éd. F. Gros, Paris, Gallimard/Seuil, 2009.</ref>. Leo Strauss et son école ont proposé une relecture « ésotérique » du texte, attentive à ce que Socrate ne dit pas et à ce qu’il suggère entre les lignes<ref>Leo Strauss, ''Studies in Platonic Political Philosophy'', Chicago, University of Chicago Press, 1983 ; ''The City and Man'', Chicago, Rand McNally, 1964.</ref>. Lire l’''Apologie'' aujourd’hui, c’est donc entrer dans un texte sédimenté par vingt-quatre siècles de commentaires, et qui n’a pas encore épuisé sa charge philosophique. Chaque époque y trouve un Socrate à sa mesure, et c’est peut-être la marque des grandes œuvres de pouvoir soutenir cette pluralité indéfinie d’interprétations. == Conclusion == L’''Apologie de Socrate'' n’est pas un plaidoyer ordinaire. Ce n’est même pas, à proprement parler, un plaidoyer au sens technique, puisque Socrate y refuse presque toutes les tactiques qui auraient pu le faire acquitter : il ne se fait pas écrire par un logographe, il ne supplie pas, il ne fait pas paraître ses enfants, il provoque le jury quand il lui faudrait le ménager. C’est une profession de foi philosophique, prononcée au moment où la vie de l’auteur est en jeu, et qui tire précisément de cet enjeu sa gravité unique. L’''Apologie'' est la preuve par la mort d’une thèse que Socrate n’a cessé de soutenir par les mots : la vertu vaut plus que la vie. Trois traits en font un texte fondateur. D’abord, il inaugure la figure du philosophe comme témoin : celui dont la cohérence entre la parole et la vie va jusqu’au sacrifice. Socrate meurt parce qu’il ne veut pas trahir ce qu’il a dit ; et Platon, en écrivant l’''Apologie'', fait de cette mort le sceau de la vérité philosophique. La philosophie, à partir de ce texte, n’est plus seulement une activité intellectuelle : elle est un engagement existentiel, et le philosophe n’est plus seulement celui qui sait, mais celui qui vit ce qu’il dit. Ensuite, le texte définit la philosophie elle-même, en l’opposant à la sophistique, à la rhétorique et à la religion populaire. Non un savoir constitué, mais un examen ; non une éloquence, mais une recherche du vrai ; non un rite, mais un service intérieur du divin. Cette triple opposition structure toute la pensée platonicienne ultérieure et, par voie de conséquence, la pensée occidentale. La philosophie, depuis Socrate, se définit comme ce qui n’est pas sophistique : un savoir désintéressé, inséparable d’une pratique de vérité. Enfin, le texte pose la question politique dans ses termes platoniciens : la cité peut-elle accueillir le philosophe ? La condamnation de Socrate se laisse lire comme une réponse négative qu’Athènes aurait donnée en acte à cette question, et c’est ainsi que Platon semble l’interpréter. L’œuvre de Platon tout entière tentera de penser une cité qui répondrait autrement, depuis les esquisses du ''Gorgias'' et de la ''République'' jusqu’à la construction ultime des ''Lois''. Le procès de Socrate est ainsi, indirectement, à l’origine de la philosophie politique occidentale. Lire l’''Apologie'' aujourd’hui, c’est s’exposer à une exigence intacte. La vie non examinée n’est pas digne d’être vécue ; la vertu vaut plus que l’argent, que la réputation, que la vie même ; la lâcheté est pire que la mort ; le juste préfère subir l’injustice plutôt que la commettre. Ces formules, qui paraissent extrêmes, sont le cœur d’une éthique dont la philosophie occidentale n’a cessé de se réclamer, même quand elle croyait s’en affranchir. Le taon, vingt-quatre siècles plus tard, pique toujours. == Notes et références == <references /> == Bibliographie sélective == === Éditions et traductions de l’''Apologie'' === * Platon, ''Apologie de Socrate. Criton'', traduction, introduction et notes par Luc Brisson, Paris, GF-Flammarion, 2016. * Platon, ''Apologie de Socrate'', traduction par Luc Brisson, présentation, notes, dossier, répertoire et glossaire par Arnaud Macé, Paris, GF-Flammarion, 2017. * Platon, ''Apologie de Socrate'', traduction, présentation et notes de Bernard et Renée Piettre, Paris, Le Livre de Poche (Librairie générale française), coll. « Libretti », 1997. * Platon, ''Apologie de Socrate'', texte établi et traduit par Maurice Croiset, Paris, Les Belles Lettres, coll. des Universités de France, 1920 (nombreuses rééditions). * Platon, ''Œuvres complètes'', sous la direction de Luc Brisson, Paris, Flammarion, 2008 (contient l’''Apologie''). === Commentaires === * Claude Chrétien, ''Platon, Apologie de Socrate'', Paris, Hatier, coll. « Profil philosophie », 1993. * Paul Allen Miller, Charles Platter, ''Plato’s Apology of Socrates: A Commentary'', Norman, University of Oklahoma Press, 2010. * Émile de Strycker, S. R. Slings, ''Plato’s Apology of Socrates: A Literary and Philosophical Study with a Running Commentary'', édité et complété par S. R. Slings, Leyde, E. J. Brill, coll. « Mnemosyne Supplements » 137, 1994. * Thomas G. West, ''Plato’s Apology of Socrates: An Interpretation with a New Translation'', Ithaca, Cornell University Press, 1979. === Études sur Socrate et l’''Apologie'' === * Louis-André Dorion, ''Socrate'', Paris, PUF, coll. « Que sais-je ? », 2004. * Gregory Vlastos, ''Socrate : ironie et philosophie morale'', trad. C. Dalimier, Paris, Aubier, 1994 (''Socrates: Ironist and Moral Philosopher'', 1991). * Gregory Vlastos, « The Socratic Elenchus », ''Oxford Studies in Ancient Philosophy'', I, 1983, p. 27-58. * Debra Nails, ''The People of Plato: A Prosopography of Plato and Other Socratics'', Indianapolis, Hackett, 2002. * Gabriele Giannantoni, ''Socratis et Socraticorum Reliquiae'', 4 vol., Naples, Bibliopolis, 1990. * Monique Canto-Sperber (dir.), ''Les Paradoxes de la connaissance. Essais sur le Ménon de Platon'', Paris, Odile Jacob, 1991. === Réceptions modernes === * G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', t. II, trad. P. Garniron, Paris, Vrin, 1971. * Søren Kierkegaard, ''Le Concept d’ironie constamment rapporté à Socrate'' (1841), trad. P.-H. Tisseau et E.-M. Jacquet-Tisseau, Paris, Éditions de l’Orante, 1975. * Friedrich Nietzsche, ''La Naissance de la tragédie'' (1872), dans ''Œuvres philosophiques complètes'', t. I, Paris, Gallimard, 1977. * E. R. Dodds, ''Les Grecs et l’irrationnel'' (1951), trad. M. Gibson, Paris, Flammarion, coll. « Champs », 1977. * Hannah Arendt, ''La Vie de l’esprit'', trad. L. Lotringer, Paris, PUF, 1981. * Michel Foucault, ''L’Herméneutique du sujet. Cours au Collège de France, 1981-1982'', éd. F. Gros, Paris, Gallimard/Seuil, 2001. * Michel Foucault, ''Le Courage de la vérité. Cours au Collège de France, 1983-1984'', éd. F. Gros, Paris, Gallimard/Seuil, 2009. * Leo Strauss, ''Studies in Platonic Political Philosophy'', Chicago, University of Chicago Press, 1983. === Sources antiques === * Aristophane, ''Les Nuées'', dans ''Théâtre complet'', t. I, trad. V.-H. Debidour, Paris, Gallimard, coll. « Folio », 1965. * Xénophon, ''Apologie de Socrate'' et ''Mémorables'', trad. P. Chambry, Paris, Garnier-Flammarion, 1967. * Diogène Laërce, ''Vies et doctrines des philosophes illustres'', trad. sous la direction de M.-O. Goulet-Cazé, Paris, Le Livre de Poche, coll. « La Pochothèque », 1999. * Plutarque, ''Du démon de Socrate'', dans ''Œuvres morales'', t. VIII, trad. J. Hani, Paris, Les Belles Lettres, 1980. * Aristote, ''Rhétorique'', trad. M. Dufour et A. Wartelle, Paris, Les Belles Lettres, 1938-1973. * Cicéron, ''Tusculanes'', trad. J. Humbert, Paris, Les Belles Lettres, 1931. r17l3mqw1acz0w80rshvevy07ymawof 763993 763990 2026-04-19T08:11:33Z PandaMystique 119061 763993 wikitext text/x-wiki == Introduction générale == {{wikisource|Apologie de Socrate (Platon)|Apologie de Socrate}} === L’événement et sa portée === En l’an 399 avant notre ère, à Athènes, un vieil homme de soixante-dix ans comparaît devant un tribunal populaire composé de cinq cent un jurés citoyens. Il s’appelle Socrate, fils de Sophronisque ; il est accusé d’impiété et de corruption de la jeunesse. Au terme d’une longue journée d’audience, à une faible majorité d’abord (telle que, selon ses propres mots en 36a, un basculement de trente voix aurait suffi à l’acquittement), puis à une majorité plus large pour la peine capitale, il sera condamné à mort. Quelques semaines plus tard, il boira la ciguë dans sa cellule, devant ses disciples, après le retour du bateau sacré de Délos. Cet événement, déjà traumatisant en son temps, est devenu le mythe fondateur de la philosophie occidentale : le moment où la cité met à mort celui qui prétendait y exercer, par la parole et l’examen, un magistère moral. L’''Apologie de Socrate'' est l’œuvre par laquelle Platon, alors âgé d’une quarantaine d’années, rend compte de ce procès. On situe sa composition probablement entre 390 et 385, soit une dizaine d’années après les faits. Le texte se présente sans cadre fictionnel : nul narrateur, nul personnage introductif, nulle scène préalable comme il en existe dans la plupart des autres dialogues. Nous sommes plongés d’emblée dans la parole de Socrate, au moment où il se lève pour prendre la parole. Contrairement à la plupart des dialogues platoniciens, Platon s’efface presque entièrement : comme le remarque B. Piettre dans son commentaire, dans aucune autre œuvre on n’a l’impression d’entendre avec une telle proximité la parole de Socrate, « comme s’il nous était donné d’assister au procès »<ref name="piettre">Bernard Piettre, ''Platon, Apologie de Socrate'', traduction, présentation et notes de Bernard et Renée Piettre, Paris, Le Livre de Poche (Librairie générale française), coll. « Libretti », 1997, p. 21.</ref>. Cela ne signifie pas que l’''Apologie'' soit une transcription sténographique du plaidoyer : Platon recompose, réorganise, stylise. Mais il s’efforce, probablement plus que dans tout autre dialogue, de restituer la voix, le ton, l’argumentation du maître. Il convient de rappeler que l’''Apologie'' de Platon n’est pas la seule défense posthume de Socrate. Xénophon a également rédigé une ''Apologie'' qui nous est parvenue, brève et d’un ton psychologique différent, ainsi que des ''Mémorables'' qui reprennent certains motifs<ref>Xénophon, ''Apologie de Socrate'' et ''Mémorables'', trad. P. Chambry, Paris, Garnier-Flammarion, 1967.</ref>. D’autres disciples, comme Eschine de Sphettos, ont composé des écrits socratiques aujourd’hui perdus<ref>Voir Gabriele Giannantoni, ''Socratis et Socraticorum Reliquiae'', 4 vol., Naples, Bibliopolis, 1990.</ref>. Un pamphlet hostile, l’''Accusation de Socrate'' du sophiste Polycrate (probablement composé vers 393-392), circulait et aurait donné à Platon l’occasion de répondre indirectement. La concurrence entre ces « socratiques » explique vraisemblablement en partie le soin que Platon met à camper son propre Socrate, qui finira par s’imposer comme la figure canonique dans la postérité. L’enjeu de l’''Apologie'' dépasse de loin la réhabilitation posthume d’un homme. À travers le procès de Socrate, Platon met en accusation la cité qui l’a condamné ; il ouvre l’espace d’une autre politique, où la philosophie, cet « amour du savoir » (''philosophía''), apparaît comme la véritable vocation civique. Condamner le philosophe, c’est pour Athènes se condamner elle-même, préférer l’ignorance au savoir, la facilité au courage, la flatterie à la vérité. L’''Apologie'' constitue ainsi, selon une formule souvent reprise, l’un des textes fondateurs de la philosophie comme discours distinct, à la fois opposé à la sophistique et à la rhétorique, et radicalement engagé dans l’existence concrète. Elle offre en outre une cristallisation exemplaire de ce qu’on appellera le « dialogue socratique », genre dont Platon fera sa forme propre de pensée. === Contexte historique du procès === Pour comprendre la portée du texte, il faut garder à l’esprit la situation d’Athènes en 399. La cité sort épuisée de la longue guerre du Péloponnèse (431-404), qui l’a opposée à Sparte et s’est soldée par la défaite totale d’Athènes. Après la capitulation, Lysandre, le général spartiate, a imposé un gouvernement oligarchique (les Trente) qui a régné par la terreur pendant environ huit mois (404-403) : exécutions sommaires, confiscations, exil des démocrates. La démocratie a été rétablie par un soulèvement armé parti de Phylè en 403, et, dans le cadre des accords de réconciliation conclus sous l’archontat d’Euclide, une amnistie générale a été proclamée pour ne pas ajouter la guerre civile aux malheurs déjà subis : il était désormais interdit, sous peine de sanctions, de se référer aux événements antérieurs à la restauration<ref>Sur cette amnistie, qu’il ne faut pas confondre avec le décret de Patroclide de 405 (qui portait sur la réhabilitation des ''atimoi''), voir Aristote, ''Constitution d’Athènes'', 39-40 ; et Xénophon, ''Helléniques'', II, 4, 38-43.</ref>. Mais les plaies étaient béantes, et le ressentiment courait souterrainement. Or Socrate avait été, dans les années antérieures, un familier de personnages qui incarnaient précisément ces désastres. Alcibiade, son disciple brillant et fantasque, avait entraîné Athènes dans la désastreuse expédition de Sicile (415-413), scandalisé la cité par l’affaire de la mutilation des hermès, puis fini par trahir sa patrie en passant chez Sparte. Critias, autre de ses proches, avait été l’un des chefs, peut-être le principal, du gouvernement des Trente, artisan de la terreur oligarchique. Charmide, oncle de Platon et lui aussi de l’entourage socratique, avait également appartenu à ce régime. Même si Socrate lui-même avait refusé de collaborer avec les Trente (comme il le rappellera dans son plaidoyer à propos de l’arrestation de Léon de Salamine), sa réputation se trouvait entachée. Le procès de 399, bien que portant officiellement sur des griefs religieux, fonctionne donc aussi comme un règlement de comptes politique, que l’amnistie interdisait pourtant de mener ouvertement. Eschine le rhéteur, quelques décennies plus tard, dira sans détour que les Athéniens avaient condamné Socrate « parce qu’il avait été le maître de Critias »<ref>Eschine, ''Contre Timarque'', I, 173.</ref>. L’accusation est portée par trois hommes. Mélétos, poète tragique obscur, plutôt jeune, est le plaignant officiel, celui qui a déposé la plainte auprès de l’archonte-roi, magistrat compétent pour les affaires religieuses. Mais Socrate sait parfaitement que le véritable moteur de la procédure est Anytos, riche tanneur, homme politique influent de la démocratie restaurée, qui avait participé à la chute des Trente. Son fils, raconte Xénophon, fréquentait Socrate et préférait ses entretiens à la tannerie paternelle : hostilité d’autant plus vive<ref>Xénophon, ''Apologie de Socrate'', 29-31.</ref>. Dans le ''Ménon'' de Platon (90b-95a), Anytos apparaît comme le type même du démocrate borné, ennemi viscéral des sophistes et plus largement des intellectuels<ref>Platon, ''Ménon'', 89e-95a.</ref>. Le troisième accusateur, Lycon, est un orateur dont la fonction semble avoir été de soutenir la plaidoirie par une péroraison énergique. La plainte, dont Diogène Laërce nous a conservé le texte<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 40.</ref>, s’énonce approximativement ainsi : <blockquote>Socrate est coupable de ne pas reconnaître les dieux que reconnaît la cité et d’introduire des divinités nouvelles ; il est aussi coupable de corrompre la jeunesse. Peine requise : la mort.</blockquote> Trois griefs, donc : la négation des dieux traditionnels, l’introduction de nouvelles divinités, la corruption de la jeunesse. Ces trois chefs d’accusation sont intimement liés dans la logique de l’accusation : en introduisant de nouvelles divinités et en niant les anciennes, Socrate aurait, par son enseignement, détourné les jeunes gens du respect dû à la religion civique, donc corrompu la cité dans ses fondements. L’accusation d’impiété (''asébeia'') était une accusation politique au sens fort, puisque la religion à Athènes n’était pas une affaire privée mais la substance même du lien civique. === Le cadre procédural === Le procès se tient à l’Héliée, le tribunal populaire, probablement dans un local situé sur l’agora. Les jurés (501 ce jour-là, nombre impair pour éviter les égalités) ont été tirés au sort le matin même parmi les six mille citoyens qui s’étaient inscrits comme héliastes pour l’année. Une journée entière est consacrée à la procédure, dont le temps est rigoureusement partagé entre l’accusation et la défense au moyen de la clepsydre, horloge à eau. Chaque partie parle pour son propre compte : ni procureur ni avocat. Il est permis de faire corroborer son discours par un orateur plus habile (ce dont Mélétos semble avoir bénéficié avec Lycon), ou de lire un discours écrit par un logographe. La tradition rapporte que Lysias, le plus grand logographe de l’époque, aurait composé pour Socrate un plaidoyer de défense ; Socrate en aurait reconnu la beauté avant de le rejeter comme ne lui convenant pas<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 40 ; Cicéron, ''De l’orateur'', I, 231.</ref>. La procédure, pour un procès où la loi n’a pas fixé la peine à l’avance (on parle alors d’''agôn timētós'', « procès à peine à estimer »), comporte deux votes. Après les plaidoiries, un premier vote tranche sur la culpabilité. En cas de condamnation, l’accusateur propose une peine (ici, la mort), l’accusé doit proposer une contre-peine, et un second vote choisit entre les deux sans possibilité de moyen terme. Cette contrainte procédurale pèse lourd sur la suite : les jurés, contraints de choisir entre deux propositions extrêmes, se trouvent ainsi, par la stratégie de Socrate refusant de proposer une peine crédible, poussés à voter la mort. C’est la procédure en deux temps qui explique la structure tripartite de l’''Apologie'' telle que Platon nous l’a transmise : d’abord le grand plaidoyer de défense (17a-35d), puis le discours de contre-proposition pénale après le verdict de culpabilité (35e-38b), enfin une dernière allocution prononcée après la condamnation à mort, adressée tour à tour aux jurés qui ont voté contre lui et à ceux qui l’ont soutenu (38c-42a). Il faut ajouter que les procès athéniens étaient des spectacles autant que des procédures. Le public y assiste ; les plaideurs usent de toute la rhétorique, des larmes de la famille éplorée, des supplications ostentatoires, des témoignages de moralité. Aristophane, dans ''Les Guêpes'' (422), ridiculise la passion que les Athéniens portaient à ces mises en scène judiciaires et le plaisir qu’ils prenaient à voir les accusés se répandre en lamentations. Socrate, comme on le verra, refuse délibérément toute cette théâtralité, ce qui nourrira, après la condamnation, son reproche aux juges : ils l’ont condamné non pas parce qu’il était coupable, mais parce qu’il n’a pas consenti à s’abaisser. === Structure et dispositif du texte === Lu rétrospectivement à travers la grille qu’Aristote constituera dans sa ''Rhétorique'', le plaidoyer de Socrate laisse reconnaître, dans son grand plan, les parties canoniques du discours judiciaire : on y distingue successivement l’exorde (17a-18a), la diabolè ou dénigrement des accusations antérieures (18a-19d), la narration ou ''diḗgēsis'' qui expose les faits (19d-24b), la réfutation proprement dite (24b-28a), l’amplification et la péroraison (28a-35d). Il ne s’agit pas de prétendre que Platon a composé son texte en suivant consciemment un schéma déjà codifié (la ''Rhétorique'' aristotélicienne est postérieure), mais de remarquer que le plaidoyer épouse, dans ses grandes articulations, les usages rhétoriques de son temps. Cette conformité apparente est constamment subvertie par l’ironie socratique : Socrate adopte la forme du discours judiciaire tout en la démentant dans son contenu, en refusant ses procédés et en retournant ses attentes. Le texte fonctionne ainsi comme une parodie philosophique de la rhétorique : il emprunte à l’art oratoire sa structure, mais seulement pour en exhiber la vanité quand il s’agit de la vérité. == Lecture suivie du texte == === Le premier discours : la défense (17a-35d) === ==== L’exorde (17a-18a) : la vérité contre l’éloquence ==== Dès les premiers mots, Socrate pose le ton. La formule d’ouverture, <blockquote>Quel effet, Athéniens, ont produit sur vous mes accusateurs, je l’ignore,</blockquote> est un exorde classique (en grec ''prooímion'') dont la fonction rhétorique, comme le rappelle Aristote dans la ''Rhétorique'' (III, 14), est de capter la bienveillance et l’attention de l’auditoire<ref>Aristote, ''Rhétorique'', III, 14, 1415a22-b30.</ref>. Socrate s’en sert pour établir immédiatement l’antithèse qui structurera tout son plaidoyer : ses accusateurs ont parlé avec persuasion, mais n’ont dit « rien de vrai ou presque » ; lui, en revanche, dira « toute la vérité ». Cette opposition entre l’éloquence ornée des adversaires et la parole nue de Socrate est un retournement ironique savamment préparé. Les accusateurs ont mis les juges en garde contre le « redoutable discoureur » qu’est Socrate. Mais, rétorque-t-il, s’ils entendent par là celui qui dit la vérité, il concède qu’il est un orateur, bien que pas à leur manière. Ses discours ne seront pas, dit-il, « des discours élégamment tournés, comme les leurs, ni même des discours qu’embellissent des expressions et des termes choisis », mais « des choses dites à l’improviste dans les termes qui [lui] viendront à l’esprit ». L’opposition technique est nette : d’un côté le discours apprêté, fondé sur la sélection du vocabulaire (''onómata''), la tournure des phrases (''rhḗmata'') et les arrangements (''kósmos'') ; de l’autre une parole qui se veut « au hasard », sans préparation. Socrate demande aux juges de lui pardonner cette façon de parler : il a soixante-dix ans, comparaît pour la première fois de sa vie devant un tribunal, et il est donc « étranger à la langue en usage ici ». Comme un véritable étranger qui parlerait dans son dialecte, il entend s’exprimer comme il le fait ordinairement, sur l’agora ou devant les comptoirs des changeurs. Le juge, conclut-il, doit juger sur le fond : <blockquote>si mes allégations sont justes ou non. Telle est en effet la vertu du juge, tandis que celle de l’orateur est de dire la vérité. (18a)</blockquote> Cette prise de position initiale est philosophiquement lourde de conséquences. Elle reformule, dès l’ouverture, l’opposition fondamentale entre la rhétorique (l’art de persuader, tel que le pratiquent sophistes et logographes) et la philosophie (recherche de la vérité par l’examen rationnel). Les sophistes, comme Gorgias ou Protagoras, avaient fait de la rhétorique un instrument neutre, capable de « faire de l’argument le plus faible l’argument le plus fort » (formule qui reviendra explicitement contre Socrate au chef d’accusation). Socrate, à l’inverse, affirme que la parole a pour fonction première de manifester le vrai, et que celui qui se défend en justice ne doit pas jouer des passions ou des artifices, mais soumettre les arguments à l’examen rationnel des jurés. On notera pourtant que cette protestation de simplicité est elle-même une figure rhétorique sophistiquée : celui qui clame qu’il ne sait pas parler parle déjà, et fort bien. Il s’agit de la ''dissimulatio artis'' que Cicéron théorisera plus tard : l’art supérieur consiste à cacher l’art. L’ironie socratique commence dès l’exorde. Socrate se présente comme un ''idiṓtēs'' (un simple particulier, un « idiot » au sens grec), étranger aux codes du tribunal, mais cette posture est elle-même un dispositif stratégique qui retourne contre l’accusation la suspicion de sophistique : ce n’est pas Socrate qui manipule les mots, ce sont les accusateurs. L’inversion est complète. ==== Les « anciens accusateurs » : la calomnie de longue durée (18a-19d) ==== Socrate introduit ensuite une distinction cruciale : il doit répondre à deux catégories d’accusations, celles de ses « accusateurs récents » (Mélétos, Anytos, Lycon), mais aussi, et d’abord, celles de ses « anciens accusateurs », qui ont « depuis de nombreuses années » répandu une image fausse de lui. Ceux-là, il les redoute davantage que les nouveaux, pour trois raisons convergentes. Ils sont d’abord nombreux : il ne s’agit pas de trois personnes identifiables, mais d’une rumeur collective. Leurs accusations sont ensuite anciennes : elles ont eu le temps de s’enraciner dans les esprits. Enfin, ils ont agi auprès des juges « dès l’enfance », à un âge où « vous aviez le moins de défiance », de sorte que les jurés en ont reçu l’empreinte avant même d’avoir l’âge de l’examiner. Quelle est la teneur de cette calomnie ancienne ? Socrate la résume en une formule qui reviendra plusieurs fois dans le texte comme un chef d’accusation fantasmatique : <blockquote>Il existe un certain Socrate, un savant, un « penseur » qui s’intéresse aux choses qui se trouvent en l’air, qui mène des recherches sur tout ce qui se trouve sous la terre et qui de l’argument le plus faible fait l’argument le plus fort. (18b)</blockquote> Cette caricature hétéroclite condense en réalité trois traits initialement distincts. Il y a d’abord la figure du physicien à la manière des présocratiques (Anaxagore, Diogène d’Apollonie), qui spéculait sur les phénomènes célestes (''tà metéōra'') et souterrains. Il y a ensuite celle de l’athée, puisque interroger la nature par la raison revenait, dans la perception populaire, à nier les dieux traditionnels (Anaxagore avait été poursuivi pour cette raison vers 433, Protagoras également, et tous deux avaient été contraints à l’exil). Il y a enfin celle du sophiste, expert en retournements dialectiques, capable de faire triompher n’importe quelle cause par le seul art des mots. L’incohérence de ce portrait (un philosophe de la nature qui serait en même temps un manipulateur rhétorique) ne l’empêche pas d’être efficace dans l’opinion. C’est le propre de la rumeur (''diabolḗ'') : elle n’a pas à être cohérente pour être puissante. Socrate le reconnaît avec lucidité : la force de cette calomnie vient précisément de ce qu’elle ne repose sur aucune source identifiable, qu’on ne peut donc ni l’interroger, ni la réfuter. Combattre ces accusateurs anonymes, dit Socrate, « c’est comme se battre contre des ombres » (18d). C’est une difficulté propre au philosophe dans la cité : face à l’opinion établie, il n’a pas d’adversaire identifiable, donc pas de prise dialectique. Socrate désigne cependant une source probable : les comédies, et en particulier ''Les Nuées'' d’Aristophane, représentées en 423 (vingt-quatre ans avant le procès)<ref>Aristophane, ''Les Nuées'', représentées pour la première fois aux Grandes Dionysies de 423 av. J.-C.</ref>. Il la mentionne d’abord anonymement, parlant d’un <blockquote>Socrate qui se balançait, en prétendant qu’il se déplaçait dans les airs et en débitant plein d’autres bêtises concernant des sujets sur lesquels je ne suis un expert ni peu ni prou. (19c)</blockquote> Le nom d’Aristophane sera explicitement prononcé peu après. Dans cette pièce, le poète comique met en scène un Socrate juché dans une corbeille suspendue, pour mieux « mêler sa pensée subtile à l’air », invoquant les Nuées comme divinités substitutives à celles de la religion populaire, enseignant à un paysan, Strepsiade, puis à son fils Phidippide, comment faire triompher le « Raisonnement injuste » et ruiner par cet apprentissage la piété filiale. La pièce s’achève d’ailleurs sur l’incendie du « Pensoir » socratique. L’enjeu pour Socrate n’est pas seulement de corriger une image fausse : c’est de montrer que les accusations de Mélétos se rabattent exactement sur cette caricature (négation des dieux, introduction de nouveautés religieuses, corruption de la jeunesse), de sorte que contre-attaquer la comédie, c’est déjà dissoudre la plainte. Il y a là un geste tranché : Socrate refuse explicitement d’être confondu, d’une part avec les physiciens qui spéculent sur la nature, d’autre part avec les sophistes qui enseignent la rhétorique contre rémunération. Il énumère d’ailleurs plusieurs sophistes célèbres (Gorgias de Léontinoi, Prodicos de Céos, Hippias d’Élis) et rappelle l’anecdote d’Événos de Paros, qui faisait payer cinq mines pour son enseignement (20b) : somme considérable, correspondant à environ un an et demi de salaire d’un ouvrier qualifié<ref name="brisson">Platon, ''Apologie de Socrate. Criton'', trad. et notes de Luc Brisson, Paris, GF-Flammarion, 2016, p. 131, note 54.</ref>. Socrate, lui, n’a rien à vendre, et c’est précisément cela, comme on le verra, qui témoigne de la pureté de sa démarche. Notons enfin une dimension cruciale de ce moment : Socrate récuse par avance toute identification à la figure du philosophe-penseur retiré du monde, que Platon décrit dans le ''Théétète'' (174a) par l’anecdote célèbre de Thalès tombant dans le puits en contemplant les étoiles<ref>Platon, ''Théétète'', 174a.</ref>. La défense de Socrate sera celle d’un homme de l’agora, immergé dans la cité, engagé dans l’examen de ses concitoyens. Le philosophe socratique n’est pas un contemplatif éloigné des affaires humaines : c’est au contraire le plus urbain des hommes, celui qui ne quitte jamais la ville (comme il le dit dans le ''Phèdre''), celui dont l’activité est fondamentalement politique, même s’il n’exerce aucune charge politique. ==== La narration : l’oracle de Delphes et la naissance de la mission (19d-24b) ==== Pour expliquer l’origine de la calomnie, Socrate engage ce qui correspond à la narration (''diḗgēsis'') du discours rhétorique. Il entreprend de raconter comment il est devenu ce personnage que certains considèrent comme un savant. Cette narration, qui occupe une part importante du discours, contient deux éléments structurants : le récit de l’oracle de Delphes et l’exposé de l’enquête qui s’ensuivit. ===== Le savoir humain et le savoir plus qu’humain (20d-21a) ===== Avant de raconter l’oracle, Socrate pose une distinction fondamentale qui constitue peut-être la pièce conceptuelle centrale de tout le plaidoyer. On dit qu’il est « savant » (''sophós'') : soit. Mais savant en quoi ? Il existe, dit-il, un savoir qui excède la mesure humaine, celui auquel prétendent, moyennant rétribution, les sophistes. Ce savoir-là, Socrate ne le possède pas ; et il aurait « des chances d’être un savant » seulement dans un sens plus modeste, celui d’un savoir qui « se rapporte à l’être humain », une sagesse humaine (''anthrōpínē sophía''). La distinction est capitale. Elle sépare deux ordres de connaissance : celui qui porte sur les choses divines (la nature, le cosmos, les causes premières), que Socrate refuse de revendiquer ; et celui qui porte sur l’humain, sur ce qu’il convient de faire pour vivre bien. Cette distinction préfigure le partage que toute l’histoire de la philosophie reprendra entre philosophie théorique et philosophie pratique, et elle annonce également la réorientation socratique qu’évoque Cicéron dans un passage célèbre : <blockquote>Socrate a fait descendre la philosophie du ciel sur la terre, l’a introduite dans les villes et même dans les maisons, et l’a obligée à s’enquérir de la vie, des mœurs, des choses bonnes et mauvaises. (''Tusculanes'', V, 10-11)<ref>Cicéron, ''Tusculanes'', V, 4, 10-11 : « Socrates autem primus philosophiam devocavit e caelo et in urbibus conlocavit et in domus etiam introduxit... »</ref></blockquote> La philosophie cesse d’être cosmologie pour devenir éthique. Pour prouver l’existence et la nature de ce savoir proprement humain, Socrate invoque un témoin insolite mais sans appel : « le dieu de Delphes », c’est-à-dire Apollon pythien. Le recours à la parole oraculaire, dans un tribunal populaire attaché à la religion civique, est rhétoriquement habile : il retourne l’accusation d’impiété en présentant Socrate comme un serviteur du dieu. ===== L’oracle et l’enquête (21a-23c) ===== C’est le moment où surgit, dans le texte, le récit de l’oracle. Chéréphon, ami d’enfance de Socrate (un homme à la passion impétueuse, démocrate, exilé sous les Trente et revenu avec la démocratie, que la comédie moquait pour sa maigreur ascétique et sa « mine d’endive »<ref>Aristophane, ''Les Guêpes'', v. 1408 ; ''Les Oiseaux'', v. 1296 ; ''Les Nuées'', v. 104.</ref>), était allé un jour à Delphes et avait eu l’audace de demander à la Pythie s’il existait quelqu’un de plus sage que Socrate. La Pythie, prêtresse d’Apollon, parle au nom du dieu et rend des oracles aux consultants : elle répondit que « personne n’était plus sage ». Chéréphon est mort entre-temps (probablement vers 403), mais son frère, présent à l’audience, pourra en témoigner. Cette réponse divine met Socrate dans un profond embarras. Il a « conscience de n’être savant ni peu ni prou ». Mais le dieu, par définition, ne peut mentir : « la loi divine l’interdit » (21b). Comment résoudre l’énigme (''aínigma'') ? Socrate décide alors d’entreprendre une vérification. Il va chercher quelqu’un de plus sage que lui, afin de pouvoir revenir à Delphes et dire : <blockquote>ce dieu m’avait désigné comme le plus sage, mais voici qui l’est davantage.</blockquote> Cette démarche, loin d’être un geste d’orgueil, est présentée comme un service rendu au dieu : c’est pour vérifier l’oracle, non pour le contredire, que Socrate entreprend son enquête. Il accorde à l’oracle une autorité suffisante pour l’interroger méthodiquement, selon un principe qui rappelle la maxime exégétique attribuée à Héraclite : « le maître dont l’oracle est à Delphes ne dit ni ne cache rien : il fait signe »<ref>Héraclite d’Éphèse, fragment DK 22 B 93 (Diels-Kranz) = fragment 14 Conche. Voir Marcel Conche, ''Héraclite. Fragments'', Paris, PUF, coll. « Épiméthée », 1986, p. 168-170.</ref>. L’enquête est méthodiquement menée dans trois directions, que Socrate parcourt successivement. Il va d’abord trouver un homme politique (dont il tait le nom, conformément à la pudeur judiciaire) qui passait pour sage. Après l’avoir interrogé, il constate que cet homme se croit sage mais ne l’est pas. Socrate tire alors la leçon capitale qui donnera son contenu à la sagesse humaine : <blockquote>Il y a des chances que je sois moi-même plus sage que cet homme. Car aucun de nous, il est vraisemblable, ne sait rien qui en vaille la peine ; mais lui pense savoir alors qu’il ne sait pas, tandis que moi, tout comme je ne sais pas, je ne pense pas non plus savoir. (21d)</blockquote> Tel est le célèbre savoir du non-savoir socratique : non pas une ignorance totale, mais la conscience lucide et réfléchie de sa propre ignorance, qui vaut davantage que l’illusion de la science. Socrate répète l’opération avec d’autres hommes politiques, et chaque fois la déception est la même, mais pire : plus l’homme est réputé, plus son ignorance est grande ; plus il est humble, plus il est proche du vrai. Socrate passe ensuite aux poètes : auteurs de tragédies, poètes dithyrambiques et autres. Il leur demande ce que signifient leurs propres œuvres, espérant d’eux un savoir, puisqu’ils produisent de la beauté. Il découvre alors qu’ils composent « non par savoir, mais par une sorte de disposition naturelle et par inspiration, comme les devins et les oracles » (22c). Les poètes disent beaucoup de belles choses sans savoir ce qu’elles veulent dire ; et, comme les hommes politiques, ils se croient, à cause de leur art, savants dans d’autres domaines où ils ne le sont pas. L’inspiration poétique est ainsi reconnue comme réelle, mais dissociée du savoir : le poète est possédé par une muse, non par une compétence. Cette analyse, qui reviendra dans le ''Ion'', est une pièce importante de la pensée platonicienne sur l’art<ref>Platon, ''Ion'', 533d-535a.</ref>. Socrate examine enfin les artisans (''cheirotéchnai''). Ici, la situation est plus nuancée. Les artisans possèdent une compétence réelle, une ''tékhnē'', que Socrate ne songe pas à leur dénier. Mais cette compétence les induit à se croire savants « dans les choses les plus importantes », c’est-à-dire dans les questions morales et politiques, alors qu’ils ne le sont pas. L’artisan, parce qu’il sait faire une paire de chaussures, se croit en droit d’avoir un avis éclairé sur la justice. C’est là un point crucial : Socrate reconnaît la légitimité de la ''tékhnē'' dans son domaine propre, mais refuse l’extrapolation de la compétence technique à la sagesse pratique. La conclusion de l’enquête est double. D’une part, Socrate conclut à la véracité de l’oracle : sa sagesse consiste en ce qu’il ne croit pas savoir ce qu’il ne sait pas. D’autre part, il comprend que l’oracle ne le désignait pas ''lui'' spécifiquement comme sage, mais se servait de son nom à titre d’exemple : <blockquote>il y a des chances, Messieurs, pour qu’en réalité le sage, ce soit le dieu, et que dans ce fameux oracle il veuille dire que la sagesse humaine a bien peu de valeur, et même aucune ; et il est clair qu’en désignant Socrate il s’est servi de mon nom pour me prendre en exemple, comme s’il disait : « Le plus sage d’entre vous, hommes, est celui qui, comme Socrate, a reconnu qu’en réalité sa sagesse ne vaut rien. » (23a-b)</blockquote> Socrate devient ainsi, non pas un savant, mais un exemple (''parádeigma'') par lequel le dieu invite tous les hommes à reconnaître la misère de leur prétention au savoir. La sagesse n’est pas une propriété de l’individu, mais une relation à l’ignorance ; elle est, on pourrait dire, éminemment ''maïeutique'' : elle fait accoucher les autres de la conscience de leur propre ignorance. ===== La mission et la naissance de la haine (23b-24b) ===== De cette interprétation de l’oracle naît la mission socratique. Socrate continue, « en service pour le dieu » (''hypēresía toû theoû''), à enquêter sur quiconque prétend être sage, pour manifester chaque fois qu’il ne l’est pas. Cette activité l’a réduit à « une grande pauvreté », car elle lui a occupé toute sa vie au détriment de ses affaires. Mais elle a aussi suscité contre lui des haines innombrables, chaque fois qu’il a démasqué l’ignorance d’un puissant ou d’un réputé. On touche ici à un mécanisme psychologique qu’il faut bien mesurer : nul n’aime être convaincu d’ignorance, surtout publiquement ; celui qui le fait, même par amour du vrai, se constitue des ennemis à proportion de sa rigueur. Les jeunes gens de bonne famille, ceux qui disposent de loisir (''scholḗ''), prennent plaisir à le voir faire ; ils tentent à leur tour d’imiter sa démarche, et ainsi « se font eux-mêmes haïr par ceux qu’ils examinent » (23c), qui ne s’en prennent pas à ces jeunes, mais à Socrate. Ce dernier devient ainsi le responsable imaginaire d’une activité critique qui déborde largement sa personne. C’est le ressort profond de l’accusation de « corruption de la jeunesse » : ce n’est pas que Socrate ait enseigné le mal, c’est que ses méthodes, imitées par ses disciples, font éclater un scandale qu’on veut lui imputer. C’est ainsi que s’est formée la réputation selon laquelle Socrate serait un « corrupteur de la jeunesse », et qu’on a repris contre lui la vieille caricature : un homme qui fait des recherches sur le ciel et la terre et qui fait triompher la mauvaise cause. Socrate tire la conclusion rhétorique : « c’est en disant la vérité que je me fais des ennemis », ce qui est, dit-il, la preuve qu’il dit vrai. Les causes de l’accusation sont là : non dans une faute réelle, mais dans le ressentiment de ceux qu’il a démasqués. Cette section du plaidoyer est philosophiquement fondamentale. Elle met en place plusieurs concepts clefs du socratisme tel que Platon l’entend. D’abord, la philosophie comme examen (''exétasis'') et non comme doctrine : on ne peut enseigner la philosophie de Socrate parce qu’elle n’est pas un corps de propositions à transmettre, mais une pratique à exercer. Ensuite, le savoir de l’ignorance comme seule forme accessible du savoir humain. Enfin, la philosophie comme mission divine, ce qui lui confère une légitimité supérieure à celle des institutions politiques. Cette dimension religieuse de la philosophie est essentielle à l’économie du texte : si la mission est divine, elle ne peut être interrompue par un décret humain, fût-il celui d’un tribunal souverain. On notera également que cette démarche, en démasquant l’ignorance des prétendus savants, introduit une différence entre savoir et non-savoir sur des sujets où la démocratie athénienne supposait, pour délibérer, qu’il n’y en avait pas. Comme l’explique un passage du ''Protagoras'' (319c-d), rédigé par Platon peu après l’''Apologie'', les assemblées démocratiques distinguent les sujets techniques (sur lesquels seuls les compétents s’expriment) et les sujets politiques (sur lesquels tous les citoyens, cordonniers, potiers, tanneurs ou menuisiers, peuvent donner leur avis)<ref>Platon, ''Protagoras'', 319b-d.</ref>. La délibération collective suppose que sur les sujets politiques, il n’existe pas de différence de compétence entre les citoyens. Or l’enquête socratique a précisément pour effet de réintroduire cette différence sur les objets où l’institution démocratique la refoulait. Se prétendre ignorant soi-même et révéler l’ignorance d’autrui sur les questions de justice ou de vertu, c’est mettre en cause le principe majoritaire là où il s’applique. On comprend en quoi, malgré les apparences, la posture socratique est subversive pour la démocratie athénienne : elle frappe à sa racine épistémologique. ==== La réfutation de Mélétos (24b-28a) ==== Socrate en vient maintenant aux accusations officielles. Il relit la plainte, <blockquote>Socrate est coupable de corrompre la jeunesse et de reconnaître non pas les dieux que la cité reconnaît, mais, au lieu de ceux-là, des divinités nouvelles,</blockquote> et entreprend de l’examiner point par point. Il utilise alors la prérogative que la loi athénienne accorde à l’accusé : interroger directement son accusateur, qui est tenu de répondre. Ce qui suit est l’un des grands morceaux dialectiques de l’''Apologie''. Socrate, en un véritable elenchos (cette réfutation par interrogation qui est sa marque de fabrique), démonte successivement les trois volets de l’accusation. On notera d’emblée un jeu de mots grec qui court tout l’interrogatoire : le nom de Mélétos (''Mélētos'') évoque le verbe ''mélei'' (« se soucier de »). Socrate va reprocher à Mélétos, précisément, de ne pas se soucier (mélein) des choses dont il prétend se soucier. L’ironie est ciselée jusque dans l’onomastique. La méthode que Socrate utilise ici est l’''elenchos'' : il part des thèses de l’interlocuteur, l’amène par des questions à en reconnaître les conséquences, puis lui fait constater la contradiction entre ces conséquences et d’autres thèses qu’il tient également pour vraies. Cette procédure, qui ne démontre rien positivement mais montre qu’une thèse est intenable, est le moteur de la dialectique socratique dans les dialogues de jeunesse de Platon<ref>Sur la méthode de l'elenchus, voir Gregory Vlastos, « The Socratic Elenchus », ''Oxford Studies in Ancient Philosophy'', I, 1983, p. 27-58.</ref>. ===== Qui rend les jeunes meilleurs ? (24b-25c) ===== Premier volet : la corruption de la jeunesse. Socrate commence par un piège dialectique. Si Mélétos accuse quelqu’un de corrompre les jeunes, c’est qu’il a à l’esprit ce qui les rend meilleurs. Qu’il le dise donc. Mélétos, pris au dépourvu, balbutie : « Les lois ». Mais ce n’est pas une personne, objecte Socrate. Il insiste : quel ''homme'' rend les jeunes meilleurs ? Mélétos répond alors, au gré d’une improvisation visiblement embarrassée : les juges, puis les membres du Conseil, puis ceux de l’Assemblée, bref tous les Athéniens ; tous, sauf Socrate. Socrate retourne alors l’argument par une analogie mémorable, celle des chevaux. Suppose-t-on que tous les hommes rendent les chevaux meilleurs, et qu’un seul les corromprait ? Non, c’est évidemment le contraire : pour les chevaux comme pour tous les animaux, seuls quelques spécialistes savent les rendre meilleurs, et la plupart des gens, s’ils s’en occupent, les nuisent. Il en va de même pour les jeunes gens. L’éducation est un art, et l’art suppose la compétence, qui n’est pas le partage du plus grand nombre. En prétendant que tous éduquent bien sauf Socrate, Mélétos révèle qu’il n’a jamais sérieusement réfléchi à ce dont il parle : il s’est désintéressé de la question. Ce qui est précisément ce que le jeu de mots sur son nom entendait suggérer. Ce premier argument est intéressant par ce qu’il laisse apercevoir de la position de Socrate à l’égard de la démocratie. L’éducation, comme le souligne le commentaire de C. Chrétien, est « une affaire politique tant la formation de l’homme paraît indissociable de celle du citoyen »<ref name="chretien">Claude Chrétien, ''Platon, Apologie de Socrate'', Paris, Hatier, coll. « Profil philosophie », 1993, p. 44.</ref>. Or, pour Mélétos et pour les Athéniens en général, la cité est à elle-même sa propre pédagogie : chaque citoyen forme, par son exemple et sa participation à la vie publique, les futurs citoyens. Socrate, à l’inverse, soutient que l’éducation, comme les autres arts, relève d’une compétence particulière, ce qui va à l’encontre du présupposé démocratique d’une compétence civique également partagée. En paraissant ne se battre que sur un détail logique, Socrate met en cause, à travers cet argument, l’un des présupposés majeurs de la démocratie athénienne : celui selon lequel chaque citoyen serait naturellement compétent pour la délibération politique. ===== Corrompre volontairement ? (25c-26a) ===== Deuxième pièce du dispositif : Socrate demande à Mélétos s’il le pense corrompre les jeunes volontairement ou involontairement. Mélétos, rageusement, répond : volontairement. Mais Socrate montre alors que cette réponse est intenable. Car si les méchants font du mal à leur entourage, et les bons du bien, alors corrompre volontairement les gens qui nous entourent (avec lesquels on vit) c’est s’exposer soi-même à en pâtir. Personne, pas même le plus ignorant, ne choisit délibérément de se nuire à soi-même. Donc, ou bien Socrate ne corrompt pas, ou bien, s’il corrompt, c’est involontairement. Et dans ce cas, la loi ne prévoit pas un procès mais une remontrance privée : si on m’avait averti, j’aurais cessé. En me traînant devant le tribunal, Mélétos prouve que son but n’est pas de me corriger mais de me punir ; ce qui est contradictoire avec l’idée d’une faute involontaire. Cet argument repose sur l’un des principes les plus fermes du socratisme, le célèbre paradoxe socratique selon lequel nul ne fait le mal volontairement (''oudeís hekṓn hamartánei''). Socrate y croit sincèrement : si l’on savait vraiment ce qu’est le bien, on ne pourrait pas ne pas le vouloir. Le vice n’est pas une perversion de la volonté, c’est une forme d’ignorance. Cette thèse, qui paraîtra contre-intuitive à toute la tradition postérieure (notamment chrétienne, qui mettra l’accent sur la malice du mal), sera développée dans plusieurs dialogues platoniciens, notamment le ''Gorgias'' et le ''Protagoras''<ref>Platon, ''Gorgias'', 466a-468e, 509c-e ; ''Protagoras'', 352b-358d.</ref>. Aristote, dans l’''Éthique à Nicomaque'', la critiquera comme négligeant la réalité de la ''akrasía'' (faiblesse de la volonté)<ref>Aristote, ''Éthique à Nicomaque'', VII, 2-3, 1145b21-1147b19.</ref>. Mais l’argument sert ici surtout à piéger Mélétos dans une contradiction : soit tu m’accuses d’une faute involontaire, et le procès est illégitime (car la procédure judiciaire vise des fautes intentionnelles) ; soit tu m’accuses d’une faute volontaire, mais celle-ci est psychologiquement impossible (personne ne choisit de se nuire à soi-même). Dans les deux cas, la plainte s’effondre. Remarquons la précision juridique : Socrate joue habilement sur la distinction athénienne entre fautes volontaires (justiciables) et involontaires (pour lesquelles la remontrance privée, la ''nouthesía'', était la procédure appropriée). ===== L’athéisme et les divinités démoniques (26a-28a) ===== Troisième volet : la question religieuse, qui est le cœur même de l’accusation. Socrate demande à Mélétos de préciser son propos : l’accuse-t-il de reconnaître des dieux différents de ceux de la cité, ou de ne reconnaître aucun dieu du tout ? La distinction est cruciale, car la plainte elle-même est ambiguë (elle évoque des « divinités nouvelles », ce qui présuppose que Socrate croit à des divinités, mais lui reproche aussi de ne pas reconnaître celles de la cité). Mélétos, avec une maladresse que Socrate exploite pleinement, s’emporte et répond : « aucun dieu du tout ». Il ajoute même, piégé par sa propre fureur, que Socrate prétend, à la manière d’Anaxagore, que « le soleil est une pierre et la lune une terre ». Socrate saisit l’occasion avec une précision chirurgicale. D’abord, il ridiculise la confusion : Anaxagore, en effet, a soutenu cette thèse physicienne, mais les livres d’Anaxagore, qui se trouvent au marché (à l’orchestre, endroit de l’agora où l’on vendait les livres), coûtent « tout au plus une drachme » ; pourquoi donc accuser Socrate d’avoir inventé ce qu’on peut lire partout et qui n’est pas de lui ? Le trait est doublement dévastateur : il innocente Socrate et il prouve l’incompétence de Mélétos, qui ne distingue pas Socrate d’Anaxagore. Ensuite, il tend son piège principal. La plainte officielle dit que Socrate introduit de nouvelles divinités (''daimónia''). Or Mélétos vient d’affirmer que Socrate n’admet aucun dieu du tout. Ces deux affirmations sont contradictoires : on ne peut pas à la fois reconnaître des divinités et ne reconnaître aucun dieu. Mélétos se contredit lui-même, et sous serment, car la plainte avait été déposée sous serment réciproque (''antōmosía''). Socrate poursuit par un syllogisme subtil. Peut-on reconnaître des « phénomènes démoniques » (''daimónia prágmata'') sans reconnaître l’existence de démons ? De même que l’on ne peut reconnaître des phénomènes hippiques sans reconnaître les chevaux, ni des phénomènes musicaux sans reconnaître les musiciens, on ne peut reconnaître des phénomènes démoniques sans reconnaître les démons. Or les démons, selon la religion grecque traditionnelle, sont soit des dieux soit des enfants de dieux. Donc, si Socrate reconnaît des démons, il reconnaît aussi des dieux, ou à tout le moins des êtres divins. La plainte se contredit elle-même : Mélétos affirme à la fois que Socrate ne reconnaît aucun dieu et qu’il reconnaît des démons, donc des dieux. Ce raisonnement est brillant sur le plan dialectique, mais il a suscité la perplexité des commentateurs. Il repose sur une définition traditionnelle des démons comme « enfants des dieux », qui n’est pas toujours stabilisée dans la culture grecque (chez Hésiode, les démons sont plutôt des hommes de l’âge d’or devenus esprits), et laisse entière la question de savoir ce que sont les « nouvelles divinités » dont Socrate était réellement accusé. La plupart des commentateurs modernes estiment que l’accusation visait précisément le fameux ''daimónion'' socratique, la voix intérieure divine dont Socrate parlera plus loin, qui serait apparue aux Athéniens comme une divinité privée, nouvelle, donc impie car non reconnue par la cité. Socrate, en déplaçant le débat sur la question abstraite de l’existence ou non des démons, élude habilement cette difficulté. C’est un procédé dialectique, non un argument de fond. Il faut aussi percevoir le geste de fond. Comme le note Claude Chrétien, Socrate, par ce raisonnement, rattache sa croyance en des « phénomènes démoniques » à une croyance minimale mais ferme en la divinité, sur un mode qui relève d’une théologie négative : il ne dit rien de positif sur les dieux, mais affirme seulement que quelque chose, dans l’expérience humaine, manifeste leur existence<ref name="chretien-30">Claude Chrétien, ''Platon, Apologie de Socrate'', ''op. cit.'', p. 30-31.</ref>. Cela est cohérent avec son agnosticisme sur les mythes (dans le ''Phèdre'', Socrate refuse de spéculer sur les aventures de Borée enlevant Orithye<ref>Platon, ''Phèdre'', 229b-230a.</ref>) et avec sa dévotion pratique à la religion de la cité (on le voit obéir aux rites dans plusieurs dialogues). Socrate est pieux en acte, agnostique en théorie : il reconnaît une transcendance divine, sans prétendre en décrire la nature. Cette position, fine et paradoxale, est à l’origine d’une tension féconde dans toute la théologie philosophique ultérieure. À la fin de cette section, Socrate conclut qu’il a suffisamment montré que l’accusation de Mélétos est sans consistance. Mais, ajoute-t-il avec lucidité, il sait bien que ce n’est pas elle qui le fera condamner : c’est la vieille calomnie, la haine accumulée au fil des années. D’autres hommes justes, avant lui, ont subi le même sort, et beaucoup en subiront après. ==== La mission divine et le modèle héroïque (28a-30c) ==== Ayant réfuté l’accusation formelle, Socrate entreprend alors ce que Piettre appelle une « amplification »<ref>Piettre, ''op. cit.'', p. 45-46 (sur le découpage du plaidoyer en réfutation et amplification).</ref> : il justifie tout son mode de vie. L’''Apologie'' bascule ici d’un plaidoyer juridique vers une proclamation philosophique. La question n’est plus « suis-je coupable de ces griefs précis ? » mais : « comment justifier un mode d’existence qui expose à la mort ? » C’est à partir de ce moment que l’''Apologie'' cesse d’être un simple plaidoyer pour devenir un manifeste. ===== Le modèle d’Achille (28b-d) ===== Socrate anticipe une objection qu’un auditoire athénien pouvait sincèrement formuler : n’as-tu pas honte, Socrate, d’avoir mené une existence qui t’expose à mourir ? Il y répond par l’évocation des héros homériques, et notamment d’Achille, la figure de référence de la vertu guerrière grecque. Quand Thétis, sa mère, lui annonça qu’il mourrait s’il vengeait Patrocle en tuant Hector, Achille répondit, selon l’''Iliade'' : <blockquote>que je meure immédiatement, pour peu que je punisse le coupable, plutôt que de rester ici, à être la risée de tous, assis sur mes vaisseaux, poids inutile de la terre.<ref>Homère, ''Iliade'', XVIII, v. 96-104 ; cité par Socrate en ''Apologie'', 28c-d.</ref></blockquote> Achille a donc méprisé la mort pour préserver l’honneur. Celui qui occupe une place, explique Socrate, doit y rester, au péril de sa vie, « sans tenir compte d’autre chose que du déshonneur ». Cet argument est une adaptation du modèle homérique, et non une simple reprise. Socrate transforme l’héroïsme aristocratique d’Achille (celui d’un demi-dieu, fils d’une déesse) en une vertu démocratique accessible à tout soldat de la phalange hoplitique : il s’agit de tenir son poste, quelle que soit sa place, qu’on l’ait choisie ou qu’on y ait été affecté par son chef (28d). La vertu n’est plus aristocratique, elle est civique ; elle n’est plus la propriété d’une élite, elle est accessible à quiconque occupe sa place avec fermeté. Cette démocratisation de la vertu héroïque prépare la transposition qui va suivre. Socrate rappelle d’ailleurs son propre passé militaire. Il a combattu à Potidée (432-429), à Amphipolis (424), et surtout à Délion (424), où son courage fut loué par Alcibiade dans le ''Banquet'' (219e et suivants<ref>Platon, ''Banquet'', 219e-221c.</ref>) et par le général Lachès dans le dialogue du même nom (''Lachès'', 181a-b<ref>Platon, ''Lachès'', 181a-b.</ref>). Comme ces soldats qui ne quittent pas leur poste, Socrate ne peut quitter celui qui lui a été assigné, par le dieu. ===== Le poste assigné par le dieu (28d-29a) ===== Socrate pose alors la transposition capitale : <blockquote>le poste qu’on m’a assigné, moi, est celui du philosophe, qui doit vivre en philosophant, en s’examinant soi-même et en examinant les autres. Je ne peux le quitter par crainte de la mort, pas plus qu’un soldat ne peut quitter le sien.</blockquote> La philosophie est ainsi présentée comme une assignation divine, équivalente à l’ordre d’un chef de guerre, et plus impérieuse encore, puisque l’ordre vient du dieu. Cette analogie entre vie philosophique et vie militaire, qui fera carrière dans toute la tradition stoïcienne (Sénèque, Épictète, Marc Aurèle en useront abondamment<ref>Voir notamment Épictète, ''Entretiens'', I, 9, 24 ; III, 24, 31-36 ; Marc Aurèle, ''Pensées'', III, 5 ; VII, 45.</ref>), fonde la philosophie comme service, comme ''officium'', comme devoir qu’on ne peut déserter sans se déshonorer. ===== L’ignorance de la mort (29a-b) ===== Vient alors l’un des passages les plus célèbres du texte, où Socrate renverse la psychologie commune du courage : <blockquote>Craindre la mort, Athéniens, ce n’est rien d’autre que se donner pour savant sans l’être ; c’est donner l’impression qu’on sait ce qu’on ne sait pas. (29a)</blockquote> Car personne ne sait ce qu’est la mort, ni si elle n’est pas pour l’homme le plus grand des biens ; mais on la redoute comme si l’on savait qu’elle est le plus grand des maux. C’est là, dit Socrate, la forme la plus répréhensible d’ignorance : croire savoir ce qu’on ne sait pas, donc la même erreur que celle des faux sages qu’il a démasqués dans son enquête. Le raisonnement est d’une rigueur remarquable. Il articule le savoir du non-savoir à l’éthique du courage. Socrate, lui, sait qu’il ne sait rien de la mort ; il ne la craint donc pas. Mais il sait en revanche, et c’est la seule « exception » à son ignorance proclamée, que <blockquote>commettre une injustice et désobéir à un meilleur que soi, dieu ou homme, cela je sais que c’est mauvais et honteux. (29b)</blockquote> On voit ici le dispositif éthique qui va commander toute la suite : entre un mal certain (l’injustice et la lâcheté) et un mal supposé mais incertain (la mort), le sage choisit sans hésiter d’éviter le premier. Le courage philosophique n’est donc pas un mépris enthousiaste de la mort, comme celui d’Achille, c’est une lucidité sur ce qui est réellement à craindre. Ce renversement de la psychologie héroïque en lucidité rationnelle est l’un des gestes fondateurs de la philosophie morale. ===== L’hypothèse de l’acquittement conditionnel (29c-30c) ===== Socrate imagine alors une situation extrême, une sorte d’expérience de pensée. Supposons que les juges lui offrent de l’acquitter à la condition qu’il cesse de philosopher. Alors Socrate répondrait : <blockquote>Athéniens, je vous suis reconnaissant et je vous aime, mais j’obéirai au dieu plutôt qu’à vous ; et tant qu’il me restera un souffle de vie, tant que j’en serai capable, je ne cesserai, soyez-en sûrs, de philosopher, de vous exhorter et de m’expliquer avec tel ou tel d’entre vous. (29d)</blockquote> Il continuerait à dire à chacun la formule qui résume toute sa mission : <blockquote>Ô excellent homme, toi qui es d’Athènes, la cité la plus grande et la plus réputée pour son savoir et sa puissance, tu n’as pas honte de t’occuper de ta fortune et des moyens de t’enrichir le plus possible, de ta réputation, des honneurs, alors que de ton intelligence, de la vérité, de ton âme et des moyens de la perfectionner, tu ne t’en occupes et ne t’en soucies aucunement ? (29d-e)</blockquote> On est ici au cœur du message socratique, tel que Platon l’a consigné. Le renversement qu’opère ce passage est philosophiquement majeur. D’une part, Socrate affirme que son obéissance au dieu l’emporte sur son obéissance à la cité : c’est, en puissance, toute la doctrine de la désobéissance civile au nom d’une norme transcendante. D’autre part, il renverse la hiérarchie des biens : la vertu ne vient pas de l’argent, mais l’argent et tous les autres biens viennent de la vertu ; il faut donc se soucier prioritairement de son âme (''psuchḗ''), et non de ses biens matériels ou de sa réputation. L’''Apologie'' est ainsi, dans la philosophie occidentale, l’un des textes où s’origine ce thème du souci de soi (''epiméleia heautoû'') compris comme soin de l’âme et examen permanent de soi-même, thème qui parcourra toute la tradition ultérieure, des écoles hellénistiques aux spirituels chrétiens, jusqu’aux lectures contemporaines de Michel Foucault qui en fera un objet majeur de ses derniers cours au Collège de France<ref name="foucault-hs">Michel Foucault, ''L’Herméneutique du sujet. Cours au Collège de France, 1981-1982'', éd. F. Gros, Paris, Gallimard/Seuil, coll. « Hautes Études », 2001, en particulier les leçons des 6, 13 et 20 janvier 1982.</ref>. Socrate ajoute alors l’une de ses déclarations les plus provocatrices : <blockquote>Là-dessus, Athéniens, croyez-en ou non Anytos, acquittez-moi ou ne m’acquittez pas, toujours est-il que je ne changerai pas de conduite, même si je devais souffrir mille morts. (30c)</blockquote> La défense ne demande plus un acquittement ; elle proclame la pérennité de la mission, quel que soit le verdict. Socrate, à ce moment précis du plaidoyer, cesse d’être un accusé pour devenir un apôtre. Cette attitude explique, rétrospectivement, l’incompréhension et l’irritation des jurés : il ne se défend pas, il les défie. ==== Socrate, « taon de la cité » (30c-31c) ==== Socrate affirme alors, avec une audace étonnante, <blockquote>si vous me condamnez à mort, ce n’est pas à moi, mais à vous-mêmes, que vous ferez le plus de tort. (30c)</blockquote> Car ni Mélétos ni Anytos ne peuvent le léser véritablement : ils peuvent le tuer, l’exiler, le priver de ses droits civiques (''atimía''), choses que certains tiendraient pour de grands malheurs, mais lui ne les tient pas pour tels. Le vrai mal est celui que font à leur âme ceux qui entreprennent de tuer injustement. Cette thèse, que Platon développera dans le ''Gorgias'' (469c) sous la forme « il vaut mieux subir l’injustice que la commettre »<ref>Platon, ''Gorgias'', 469c : « [...] je choisirais de subir plutôt que de commettre l’injustice. » Voir aussi 474b-479e.</ref>, est probablement la plus radicale de toutes les thèses morales de l’antiquité. Surgit alors la célèbre image du taon. Socrate est <blockquote>un homme attaché à la cité par le dieu, comme le serait un taon au flanc d’un cheval de grande taille et de bonne race, mais qui se montrerait un peu mou en raison même de sa taille et qui aurait besoin d’être réveillé par l’insecte. (30e)</blockquote> Athènes est ce cheval noble mais assoupi ; Socrate est l’insecte qui le pique, le réveille, le harcèle. Le dieu lui a donné cette mission, qui explique qu’il passe son temps à aborder chacun, « comme un père ou un frère plus âgé », pour le persuader d’avoir souci de la vertu. Cette image mérite qu’on s’y arrête, tant elle est dense. Elle articule trois éléments. D’abord, la noblesse du cheval : Athènes n’est pas critiquée absolument, mais reconnue pour ce qu’elle est, la plus belle cité du monde grec, de « grande taille et de bonne race ». Ensuite, son engourdissement : cette grandeur même la rend molle, somnolente, incapable de s’ébrouer spontanément. Enfin, la nécessité du taon : seule une figure dérangeante, insupportable, inutile en apparence, peut réveiller la cité. Le taon n’est pas à sa place dans le cheval ; il est un corps étranger, irritant ; mais précisément, c’est de cette position dérangeante que vient son utilité. La philosophie est pensée ici comme critique nécessaire, comme dissidence féconde, comme décalage qui maintient la cité vivante. On voit se dessiner une dialectique subtile. Socrate est indissociablement dedans et dehors : citoyen d’Athènes, engagé dans sa cité, respectueux de ses lois au point d’accepter la mort plutôt que de fuir (comme l’exprimera le ''Criton''<ref>Platon, ''Criton'', 50a-54d.</ref>), et en même temps étranger à ses conformismes, à ses illusions, à ses complaisances. Sans le taon, le cheval dormirait ; mais le cheval peut aussi, d’un mouvement irrité, écraser le taon. C’est exactement ce qui se passe au procès. L’image contient en elle-même une prophétie : tuer le taon, c’est se priver de la piqûre bienfaisante, condamner la cité au sommeil. D’où la prédiction de Socrate : <blockquote>en suite de quoi, vous passeriez votre vie à dormir, à moins que le dieu, ayant souci de vous, ne vous envoie quelqu’un d’autre. (31a)</blockquote> La suite de l’histoire, à tout le moins celle de la pensée, dira que cet autre sera Platon lui-même, puis Aristote, et la longue lignée des philosophes que l’''Apologie'' aura rendus possibles. Socrate apporte ensuite une preuve empirique de son désintéressement : sa pauvreté. Si son activité avait un but intéressé, si elle rapportait un salaire, on pourrait douter de la pureté de ses motivations. Mais ses accusateurs, malgré leur acharnement, n’ont pu produire aucun témoin attestant qu’il ait jamais exigé ou reçu un salaire. Sa misère est la meilleure preuve qu’il dit vrai. Cette insistance sur la gratuité de son enseignement est une pique adressée aux sophistes, qui se faisaient richement rémunérer, et un trait supplémentaire qui distingue la philosophie socratique de la ''téchnē'' marchande des sophistes. Socrate n’est pas un prestataire de services ; il est un serviteur du dieu. ==== Le démon et la prudence politique (31c-32e) ==== Une objection se présente naturellement : si Socrate est ce grand conseiller des particuliers, pourquoi ne monte-t-il pas à la tribune pour conseiller la cité elle-même dans ses assemblées ? La réponse est le fameux passage sur le ''daimónion'' socratique. ===== Qu’est-ce que le démon de Socrate ? (31c-d) ===== Socrate confie aux juges ce qu’il a déjà dit « maintes fois en maints endroits » : <blockquote>il m’advient quelque chose de divin et de démonique (''theîón ti kai daimónion''), une voix intérieure qui, depuis [mon] enfance, [...] chaque fois qu’elle m’advient, me détourne toujours de ce que je me propose de faire, mais jamais ne m’y encourage. (31c-d)</blockquote> Cette voix a trois caractéristiques remarquables. Elle est toujours dissuasive : « jamais elle ne m’y encourage ». Elle est personnelle : elle ne s’adresse qu’à Socrate. Elle est présente depuis l’enfance, donc constitutive de son rapport au monde. Elle s’est précisément opposée à son entrée en politique. Il s’agit donc, littéralement, de cette « divinité nouvelle » que l’accusation lui impute, ce que Mélétos, ironise Socrate, a d’ailleurs « consigné dans son acte d’accusation » (31d). Cette ironie est cinglante : l’accusation a pris pour un crime ce que Socrate revendique comme une grâce. La nature du ''daimónion'' a fait l’objet de multiples interprétations, dès l’Antiquité et jusqu’aux temps modernes. Plutarque a consacré un traité entier à la question (''Du démon de Socrate'') dans lequel il discute plusieurs hypothèses<ref>Plutarque, ''Du démon de Socrate'' (''De genio Socratis''), dans ''Œuvres morales'', t. VIII, trad. J. Hani, Paris, Les Belles Lettres, 1980.</ref>. À l’époque moderne, on l’a interprété tour à tour comme une hallucination d’un névrosé<ref>F. Lélut, ''Du démon de Socrate, spécimen d’une application de la science psychologique à celle de l’histoire'', Paris, Trinquart, 1836.</ref>, comme une manifestation de l’inconscient<ref>Arthur Koestler, ''Le Démon de Socrate'', Paris, Calmann-Lévy, 1970.</ref>, comme la voix de la conscience morale<ref>G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', tome II (sur Socrate), trad. G. Marmasse, Paris, Vrin, 2007, p. 316-321.</ref>, comme une inspiration divine authentique<ref>Henri Bergson, ''Les Deux sources de la morale et de la religion'' (1932), dans ''Œuvres'', éd. du Centenaire, Paris, PUF, 1959, p. 1027.</ref>, comme une intuition irrationnelle<ref>E. R. Dodds, ''Les Grecs et l’irrationnel'' (1951), trad. M. Gibson, Paris, Flammarion, coll. « Champs », 1977, chap. VII.</ref>. Nietzsche y voyait, de manière originale, la monstruosité d’un homme chez qui l’instinct, contrairement à l’ordinaire, ne crée pas mais critique, et chez qui la conscience rationnelle est au contraire créatrice : Socrate comme « homme théorique », rupture dans l’histoire de l’esprit grec dionysiaque<ref name="nietzsche-nt">Friedrich Nietzsche, ''La Naissance de la tragédie'' (1872), § 13-15, trad. P. Lacoue-Labarthe, dans ''Œuvres philosophiques complètes'', t. I, Paris, Gallimard, 1977.</ref>. Il faut probablement admettre, avec Claude Chrétien, que le démon est irréductible à une simple astuce défensive ou à un symbole : Socrate y croyait réellement, au point de risquer sa vie en le suivant<ref>Chrétien, ''op. cit.'', p. 28-29.</ref>. Son caractère purement négatif (il inhibe, ne prescrit jamais) en fait un signe du divin dans la vie humaine, mais un signe essentiellement limitatif : la divinité indique seulement ce qu’il ne faut pas faire, et laisse à l’homme la responsabilité de chercher, par l’examen rationnel, ce qu’il doit faire. Cette structure, où le divin ne donne pas la vérité mais seulement la limite, est cohérente avec la théologie négative que Socrate manifeste dans tout le plaidoyer : nous ne savons pas positivement ce que sont les dieux, mais nous recevons d’eux des signes qui nous empêchent de nous égarer. ===== Pourquoi pas la politique ? (31d-32a) ===== Socrate explique que si le démon l’a détourné de la politique, c’est pour préserver sa vie : « s’il y avait longtemps que j’avais entrepris de faire de la politique, il y a longtemps que je serais mort ». Et il formule alors une sentence vertigineuse, qui est peut-être la plus critique de l’''Apologie'' à l’égard de la démocratie athénienne : <blockquote>il n’y a personne au monde qui puisse garder la vie sauve s’il s’oppose loyalement à vous ou à toute autre collectivité, et s’il cherche à empêcher qu’il ne se produise dans la cité de nombreuses injustices et illégalités. Mais nécessairement tout vrai champion de la justice, s’il veut garder la vie sauve ne serait-ce qu’un peu de temps, doit vivre en simple particulier (''idiōteúein'') mais non en homme public. (32a)</blockquote> Cette sentence est d’une portée immense. Elle signifie que la politique telle qu’elle se pratique à Athènes est incompatible avec la justice. Le juste, s’il veut vivre, doit rester à l’écart des affaires publiques ; et s’il y entre, il doit s’attendre à mourir. C’est la cassure socratique avec la tradition civique grecque, qui voyait dans la participation politique (''politeía'') la plus haute réalisation de l’homme libre. L’homme proprement libre, pour les Grecs classiques, c’est le citoyen qui prend part aux assemblées ; l’''idiṓtēs'' qui se retire dans la sphère privée est, sinon méprisé, du moins considéré comme incomplet. Socrate renverse cette hiérarchie : la vraie vie politique, pour le juste, passe par le retrait de la politique officielle et par une politique privée, celle de la discussion personne à personne, de l’enseignement moral qui opère non par les discours publics mais par l’examen intime de chacun. C’est, comme le suggère la présentation de la collection Flammarion, « l’espace d’une autre politique »<ref name="brisson-mace-presentation">Arnaud Macé, « Présentation », dans Platon, ''Apologie de Socrate'', traduction par Luc Brisson, Paris, GF-Flammarion, 2017.</ref>. Cette thèse, qu’on peut lire comme une désertion civique, est aussi une critique profonde des conditions de la délibération démocratique. Elle rejoint ce que Platon développera dans la ''République'' : la cité idéale est celle où la philosophie serait au pouvoir, non celle où elle est écrasée par le plus grand nombre. Mais l’''Apologie'' n’est pas encore la ''République'' : Socrate n’y propose pas une contre-cité, il y constate seulement qu’aucune cité existante ne permet au juste de participer à ses affaires sans se renier. ===== Les deux épisodes probants (32a-e) ===== Socrate prouve cette thèse par deux épisodes biographiques, choisis avec soin. Le premier se situe sous la démocratie, en 406, l’année du procès des généraux des Arginuses. Les Athéniens avaient remporté une victoire navale importante contre Sparte près des îles Arginuses (au large de Lesbos), mais les généraux victorieux avaient été empêchés par une tempête de ramasser les cadavres et les naufragés athéniens, violation grave des usages religieux. Rentrés à Athènes, ils furent mis en cause ; mais au lieu de leur garantir un procès individuel comme l’exigeait le droit athénien, l’Assemblée, emportée par la colère populaire, voulut les juger en bloc. Socrate siégeait ce jour-là au Conseil, comme prytane pour sa tribu, l’Antiochide. Il fut le seul des cinquante prytanes à refuser de mettre aux voix cette motion collective, malgré les menaces et les cris de la foule<ref>Le récit détaillé du procès des généraux des Arginuses se trouve chez Xénophon, ''Helléniques'', I, 7, 1-35.</ref>. Les orateurs voulaient le faire arrêter sur-le-champ, les citoyens eux-mêmes l’y encourageaient ; il tint bon. Les généraux furent néanmoins jugés et six d’entre eux exécutés. Peu après, Athènes regretta sa décision, mais Socrate avait risqué sa vie pour la légalité, sans succès immédiat. Le second épisode se situe sous l’oligarchie, en 404. Les Trente l’avaient convoqué avec quatre autres citoyens à la Tholos (la rotonde, siège des prytanes occupée par le régime) et lui avaient ordonné d’aller chercher à Salamine un riche citoyen démocrate, Léon, pour qu’il soit exécuté et que ses biens soient confisqués. C’était une manœuvre classique des Trente : compromettre un maximum de citoyens dans leurs crimes pour les rendre solidaires du régime<ref>Xénophon, ''Helléniques'', II, 3, 39 ; Platon, ''Lettre VII'', 324d-325a.</ref>. Socrate, lui, refusa l’ordre. Il rentra simplement chez lui pendant que les quatre autres allaient chercher Léon, qui fut assassiné. Socrate, rappelle-t-il, aurait probablement payé cela de sa vie si les Trente n’avaient pas été renversés peu après (c’était le cas : le régime tomba en 403). Ces deux épisodes sont politiquement remarquables, et Platon les a sans doute choisis avec une intention nette. Ils montrent Socrate s’opposant également aux excès de la démocratie (procès des Arginuses) et à ceux de l’oligarchie (affaire de Léon), par fidélité à une justice supérieure au régime en place. Il n’est ni un démocrate de conviction ni un oligarque : il est un homme qui, dans l’un et l’autre cas, risque sa vie pour ne pas commettre d’injustice. Cette double symétrie est cruciale : elle répond par avance à tous ceux qui, dans la démocratie restaurée de 399, voudraient voir en Socrate un sympathisant des Trente, en raison de ses liens avec Critias et Charmide. Platon montre au contraire que Socrate a résisté aux Trente au péril de sa vie. Mais il montre également qu’il a résisté à la démocratie elle-même, quand celle-ci violait le droit. La neutralité politique de Socrate, ou plutôt cet au-delà du partisanisme, constitue une part de sa radicalité, qui déconcerte tous ceux qui voudraient l’enrôler dans un camp. ==== Socrate et ses « disciples » (33a-34b) ==== Socrate en vient alors au dernier volet du premier discours : la question des jeunes gens qu’on l’accuse d’avoir corrompus. Il récuse d’abord le terme même de « disciple » : <blockquote>je n’ai jamais, moi, été le maître (''didáskalos'') de personne. (33a)</blockquote> Cette affirmation est importante. Elle distingue radicalement Socrate des sophistes, qui se présentaient comme maîtres (''didáskaloi'') et vendaient un enseignement structuré ; Socrate n’a jamais promis un enseignement, jamais fait payer, jamais suivi un programme ; il a parlé à quiconque voulait l’écouter, jeunes et vieux, riches et pauvres, sans distinction, et n’est pas responsable de ce que chacun devient au sortir de la conversation. Cette posture est philosophiquement significative : elle implique que la philosophie n’est pas transmissible comme une technique, mais seulement comme une pratique qu’on ne peut qu’imiter. Socrate produit alors un argument a contrario d’une grande force. Si réellement il avait corrompu les jeunes, pourquoi n’est-ce pas ''eux-mêmes'', devenus adultes, qui viendraient témoigner contre lui ? Ou du moins leurs proches, parents, frères, qui auraient à se plaindre de cette corruption et en seraient les premiers concernés ? Or, bien au contraire, nombre de ses familiers, ou de leurs proches, sont présents à l’audience pour le soutenir. Socrate en énumère plusieurs, nommément, dans un passage qui vaut témoignage historique : Criton et son fils Critobule, Lysanias de Sphettos père d’Eschine (l’auteur de dialogues socratiques), Antiphon père d’Épigène, Nicostratos frère de Théodotos, ainsi qu’Adimante (le frère aîné de Platon) et plusieurs autres<ref>''Apologie'', 33d-34a. Sur ces personnages, voir Debra Nails, ''The People of Plato: A Prosopography of Plato and Other Socratics'', Indianapolis, Hackett, 2002.</ref>. <blockquote>Je pourrais citer pour vous beaucoup d’autres hommes, parmi lesquels il aurait fallu que Mélétos produise, au cours de son discours, quelque témoin. (34a)</blockquote> Cette preuve par les témoins absents est d’une grande force logique : l’accusateur a été incapable de trouver, parmi tous ceux qui auraient dû être les premières victimes, un seul pour le blâmer. C’est ce qu’on appelle un argument ''a silentio'' : le silence des supposées victimes prouve l’innocence de l’accusé. On notera que Platon se fait ici historien. En nommant les disciples présents, il fixe un moment dans le temps et offre à la postérité un témoignage vérifiable. Il se nomme lui-même plus loin (34a, puis 38b), parmi les amis prêts à se porter caution pour l’amende. Cette présence documentaire est rare dans les dialogues platoniciens, où Platon s’efface généralement : ici, il est témoin du procès de son maître. ==== Le refus du pathos (34b-35d) ==== Socrate aborde alors la péroraison de son premier discours. Il sait parfaitement ce qu’on attend d’un accusé athénien au moment crucial : larmes, supplications, présentation de la femme et des enfants éplorés, appel à la pitié, théâtralité de la détresse. Cette mise en scène, connue de tous par les comédies d’Aristophane et par l’habitude des tribunaux, était devenue quasi rituelle<ref>Sur la caricature du tribunal populaire, voir Aristophane, ''Les Guêpes'', v. 548-630.</ref>. Socrate a trois fils, dont l’un est déjà adolescent (''meirákion'') et les deux autres encore petits ; il pourrait les faire paraître, lui qui ne refuse pas d’être un homme. Mais il ne le fera pas. Pourquoi ? Deux motifs convergent. D’abord, par souci de l’honneur : un homme de sa réputation, réelle ou supposée, ne peut s’abaisser à ces scènes sans se ridiculiser et sans « ridiculiser la cité ». Les citoyens étrangers qui l’observent et qui connaissent la réputation d’Athènes s’étonneraient de voir les plus éminents de ses hommes se comporter de manière indigne. Socrate invoque la ''dóxa'' (l’opinion) d’Athènes aux yeux du monde grec pour justifier son refus de jouer le jeu. Mais surtout, et c’est le second motif, plus profond : il ne serait pas juste de supplier le juge. Et ici, Socrate formule une analyse capitale de la fonction judiciaire : <blockquote>le juge ne siège pas pour réduire la justice en faveur (''charízesthai''), mais pour décider de ce qui est juste ; et il a fait serment non de favoriser qui lui plaît, mais de rendre la justice selon les lois. (35c)</blockquote> Le juge athénien prêtait en effet un serment (l’''héliastikós hórkos'') par lequel il s’engageait à juger selon les lois<ref>Sur le serment héliastique, voir Démosthène, ''Contre Timocrate'', 149-151 ; Adriaan Lanni, ''Law and Justice in the Courts of Classical Athens'', Cambridge, Cambridge University Press, 2006, p. 75-76.</ref>. Supplier les juges, c’est leur demander de se parjurer, donc d’introduire le parjure dans la cité, donc de commettre une impiété effective contre les dieux garants du serment. Le geste de Socrate est ici d’une cohérence parfaite et quasi mathématique. Lui qui est accusé d’impiété ne peut, à l’instant critique, demander aux juges de commettre une vraie impiété (le parjure) pour le sauver. Ce serait confirmer dans les faits, activement, l’accusation qu’il réfute en paroles. Il préfère la mort à cet abaissement, qui serait en outre, non plus imaginairement mais réellement, une atteinte aux dieux de la cité. Par ce refus, Socrate démontre par les actes ce qu’il prétendait par les mots : il est le véritable pieux, et ce sont les accusateurs qui, en voulant la mort d’un juste, pratiquent la vraie impiété. Le premier discours s’achève là. Les juges votent. Socrate est déclaré coupable. La majorité est étroite : si trente voix s’étaient portées sur l’autre bord, Socrate aurait été acquitté. Pour un jury de 501, cela suggère un vote d’environ 280 contre 221 (chiffres que Diogène Laërce confirme dans son récit<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 41.</ref>, mais qui ne sont pas dans Platon). Ce qui est frappant, c’est la relative faiblesse de la condamnation : l’acquittement était à portée. === Le second discours : la contre-peine (35e-38b) === La procédure athénienne exige maintenant que Socrate propose une peine alternative à celle réclamée par l’accusation. Mélétos a proposé la mort. L’usage voulait qu’on proposât une peine sensiblement moins sévère (un lourd exil, une amende importante) pour donner au jury une alternative crédible. Le calcul tacite, dans les procès à peine à estimer, consistait à offrir une sanction à peine en deçà de celle demandée par l’accusateur, de manière à ne pas trop décevoir les attentes du jury tout en se ménageant un sort moins dur. Socrate va adopter une tout autre stratégie. ==== La contingence du vote (35e-36b) ==== Socrate remarque d’abord, avec une ironie qui frise la provocation, qu’il est étonné non pas d’avoir été condamné, mais de l’avoir été à une si faible majorité. Il s’attendait à une condamnation bien plus nette. Si trente voix de plus avaient basculé, il aurait été acquitté. Il observe aussi que, ce qui le perd véritablement, ce n’est pas Mélétos : car sans l’appui d’Anytos et de Lycon, le seul Mélétos, compte tenu des voix obtenues, aurait dû payer mille drachmes d’amende pour n’avoir pas recueilli le cinquième des suffrages (règle destinée à décourager les plaintes frivoles). En divisant malicieusement les voix reçues entre ses trois accusateurs, Socrate montre que Mélétos seul n’aurait pas obtenu sa condamnation. Cette remarque, en apparence technique, est profondément déstabilisante : elle montre que la procédure qui vient de condamner Socrate est elle-même contingente, dépendante du nombre d’accusateurs autant que du fond du dossier. Elle suggère, au passage, que la véritable force du parti de l’accusation réside dans Anytos, l’homme politique, non dans Mélétos, le plaignant nominal. Le procès apparaît donc comme un montage politique, sous un habillage religieux. ==== La proposition du Prytanée (36b-37a) ==== Quelle contre-peine proposer ? Socrate prend la question au sérieux, mais dans un sens retourné : quelle peine mérité-je ? Il rappelle toute sa vie : avoir négligé les affaires, l’argent, les magistratures, les assemblées et les honneurs, pour se consacrer au service privé de la vertu, <blockquote>en essayant de convaincre chacun d’entre vous de ne pas se préoccuper de ses affaires personnelles avant de se préoccuper, pour lui-même, de la façon de devenir le meilleur et le plus sensé possible. (36c)</blockquote> Que mérite un homme ainsi ? Un bon traitement, dit-il, et non une peine. Il propose donc non pas un châtiment, mais une récompense : être nourri aux frais de l’État au Prytanée. Le Prytanée était à Athènes l’édifice public où la cité nourrissait, aux frais de l’État, les prytanes en exercice, les hôtes officiels et les citoyens illustres, notamment les vainqueurs des jeux Olympiques et les bienfaiteurs de la patrie. C’était la plus haute distinction civique, l’équivalent d’une reconnaissance par l’État comme héros ou sauveur de la cité. Socrate explique qu’il la mérite plus que quiconque : <blockquote>si celui-ci [le vainqueur olympique] vous procure l’apparence du bonheur, je vous en offre, moi, la réalité ; lui n’a aucun besoin d’être nourri, mais moi, j’en ai besoin. (36d-e)</blockquote> L’argument est double : Socrate est un bienfaiteur réel (plus que le champion olympique, dont la gloire n’est que sportive) et il est pauvre (donc il a besoin de ce soutien alimentaire, alors que le champion en a moins besoin). Cette proposition est manifestement provocatrice, et aucun commentateur n’en doute. Socrate ne joue plus le jeu de la procédure ; il retourne le tribunal. Pour un jury qui vient de le condamner, proposer d’être traité en héros civique est une humiliation délibérée. Comment comprendre cette audace ? Plusieurs explications se combinent. D’abord, une cohérence logique : Socrate est convaincu de n’avoir commis aucune injustice, il refuse donc de s’infliger une peine comme s’il était coupable. Ensuite, une fidélité à sa parole : s’il proposait une peine qu’il estime injuste, il trahirait son principe d’agir toujours selon le juste. Enfin, peut-être, une acceptation anticipée de la mort : il est âgé (soixante-dix ans, espérance de vie largement dépassée dans l’Antiquité), il a accompli sa mission, il ne craint pas la sentence, il n’a donc aucune raison de ruser. À quoi s’ajoute, plus subtilement, un calcul dramatique : proposer une vraie contre-peine reviendrait à reconnaître la compétence du tribunal ; en proposant une récompense, Socrate refuse symboliquement la sentence avant même qu’elle ne tombe. ==== L’examen des autres peines et la proposition d’amende (37a-38b) ==== Socrate continue son raisonnement avec une rigueur didactique : puisque je sais que je ne me cause volontairement de tort à personne, je ne vais pas, loin de là, m’en causer à moi-même en proposant une peine qui serait un tort. Il passe alors en revue, méthodiquement, les peines possibles. La prison ? Ce serait passer sa vie soumis au pouvoir des Onze, les magistrats qui administraient les prisons et les exécutions, donc à une sujétion indigne. Une amende lourde assortie de contrainte par corps jusqu’au paiement ? Cela revient au même : il n’a pas d’argent. L’exil ? C’est ici que Socrate développe sa réponse la plus fine. Il serait absurde, dit-il, de choisir l’exil : si ses propres concitoyens ne supportent plus ses entretiens, au point d’en vouloir « se débarrasser », pourquoi les étrangers les supporteraient-ils davantage ? Il serait chassé de ville en ville. Et ne pourrait-il vivre en se taisant ? Non, et ici vient l’un des sommets du texte : cesser de philosopher équivaudrait à désobéir au dieu. <blockquote>Il n’y a pas pour un homme de plus grand bien que de s’entretenir chaque jour de la vertu et des autres sujets dont vous m’entendez discuter, en examinant moi-même les autres ; car une vie sans examen n’est pas digne d’être vécue par un homme. (38a)</blockquote> Cette dernière formule, en grec ''ho anéxetastos bíos ou biōtós anthrṓpōi'', est probablement la plus célèbre de toute l’''Apologie'' et peut-être de toute la philosophie antique. Elle condense le programme de la philosophie socratique : la vie doit être soumise à un examen (''exétasis'') constant, à une interrogation rationnelle sur ses buts et ses valeurs. Sans cet examen, on ne vit pas une vie proprement humaine. Formule vertigineuse, qui fait de la philosophie non pas un luxe intellectuel mais la condition même d’une existence digne de ce nom. Elle suggère qu’il existe un seuil d’humanité : en deçà de l’examen, l’homme n’est pas pleinement homme. La philosophie cesse alors d’être une option ajoutée à la vie pour devenir l’élément qui en fait une vie humaine. Finalement, Socrate concède une mine d’argent (somme modique, correspondant à peu près à trois mois de salaire d’un ouvrier qualifié), mais accepte, sur la pression de ses amis (Platon, Criton, Critobule, Apollodore), de proposer trente mines, avec leur caution personnelle. C’est une somme importante, équivalent à plusieurs années de salaire, mais manifestement insuffisante face à la proposition de mort, et la manière dont elle est introduite (sous la pression des amis, comme en dernier recours) ne masque pas que Socrate lui-même n’y adhère pas vraiment. Le jury vote une seconde fois. Cette fois, la majorité est beaucoup plus nette : selon les chiffres traditionnels rapportés par Diogène Laërce, quatre-vingts jurés supplémentaires ont voté contre Socrate par rapport au premier vote, manifestement indignés par son attitude. Socrate est condamné à mort. === Le troisième discours : après la condamnation (38c-42a) === Ce troisième discours n’est pas prévu par la procédure. Une fois la sentence prononcée, les magistrats passent aux formalités d’enregistrement, notamment la notification aux Onze, chefs des geôliers et du bourreau. Socrate prend pourtant la parole une dernière fois, sans doute pendant que les Onze procèdent à ces écritures, pour s’adresser à la partie de l’assistance qui est restée sur place. Il s’agit d’une péroraison spontanée, hors procédure, probablement élargie et composée par Platon pour les besoins du texte, même si l’événement lui-même est plausible. Ce discours se divise en deux moments : d’abord aux jurés qui ont voté sa mort, puis à ceux qui ont voté son acquittement. ==== Aux jurés de la condamnation (38c-39d) ==== Socrate commence par un constat ironique : les Athéniens ont gagné peu de temps, il est âgé, il serait mort bientôt de toute façon. Mais en échange, ils vont gagner <blockquote>le renom, auprès des gens avides de diffamer notre cité, d’avoir fait mourir un sage en la personne de Socrate. (38c)</blockquote> La réputation d’Athènes souffrira de ce procès bien au-delà de ce qu’elle a cru gagner. La prédiction est parfaitement exacte : la condamnation de Socrate a, dans la mémoire collective de l’Occident, sérieusement terni l’image de la démocratie athénienne. Surtout, Socrate retourne contre les juges l’argument central de son plaidoyer : s’il n’a pas réussi à les persuader, ce n’est pas faute d’arguments, mais parce qu’il n’a pas voulu employer les tactiques indignes (larmes, supplications) qu’ils attendaient. <blockquote>Je préfère de beaucoup mourir après m’être défendu comme je l’ai fait plutôt que vivre après un plaidoyer à leur façon. (38e)</blockquote> Et il prononce cette antithèse fameuse : <blockquote>la difficulté n’est pas d’échapper à la mort, elle est bien plus d’échapper à la lâcheté (''ponēría''), car elle court plus vite que la mort. En l’occurrence, moi qui suis lent et vieux, j’ai été rattrapé par la plus lente des deux [la mort], cependant que mes accusateurs, qui sont lestes et rapides, ont été rattrapés par la plus rapide, qui est la méchanceté. (39a-b)</blockquote> L’image est saisissante : chaque homme est poursuivi par son destin, mais les uns sont rattrapés par la mort physique et les autres par la mort morale, infiniment plus grave. Socrate se permet alors une prophétie (''manteúomai''). Il est, dit-il, <blockquote>au point où les hommes prophétisent le mieux, quand ils sont à la veille de mourir, (39c)</blockquote> allusion à la croyance grecque selon laquelle les mourants acquièrent un don de divination (voir le motif du chant du cygne dans le ''Phédon'' 84e<ref>Platon, ''Phédon'', 84e-85b : « les cygnes [...], lorsqu’ils sentent qu’ils vont mourir, chantent ce jour-là plus fort et plus beau qu’ils n’ont jamais chanté, dans la joie d’aller trouver le dieu dont ils sont les serviteurs ».</ref>). Sa prophétie est terrible : <blockquote>un châtiment vous viendra aussitôt après ma mort, bien plus pénible que celui par lequel vous m’aurez tué. [...] Le nombre croîtra de ceux qui vous demanderont des comptes, que je retenais jusqu’ici, sans que vous vous en aperceviez ; et ils seront d’autant plus pénibles qu’ils sont plus jeunes. (39c-d)</blockquote> Tuer n’est pas la façon de se délivrer du blâme ; la seule manière honorable est « de se préparer soi-même à être le meilleur possible ». Cette prophétie peut sembler s’accomplir, dans une certaine mesure, par l’histoire qui suivra : les « petits socratiques » (Antisthène le cynique, Aristippe, Euclide de Mégare), puis Platon et son Académie, puis Aristote et le Lycée, poursuivront inlassablement l’interrogation commencée par Socrate. Le procès et la mort de Socrate peuvent ainsi être lus comme le moment à partir duquel la philosophie s’affirme, dans la tradition platonicienne, comme une vocation publique à part entière. ==== Aux jurés de l’acquittement (39e-42a) ==== Socrate se tourne alors vers ceux qui ont voté pour lui, qu’il appelle désormais, seuls, ''juges'' véritables. Cette distinction terminologique est capitale : avant le verdict, tous étaient indistinctement ''andres'' (« messieurs ») ou ''Athēnaîoi'' (« Athéniens ») ; maintenant, seuls ceux qui ont voté juste méritent, selon Socrate, le titre de juges (''dikastaí''). Le tribunal vient d’être scindé en deux catégories asymétriques. Il leur confie deux pensées, l’une sur le signe divin, l’autre sur la mort. ===== Le silence du démon (39e-40c) ===== Son démon, dit-il, avait coutume de l’arrêter chaque fois qu’il s’apprêtait à faire quelque chose de mauvais, même dans des circonstances mineures. Or, aujourd’hui, depuis le matin, tout au long de cette journée qui l’a mené à la condamnation à mort, le démon ne s’est pas manifesté une seule fois. Il ne l’a pas arrêté au moment où il quittait son domicile, ni lorsqu’il montait au tribunal, ni à aucun moment de son plaidoyer. Cette absence est elle-même un signe : <blockquote>il y a des chances pour que ce qui m’est arrivé soit un bien ; et c’est nous qui faisons des suppositions incorrectes quand nous considérons la mort comme un mal. (40b-c)</blockquote> Le silence du démon est la preuve, pour Socrate, que la voie qu’il suit est la bonne. L’argument est philosophiquement subtil. Socrate ne dit pas qu’il sait que la mort est un bien ; il dit qu’il a une raison de penser que ce qui lui arrive est un bien, puisque le dieu (par la voix du démon) ne l’a pas arrêté. C’est un argument ''e silentio'' transposé sur le plan religieux : le silence divin, pour qui est habitué à être averti, vaut approbation. Cette structure de raisonnement est fragile, mais elle est cohérente avec la théologie socratique : la divinité se manifeste par ses interventions plutôt que par ses paroles positives ; son silence, quand il y a habitude d’intervention, est significatif. ===== Les deux hypothèses sur la mort (40c-41d) ===== Socrate propose alors une méditation sur la mort, l’une des plus belles pages de la philosophie antique, construite comme une alternative raisonnée. De deux choses l’une : ou bien la mort est l’absence de toute sensation, ou bien elle est une migration (''metoíkēsis'') de l’âme de ce lieu vers un autre lieu. Dans la première hypothèse (la mort comme absence de sensation), la mort ressemble au sommeil sans rêve. Or qui n’aimerait pas, si on lui demandait de choisir entre une nuit de sommeil profond sans rêves et toutes les autres nuits et jours de sa vie, reconnaître que cette nuit est plus précieuse que la plupart ? Même le Grand Roi de Perse (homme réputé le plus heureux du monde aux yeux des Grecs) trouverait peu de jours comparables à une telle nuit. Si la mort est cela, alors la totalité du temps après la mort se réduit à « une seule nuit », et cette nuit est un gain. L’argument est intéressant par sa structure : il part d’une expérience commune (le sommeil sans rêve) pour désamorcer la peur métaphysique de la mort. Si l’on aime le sommeil quand il nous prend, pourquoi craindre la mort si elle lui ressemble ? La stratégie argumentative, qui rappelle celle d’Épicure et de Lucrèce plus tard (« la mort n’est rien pour nous »<ref>Épicure, ''Lettre à Ménécée'', § 125 : « la mort n’est rien pour nous, puisque tant que nous sommes, la mort n’est pas, et quand la mort est là, nous ne sommes plus ». Lucrèce, ''De la nature des choses'', III, v. 830 et suiv.</ref>), désarticule la crainte en la confrontant à ce que nous expérimentons quotidiennement. Dans la seconde hypothèse (la mort comme migration), la mort est un voyage vers l’au-delà, où l’on retrouve tous les morts. Que pourrait-on imaginer de plus heureux ? On serait délivré des juges qui prétendent juger ici-bas, pour rencontrer les vrais juges dont on dit qu’ils y rendent la justice : Minos, Rhadamante, Éaque (les trois juges infernaux traditionnels), plus Triptolème (qui les remplace parfois dans l’iconographie attique). On rencontrerait Orphée, Musée, Hésiode, Homère, les grandes figures poétiques et religieuses du passé. On pourrait y continuer, sans cette fois risquer la mort, l’activité d’examen qui fut la sienne sur terre : interroger Palamède, Ajax (tous deux morts par jugements injustes, comme lui-même : la comparaison est évidemment à son avantage), le chef de l’armée grecque à Troie (Agamemnon), Ulysse, Sisyphe, « et tant d’autres hommes et femmes qu’on pourrait nommer ». <blockquote>Discuter avec ceux de là-bas, vivre en leur société, les soumettre à examen, ne serait-ce pas le comble du bonheur ? Aussi bien, les gens de là-bas ne mettent à mort personne pour ce motif. (41c)</blockquote> Ce passage est riche de tonalités. Il y a une évidente dimension humoristique : Socrate imagine l’au-delà comme la continuation indéfinie de son activité terrestre, l’examen dialectique, mais cette fois sans risque, puisqu’on n’y meurt plus. L’Hadès devient une agora élargie à tous les temps. Il y a également une dimension consolatoire : la comparaison avec Palamède et Ajax, héros victimes de procès injustes, ennoblit le sort de Socrate. Il y a enfin une dimension ironique vis-à-vis des jurés athéniens : les vrais juges ne sont pas à Athènes, mais dans l’Hadès, et Socrate se réjouit d’aller les retrouver. Ce passage a suscité des interprétations contrastées. Certains commentateurs y voient une réelle espérance platonicienne en l’immortalité de l’âme, telle qu’elle sera développée dans le ''Phédon''<ref>Platon, ''Phédon'', 80a-84b, 105c-107a.</ref>. D’autres, comme Chrétien, soulignent que le Socrate de l’''Apologie'' reste fondamentalement agnostique : il présente deux hypothèses, il ne tranche pas entre elles, et l’imagination y a au moins autant de part que la raison<ref>Chrétien, ''op. cit.'', p. 32-36.</ref>. Socrate lui-même conclut prudemment : <blockquote>aucun mal ne peut toucher un homme de bien ni pendant sa vie ni après sa mort, et les dieux ne se désintéressent pas de son sort. (41d)</blockquote> Cette conclusion n’affirme pas dogmatiquement une survie, mais énonce une foi pratique : quoi qu’il arrive, le juste n’a rien à craindre. L’''Apologie'', contrairement au ''Phédon'', ne construit pas de doctrine positive sur l’immortalité ; elle se tient au seuil d’une telle doctrine, dans un agnosticisme serein. ===== Le testament (41e-42a) ===== Socrate clôt son discours par un testament pour ses fils. Il ne demande qu’une chose aux Athéniens : <blockquote>Quand mes fils seront grands, punissez-les, citoyens, en les tourmentant comme je vous tourmentais, pour peu qu’ils vous paraissent se soucier d’argent ou de n’importe quoi d’autre plus que de la vertu. Et, s’ils croient être quelque chose, alors qu’ils ne sont rien, adressez-leur le reproche que je vous adressais. (41e)</blockquote> La mission philosophique se transmet ainsi, comme un héritage inversé : Socrate demande à ses bourreaux de devenir eux-mêmes, envers ses enfants, ce qu’il était pour eux, des tourmenteurs par la vertu. C’est le pardon actif d’un homme qui refuse de laisser sa mort briser la chaîne de l’examen. Puis vient la dernière phrase, l’une des plus célèbres de la littérature philosophique : <blockquote>Mais voici déjà l’heure de partir, moi pour mourir et vous pour vivre. De mon sort ou du vôtre lequel est le meilleur ? La réponse reste incertaine pour tout le monde, sauf pour la divinité. (''plḕn hē tôi theôi'', 42a)</blockquote> Cette clôture ouvre, sur l’indécidable, l’agnosticisme ultime. Socrate ne sait pas, nul ne sait, lequel est le plus heureux, de lui qui va mourir ou de ses juges qui vont continuer à vivre. Seule la divinité le sait. L’''Apologie'' se ferme ainsi sur le mot même de la sagesse socratique : la reconnaissance de l’ignorance humaine, couplée à la confiance paisible en un ordre divin qui excède notre mesure. Rien n’est affirmé dogmatiquement ; tout se termine sur une interrogation qui n’attend pas de réponse humaine. C’est une fin d’une sobriété admirable, qui évite à la fois la plainte et la consolation artificielle. Elle est, par son rythme et par son contenu, digne d’une page d’évangile ou d’un chapitre des ''Pensées'' de Marc-Aurèle. == Concepts et thèmes majeurs == Une lecture suivie ne serait pas complète sans reprendre quelques-uns des grands thèmes qui courent à travers le texte et en font la portée philosophique durable. === La sagesse humaine : le savoir du non-savoir === Le concept central de l’''Apologie'' est celui de la sagesse humaine (''anthrōpínē sophía'', 20d). Socrate n’est pas savant au sens fort du terme, c’est-à-dire à la manière des sophistes qui prétendaient posséder un savoir sur les choses divines, sur la nature, sur la politique, sur la vertu. Mais il possède une sagesse proprement humaine, qui consiste à reconnaître que l’on ne sait pas. Ce savoir du non-savoir n’est pas un scepticisme, encore moins un renoncement. C’est une position éthique : celui qui sait qu’il ne sait pas est disposé à chercher, à interroger, à examiner ; celui qui croit savoir est fermé à toute remise en question et, par là même, incapable de tout progrès. Cette sagesse n’est pourtant pas purement négative. Socrate affirme savoir au moins deux choses : qu’il est mauvais et laid de commettre l’injustice et de désobéir à un meilleur que soi (29b), et que la vie non examinée n’est pas digne d’être vécue (38a). De ces deux « savoirs » découle tout le comportement de Socrate au procès : il ne peut trahir la justice pour sauver sa vie, et il ne peut cesser d’examiner les autres sans trahir sa vocation propre. On voit donc que le savoir du non-savoir n’est pas une position d’ignorance totale, mais une structure articulée : une ignorance avouée sur les grandes questions métaphysiques, et une certitude pratique sur les exigences éthiques. Cette combinaison fait du socratisme une forme de philosophie pratique : elle rend possible l’action juste sans requérir une science achevée. Il faut enfin noter que le savoir du non-savoir est peut-être la thèse la plus féconde du socratisme pour l’histoire de la pensée. Toute la philosophie postérieure, chaque fois qu’elle commence par un doute méthodique (Descartes), par une critique des prétentions de la raison (Kant), par une déconstruction des évidences (phénoménologie), s’inscrit dans le sillage de l’interrogation socratique. La philosophie moderne est, en ce sens, socratique par son geste inaugural, même quand elle ne l’est plus par ses conclusions. === La méthode de l’elenchus === L’''Apologie'' donne à voir, en acte, la méthode philosophique propre de Socrate : l’elenchus ou réfutation par interrogation. Elle apparaît à plusieurs reprises, mais surtout dans l’interrogatoire de Mélétos (24b-28a). Son fonctionnement est simple dans son principe : Socrate ne pose pas directement ses propres thèses ; il prend pour point de départ celles de son interlocuteur, puis, par une série de questions dont chacune requiert une réponse qui semble évidente, il conduit l’interlocuteur à reconnaître des conséquences incompatibles avec d’autres thèses qu’il tient également pour vraies. La contradiction ainsi mise au jour n’est pas celle de Socrate, mais celle de l’interlocuteur avec lui-même. Cette méthode a plusieurs vertus philosophiques. D’abord, elle respecte la liberté de l’interlocuteur : Socrate ne lui impose rien, il l’amène seulement à voir ce qu’il pensait déjà. Ensuite, elle produit un savoir négatif sûr : la réfutation, à défaut de démontrer le vrai, prouve au moins que la thèse examinée est fausse. Enfin, elle a un effet moral : elle introduit chez l’interlocuteur l’expérience de l’''aporía'' (la perplexité), qui est le point de départ possible d’une recherche véritable. L’interlocuteur, déchargé de son illusion de savoir, peut commencer à apprendre. L’elenchus est ainsi, au sens propre, maïeutique : il fait accoucher les esprits. Mais l’elenchus a aussi ses limites, que l’''Apologie'' laisse apercevoir. Il peut blesser l’amour-propre ; il peut créer des haines durables ; il peut donner à ceux qui en sont la cible l’impression d’être humiliés publiquement. Socrate lui-même en témoigne : son enquête a suscité contre lui des rancœurs innombrables. La philosophie a un coût social, que l’elenchus rend visible avec une particulière acuité. === Le souci de l’âme === Le message moral central de l’''Apologie'' tient dans une exhortation : il faut se soucier prioritairement de son âme (''psuchḗ'') et de son amélioration, non de son corps, de sa richesse ou de sa réputation (29d-30b). <blockquote>La vertu ne naît pas de l’argent, mais c’est de la vertu que naissent et l’argent et tout le reste des biens utiles aux hommes, aussi bien privés que publics. (30b)</blockquote> Ce renversement est l’une des sources principales de la morale occidentale : la hiérarchie des biens est réordonnée autour du bien intérieur, et la richesse extérieure n’a de valeur qu’en tant qu’elle découle d’une vertu préalable. Ce souci de soi (''epiméleia heautoû'') n’est pas égoïste. Il est au contraire la condition du souci des autres : on ne peut aider autrui à améliorer son âme sans avoir travaillé à la sienne. Socrate harcèle ses concitoyens parce qu’il veut qu’ils prennent soin de leur âme, non parce qu’il veut sauver la sienne à ses dépens. Et c’est précisément parce qu’il se soucie de la cité qu’il la secoue : le taon pique le cheval pour le réveiller, non pour le faire souffrir. Cette thèse a eu une postérité immense. Elle est reprise, transformée, intériorisée par les écoles hellénistiques (stoïciens, épicuriens), qui en font le cœur de la sagesse pratique. Elle passe ensuite dans le christianisme, où le « soin de l’âme » devient le salut personnel. À l’époque moderne, Michel Foucault lui a consacré une part importante de ses derniers cours, voyant dans le souci de soi une alternative à l’éthique cartésienne fondée sur la seule connaissance de soi<ref>Michel Foucault, ''Le Souci de soi'' (''Histoire de la sexualité'', t. III), Paris, Gallimard, 1984 ; et ''L’Herméneutique du sujet'', ''op. cit.''</ref>. L’''Apologie'' est à l’origine de cette longue tradition, même si le souci de soi socratique reste, par certains aspects, très différent des élaborations postérieures : il est moins un travail sur soi qu’un examen de soi par la discussion avec autrui. === La philosophie comme mission divine === L’''Apologie'' fonde la légitimité de la philosophie sur une mission divine. Socrate ne philosophe pas par goût ou par choix : il le fait parce que le dieu lui en a fait l’ordre, à travers l’oracle de Delphes et à travers le démon. Cette dimension religieuse est essentielle pour comprendre la posture socratique. S’il pouvait cesser d’interroger, il le ferait peut-être (c’est une activité ingrate et dangereuse) ; mais il ne le peut pas, car ce serait désobéir au dieu. La philosophie est ainsi un service sacré, équivalent à celui des prêtres ou des devins, mais accompli par d’autres moyens : non par le rite, mais par l’examen rationnel. Cette dimension place Socrate dans une position paradoxale par rapport aux accusations d’impiété. L’homme que l’on accuse de ne pas croire aux dieux est en réalité celui qui les sert le plus fidèlement, au point de mourir pour leur obéir. Platon retourne ainsi l’accusation : les véritables impies sont ceux qui, en condamnant Socrate, refusent le présent que le dieu leur a fait. Le procès apparaît alors sous un jour inversé : non plus un acte de piété de la cité contre un impie, mais un acte d’impiété de la cité contre un serviteur du dieu. Ce thème a eu un écho particulier dans la tradition chrétienne, qui a parfois vu en Socrate une figure prophétique du Christ : un juste mis à mort par une communauté religieuse qui croyait servir ses dieux en le tuant. Justin martyr, au IIᵉ siècle, comparera explicitement Socrate et Jésus, voyant dans le philosophe athénien une préfiguration providentielle de la Passion<ref>Justin de Naplouse, ''Première Apologie'', 46, 1-4 : les chrétiens considèrent comme chrétiens avant la lettre « ceux qui ont vécu avec le Logos [...] parmi les Grecs, Socrate, Héraclite et ceux qui leur furent semblables ». Voir aussi ''Seconde Apologie'', 10, 5-6.</ref>. La philosophie chrétienne primitive a ainsi trouvé dans l’''Apologie'' une matrice pour penser la martyrologie. === La justice supérieure === Le thème de la justice traverse tout le texte. Socrate se présente comme un homme profondément respectueux des lois : il a risqué sa vie pour le respect de la procédure sous la démocratie (affaire des Arginuses) ; il mourra par fidélité aux lois dans le ''Criton'' plutôt que de s’évader. Mais son obéissance aux lois n’est pas inconditionnelle : il y a une justice supérieure, fondée sur des valeurs, qui commande dans certains cas la désobéissance civile, comme lorsqu’il refuse d’exécuter les ordres des Trente concernant Léon de Salamine. Cette justice supérieure n’est pas un simple idéal abstrait ; elle est, pour Socrate, ce qui fait le prix (''axía'') de la vie humaine. Elle a une origine divine et s’impose à la conscience au-delà des conventions sociales. On voit déjà poindre ici, avant les développements platoniciens de la ''République'', l’idée d’une justice en soi, distincte de la justice légale, et qui fonde celle-ci sans s’y réduire. Antigone, chez Sophocle, invoquait déjà les « lois non écrites » des dieux contre les décrets humains<ref>Sophocle, ''Antigone'', v. 450-460.</ref> ; Socrate, à sa manière, s’inscrit dans cette tradition. Mais il la rationalise : ce n’est plus le simple respect d’une tradition familiale ou religieuse, c’est la fidélité à un ordre supérieur accessible par l’examen rationnel. La philosophie devient ainsi le lieu où se manifeste cette justice transcendante, dont les lois humaines ne sont qu’une approximation imparfaite. Cette double fidélité (aux lois de la cité et à une justice supérieure) est source d’une tension qui traversera toute la tradition philosophique et juridique occidentale. Elle est au cœur des doctrines du droit naturel<ref>Voir notamment Cicéron, ''De republica'', III, 33 sur la ''lex vera'' ; Thomas d’Aquin, ''Somme théologique'', I-II, q. 94 sur la loi naturelle.</ref>, des théories modernes de la désobéissance civile (Thoreau, Gandhi, Martin Luther King), et des débats contemporains sur la légitimité du droit positif. === La mort et le courage === Le rapport de Socrate à la mort est une pièce maîtresse du texte. Sa thèse est à double face. D’un côté, la mort en elle-même est inconnue : nul ne sait si elle est un mal ou un bien, et la craindre comme un mal certain est la plus répréhensible des ignorances. De l’autre, il y a pire que la mort : la lâcheté, l’injustice, l’abandon de son poste. Le courage socratique n’est donc pas, comme celui des héros homériques, la volonté exaltée de mourir pour l’honneur ; c’est la lucidité sur ce qui est réellement à craindre, non la mort mais le vice. Ce renversement a nourri toute la morale stoïcienne (qui en fait un de ses principes cardinaux : ne rien craindre de ce qui ne dépend pas de nous, donc pas la mort) puis, par des voies détournées, la pensée chrétienne du martyre. Il faut insister sur la finesse de l’analyse socratique. Elle ne dit pas : « la mort est un bien » (affirmation dogmatique contraire à son savoir du non-savoir). Elle ne dit pas non plus : « la mort est indifférente » (comme le diront plus tard les stoïciens). Elle dit : « la mort est inconnue, donc je ne peux la craindre comme un mal certain ; mais l’injustice est un mal certain, donc je peux la craindre ». Le courage n’est pas fondé sur une espérance métaphysique, mais sur une hiérarchie des savoirs : le connu prime sur l’inconnu, et j’organise ma conduite en fonction de ce que je sais. Cette analyse aura un long destin. Épicure la reprendra pour dire : « la mort n’est rien pour nous »<ref>Épicure, ''Lettre à Ménécée'', § 125.</ref>. Les stoïciens la transformeront en doctrine de l’indifférence aux choses externes. Montaigne en fera un objet central de ses ''Essais''<ref>Montaigne, ''Essais'', I, 20 « Que philosopher, c’est apprendre à mourir » ; III, 12 « De la physionomie ».</ref>. Heidegger, au XXᵉ siècle, retournera au Socrate de l’''Apologie'' en interrogeant le rapport authentique à la mort comme condition d’une existence propre<ref>Martin Heidegger, ''Sein und Zeit'' (1927), § 46-53, sur l’''être-pour-la-mort''.</ref>. Chaque fois, c’est le même geste inaugural qui est repris, celui d’une mort désarmée par la pensée. === Philosophie et cité : l’autre politique === L’''Apologie'' esquisse une conception originale de la politique. Socrate se déclare non-politique au sens courant du terme : il n’a pas fréquenté l’assemblée, il n’a pas cherché les magistratures, il n’a pas fait carrière publique. Mais il se présente comme le plus politique des Athéniens au sens profond : il s’est occupé de la cité elle-même (plus que de ses affaires), en se souciant du perfectionnement de ses concitoyens. C’est une autre politique, qui ne passe pas par les institutions officielles (corrompues selon lui par la démagogie et l’ignorance) mais par la conversation privée, par l’interpellation personnelle, par la formation morale. Cette vision a un versant critique radical vis-à-vis de la démocratie athénienne, qui émerge des épisodes des Arginuses et de Léon de Salamine : la démocratie, livrée à ses passions, peut se conduire de manière aussi injuste qu’une tyrannie. Il serait naïf de voir en Socrate un démocrate simple ou un anti-démocrate simple ; il est un critique des deux régimes en tant qu’ils s’éloignent de la justice. Mais sa critique vise, au-delà des régimes, la capacité même des collectivités humaines à délibérer justement : « il n’y a personne au monde qui puisse garder la vie sauve s’il s’oppose loyalement à vous ou à toute autre collectivité » (32a). Ce diagnostic, désabusé, n’est pas tant politique qu’anthropologique : les foules, quelles qu’elles soient, résistent mal à l’examen rationnel. Mais cette critique a aussi un versant constructif. La philosophie, par l’examen qu’elle exerce sur les esprits, prépare la possibilité d’une politique véritablement juste. Platon développera cette intuition dans la ''République'', en allant jusqu’à imaginer une cité où les philosophes seraient rois, mais ce développement excède le cadre de l’''Apologie''. Au moment où Platon écrit ce texte, la cité idéale n’est pas encore pensée ; il y a seulement la dénonciation d’une cité qui a tué son meilleur citoyen, et la promesse implicite d’une pensée qui prolongera la mission interrompue. === L’ironie socratique === Un mot enfin sur l’ironie, qui est omniprésente dans le texte, et qu’il faut distinguer en plusieurs registres. Il y a d’abord l’ironie au sens étroit : dire le contraire de ce que l’on pense, comme lorsque Socrate qualifie Mélétos de « bon citoyen » et « patriote ». Il y a ensuite l’ironie dialectique : amener l’interlocuteur à se contredire lui-même par des questions prétendument naïves, alors que Socrate sait parfaitement où il le mène. Il y a enfin l’ironie existentielle : vivre de telle façon que toute son existence est un démenti de ce qu’on attendrait d’un homme dans sa situation. La proposition d’être nourri au Prytanée relève de cette dernière : Socrate, condamné, se présente comme un bienfaiteur ; il retourne les rôles, fait du tribunal une scène tragicomique. Cette ironie n’est pas seulement un trait de style. Elle est l’expression d’une distance philosophique à l’égard des conventions et des évidences. Celui qui a compris que le savoir commun est illusoire, que la rhétorique est trompeuse, que les hiérarchies sociales reposent sur des malentendus, ne peut plus prendre au sérieux les rituels qui ordonnent la vie ordinaire. L’ironie socratique est le signe visible d’une conscience libre, qui refuse de se soumettre aux attentes. C’est pourquoi Søren Kierkegaard, au XIXᵉ siècle, en a fait dans sa thèse sur ''Le Concept d’ironie'' le trait caractéristique de la subjectivité philosophique naissante : avec Socrate, la conscience se sépare du monde, s’intériorise, devient sujet<ref name="kierkegaard">Søren Kierkegaard, ''Le Concept d’ironie constamment rapporté à Socrate'' (''Om Begrebet Ironi'', 1841), trad. P.-H. Tisseau et E.-M. Jacquet-Tisseau, dans ''Œuvres complètes'', t. II, Paris, Éditions de l’Orante, 1975.</ref>. L’ironie est ce mode de la subjectivité qui se pose en se distinguant de ce qui est. == Questions de lecture et postérité == === Le Socrate de l’''Apologie'' et le Socrate historique === Une question classique est de savoir dans quelle mesure l’''Apologie'' restitue fidèlement le plaidoyer effectivement prononcé par Socrate en 399. Les éléments de réponse sont partiels. D’une part, Platon, jeune témoin du procès (il avait environ vingt-huit ans), avait de puissantes raisons d’être fidèle : la mémoire des jurés qui liraient le texte était fraîche, et toute infidélité flagrante aurait nui à la thèse défensive. D’autre part, Xénophon, dans sa propre ''Apologie'', confirme certains points centraux (l’oracle de Delphes, le refus de préparer sa défense, le rôle du démon, l’attitude provocante devant le tribunal). Les convergences entre Platon et Xénophon, qui écrivent séparément, garantissent la réalité d’un noyau historique. Pour autant, l’''Apologie'' de Platon est une œuvre écrite, composée probablement plusieurs années après le procès, et qui obéit aux lois de la composition littéraire. La structure en trois discours parfaitement articulée, la densité dialectique de l’interrogatoire de Mélétos, la beauté rythmique de certaines périodes (la finale : « moi pour mourir et vous pour vivre… ») relèvent de l’art platonicien. Le témoignage historique et la recréation littéraire ne sont pas dissociables. La question du « Socrate historique » a été débattue à l’infini. On distingue traditionnellement plusieurs Socrate : celui d’Aristophane (caricatural), celui de Xénophon (pragmatique, moraliste de bon sens), celui de Platon (dialectique et idéaliste), celui d’Aristote (prédécesseur des idées, attribuant à Socrate la recherche des définitions universelles et l’induction<ref>Aristote, ''Métaphysique'', XIII, 4, 1078b17-30 : « deux choses peuvent à bon droit être attribuées à Socrate : les raisonnements inductifs et la définition universelle ».</ref>). Lequel est le vrai ? La réponse la plus raisonnable est qu’aucun ne l’est entièrement, mais qu’ils donnent, collectivement, une image composite d’un homme dont la puissance personnelle a dépassé de beaucoup ce que les documents peuvent restituer. L’''Apologie'' est probablement, de tous les textes, celui qui se tient le plus près de la voix réellement entendue, parce que Platon y a choisi, exceptionnellement, de s’effacer devant son maître. Il est usuel, chez les commentateurs, de distinguer dans l’''Apologie'' des couches. Certains éléments semblent historiques au plus haut degré : le cadre procédural, les noms des accusateurs, l’attitude refusant la supplication, la référence à Chéréphon (mort avant le procès, mais dont les héritiers étaient vivants pour confirmer ou démentir), la condamnation et son déroulement. D’autres éléments sont vraisemblablement platoniciens : l’articulation en trois discours parfaitement équilibrés, la méditation finale sur la mort, peut-être la prophétie adressée aux juges condamnateurs. Mais tout cela relève d’appréciations délicates, et Platon lui-même dans la ''Lettre VII'' revendique le droit de penser philosophiquement à partir du Socrate qu’il a connu<ref>Platon, ''Lettre VII'', 324d-326b. Sur la question socratique, voir Louis-André Dorion, ''Socrate'', Paris, PUF, coll. « Que sais-je ? », 2004.</ref>. === La postérité de l’''Apologie'' === L’''Apologie de Socrate'' est l’un des textes les plus lus, les plus imités, les plus commentés de la philosophie occidentale. Sa postérité ne se laisse pas résumer en quelques lignes, mais on peut en indiquer quelques étapes majeures. Dans l’Antiquité, l’''Apologie'' est immédiatement imitée : Xénophon en donne sa version ; des apologies perdues, d’Eschine de Sphettos ou de Lysias, avaient également circulé. Au IIᵉ siècle, Apulée compose une ''Apologie'' (sur son propre procès en magie) qui imite la structure platonicienne<ref>Apulée, ''Apologie ou De la magie'', texte établi et traduit par Paul Vallette, Paris, Les Belles Lettres, 1924.</ref>. Les écoles hellénistiques (stoïciens, cyniques) prennent Socrate comme figure tutélaire du sage qui meurt pour sa vérité : Épictète et Marc Aurèle se réfèrent constamment à lui<ref>Voir notamment Épictète, ''Entretiens'', II, 1, 32 ; II, 2, 8-20 ; et Marc Aurèle, ''Pensées'', VII, 19 ; XI, 25, 28.</ref>. Les cyniques voient en lui le philosophe de la pauvreté et de la provocation, exemple d’une existence libre des conventions. La tradition chrétienne primitive trouve dans Socrate une figure prophétique. Justin martyr, au IIᵉ siècle, fait de Socrate un « chrétien avant le Christ », guidé par le Logos divin<ref>Justin, ''Première Apologie'', 46 ; ''Seconde Apologie'', 10.</ref>. Les Pères de l’Église le mentionnent souvent, tantôt pour s’en réclamer (Clément d’Alexandrie, Origène), tantôt pour le mettre à distance (Tertullien)<ref>Clément d’Alexandrie, ''Stromates'', I, 14 ; Tertullien, ''Apologétique'', 46.</ref>. La mort de Socrate, rapprochée du martyre, fournit un modèle de témoignage pour la vérité. Toute la hagiographie martyrologique se nourrit, en partie, de l’''Apologie''. À la Renaissance, la redécouverte de Platon par Marsile Ficin et l’Académie florentine met l’''Apologie'' au centre du canon philosophique<ref>Marsile Ficin traduit les œuvres complètes de Platon en latin (''Platonis Opera Omnia'', Florence, 1484), rendant l’''Apologie'' accessible à l’Europe savante.</ref>. Montaigne, dans les ''Essais'', lui consacre plusieurs chapitres et voit en Socrate la figure même de la sagesse sans science, du bon sens humain qui vaut mieux que toutes les spéculations. <blockquote>Socrate fait mouvoir son âme d’un mouvement naturel et commun. Ainsi dit un paysan, ainsi dit une femme. (Montaigne, ''Essais'', III, 12)<ref>Montaigne, ''Essais'', III, 12 « De la physionomie », édition Villey-Saulnier, PUF, 1965, p. 1037-1038.</ref></blockquote> À l’époque des Lumières, l’''Apologie'' devient un manifeste anticlérical. Voltaire y voit le procès de l’intolérance religieuse, Diderot une défense de la libre pensée, Rousseau un modèle d’éthique civile. David peint ''La Mort de Socrate'' (1787)<ref>Jacques-Louis David, ''La Mort de Socrate'', 1787, huile sur toile, 129,5 × 196,2 cm, New York, Metropolitan Museum of Art.</ref>, tableau emblématique où le philosophe saisit la coupe avec sérénité tandis que ses disciples se lamentent : image iconique qui influencera durablement l’imaginaire philosophique. Au XIXᵉ siècle, Hegel, Kierkegaard et Nietzsche proposent trois lectures marquantes. Hegel, dans ses ''Leçons sur l’histoire de la philosophie'', voit Socrate comme le moment où la conscience morale s’intériorise, et dans sa condamnation le conflit tragique entre l’ancienne cité et la subjectivité naissante<ref>G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', t. II, trad. P. Garniron, Paris, Vrin, 1971, p. 263-328.</ref>. Kierkegaard fait de l’ironie socratique, analysée dans ''Le Concept d’ironie'' (1841), le modèle de la subjectivité existentielle<ref name="kierkegaard" />. Nietzsche, dans ''La Naissance de la tragédie'' (1872), voit au contraire en Socrate le destructeur du tragique grec, l’homme théorique qui substitue la raison critique à la sagesse instinctive, et accomplit ainsi une « monstruosité par défaut »<ref name="nietzsche-nt" />. Les trois lectures sont opposées, mais elles attestent toutes la centralité de Socrate dans la pensée moderne. Au XXᵉ siècle, l’''Apologie'' continue d’être un lieu de lecture privilégié. Hannah Arendt, dans ''La Vie de l’esprit'', en fait le modèle de la pensée responsable<ref>Hannah Arendt, ''La Vie de l’esprit'', trad. L. Lotringer, Paris, PUF, 1981 ; voir aussi « Philosophie et politique », ''Les Cahiers de philosophie'', n° 4, 1987.</ref>. Michel Foucault, dans ses derniers cours au Collège de France (''Le Courage de la vérité'', 1984), y voit le texte fondateur de la ''parrhēsía'', c’est-à-dire du « franc-parler » philosophique qui engage la vie de celui qui parle<ref>Michel Foucault, ''Le Courage de la vérité. Le Gouvernement de soi et des autres II. Cours au Collège de France, 1983-1984'', éd. F. Gros, Paris, Gallimard/Seuil, 2009.</ref>. Leo Strauss et son école ont proposé une relecture « ésotérique » du texte, attentive à ce que Socrate ne dit pas et à ce qu’il suggère entre les lignes<ref>Leo Strauss, ''Studies in Platonic Political Philosophy'', Chicago, University of Chicago Press, 1983 ; ''The City and Man'', Chicago, Rand McNally, 1964.</ref>. Lire l’''Apologie'' aujourd’hui, c’est donc entrer dans un texte sédimenté par vingt-quatre siècles de commentaires, et qui n’a pas encore épuisé sa charge philosophique. Chaque époque y trouve un Socrate à sa mesure, et c’est peut-être la marque des grandes œuvres de pouvoir soutenir cette pluralité indéfinie d’interprétations. == Conclusion == L’''Apologie de Socrate'' n’est pas un plaidoyer ordinaire. Ce n’est même pas, à proprement parler, un plaidoyer au sens technique, puisque Socrate y refuse presque toutes les tactiques qui auraient pu le faire acquitter : il ne se fait pas écrire par un logographe, il ne supplie pas, il ne fait pas paraître ses enfants, il provoque le jury quand il lui faudrait le ménager. C’est une profession de foi philosophique, prononcée au moment où la vie de l’auteur est en jeu, et qui tire précisément de cet enjeu sa gravité unique. L’''Apologie'' est la preuve par la mort d’une thèse que Socrate n’a cessé de soutenir par les mots : la vertu vaut plus que la vie. Trois traits en font un texte fondateur. D’abord, il inaugure la figure du philosophe comme témoin : celui dont la cohérence entre la parole et la vie va jusqu’au sacrifice. Socrate meurt parce qu’il ne veut pas trahir ce qu’il a dit ; et Platon, en écrivant l’''Apologie'', fait de cette mort le sceau de la vérité philosophique. La philosophie, à partir de ce texte, n’est plus seulement une activité intellectuelle : elle est un engagement existentiel, et le philosophe n’est plus seulement celui qui sait, mais celui qui vit ce qu’il dit. Ensuite, le texte définit la philosophie elle-même, en l’opposant à la sophistique, à la rhétorique et à la religion populaire. Non un savoir constitué, mais un examen ; non une éloquence, mais une recherche du vrai ; non un rite, mais un service intérieur du divin. Cette triple opposition structure toute la pensée platonicienne ultérieure et, par voie de conséquence, la pensée occidentale. La philosophie, depuis Socrate, se définit comme ce qui n’est pas sophistique : un savoir désintéressé, inséparable d’une pratique de vérité. Enfin, le texte pose la question politique dans ses termes platoniciens : la cité peut-elle accueillir le philosophe ? La condamnation de Socrate se laisse lire comme une réponse négative qu’Athènes aurait donnée en acte à cette question, et c’est ainsi que Platon semble l’interpréter. L’œuvre de Platon tout entière tentera de penser une cité qui répondrait autrement, depuis les esquisses du ''Gorgias'' et de la ''République'' jusqu’à la construction ultime des ''Lois''. Le procès de Socrate est ainsi, indirectement, à l’origine de la philosophie politique occidentale. Lire l’''Apologie'' aujourd’hui, c’est s’exposer à une exigence intacte. La vie non examinée n’est pas digne d’être vécue ; la vertu vaut plus que l’argent, que la réputation, que la vie même ; la lâcheté est pire que la mort ; le juste préfère subir l’injustice plutôt que la commettre. Ces formules, qui paraissent extrêmes, sont le cœur d’une éthique dont la philosophie occidentale n’a cessé de se réclamer, même quand elle croyait s’en affranchir. Le taon, vingt-quatre siècles plus tard, pique toujours. == Notes et références == <references /> == Bibliographie sélective == === Éditions et traductions de l’''Apologie'' === * Platon, ''Apologie de Socrate. Criton'', traduction, introduction et notes par Luc Brisson, Paris, GF-Flammarion, 2016. * Platon, ''Apologie de Socrate'', traduction par Luc Brisson, présentation, notes, dossier, répertoire et glossaire par Arnaud Macé, Paris, GF-Flammarion, 2017. * Platon, ''Apologie de Socrate'', traduction, présentation et notes de Bernard et Renée Piettre, Paris, Le Livre de Poche (Librairie générale française), coll. « Libretti », 1997. * Platon, ''Apologie de Socrate'', texte établi et traduit par Maurice Croiset, Paris, Les Belles Lettres, coll. des Universités de France, 1920 (nombreuses rééditions). * Platon, ''Œuvres complètes'', sous la direction de Luc Brisson, Paris, Flammarion, 2008 (contient l’''Apologie''). === Commentaires === * Claude Chrétien, ''Platon, Apologie de Socrate'', Paris, Hatier, coll. « Profil philosophie », 1993. * Paul Allen Miller, Charles Platter, ''Plato’s Apology of Socrates: A Commentary'', Norman, University of Oklahoma Press, 2010. * Émile de Strycker, S. R. Slings, ''Plato’s Apology of Socrates: A Literary and Philosophical Study with a Running Commentary'', édité et complété par S. R. Slings, Leyde, E. J. Brill, coll. « Mnemosyne Supplements » 137, 1994. * Thomas G. West, ''Plato’s Apology of Socrates: An Interpretation with a New Translation'', Ithaca, Cornell University Press, 1979. === Études sur Socrate et l’''Apologie'' === * Louis-André Dorion, ''Socrate'', Paris, PUF, coll. « Que sais-je ? », 2004. * Gregory Vlastos, ''Socrate : ironie et philosophie morale'', trad. C. Dalimier, Paris, Aubier, 1994 (''Socrates: Ironist and Moral Philosopher'', 1991). * Gregory Vlastos, « The Socratic Elenchus », ''Oxford Studies in Ancient Philosophy'', I, 1983, p. 27-58. * Debra Nails, ''The People of Plato: A Prosopography of Plato and Other Socratics'', Indianapolis, Hackett, 2002. * Gabriele Giannantoni, ''Socratis et Socraticorum Reliquiae'', 4 vol., Naples, Bibliopolis, 1990. * Monique Canto-Sperber (dir.), ''Les Paradoxes de la connaissance. Essais sur le Ménon de Platon'', Paris, Odile Jacob, 1991. === Réceptions modernes === * G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', t. II, trad. P. Garniron, Paris, Vrin, 1971. * Søren Kierkegaard, ''Le Concept d’ironie constamment rapporté à Socrate'' (1841), trad. P.-H. Tisseau et E.-M. Jacquet-Tisseau, Paris, Éditions de l’Orante, 1975. * Friedrich Nietzsche, ''La Naissance de la tragédie'' (1872), dans ''Œuvres philosophiques complètes'', t. I, Paris, Gallimard, 1977. * E. R. Dodds, ''Les Grecs et l’irrationnel'' (1951), trad. M. Gibson, Paris, Flammarion, coll. « Champs », 1977. * Hannah Arendt, ''La Vie de l’esprit'', trad. L. Lotringer, Paris, PUF, 1981. * Michel Foucault, ''L’Herméneutique du sujet. Cours au Collège de France, 1981-1982'', éd. F. Gros, Paris, Gallimard/Seuil, 2001. * Michel Foucault, ''Le Courage de la vérité. Cours au Collège de France, 1983-1984'', éd. F. Gros, Paris, Gallimard/Seuil, 2009. * Leo Strauss, ''Studies in Platonic Political Philosophy'', Chicago, University of Chicago Press, 1983. === Sources antiques === * Aristophane, ''Les Nuées'', dans ''Théâtre complet'', t. I, trad. V.-H. Debidour, Paris, Gallimard, coll. « Folio », 1965. * Xénophon, ''Apologie de Socrate'' et ''Mémorables'', trad. P. Chambry, Paris, Garnier-Flammarion, 1967. * Diogène Laërce, ''Vies et doctrines des philosophes illustres'', trad. sous la direction de M.-O. Goulet-Cazé, Paris, Le Livre de Poche, coll. « La Pochothèque », 1999. * Plutarque, ''Du démon de Socrate'', dans ''Œuvres morales'', t. VIII, trad. J. Hani, Paris, Les Belles Lettres, 1980. * Aristote, ''Rhétorique'', trad. M. Dufour et A. Wartelle, Paris, Les Belles Lettres, 1938-1973. * Cicéron, ''Tusculanes'', trad. J. Humbert, Paris, Les Belles Lettres, 1931. rmo22hh1rp26pkhmwcwrkf7vcp4wj4y 763994 763993 2026-04-19T08:22:21Z PandaMystique 119061 763994 wikitext text/x-wiki == Introduction générale == {{wikisource|Apologie de Socrate (Platon)|Apologie de Socrate}} === L’événement et sa portée === [[Fichier:Socrate du Louvre.jpg|vignette|droite|upright=0.9|Portrait de Socrate, marbre, copie romaine d’époque impériale reproduisant un original grec attribué à Lysippe (IV{{e}} siècle av. J.-C.). Musée du Louvre, Paris (inv. Ma 59).]] En l’an 399 avant notre ère, à Athènes, un vieil homme de soixante-dix ans comparaît devant un tribunal populaire composé de cinq cent un jurés citoyens. Il s’appelle Socrate, fils de Sophronisque ; il est accusé d’impiété et de corruption de la jeunesse. Au terme d’une longue journée d’audience, à une faible majorité d’abord (telle que, selon ses propres mots en 36a, un basculement de trente voix aurait suffi à l’acquittement), puis à une majorité plus large pour la peine capitale, il sera condamné à mort. Quelques semaines plus tard, il boira la ciguë dans sa cellule, devant ses disciples, après le retour du bateau sacré de Délos. Cet événement, déjà traumatisant en son temps, est devenu le mythe fondateur de la philosophie occidentale : le moment où la cité met à mort celui qui prétendait y exercer, par la parole et l’examen, un magistère moral. L’''Apologie de Socrate'' est l’œuvre par laquelle Platon, alors âgé d’une quarantaine d’années, rend compte de ce procès. On situe sa composition probablement entre 390 et 385, soit une dizaine d’années après les faits. Le texte se présente sans cadre fictionnel : nul narrateur, nul personnage introductif, nulle scène préalable comme il en existe dans la plupart des autres dialogues. Nous sommes plongés d’emblée dans la parole de Socrate, au moment où il se lève pour prendre la parole. Contrairement à la plupart des dialogues platoniciens, Platon s’efface presque entièrement : comme le remarque B. Piettre dans son commentaire, dans aucune autre œuvre on n’a l’impression d’entendre avec une telle proximité la parole de Socrate, « comme s’il nous était donné d’assister au procès »<ref name="piettre">Bernard Piettre, ''Platon, Apologie de Socrate'', traduction, présentation et notes de Bernard et Renée Piettre, Paris, Le Livre de Poche (Librairie générale française), coll. « Libretti », 1997, p. 21.</ref>. Cela ne signifie pas que l’''Apologie'' soit une transcription sténographique du plaidoyer : Platon recompose, réorganise, stylise. Mais il s’efforce, probablement plus que dans tout autre dialogue, de restituer la voix, le ton, l’argumentation du maître. Il convient de rappeler que l’''Apologie'' de Platon n’est pas la seule défense posthume de Socrate. Xénophon a également rédigé une ''Apologie'' qui nous est parvenue, brève et d’un ton psychologique différent, ainsi que des ''Mémorables'' qui reprennent certains motifs<ref>Xénophon, ''Apologie de Socrate'' et ''Mémorables'', trad. P. Chambry, Paris, Garnier-Flammarion, 1967.</ref>. D’autres disciples, comme Eschine de Sphettos, ont composé des écrits socratiques aujourd’hui perdus<ref>Voir Gabriele Giannantoni, ''Socratis et Socraticorum Reliquiae'', 4 vol., Naples, Bibliopolis, 1990.</ref>. Un pamphlet hostile, l’''Accusation de Socrate'' du sophiste Polycrate (probablement composé vers 393-392), circulait et aurait donné à Platon l’occasion de répondre indirectement. La concurrence entre ces « socratiques » explique vraisemblablement en partie le soin que Platon met à camper son propre Socrate, qui finira par s’imposer comme la figure canonique dans la postérité. L’enjeu de l’''Apologie'' dépasse de loin la réhabilitation posthume d’un homme. À travers le procès de Socrate, Platon met en accusation la cité qui l’a condamné ; il ouvre l’espace d’une autre politique, où la philosophie, cet « amour du savoir » (''philosophía''), apparaît comme la véritable vocation civique. Condamner le philosophe, c’est pour Athènes se condamner elle-même, préférer l’ignorance au savoir, la facilité au courage, la flatterie à la vérité. L’''Apologie'' constitue ainsi, selon une formule souvent reprise, l’un des textes fondateurs de la philosophie comme discours distinct, à la fois opposé à la sophistique et à la rhétorique, et radicalement engagé dans l’existence concrète. Elle offre en outre une cristallisation exemplaire de ce qu’on appellera le « dialogue socratique », genre dont Platon fera sa forme propre de pensée. === Contexte historique du procès === Pour comprendre la portée du texte, il faut garder à l’esprit la situation d’Athènes en 399. La cité sort épuisée de la longue guerre du Péloponnèse (431-404), qui l’a opposée à Sparte et s’est soldée par la défaite totale d’Athènes. Après la capitulation, Lysandre, le général spartiate, a imposé un gouvernement oligarchique (les Trente) qui a régné par la terreur pendant environ huit mois (404-403) : exécutions sommaires, confiscations, exil des démocrates. La démocratie a été rétablie par un soulèvement armé parti de Phylè en 403, et, dans le cadre des accords de réconciliation conclus sous l’archontat d’Euclide, une amnistie générale a été proclamée pour ne pas ajouter la guerre civile aux malheurs déjà subis : il était désormais interdit, sous peine de sanctions, de se référer aux événements antérieurs à la restauration<ref>Sur cette amnistie, qu’il ne faut pas confondre avec le décret de Patroclide de 405 (qui portait sur la réhabilitation des ''atimoi''), voir Aristote, ''Constitution d’Athènes'', 39-40 ; et Xénophon, ''Helléniques'', II, 4, 38-43.</ref>. Mais les plaies étaient béantes, et le ressentiment courait souterrainement. Or Socrate avait été, dans les années antérieures, un familier de personnages qui incarnaient précisément ces désastres. Alcibiade, son disciple brillant et fantasque, avait entraîné Athènes dans la désastreuse expédition de Sicile (415-413), scandalisé la cité par l’affaire de la mutilation des hermès, puis fini par trahir sa patrie en passant chez Sparte. Critias, autre de ses proches, avait été l’un des chefs, peut-être le principal, du gouvernement des Trente, artisan de la terreur oligarchique. Charmide, oncle de Platon et lui aussi de l’entourage socratique, avait également appartenu à ce régime. Même si Socrate lui-même avait refusé de collaborer avec les Trente (comme il le rappellera dans son plaidoyer à propos de l’arrestation de Léon de Salamine), sa réputation se trouvait entachée. Le procès de 399, bien que portant officiellement sur des griefs religieux, fonctionne donc aussi comme un règlement de comptes politique, que l’amnistie interdisait pourtant de mener ouvertement. Eschine le rhéteur, quelques décennies plus tard, dira sans détour que les Athéniens avaient condamné Socrate « parce qu’il avait été le maître de Critias »<ref>Eschine, ''Contre Timarque'', I, 173.</ref>. L’accusation est portée par trois hommes. Mélétos, poète tragique obscur, plutôt jeune, est le plaignant officiel, celui qui a déposé la plainte auprès de l’archonte-roi, magistrat compétent pour les affaires religieuses. Mais Socrate sait parfaitement que le véritable moteur de la procédure est Anytos, riche tanneur, homme politique influent de la démocratie restaurée, qui avait participé à la chute des Trente. Son fils, raconte Xénophon, fréquentait Socrate et préférait ses entretiens à la tannerie paternelle : hostilité d’autant plus vive<ref>Xénophon, ''Apologie de Socrate'', 29-31.</ref>. Dans le ''Ménon'' de Platon (90b-95a), Anytos apparaît comme le type même du démocrate borné, ennemi viscéral des sophistes et plus largement des intellectuels<ref>Platon, ''Ménon'', 89e-95a.</ref>. Le troisième accusateur, Lycon, est un orateur dont la fonction semble avoir été de soutenir la plaidoirie par une péroraison énergique. La plainte, dont Diogène Laërce nous a conservé le texte<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 40.</ref>, s’énonce approximativement ainsi : <blockquote>Socrate est coupable de ne pas reconnaître les dieux que reconnaît la cité et d’introduire des divinités nouvelles ; il est aussi coupable de corrompre la jeunesse. Peine requise : la mort.</blockquote> Trois griefs, donc : la négation des dieux traditionnels, l’introduction de nouvelles divinités, la corruption de la jeunesse. Ces trois chefs d’accusation sont intimement liés dans la logique de l’accusation : en introduisant de nouvelles divinités et en niant les anciennes, Socrate aurait, par son enseignement, détourné les jeunes gens du respect dû à la religion civique, donc corrompu la cité dans ses fondements. L’accusation d’impiété (''asébeia'') était une accusation politique au sens fort, puisque la religion à Athènes n’était pas une affaire privée mais la substance même du lien civique. === Le cadre procédural === Le procès se tient à l’Héliée, le tribunal populaire, probablement dans un local situé sur l’agora. Les jurés (501 ce jour-là, nombre impair pour éviter les égalités) ont été tirés au sort le matin même parmi les six mille citoyens qui s’étaient inscrits comme héliastes pour l’année. Une journée entière est consacrée à la procédure, dont le temps est rigoureusement partagé entre l’accusation et la défense au moyen de la clepsydre, horloge à eau. Chaque partie parle pour son propre compte : ni procureur ni avocat. Il est permis de faire corroborer son discours par un orateur plus habile (ce dont Mélétos semble avoir bénéficié avec Lycon), ou de lire un discours écrit par un logographe. La tradition rapporte que Lysias, le plus grand logographe de l’époque, aurait composé pour Socrate un plaidoyer de défense ; Socrate en aurait reconnu la beauté avant de le rejeter comme ne lui convenant pas<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 40 ; Cicéron, ''De l’orateur'', I, 231.</ref>. La procédure, pour un procès où la loi n’a pas fixé la peine à l’avance (on parle alors d’''agôn timētós'', « procès à peine à estimer »), comporte deux votes. Après les plaidoiries, un premier vote tranche sur la culpabilité. En cas de condamnation, l’accusateur propose une peine (ici, la mort), l’accusé doit proposer une contre-peine, et un second vote choisit entre les deux sans possibilité de moyen terme. Cette contrainte procédurale pèse lourd sur la suite : les jurés, contraints de choisir entre deux propositions extrêmes, se trouvent ainsi, par la stratégie de Socrate refusant de proposer une peine crédible, poussés à voter la mort. C’est la procédure en deux temps qui explique la structure tripartite de l’''Apologie'' telle que Platon nous l’a transmise : d’abord le grand plaidoyer de défense (17a-35d), puis le discours de contre-proposition pénale après le verdict de culpabilité (35e-38b), enfin une dernière allocution prononcée après la condamnation à mort, adressée tour à tour aux jurés qui ont voté contre lui et à ceux qui l’ont soutenu (38c-42a). Il faut ajouter que les procès athéniens étaient des spectacles autant que des procédures. Le public y assiste ; les plaideurs usent de toute la rhétorique, des larmes de la famille éplorée, des supplications ostentatoires, des témoignages de moralité. Aristophane, dans ''Les Guêpes'' (422), ridiculise la passion que les Athéniens portaient à ces mises en scène judiciaires et le plaisir qu’ils prenaient à voir les accusés se répandre en lamentations. Socrate, comme on le verra, refuse délibérément toute cette théâtralité, ce qui nourrira, après la condamnation, son reproche aux juges : ils l’ont condamné non pas parce qu’il était coupable, mais parce qu’il n’a pas consenti à s’abaisser. === Structure et dispositif du texte === Lu rétrospectivement à travers la grille qu’Aristote constituera dans sa ''Rhétorique'', le plaidoyer de Socrate laisse reconnaître, dans son grand plan, les parties canoniques du discours judiciaire : on y distingue successivement l’exorde (17a-18a), la diabolè ou dénigrement des accusations antérieures (18a-19d), la narration ou ''diḗgēsis'' qui expose les faits (19d-24b), la réfutation proprement dite (24b-28a), l’amplification et la péroraison (28a-35d). Il ne s’agit pas de prétendre que Platon a composé son texte en suivant consciemment un schéma déjà codifié (la ''Rhétorique'' aristotélicienne est postérieure), mais de remarquer que le plaidoyer épouse, dans ses grandes articulations, les usages rhétoriques de son temps. Cette conformité apparente est constamment subvertie par l’ironie socratique : Socrate adopte la forme du discours judiciaire tout en la démentant dans son contenu, en refusant ses procédés et en retournant ses attentes. Le texte fonctionne ainsi comme une parodie philosophique de la rhétorique : il emprunte à l’art oratoire sa structure, mais seulement pour en exhiber la vanité quand il s’agit de la vérité. == Lecture suivie du texte == === Le premier discours : la défense (17a-35d) === ==== L’exorde (17a-18a) : la vérité contre l’éloquence ==== Dès les premiers mots, Socrate pose le ton. La formule d’ouverture, <blockquote>Quel effet, Athéniens, ont produit sur vous mes accusateurs, je l’ignore,</blockquote> est un exorde classique (en grec ''prooímion'') dont la fonction rhétorique, comme le rappelle Aristote dans la ''Rhétorique'' (III, 14), est de capter la bienveillance et l’attention de l’auditoire<ref>Aristote, ''Rhétorique'', III, 14, 1415a22-b30.</ref>. Socrate s’en sert pour établir immédiatement l’antithèse qui structurera tout son plaidoyer : ses accusateurs ont parlé avec persuasion, mais n’ont dit « rien de vrai ou presque » ; lui, en revanche, dira « toute la vérité ». Cette opposition entre l’éloquence ornée des adversaires et la parole nue de Socrate est un retournement ironique savamment préparé. Les accusateurs ont mis les juges en garde contre le « redoutable discoureur » qu’est Socrate. Mais, rétorque-t-il, s’ils entendent par là celui qui dit la vérité, il concède qu’il est un orateur, bien que pas à leur manière. Ses discours ne seront pas, dit-il, « des discours élégamment tournés, comme les leurs, ni même des discours qu’embellissent des expressions et des termes choisis », mais « des choses dites à l’improviste dans les termes qui [lui] viendront à l’esprit ». L’opposition technique est nette : d’un côté le discours apprêté, fondé sur la sélection du vocabulaire (''onómata''), la tournure des phrases (''rhḗmata'') et les arrangements (''kósmos'') ; de l’autre une parole qui se veut « au hasard », sans préparation. Socrate demande aux juges de lui pardonner cette façon de parler : il a soixante-dix ans, comparaît pour la première fois de sa vie devant un tribunal, et il est donc « étranger à la langue en usage ici ». Comme un véritable étranger qui parlerait dans son dialecte, il entend s’exprimer comme il le fait ordinairement, sur l’agora ou devant les comptoirs des changeurs. Le juge, conclut-il, doit juger sur le fond : <blockquote>si mes allégations sont justes ou non. Telle est en effet la vertu du juge, tandis que celle de l’orateur est de dire la vérité. (18a)</blockquote> Cette prise de position initiale est philosophiquement lourde de conséquences. Elle reformule, dès l’ouverture, l’opposition fondamentale entre la rhétorique (l’art de persuader, tel que le pratiquent sophistes et logographes) et la philosophie (recherche de la vérité par l’examen rationnel). Les sophistes, comme Gorgias ou Protagoras, avaient fait de la rhétorique un instrument neutre, capable de « faire de l’argument le plus faible l’argument le plus fort » (formule qui reviendra explicitement contre Socrate au chef d’accusation). Socrate, à l’inverse, affirme que la parole a pour fonction première de manifester le vrai, et que celui qui se défend en justice ne doit pas jouer des passions ou des artifices, mais soumettre les arguments à l’examen rationnel des jurés. On notera pourtant que cette protestation de simplicité est elle-même une figure rhétorique sophistiquée : celui qui clame qu’il ne sait pas parler parle déjà, et fort bien. Il s’agit de la ''dissimulatio artis'' que Cicéron théorisera plus tard : l’art supérieur consiste à cacher l’art. L’ironie socratique commence dès l’exorde. Socrate se présente comme un ''idiṓtēs'' (un simple particulier, un « idiot » au sens grec), étranger aux codes du tribunal, mais cette posture est elle-même un dispositif stratégique qui retourne contre l’accusation la suspicion de sophistique : ce n’est pas Socrate qui manipule les mots, ce sont les accusateurs. L’inversion est complète. ==== Les « anciens accusateurs » : la calomnie de longue durée (18a-19d) ==== Socrate introduit ensuite une distinction cruciale : il doit répondre à deux catégories d’accusations, celles de ses « accusateurs récents » (Mélétos, Anytos, Lycon), mais aussi, et d’abord, celles de ses « anciens accusateurs », qui ont « depuis de nombreuses années » répandu une image fausse de lui. Ceux-là, il les redoute davantage que les nouveaux, pour trois raisons convergentes. Ils sont d’abord nombreux : il ne s’agit pas de trois personnes identifiables, mais d’une rumeur collective. Leurs accusations sont ensuite anciennes : elles ont eu le temps de s’enraciner dans les esprits. Enfin, ils ont agi auprès des juges « dès l’enfance », à un âge où « vous aviez le moins de défiance », de sorte que les jurés en ont reçu l’empreinte avant même d’avoir l’âge de l’examiner. Quelle est la teneur de cette calomnie ancienne ? Socrate la résume en une formule qui reviendra plusieurs fois dans le texte comme un chef d’accusation fantasmatique : <blockquote>Il existe un certain Socrate, un savant, un « penseur » qui s’intéresse aux choses qui se trouvent en l’air, qui mène des recherches sur tout ce qui se trouve sous la terre et qui de l’argument le plus faible fait l’argument le plus fort. (18b)</blockquote> Cette caricature hétéroclite condense en réalité trois traits initialement distincts. Il y a d’abord la figure du physicien à la manière des présocratiques (Anaxagore, Diogène d’Apollonie), qui spéculait sur les phénomènes célestes (''tà metéōra'') et souterrains. Il y a ensuite celle de l’athée, puisque interroger la nature par la raison revenait, dans la perception populaire, à nier les dieux traditionnels (Anaxagore avait été poursuivi pour cette raison vers 433, Protagoras également, et tous deux avaient été contraints à l’exil). Il y a enfin celle du sophiste, expert en retournements dialectiques, capable de faire triompher n’importe quelle cause par le seul art des mots. L’incohérence de ce portrait (un philosophe de la nature qui serait en même temps un manipulateur rhétorique) ne l’empêche pas d’être efficace dans l’opinion. C’est le propre de la rumeur (''diabolḗ'') : elle n’a pas à être cohérente pour être puissante. Socrate le reconnaît avec lucidité : la force de cette calomnie vient précisément de ce qu’elle ne repose sur aucune source identifiable, qu’on ne peut donc ni l’interroger, ni la réfuter. Combattre ces accusateurs anonymes, dit Socrate, « c’est comme se battre contre des ombres » (18d). C’est une difficulté propre au philosophe dans la cité : face à l’opinion établie, il n’a pas d’adversaire identifiable, donc pas de prise dialectique. Socrate désigne cependant une source probable : les comédies, et en particulier ''Les Nuées'' d’Aristophane, représentées en 423 (vingt-quatre ans avant le procès)<ref>Aristophane, ''Les Nuées'', représentées pour la première fois aux Grandes Dionysies de 423 av. J.-C.</ref>. Il la mentionne d’abord anonymement, parlant d’un <blockquote>Socrate qui se balançait, en prétendant qu’il se déplaçait dans les airs et en débitant plein d’autres bêtises concernant des sujets sur lesquels je ne suis un expert ni peu ni prou. (19c)</blockquote> Le nom d’Aristophane sera explicitement prononcé peu après. Dans cette pièce, le poète comique met en scène un Socrate juché dans une corbeille suspendue, pour mieux « mêler sa pensée subtile à l’air », invoquant les Nuées comme divinités substitutives à celles de la religion populaire, enseignant à un paysan, Strepsiade, puis à son fils Phidippide, comment faire triompher le « Raisonnement injuste » et ruiner par cet apprentissage la piété filiale. La pièce s’achève d’ailleurs sur l’incendie du « Pensoir » socratique. L’enjeu pour Socrate n’est pas seulement de corriger une image fausse : c’est de montrer que les accusations de Mélétos se rabattent exactement sur cette caricature (négation des dieux, introduction de nouveautés religieuses, corruption de la jeunesse), de sorte que contre-attaquer la comédie, c’est déjà dissoudre la plainte. Il y a là un geste tranché : Socrate refuse explicitement d’être confondu, d’une part avec les physiciens qui spéculent sur la nature, d’autre part avec les sophistes qui enseignent la rhétorique contre rémunération. Il énumère d’ailleurs plusieurs sophistes célèbres (Gorgias de Léontinoi, Prodicos de Céos, Hippias d’Élis) et rappelle l’anecdote d’Événos de Paros, qui faisait payer cinq mines pour son enseignement (20b) : somme considérable, correspondant à environ un an et demi de salaire d’un ouvrier qualifié<ref name="brisson">Platon, ''Apologie de Socrate. Criton'', trad. et notes de Luc Brisson, Paris, GF-Flammarion, 2016, p. 131, note 54.</ref>. Socrate, lui, n’a rien à vendre, et c’est précisément cela, comme on le verra, qui témoigne de la pureté de sa démarche. Notons enfin une dimension cruciale de ce moment : Socrate récuse par avance toute identification à la figure du philosophe-penseur retiré du monde, que Platon décrit dans le ''Théétète'' (174a) par l’anecdote célèbre de Thalès tombant dans le puits en contemplant les étoiles<ref>Platon, ''Théétète'', 174a.</ref>. La défense de Socrate sera celle d’un homme de l’agora, immergé dans la cité, engagé dans l’examen de ses concitoyens. Le philosophe socratique n’est pas un contemplatif éloigné des affaires humaines : c’est au contraire le plus urbain des hommes, celui qui ne quitte jamais la ville (comme il le dit dans le ''Phèdre''), celui dont l’activité est fondamentalement politique, même s’il n’exerce aucune charge politique. ==== La narration : l’oracle de Delphes et la naissance de la mission (19d-24b) ==== Pour expliquer l’origine de la calomnie, Socrate engage ce qui correspond à la narration (''diḗgēsis'') du discours rhétorique. Il entreprend de raconter comment il est devenu ce personnage que certains considèrent comme un savant. Cette narration, qui occupe une part importante du discours, contient deux éléments structurants : le récit de l’oracle de Delphes et l’exposé de l’enquête qui s’ensuivit. ===== Le savoir humain et le savoir plus qu’humain (20d-21a) ===== Avant de raconter l’oracle, Socrate pose une distinction fondamentale qui constitue peut-être la pièce conceptuelle centrale de tout le plaidoyer. On dit qu’il est « savant » (''sophós'') : soit. Mais savant en quoi ? Il existe, dit-il, un savoir qui excède la mesure humaine, celui auquel prétendent, moyennant rétribution, les sophistes. Ce savoir-là, Socrate ne le possède pas ; et il aurait « des chances d’être un savant » seulement dans un sens plus modeste, celui d’un savoir qui « se rapporte à l’être humain », une sagesse humaine (''anthrōpínē sophía''). La distinction est capitale. Elle sépare deux ordres de connaissance : celui qui porte sur les choses divines (la nature, le cosmos, les causes premières), que Socrate refuse de revendiquer ; et celui qui porte sur l’humain, sur ce qu’il convient de faire pour vivre bien. Cette distinction préfigure le partage que toute l’histoire de la philosophie reprendra entre philosophie théorique et philosophie pratique, et elle annonce également la réorientation socratique qu’évoque Cicéron dans un passage célèbre : <blockquote>Socrate a fait descendre la philosophie du ciel sur la terre, l’a introduite dans les villes et même dans les maisons, et l’a obligée à s’enquérir de la vie, des mœurs, des choses bonnes et mauvaises. (''Tusculanes'', V, 10-11)<ref>Cicéron, ''Tusculanes'', V, 4, 10-11 : « Socrates autem primus philosophiam devocavit e caelo et in urbibus conlocavit et in domus etiam introduxit... »</ref></blockquote> La philosophie cesse d’être cosmologie pour devenir éthique. Pour prouver l’existence et la nature de ce savoir proprement humain, Socrate invoque un témoin insolite mais sans appel : « le dieu de Delphes », c’est-à-dire Apollon pythien. Le recours à la parole oraculaire, dans un tribunal populaire attaché à la religion civique, est rhétoriquement habile : il retourne l’accusation d’impiété en présentant Socrate comme un serviteur du dieu. ===== L’oracle et l’enquête (21a-23c) ===== [[Fichier:John Collier - Priestess of Delphi.jpg|vignette|gauche|upright=0.85|John Collier, ''La Prêtresse de Delphes'', 1891, huile sur toile, Art Gallery of South Australia. La Pythie est figurée assise sur son trépied, dans les vapeurs qui montent de la faille, laurier en main. C’est à cette prêtresse que Chéréphon aurait posé sa question, selon le récit que fait Socrate en ''Apologie'' 20e-21a.]] C’est le moment où surgit, dans le texte, le récit de l’oracle. Chéréphon, ami d’enfance de Socrate (un homme à la passion impétueuse, démocrate, exilé sous les Trente et revenu avec la démocratie, que la comédie moquait pour sa maigreur ascétique et sa « mine d’endive »<ref>Aristophane, ''Les Guêpes'', v. 1408 ; ''Les Oiseaux'', v. 1296 ; ''Les Nuées'', v. 104.</ref>), était allé un jour à Delphes et avait eu l’audace de demander à la Pythie s’il existait quelqu’un de plus sage que Socrate. La Pythie, prêtresse d’Apollon, parle au nom du dieu et rend des oracles aux consultants : elle répondit que « personne n’était plus sage ». Chéréphon est mort entre-temps (probablement vers 403), mais son frère, présent à l’audience, pourra en témoigner. Cette réponse divine met Socrate dans un profond embarras. Il a « conscience de n’être savant ni peu ni prou ». Mais le dieu, par définition, ne peut mentir : « la loi divine l’interdit » (21b). Comment résoudre l’énigme (''aínigma'') ? Socrate décide alors d’entreprendre une vérification. Il va chercher quelqu’un de plus sage que lui, afin de pouvoir revenir à Delphes et dire : <blockquote>ce dieu m’avait désigné comme le plus sage, mais voici qui l’est davantage.</blockquote> Cette démarche, loin d’être un geste d’orgueil, est présentée comme un service rendu au dieu : c’est pour vérifier l’oracle, non pour le contredire, que Socrate entreprend son enquête. Il accorde à l’oracle une autorité suffisante pour l’interroger méthodiquement, selon un principe qui rappelle la maxime exégétique attribuée à Héraclite : « le maître dont l’oracle est à Delphes ne dit ni ne cache rien : il fait signe »<ref>Héraclite d’Éphèse, fragment DK 22 B 93 (Diels-Kranz) = fragment 14 Conche. Voir Marcel Conche, ''Héraclite. Fragments'', Paris, PUF, coll. « Épiméthée », 1986, p. 168-170.</ref>. L’enquête est méthodiquement menée dans trois directions, que Socrate parcourt successivement. Il va d’abord trouver un homme politique (dont il tait le nom, conformément à la pudeur judiciaire) qui passait pour sage. Après l’avoir interrogé, il constate que cet homme se croit sage mais ne l’est pas. Socrate tire alors la leçon capitale qui donnera son contenu à la sagesse humaine : <blockquote>Il y a des chances que je sois moi-même plus sage que cet homme. Car aucun de nous, il est vraisemblable, ne sait rien qui en vaille la peine ; mais lui pense savoir alors qu’il ne sait pas, tandis que moi, tout comme je ne sais pas, je ne pense pas non plus savoir. (21d)</blockquote> Tel est le célèbre savoir du non-savoir socratique : non pas une ignorance totale, mais la conscience lucide et réfléchie de sa propre ignorance, qui vaut davantage que l’illusion de la science. Socrate répète l’opération avec d’autres hommes politiques, et chaque fois la déception est la même, mais pire : plus l’homme est réputé, plus son ignorance est grande ; plus il est humble, plus il est proche du vrai. Socrate passe ensuite aux poètes : auteurs de tragédies, poètes dithyrambiques et autres. Il leur demande ce que signifient leurs propres œuvres, espérant d’eux un savoir, puisqu’ils produisent de la beauté. Il découvre alors qu’ils composent « non par savoir, mais par une sorte de disposition naturelle et par inspiration, comme les devins et les oracles » (22c). Les poètes disent beaucoup de belles choses sans savoir ce qu’elles veulent dire ; et, comme les hommes politiques, ils se croient, à cause de leur art, savants dans d’autres domaines où ils ne le sont pas. L’inspiration poétique est ainsi reconnue comme réelle, mais dissociée du savoir : le poète est possédé par une muse, non par une compétence. Cette analyse, qui reviendra dans le ''Ion'', est une pièce importante de la pensée platonicienne sur l’art<ref>Platon, ''Ion'', 533d-535a.</ref>. Socrate examine enfin les artisans (''cheirotéchnai''). Ici, la situation est plus nuancée. Les artisans possèdent une compétence réelle, une ''tékhnē'', que Socrate ne songe pas à leur dénier. Mais cette compétence les induit à se croire savants « dans les choses les plus importantes », c’est-à-dire dans les questions morales et politiques, alors qu’ils ne le sont pas. L’artisan, parce qu’il sait faire une paire de chaussures, se croit en droit d’avoir un avis éclairé sur la justice. C’est là un point crucial : Socrate reconnaît la légitimité de la ''tékhnē'' dans son domaine propre, mais refuse l’extrapolation de la compétence technique à la sagesse pratique. La conclusion de l’enquête est double. D’une part, Socrate conclut à la véracité de l’oracle : sa sagesse consiste en ce qu’il ne croit pas savoir ce qu’il ne sait pas. D’autre part, il comprend que l’oracle ne le désignait pas ''lui'' spécifiquement comme sage, mais se servait de son nom à titre d’exemple : <blockquote>il y a des chances, Messieurs, pour qu’en réalité le sage, ce soit le dieu, et que dans ce fameux oracle il veuille dire que la sagesse humaine a bien peu de valeur, et même aucune ; et il est clair qu’en désignant Socrate il s’est servi de mon nom pour me prendre en exemple, comme s’il disait : « Le plus sage d’entre vous, hommes, est celui qui, comme Socrate, a reconnu qu’en réalité sa sagesse ne vaut rien. » (23a-b)</blockquote> Socrate devient ainsi, non pas un savant, mais un exemple (''parádeigma'') par lequel le dieu invite tous les hommes à reconnaître la misère de leur prétention au savoir. La sagesse n’est pas une propriété de l’individu, mais une relation à l’ignorance ; elle est, on pourrait dire, éminemment ''maïeutique'' : elle fait accoucher les autres de la conscience de leur propre ignorance. ===== La mission et la naissance de la haine (23b-24b) ===== De cette interprétation de l’oracle naît la mission socratique. Socrate continue, « en service pour le dieu » (''hypēresía toû theoû''), à enquêter sur quiconque prétend être sage, pour manifester chaque fois qu’il ne l’est pas. Cette activité l’a réduit à « une grande pauvreté », car elle lui a occupé toute sa vie au détriment de ses affaires. Mais elle a aussi suscité contre lui des haines innombrables, chaque fois qu’il a démasqué l’ignorance d’un puissant ou d’un réputé. On touche ici à un mécanisme psychologique qu’il faut bien mesurer : nul n’aime être convaincu d’ignorance, surtout publiquement ; celui qui le fait, même par amour du vrai, se constitue des ennemis à proportion de sa rigueur. Les jeunes gens de bonne famille, ceux qui disposent de loisir (''scholḗ''), prennent plaisir à le voir faire ; ils tentent à leur tour d’imiter sa démarche, et ainsi « se font eux-mêmes haïr par ceux qu’ils examinent » (23c), qui ne s’en prennent pas à ces jeunes, mais à Socrate. Ce dernier devient ainsi le responsable imaginaire d’une activité critique qui déborde largement sa personne. C’est le ressort profond de l’accusation de « corruption de la jeunesse » : ce n’est pas que Socrate ait enseigné le mal, c’est que ses méthodes, imitées par ses disciples, font éclater un scandale qu’on veut lui imputer. C’est ainsi que s’est formée la réputation selon laquelle Socrate serait un « corrupteur de la jeunesse », et qu’on a repris contre lui la vieille caricature : un homme qui fait des recherches sur le ciel et la terre et qui fait triompher la mauvaise cause. Socrate tire la conclusion rhétorique : « c’est en disant la vérité que je me fais des ennemis », ce qui est, dit-il, la preuve qu’il dit vrai. Les causes de l’accusation sont là : non dans une faute réelle, mais dans le ressentiment de ceux qu’il a démasqués. Cette section du plaidoyer est philosophiquement fondamentale. Elle met en place plusieurs concepts clefs du socratisme tel que Platon l’entend. D’abord, la philosophie comme examen (''exétasis'') et non comme doctrine : on ne peut enseigner la philosophie de Socrate parce qu’elle n’est pas un corps de propositions à transmettre, mais une pratique à exercer. Ensuite, le savoir de l’ignorance comme seule forme accessible du savoir humain. Enfin, la philosophie comme mission divine, ce qui lui confère une légitimité supérieure à celle des institutions politiques. Cette dimension religieuse de la philosophie est essentielle à l’économie du texte : si la mission est divine, elle ne peut être interrompue par un décret humain, fût-il celui d’un tribunal souverain. On notera également que cette démarche, en démasquant l’ignorance des prétendus savants, introduit une différence entre savoir et non-savoir sur des sujets où la démocratie athénienne supposait, pour délibérer, qu’il n’y en avait pas. Comme l’explique un passage du ''Protagoras'' (319c-d), rédigé par Platon peu après l’''Apologie'', les assemblées démocratiques distinguent les sujets techniques (sur lesquels seuls les compétents s’expriment) et les sujets politiques (sur lesquels tous les citoyens, cordonniers, potiers, tanneurs ou menuisiers, peuvent donner leur avis)<ref>Platon, ''Protagoras'', 319b-d.</ref>. La délibération collective suppose que sur les sujets politiques, il n’existe pas de différence de compétence entre les citoyens. Or l’enquête socratique a précisément pour effet de réintroduire cette différence sur les objets où l’institution démocratique la refoulait. Se prétendre ignorant soi-même et révéler l’ignorance d’autrui sur les questions de justice ou de vertu, c’est mettre en cause le principe majoritaire là où il s’applique. On comprend en quoi, malgré les apparences, la posture socratique est subversive pour la démocratie athénienne : elle frappe à sa racine épistémologique. ==== La réfutation de Mélétos (24b-28a) ==== Socrate en vient maintenant aux accusations officielles. Il relit la plainte, <blockquote>Socrate est coupable de corrompre la jeunesse et de reconnaître non pas les dieux que la cité reconnaît, mais, au lieu de ceux-là, des divinités nouvelles,</blockquote> et entreprend de l’examiner point par point. Il utilise alors la prérogative que la loi athénienne accorde à l’accusé : interroger directement son accusateur, qui est tenu de répondre. Ce qui suit est l’un des grands morceaux dialectiques de l’''Apologie''. Socrate, en un véritable elenchos (cette réfutation par interrogation qui est sa marque de fabrique), démonte successivement les trois volets de l’accusation. On notera d’emblée un jeu de mots grec qui court tout l’interrogatoire : le nom de Mélétos (''Mélētos'') évoque le verbe ''mélei'' (« se soucier de »). Socrate va reprocher à Mélétos, précisément, de ne pas se soucier (mélein) des choses dont il prétend se soucier. L’ironie est ciselée jusque dans l’onomastique. La méthode que Socrate utilise ici est l’''elenchos'' : il part des thèses de l’interlocuteur, l’amène par des questions à en reconnaître les conséquences, puis lui fait constater la contradiction entre ces conséquences et d’autres thèses qu’il tient également pour vraies. Cette procédure, qui ne démontre rien positivement mais montre qu’une thèse est intenable, est le moteur de la dialectique socratique dans les dialogues de jeunesse de Platon<ref>Sur la méthode de l'elenchus, voir Gregory Vlastos, « The Socratic Elenchus », ''Oxford Studies in Ancient Philosophy'', I, 1983, p. 27-58.</ref>. ===== Qui rend les jeunes meilleurs ? (24b-25c) ===== Premier volet : la corruption de la jeunesse. Socrate commence par un piège dialectique. Si Mélétos accuse quelqu’un de corrompre les jeunes, c’est qu’il a à l’esprit ce qui les rend meilleurs. Qu’il le dise donc. Mélétos, pris au dépourvu, balbutie : « Les lois ». Mais ce n’est pas une personne, objecte Socrate. Il insiste : quel ''homme'' rend les jeunes meilleurs ? Mélétos répond alors, au gré d’une improvisation visiblement embarrassée : les juges, puis les membres du Conseil, puis ceux de l’Assemblée, bref tous les Athéniens ; tous, sauf Socrate. Socrate retourne alors l’argument par une analogie mémorable, celle des chevaux. Suppose-t-on que tous les hommes rendent les chevaux meilleurs, et qu’un seul les corromprait ? Non, c’est évidemment le contraire : pour les chevaux comme pour tous les animaux, seuls quelques spécialistes savent les rendre meilleurs, et la plupart des gens, s’ils s’en occupent, les nuisent. Il en va de même pour les jeunes gens. L’éducation est un art, et l’art suppose la compétence, qui n’est pas le partage du plus grand nombre. En prétendant que tous éduquent bien sauf Socrate, Mélétos révèle qu’il n’a jamais sérieusement réfléchi à ce dont il parle : il s’est désintéressé de la question. Ce qui est précisément ce que le jeu de mots sur son nom entendait suggérer. Ce premier argument est intéressant par ce qu’il laisse apercevoir de la position de Socrate à l’égard de la démocratie. L’éducation, comme le souligne le commentaire de C. Chrétien, est « une affaire politique tant la formation de l’homme paraît indissociable de celle du citoyen »<ref name="chretien">Claude Chrétien, ''Platon, Apologie de Socrate'', Paris, Hatier, coll. « Profil philosophie », 1993, p. 44.</ref>. Or, pour Mélétos et pour les Athéniens en général, la cité est à elle-même sa propre pédagogie : chaque citoyen forme, par son exemple et sa participation à la vie publique, les futurs citoyens. Socrate, à l’inverse, soutient que l’éducation, comme les autres arts, relève d’une compétence particulière, ce qui va à l’encontre du présupposé démocratique d’une compétence civique également partagée. En paraissant ne se battre que sur un détail logique, Socrate met en cause, à travers cet argument, l’un des présupposés majeurs de la démocratie athénienne : celui selon lequel chaque citoyen serait naturellement compétent pour la délibération politique. ===== Corrompre volontairement ? (25c-26a) ===== Deuxième pièce du dispositif : Socrate demande à Mélétos s’il le pense corrompre les jeunes volontairement ou involontairement. Mélétos, rageusement, répond : volontairement. Mais Socrate montre alors que cette réponse est intenable. Car si les méchants font du mal à leur entourage, et les bons du bien, alors corrompre volontairement les gens qui nous entourent (avec lesquels on vit) c’est s’exposer soi-même à en pâtir. Personne, pas même le plus ignorant, ne choisit délibérément de se nuire à soi-même. Donc, ou bien Socrate ne corrompt pas, ou bien, s’il corrompt, c’est involontairement. Et dans ce cas, la loi ne prévoit pas un procès mais une remontrance privée : si on m’avait averti, j’aurais cessé. En me traînant devant le tribunal, Mélétos prouve que son but n’est pas de me corriger mais de me punir ; ce qui est contradictoire avec l’idée d’une faute involontaire. Cet argument repose sur l’un des principes les plus fermes du socratisme, le célèbre paradoxe socratique selon lequel nul ne fait le mal volontairement (''oudeís hekṓn hamartánei''). Socrate y croit sincèrement : si l’on savait vraiment ce qu’est le bien, on ne pourrait pas ne pas le vouloir. Le vice n’est pas une perversion de la volonté, c’est une forme d’ignorance. Cette thèse, qui paraîtra contre-intuitive à toute la tradition postérieure (notamment chrétienne, qui mettra l’accent sur la malice du mal), sera développée dans plusieurs dialogues platoniciens, notamment le ''Gorgias'' et le ''Protagoras''<ref>Platon, ''Gorgias'', 466a-468e, 509c-e ; ''Protagoras'', 352b-358d.</ref>. Aristote, dans l’''Éthique à Nicomaque'', la critiquera comme négligeant la réalité de la ''akrasía'' (faiblesse de la volonté)<ref>Aristote, ''Éthique à Nicomaque'', VII, 2-3, 1145b21-1147b19.</ref>. Mais l’argument sert ici surtout à piéger Mélétos dans une contradiction : soit tu m’accuses d’une faute involontaire, et le procès est illégitime (car la procédure judiciaire vise des fautes intentionnelles) ; soit tu m’accuses d’une faute volontaire, mais celle-ci est psychologiquement impossible (personne ne choisit de se nuire à soi-même). Dans les deux cas, la plainte s’effondre. Remarquons la précision juridique : Socrate joue habilement sur la distinction athénienne entre fautes volontaires (justiciables) et involontaires (pour lesquelles la remontrance privée, la ''nouthesía'', était la procédure appropriée). ===== L’athéisme et les divinités démoniques (26a-28a) ===== Troisième volet : la question religieuse, qui est le cœur même de l’accusation. Socrate demande à Mélétos de préciser son propos : l’accuse-t-il de reconnaître des dieux différents de ceux de la cité, ou de ne reconnaître aucun dieu du tout ? La distinction est cruciale, car la plainte elle-même est ambiguë (elle évoque des « divinités nouvelles », ce qui présuppose que Socrate croit à des divinités, mais lui reproche aussi de ne pas reconnaître celles de la cité). Mélétos, avec une maladresse que Socrate exploite pleinement, s’emporte et répond : « aucun dieu du tout ». Il ajoute même, piégé par sa propre fureur, que Socrate prétend, à la manière d’Anaxagore, que « le soleil est une pierre et la lune une terre ». Socrate saisit l’occasion avec une précision chirurgicale. D’abord, il ridiculise la confusion : Anaxagore, en effet, a soutenu cette thèse physicienne, mais les livres d’Anaxagore, qui se trouvent au marché (à l’orchestre, endroit de l’agora où l’on vendait les livres), coûtent « tout au plus une drachme » ; pourquoi donc accuser Socrate d’avoir inventé ce qu’on peut lire partout et qui n’est pas de lui ? Le trait est doublement dévastateur : il innocente Socrate et il prouve l’incompétence de Mélétos, qui ne distingue pas Socrate d’Anaxagore. Ensuite, il tend son piège principal. La plainte officielle dit que Socrate introduit de nouvelles divinités (''daimónia''). Or Mélétos vient d’affirmer que Socrate n’admet aucun dieu du tout. Ces deux affirmations sont contradictoires : on ne peut pas à la fois reconnaître des divinités et ne reconnaître aucun dieu. Mélétos se contredit lui-même, et sous serment, car la plainte avait été déposée sous serment réciproque (''antōmosía''). Socrate poursuit par un syllogisme subtil. Peut-on reconnaître des « phénomènes démoniques » (''daimónia prágmata'') sans reconnaître l’existence de démons ? De même que l’on ne peut reconnaître des phénomènes hippiques sans reconnaître les chevaux, ni des phénomènes musicaux sans reconnaître les musiciens, on ne peut reconnaître des phénomènes démoniques sans reconnaître les démons. Or les démons, selon la religion grecque traditionnelle, sont soit des dieux soit des enfants de dieux. Donc, si Socrate reconnaît des démons, il reconnaît aussi des dieux, ou à tout le moins des êtres divins. La plainte se contredit elle-même : Mélétos affirme à la fois que Socrate ne reconnaît aucun dieu et qu’il reconnaît des démons, donc des dieux. Ce raisonnement est brillant sur le plan dialectique, mais il a suscité la perplexité des commentateurs. Il repose sur une définition traditionnelle des démons comme « enfants des dieux », qui n’est pas toujours stabilisée dans la culture grecque (chez Hésiode, les démons sont plutôt des hommes de l’âge d’or devenus esprits), et laisse entière la question de savoir ce que sont les « nouvelles divinités » dont Socrate était réellement accusé. La plupart des commentateurs modernes estiment que l’accusation visait précisément le fameux ''daimónion'' socratique, la voix intérieure divine dont Socrate parlera plus loin, qui serait apparue aux Athéniens comme une divinité privée, nouvelle, donc impie car non reconnue par la cité. Socrate, en déplaçant le débat sur la question abstraite de l’existence ou non des démons, élude habilement cette difficulté. C’est un procédé dialectique, non un argument de fond. Il faut aussi percevoir le geste de fond. Comme le note Claude Chrétien, Socrate, par ce raisonnement, rattache sa croyance en des « phénomènes démoniques » à une croyance minimale mais ferme en la divinité, sur un mode qui relève d’une théologie négative : il ne dit rien de positif sur les dieux, mais affirme seulement que quelque chose, dans l’expérience humaine, manifeste leur existence<ref name="chretien-30">Claude Chrétien, ''Platon, Apologie de Socrate'', ''op. cit.'', p. 30-31.</ref>. Cela est cohérent avec son agnosticisme sur les mythes (dans le ''Phèdre'', Socrate refuse de spéculer sur les aventures de Borée enlevant Orithye<ref>Platon, ''Phèdre'', 229b-230a.</ref>) et avec sa dévotion pratique à la religion de la cité (on le voit obéir aux rites dans plusieurs dialogues). Socrate est pieux en acte, agnostique en théorie : il reconnaît une transcendance divine, sans prétendre en décrire la nature. Cette position, fine et paradoxale, est à l’origine d’une tension féconde dans toute la théologie philosophique ultérieure. À la fin de cette section, Socrate conclut qu’il a suffisamment montré que l’accusation de Mélétos est sans consistance. Mais, ajoute-t-il avec lucidité, il sait bien que ce n’est pas elle qui le fera condamner : c’est la vieille calomnie, la haine accumulée au fil des années. D’autres hommes justes, avant lui, ont subi le même sort, et beaucoup en subiront après. ==== La mission divine et le modèle héroïque (28a-30c) ==== Ayant réfuté l’accusation formelle, Socrate entreprend alors ce que Piettre appelle une « amplification »<ref>Piettre, ''op. cit.'', p. 45-46 (sur le découpage du plaidoyer en réfutation et amplification).</ref> : il justifie tout son mode de vie. L’''Apologie'' bascule ici d’un plaidoyer juridique vers une proclamation philosophique. La question n’est plus « suis-je coupable de ces griefs précis ? » mais : « comment justifier un mode d’existence qui expose à la mort ? » C’est à partir de ce moment que l’''Apologie'' cesse d’être un simple plaidoyer pour devenir un manifeste. ===== Le modèle d’Achille (28b-d) ===== Socrate anticipe une objection qu’un auditoire athénien pouvait sincèrement formuler : n’as-tu pas honte, Socrate, d’avoir mené une existence qui t’expose à mourir ? Il y répond par l’évocation des héros homériques, et notamment d’Achille, la figure de référence de la vertu guerrière grecque. Quand Thétis, sa mère, lui annonça qu’il mourrait s’il vengeait Patrocle en tuant Hector, Achille répondit, selon l’''Iliade'' : <blockquote>que je meure immédiatement, pour peu que je punisse le coupable, plutôt que de rester ici, à être la risée de tous, assis sur mes vaisseaux, poids inutile de la terre.<ref>Homère, ''Iliade'', XVIII, v. 96-104 ; cité par Socrate en ''Apologie'', 28c-d.</ref></blockquote> Achille a donc méprisé la mort pour préserver l’honneur. Celui qui occupe une place, explique Socrate, doit y rester, au péril de sa vie, « sans tenir compte d’autre chose que du déshonneur ». Cet argument est une adaptation du modèle homérique, et non une simple reprise. Socrate transforme l’héroïsme aristocratique d’Achille (celui d’un demi-dieu, fils d’une déesse) en une vertu démocratique accessible à tout soldat de la phalange hoplitique : il s’agit de tenir son poste, quelle que soit sa place, qu’on l’ait choisie ou qu’on y ait été affecté par son chef (28d). La vertu n’est plus aristocratique, elle est civique ; elle n’est plus la propriété d’une élite, elle est accessible à quiconque occupe sa place avec fermeté. Cette démocratisation de la vertu héroïque prépare la transposition qui va suivre. Socrate rappelle d’ailleurs son propre passé militaire. Il a combattu à Potidée (432-429), à Amphipolis (424), et surtout à Délion (424), où son courage fut loué par Alcibiade dans le ''Banquet'' (219e et suivants<ref>Platon, ''Banquet'', 219e-221c.</ref>) et par le général Lachès dans le dialogue du même nom (''Lachès'', 181a-b<ref>Platon, ''Lachès'', 181a-b.</ref>). Comme ces soldats qui ne quittent pas leur poste, Socrate ne peut quitter celui qui lui a été assigné, par le dieu. ===== Le poste assigné par le dieu (28d-29a) ===== Socrate pose alors la transposition capitale : <blockquote>le poste qu’on m’a assigné, moi, est celui du philosophe, qui doit vivre en philosophant, en s’examinant soi-même et en examinant les autres. Je ne peux le quitter par crainte de la mort, pas plus qu’un soldat ne peut quitter le sien.</blockquote> La philosophie est ainsi présentée comme une assignation divine, équivalente à l’ordre d’un chef de guerre, et plus impérieuse encore, puisque l’ordre vient du dieu. Cette analogie entre vie philosophique et vie militaire, qui fera carrière dans toute la tradition stoïcienne (Sénèque, Épictète, Marc Aurèle en useront abondamment<ref>Voir notamment Épictète, ''Entretiens'', I, 9, 24 ; III, 24, 31-36 ; Marc Aurèle, ''Pensées'', III, 5 ; VII, 45.</ref>), fonde la philosophie comme service, comme ''officium'', comme devoir qu’on ne peut déserter sans se déshonorer. ===== L’ignorance de la mort (29a-b) ===== Vient alors l’un des passages les plus célèbres du texte, où Socrate renverse la psychologie commune du courage : <blockquote>Craindre la mort, Athéniens, ce n’est rien d’autre que se donner pour savant sans l’être ; c’est donner l’impression qu’on sait ce qu’on ne sait pas. (29a)</blockquote> Car personne ne sait ce qu’est la mort, ni si elle n’est pas pour l’homme le plus grand des biens ; mais on la redoute comme si l’on savait qu’elle est le plus grand des maux. C’est là, dit Socrate, la forme la plus répréhensible d’ignorance : croire savoir ce qu’on ne sait pas, donc la même erreur que celle des faux sages qu’il a démasqués dans son enquête. Le raisonnement est d’une rigueur remarquable. Il articule le savoir du non-savoir à l’éthique du courage. Socrate, lui, sait qu’il ne sait rien de la mort ; il ne la craint donc pas. Mais il sait en revanche, et c’est la seule « exception » à son ignorance proclamée, que <blockquote>commettre une injustice et désobéir à un meilleur que soi, dieu ou homme, cela je sais que c’est mauvais et honteux. (29b)</blockquote> On voit ici le dispositif éthique qui va commander toute la suite : entre un mal certain (l’injustice et la lâcheté) et un mal supposé mais incertain (la mort), le sage choisit sans hésiter d’éviter le premier. Le courage philosophique n’est donc pas un mépris enthousiaste de la mort, comme celui d’Achille, c’est une lucidité sur ce qui est réellement à craindre. Ce renversement de la psychologie héroïque en lucidité rationnelle est l’un des gestes fondateurs de la philosophie morale. ===== L’hypothèse de l’acquittement conditionnel (29c-30c) ===== Socrate imagine alors une situation extrême, une sorte d’expérience de pensée. Supposons que les juges lui offrent de l’acquitter à la condition qu’il cesse de philosopher. Alors Socrate répondrait : <blockquote>Athéniens, je vous suis reconnaissant et je vous aime, mais j’obéirai au dieu plutôt qu’à vous ; et tant qu’il me restera un souffle de vie, tant que j’en serai capable, je ne cesserai, soyez-en sûrs, de philosopher, de vous exhorter et de m’expliquer avec tel ou tel d’entre vous. (29d)</blockquote> Il continuerait à dire à chacun la formule qui résume toute sa mission : <blockquote>Ô excellent homme, toi qui es d’Athènes, la cité la plus grande et la plus réputée pour son savoir et sa puissance, tu n’as pas honte de t’occuper de ta fortune et des moyens de t’enrichir le plus possible, de ta réputation, des honneurs, alors que de ton intelligence, de la vérité, de ton âme et des moyens de la perfectionner, tu ne t’en occupes et ne t’en soucies aucunement ? (29d-e)</blockquote> On est ici au cœur du message socratique, tel que Platon l’a consigné. Le renversement qu’opère ce passage est philosophiquement majeur. D’une part, Socrate affirme que son obéissance au dieu l’emporte sur son obéissance à la cité : c’est, en puissance, toute la doctrine de la désobéissance civile au nom d’une norme transcendante. D’autre part, il renverse la hiérarchie des biens : la vertu ne vient pas de l’argent, mais l’argent et tous les autres biens viennent de la vertu ; il faut donc se soucier prioritairement de son âme (''psuchḗ''), et non de ses biens matériels ou de sa réputation. L’''Apologie'' est ainsi, dans la philosophie occidentale, l’un des textes où s’origine ce thème du souci de soi (''epiméleia heautoû'') compris comme soin de l’âme et examen permanent de soi-même, thème qui parcourra toute la tradition ultérieure, des écoles hellénistiques aux spirituels chrétiens, jusqu’aux lectures contemporaines de Michel Foucault qui en fera un objet majeur de ses derniers cours au Collège de France<ref name="foucault-hs">Michel Foucault, ''L’Herméneutique du sujet. Cours au Collège de France, 1981-1982'', éd. F. Gros, Paris, Gallimard/Seuil, coll. « Hautes Études », 2001, en particulier les leçons des 6, 13 et 20 janvier 1982.</ref>. Socrate ajoute alors l’une de ses déclarations les plus provocatrices : <blockquote>Là-dessus, Athéniens, croyez-en ou non Anytos, acquittez-moi ou ne m’acquittez pas, toujours est-il que je ne changerai pas de conduite, même si je devais souffrir mille morts. (30c)</blockquote> La défense ne demande plus un acquittement ; elle proclame la pérennité de la mission, quel que soit le verdict. Socrate, à ce moment précis du plaidoyer, cesse d’être un accusé pour devenir un apôtre. Cette attitude explique, rétrospectivement, l’incompréhension et l’irritation des jurés : il ne se défend pas, il les défie. ==== Socrate, « taon de la cité » (30c-31c) ==== Socrate affirme alors, avec une audace étonnante, <blockquote>si vous me condamnez à mort, ce n’est pas à moi, mais à vous-mêmes, que vous ferez le plus de tort. (30c)</blockquote> Car ni Mélétos ni Anytos ne peuvent le léser véritablement : ils peuvent le tuer, l’exiler, le priver de ses droits civiques (''atimía''), choses que certains tiendraient pour de grands malheurs, mais lui ne les tient pas pour tels. Le vrai mal est celui que font à leur âme ceux qui entreprennent de tuer injustement. Cette thèse, que Platon développera dans le ''Gorgias'' (469c) sous la forme « il vaut mieux subir l’injustice que la commettre »<ref>Platon, ''Gorgias'', 469c : « [...] je choisirais de subir plutôt que de commettre l’injustice. » Voir aussi 474b-479e.</ref>, est probablement la plus radicale de toutes les thèses morales de l’antiquité. Surgit alors la célèbre image du taon. Socrate est <blockquote>un homme attaché à la cité par le dieu, comme le serait un taon au flanc d’un cheval de grande taille et de bonne race, mais qui se montrerait un peu mou en raison même de sa taille et qui aurait besoin d’être réveillé par l’insecte. (30e)</blockquote> Athènes est ce cheval noble mais assoupi ; Socrate est l’insecte qui le pique, le réveille, le harcèle. Le dieu lui a donné cette mission, qui explique qu’il passe son temps à aborder chacun, « comme un père ou un frère plus âgé », pour le persuader d’avoir souci de la vertu. Cette image mérite qu’on s’y arrête, tant elle est dense. Elle articule trois éléments. D’abord, la noblesse du cheval : Athènes n’est pas critiquée absolument, mais reconnue pour ce qu’elle est, la plus belle cité du monde grec, de « grande taille et de bonne race ». Ensuite, son engourdissement : cette grandeur même la rend molle, somnolente, incapable de s’ébrouer spontanément. Enfin, la nécessité du taon : seule une figure dérangeante, insupportable, inutile en apparence, peut réveiller la cité. Le taon n’est pas à sa place dans le cheval ; il est un corps étranger, irritant ; mais précisément, c’est de cette position dérangeante que vient son utilité. La philosophie est pensée ici comme critique nécessaire, comme dissidence féconde, comme décalage qui maintient la cité vivante. On voit se dessiner une dialectique subtile. Socrate est indissociablement dedans et dehors : citoyen d’Athènes, engagé dans sa cité, respectueux de ses lois au point d’accepter la mort plutôt que de fuir (comme l’exprimera le ''Criton''<ref>Platon, ''Criton'', 50a-54d.</ref>), et en même temps étranger à ses conformismes, à ses illusions, à ses complaisances. Sans le taon, le cheval dormirait ; mais le cheval peut aussi, d’un mouvement irrité, écraser le taon. C’est exactement ce qui se passe au procès. L’image contient en elle-même une prophétie : tuer le taon, c’est se priver de la piqûre bienfaisante, condamner la cité au sommeil. D’où la prédiction de Socrate : <blockquote>en suite de quoi, vous passeriez votre vie à dormir, à moins que le dieu, ayant souci de vous, ne vous envoie quelqu’un d’autre. (31a)</blockquote> La suite de l’histoire, à tout le moins celle de la pensée, dira que cet autre sera Platon lui-même, puis Aristote, et la longue lignée des philosophes que l’''Apologie'' aura rendus possibles. Socrate apporte ensuite une preuve empirique de son désintéressement : sa pauvreté. Si son activité avait un but intéressé, si elle rapportait un salaire, on pourrait douter de la pureté de ses motivations. Mais ses accusateurs, malgré leur acharnement, n’ont pu produire aucun témoin attestant qu’il ait jamais exigé ou reçu un salaire. Sa misère est la meilleure preuve qu’il dit vrai. Cette insistance sur la gratuité de son enseignement est une pique adressée aux sophistes, qui se faisaient richement rémunérer, et un trait supplémentaire qui distingue la philosophie socratique de la ''téchnē'' marchande des sophistes. Socrate n’est pas un prestataire de services ; il est un serviteur du dieu. ==== Le démon et la prudence politique (31c-32e) ==== Une objection se présente naturellement : si Socrate est ce grand conseiller des particuliers, pourquoi ne monte-t-il pas à la tribune pour conseiller la cité elle-même dans ses assemblées ? La réponse est le fameux passage sur le ''daimónion'' socratique. ===== Qu’est-ce que le démon de Socrate ? (31c-d) ===== Socrate confie aux juges ce qu’il a déjà dit « maintes fois en maints endroits » : <blockquote>il m’advient quelque chose de divin et de démonique (''theîón ti kai daimónion''), une voix intérieure qui, depuis [mon] enfance, [...] chaque fois qu’elle m’advient, me détourne toujours de ce que je me propose de faire, mais jamais ne m’y encourage. (31c-d)</blockquote> Cette voix a trois caractéristiques remarquables. Elle est toujours dissuasive : « jamais elle ne m’y encourage ». Elle est personnelle : elle ne s’adresse qu’à Socrate. Elle est présente depuis l’enfance, donc constitutive de son rapport au monde. Elle s’est précisément opposée à son entrée en politique. Il s’agit donc, littéralement, de cette « divinité nouvelle » que l’accusation lui impute, ce que Mélétos, ironise Socrate, a d’ailleurs « consigné dans son acte d’accusation » (31d). Cette ironie est cinglante : l’accusation a pris pour un crime ce que Socrate revendique comme une grâce. La nature du ''daimónion'' a fait l’objet de multiples interprétations, dès l’Antiquité et jusqu’aux temps modernes. Plutarque a consacré un traité entier à la question (''Du démon de Socrate'') dans lequel il discute plusieurs hypothèses<ref>Plutarque, ''Du démon de Socrate'' (''De genio Socratis''), dans ''Œuvres morales'', t. VIII, trad. J. Hani, Paris, Les Belles Lettres, 1980.</ref>. À l’époque moderne, on l’a interprété tour à tour comme une hallucination d’un névrosé<ref>F. Lélut, ''Du démon de Socrate, spécimen d’une application de la science psychologique à celle de l’histoire'', Paris, Trinquart, 1836.</ref>, comme une manifestation de l’inconscient<ref>Arthur Koestler, ''Le Démon de Socrate'', Paris, Calmann-Lévy, 1970.</ref>, comme la voix de la conscience morale<ref>G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', tome II (sur Socrate), trad. G. Marmasse, Paris, Vrin, 2007, p. 316-321.</ref>, comme une inspiration divine authentique<ref>Henri Bergson, ''Les Deux sources de la morale et de la religion'' (1932), dans ''Œuvres'', éd. du Centenaire, Paris, PUF, 1959, p. 1027.</ref>, comme une intuition irrationnelle<ref>E. R. Dodds, ''Les Grecs et l’irrationnel'' (1951), trad. M. Gibson, Paris, Flammarion, coll. « Champs », 1977, chap. VII.</ref>. Nietzsche y voyait, de manière originale, la monstruosité d’un homme chez qui l’instinct, contrairement à l’ordinaire, ne crée pas mais critique, et chez qui la conscience rationnelle est au contraire créatrice : Socrate comme « homme théorique », rupture dans l’histoire de l’esprit grec dionysiaque<ref name="nietzsche-nt">Friedrich Nietzsche, ''La Naissance de la tragédie'' (1872), § 13-15, trad. P. Lacoue-Labarthe, dans ''Œuvres philosophiques complètes'', t. I, Paris, Gallimard, 1977.</ref>. Il faut probablement admettre, avec Claude Chrétien, que le démon est irréductible à une simple astuce défensive ou à un symbole : Socrate y croyait réellement, au point de risquer sa vie en le suivant<ref>Chrétien, ''op. cit.'', p. 28-29.</ref>. Son caractère purement négatif (il inhibe, ne prescrit jamais) en fait un signe du divin dans la vie humaine, mais un signe essentiellement limitatif : la divinité indique seulement ce qu’il ne faut pas faire, et laisse à l’homme la responsabilité de chercher, par l’examen rationnel, ce qu’il doit faire. Cette structure, où le divin ne donne pas la vérité mais seulement la limite, est cohérente avec la théologie négative que Socrate manifeste dans tout le plaidoyer : nous ne savons pas positivement ce que sont les dieux, mais nous recevons d’eux des signes qui nous empêchent de nous égarer. ===== Pourquoi pas la politique ? (31d-32a) ===== Socrate explique que si le démon l’a détourné de la politique, c’est pour préserver sa vie : « s’il y avait longtemps que j’avais entrepris de faire de la politique, il y a longtemps que je serais mort ». Et il formule alors une sentence vertigineuse, qui est peut-être la plus critique de l’''Apologie'' à l’égard de la démocratie athénienne : <blockquote>il n’y a personne au monde qui puisse garder la vie sauve s’il s’oppose loyalement à vous ou à toute autre collectivité, et s’il cherche à empêcher qu’il ne se produise dans la cité de nombreuses injustices et illégalités. Mais nécessairement tout vrai champion de la justice, s’il veut garder la vie sauve ne serait-ce qu’un peu de temps, doit vivre en simple particulier (''idiōteúein'') mais non en homme public. (32a)</blockquote> Cette sentence est d’une portée immense. Elle signifie que la politique telle qu’elle se pratique à Athènes est incompatible avec la justice. Le juste, s’il veut vivre, doit rester à l’écart des affaires publiques ; et s’il y entre, il doit s’attendre à mourir. C’est la cassure socratique avec la tradition civique grecque, qui voyait dans la participation politique (''politeía'') la plus haute réalisation de l’homme libre. L’homme proprement libre, pour les Grecs classiques, c’est le citoyen qui prend part aux assemblées ; l’''idiṓtēs'' qui se retire dans la sphère privée est, sinon méprisé, du moins considéré comme incomplet. Socrate renverse cette hiérarchie : la vraie vie politique, pour le juste, passe par le retrait de la politique officielle et par une politique privée, celle de la discussion personne à personne, de l’enseignement moral qui opère non par les discours publics mais par l’examen intime de chacun. C’est, comme le suggère la présentation de la collection Flammarion, « l’espace d’une autre politique »<ref name="brisson-mace-presentation">Arnaud Macé, « Présentation », dans Platon, ''Apologie de Socrate'', traduction par Luc Brisson, Paris, GF-Flammarion, 2017.</ref>. Cette thèse, qu’on peut lire comme une désertion civique, est aussi une critique profonde des conditions de la délibération démocratique. Elle rejoint ce que Platon développera dans la ''République'' : la cité idéale est celle où la philosophie serait au pouvoir, non celle où elle est écrasée par le plus grand nombre. Mais l’''Apologie'' n’est pas encore la ''République'' : Socrate n’y propose pas une contre-cité, il y constate seulement qu’aucune cité existante ne permet au juste de participer à ses affaires sans se renier. ===== Les deux épisodes probants (32a-e) ===== Socrate prouve cette thèse par deux épisodes biographiques, choisis avec soin. Le premier se situe sous la démocratie, en 406, l’année du procès des généraux des Arginuses. Les Athéniens avaient remporté une victoire navale importante contre Sparte près des îles Arginuses (au large de Lesbos), mais les généraux victorieux avaient été empêchés par une tempête de ramasser les cadavres et les naufragés athéniens, violation grave des usages religieux. Rentrés à Athènes, ils furent mis en cause ; mais au lieu de leur garantir un procès individuel comme l’exigeait le droit athénien, l’Assemblée, emportée par la colère populaire, voulut les juger en bloc. Socrate siégeait ce jour-là au Conseil, comme prytane pour sa tribu, l’Antiochide. Il fut le seul des cinquante prytanes à refuser de mettre aux voix cette motion collective, malgré les menaces et les cris de la foule<ref>Le récit détaillé du procès des généraux des Arginuses se trouve chez Xénophon, ''Helléniques'', I, 7, 1-35.</ref>. Les orateurs voulaient le faire arrêter sur-le-champ, les citoyens eux-mêmes l’y encourageaient ; il tint bon. Les généraux furent néanmoins jugés et six d’entre eux exécutés. Peu après, Athènes regretta sa décision, mais Socrate avait risqué sa vie pour la légalité, sans succès immédiat. Le second épisode se situe sous l’oligarchie, en 404. Les Trente l’avaient convoqué avec quatre autres citoyens à la Tholos (la rotonde, siège des prytanes occupée par le régime) et lui avaient ordonné d’aller chercher à Salamine un riche citoyen démocrate, Léon, pour qu’il soit exécuté et que ses biens soient confisqués. C’était une manœuvre classique des Trente : compromettre un maximum de citoyens dans leurs crimes pour les rendre solidaires du régime<ref>Xénophon, ''Helléniques'', II, 3, 39 ; Platon, ''Lettre VII'', 324d-325a.</ref>. Socrate, lui, refusa l’ordre. Il rentra simplement chez lui pendant que les quatre autres allaient chercher Léon, qui fut assassiné. Socrate, rappelle-t-il, aurait probablement payé cela de sa vie si les Trente n’avaient pas été renversés peu après (c’était le cas : le régime tomba en 403). Ces deux épisodes sont politiquement remarquables, et Platon les a sans doute choisis avec une intention nette. Ils montrent Socrate s’opposant également aux excès de la démocratie (procès des Arginuses) et à ceux de l’oligarchie (affaire de Léon), par fidélité à une justice supérieure au régime en place. Il n’est ni un démocrate de conviction ni un oligarque : il est un homme qui, dans l’un et l’autre cas, risque sa vie pour ne pas commettre d’injustice. Cette double symétrie est cruciale : elle répond par avance à tous ceux qui, dans la démocratie restaurée de 399, voudraient voir en Socrate un sympathisant des Trente, en raison de ses liens avec Critias et Charmide. Platon montre au contraire que Socrate a résisté aux Trente au péril de sa vie. Mais il montre également qu’il a résisté à la démocratie elle-même, quand celle-ci violait le droit. La neutralité politique de Socrate, ou plutôt cet au-delà du partisanisme, constitue une part de sa radicalité, qui déconcerte tous ceux qui voudraient l’enrôler dans un camp. ==== Socrate et ses « disciples » (33a-34b) ==== Socrate en vient alors au dernier volet du premier discours : la question des jeunes gens qu’on l’accuse d’avoir corrompus. Il récuse d’abord le terme même de « disciple » : <blockquote>je n’ai jamais, moi, été le maître (''didáskalos'') de personne. (33a)</blockquote> Cette affirmation est importante. Elle distingue radicalement Socrate des sophistes, qui se présentaient comme maîtres (''didáskaloi'') et vendaient un enseignement structuré ; Socrate n’a jamais promis un enseignement, jamais fait payer, jamais suivi un programme ; il a parlé à quiconque voulait l’écouter, jeunes et vieux, riches et pauvres, sans distinction, et n’est pas responsable de ce que chacun devient au sortir de la conversation. Cette posture est philosophiquement significative : elle implique que la philosophie n’est pas transmissible comme une technique, mais seulement comme une pratique qu’on ne peut qu’imiter. Socrate produit alors un argument a contrario d’une grande force. Si réellement il avait corrompu les jeunes, pourquoi n’est-ce pas ''eux-mêmes'', devenus adultes, qui viendraient témoigner contre lui ? Ou du moins leurs proches, parents, frères, qui auraient à se plaindre de cette corruption et en seraient les premiers concernés ? Or, bien au contraire, nombre de ses familiers, ou de leurs proches, sont présents à l’audience pour le soutenir. Socrate en énumère plusieurs, nommément, dans un passage qui vaut témoignage historique : Criton et son fils Critobule, Lysanias de Sphettos père d’Eschine (l’auteur de dialogues socratiques), Antiphon père d’Épigène, Nicostratos frère de Théodotos, ainsi qu’Adimante (le frère aîné de Platon) et plusieurs autres<ref>''Apologie'', 33d-34a. Sur ces personnages, voir Debra Nails, ''The People of Plato: A Prosopography of Plato and Other Socratics'', Indianapolis, Hackett, 2002.</ref>. <blockquote>Je pourrais citer pour vous beaucoup d’autres hommes, parmi lesquels il aurait fallu que Mélétos produise, au cours de son discours, quelque témoin. (34a)</blockquote> Cette preuve par les témoins absents est d’une grande force logique : l’accusateur a été incapable de trouver, parmi tous ceux qui auraient dû être les premières victimes, un seul pour le blâmer. C’est ce qu’on appelle un argument ''a silentio'' : le silence des supposées victimes prouve l’innocence de l’accusé. On notera que Platon se fait ici historien. En nommant les disciples présents, il fixe un moment dans le temps et offre à la postérité un témoignage vérifiable. Il se nomme lui-même plus loin (34a, puis 38b), parmi les amis prêts à se porter caution pour l’amende. Cette présence documentaire est rare dans les dialogues platoniciens, où Platon s’efface généralement : ici, il est témoin du procès de son maître. ==== Le refus du pathos (34b-35d) ==== Socrate aborde alors la péroraison de son premier discours. Il sait parfaitement ce qu’on attend d’un accusé athénien au moment crucial : larmes, supplications, présentation de la femme et des enfants éplorés, appel à la pitié, théâtralité de la détresse. Cette mise en scène, connue de tous par les comédies d’Aristophane et par l’habitude des tribunaux, était devenue quasi rituelle<ref>Sur la caricature du tribunal populaire, voir Aristophane, ''Les Guêpes'', v. 548-630.</ref>. Socrate a trois fils, dont l’un est déjà adolescent (''meirákion'') et les deux autres encore petits ; il pourrait les faire paraître, lui qui ne refuse pas d’être un homme. Mais il ne le fera pas. Pourquoi ? Deux motifs convergent. D’abord, par souci de l’honneur : un homme de sa réputation, réelle ou supposée, ne peut s’abaisser à ces scènes sans se ridiculiser et sans « ridiculiser la cité ». Les citoyens étrangers qui l’observent et qui connaissent la réputation d’Athènes s’étonneraient de voir les plus éminents de ses hommes se comporter de manière indigne. Socrate invoque la ''dóxa'' (l’opinion) d’Athènes aux yeux du monde grec pour justifier son refus de jouer le jeu. Mais surtout, et c’est le second motif, plus profond : il ne serait pas juste de supplier le juge. Et ici, Socrate formule une analyse capitale de la fonction judiciaire : <blockquote>le juge ne siège pas pour réduire la justice en faveur (''charízesthai''), mais pour décider de ce qui est juste ; et il a fait serment non de favoriser qui lui plaît, mais de rendre la justice selon les lois. (35c)</blockquote> Le juge athénien prêtait en effet un serment (l’''héliastikós hórkos'') par lequel il s’engageait à juger selon les lois<ref>Sur le serment héliastique, voir Démosthène, ''Contre Timocrate'', 149-151 ; Adriaan Lanni, ''Law and Justice in the Courts of Classical Athens'', Cambridge, Cambridge University Press, 2006, p. 75-76.</ref>. Supplier les juges, c’est leur demander de se parjurer, donc d’introduire le parjure dans la cité, donc de commettre une impiété effective contre les dieux garants du serment. Le geste de Socrate est ici d’une cohérence parfaite et quasi mathématique. Lui qui est accusé d’impiété ne peut, à l’instant critique, demander aux juges de commettre une vraie impiété (le parjure) pour le sauver. Ce serait confirmer dans les faits, activement, l’accusation qu’il réfute en paroles. Il préfère la mort à cet abaissement, qui serait en outre, non plus imaginairement mais réellement, une atteinte aux dieux de la cité. Par ce refus, Socrate démontre par les actes ce qu’il prétendait par les mots : il est le véritable pieux, et ce sont les accusateurs qui, en voulant la mort d’un juste, pratiquent la vraie impiété. Le premier discours s’achève là. Les juges votent. Socrate est déclaré coupable. La majorité est étroite : si trente voix s’étaient portées sur l’autre bord, Socrate aurait été acquitté. Pour un jury de 501, cela suggère un vote d’environ 280 contre 221 (chiffres que Diogène Laërce confirme dans son récit<ref>Diogène Laërce, ''Vies et doctrines des philosophes illustres'', II, 41.</ref>, mais qui ne sont pas dans Platon). Ce qui est frappant, c’est la relative faiblesse de la condamnation : l’acquittement était à portée. === Le second discours : la contre-peine (35e-38b) === La procédure athénienne exige maintenant que Socrate propose une peine alternative à celle réclamée par l’accusation. Mélétos a proposé la mort. L’usage voulait qu’on proposât une peine sensiblement moins sévère (un lourd exil, une amende importante) pour donner au jury une alternative crédible. Le calcul tacite, dans les procès à peine à estimer, consistait à offrir une sanction à peine en deçà de celle demandée par l’accusateur, de manière à ne pas trop décevoir les attentes du jury tout en se ménageant un sort moins dur. Socrate va adopter une tout autre stratégie. ==== La contingence du vote (35e-36b) ==== Socrate remarque d’abord, avec une ironie qui frise la provocation, qu’il est étonné non pas d’avoir été condamné, mais de l’avoir été à une si faible majorité. Il s’attendait à une condamnation bien plus nette. Si trente voix de plus avaient basculé, il aurait été acquitté. Il observe aussi que, ce qui le perd véritablement, ce n’est pas Mélétos : car sans l’appui d’Anytos et de Lycon, le seul Mélétos, compte tenu des voix obtenues, aurait dû payer mille drachmes d’amende pour n’avoir pas recueilli le cinquième des suffrages (règle destinée à décourager les plaintes frivoles). En divisant malicieusement les voix reçues entre ses trois accusateurs, Socrate montre que Mélétos seul n’aurait pas obtenu sa condamnation. Cette remarque, en apparence technique, est profondément déstabilisante : elle montre que la procédure qui vient de condamner Socrate est elle-même contingente, dépendante du nombre d’accusateurs autant que du fond du dossier. Elle suggère, au passage, que la véritable force du parti de l’accusation réside dans Anytos, l’homme politique, non dans Mélétos, le plaignant nominal. Le procès apparaît donc comme un montage politique, sous un habillage religieux. ==== La proposition du Prytanée (36b-37a) ==== Quelle contre-peine proposer ? Socrate prend la question au sérieux, mais dans un sens retourné : quelle peine mérité-je ? Il rappelle toute sa vie : avoir négligé les affaires, l’argent, les magistratures, les assemblées et les honneurs, pour se consacrer au service privé de la vertu, <blockquote>en essayant de convaincre chacun d’entre vous de ne pas se préoccuper de ses affaires personnelles avant de se préoccuper, pour lui-même, de la façon de devenir le meilleur et le plus sensé possible. (36c)</blockquote> Que mérite un homme ainsi ? Un bon traitement, dit-il, et non une peine. Il propose donc non pas un châtiment, mais une récompense : être nourri aux frais de l’État au Prytanée. Le Prytanée était à Athènes l’édifice public où la cité nourrissait, aux frais de l’État, les prytanes en exercice, les hôtes officiels et les citoyens illustres, notamment les vainqueurs des jeux Olympiques et les bienfaiteurs de la patrie. C’était la plus haute distinction civique, l’équivalent d’une reconnaissance par l’État comme héros ou sauveur de la cité. Socrate explique qu’il la mérite plus que quiconque : <blockquote>si celui-ci [le vainqueur olympique] vous procure l’apparence du bonheur, je vous en offre, moi, la réalité ; lui n’a aucun besoin d’être nourri, mais moi, j’en ai besoin. (36d-e)</blockquote> L’argument est double : Socrate est un bienfaiteur réel (plus que le champion olympique, dont la gloire n’est que sportive) et il est pauvre (donc il a besoin de ce soutien alimentaire, alors que le champion en a moins besoin). Cette proposition est manifestement provocatrice, et aucun commentateur n’en doute. Socrate ne joue plus le jeu de la procédure ; il retourne le tribunal. Pour un jury qui vient de le condamner, proposer d’être traité en héros civique est une humiliation délibérée. Comment comprendre cette audace ? Plusieurs explications se combinent. D’abord, une cohérence logique : Socrate est convaincu de n’avoir commis aucune injustice, il refuse donc de s’infliger une peine comme s’il était coupable. Ensuite, une fidélité à sa parole : s’il proposait une peine qu’il estime injuste, il trahirait son principe d’agir toujours selon le juste. Enfin, peut-être, une acceptation anticipée de la mort : il est âgé (soixante-dix ans, espérance de vie largement dépassée dans l’Antiquité), il a accompli sa mission, il ne craint pas la sentence, il n’a donc aucune raison de ruser. À quoi s’ajoute, plus subtilement, un calcul dramatique : proposer une vraie contre-peine reviendrait à reconnaître la compétence du tribunal ; en proposant une récompense, Socrate refuse symboliquement la sentence avant même qu’elle ne tombe. ==== L’examen des autres peines et la proposition d’amende (37a-38b) ==== Socrate continue son raisonnement avec une rigueur didactique : puisque je sais que je ne me cause volontairement de tort à personne, je ne vais pas, loin de là, m’en causer à moi-même en proposant une peine qui serait un tort. Il passe alors en revue, méthodiquement, les peines possibles. La prison ? Ce serait passer sa vie soumis au pouvoir des Onze, les magistrats qui administraient les prisons et les exécutions, donc à une sujétion indigne. Une amende lourde assortie de contrainte par corps jusqu’au paiement ? Cela revient au même : il n’a pas d’argent. L’exil ? C’est ici que Socrate développe sa réponse la plus fine. Il serait absurde, dit-il, de choisir l’exil : si ses propres concitoyens ne supportent plus ses entretiens, au point d’en vouloir « se débarrasser », pourquoi les étrangers les supporteraient-ils davantage ? Il serait chassé de ville en ville. Et ne pourrait-il vivre en se taisant ? Non, et ici vient l’un des sommets du texte : cesser de philosopher équivaudrait à désobéir au dieu. <blockquote>Il n’y a pas pour un homme de plus grand bien que de s’entretenir chaque jour de la vertu et des autres sujets dont vous m’entendez discuter, en examinant moi-même les autres ; car une vie sans examen n’est pas digne d’être vécue par un homme. (38a)</blockquote> Cette dernière formule, en grec ''ho anéxetastos bíos ou biōtós anthrṓpōi'', est probablement la plus célèbre de toute l’''Apologie'' et peut-être de toute la philosophie antique. Elle condense le programme de la philosophie socratique : la vie doit être soumise à un examen (''exétasis'') constant, à une interrogation rationnelle sur ses buts et ses valeurs. Sans cet examen, on ne vit pas une vie proprement humaine. Formule vertigineuse, qui fait de la philosophie non pas un luxe intellectuel mais la condition même d’une existence digne de ce nom. Elle suggère qu’il existe un seuil d’humanité : en deçà de l’examen, l’homme n’est pas pleinement homme. La philosophie cesse alors d’être une option ajoutée à la vie pour devenir l’élément qui en fait une vie humaine. Finalement, Socrate concède une mine d’argent (somme modique, correspondant à peu près à trois mois de salaire d’un ouvrier qualifié), mais accepte, sur la pression de ses amis (Platon, Criton, Critobule, Apollodore), de proposer trente mines, avec leur caution personnelle. C’est une somme importante, équivalent à plusieurs années de salaire, mais manifestement insuffisante face à la proposition de mort, et la manière dont elle est introduite (sous la pression des amis, comme en dernier recours) ne masque pas que Socrate lui-même n’y adhère pas vraiment. Le jury vote une seconde fois. Cette fois, la majorité est beaucoup plus nette : selon les chiffres traditionnels rapportés par Diogène Laërce, quatre-vingts jurés supplémentaires ont voté contre Socrate par rapport au premier vote, manifestement indignés par son attitude. Socrate est condamné à mort. === Le troisième discours : après la condamnation (38c-42a) === Ce troisième discours n’est pas prévu par la procédure. Une fois la sentence prononcée, les magistrats passent aux formalités d’enregistrement, notamment la notification aux Onze, chefs des geôliers et du bourreau. Socrate prend pourtant la parole une dernière fois, sans doute pendant que les Onze procèdent à ces écritures, pour s’adresser à la partie de l’assistance qui est restée sur place. Il s’agit d’une péroraison spontanée, hors procédure, probablement élargie et composée par Platon pour les besoins du texte, même si l’événement lui-même est plausible. Ce discours se divise en deux moments : d’abord aux jurés qui ont voté sa mort, puis à ceux qui ont voté son acquittement. ==== Aux jurés de la condamnation (38c-39d) ==== Socrate commence par un constat ironique : les Athéniens ont gagné peu de temps, il est âgé, il serait mort bientôt de toute façon. Mais en échange, ils vont gagner <blockquote>le renom, auprès des gens avides de diffamer notre cité, d’avoir fait mourir un sage en la personne de Socrate. (38c)</blockquote> La réputation d’Athènes souffrira de ce procès bien au-delà de ce qu’elle a cru gagner. La prédiction est parfaitement exacte : la condamnation de Socrate a, dans la mémoire collective de l’Occident, sérieusement terni l’image de la démocratie athénienne. Surtout, Socrate retourne contre les juges l’argument central de son plaidoyer : s’il n’a pas réussi à les persuader, ce n’est pas faute d’arguments, mais parce qu’il n’a pas voulu employer les tactiques indignes (larmes, supplications) qu’ils attendaient. <blockquote>Je préfère de beaucoup mourir après m’être défendu comme je l’ai fait plutôt que vivre après un plaidoyer à leur façon. (38e)</blockquote> Et il prononce cette antithèse fameuse : <blockquote>la difficulté n’est pas d’échapper à la mort, elle est bien plus d’échapper à la lâcheté (''ponēría''), car elle court plus vite que la mort. En l’occurrence, moi qui suis lent et vieux, j’ai été rattrapé par la plus lente des deux [la mort], cependant que mes accusateurs, qui sont lestes et rapides, ont été rattrapés par la plus rapide, qui est la méchanceté. (39a-b)</blockquote> L’image est saisissante : chaque homme est poursuivi par son destin, mais les uns sont rattrapés par la mort physique et les autres par la mort morale, infiniment plus grave. Socrate se permet alors une prophétie (''manteúomai''). Il est, dit-il, <blockquote>au point où les hommes prophétisent le mieux, quand ils sont à la veille de mourir, (39c)</blockquote> allusion à la croyance grecque selon laquelle les mourants acquièrent un don de divination (voir le motif du chant du cygne dans le ''Phédon'' 84e<ref>Platon, ''Phédon'', 84e-85b : « les cygnes [...], lorsqu’ils sentent qu’ils vont mourir, chantent ce jour-là plus fort et plus beau qu’ils n’ont jamais chanté, dans la joie d’aller trouver le dieu dont ils sont les serviteurs ».</ref>). Sa prophétie est terrible : <blockquote>un châtiment vous viendra aussitôt après ma mort, bien plus pénible que celui par lequel vous m’aurez tué. [...] Le nombre croîtra de ceux qui vous demanderont des comptes, que je retenais jusqu’ici, sans que vous vous en aperceviez ; et ils seront d’autant plus pénibles qu’ils sont plus jeunes. (39c-d)</blockquote> Tuer n’est pas la façon de se délivrer du blâme ; la seule manière honorable est « de se préparer soi-même à être le meilleur possible ». Cette prophétie peut sembler s’accomplir, dans une certaine mesure, par l’histoire qui suivra : les « petits socratiques » (Antisthène le cynique, Aristippe, Euclide de Mégare), puis Platon et son Académie, puis Aristote et le Lycée, poursuivront inlassablement l’interrogation commencée par Socrate. Le procès et la mort de Socrate peuvent ainsi être lus comme le moment à partir duquel la philosophie s’affirme, dans la tradition platonicienne, comme une vocation publique à part entière. ==== Aux jurés de l’acquittement (39e-42a) ==== Socrate se tourne alors vers ceux qui ont voté pour lui, qu’il appelle désormais, seuls, ''juges'' véritables. Cette distinction terminologique est capitale : avant le verdict, tous étaient indistinctement ''andres'' (« messieurs ») ou ''Athēnaîoi'' (« Athéniens ») ; maintenant, seuls ceux qui ont voté juste méritent, selon Socrate, le titre de juges (''dikastaí''). Le tribunal vient d’être scindé en deux catégories asymétriques. Il leur confie deux pensées, l’une sur le signe divin, l’autre sur la mort. ===== Le silence du démon (39e-40c) ===== Son démon, dit-il, avait coutume de l’arrêter chaque fois qu’il s’apprêtait à faire quelque chose de mauvais, même dans des circonstances mineures. Or, aujourd’hui, depuis le matin, tout au long de cette journée qui l’a mené à la condamnation à mort, le démon ne s’est pas manifesté une seule fois. Il ne l’a pas arrêté au moment où il quittait son domicile, ni lorsqu’il montait au tribunal, ni à aucun moment de son plaidoyer. Cette absence est elle-même un signe : <blockquote>il y a des chances pour que ce qui m’est arrivé soit un bien ; et c’est nous qui faisons des suppositions incorrectes quand nous considérons la mort comme un mal. (40b-c)</blockquote> Le silence du démon est la preuve, pour Socrate, que la voie qu’il suit est la bonne. L’argument est philosophiquement subtil. Socrate ne dit pas qu’il sait que la mort est un bien ; il dit qu’il a une raison de penser que ce qui lui arrive est un bien, puisque le dieu (par la voix du démon) ne l’a pas arrêté. C’est un argument ''e silentio'' transposé sur le plan religieux : le silence divin, pour qui est habitué à être averti, vaut approbation. Cette structure de raisonnement est fragile, mais elle est cohérente avec la théologie socratique : la divinité se manifeste par ses interventions plutôt que par ses paroles positives ; son silence, quand il y a habitude d’intervention, est significatif. ===== Les deux hypothèses sur la mort (40c-41d) ===== [[Fichier:David - The Death of Socrates.jpg|vignette|centre|upright=2.0|Jacques-Louis David, ''La Mort de Socrate'', 1787, huile sur toile, 129,5 × 196,2 cm, New York, Metropolitan Museum of Art. Le tableau illustre à proprement parler la scène finale du ''Phédon'' plutôt que l’''Apologie'' ; il est cependant devenu l’image emblématique, dans la culture occidentale moderne, du philosophe maintenant, jusque dans la mort, la cohérence entre sa parole et sa vie.]] Socrate propose alors une méditation sur la mort, l’une des plus belles pages de la philosophie antique, construite comme une alternative raisonnée. De deux choses l’une : ou bien la mort est l’absence de toute sensation, ou bien elle est une migration (''metoíkēsis'') de l’âme de ce lieu vers un autre lieu. Dans la première hypothèse (la mort comme absence de sensation), la mort ressemble au sommeil sans rêve. Or qui n’aimerait pas, si on lui demandait de choisir entre une nuit de sommeil profond sans rêves et toutes les autres nuits et jours de sa vie, reconnaître que cette nuit est plus précieuse que la plupart ? Même le Grand Roi de Perse (homme réputé le plus heureux du monde aux yeux des Grecs) trouverait peu de jours comparables à une telle nuit. Si la mort est cela, alors la totalité du temps après la mort se réduit à « une seule nuit », et cette nuit est un gain. L’argument est intéressant par sa structure : il part d’une expérience commune (le sommeil sans rêve) pour désamorcer la peur métaphysique de la mort. Si l’on aime le sommeil quand il nous prend, pourquoi craindre la mort si elle lui ressemble ? La stratégie argumentative, qui rappelle celle d’Épicure et de Lucrèce plus tard (« la mort n’est rien pour nous »<ref>Épicure, ''Lettre à Ménécée'', § 125 : « la mort n’est rien pour nous, puisque tant que nous sommes, la mort n’est pas, et quand la mort est là, nous ne sommes plus ». Lucrèce, ''De la nature des choses'', III, v. 830 et suiv.</ref>), désarticule la crainte en la confrontant à ce que nous expérimentons quotidiennement. Dans la seconde hypothèse (la mort comme migration), la mort est un voyage vers l’au-delà, où l’on retrouve tous les morts. Que pourrait-on imaginer de plus heureux ? On serait délivré des juges qui prétendent juger ici-bas, pour rencontrer les vrais juges dont on dit qu’ils y rendent la justice : Minos, Rhadamante, Éaque (les trois juges infernaux traditionnels), plus Triptolème (qui les remplace parfois dans l’iconographie attique). On rencontrerait Orphée, Musée, Hésiode, Homère, les grandes figures poétiques et religieuses du passé. On pourrait y continuer, sans cette fois risquer la mort, l’activité d’examen qui fut la sienne sur terre : interroger Palamède, Ajax (tous deux morts par jugements injustes, comme lui-même : la comparaison est évidemment à son avantage), le chef de l’armée grecque à Troie (Agamemnon), Ulysse, Sisyphe, « et tant d’autres hommes et femmes qu’on pourrait nommer ». <blockquote>Discuter avec ceux de là-bas, vivre en leur société, les soumettre à examen, ne serait-ce pas le comble du bonheur ? Aussi bien, les gens de là-bas ne mettent à mort personne pour ce motif. (41c)</blockquote> Ce passage est riche de tonalités. Il y a une évidente dimension humoristique : Socrate imagine l’au-delà comme la continuation indéfinie de son activité terrestre, l’examen dialectique, mais cette fois sans risque, puisqu’on n’y meurt plus. L’Hadès devient une agora élargie à tous les temps. Il y a également une dimension consolatoire : la comparaison avec Palamède et Ajax, héros victimes de procès injustes, ennoblit le sort de Socrate. Il y a enfin une dimension ironique vis-à-vis des jurés athéniens : les vrais juges ne sont pas à Athènes, mais dans l’Hadès, et Socrate se réjouit d’aller les retrouver. Ce passage a suscité des interprétations contrastées. Certains commentateurs y voient une réelle espérance platonicienne en l’immortalité de l’âme, telle qu’elle sera développée dans le ''Phédon''<ref>Platon, ''Phédon'', 80a-84b, 105c-107a.</ref>. D’autres, comme Chrétien, soulignent que le Socrate de l’''Apologie'' reste fondamentalement agnostique : il présente deux hypothèses, il ne tranche pas entre elles, et l’imagination y a au moins autant de part que la raison<ref>Chrétien, ''op. cit.'', p. 32-36.</ref>. Socrate lui-même conclut prudemment : <blockquote>aucun mal ne peut toucher un homme de bien ni pendant sa vie ni après sa mort, et les dieux ne se désintéressent pas de son sort. (41d)</blockquote> Cette conclusion n’affirme pas dogmatiquement une survie, mais énonce une foi pratique : quoi qu’il arrive, le juste n’a rien à craindre. L’''Apologie'', contrairement au ''Phédon'', ne construit pas de doctrine positive sur l’immortalité ; elle se tient au seuil d’une telle doctrine, dans un agnosticisme serein. ===== Le testament (41e-42a) ===== Socrate clôt son discours par un testament pour ses fils. Il ne demande qu’une chose aux Athéniens : <blockquote>Quand mes fils seront grands, punissez-les, citoyens, en les tourmentant comme je vous tourmentais, pour peu qu’ils vous paraissent se soucier d’argent ou de n’importe quoi d’autre plus que de la vertu. Et, s’ils croient être quelque chose, alors qu’ils ne sont rien, adressez-leur le reproche que je vous adressais. (41e)</blockquote> La mission philosophique se transmet ainsi, comme un héritage inversé : Socrate demande à ses bourreaux de devenir eux-mêmes, envers ses enfants, ce qu’il était pour eux, des tourmenteurs par la vertu. C’est le pardon actif d’un homme qui refuse de laisser sa mort briser la chaîne de l’examen. Puis vient la dernière phrase, l’une des plus célèbres de la littérature philosophique : <blockquote>Mais voici déjà l’heure de partir, moi pour mourir et vous pour vivre. De mon sort ou du vôtre lequel est le meilleur ? La réponse reste incertaine pour tout le monde, sauf pour la divinité. (''plḕn hē tôi theôi'', 42a)</blockquote> Cette clôture ouvre, sur l’indécidable, l’agnosticisme ultime. Socrate ne sait pas, nul ne sait, lequel est le plus heureux, de lui qui va mourir ou de ses juges qui vont continuer à vivre. Seule la divinité le sait. L’''Apologie'' se ferme ainsi sur le mot même de la sagesse socratique : la reconnaissance de l’ignorance humaine, couplée à la confiance paisible en un ordre divin qui excède notre mesure. Rien n’est affirmé dogmatiquement ; tout se termine sur une interrogation qui n’attend pas de réponse humaine. C’est une fin d’une sobriété admirable, qui évite à la fois la plainte et la consolation artificielle. Elle est, par son rythme et par son contenu, digne d’une page d’évangile ou d’un chapitre des ''Pensées'' de Marc-Aurèle. == Concepts et thèmes majeurs == Une lecture suivie ne serait pas complète sans reprendre quelques-uns des grands thèmes qui courent à travers le texte et en font la portée philosophique durable. === La sagesse humaine : le savoir du non-savoir === Le concept central de l’''Apologie'' est celui de la sagesse humaine (''anthrōpínē sophía'', 20d). Socrate n’est pas savant au sens fort du terme, c’est-à-dire à la manière des sophistes qui prétendaient posséder un savoir sur les choses divines, sur la nature, sur la politique, sur la vertu. Mais il possède une sagesse proprement humaine, qui consiste à reconnaître que l’on ne sait pas. Ce savoir du non-savoir n’est pas un scepticisme, encore moins un renoncement. C’est une position éthique : celui qui sait qu’il ne sait pas est disposé à chercher, à interroger, à examiner ; celui qui croit savoir est fermé à toute remise en question et, par là même, incapable de tout progrès. Cette sagesse n’est pourtant pas purement négative. Socrate affirme savoir au moins deux choses : qu’il est mauvais et laid de commettre l’injustice et de désobéir à un meilleur que soi (29b), et que la vie non examinée n’est pas digne d’être vécue (38a). De ces deux « savoirs » découle tout le comportement de Socrate au procès : il ne peut trahir la justice pour sauver sa vie, et il ne peut cesser d’examiner les autres sans trahir sa vocation propre. On voit donc que le savoir du non-savoir n’est pas une position d’ignorance totale, mais une structure articulée : une ignorance avouée sur les grandes questions métaphysiques, et une certitude pratique sur les exigences éthiques. Cette combinaison fait du socratisme une forme de philosophie pratique : elle rend possible l’action juste sans requérir une science achevée. Il faut enfin noter que le savoir du non-savoir est peut-être la thèse la plus féconde du socratisme pour l’histoire de la pensée. Toute la philosophie postérieure, chaque fois qu’elle commence par un doute méthodique (Descartes), par une critique des prétentions de la raison (Kant), par une déconstruction des évidences (phénoménologie), s’inscrit dans le sillage de l’interrogation socratique. La philosophie moderne est, en ce sens, socratique par son geste inaugural, même quand elle ne l’est plus par ses conclusions. === La méthode de l’elenchus === L’''Apologie'' donne à voir, en acte, la méthode philosophique propre de Socrate : l’elenchus ou réfutation par interrogation. Elle apparaît à plusieurs reprises, mais surtout dans l’interrogatoire de Mélétos (24b-28a). Son fonctionnement est simple dans son principe : Socrate ne pose pas directement ses propres thèses ; il prend pour point de départ celles de son interlocuteur, puis, par une série de questions dont chacune requiert une réponse qui semble évidente, il conduit l’interlocuteur à reconnaître des conséquences incompatibles avec d’autres thèses qu’il tient également pour vraies. La contradiction ainsi mise au jour n’est pas celle de Socrate, mais celle de l’interlocuteur avec lui-même. Cette méthode a plusieurs vertus philosophiques. D’abord, elle respecte la liberté de l’interlocuteur : Socrate ne lui impose rien, il l’amène seulement à voir ce qu’il pensait déjà. Ensuite, elle produit un savoir négatif sûr : la réfutation, à défaut de démontrer le vrai, prouve au moins que la thèse examinée est fausse. Enfin, elle a un effet moral : elle introduit chez l’interlocuteur l’expérience de l’''aporía'' (la perplexité), qui est le point de départ possible d’une recherche véritable. L’interlocuteur, déchargé de son illusion de savoir, peut commencer à apprendre. L’elenchus est ainsi, au sens propre, maïeutique : il fait accoucher les esprits. Mais l’elenchus a aussi ses limites, que l’''Apologie'' laisse apercevoir. Il peut blesser l’amour-propre ; il peut créer des haines durables ; il peut donner à ceux qui en sont la cible l’impression d’être humiliés publiquement. Socrate lui-même en témoigne : son enquête a suscité contre lui des rancœurs innombrables. La philosophie a un coût social, que l’elenchus rend visible avec une particulière acuité. === Le souci de l’âme === Le message moral central de l’''Apologie'' tient dans une exhortation : il faut se soucier prioritairement de son âme (''psuchḗ'') et de son amélioration, non de son corps, de sa richesse ou de sa réputation (29d-30b). <blockquote>La vertu ne naît pas de l’argent, mais c’est de la vertu que naissent et l’argent et tout le reste des biens utiles aux hommes, aussi bien privés que publics. (30b)</blockquote> Ce renversement est l’une des sources principales de la morale occidentale : la hiérarchie des biens est réordonnée autour du bien intérieur, et la richesse extérieure n’a de valeur qu’en tant qu’elle découle d’une vertu préalable. Ce souci de soi (''epiméleia heautoû'') n’est pas égoïste. Il est au contraire la condition du souci des autres : on ne peut aider autrui à améliorer son âme sans avoir travaillé à la sienne. Socrate harcèle ses concitoyens parce qu’il veut qu’ils prennent soin de leur âme, non parce qu’il veut sauver la sienne à ses dépens. Et c’est précisément parce qu’il se soucie de la cité qu’il la secoue : le taon pique le cheval pour le réveiller, non pour le faire souffrir. Cette thèse a eu une postérité immense. Elle est reprise, transformée, intériorisée par les écoles hellénistiques (stoïciens, épicuriens), qui en font le cœur de la sagesse pratique. Elle passe ensuite dans le christianisme, où le « soin de l’âme » devient le salut personnel. À l’époque moderne, Michel Foucault lui a consacré une part importante de ses derniers cours, voyant dans le souci de soi une alternative à l’éthique cartésienne fondée sur la seule connaissance de soi<ref>Michel Foucault, ''Le Souci de soi'' (''Histoire de la sexualité'', t. III), Paris, Gallimard, 1984 ; et ''L’Herméneutique du sujet'', ''op. cit.''</ref>. L’''Apologie'' est à l’origine de cette longue tradition, même si le souci de soi socratique reste, par certains aspects, très différent des élaborations postérieures : il est moins un travail sur soi qu’un examen de soi par la discussion avec autrui. === La philosophie comme mission divine === L’''Apologie'' fonde la légitimité de la philosophie sur une mission divine. Socrate ne philosophe pas par goût ou par choix : il le fait parce que le dieu lui en a fait l’ordre, à travers l’oracle de Delphes et à travers le démon. Cette dimension religieuse est essentielle pour comprendre la posture socratique. S’il pouvait cesser d’interroger, il le ferait peut-être (c’est une activité ingrate et dangereuse) ; mais il ne le peut pas, car ce serait désobéir au dieu. La philosophie est ainsi un service sacré, équivalent à celui des prêtres ou des devins, mais accompli par d’autres moyens : non par le rite, mais par l’examen rationnel. Cette dimension place Socrate dans une position paradoxale par rapport aux accusations d’impiété. L’homme que l’on accuse de ne pas croire aux dieux est en réalité celui qui les sert le plus fidèlement, au point de mourir pour leur obéir. Platon retourne ainsi l’accusation : les véritables impies sont ceux qui, en condamnant Socrate, refusent le présent que le dieu leur a fait. Le procès apparaît alors sous un jour inversé : non plus un acte de piété de la cité contre un impie, mais un acte d’impiété de la cité contre un serviteur du dieu. Ce thème a eu un écho particulier dans la tradition chrétienne, qui a parfois vu en Socrate une figure prophétique du Christ : un juste mis à mort par une communauté religieuse qui croyait servir ses dieux en le tuant. Justin martyr, au IIᵉ siècle, comparera explicitement Socrate et Jésus, voyant dans le philosophe athénien une préfiguration providentielle de la Passion<ref>Justin de Naplouse, ''Première Apologie'', 46, 1-4 : les chrétiens considèrent comme chrétiens avant la lettre « ceux qui ont vécu avec le Logos [...] parmi les Grecs, Socrate, Héraclite et ceux qui leur furent semblables ». Voir aussi ''Seconde Apologie'', 10, 5-6.</ref>. La philosophie chrétienne primitive a ainsi trouvé dans l’''Apologie'' une matrice pour penser la martyrologie. === La justice supérieure === Le thème de la justice traverse tout le texte. Socrate se présente comme un homme profondément respectueux des lois : il a risqué sa vie pour le respect de la procédure sous la démocratie (affaire des Arginuses) ; il mourra par fidélité aux lois dans le ''Criton'' plutôt que de s’évader. Mais son obéissance aux lois n’est pas inconditionnelle : il y a une justice supérieure, fondée sur des valeurs, qui commande dans certains cas la désobéissance civile, comme lorsqu’il refuse d’exécuter les ordres des Trente concernant Léon de Salamine. Cette justice supérieure n’est pas un simple idéal abstrait ; elle est, pour Socrate, ce qui fait le prix (''axía'') de la vie humaine. Elle a une origine divine et s’impose à la conscience au-delà des conventions sociales. On voit déjà poindre ici, avant les développements platoniciens de la ''République'', l’idée d’une justice en soi, distincte de la justice légale, et qui fonde celle-ci sans s’y réduire. Antigone, chez Sophocle, invoquait déjà les « lois non écrites » des dieux contre les décrets humains<ref>Sophocle, ''Antigone'', v. 450-460.</ref> ; Socrate, à sa manière, s’inscrit dans cette tradition. Mais il la rationalise : ce n’est plus le simple respect d’une tradition familiale ou religieuse, c’est la fidélité à un ordre supérieur accessible par l’examen rationnel. La philosophie devient ainsi le lieu où se manifeste cette justice transcendante, dont les lois humaines ne sont qu’une approximation imparfaite. Cette double fidélité (aux lois de la cité et à une justice supérieure) est source d’une tension qui traversera toute la tradition philosophique et juridique occidentale. Elle est au cœur des doctrines du droit naturel<ref>Voir notamment Cicéron, ''De republica'', III, 33 sur la ''lex vera'' ; Thomas d’Aquin, ''Somme théologique'', I-II, q. 94 sur la loi naturelle.</ref>, des théories modernes de la désobéissance civile (Thoreau, Gandhi, Martin Luther King), et des débats contemporains sur la légitimité du droit positif. === La mort et le courage === Le rapport de Socrate à la mort est une pièce maîtresse du texte. Sa thèse est à double face. D’un côté, la mort en elle-même est inconnue : nul ne sait si elle est un mal ou un bien, et la craindre comme un mal certain est la plus répréhensible des ignorances. De l’autre, il y a pire que la mort : la lâcheté, l’injustice, l’abandon de son poste. Le courage socratique n’est donc pas, comme celui des héros homériques, la volonté exaltée de mourir pour l’honneur ; c’est la lucidité sur ce qui est réellement à craindre, non la mort mais le vice. Ce renversement a nourri toute la morale stoïcienne (qui en fait un de ses principes cardinaux : ne rien craindre de ce qui ne dépend pas de nous, donc pas la mort) puis, par des voies détournées, la pensée chrétienne du martyre. Il faut insister sur la finesse de l’analyse socratique. Elle ne dit pas : « la mort est un bien » (affirmation dogmatique contraire à son savoir du non-savoir). Elle ne dit pas non plus : « la mort est indifférente » (comme le diront plus tard les stoïciens). Elle dit : « la mort est inconnue, donc je ne peux la craindre comme un mal certain ; mais l’injustice est un mal certain, donc je peux la craindre ». Le courage n’est pas fondé sur une espérance métaphysique, mais sur une hiérarchie des savoirs : le connu prime sur l’inconnu, et j’organise ma conduite en fonction de ce que je sais. Cette analyse aura un long destin. Épicure la reprendra pour dire : « la mort n’est rien pour nous »<ref>Épicure, ''Lettre à Ménécée'', § 125.</ref>. Les stoïciens la transformeront en doctrine de l’indifférence aux choses externes. Montaigne en fera un objet central de ses ''Essais''<ref>Montaigne, ''Essais'', I, 20 « Que philosopher, c’est apprendre à mourir » ; III, 12 « De la physionomie ».</ref>. Heidegger, au XXᵉ siècle, retournera au Socrate de l’''Apologie'' en interrogeant le rapport authentique à la mort comme condition d’une existence propre<ref>Martin Heidegger, ''Sein und Zeit'' (1927), § 46-53, sur l’''être-pour-la-mort''.</ref>. Chaque fois, c’est le même geste inaugural qui est repris, celui d’une mort désarmée par la pensée. === Philosophie et cité : l’autre politique === L’''Apologie'' esquisse une conception originale de la politique. Socrate se déclare non-politique au sens courant du terme : il n’a pas fréquenté l’assemblée, il n’a pas cherché les magistratures, il n’a pas fait carrière publique. Mais il se présente comme le plus politique des Athéniens au sens profond : il s’est occupé de la cité elle-même (plus que de ses affaires), en se souciant du perfectionnement de ses concitoyens. C’est une autre politique, qui ne passe pas par les institutions officielles (corrompues selon lui par la démagogie et l’ignorance) mais par la conversation privée, par l’interpellation personnelle, par la formation morale. Cette vision a un versant critique radical vis-à-vis de la démocratie athénienne, qui émerge des épisodes des Arginuses et de Léon de Salamine : la démocratie, livrée à ses passions, peut se conduire de manière aussi injuste qu’une tyrannie. Il serait naïf de voir en Socrate un démocrate simple ou un anti-démocrate simple ; il est un critique des deux régimes en tant qu’ils s’éloignent de la justice. Mais sa critique vise, au-delà des régimes, la capacité même des collectivités humaines à délibérer justement : « il n’y a personne au monde qui puisse garder la vie sauve s’il s’oppose loyalement à vous ou à toute autre collectivité » (32a). Ce diagnostic, désabusé, n’est pas tant politique qu’anthropologique : les foules, quelles qu’elles soient, résistent mal à l’examen rationnel. Mais cette critique a aussi un versant constructif. La philosophie, par l’examen qu’elle exerce sur les esprits, prépare la possibilité d’une politique véritablement juste. Platon développera cette intuition dans la ''République'', en allant jusqu’à imaginer une cité où les philosophes seraient rois, mais ce développement excède le cadre de l’''Apologie''. Au moment où Platon écrit ce texte, la cité idéale n’est pas encore pensée ; il y a seulement la dénonciation d’une cité qui a tué son meilleur citoyen, et la promesse implicite d’une pensée qui prolongera la mission interrompue. === L’ironie socratique === Un mot enfin sur l’ironie, qui est omniprésente dans le texte, et qu’il faut distinguer en plusieurs registres. Il y a d’abord l’ironie au sens étroit : dire le contraire de ce que l’on pense, comme lorsque Socrate qualifie Mélétos de « bon citoyen » et « patriote ». Il y a ensuite l’ironie dialectique : amener l’interlocuteur à se contredire lui-même par des questions prétendument naïves, alors que Socrate sait parfaitement où il le mène. Il y a enfin l’ironie existentielle : vivre de telle façon que toute son existence est un démenti de ce qu’on attendrait d’un homme dans sa situation. La proposition d’être nourri au Prytanée relève de cette dernière : Socrate, condamné, se présente comme un bienfaiteur ; il retourne les rôles, fait du tribunal une scène tragicomique. Cette ironie n’est pas seulement un trait de style. Elle est l’expression d’une distance philosophique à l’égard des conventions et des évidences. Celui qui a compris que le savoir commun est illusoire, que la rhétorique est trompeuse, que les hiérarchies sociales reposent sur des malentendus, ne peut plus prendre au sérieux les rituels qui ordonnent la vie ordinaire. L’ironie socratique est le signe visible d’une conscience libre, qui refuse de se soumettre aux attentes. C’est pourquoi Søren Kierkegaard, au XIXᵉ siècle, en a fait dans sa thèse sur ''Le Concept d’ironie'' le trait caractéristique de la subjectivité philosophique naissante : avec Socrate, la conscience se sépare du monde, s’intériorise, devient sujet<ref name="kierkegaard">Søren Kierkegaard, ''Le Concept d’ironie constamment rapporté à Socrate'' (''Om Begrebet Ironi'', 1841), trad. P.-H. Tisseau et E.-M. Jacquet-Tisseau, dans ''Œuvres complètes'', t. II, Paris, Éditions de l’Orante, 1975.</ref>. L’ironie est ce mode de la subjectivité qui se pose en se distinguant de ce qui est. == Questions de lecture et postérité == === Le Socrate de l’''Apologie'' et le Socrate historique === Une question classique est de savoir dans quelle mesure l’''Apologie'' restitue fidèlement le plaidoyer effectivement prononcé par Socrate en 399. Les éléments de réponse sont partiels. D’une part, Platon, jeune témoin du procès (il avait environ vingt-huit ans), avait de puissantes raisons d’être fidèle : la mémoire des jurés qui liraient le texte était fraîche, et toute infidélité flagrante aurait nui à la thèse défensive. D’autre part, Xénophon, dans sa propre ''Apologie'', confirme certains points centraux (l’oracle de Delphes, le refus de préparer sa défense, le rôle du démon, l’attitude provocante devant le tribunal). Les convergences entre Platon et Xénophon, qui écrivent séparément, garantissent la réalité d’un noyau historique. Pour autant, l’''Apologie'' de Platon est une œuvre écrite, composée probablement plusieurs années après le procès, et qui obéit aux lois de la composition littéraire. La structure en trois discours parfaitement articulée, la densité dialectique de l’interrogatoire de Mélétos, la beauté rythmique de certaines périodes (la finale : « moi pour mourir et vous pour vivre… ») relèvent de l’art platonicien. Le témoignage historique et la recréation littéraire ne sont pas dissociables. La question du « Socrate historique » a été débattue à l’infini. On distingue traditionnellement plusieurs Socrate : celui d’Aristophane (caricatural), celui de Xénophon (pragmatique, moraliste de bon sens), celui de Platon (dialectique et idéaliste), celui d’Aristote (prédécesseur des idées, attribuant à Socrate la recherche des définitions universelles et l’induction<ref>Aristote, ''Métaphysique'', XIII, 4, 1078b17-30 : « deux choses peuvent à bon droit être attribuées à Socrate : les raisonnements inductifs et la définition universelle ».</ref>). Lequel est le vrai ? La réponse la plus raisonnable est qu’aucun ne l’est entièrement, mais qu’ils donnent, collectivement, une image composite d’un homme dont la puissance personnelle a dépassé de beaucoup ce que les documents peuvent restituer. L’''Apologie'' est probablement, de tous les textes, celui qui se tient le plus près de la voix réellement entendue, parce que Platon y a choisi, exceptionnellement, de s’effacer devant son maître. Il est usuel, chez les commentateurs, de distinguer dans l’''Apologie'' des couches. Certains éléments semblent historiques au plus haut degré : le cadre procédural, les noms des accusateurs, l’attitude refusant la supplication, la référence à Chéréphon (mort avant le procès, mais dont les héritiers étaient vivants pour confirmer ou démentir), la condamnation et son déroulement. D’autres éléments sont vraisemblablement platoniciens : l’articulation en trois discours parfaitement équilibrés, la méditation finale sur la mort, peut-être la prophétie adressée aux juges condamnateurs. Mais tout cela relève d’appréciations délicates, et Platon lui-même dans la ''Lettre VII'' revendique le droit de penser philosophiquement à partir du Socrate qu’il a connu<ref>Platon, ''Lettre VII'', 324d-326b. Sur la question socratique, voir Louis-André Dorion, ''Socrate'', Paris, PUF, coll. « Que sais-je ? », 2004.</ref>. === La postérité de l’''Apologie'' === [[Fichier:Statues of Plato (left) and Socrates (right) by Leonidas Drosis at the Academy of Athens.jpg|vignette|droite|upright=1.2|Statues de Platon (à gauche) et de Socrate (à droite), par le sculpteur Leonidas Drosis (seconde moitié du XIX{{e}} siècle), à l’entrée de l’Académie d’Athènes. La réunion des deux figures sur le fronton d’une institution savante moderne témoigne de la postérité canonique que le procès aura contribué à instituer.]] L’''Apologie de Socrate'' est l’un des textes les plus lus, les plus imités, les plus commentés de la philosophie occidentale. Sa postérité ne se laisse pas résumer en quelques lignes, mais on peut en indiquer quelques étapes majeures. Dans l’Antiquité, l’''Apologie'' est immédiatement imitée : Xénophon en donne sa version ; des apologies perdues, d’Eschine de Sphettos ou de Lysias, avaient également circulé. Au IIᵉ siècle, Apulée compose une ''Apologie'' (sur son propre procès en magie) qui imite la structure platonicienne<ref>Apulée, ''Apologie ou De la magie'', texte établi et traduit par Paul Vallette, Paris, Les Belles Lettres, 1924.</ref>. Les écoles hellénistiques (stoïciens, cyniques) prennent Socrate comme figure tutélaire du sage qui meurt pour sa vérité : Épictète et Marc Aurèle se réfèrent constamment à lui<ref>Voir notamment Épictète, ''Entretiens'', II, 1, 32 ; II, 2, 8-20 ; et Marc Aurèle, ''Pensées'', VII, 19 ; XI, 25, 28.</ref>. Les cyniques voient en lui le philosophe de la pauvreté et de la provocation, exemple d’une existence libre des conventions. La tradition chrétienne primitive trouve dans Socrate une figure prophétique. Justin martyr, au IIᵉ siècle, fait de Socrate un « chrétien avant le Christ », guidé par le Logos divin<ref>Justin, ''Première Apologie'', 46 ; ''Seconde Apologie'', 10.</ref>. Les Pères de l’Église le mentionnent souvent, tantôt pour s’en réclamer (Clément d’Alexandrie, Origène), tantôt pour le mettre à distance (Tertullien)<ref>Clément d’Alexandrie, ''Stromates'', I, 14 ; Tertullien, ''Apologétique'', 46.</ref>. La mort de Socrate, rapprochée du martyre, fournit un modèle de témoignage pour la vérité. Toute la hagiographie martyrologique se nourrit, en partie, de l’''Apologie''. À la Renaissance, la redécouverte de Platon par Marsile Ficin et l’Académie florentine met l’''Apologie'' au centre du canon philosophique<ref>Marsile Ficin traduit les œuvres complètes de Platon en latin (''Platonis Opera Omnia'', Florence, 1484), rendant l’''Apologie'' accessible à l’Europe savante.</ref>. Montaigne, dans les ''Essais'', lui consacre plusieurs chapitres et voit en Socrate la figure même de la sagesse sans science, du bon sens humain qui vaut mieux que toutes les spéculations. <blockquote>Socrate fait mouvoir son âme d’un mouvement naturel et commun. Ainsi dit un paysan, ainsi dit une femme. (Montaigne, ''Essais'', III, 12)<ref>Montaigne, ''Essais'', III, 12 « De la physionomie », édition Villey-Saulnier, PUF, 1965, p. 1037-1038.</ref></blockquote> À l’époque des Lumières, l’''Apologie'' devient un manifeste anticlérical. Voltaire y voit le procès de l’intolérance religieuse, Diderot une défense de la libre pensée, Rousseau un modèle d’éthique civile. David peint ''La Mort de Socrate'' (1787)<ref>Jacques-Louis David, ''La Mort de Socrate'', 1787, huile sur toile, 129,5 × 196,2 cm, New York, Metropolitan Museum of Art.</ref>, tableau emblématique où le philosophe saisit la coupe avec sérénité tandis que ses disciples se lamentent : image iconique qui influencera durablement l’imaginaire philosophique. Au XIXᵉ siècle, Hegel, Kierkegaard et Nietzsche proposent trois lectures marquantes. Hegel, dans ses ''Leçons sur l’histoire de la philosophie'', voit Socrate comme le moment où la conscience morale s’intériorise, et dans sa condamnation le conflit tragique entre l’ancienne cité et la subjectivité naissante<ref>G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', t. II, trad. P. Garniron, Paris, Vrin, 1971, p. 263-328.</ref>. Kierkegaard fait de l’ironie socratique, analysée dans ''Le Concept d’ironie'' (1841), le modèle de la subjectivité existentielle<ref name="kierkegaard" />. Nietzsche, dans ''La Naissance de la tragédie'' (1872), voit au contraire en Socrate le destructeur du tragique grec, l’homme théorique qui substitue la raison critique à la sagesse instinctive, et accomplit ainsi une « monstruosité par défaut »<ref name="nietzsche-nt" />. Les trois lectures sont opposées, mais elles attestent toutes la centralité de Socrate dans la pensée moderne. Au XXᵉ siècle, l’''Apologie'' continue d’être un lieu de lecture privilégié. Hannah Arendt, dans ''La Vie de l’esprit'', en fait le modèle de la pensée responsable<ref>Hannah Arendt, ''La Vie de l’esprit'', trad. L. Lotringer, Paris, PUF, 1981 ; voir aussi « Philosophie et politique », ''Les Cahiers de philosophie'', n° 4, 1987.</ref>. Michel Foucault, dans ses derniers cours au Collège de France (''Le Courage de la vérité'', 1984), y voit le texte fondateur de la ''parrhēsía'', c’est-à-dire du « franc-parler » philosophique qui engage la vie de celui qui parle<ref>Michel Foucault, ''Le Courage de la vérité. Le Gouvernement de soi et des autres II. Cours au Collège de France, 1983-1984'', éd. F. Gros, Paris, Gallimard/Seuil, 2009.</ref>. Leo Strauss et son école ont proposé une relecture « ésotérique » du texte, attentive à ce que Socrate ne dit pas et à ce qu’il suggère entre les lignes<ref>Leo Strauss, ''Studies in Platonic Political Philosophy'', Chicago, University of Chicago Press, 1983 ; ''The City and Man'', Chicago, Rand McNally, 1964.</ref>. Lire l’''Apologie'' aujourd’hui, c’est donc entrer dans un texte sédimenté par vingt-quatre siècles de commentaires, et qui n’a pas encore épuisé sa charge philosophique. Chaque époque y trouve un Socrate à sa mesure, et c’est peut-être la marque des grandes œuvres de pouvoir soutenir cette pluralité indéfinie d’interprétations. == Conclusion == L’''Apologie de Socrate'' n’est pas un plaidoyer ordinaire. Ce n’est même pas, à proprement parler, un plaidoyer au sens technique, puisque Socrate y refuse presque toutes les tactiques qui auraient pu le faire acquitter : il ne se fait pas écrire par un logographe, il ne supplie pas, il ne fait pas paraître ses enfants, il provoque le jury quand il lui faudrait le ménager. C’est une profession de foi philosophique, prononcée au moment où la vie de l’auteur est en jeu, et qui tire précisément de cet enjeu sa gravité unique. L’''Apologie'' est la preuve par la mort d’une thèse que Socrate n’a cessé de soutenir par les mots : la vertu vaut plus que la vie. Trois traits en font un texte fondateur. D’abord, il inaugure la figure du philosophe comme témoin : celui dont la cohérence entre la parole et la vie va jusqu’au sacrifice. Socrate meurt parce qu’il ne veut pas trahir ce qu’il a dit ; et Platon, en écrivant l’''Apologie'', fait de cette mort le sceau de la vérité philosophique. La philosophie, à partir de ce texte, n’est plus seulement une activité intellectuelle : elle est un engagement existentiel, et le philosophe n’est plus seulement celui qui sait, mais celui qui vit ce qu’il dit. Ensuite, le texte définit la philosophie elle-même, en l’opposant à la sophistique, à la rhétorique et à la religion populaire. Non un savoir constitué, mais un examen ; non une éloquence, mais une recherche du vrai ; non un rite, mais un service intérieur du divin. Cette triple opposition structure toute la pensée platonicienne ultérieure et, par voie de conséquence, la pensée occidentale. La philosophie, depuis Socrate, se définit comme ce qui n’est pas sophistique : un savoir désintéressé, inséparable d’une pratique de vérité. Enfin, le texte pose la question politique dans ses termes platoniciens : la cité peut-elle accueillir le philosophe ? La condamnation de Socrate se laisse lire comme une réponse négative qu’Athènes aurait donnée en acte à cette question, et c’est ainsi que Platon semble l’interpréter. L’œuvre de Platon tout entière tentera de penser une cité qui répondrait autrement, depuis les esquisses du ''Gorgias'' et de la ''République'' jusqu’à la construction ultime des ''Lois''. Le procès de Socrate est ainsi, indirectement, à l’origine de la philosophie politique occidentale. Lire l’''Apologie'' aujourd’hui, c’est s’exposer à une exigence intacte. La vie non examinée n’est pas digne d’être vécue ; la vertu vaut plus que l’argent, que la réputation, que la vie même ; la lâcheté est pire que la mort ; le juste préfère subir l’injustice plutôt que la commettre. Ces formules, qui paraissent extrêmes, sont le cœur d’une éthique dont la philosophie occidentale n’a cessé de se réclamer, même quand elle croyait s’en affranchir. Le taon, vingt-quatre siècles plus tard, pique toujours. == Notes et références == <references /> == Bibliographie sélective == === Éditions et traductions de l’''Apologie'' === * Platon, ''Apologie de Socrate. Criton'', traduction, introduction et notes par Luc Brisson, Paris, GF-Flammarion, 2016. * Platon, ''Apologie de Socrate'', traduction par Luc Brisson, présentation, notes, dossier, répertoire et glossaire par Arnaud Macé, Paris, GF-Flammarion, 2017. * Platon, ''Apologie de Socrate'', traduction, présentation et notes de Bernard et Renée Piettre, Paris, Le Livre de Poche (Librairie générale française), coll. « Libretti », 1997. * Platon, ''Apologie de Socrate'', texte établi et traduit par Maurice Croiset, Paris, Les Belles Lettres, coll. des Universités de France, 1920 (nombreuses rééditions). * Platon, ''Œuvres complètes'', sous la direction de Luc Brisson, Paris, Flammarion, 2008 (contient l’''Apologie''). === Commentaires === * Claude Chrétien, ''Platon, Apologie de Socrate'', Paris, Hatier, coll. « Profil philosophie », 1993. * Paul Allen Miller, Charles Platter, ''Plato’s Apology of Socrates: A Commentary'', Norman, University of Oklahoma Press, 2010. * Émile de Strycker, S. R. Slings, ''Plato’s Apology of Socrates: A Literary and Philosophical Study with a Running Commentary'', édité et complété par S. R. Slings, Leyde, E. J. Brill, coll. « Mnemosyne Supplements » 137, 1994. * Thomas G. West, ''Plato’s Apology of Socrates: An Interpretation with a New Translation'', Ithaca, Cornell University Press, 1979. === Études sur Socrate et l’''Apologie'' === * Louis-André Dorion, ''Socrate'', Paris, PUF, coll. « Que sais-je ? », 2004. * Gregory Vlastos, ''Socrate : ironie et philosophie morale'', trad. C. Dalimier, Paris, Aubier, 1994 (''Socrates: Ironist and Moral Philosopher'', 1991). * Gregory Vlastos, « The Socratic Elenchus », ''Oxford Studies in Ancient Philosophy'', I, 1983, p. 27-58. * Debra Nails, ''The People of Plato: A Prosopography of Plato and Other Socratics'', Indianapolis, Hackett, 2002. * Gabriele Giannantoni, ''Socratis et Socraticorum Reliquiae'', 4 vol., Naples, Bibliopolis, 1990. * Monique Canto-Sperber (dir.), ''Les Paradoxes de la connaissance. Essais sur le Ménon de Platon'', Paris, Odile Jacob, 1991. === Réceptions modernes === * G. W. F. Hegel, ''Leçons sur l’histoire de la philosophie'', t. II, trad. P. Garniron, Paris, Vrin, 1971. * Søren Kierkegaard, ''Le Concept d’ironie constamment rapporté à Socrate'' (1841), trad. P.-H. Tisseau et E.-M. Jacquet-Tisseau, Paris, Éditions de l’Orante, 1975. * Friedrich Nietzsche, ''La Naissance de la tragédie'' (1872), dans ''Œuvres philosophiques complètes'', t. I, Paris, Gallimard, 1977. * E. R. Dodds, ''Les Grecs et l’irrationnel'' (1951), trad. M. Gibson, Paris, Flammarion, coll. « Champs », 1977. * Hannah Arendt, ''La Vie de l’esprit'', trad. L. Lotringer, Paris, PUF, 1981. * Michel Foucault, ''L’Herméneutique du sujet. Cours au Collège de France, 1981-1982'', éd. F. Gros, Paris, Gallimard/Seuil, 2001. * Michel Foucault, ''Le Courage de la vérité. Cours au Collège de France, 1983-1984'', éd. F. Gros, Paris, Gallimard/Seuil, 2009. * Leo Strauss, ''Studies in Platonic Political Philosophy'', Chicago, University of Chicago Press, 1983. === Sources antiques === * Aristophane, ''Les Nuées'', dans ''Théâtre complet'', t. I, trad. V.-H. Debidour, Paris, Gallimard, coll. « Folio », 1965. * Xénophon, ''Apologie de Socrate'' et ''Mémorables'', trad. P. Chambry, Paris, Garnier-Flammarion, 1967. * Diogène Laërce, ''Vies et doctrines des philosophes illustres'', trad. sous la direction de M.-O. Goulet-Cazé, Paris, Le Livre de Poche, coll. « La Pochothèque », 1999. * Plutarque, ''Du démon de Socrate'', dans ''Œuvres morales'', t. VIII, trad. J. Hani, Paris, Les Belles Lettres, 1980. * Aristote, ''Rhétorique'', trad. M. Dufour et A. Wartelle, Paris, Les Belles Lettres, 1938-1973. * Cicéron, ''Tusculanes'', trad. J. Humbert, Paris, Les Belles Lettres, 1931. ihck5vbt5chtwvyzmabu9j1ba9lsww6