<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>C++, Qt et GPU &#187; Intermédiaire</title>
	<atom:link href="https://blog.developpez.com/gpu/?cat=11&#038;feed=rss2" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/gpu</link>
	<description></description>
	<lastBuildDate>Fri, 24 May 2013 17:02:21 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Compléments sur l&#8217;article &#171;&#160;implémenter un Voxel Cone Tracing&#160;&#187;</title>
		<link>https://blog.developpez.com/gpu/?p=214</link>
		<comments>https://blog.developpez.com/gpu/?p=214#comments</comments>
		<pubDate>Sun, 12 May 2013 20:45:59 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[Intermédiaire]]></category>
		<category><![CDATA[OpenGL]]></category>
		<category><![CDATA[Techniques]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/gpu/?p=214</guid>
		<description><![CDATA[J&#8217;ai récement traduit l&#8217;article Implémenter un Voxel Cone Tracing, qui présente quelques difficultés techniques pour ceux qui ne sont pas habitués avec les concepts présentés dans cet article. Ce billet de blog à pour objectif de présenter ces concepts. Quelques &#8230; <a href="https://blog.developpez.com/gpu/?p=214">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>J&rsquo;ai récement traduit l&rsquo;article <a href="http://simonstechblog.blogspot.fr/2013/01/implementing-voxel-cone-tracing.html">Implémenter un Voxel Cone Tracing</a>, qui présente quelques difficultés techniques pour ceux qui ne sont pas habitués avec les concepts présentés dans cet article. Ce billet de blog à pour objectif de présenter ces concepts.<br />
<span id="more-214"></span></p>
<h1>Quelques définitions</h1>
<h2>Illumination globale</h2>
<p>Les techniques d&rsquo;illuminations globales (GI) visent à améliorer le rendu d&rsquo;une scène 3D, en prenant en compte aussi bien la lumière directe (les sources de lumières qui éclairent les objets) que la lumière indirecte (réflexion de la lumière sur les objets, qui éclaire d&rsquo;autres objets). Ces techniques permettent d&rsquo;obtenir des rendus réalistes, au prix d&rsquo;un temps de calcul relativement long (parfois trop pour être compatible avec du rendu en temps réel). Un domaine de la recherche actuelle vise à développer de nouveaux algorithmes d&rsquo;illumination globale compatible avec le temps réel (jeux 3D).</p>
<p style="text-align: center"><img src="http://upload.wikimedia.org/wikipedia/commons/0/0d/Global_illumination.JPG" alt="Exemple d'une scène rendu avec l'illumination globale" /></p>
<p style="text-align: center">(source : <a href="http://fr.wikipedia.org/wiki/Illumination_globale">Wikipédia &#8211; Illumination globale</a>)</p>
<h3>Illumination indirecte</h3>
<p>Dans l&rsquo;illumination directe (à gauche sur l&rsquo;illustration suivante), la lumière provenant d&rsquo;une source est directement réfléchie sur une surface, puis renvoyé vers la caméra. C&rsquo;est le modèle classique de calcul de la lumière en 3D, que l&rsquo;on retrouve par exemple dans le <a href="http://cpp.developpez.com/redaction/data/pages/users/gbdivers/qtopengl/?page=opengl#L5-A">modèle de Phong</a>. La lumière est alors décomposée en plusieurs composantes : la lumière ambiante (qui représente une illumination globale constante), la lumière diffuse (qui représente une diffusion de la lumière sur la surface) et la lumière spéculaire (qui représente une réflexion sur la surface).</p>
<p style="text-align: center"><img src="http://www.cgg-journal.com/2010-1/02/files/VPLs.png" alt="Principe de l'illumination indirecte" /></p>
<p style="text-align: center">(Source : <a href="http://www.cgg-journal.com/2010-1/02/index.html">Real-Time Global Illumination for Point Cloud Scenes</a>)</p>
<p>Dans l&rsquo;illumination indirecte, on considère que chaque surface qui reçoit de la lumière va à son tour en renvoyer. Il n&rsquo;y a donc plus qu&rsquo;une seule source de lumière, mais une infinité, correspondant à chaque surface qui renvoie de la lumière. Pour réaliser le rendu d&rsquo;une scène, il faut donc procéder de la façon suivante :</p>
<ol>
<li>pour chaque surface S1 visible par la caméra ;</li>
<li>pour chaque surface S2 visible depuis la surface S1 ;</li>
<li>calculer la lumière reçue par la surface S2 provenant de la source ;</li>
<li>calculer la lumière renvoyée par la surface S2 vers la surface S1 ;</li>
<li>calculer la lumière reçue par la surface S1 provenant de la surface S2 ;</li>
<li>calculer la lumière totale renvoyée par la surface S1 vers la caméra.</li>
</ol>
<p>Chaque étape doit prendre en compte plusieurs paramètres. Pour (3), il faut par exemple prendre en compte la distance entre la source et la surface S2 et l&rsquo;atténuation de la lumière en fonction de la distance. Pour (4), il faut prendre en compte les caractéristiques de la surface et sa capacité à réfléchir la lumière. Pour (5), il faut prendre en compte la distance entre les surfaces S1 et S2 et l&rsquo;angle d&rsquo;incidence. Pour (6), les capacités de réflexion de la surface S1 et la distance entre la surface S1 et la caméra.</p>
<p>Cette première approche simple peut largement être améliorée (et complexifiée), en prenant en compte par exemple que la lumière peut se réfléchir plusieurs fois sur des surfaces avant d&rsquo;atteindre la caméra (particulièrement important lorsque les surfaces sont très réfléchissantes, comme du verre ou du métal poli), de la diffusion de la lumière dans le brouillard ou la poussière (<a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch13.html">lumière volumétrique</a>, <a href="http://http.developer.nvidia.com/GPUGems/gpugems_ch21.html">éclat lumineux</a>), la présence de plusieurs sources lumineuses (voir des milliers de sources lumineuses dans le cas de particules incandescentes).</p>
<h3>Lancé de rayons</h3>
<p>Il existe plusieurs techniques de &laquo;&nbsp;lancés&nbsp;&raquo;. Elles reposent sur l&rsquo;idée que la lumière est constituée de rayons qui vont partir de des sources lumineuses, se réfléchir, diffuser et réfracter sur les différentes surfaces (chaque surface pouvant renvoyer plusieurs rayons), puis atteindre la caméra. Dans les techniques de photon mapping, on part des sources lumineuses, on émet des rayons et on les suit jusqu&rsquo;à la caméra (cette technique est décrite dans un autre <a href="http://simonstechblog.blogspot.fr/2012/06/photon-mapping-part-1.html">article du blog de Simon</a> et fera l&rsquo;objet d&rsquo;une traduction). Dans les techniques de lancé de rayons, on part de la caméra, on lance un rayon pour chaque pixel de l&rsquo;image finale et l&rsquo;on remonte jusqu&rsquo;aux sources de lumière (illustration suivante).</p>
<p style="text-align: center"><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Ray_trace_diagram.svg/300px-Ray_trace_diagram.svg.png" alt="Principe du lancé de rayons" /></p>
<p style="text-align: center">(Source : <a href="http://en.wikipedia.org/wiki/Ray_tracing_(graphics)">Wikipédia &#8211; Raytracing</a>)</p>
<p>Dans la technique du lancé de cônes (Cone Tracing), présentée dans l&rsquo;article traduit, on remplace simplement les rayons par des cônes pour le calcul de l&rsquo;illumination.</p>
<h3>Occlusion ambiante</h3>
<p>Cette technique fera également l&rsquo;objet de <a href="http://simonstechblog.blogspot.fr/2011/06/ssao-using-line-integrals.html">plusieurs</a> <a href="http://simonstechblog.blogspot.fr/2012/10/angle-based-ssao.html">traductions</a> du blog de Simon, je la détaillerai à ce moment là.</p>
<p>Simplement pour comprendre l&rsquo;idée générale : lorsque deux surfaces sont proches (dans une fissure, un trou), la lumière indirecte diminue fortement et l&rsquo;on observe des ombres douces. Plus les surfaces sont proches et fermées, plus l&rsquo;ombrage sera important. L&rsquo;illustration suivante représente la même scène sans (en haut) et avec l&rsquo;occlusion ambiante (en bas). Remarquez en particulier l&rsquo;ombre dans l&rsquo;angle du mur et du sol et entre la sphère dans l&rsquo;image du bas.</p>
<p style="text-align: center"><img src="http://blog.developpez.com/gpu/files/2013/02/ambient-occlusion-2.png" /></p>
<p>Cette technique améliore fortement la qualité du rendu et commence à être très utilisée dans les jeux. Un de ses points forts est que pour un objet statique (ou peu dynamique), l&rsquo;occlusion ambiante ne varie pas (ou peu) et il est possible de la pré-calculer, le résultat étant mis dans une texture. Il suffit ensuite d&rsquo;appliquer la texture sur l&rsquo;objet lors du rendu, comme n&rsquo;importe quelle autre texture.</p>
<p>Il existe plusieurs algorithmes de calcul de l&rsquo;occlusion ambiante, mais pour comprendre le principe, voyons un exemple simple représenté dans l&rsquo;illustration suivante. Pour chaque point d&rsquo;une surface, on va lancer un nombre fixé de rayons qui vont parcourir une distance fixée aussi dans toutes les directions. On compte ensuite le nombre de rayons qui ne rencontrent pas une autre surface et on calcul le rapport entre le nombre de rayons ayant rencontrés une surface et le nombre de rayons lancés. Ainsi, dans l&rsquo;illustration suivante, pour le point A, sur les six rayons lancés, aucun ne rencontre de surfaces, le rapport est de 0/6 = 0 %. Pour le point B, seul quatre rayons ne rencontrent pas de surfaces sur les six lancés, le rapport est de 4/6 = 66 %. Pour le point C, deux rayons ne rencontrent pas de surfaces, le rapport est de 2/6 = 33 %.</p>
<p style="text-align: center"><img src="http://blog.developpez.com/gpu/files/2013/02/ambient-occlusion.png" /></p>
<p>Il suffit ensuite d&rsquo;atténuer la lumière pour chaque point en fonction du rapport calculé. Plus le rapport est proche de 0 %, plus la surface sera sombre et plus le rapport est proche de 100 %, plus la surface est claire.</p>
<p>Pour obtenir un bon rendu, on pourra augmenter le nombre de point et le nombre de rayons lancés, mais l&rsquo;impact sur les performances sera important.</p>
<h2>Partitionnement de l&rsquo;espace</h2>
<h3>Voxel</h3>
<p>Le principe des voxels est relativement simple : ils sont l&rsquo;équivalent en 3D des pixels pour la 2D. Un exemple bien connu d&rsquo;utilisation des voxels est le jeu Minecraft, dans lequel chaque élément (terrain, objets) sont représentés par des cubes.</p>
<p style="text-align: center"><img src="http://upload.wikimedia.org/wikipedia/en/7/74/Minecraft_city_hall.png" alt="Un exemple d'utilisation des voxels bien connu : Minecraft" /></p>
<p style="text-align: center">(Source : <a href="http://en.wikipedia.org/wiki/Minecraft">Wikiépdia &#8211; Minecraft</a>)</p>
<p>Les voxels sont particulièrement gourmand en termes de performances et sont encore peu utilisés pour les jeux vidéos. Ils sont particulièrement adaptés pour le rendu de volumes transparents (verre, liquide, fumée, nuage) ou de géométries très complexes (pour le rendu d&rsquo;un mesh complexe, une approche utilisant une version <a href="http://en.wikipedia.org/wiki/Low_poly">low poly</a> du mesh et une texture des <a href="http://fr.wikipedia.org/wiki/Displacement_mapping">Displacement</a>/<a href="http://fr.wikipedia.org/wiki/Normal_mapping">Normal</a>/<a href="http://fr.wikipedia.org/wiki/Bump_mapping">Bump</a> mapping).</p>
<h3>Octree</h3>
<p>Un octree est une structure permettant de partitionner l&rsquo;espace dans un arbre, permettant par exemple de faciliter la recherche d&rsquo;un élément par rapport à sa position 3D. La construction d&rsquo;un tel arbre est simple : à chaque itération, le cube représentant une partie de l&rsquo;espace est divisé en 8 cubes.</p>
<p style="text-align: center"><img src="http://http.developer.nvidia.com/GPUGems2/elementLinks/37_octree_03.jpg" alt="Division de l'espace avec un octree" /></p>
<p style="text-align: center">(Source : <a href="http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter37.html">GPU Gems 2 &#8211; Octree Textures on the GPU</a>)</p>
<p>L&rsquo;intérêt des octrees avec les voxels est que l&rsquo;on va pouvoir utiliser une structure &laquo;&nbsp;creuse&nbsp;&raquo; : les zones qui sont vides ne seront pas divisées en cubes plus petits, simplifiant ainsi les algorithmes de recherche et la mémoire.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Un ColorPicker avec Qt – Benchmark et optimisations</title>
		<link>https://blog.developpez.com/gpu/?p=390</link>
		<comments>https://blog.developpez.com/gpu/?p=390#comments</comments>
		<pubDate>Fri, 15 Feb 2013 18:52:59 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Intermédiaire]]></category>
		<category><![CDATA[OpenGL]]></category>
		<category><![CDATA[Qt]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/gpu/?p=390</guid>
		<description><![CDATA[Le sujet du premier exercice proposé par la rubrique Qt de Developpez.com consistait à créer un widget permettant de sélectionner les différentes nuances de gris à partir d&#8217;une teinte définie. Deux méthodes ont été proposer pour générer ces nuances de &#8230; <a href="https://blog.developpez.com/gpu/?p=390">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Le sujet du premier exercice proposé par la rubrique Qt de Developpez.com consistait à créer un widget permettant de sélectionner les différentes nuances de gris à partir d&rsquo;une teinte définie.<br />
<span id="more-390"></span><br />
Deux méthodes ont été proposer pour générer ces nuances de gris :</p>
<ul>
<li>utiliser deux boucles for imbriquées pour parcourir chaque pixel du widget et calculer la couleur correspondante, en faisant varier les paramètres S et V dans l&rsquo;espace colorimétrique HSV ;</li>
<li>utiliser deux gradient (à l&rsquo;aide de la classe QLinearGradient), le premier, horizontal, allant du blanc à gauche à la teinte choisie à droite, et le second, vertical, allant du transparent en haut au noir en bas.</li>
</ul>
<p>Je vais présenter ici comment implémenter un benchmark avec Qt pour tester la rapidité de ces différentes méthodes et présenter deux autres méthodes pour accélérer le rendu : l&rsquo;accès direct au tampon mémoire contenant l&rsquo;image finale et l&rsquo;accélération matériel sur carte graphique.</p>
<h1>Création du programme Benchmark</h1>
<p>Qt offre différents outils facilitant la création de tests unitaires et de benchmarks, rassemblés dans le module <a href="http://qt.developpez.com/doc/4.6/QtTest/">QtTest</a>. Différents tutoriels permettent de se familiariser progressivement avec ce module.</p>
<h2>Benchmark.pro</h2>
<p>Pour créer notre programme Benchmark, commençons par créer un fichier de projet Benchmark.pro. Ce fichier de projet contient les déclarations de base suivantes : la liste des modules utilisés (core, gui puisse que l&rsquo;on teste les fonctions de dessin et testlib) et le nom et le type de binaire généré (une application Benchmark dans notre cas) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">QT <span style="color: #000040;">+</span><span style="color: #000080;">=</span> core gui testlib<br />
TARGET <span style="color: #000080;">=</span> Benchmark<br />
TEMPLATE <span style="color: #000080;">=</span> app</div></td></tr></tbody></table></div>
<p>On ajoute ensuite la liste des fichiers à inclure dans notre projet. On va simplement créer une classe Benchmark : on ajoute donc les fichiers d&rsquo;en-tête et d&rsquo;implémentation :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SOURCES <span style="color: #000040;">+</span><span style="color: #000080;">=</span> benchmark.<span style="color: #007788;">cpp</span><br />
HEADERS &nbsp;<span style="color: #000040;">+</span><span style="color: #000080;">=</span> benchmark.<span style="color: #007788;">h</span></div></td></tr></tbody></table></div>
<h2>benchmark.h</h2>
<p>La classe Benchmark hérite de QObject (pour pourvoir utiliser le système de signaux et slots) et contient deux slots privés, un pour chaque méthode que l&rsquo;on souhaite tester. Ces slots seront automatiquement appelés à l&rsquo;exécution du programme. A part cela, le contenu de la classe est minimaliste :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#ifndef BENCHMARK_H</span><br />
<span style="color: #339900;">#define BENCHMARK_H</span><br />
<span style="color: #339900;">#include &lt;QtTest/QtTest&gt;</span><br />
<span style="color: #0000ff;">class</span> Benchmark <span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> QObject<br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; Q_OBJECT<br />
<span style="color: #0000ff;">private</span> slots<span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> hsvGradient<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> doubleLinearGradient<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
<span style="color: #339900;">#endif // BENCHMARK_H</span></div></td></tr></tbody></table></div>
<h2>benchmark.cpp</h2>
<p>Dans le fichier d&rsquo;implémentation, on ajoute chaque méthode dans les slots correspondants :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#include &quot;benchmark.h&quot;</span><br />
<br />
<span style="color: #0000ff;">void</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">hsvGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
<span style="color: #008000;">&#125;</span><br />
<span style="color: #0000ff;">void</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">doubleLinearGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Il faut également ajouter la macro QTEST_MAIN, qui crée une fonction main, qui aura pour fonction de créer une instance de la classe Benchmark et d&rsquo;exécuter les slots privés :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">QTEST_MAIN<span style="color: #008000;">&#40;</span>Benchmark<span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<h2>Méthode des deux boucles for</h2>
<p>Pour mesurer le temps d&rsquo;exécution d&rsquo;une méthode, on utilise la macro BENCHMARK. Le temps du reste du code contenu dans le slot mais en dehors du bloc précédent la macro n&rsquo;est pas évalué. Pour vérifier que le code généré correctement les nuances de gris, on enregistre les images produites :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#include &lt;QtGui/QPainter&gt;</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> width <span style="color: #000080;">=</span> <span style="color: #0000dd;">500</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> height <span style="color: #000080;">=</span> <span style="color: #0000dd;">500</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">const</span> QColor m_main_color <span style="color: #000080;">=</span> Qt<span style="color: #008080;">::</span><span style="color: #007788;">red</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">void</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">hsvGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; QImage image <span style="color: #000080;">=</span> QImage<span style="color: #008000;">&#40;</span>QSize<span style="color: #008000;">&#40;</span>width, height<span style="color: #008000;">&#41;</span>, QImage<span style="color: #008080;">::</span><span style="color: #007788;">Format_ARGB32</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QBENCHMARK<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">float</span> h <span style="color: #000080;">=</span> m_main_color.<span style="color: #007788;">hsvHueF</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> s<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> s<span style="color: #000080;">&lt;</span>width<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>s<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> v<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> v<span style="color: #000080;">&lt;</span>height<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>v<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; QColor color <span style="color: #000080;">=</span> QColor<span style="color: #008080;">::</span><span style="color: #007788;">fromHsvF</span><span style="color: #008000;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; h,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color:#800080;">1.0</span> <span style="color: #000040;">*</span> s <span style="color: #000040;">/</span> <span style="color: #008000;">&#40;</span>width<span style="color: #000040;">-</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color:#800080;">1.0</span> <span style="color: #000040;">-</span> <span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span> <span style="color: #000040;">*</span> v <span style="color: #000040;">/</span> <span style="color: #008000;">&#40;</span>height<span style="color: #000040;">-</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; image.<span style="color: #007788;">setPixel</span><span style="color: #008000;">&#40;</span>s, v, color.<span style="color: #007788;">rgb</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; image.<span style="color: #007788;">save</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;hsvGradient.png&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h2>Méthode des deux gradients linéaires</h2>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">doubleLinearGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; QImage image<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QPixmap pixmap<span style="color: #008000;">&#40;</span>QSize<span style="color: #008000;">&#40;</span>width, height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QPainter painter<span style="color: #008000;">&#40;</span><span style="color: #000040;">&amp;</span>pixmap<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; painter.<span style="color: #007788;">setPen</span><span style="color: #008000;">&#40;</span>QPen<span style="color: #008000;">&#40;</span>Qt<span style="color: #008080;">::</span><span style="color: #007788;">NoPen</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QBENCHMARK<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; QLinearGradient h_gradient<span style="color: #008000;">&#40;</span>QPointF<span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span>, <span style="color:#800080;">0.0</span><span style="color: #008000;">&#41;</span>, QPointF<span style="color: #008000;">&#40;</span>width, <span style="color:#800080;">0.0</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; h_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, Qt<span style="color: #008080;">::</span><span style="color: #007788;">white</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; h_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span>, m_main_color<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; painter.<span style="color: #007788;">setBrush</span><span style="color: #008000;">&#40;</span>QBrush<span style="color: #008000;">&#40;</span>h_gradient<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; painter.<span style="color: #007788;">drawRect</span><span style="color: #008000;">&#40;</span>QRect<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span>, width, height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; QLinearGradient v_gradient<span style="color: #008000;">&#40;</span>QPointF<span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span>, <span style="color:#800080;">0.0</span><span style="color: #008000;">&#41;</span>, QPointF<span style="color: #008000;">&#40;</span><span style="color:#800080;">0.0</span>, height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; v_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, Qt<span style="color: #008080;">::</span><span style="color: #007788;">transparent</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; v_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span>, Qt<span style="color: #008080;">::</span><span style="color: #007788;">black</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; painter.<span style="color: #007788;">setBrush</span><span style="color: #008000;">&#40;</span>QBrush<span style="color: #008000;">&#40;</span>v_gradient<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; painter.<span style="color: #007788;">drawRect</span><span style="color: #008000;">&#40;</span>QRect<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span>, width, height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; image <span style="color: #000080;">=</span> pixmap.<span style="color: #007788;">toImage</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; image.<span style="color: #007788;">save</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;doubleLinearGradient.png&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Le code étant expliqué dans la solution de l&rsquo;exercice, je n&rsquo;entre pas dans le détail ici.</p>
<h2>Résultat du benchmark</h2>
<p>Lorsque le programme est exécuté, des messages sont générés automatiquement à chaque étape du processus. L&rsquo;en-tête décrit simplement le nom du programme et les versions de QtTest et de Qt utilisées :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000040;">*********</span> Start testing of Benchmark <span style="color: #000040;">*********</span><br />
Config<span style="color: #008080;">:</span> Using QTest library 4.7.0, Qt 4.7.0</div></td></tr></tbody></table></div>
<p>Le premier slot que lance le programme est un slot spécifique, initTestCase, généré par défaut et ne faisant rien. Ce slot permet, si on le souhaite, d&rsquo;initialiser des objets pour l&rsquo;ensemble du programme :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">initTestCase</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>Vient ensuite les différents slots que l&rsquo;on a créé. Le premier est hsvGradient. Lorsque le temps d&rsquo;exécution du slots est trop petit, la mesure risque de ne pas être précise. Pour palier à cet inconvénient, le code contenu dans le bloc de code suivant la macro BENCHMARK peut être exécuté plusieurs fois, le temps indiqué correspondant au temps moyen mesuré. Ici, le slot hsvGradient a été exécuté deux fois et le temps moyen est de 47 millisecondes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RESULT <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">hsvGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">:</span><br />
<span style="color: #0000dd;">47</span> msecs per iteration <span style="color: #008000;">&#40;</span>total<span style="color: #008080;">:</span> <span style="color: #0000dd;">94</span>, iterations<span style="color: #008080;">:</span> <span style="color: #0000dd;">2</span><span style="color: #008000;">&#41;</span><br />
PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">hsvGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>Pour le slot doubleLinearGradient, le code a été exécuté une seule fois et le temps mesuré est de 57 millisecondes. On voit donc ici que cette méthode est plus lente que la première version (sur le système testé ! C&rsquo;est à dire Linux dans mon cas. Sous Windows ou Max OS X, il est possible que les résultats soient différents). Cette différence peut s&rsquo;expliquer par le fait que la génération de deux gradients et la gestion de la transparence est plus lente que l&rsquo;accès directe aux pixels, sur le système de rendu par défaut (le système Raster) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RESULT <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">doubleLinearGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">:</span><br />
<span style="color: #0000dd;">57</span> msecs per iteration <span style="color: #008000;">&#40;</span>total<span style="color: #008080;">:</span> <span style="color: #0000dd;">57</span>, iterations<span style="color: #008080;">:</span> <span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><br />
PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">doubleLinearGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>Le programme exécute ensuite un slot pour libérer les ressources initialisées dans la fonction initTestCase :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">cleanupTestCase</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>Pour terminer, le programme indique le nombre de slot exécutés sans incident (passed), ceux qui ont échoué (failed) et ceux qui n&rsquo;ont pas été exécutés (skipped) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Totals<span style="color: #008080;">:</span> <span style="color: #0000dd;">4</span> passed, <span style="color: #0000dd;">0</span> failed, <span style="color: #0000dd;">0</span> skipped<br />
<span style="color: #000040;">*********</span> Finished testing of Benchmark <span style="color: #000040;">*********</span></div></td></tr></tbody></table></div>
<h1>Rendu dans un tampon mémoire</h1>
<p>Dans la première méthode, on utilise la fonction Qimage::setPixel pour modifier chaque pixel un par un. Cette méthode permet d&rsquo;utiliser une interface sécurisée et simple. Malheureusement, les calculs (calcul de l&rsquo;adresse mémoire correspondant au pixel, conversion de l&rsquo;espace colorimétrique HSV en RGB) et tests effectués en interne (la position du pixel est bien dans l&rsquo;image ? Les valeurs de la couleurs sont-elles correctes ?) peuvent ralentir fortement l&rsquo;exécution.<br />
Il est alors possible d&rsquo;optimiser le rendu en accédant directement à la mémoire à l&rsquo;aide de pointeurs : on créer un bloc mémoire de taille suffisante que l&rsquo;on alloue à une image puis on accède à chaque octet un par un.</p>
<p>ATTENTION : ce type d&rsquo;approche peut comporter certains risques : en cas d&rsquo;erreur dans le code, il est possible que l&rsquo;on écrive dans une zone mémoire qui ne correspond pas à l&rsquo;image. Aucun test n&rsquo;est effectué pour vérifier que le pointeur pointe vers une zone valide. L&rsquo;application peut devenir instable. De plus, il est possible que le code ne soit pas portable et donne des résultats différents en fonction des systèmes d&rsquo;exploitation utilisés (je l&rsquo;ai testé uniquement sur Linux, pensez à bien enregistrer tous vos documents avant de lancer le programme sur un autre système:) ).</p>
<p>La première chose a faire est de créer une zone mémoire de taille suffisante : un pixel étant codé sur 32 bits (un octet par composante de la couleur et quatre composantes : rouge, vert, bleu et alpha), il faudra donc réserver un bloc mémoire de width * height * 4 pour contenir toute l&rsquo;image :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">drawInBuffer</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; uchar buffer<span style="color: #008000;">&#91;</span>width <span style="color: #000040;">*</span> height <span style="color: #000040;">*</span> <span style="color: #0000dd;">4</span><span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QImage image<span style="color: #008000;">&#40;</span>buffer, width, height, QImage<span style="color: #008080;">::</span><span style="color: #007788;">Format_ARGB32</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Il n&rsquo;est pas nécessaire d&rsquo;initialiser chaque octet du bloc mémoire (avec memset par exemple) puisque l&rsquo;algorithme utilisé garanti que chaque octet sera initialisé durant l&rsquo;exécution de celui-ci.</p>
<p>Pour commencer, on crée un pointeur pointant vers le premier octet du bloc mémoire :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; QBENCHMARK<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; uchar<span style="color: #000040;">*</span> pbuffer <span style="color: #000080;">=</span> <span style="color: #000040;">&amp;</span>buffer<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>On utilise deux boucle for, comme dans la première méthode, pour parcourir chaque pixel (en fait, on parcourt le bloc mémoire linéairement, en « déplaçant » le pointeur octet par octet et non en accédant aux coordonnées (x, y) comme dans le cas d&rsquo;une image) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> i<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> i<span style="color: #000080;">&lt;</span>width<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>i<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666;">//uchar green_blue = red;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> j<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> j<span style="color: #000080;">&lt;</span>height<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>j<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span></div></td></tr></tbody></table></div>
<p>Les valeurs de composantes RGB sont calculés par la méthode présentée dans la version QML de la solution de l&#039;exercice :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">float</span> a <span style="color: #000080;">=</span> <span style="color:#800080;">255.0</span> <span style="color: #000040;">*</span> <span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span> <span style="color: #000040;">-</span> <span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span> <span style="color: #000040;">*</span> i <span style="color: #000040;">/</span> width<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">float</span> b <span style="color: #000080;">=</span> a <span style="color: #000040;">*</span> <span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span> <span style="color: #000040;">-</span> <span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span> <span style="color: #000040;">*</span> j <span style="color: #000040;">/</span> height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Ensuite, pour chaque composante, on affecte la valeur calculée (sous forme de uchar pour être sur qu&#039;elle occupe bien à un octet en mémoire) à l&#039;octet pointé par le pointeur (en dé-référençant celui-ci) puis on « avance » le pointeur d&rsquo;un octet :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">*</span>pbuffer <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span>uchar<span style="color: #008000;">&#41;</span> b<span style="color: #008080;">;</span> <span style="color: #666666;">// blue</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">++</span>pbuffer<span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>On fait de même pour les autres composantes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">*</span>pbuffer <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span>uchar<span style="color: #008000;">&#41;</span> b<span style="color: #008080;">;</span> <span style="color: #666666;">// green</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">++</span>pbuffer<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">*</span>pbuffer <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span>uchar<span style="color: #008000;">&#41;</span> a<span style="color: #008080;">;</span> <span style="color: #666666;">// red</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">++</span>pbuffer<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">*</span>pbuffer <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span>uchar<span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">255</span><span style="color: #008080;">;</span> <span style="color: #666666;">// alpha</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">++</span>pbuffer<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Pour terminer, on enregistre l&rsquo;image générée :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; image.<span style="color: #007788;">save</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;buffured.png&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h2>Résultat du benchmark</h2>
<p>Les messages de sortie d&rsquo;application générés par le slot drawInBuffer indique que cette méthode est plus rapide d&rsquo;un facteur 10 que les deux autres méthodes, avec un temps d&rsquo;exécution moyen de 3,9 millisecondes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RESULT <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">drawInBuffer</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">:</span><br />
<span style="color:#800080;">3.9</span> msecs per iteration <span style="color: #008000;">&#40;</span>total<span style="color: #008080;">:</span> <span style="color: #0000dd;">63</span>, iterations<span style="color: #008080;">:</span> <span style="color: #0000dd;">16</span><span style="color: #008000;">&#41;</span><br />
PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">drawInBuffer</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<h1>Optimisation de l&rsquo;algorithme</h1>
<p>Jusqu&rsquo;à présent, nous avons simplement travailler sur l&rsquo;implémentation pour optimiser le rendu des nuances de gris. Une autre approche consiste à travailler sur l&rsquo;algorithme utilisé et d&rsquo;essayer de diminuer les calculs effectués.<br />
Quelque soit l&rsquo;approche utilisée, il sera nécessaire de parcourir l&rsquo;ensemble des pixels du widget, soit explicitement, comme dans le cas de la première méthode présentée, soit implicitement, comme dans le cas des deux QLinearGradient. On ne pourra donc pas optimiser cette partie (on peut au mieux éviter de parcourir plusieurs fois chaque pixel, comme on fait avec la méthode avec les deux QLinearGradient).<br />
On peut remarquer que, dans chaque méthode présentée jusqu&rsquo;ici, on travaille avec des nombres réels, ce qui n&rsquo;est pas optimal. L&rsquo;idéal serait de travailler qu&rsquo;avec des nombres entiers, mieux gérés par les processeurs, et d&rsquo;effectuer que des opérations élémentaires : addition, soustraction et multiplication.</p>
<p>Regardons en détail ce que l&rsquo;on souhaite faire lorsque l&rsquo;on parcourt un ligne pour une composante en particulier. La composante est une valeur entière allant de 0 à 255 (ou de 255 à 0) pour les pixels allant de 0 à N (avec N &gt; 256 pour être sur d&rsquo;afficher toutes les nuances). Il faut donc parcourir chaque pixel et incrémenter, quand c&rsquo;est nécessaire, la composante. Si on représente cela avec un graphique 2D, on obtient :</p>
<p><img src="http://blog.developpez.com/gpu/files/2013/02/droite.png" class="aligncenter" /></p>
<p>avec x, l&rsquo;index des pixels, et y, la composante de la couleur (source : <a href="http://commons.wikimedia.org/wiki/File:Bresenham_run-based.svg">Wikipédia</a>).</p>
<p>Le lecteur averti aura compris l&rsquo;idée sous-jacente : calculer la composante de la couleur revient à dessiner une ligne dans un espace (x, y) discret. Or, il existe un algorithme très performant pour réaliser cela : <a href="http://fr.wikipedia.org/wiki/Algorithme_de_trac.%C3%A9_de_segment_de_Bresenham">l&rsquo;algorithme de Bresenham</a>. Il suffit juste de remplacer y par la composante de la couleur. Le lecteur se reportera à l&rsquo;article de Wikipédia pour l&rsquo;explication du principe de cet algorithme.</p>
<p>L&rsquo;implémentation de l&rsquo;algorithme est assez triviale à partir du pseudo-code. On fera attention aux notations utilisées, qui correspondent au pseudo-code, c&rsquo;est à dire que y1 correspondent à la composante de la couleur et non à la position verticale du pixel. La variable i est utilisée pour parcourir verticalement l&rsquo;image dans une boucle for :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">QImage image<span style="color: #008000;">&#40;</span>width, height, Qimage<span style="color: #008080;">::</span><span style="color: #007788;">Format_ARGB32</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<br />
<span style="color: #0000ff;">int</span> x1 <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> <span style="color: #666666;">// premier pixel</span><br />
<span style="color: #0000ff;">int</span> y1 <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> <span style="color: #666666;">// composante dans le premier pixel</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> x2 <span style="color: #000080;">=</span> width <span style="color: #000040;">-</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span> <span style="color: #666666;">// dernier pixel</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> y2 <span style="color: #000080;">=</span> <span style="color: #0000dd;">255</span><span style="color: #008080;">;</span> <span style="color: #666666;">// composante dans le dernier pixel</span><br />
<span style="color: #0000ff;">int</span> e <span style="color: #000080;">=</span> x2 <span style="color: #000040;">-</span> x1<span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> dx <span style="color: #000080;">=</span> <span style="color: #0000dd;">2</span> <span style="color: #000040;">*</span> e<span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> dy <span style="color: #000080;">=</span> <span style="color: #0000dd;">2</span> <span style="color: #000040;">*</span> <span style="color: #008000;">&#40;</span>y2 <span style="color: #000040;">-</span> y1<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>x1 <span style="color: #000080;">&lt;=</span> x2<span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> i<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> i<span style="color: #000080;">&lt;</span>height<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>i<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; image.<span style="color: #007788;">setPixel</span><span style="color: #008000;">&#40;</span>QPoint<span style="color: #008000;">&#40;</span>x1, i<span style="color: #008000;">&#41;</span>, qRgb<span style="color: #008000;">&#40;</span>y1, y1, y1<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #000040;">++</span>x1<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; e <span style="color: #000040;">-</span><span style="color: #000080;">=</span> dy<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>e <span style="color: #000080;">&lt;=</span> <span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">++</span>y1<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; e <span style="color: #000040;">+</span><span style="color: #000080;">=</span> dx<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
<span style="color: #008000;">&#125;</span><br />
image.<span style="color: #007788;">save</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;bresenham.png&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>L&#039;image générée est un gradient allant du noir au blanc :</p>
<p><img src="http://blog.developpez.com/gpu/files/2013/02/gradient.png" class="aligncenter" /></p>
<p>Pour générer toutes les nuances, il va falloir imbriquer deux fois algorithme, le premier allant du blanc à la teinte choisie (première ligne de pixels) et le second allant de la teinte (pixel de  la première ligne) au noir (pixel de la dernière ligne). Il faut également gérer chaque composante indépendamment.</p>
<p>Pour terminer, il est possible de combiner cette méthode avec la méthode utilisant un tampon mémoire. Le code est très proche du précédent.</p>
<h2>Résultat du benchmark</h2>
<p>La méthode utilisant l&rsquo;algorithme amélioré permet de gagner un facteur 3 à 4 par rapport aux premières méthodes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RESULT <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">bresenham</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">:</span><br />
<span style="color: #0000dd;">14</span> msecs per iteration <span style="color: #008000;">&#40;</span>total<span style="color: #008080;">:</span> <span style="color: #0000dd;">58</span>, iterations<span style="color: #008080;">:</span> <span style="color: #0000dd;">4</span><span style="color: #008000;">&#41;</span><br />
PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">bresenham</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>La version combinant les deux approches permet un gain de performance d&rsquo;un facteur 4 par rapport à la méthode utilisant un tampon mémoire et un facteur 50 par rapport au premières méthodes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RESULT <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">bresenhamInBuffer</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">:</span><br />
<span style="color:#800080;">1.1</span> msecs per iteration <span style="color: #008000;">&#40;</span>total<span style="color: #008080;">:</span> <span style="color: #0000dd;">72</span>, iterations<span style="color: #008080;">:</span> <span style="color: #0000dd;">64</span><span style="color: #008000;">&#41;</span><br />
PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">bresenhamInBuffer</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<h1>Accélération matériel sur carte graphique</h1>
<p>Pour réaliser le rendu des nuances de gris sur carte graphique, on utilise une classe dérivée de QGLWidget. Il suffit de dessiner un rectangle s&rsquo;adaptant à la taille du widget, le reste du travail de rendu est réalisé dans le fragment shader : on calcule le rapport entre la position du pixel et la largeur du widget (passé en paramètre) pour calculer la nuance de gris correspondante :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #FF0000;">&quot;uniform vec4 main_color;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot;uniform vec2 size;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot;vec4 pixel_color;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot;void main(void)<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot;{<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot; &nbsp; &nbsp;pixel_color.r = (gl_FragCoord.y / size.y) * (1.0 + (gl_FragCoord.x / size.x) * (main_color.r - 1.0));<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot; &nbsp; &nbsp;pixel_color.g = (gl_FragCoord.y / size.y) * (1.0 + (gl_FragCoord.x / size.x) * (main_color.g - 1.0));<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot; &nbsp; &nbsp;pixel_color.b = (gl_FragCoord.y / size.y) * (1.0 + (gl_FragCoord.x / size.x) * (main_color.b - 1.0));<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot; &nbsp; &nbsp;pixel_color.a = main_color.a;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot; &nbsp; &nbsp;gl_FragColor = pixel_color;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot;}<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Pour que le fragment shader soit appliqué sur chaque pixel du widget, il suffit de dessiner un quadrilatère sur la totalité de la surface :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">glClear<span style="color: #008000;">&#40;</span>GL_COLOR_BUFFER_BIT<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
glBegin<span style="color: #008000;">&#40;</span>GL_QUADS<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; glVertex2f<span style="color: #008000;">&#40;</span><span style="color: #000040;">-</span><span style="color:#800080;">1.0</span>, <span style="color: #000040;">-</span><span style="color:#800080;">1.0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; glVertex2f<span style="color: #008000;">&#40;</span><span style="color: #000040;">-</span><span style="color:#800080;">1.0</span>, <span style="color:#800080;">1.0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; glVertex2f<span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span>, <span style="color:#800080;">1.0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; glVertex2f<span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span>, <span style="color: #000040;">-</span><span style="color:#800080;">1.0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
glEnd<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Les différents paramètres utilisés dans le fragment shader sont définis à l&rsquo;aide des fonctions uniformLocation et setUniformValue. Par exemple, pour mettre à jour la taille du widget :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">position_location <span style="color: #000080;">=</span> program<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>uniformLocation<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;position&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<br />
QVector2D s <span style="color: #000080;">=</span> QVector2D<span style="color: #008000;">&#40;</span>width, height<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
program<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>setUniformValue<span style="color: #008000;">&#40;</span>size_location, s<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Par contre, je n&rsquo;ai pas réussit à utiliser les fonctions de benchmarks de Qt Test pour tester la rapidité du rendu par cette méthode. Pour pouvoir mesurer le temps, j&rsquo;ai ajouté un calcul du nombre de FPS (frame par seconde) avec un simple compteur, incrémenté à chaque cycle et remis à zéro toutes les secondes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">int</span> delay <span style="color: #000080;">=</span> m_time.<span style="color: #007788;">msecsTo</span><span style="color: #008000;">&#40;</span>QTime<span style="color: #008080;">::</span><span style="color: #007788;">currentTime</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span> delay <span style="color: #000080;">&gt;=</span> <span style="color: #0000dd;">1000</span> <span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>m_fps <span style="color: #000080;">&gt;</span> m_fps_max<span style="color: #008000;">&#41;</span> m_fps_max <span style="color: #000080;">=</span> m_fps<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; setWindowTitle<span style="color: #008000;">&#40;</span>QString<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;fps:%1 - max:%2 - w:%3 - h:%4&quot;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #007788;">arg</span><span style="color: #008000;">&#40;</span>m_fps<span style="color: #008000;">&#41;</span>.<span style="color: #007788;">arg</span><span style="color: #008000;">&#40;</span>m_fps_max<span style="color: #008000;">&#41;</span>.<span style="color: #007788;">arg</span><span style="color: #008000;">&#40;</span>width<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>.<span style="color: #007788;">arg</span><span style="color: #008000;">&#40;</span>height<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; m_fps <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; m_time <span style="color: #000080;">=</span> QTime<span style="color: #008080;">::</span><span style="color: #007788;">currentTime</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><br />
<span style="color: #0000ff;">else</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000040;">++</span>m_fps<span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Pour mettre à jour la widget, j&rsquo;utilise un QTimer avec un délai de 0.</p>
<p>ATTENTION : Il en faut pas laisser le programme tourner trop longtemps avec cette méthode. Le programme tourne en boucle et utilise la carte graphique au maximum de ses capacités, ce qui augmente progressivement sa température. Personnellement, je n&rsquo;ai pas pris le risque de tester si cela pouvait endommager la carte graphique.</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">m_timer <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> QTimer<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">this</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
connect<span style="color: #008000;">&#40;</span>m_timer, SIGNAL<span style="color: #008000;">&#40;</span>timeout<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>, <span style="color: #0000dd;">this</span>, SLOT<span style="color: #008000;">&#40;</span>timerUpdate<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
m_timer<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>start<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">20</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<br />
<span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">timerUpdate</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; updateGL<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h2>Performances</h2>
<p>J&rsquo;ai réalisé le test sur un ordinateur portable équipé d&rsquo;une carte Nvidia 8600M GT avec 512 Mo et un widget de 256 x 256 pixels. Avec cette configuration, les mesures donnent un FPS moyen de 5000 fps avec de pointes à 5200 fps, c&rsquo;est à dire des temps de rendu de l&rsquo;ordre de 0,2 millisecondes (soit 250 fois plus rapide que les premières versions).</p>
<h1>Les sources</h1>
<p><a href="http://www.developpez.net/forums/attachments/p67224d1284552523/c-cpp/bibliotheques/qt/exercice-qt-color-picker-deuxieme-partie-lancee/benchmark.zip/">Télécharger les sources des benchmarks.</a></p>
<p><a href="http://www.developpez.net/forums/attachments/p67225d1284552535/c-cpp/bibliotheques/qt/exercice-qt-color-picker-deuxieme-partie-lancee/colorpickeropengl.zip/">Télécharger les sources de la version OpenGL.</a></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
