Libnoise et Shader CG/HLSL

Questions à propos du scripting Shader.
Avatar de l’utilisateur
gabriel
Messages : 174
Inscription : 08 Oct 2011 22:32
Localisation : Devant mon clavier

Re: Libnoise et Shader CG/HLSL

Message par gabriel » 18 Déc 2011 15:37

bonjour,
gabriel a écrit :
Bon après avoir retourner le problème dans tous les sens le meilleur compromis trouvé est à
59 instructions (6 texture, 53 arithmetic) sur 84 cyles pour un noise 2d
J'ai trouvé d'autres sens pour retourner le problème ;-)
Fx Composer intègre un desassembleur, je me suis donc armé d'une nouvelle boite d'aspirine pour voir ce qu'on pouvait en faire.

Je passe les détails mais à la fin de la fonction, on calcule la contribution de bruit pour chaque vertice.
Basiquement, si la contribution de bruit est inférieur à 0, on ne va pas calculer la valeur. cela a du sens car consommer quelques cycles de calcul pour un résultat qui sera finalement jeté à la fin n'est pas payant. Cela revient moins cher de tester une condition ...

Code : Tout sélectionner

	
// Calculate the contribution from the three corners
float3 t = 0.5 - float3(dot(xy0, xy0), dot(xy12.xy, xy12.xy), dot(xy12.zw, xy12.zw));
	
// Noise contributions from the three corners
float3 n = 0; 

if(t.x > 0){
		// Just one lookup table on lnoise_random
		n.x = pow(t.x, 4) * lnoise_FS_lookupGrad3FromRandomAndDot(i0.x + lnoise_FS_lookupRandom(i0.y) , xy0);
	}//end if
	
	if(t.y > 0){
		n.y = pow(t.y, 4) * lnoise_FS_lookupGrad3FromRandomAndDot(i0.x + i1.x + lnoise_FS_lookupRandom(i0.y + i1.y) , xy12.xy);
	}//end if 

	if(t.z > 0){
		n.z = pow(t.z, 4) * lnoise_FS_lookupGrad3FromRandomAndDot(i2.x + lnoise_FS_lookupRandom(i2.y) , xy12.zw);
	}//end if
... sauf qu'avec le GPU les instructions de saut n'existent pas (jne, jmp, ...). On ne peut donc pas effectuer de branchement conditionnel. En étudiant le code assembleur, je me suis rendu compte que la valeur du bruit à chaque vertice était calculée puis la comparaison effectuée pour éventuellement modifier le résultat !

J'ai donc optimisé cela en calculant t mais en utilisant la fonction max (0, %calcul%) ce qui fait que si %calcul% est inférieur à 0, t vaut 0, sinon %calcul%
si t vaut 0, tous les calculs liés à n (dépendant de t) seront alors égale à 0 ( pow(t, 4) * ).

Code : Tout sélectionner

	// Calculate the contribution from the three corners
	float3 t = max(0, (0.5).xxx - float3(dot(xy0, xy0), dot(xy12.xy, xy12.xy), dot(xy12.zw, xy12.zw)));
	
	// Noise contributions from the three corners
	// Just one lookup table on lnoise_random
	float3 n = pow(t, 4) * float3(
		lnoise_FS_lookupGrad3FromRandomAndDot(i0.x + lnoise_FS_lookupRandom(i0.y) , xy0).x,
		lnoise_FS_lookupGrad3FromRandomAndDot(i1.x + lnoise_FS_lookupRandom(i1.y) , xy12.xy).x,
		lnoise_FS_lookupGrad3FromRandomAndDot(i2.x + lnoise_FS_lookupRandom(i2.y) , xy12.zw).x
	);
Résultat :
approximately 54 instruction slots used (6 texture, 48 arithmetic)

De plus, en farfouillant dans le sujet j'ai appris quand dans certaines conditions, faire un lookup sur une texture dans un une condition était interdit par le GPU ...
error X6077: texld/texldb/texldp/dsx/dsy instructions with r# as source cannot be used inside dynamic conditional 'if' blocks, dynamic conditional subroutine calls, or loop/rep with break*.
en résumé
There are cases where usage of IF can be a good idea, but branching around for 1 line definitely costs more than it helps as a single vector op is a single tick instruction
Prochainement sur vos écrans

Avatar de l’utilisateur
gabriel
Messages : 174
Inscription : 08 Oct 2011 22:32
Localisation : Devant mon clavier

Re: Libnoise et Shader CG/HLSL

Message par gabriel » 22 Déc 2011 01:06

Bonsoir,

signe de vie !
approximately 48 instruction slots used (6 texture, 42 arithmetic)

Je peux presque plus faire mieux :x
J'ai fait un shader dans unity, ca marche. J'ai fait un test de perf entre ma texture sinfractal (128px*128px) et mon shader sinfractal:
- 24 ms/frm pour le code c#
- 1.8~2.1 ms/frm pour le shader (sur un plane), presque le même frame rate qu'avec une scène vide

C'est sans appel ...

J'ai pu descendre à 42 arithmetic en usant des fonctions de calcul parallèle. Faut vraiment se débrancher du code cpu ! En Gpu on peut faire un multiply + add sur un float 4 en une seule instruction ... Hier soir, j'ai passé 3 heures sur comment optimiser 3 lignes de code ... :D

J'ai trouvé une très bonne implémentation en GLSL (que j'ai aussitôt porté en hlsl ;). Elle est à 63 instructions mais n'utilisent pas de lookup texture et donc peut-être utilisée dans un vertex shader.
Je me suis rendu compte que nos premières lignes de code sont identiques, donc finalement ca m'a plutôt rassuré.

Je me penche sur la version 3D et 4D puis je ferai une publication sous forme de fichiers à inclure pour être utilisé dans des shader hlsl/glsl

Tout ceci fut vraiment très instructif !
Prochainement sur vos écrans

Avatar de l’utilisateur
Franck
Bricoleur
Bricoleur
Messages : 2884
Inscription : 08 Jan 2011 18:43
Localisation : Tours

Re: Libnoise et Shader CG/HLSL

Message par Franck » 22 Déc 2011 20:42

Gabriel, tu me subjugues...
Dés fois j'bug, dés fois j'bug pas.

Avatar de l’utilisateur
gabriel
Messages : 174
Inscription : 08 Oct 2011 22:32
Localisation : Devant mon clavier

Re: Libnoise et Shader CG/HLSL

Message par gabriel » 23 Déc 2011 23:47

Moi je sais que je fais toujours plaisir ... soit quand j'arrive, soit quand je repars :D
Bon, j'ai du noise en 2d, 3d avec lookup texture, sans lookup texture. J'ai mis de coté pour le moment le 4d.
J'ai pondu un vrai mais simple shader unity. Ca marche bien. Je confirme les perfs. Au moins 10x en gpu mais l'usage se limite quand même. Plus on charge la mule pour exploiter le bruit, plus ca se gave d'instruction. Faut donc trouver un bon compromis entre la génération hors ligne et temps réels. Pour des animations, on peut par exemple précalculer des textures sur 10/15 frames avec un raccord entre la 15 et la 1 et les faire tourner en boucle avec un shader.

Franchement, je ne pense pas qu'on puisse intensivement utiliser du bruit. 48 instructions pour une valeur, c'est assez prohibitif. C'est presque autant que pour générer un éclairage moyen, sauf à avoir une implémentation hardware, ce qui n'est pas gagné. Par contre, cela peut avoir un intérêt certain pour le calcul hors ligne car la version cpu bien que modulaire trouve quand même une limite à un certain moment ... à moins d'avoir une petite ferme de calcul. Ma prochaine étape est de pouvoir utiliser à la fois le cpu et le gpu pour calculer. Une noisemap simple avec 7 octaves à 1024 pixels/² se calcule à 250/fps ... alors que la même en cpu prends 10.5 seconde ... L'idée est donc de gérer une logique d'assemblage en cpu et d'envoyer les calculs au gpu. Technique je sais exactement comment le faire sauf pour récupérer le résultat du calcul. Bon, j'en suis à une simple étude théorique pour le moment.

Dans l'immédiat, je vais m'atteler à pondre une démo, publier les sources et jouer un peu avec ;-)
Dernière édition par gabriel le 31 Déc 2011 04:29, édité 1 fois.
Prochainement sur vos écrans

Avatar de l’utilisateur
gabriel
Messages : 174
Inscription : 08 Oct 2011 22:32
Localisation : Devant mon clavier

Re: Libnoise et Shader CG/HLSL

Message par gabriel » 26 Déc 2011 00:42

Bonjour,
Max a écrit : je passerais par un texture shader, t'attaque directement la VRAM, et en suite tu fais ce que tu veux avec ta texture procédurale (mapping, sauvegarde, etc...). DirectX propose des fonctions simples (si on peut dire) pour y arriver. Par contre avec Unity, là, je sais pas (du moins pas encore)...
Bon après avoir fait joujou, je me suis penché là dessus. Alors, pour recadrer le contexte, je n'ai pas forcément besoin de calculer en temps réel ma texture. En fait, une seule fois suffit. Dès qu'on commence à empiler des modules de bruit, le temps de traitement s'allonge si on utilise le cpu. En revanche, utiliser le GPU augmente très significativement les temps de traitement. Comme je le disais, sur une texture basique de bruit (simplex avec un filtre sinFractal à 6 octaves), sur une surface de 1024px², en cpu je calcule en 1.2 seconde, en gpu en 3 ms. Le gain n'est pas à démontrer. Mon idée est de pouvoir utiliser le GPU pour créer une image, donc ne pas l'afficher à l'écran.

1 - J'ai donc suivi ta piste des textureShader.
C'est exactement ce dont j'ai besoin sauf que le shader est exécuté sur le cpu ce qui ne m'avance pas.

2 - J'ai poursuivi la piste des renderToTexture. Alors là ca sent meilleur car on peut rendre la scène dans une image. Théoriquement, je devrais afficher une scène de 1024px², mettre un plane exactement devant puis lancer l'affichage de la scène avec le shader sur le plane, enregistrer le tout dans une image. Comme je suis dans ma période c#, j'ai cherché de quoi utiliser directx en c# "facilement". Xna bien évidement mais aussi SlimDX. On peut faire cela avec les classes RenderToSurface et Surface.
Voici un exemple ace Xna
http://www.riemers.net/eng/Tutorials/Di ... exture.php
On obtient donc une texture qu'on peut ensuite enregistrer dans un fichier.

3 - Donc la solution 2 semble une bonne alternative sauf que finalement on hack un peu le processus. Ca revient en gros à faire un imprimer écran :D (j'y avais pensé au début). De mémoire, j'avais entendu parlé de faire du calcul avec le gpu sans pour autant avoir l'idée de faire un rendu : gpgpu pour General purpose GPU et les compute shader. Rapidement, on tombe sur Cuda (nvidia), Stream(Ati), OpenCL (ati/nvidia) et direct compute (ati/nvidia)
... Alors là c'est du grand art ! On met de coté l'aspect graphique, on réalise un shader qui ne s'occupe que de faire du calcul, ce dont j'ai exactement besoin, en utilisant le GPU. Néanmoins, on peut utiliser cette méthode en liaison avec du rendu 3d standard. On peut par exemple, à certains moments calculer une portion de terrain avec un compute shader dans un thread, pour venir ensuite incorporer le mesh calculé dans la scène texturée avec un pixel shader.
Le compute shader c'est véritablement une unité de calcul indépendant du pipeline graphique standard. Bon, j'avoue avoir eu une demi molle :D en voyant quelques exemples

Un compute shader pour créer une image. Bon ok on reste beaucoup dans le besoin graphique sauf que ce n'est pas un pixel shader
http://recreationstudios.blogspot.com/2 ... ample.html

Des effets dans paint.net
http://forums.getpaint.net/index.php?/t ... ctcompute/
Alors là c'est la crise. Sur des effets de flou, le gars obtiens des perfs de 12000 % sur une image de 4800 x 6400. Oui, oui c'est bien douze mille pourcent et pas 12 pourcent ;)
CPU (Approx Times) 16min 47.6s
GPU Speed Increase (Approx) 8281ms + 12,067%

Bon alors là c'est le bon exemple, cpu, gpu, gpgpu. La structure du mesh en calculé en gpgpu, le rendu en gpu le tout piloté par le cpu
http://www.youtube.com/watch?v=K1I4kts5mqc

Deux clopes plus tard, mes esprits retrouvés, je reviens sur terre : Je n'ai pas de carte graphique compatible dx11, shader model 5 et direct compute. :cry: mais c'est vraiment une grosse bonne nouvelle pour moi 8-)

Je vais donc faire un peu de ménage dans mon bordel et, en attendant d'éventuellement prendre une nouvelle carte graphique, je vais me pencher sur la solution 2.
Prochainement sur vos écrans

Avatar de l’utilisateur
ZJP
Messages : 5748
Inscription : 15 Déc 2009 06:00

Re: Libnoise et Shader CG/HLSL

Message par ZJP » 26 Déc 2011 16:48

Salut,

Très complets tes posts. Merci. 8-)
Parmi les "tools" qui pourraient te servir, il y a GeeXLab : http://www.geeks3d.com/softwares/

JP

Avatar de l’utilisateur
gabriel
Messages : 174
Inscription : 08 Oct 2011 22:32
Localisation : Devant mon clavier

Re: Libnoise et Shader CG/HLSL

Message par gabriel » 31 Déc 2011 04:27

Bonjour,

Bon à mon tour d'envoyer les perfs de fou ;-)

Alors avec un SimplexNoise 3D + sumFractal à 10 octaves sur un plan de 1024 px², on a 10 485 760 d'opérations pour la création de la noise map et 1 048 576 d'opérations pour le rendu

Ma (vieille) machine Intel core 2 duo 1.5ghz et une ati radeon hd 2600
En cpu (libnoise dotnet), release build sans debug,
A la première phase, on calcule la valeur de bruit à chaque coordonnée x,y puis, en seconde phase, suivant les valeurs de chaque coordonnées x,y, on réaligne la valeur sur [0 .. 255] et on affiche ensuite l'image dans un picture box (copie de la DataMap vers une Bitmap)

1 - Création de la noise map : 2. 90 secondes
2 - Rendu de l'image (grayscale) : 3.3650 secondes
Total : 6.2750 secondes


En gpu (libnoise GPU/HLSL sur slimDX + dx11/fx_5_0), release build avec debug
A la première phase, on utilise un pixel shader pour calculer et rendre la couleur de chaque pixel au coordonnées x,y, puis, en seconde phase, on duplique le backbuffer obtenu et, en troisième phase, on sauvegarde le buffer dans un fichier png enregistré sur le disque.

1 - Création de la noise map : 0,000453043 secondes
2 - Transfert du backbuffer: 0,0002675828 secondes
3 - Sauvegarde de l'image (png) : 0,5727661 secondes
Total = 0.5734 secondes

Les deux processus sont à peu près équivalent, pas les temps de traitement ! D'ailleurs, je ne sais pas pourquoi je mets un s à seconde, il n'y en a même pas une !! :D

J'ai utilisé SlimDX comme api c# autour de dx11 car :
- colle plus à l'api directX que XNA
- pas orienté développement de jeu mais peu bien sûr servir à cela
- supporte dx11 (XNA non), ce qui m'intéresse pour utiliser plus tard les compute shader
- opensource (MIT licence), on peut donc voir ce que fait le wrapper ce qui est très instructif

J'aurai pu aussi utiliser OpenTk qui est (à peu près) son pendant pour OpenGL mais chaque api à la fois :)

Par contre, les ressources slimDX sont assez réduites mais toutes bonnes infos sur dx11 et XNA est à peu près compatible. Pour ce que je j'avais à faire, le tutorial "SimpleTriangle" de leur site était suffisant. Il y a un autre bon tutorial ici http://blogs.msdn.com/b/eternalcoding/a ... 3d-11.aspx
Je recommande de faire un checkout sur le dossier trunk/sample ou de regarder ici http://code.google.com/p/slimdx/source/ ... %2Fsamples

Pour extraire le backbuffer, j'ai trois technique simple :
Après avoir appeler swapChain.Present(0, PresentFlags.None);

Resource.SaveTextureToFile(context, renderTarget.Resource, ImageFileFormat.Png, "test_1.png");

ou

Texture2D.ToFile(context, (Texture2D)renderTarget.Resource, ImageFileFormat.Png, "test_2.png");

ou

Texture2D duplicate = Resource.FromSwapChain<Texture2D>(swapChain, 0);
context.CopyResource(renderTarget.Resource, duplicate);
Texture2D.ToFile(context, duplicate, ImageFileFormat.Png, "test_3.png");
duplicate.Dispose();

Les méthodes 1 et 2 sont équivalentes mais déclenchent un exception si on bouge/redimensionne la fenêtre. Voici l'issue ici http://code.google.com/p/slimdx/issues/detail?id=856

La suite :
- intégrer le générateur GPU dans mon programme de démonstration au sein d'un panel, en profiter pour mettre beaucoup d'ordre dans le code
- publier le tout
- trouver une méthode pour générer à partir d'une description de modules le moyen de produire le résultat soit en cpu, soit en gpu -> créer un mini langage de description
- publier le tout
Prochainement sur vos écrans

Avatar de l’utilisateur
Alesk
Messages : 2303
Inscription : 13 Mars 2012 09:09
Localisation : Bordeaux - France
Contact :

Re: Libnoise et Shader CG/HLSL

Message par Alesk » 22 Mars 2012 00:06

Mon héro !!!!

Je cherche désespérément un moyen de générer un noise 3D pour améliorer le rendu de ma fumée, démo ici : viewtopic.php?f=12&t=3403

Pour le moment, je gruge avec une texture 2D précalculée, c'est pas mal, mais pas parfait.

Si ton shader permet de générer ce noise en world space, ça collera parfaitement avec ce dont j'ai besoin :)
Dis-moi que c'est possible, please ! :mrgreen:

Répondre

Revenir vers « les Shaders »