IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Arduino Discussion :

Assembleur dans Arduino


Sujet :

Arduino

  1. #1
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut Assembleur dans Arduino
    Bonjour,

    J'ai quasi terminé mon projet d'interface ethernet avec écran TFT tactile.
    Le matériel utilisé est Arduino UNO + ILI9341 TFT + XPT2046 touch + Shield Ethernet II

    Une fonction impacte fortement la vitesse d'affichage
    Elle sert à faire des "dégradés" de couleur :
    - les dégradés peuvent être hozirontaux ou verticaux (couleur fonction de X ou de Y)
    - le dégradé peut être "normal" (couleur de départ -> couleur d'arrivée)
    - le dégradé peut être "arc en ciel"
    - il y a un dégradé "fixed middle", c'est un mode "3 couleurs" : couleur de départ si X < début, couleur d'arrivée si X > fin et couleur fixe à la place du dégradé

    Cela peut sembler étrange mais c'est très pratique.
    Par exemple vous voulez dessiner un graphique type "équaliseur HiFi", hé bien grâce au dégradé "fixed middle" vous dessinez les rectangles de votre équaliseur et ils auront automatiquement la zone verte, la zone jaune éventuelle et la zone rouge éventuelle comme il faut.

    Les dégradés permettent des effets visuels intéressants qui compensent le fait qu'on ne puisse faire que des graphismes monochromes (à la base, une forme élémentaire n'a qu'une seule couleur) et sans antialiasing
    Ainsi, en mettant un rectangle, un ou deux triangles ou un bitmap dessinés avec une couleur dégradée, on peut avoir des dessins ou des icônes donnant une impression de relief ou une impression de couleur comme sur un écran d'ordinateur.
    Cela permet à ma petite interface qui a peu de ressources de proposer des graphismes dignes de l'an 2020

    J'ai en grande partie résolu le problème de performance en appelant cette fonction le moins souvent possible.
    En fonction du type de dégradé (horizontal ou vertical), je me débrouille pour dessiner les graphismes soit avec des segments de droite horizontaux ou soit avec des segments de droite verticaux.
    Du coup la fonction est appelée un fois par ligne au lieu d'être appelée "bêtement" à chaque pixel.
    C'est très efficace.
    Je peux faire un dégradé qui remplit tout l'écran et ça ne demande que 7% de temps d'execution en plus qu'avec une couleur unique (186 millisecondes au lieu de 174)

    Mais j'ai quand même quelques fonctions graphiques où ce principe ne peut pas être utilisé, et donc il faut calculer la couleur pour chaque pixel
    J'ai aussi des fonctions graphiques qui ne dessinent que de petits segments de droite horizontaux ou verticaux, elles sont pénalisées.
    Dans le cas le plus défavorable, typiquement une page de texte "opaque" avec couleur dégradé pour le texte et pour le fond, on peut dépasser une seconde pour l'affichage.

    Rien de dramatique, si vous connaissez la librairie ILI9341 Adafruit vous trouverez certainement que ma version est déjà bien plus rapide !

    J'ai fait un benchmark et la plupart des exemples sont rapides (moins de 200 ms) et la quasi totalité est sous les 400 ms.
    J'aimerais bien arriver à optimiser encore un petit peu plus pour que tout soit rapide

    Voici la fonction :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
     
    // Variables globales
    uint8_t ColGradient_R; // R value when X or Y = ColGradient_XY_min
    uint8_t ColGradient_G;
    uint8_t ColGradient_B;
    int8_t ColGradient_DR; // R value when X or Y = ColGradient_XY_min + ColGradient_XY_delta is ColGradient_R + ColGradient_DR 
    int8_t ColGradient_DG;
    int8_t ColGradient_DB;
    bool ColGradient_Mode;
    bool ColGradient_X_Direction;
    int ColGradient_XY_min;
    int ColGradient_XY_delta;
    bool ColGradient_Swap;
    int ColGradient_WH;
     
    // ...
     
    unsigned int ColGradient_Colour_Calc(int xy) {
    	if (ColGradient_Swap) {
    		xy = ColGradient_WH  - xy;
    	} else {
    		xy -= ColGradient_XY_min;
    	}
    	int delta = ColGradient_XY_delta;
    	if (xy >= delta) {
    		return ColGradient2;
    	} else if (xy <= 0) {
    		return ColGradient1;
    	} else {
    		// ColGradient_Type 
    		// 00000000 Gradient      
    		// 01000000 Rainbow       
    		// 11000000 Rainbow Inv   
    		// 10000000 FixedMiddle   
    		if (ColGradient_Type == 0b10000000) {
    			return Colour[Coul_Index];
    		} else {
    			byte R;
    			byte G;
    			byte B;
    			if (ColGradient_Type & 0b01000000) {
    				if (ColGradient_Type & 0b10000000) xy = delta - xy;
    				xy *= 4;
    				if (xy < delta) {
    					R = 31;
    					G = 63 * xy / delta;
    					B = 0;
    				} else if (xy < 2 * delta) {
    					R = 31 * (2 * delta - xy) / delta;
    					G = 63;
    					B = 0;
    				} else if (xy < 3 * delta) {
    					R = 0;
    					G = 63 * (3 * delta - xy) / delta;
    					B = 31 * (xy - 2 * delta) / delta;
    				} else {
    					R = 31 * (xy - 3 * delta) / delta;
    					G = 0;
    					B = 31;
    				}
    			} else {
    				R = ColGradient_R;
    				G = ColGradient_G;
    				B = ColGradient_B;
    				//if (ColGradient_DR !=0) R += ColGradient_DR * xy / delta;
    				//if (ColGradient_DG !=0) G += ColGradient_DG * xy / delta;
    				//if (ColGradient_DB !=0) B += ColGradient_DB * xy / delta;
    				byte k = (xy << 6) / delta;
    				if (ColGradient_DR !=0) R += (ColGradient_DR * k) >> 6;
    				if (ColGradient_DG !=0) G += (ColGradient_DG * k) >> 6;
    				if (ColGradient_DB !=0) B += (ColGradient_DB * k) >> 6;
    			}
    			return (R << 11) | (G << 5) | B;
    		}
    	}
    }
    Je me demande si je ne devrais pas coder cette fonction en assembleur.
    Ou au moins la partie la plus lente qui est celle-ci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
    			byte R;
    			byte G;
    			byte B;
    			if (ColGradient_Type & 0b01000000) {
    				if (ColGradient_Type & 0b10000000) xy = delta - xy;
    				xy *= 4;
    				if (xy < delta) {
    					R = 31;
    					G = 63 * xy / delta;
    					B = 0;
    				} else if (xy < 2 * delta) {
    					R = 31 * (2 * delta - xy) / delta;
    					G = 63;
    					B = 0;
    				} else if (xy < 3 * delta) {
    					R = 0;
    					G = 63 * (3 * delta - xy) / delta;
    					B = 31 * (xy - 2 * delta) / delta;
    				} else {
    					R = 31 * (xy - 3 * delta) / delta;
    					G = 0;
    					B = 31;
    				}
    			} else {
    				R = ColGradient_R;
    				G = ColGradient_G;
    				B = ColGradient_B;
    				//if (ColGradient_DR !=0) R += ColGradient_DR * xy / delta;
    				//if (ColGradient_DG !=0) G += ColGradient_DG * xy / delta;
    				//if (ColGradient_DB !=0) B += ColGradient_DB * xy / delta;
    				byte k = (xy << 6) / delta;
    				if (ColGradient_DR !=0) R += (ColGradient_DR * k) >> 6;
    				if (ColGradient_DG !=0) G += (ColGradient_DG * k) >> 6;
    				if (ColGradient_DB !=0) B += (ColGradient_DB * k) >> 6;
    			}
    			return (R << 11) | (G << 5) | B;
    Dans mon projet j'ai déjà des bouts d'assembleur provenant des bibliothèques que j'ai modifié ; voici un example :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    	// 17 cycle delay (including "call")
    	static void delay17() __attribute__((noinline)) __attribute__((naked))	{
    		__asm__ __volatile__
    		(
    									// +4 (call to get here)
    			"	adiw	r24,0\n"	// +2 (2-cycle NOP)
    			"	adiw	r24,0\n"	// +2 (2-cycle NOP)
    			"	adiw	r24,0\n"	// +2 (2-cycle NOP)
    			"	adiw	r24,0\n"	// +2 (2-cycle NOP)
    #if !defined(__AVR_HAVE_RAMPD__)	
    			"	nop\n"				// +1 (2-cycle NOP)
    #endif
    			"	ret\n"				// +4 (or +5 on >64KB AVR with RAMPD reg)
    									// = 17 cycles
    			: : : 
    		);
    	}
     
    	// SPI 16-bit write with minimal hand-tuned delay (assuming max DIV2 SPI rate)
    	static INLINE void spiWrite16(uint16_t data) INLINE_OPT	{
    		uint8_t temp;
    		__asm__ __volatile__
    		(
    			"	out	%[spi],%[hi]\n"				// write SPI data (18 cycles until next write)
    			"	call	_ZN11PDQ_ILI93417delay17Ev\n"	// call mangled delay17 (compiler would needlessly save/restore regs)
    			"	out	%[spi],%[lo]\n"				// write SPI data (18 cycles until next write)
    			"	call	_ZN11PDQ_ILI93417delay17Ev\n"	// call mangled delay17 (compiler would needlessly save/restore regs)
     
    			: [temp] "=d" (temp)
    			: [spi] "i" (_SFR_IO_ADDR(SPDR)), [lo] "r" ((uint8_t)data), [hi] "r" ((uint8_t)(data>>8))
    			: 
    		);
    	}
    Qu'en pensez vous ?

    Sans surprise, c'est les multiplications et surtout la division qui est coûteuse.
    La ligne "byte k = (xy << 6) / delta" qui permet d'économiser jusque 3 divisions m'a fait gagné pas mal de temps (c'est une astuce un peu barbare qui tiens compte du fait que les couleurs sont sur 16 bits donc R5 G6 B5)

    Comment faire, ou trouver des ressources ?

    Même si ça ne marche pas et/ou si l'optimisation est faible, ce sera toujours une chose intéressante à essayer et à faire

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  2. #2
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut Couleurs, dégradés et plus si affinités
    Bonjour,

    Il est possible de multiplier facilement les performances en considérant qu'un dégradé n'est qu'une collection de droites : R(x) = dR*(x-x0) + R0, G(x) = dG*(x-x0) + G0, B(x) = dB*(x-x0) + B0 avec dR = (R1-R0)/(x1-x0) etc.

    La deuxième considération est que ce sont de ligne discrètes.

    Il s'ensuit que les algorithmes de Bresenham, du demi-point fonctionnent très bien et évitent toute multiplication et division : voir https://fr.wikipedia.org/wiki/Algori...t_de_Bresenham.

    Pour faire des choses plus variées, il est possible de travailler avec une seule droite y(x) servant d'indice dans un tableau de couleurs (par exemple rainbow) mais ça occupe de la mémoire.

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  3. #3
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Bonjour,

    J'ai eu les mêmes idées que toi

    Citation Envoyé par Guesset Voir le message
    Il est possible de multiplier facilement les performances en considérant qu'un dégradé n'est qu'une collection de droites : R(x) = dR*(x-x0) + R0, G(x) = dG*(x-x0) + G0, B(x) = dB*(x-x0) + B0 avec dR = (R1-R0)/(x1-x0)
    Dans ton exemple, DR est un float et en termes de performance c'est pire, sans parler de la taille du code.

    Mon calcul n'utilise que des entiers, les divisions sont des divisions entières.

    Citation Envoyé par Guesset Voir le message
    Il s'ensuit que les algorithmes de Bresenham, du demi-point fonctionnent très bien et évitent toute multiplication et division : voir https://fr.wikipedia.org/wiki/Algori...t_de_Bresenham.
    Je connais cet algorithme, je m'en sert pour les droites et les cercles.

    Le soucis est que je n'appelle pas forcément le calcul du dégradé "dans l'ordre", c'est à dire avec des valeurs croissantes et contigües de X ou Y
    Or Bresenham est un algorithme itératif... donc ce n'est pas possible.

    Citation Envoyé par Guesset Voir le message
    Pour faire des choses plus variées, il est possible de travailler avec une seule droite y(x) servant d'indice dans un tableau de couleurs (par exemple rainbow) mais ça occupe de la mémoire.
    En effet, ça occupe de la mémoire : 16 bits (2 octets) par couleur x 320 pixels = 640 octets pour un dégradé, il faudrait 640 autres pour le dégradé du fond, soit 1280 octets, les 2/3 de la RAM de l'Arduino UNO, ce n'est pas possible pour mon application

    Mon idée est de passer tout ou partie de cette fonction en assembleur, si j'arrive à gagner quelques cycles ce serait pas mal.

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  4. #4
    Expert éminent sénior
    Avatar de Auteur
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    7 646
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 7 646
    Points : 11 135
    Points
    11 135
    Par défaut
    Dans mes (vieux) cours d'assembleur, on me disait que le langage était spécifique au contrôleur programmé. En d'autres termes, il y avait assembleur et assembleur.
    Là, les instructions utilisées ne me parlent pas du tout. Tu as récupéré de l'assembleur écrit dans des bibliothèques, es-tu sûr qu'il fonctionnera avec l'Arduino que tu vas utiliser ? D'ailleurs, y a-t-il une doc Arduino sur les instructions assembleurs qu'il est possible d'utiliser ?

  5. #5
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Bonjour,

    je confirme que ça fonctionne

    le code ASM est issu de la bibliothèque, et c'est le code ASM spécifique AVR / Arduino UNO de la bibliothèque d'origine

    après, c'est du code ASM pour le compilateur AVR utilisé par l'IDE Arduino (donc le compilateur de base Atmel) il y a très certainement une syntaxe particulière

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  6. #6
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut Point à point
    Bonjour,

    Citation Envoyé par electroremy Voir le message
    Dans ton exemple, DR est un float et en termes de performance c'est pire, sans parler de la taille du code.
    Ce n'est pas un exemple mais juste une présentation du problème pour amener, avec l'autre considération sur la discrétisation, les algorithmes incrémentaux. Que tu ne peux, semble-t-il, utiliser.

    Citation Envoyé par electroremy Voir le message
    En effet, ça occupe de la mémoire : 16 bits (2 octets) par couleur x 320 pixels = 640 octets pour un dégradé, il faudrait 640 autres pour le dégradé du fond, soit 1280 octets, les 2/3 de la RAM de l'Arduino UNO, ce n'est pas possible pour mon application.
    Le dégradé n'a pas nécessairement besoin de faire la largeur de l'écran. Par exemple il peut être cyclique. Et ne pas être en RAM.

    Citation Envoyé par electroremy Voir le message
    Mon idée est de passer tout ou partie de cette fonction en assembleur, si j'arrive à gagner quelques cycles ce serait pas mal.
    A chaque fois que l'assembleur permet pas de faire autrement, il y a de bonnes chances d'avoir une nette amélioration, mais si tu es contraint de reproduire le même schéma, il sera difficile de faire mieux que le compilateur.

    Une solution intermédiaire peut être. Pourquoi ne pas créer une ligne (colonne) de dégradé jetable (tableau dynamique). Tu consommerais certes de la mémoire mais seulement temporairement.

    Il y a une autre approche. Même si tu ne peux intégralement repeindre dans un dégradé, il est vraisemblable que tu ne dessines pas des points épars mais des petits segments. Auquel cas tu peux recalculer les extrémités (avec tes fonctions) mais appliquer un Bresenham au tracé proprement dit du segment. Si la taille moyenne d'un segment est, par exemple, 8 tu devrais être au moins 2 fois plus rapide (2 points classique + 6 points en Bresenham). Si le Segment est orthogonal au dégradé le gain sera plus important encore. C'est même adaptable, avec quelques efforts, à un segment en biais.

    Salut
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  7. #7
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut Nettoyage de printemps
    Bonjour,

    J'ai essayé de passer un coup de polish sur le code.

    J'ai un problème sur le rainbow. Tantôt les changements de couleur sont pointus tantôt ils présentent des méplats (et certaines valeurs non atteignables). J'ai pris l'option de systématiquement avoir des méplats ce qui permet d'avoir plus de puissances de 2 et donc moins de multiplications.
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    unsigned int ColGradient_Colour_Calc(int xy) {
       if (ColGradient_Swap) xy = ColGradient_WH  - xy; else xy -= ColGradient_XY_min;
       int delta = ColGradient_XY_delta;
       if (xy >= delta) return ColGradient2;
       if (xy <= 0) || (delta <= 0) return ColGradient1;
       // ColGradient_Type 
       // 00000000 Gradient      
       // 01000000 Rainbow       
       // 11000000 Rainbow Inv   
       // 10000000 FixedMiddle   
       if (ColGradient_Type == 0b10000000) return Colour[Coul_Index];
       byte R;
       byte G;
       byte B;
       if (ColGradient_Type & 0b01000000) {
          if (ColGradient_Type & 0b10000000) xy = delta - xy;
          xy *= 4;
          if (xy < delta) {
             //G = (63 * xy) / delta;                  // Pas 64 ? Gmax < 63*Delta/Delta = 63, Gmax = 62
             G = (xy << 6) / delta;                  // Avec 64 Gmin = 0 -> Gmax = 63
             return 0xF800 | (G << 5);               // R = 31 et B = 0
          } else if (xy < 2 * delta) {
             R = 63 - (xy << 5) / delta;             // Eviter * 31, Rmax = 31 -> Rmin =  0
             return (R << 11) | 0x7E0;               // G = 63 et B = 0
          } else if (xy - delta < 2 * delta) {       // Eviter * 3
             G = 191 - (xy << 6) / delta;            // Eviter * 63, Gmax = 63 -> Gmin =  0
             B = (xy << 5) / delta - 64;             // Eviter * 31, Bmin =  0 -> Bmax = 31
             return (G << 5) | B;                    // R = 0
          } else {
             R = (xy << 5) / delta - 96;             // Eviter * 31, Rmin =  0 -> Rmax = 30 ?
             return (R << 11) | 31;                  // G = 0, B = 31
          }
       }                                             // Hypothèses Comp/Branch : 2, * : 6, >> : 3
       byte k = (xy << 6) / delta;                   //  test 63*(2+6+3) + 1*2 = 695, <op> = 10.85
       R = ColGradient_R + (ColGradient_DR * k) >> 6;// !test 64*(6+3) = 576,         <op> = 9
       G = ColGradient_G + (ColGradient_DG * k) >> 6;
       B = ColGradient_B + (ColGradient_DB * k) >> 6;
       return (R << 11) | (G << 5) | B;
    }
    Salut
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  8. #8
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Citation Envoyé par Guesset Voir le message
    Bonjour,

    J'ai essayé de passer un coup de polish sur le code.

    J'ai un problème sur le rainbow. Tantôt les changements de couleur sont pointus tantôt ils présentent des méplats (et certaines valeurs non atteignables). J'ai pris l'option de systématiquement avoir des méplats ce qui permet d'avoir plus de puissances de 2 et donc moins de multiplications.

    Salut
    Tes idées sont bonnes

    Une petite erreur if (xy <= 0) || (delta <= 0) return ColGradient1; au lieu de if ((xy <= 0) || (delta <= 0)) return ColGradient1; mais je vais pas jeter la pierre je fais ça tout le temps aussi

    Ton code est plus compact, 18 octets de flash en mois

    Il y a un bug sur le rainbow malheureusement, des bandes de couleurs ne sont pas correctes

    J'ai refait le benchmark complet, ton code est un peu plus lent que le miens (entre 1 et 10 ms de perdue pour chaque exemple)

    Il y a les optimisations du compilateur Arduino qui entrent en scène...

    Je crois me souvenir qu'on peu obtenir le code assembleur généré, il faudrait que je puisse retrouver celui correspond à la fonction, on verra vraiment si c'est optimisable ou pas.

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  9. #9
    Modérateur

    Homme Profil pro
    Ingénieur électricien
    Inscrit en
    Septembre 2008
    Messages
    1 266
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur électricien

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 266
    Points : 4 810
    Points
    4 810
    Par défaut
    Bonsoir à tous

    Oui l'assembleur est spécifique, et il a même le droit à sa documentation spécifique : AVR Instruction Set Manual
    A la fin de la doc de chaque microcontrôleur il y a un tableau récapitulatif (Instruction Set Summary) reprenant les instructions supportées par ce micro en particulier. (Imprimé, il ne fait que 2 ou 3 pages, cela fait un très bon aide-mémoire). Et attention certains micros n'implémentent pas toutes les instructions.

    Locoduino vient de sortie une bonne série d'articles sur la programmation en assembleur des Arduino AVR, il reprend tout ce que je t'aurais indiqué.

    Par contre je m'insurge sur la raison de départ.
    Ce n'est pas le passage à l'ASM qui permet l'optimisation d'un programme, mais l’abandon de la couche Arduino et un algorithme adapté qui font la majorité de l'optimisation.
    Il est possible d’obtenir 99% de l'optimisation simplement en codant en C "AVR" au lieu du C++ "Arduino". Par contre connaitre l'assembleur du micro en question permet de mieux connaître le micro et d'identifier les instructions longues à exécuter et/ou grosses en mémoire.
    Dans le C "AVR" j'inclue le fait de coder en bas niveau, soit une programmation très simple n'utilisant que les opérations hardwired et optimisant les types de variable (toujours des entiers et ayant le nombre de byte minimal).

    Le seul avantage de l'ASM c'est de forcer à réfléchir sur les optimisations de l’algorithme, alors qu'en C il faut trouver où cela pèche.
    Par contre l'ASM, c'est une horreurs dans la gestion des variables (normal il n'y en a pas) et donc dès que l'on dépasse un petit programme on aura vite fait une erreur...

    Dans ton code c'est les divisions (même entières) qui doivent consommer la plus grande partie du temps de calcul, car la division n'est pas câblée, c'est une réalisation sur base de boucles.
    Calculer l'inverse de Delta (voir même avec une lookup table si c'est un byte), puis faire le calcul en prenant soit une multiplication par Delta, soit une multiplication par l'inverse de Delta (et un décalage) peut considérablement accélérer le code.

    Donc faire un apprentissage de l'assembleur, pour mieux connaître la bête, oui pourquoi pas. Faire un projet utilisant la majorité des capacités d'un Mega, c'est prise de tête assurée.

    Bonne suite

    Delias

  10. #10
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut Plus vite au ralenti
    Bonjour,

    Désolé pour les parenthèses

    Même si je n'en attendais pas des gains substantiels, je suis étonné que les suppressions de multiplications, de décalages et les return directs n'aient pas amélioré un tant soit peu les performances.

    Tu sais qu'elle erreur j'ai faite sur le rainbow ?

    Salut
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  11. #11
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Citation Envoyé par Delias Voir le message
    Connaitre l'assembleur du micro en question permet de mieux connaître le micro et d'identifier les instructions longues à exécuter et/ou grosses en mémoire.
    Dans le C "AVR" j'inclue le fait de coder en bas niveau, soit une programmation très simple n'utilisant que les opérations hardwired et optimisant les types de variable (toujours des entiers et ayant le nombre de byte minimal).
    C'est ce qu'a fait Hackaday avec la librairie Adafruit (les bout d'assembleurs viennent d'eux)
    Le gain est conséquent (vitesse x 12)
    J'ai repris leur travail, en poursuivant les optimisations pour avoir :
    - un code plus compact
    - plus fonctionnalités
    - un petit peu plus de vitesse

    Quand on fait des mesures de temps de calcul, on se rend bien compte qu'on a seulement certaines parties critiques qui consomment beaucoup de temps, c'est ces parties là qu'il faut optimiser.

    Citation Envoyé par Delias Voir le message
    Le seul avantage de l'ASM c'est de forcer à réfléchir sur les optimisations de l’algorithme, alors qu'en C il faut trouver où cela pèche.
    Par contre l'ASM, c'est une horreurs dans la gestion des variables (normal il n'y en a pas) et donc dès que l'on dépasse un petit programme on aura vite fait une erreur...
    Oui c'est pas simple...
    Pour mon projet avec l'AT89C2051 j'avais tout fait en assembleur mais dans ce projet j'avais très peu de variables, c'était un projet assez "proche" de l'électronique, j'ai utilisé au maximum les registres et le code était rapide et compact.

    Ici, mon projet d'interface TFT tactile Ethernet est une sorte de terminal VT100 couleur miniature. C'est un gros projet.

    Quand je publierais le projet, s'il a du succès, peut être que quelqu'un arrivera à le rendre encore mieux (plus rapide et/ou plus compact).

    Citation Envoyé par Delias Voir le message
    Dans ton code c'est les divisions (même entières) qui doivent consommer la plus grande partie du temps de calcul, car la division n'est pas câblée, c'est une réalisation sur base de boucles.
    Oui, c'est la division qui coute cher, on est tous d'accord.

    Si vous regardez bien le code, vous verrez que la division n'est appelée qu'une fois à chaque appel de la fonction, sauf pour une des quatre bandes de couleur du Rainbow où la division est appelée une deuxième fois.

    On a l'impression qu'il y a des divisons partout mais elles sont dans des portions de code qui ne s'exécutent jamais ensemble (blocs if ... else)

    Citation Envoyé par Delias Voir le message
    Calculer l'inverse de Delta (voir même avec une lookup table si c'est un byte), puis faire le calcul en prenant soit une multiplication par Delta, soit une multiplication par l'inverse de Delta (et un décalage) peut considérablement accélérer le code.
    Delta est un entier signé (de -320 à +320)

    L'inverse de delta est un float...

    Ton idée est bonne : calculer l'inverse de delta multipliée par une grosse puissance de 2 pour que ce ne soit plus un float, de cette façon on ne fait que des multiplications

    C'est un peu ce principe que j'ai fait avec la variable 'k' :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    			R = ColGradient_R;
    				G = ColGradient_G;
    				B = ColGradient_B;
    				//if (ColGradient_DR !=0) R += ColGradient_DR * xy / delta;
    				//if (ColGradient_DG !=0) G += ColGradient_DG * xy / delta;
    				//if (ColGradient_DB !=0) B += ColGradient_DB * xy / delta;
    				byte k = (xy << 6) / delta;
    				if (ColGradient_DR !=0) R += (ColGradient_DR * k) >> 6;
    				if (ColGradient_DG !=0) G += (ColGradient_DG * k) >> 6;
    				if (ColGradient_DB !=0) B += (ColGradient_DB * k) >> 6;
    Les "<< 6" et ">> 6" tiennent compte du fait qu'une composante R, G ou B est comprise entre 0 et 61 maximum (et pas 255)

    Mais attention ici k n'est pas l'inverse de delta... k = (xy << 6) / delta... k est une variable qui tiens dans un byte c'est pour ça que cette optimisation est valable.

    Car si, pour les calculs soient justes, l'inverse de delta est dans un entier sur 16 bits ou pire sur 32 bits ça va casser la performance.

    C'est même un peu plus compliqué...

    Comme je l'ai dit plus haut, la division n'est appelée qu'une fois à chaque appel de la fonction (sauf pour une des quatre bandes de couleur du Rainbow où la division est appelée une deuxième fois)

    => donc pour optimiser la division, il faudrait trouver le moyen de la sortir de la fonction ; pour cela, il faut évidemment que la division ne dépende que de 'ColGradient_XY_delta' et pas de 'xy'.

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  12. #12
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Citation Envoyé par Guesset Voir le message
    Même si je n'en attendais pas des gains substantiels, je suis étonné que les suppressions de multiplications, de décalages et les return directs n'aient pas amélioré un tant soit peu les performances.

    Tu sais qu'elle erreur j'ai faite sur le rainbow ?
    D'abord merci d'avoir passé du temps à proposer une nouvelle version

    Pour l'erreur je vais faire une photo pour te montrer

    Moi aussi je suis supris... je pense que l'explication est la suivante : les returns doivent être compilés comme des "jump" avec une adresse sur deux octets

    Ils rendent la fonction plus longue ce qui doit casser certaines optimisations du compilateur

    Cette fonction n'est pas si simple. Je me suis bien cassé les dents dessus avant qu'elle marche correctement.

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  13. #13
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 185
    Points : 11 551
    Points
    11 551
    Par défaut
    Bonjour,
    J'ai lu rapidement et peut être en avez vous déjà parlé mais le réglage des optimisations du compilateur ont une grande influence sur ce qu'on pense avoir nous même optimisé.

    De plus je ne serai pas surpris que le compilateur n'essaie pas de transformer par défaut les multiplications/divisions par 2 par des décalages, ou fasse déjà des grandes simplifications (voir insoupçonnées) après l'analyse du code, etc...

    Nom : Capture024.png
Affichages : 237
Taille : 31,6 Ko
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  14. #14
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Citation Envoyé par Vincent PETIT Voir le message
    J'ai lu rapidement et peut être en avez vous déjà parlé mais le réglage des optimisations du compilateur ont une grande influence sur ce qu'on pense avoir nous même optimisé.
    Tout est compilé en Os

    J'utilise 100% de la flash

    A chaque fois qu'une de mes améliorations faisait économiser de la flash, j'en ai profité pour ajouter des fonctionnalités et/ou améliorer encore la vitesse en faisant des variantes de fonction selon le sens du dégradé par exemple.

    Utiliser 100% de la flash c'est bien : pas de gâchis

    Citation Envoyé par Vincent PETIT Voir le message
    De plus je ne serai pas surpris que le compilateur n'essaie pas de transformer par défaut les multiplications/divisions par 2 par des décalages, ou fasse déjà des grandes simplifications (voir insoupçonnées) après l'analyse du code, etc...
    Oui le compilateur fait ça. J'en ai eu la preuve en remplaçant des multiplications/division par 2^n par des décalages : résultat : même taille de flash et même vitesse

    A noter que malgré l'option Os, mon code est quand même rapide.

    Quelqu'un avait publié un benchmark, par rapport à l'absence d'optimisation, l'option "Os" augmente aussi la vitesse.

    Le compilateur fait quand même du bon boulot

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  15. #15
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut Bis Repetita
    Bonjour,

    J'ai revu l'enfilade de tests afin d'en faire un arbre (2 tests en moyenne contre 2.25). J'ai supprimé les else qui ne servaient à rien. Cela n'a pas d'incidence sur la rapidité mais pourrait en avoir sur la taille du code si le compilateur ne s'aperçoit pas que le else n'est pas atteignable en séquence et place, après le return, un saut pour éviter le bloc du else .

    J'ai repris le principe de k pour rainbow ce qui permet d'optimiser la bande vert->bleu. En outre cela simplifie les tests et les rend plus lisible.

    Ce qui peut (et pouvait) ralentir était le test sur Delta pour s'assurer qu'il n'était pas négatif ou nul (peut être que cette précaution est inutile). Par ailleurs, l'hypothèse que la probabilité des ColGradient_Dx était faible m'a incité à faire sauter ces tests qui consomment dans tous les cas pour un gain éventuellement rare. Si cette hypothèse est fausse il faut les rétablir :
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
       R = ColGradient_R + (ColGradient_DR ? (ColGradient_DR * A) >> 6: 0);
       G = ColGradient_G + (ColGradient_DR ? (ColGradient_DG * A) >> 6: 0);
       B = ColGradient_B + (ColGradient_DR ? (ColGradient_DB * A) >> 6: 0);

    J'ai tout passer en uint16_t ce qui est discutable puisque c'est le pari que travailler en 16 bit sur un uC 8 sera compensé par toutes les conversions implicites évitées.

    Je n'ai pas trouvé l'erreur sur rainbow. On pourrait ne pas utiliser de variables RGB pour celui-ci et tout calculer dans les return (mais peut être le compilateur fera cette optimisation de variable). La séquence est Rouge-Jaune-Vert-Bleu-Magenta mais en prenant Rouge-Jaune-Vert-Cyan-Bleu cela aurait l'insigne avantage de faire disparaître la bande Vert-Bleu à double calcul

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    uint16_t ColGradient_Colour_Calc(int xy) {
       if (ColGradient_Swap) xy = ColGradient_WH - xy; else xy -= ColGradient_XY_min;
       if ((xy <= 0) || (ColGradient_XY_delta <= 0)) return ColGradient1;
       uint16_t delta = ColGradient_XY_delta;
       if (xy >= delta) return ColGradient2;
       // ColGradient_Type 
       // 00000000 Gradient, 01000000 Rainbow, 11000000 Rainbow Inv, 10000000 FixedMiddle   
       if (ColGradient_Type == 0b10000000) return Colour[Coul_Index];
       uint16_t R, G, B, A;
       if (ColGradient_Type & 0b01000000) {
          A = (xy << 8) / delta;                     // A =  0 -> 255   
          if (ColGradient_Type & 0b10000000) A ^= 255;
          if (A < 128) {
             if (A < 64)                             // A =  0-> 63 ______________________Rouge -> Jaune
                return 0xF800 | (A << 5);            //             R = 31,      G =  0->63,  B = 0
             R = 63 - (A >> 1);                      // A = 64->127 _______________________Jaune -> Vert
             return (R << 11) | 0x7E0;               //             R = 31-> 0,  G = 63,      B = 0
          }
          if (A < 192) {                             // A =128->191 ________________________Vert -> Bleu
             G = 191 - A;                            //             R =  0,      G = 63-> 0,  B = 0-> 31
             B = (A >> 1) - 64;
             return (G << 5) | B;
          }
          R = (A >> 1) - 96;                         // A =192->255 ________________________Bleu -> Cyan
          return (R << 11) | 31;                     //             R =  0->31,  G =  0,      B = 31
       }                                             
       A = (xy << 6) / delta;                   
       R = ColGradient_R + (ColGradient_DR * A) >> 6;// Hypothèses Comp/Branch : 2, * : 6, >> : 3
       G = ColGradient_G + (ColGradient_DG * A) >> 6;//  test 63*(2+6+3) + 1*2 = 695, <op> = 10.85
       B = ColGradient_B + (ColGradient_DB * A) >> 6;// !test 64*(6+3) = 576,         <op> = 9      20%
       return (R << 11) | (G << 5) | B;
    }

    En espérant que cela améliorera un peu la rapidité du bidule. Mais je pense que cela serait beaucoup plus efficace conjugué avec des segments ou des box en dégradées (avec l'aide d'algo de tracé de ligne rapide) réservant le travail point par point aux seuls cas irréductibles.

    Si les décalages sur AVR ont un coût proportionnel à (ou même simplement croissant avec) l'amplitude du décalage; il serait opportun d'en factoriser certains.

    Salut
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  16. #16
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Salut,

    Alors il y a un gain de 178 octets de flash
    Et pour la vitesse :
    - Fixed Middle H : gain moyen de 1,6%
    - Fixed Middle V : gain moyen de 0,7%
    - Normal H : perte moyenne de 1,0%
    - Normal V : perte moyenne de 1,1%
    - Rainbow H : gain moyen de 9,4%
    - Rainbow V : gain moyen de 6,0%

    Malheureusement il y a des bugs de couleurs pour le dégradé normal et pour le rainbow (ton code c'est les images du haut, mon code original celles en bas):

    Nom : Degrades.jpg
Affichages : 225
Taille : 754,4 Ko

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  17. #17
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Bonjour,

    Bonne nouvelle j'ai pu retravailler le code, c'est plus compact et plus rapide, il n'y a plus le bug pour le gradient normal
    Gain de 216 octets de flash
    Pour la vitesse :
    - Fixed Middle H : gain moyen de 1,7%
    - Fixed Middle V : gain moyen de 0,7%
    - Normal H : gain moyen de 3,1%
    - Normal V : gain moyen de 2,0%
    - Rainbow H : gain moyen de 10,0%
    - Rainbow V : gain moyen de 6,5%

    Mais toujours le bug sur le rainbow V ; dans ce cas de figure 'xy' et 'delta' dépassent 255 et vont jusque 319, je pense que le problème est là

    Voici le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    uint16_t ColGradient_Colour_Calc(int xy) {
       if (ColGradient_Swap) xy = ColGradient_WH - xy; else xy -= ColGradient_XY_min;
       if (xy <= 0) return ColGradient1;
       uint16_t delta = ColGradient_XY_delta;
       if (xy >= delta) return ColGradient2;
    	// ColGradient_Type 
    	// 00000000 Gradient      
    	// 01000000 Rainbow       
    	// 11000000 Rainbow Inv   
    	// 10000000 FixedMiddle
       if (ColGradient_Type == 0b10000000) return Colour[Coul_Index];
       byte R, G, B;
       uint16_t A;
       if (ColGradient_Type & 0b01000000) {
          A = (xy << 8) / delta;                     // A =  0 -> 255   
          if (ColGradient_Type & 0b10000000) A ^= 255;
          if (A < 128) {
             if (A < 64)                             // A =  0-> 63 ______________________Rouge -> Jaune
                return 0xF800 | (A << 5);            //             R = 31,      G =  0->63,  B = 0
             R = 63 - (A >> 1);                      // A = 64->127 _______________________Jaune -> Vert
             return (R << 11) | 0x7E0;               //             R = 31-> 0,  G = 63,      B = 0
          }
          if (A < 192) {                             // A =128->191 ________________________Vert -> Bleu
             G = 191 - A;                            //             R =  0,      G = 63-> 0,  B = 0-> 31
             B = (A >> 1) - 64;
             return (G << 5) | B;
          }
          R = (A >> 1) - 96;                         // A =192->255 ________________________Bleu -> Cyan
          return (R << 11) | 31;                     //             R =  0->31,  G =  0,      B = 31
       }                                             
    	R = ColGradient_R;
    	G = ColGradient_G;
    	B = ColGradient_B;
    	byte k = (xy << 6) / delta;
    	if (ColGradient_DR !=0) R += (ColGradient_DR * k) >> 6;
    	if (ColGradient_DG !=0) G += (ColGradient_DG * k) >> 6;
    	if (ColGradient_DB !=0) B += (ColGradient_DB * k) >> 6;
       return (R << 11) | (G << 5) | B;
    }
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  18. #18
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Bonjour,

    Ca fonctionne maintenant

    Le problème était tout bête dans A = (xy << 8) / delta, si xy > 255, alors le résultat de (xy<<8) ne tiens pas dans un uint16_t

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    uint16_t ColGradient_Colour_Calc(int xy) {
       if (ColGradient_Swap) xy = ColGradient_WH - xy; else xy -= ColGradient_XY_min;
       if (xy <= 0) return ColGradient1;
       uint16_t delta = ColGradient_XY_delta;
       if (xy >= delta) return ColGradient2;
    	// ColGradient_Type 
    	// 00000000 Gradient      
    	// 01000000 Rainbow       
    	// 11000000 Rainbow Inv   
    	// 10000000 FixedMiddle
       if (ColGradient_Type == 0b10000000) return Colour[Coul_Index];
       byte R, G, B;
       uint16_t A;
       if (ColGradient_Type & 0b01000000) {
    	  if (xy > 255) {
    		A = (xy << 7) / delta;                     // A =  0 -> 127   
    		A <<= 1; 
    	  } else {
    		A = (xy << 8) / delta;                     // A =  0 -> 255   
    	  }
          if (ColGradient_Type & 0b10000000) A ^= 255;
          if (A < 128) {
             if (A < 64)                             // A =  0-> 63 ______________________Rouge -> Jaune
                return 0xF800 | (A << 5);            //             R = 31,      G =  0->63,  B = 0
             R = 63 - (A >> 1);                      // A = 64->127 _______________________Jaune -> Vert
             return (R << 11) | 0x7E0;               //             R = 31-> 0,  G = 63,      B = 0
          }
          if (A < 192) {                             // A =128->191 ________________________Vert -> Bleu
             G = 191 - A;                            //             R =  0,      G = 63-> 0,  B = 0-> 31
             B = (A >> 1) - 64;
             return (G << 5) | B;
          }
          R = (A >> 1) - 96;                         // A =192->255 ________________________Bleu -> Cyan
          return (R << 11) | 31;                     //             R =  0->31,  G =  0,      B = 31
       }                                             
    	R = ColGradient_R;
    	G = ColGradient_G;
    	B = ColGradient_B;
    	byte k = (xy << 6) / delta;
    	if (ColGradient_DR !=0) R += (ColGradient_DR * k) >> 6;
    	if (ColGradient_DG !=0) G += (ColGradient_DG * k) >> 6;
    	if (ColGradient_DB !=0) B += (ColGradient_DB * k) >> 6;
       return (R << 11) | (G << 5) | B;
    }
    Gain de 160 octets de flash
    Pour la vitesse :
    - Fixed Middle H : gain moyen de 1,7%
    - Fixed Middle V : gain moyen de 0,7%
    - Normal H : gain moyen de 3,1%
    - Normal V : gain moyen de 1,9%
    - Rainbow H : gain moyen de 9,4%
    - Rainbow V : gain moyen de 6,2%

    Je me demande si on peut améliorer les choses en faisant varier A de 0 à 127 plutôt que 0 à 255 ?
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  19. #19
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Une légère amélioration en utilisant un byte pour 'A' :

    Gain de 178 octets de flash
    Pour la vitesse :
    - Fixed Middle H : gain moyen de 1,9%
    - Fixed Middle V : gain moyen de 0,9%
    - Normal H : gain moyen de 3,3%
    - Normal V : gain moyen de 2,1%
    - Rainbow H : gain moyen de 9,6%
    - Rainbow V : gain moyen de 6,3%

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    uint16_t ColGradient_Colour_Calc(int xy) {
       if (ColGradient_Swap) xy = ColGradient_WH - xy; else xy -= ColGradient_XY_min;
       if (xy <= 0) return ColGradient1;
       uint16_t delta = ColGradient_XY_delta;
       if (xy >= delta) return ColGradient2;
    	// ColGradient_Type 
    	// 00000000 Gradient      
    	// 01000000 Rainbow       
    	// 11000000 Rainbow Inv   
    	// 10000000 FixedMiddle
       if (ColGradient_Type == 0b10000000) return Colour[Coul_Index];
       byte R, G, B;
       byte A;
       if (ColGradient_Type & 0b01000000) {
    	  if (xy > 255) {
    		delta = (xy << 7) / delta;                     
    		A = delta << 1; 
    	  } else {
    		delta = (xy << 8) / delta;                     
    		A = delta;
    	  }
    	  // A =  0 -> 255   
          if (ColGradient_Type & 0b10000000) A ^= 255;
          if (A < 128) {
             if (A < 64)                             // A =  0-> 63 ______________________Rouge -> Jaune
                return 0xF800 | (A << 5);            //             R = 31,      G =  0->63,  B = 0
             R = 63 - (A >> 1);                      // A = 64->127 _______________________Jaune -> Vert
             return (R << 11) | 0x7E0;               //             R = 31-> 0,  G = 63,      B = 0
          }
          if (A < 192) {                             // A =128->191 ________________________Vert -> Bleu
             G = 191 - A;                            //             R =  0,      G = 63-> 0,  B = 0-> 31
             B = (A >> 1) - 64;
             return (G << 5) | B;
          }
          R = (A >> 1) - 96;                         // A =192->255 ________________________Bleu -> Cyan
          return (R << 11) | 31;                     //             R =  0->31,  G =  0,      B = 31
       }                                             
    	R = ColGradient_R;
    	G = ColGradient_G;
    	B = ColGradient_B;
    	byte k = (xy << 6) / delta;
    	if (ColGradient_DR !=0) R += (ColGradient_DR * k) >> 6;
    	if (ColGradient_DG !=0) G += (ColGradient_DG * k) >> 6;
    	if (ColGradient_DB !=0) B += (ColGradient_DB * k) >> 6;
       return (R << 11) | (G << 5) | B;
    }
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  20. #20
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 932
    Points : 1 267
    Points
    1 267
    Par défaut
    Dernier raffinement : dans les premiers tests sur 'xy' remplacer <= et >= par < et >

    cela évite d'avoir les couleurs ColGradient1 et ColGradient2 au début et à la fin pour les dégradés sur la totalité de l'écran

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    uint16_t ColGradient_Colour_Calc(int xy) {
       if (ColGradient_Swap) xy = ColGradient_WH - xy; else xy -= ColGradient_XY_min;
       if (xy < 0) return ColGradient1;
       uint16_t delta = ColGradient_XY_delta;
       if (xy > delta) return ColGradient2;
    	// ColGradient_Type 
    	// 00000000 Gradient      
    	// 01000000 Rainbow       
    	// 11000000 Rainbow Inv   
    	// 10000000 FixedMiddle
       if (ColGradient_Type == 0b10000000) return Colour[Coul_Index];
       byte R, G, B;
       byte A;
       if (ColGradient_Type & 0b01000000) {
    	  if (xy > 255) {
    		delta = (xy << 7) / delta;                     
    		A = delta << 1; 
    	  } else {
    		delta = (xy << 8) / delta;                     
    		A = delta;
    	  }
    	  // A =  0 -> 255   
          if (ColGradient_Type & 0b10000000) A = 255 - A; //A ^= 255;
          if (A < 128) {
             if (A < 64)                             // A =  0-> 63 ______________________Rouge -> Jaune
                return 0xF800 | (A << 5);            //             R = 31,      G =  0->63,  B = 0
             R = 63 - (A >> 1);                      // A = 64->127 _______________________Jaune -> Vert
             return (R << 11) | 0x7E0;               //             R = 31-> 0,  G = 63,      B = 0
          }
          if (A < 192) {                             // A =128->191 ________________________Vert -> Bleu
             G = 191 - A;                            //             R =  0,      G = 63-> 0,  B = 0-> 31
             B = (A >> 1) - 64;
             return (G << 5) | B;
          }
          R = (A >> 1) - 96;                         // A =192->255 ________________________Bleu -> Cyan
          return (R << 11) | 31;                     //             R =  0->31,  G =  0,      B = 31
       }                                             
    	R = ColGradient_R;
    	G = ColGradient_G;
    	B = ColGradient_B;
    	byte k = (xy << 6) / delta;
    	if (ColGradient_DR !=0) R += (ColGradient_DR * k) >> 6;
    	if (ColGradient_DG !=0) G += (ColGradient_DG * k) >> 6;
    	if (ColGradient_DB !=0) B += (ColGradient_DB * k) >> 6;
       return (R << 11) | (G << 5) | B;
    }
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

Discussions similaires

  1. introduire du code assembleur dans un programme C++
    Par hzaki1986 dans le forum C++
    Réponses: 1
    Dernier message: 14/04/2007, 18h14
  2. Problème d'inclusion de code Assembleur dans du C
    Par tnarol dans le forum x86 32-bits / 64-bits
    Réponses: 5
    Dernier message: 06/09/2006, 19h56
  3. probleme d'insertion de code assembleur dans du c++
    Par Pragmateek dans le forum C++
    Réponses: 4
    Dernier message: 06/03/2006, 00h30
  4. Réponses: 5
    Dernier message: 14/01/2006, 16h16
  5. EDI pour intégrer efficacement de l'assembleur dans du C/C++
    Par Selenite dans le forum Choisir un environnement de développement
    Réponses: 2
    Dernier message: 04/04/2004, 19h27

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo