Comment puis-je faire une démo graphique sans tampon d'image?
Je programme un microcontrôleur - un petit ordinateur 8 bits qui a environ 100Mhz - 300Mhz de vitesse d'horloge du processeur et 64K de RAM d'application (en fait ROM), mais seulement une petite quantité de RAM (6K) - loin d'être suffisant pour un tampon de trame graphique.
Je souhaite donc afficher une démo graphique animée sur cette machine. Il semble avoir une puissance de calcul suffisante pour calculer des graphiques animés.
Pour obtenir des graphiques animés à afficher, je vais en quelque sorte avoir besoin d'un programme informatique qui, chaque fois qu'il est appelé, renvoie une seule ligne de pixels d'un cadre d'image, qui est ensuite affichée. Le programme du contrôleur d'affichage continue à faire cela, appelant ligne après ligne jusqu'à ce qu'il ait affiché une image entière, puis il recommence en haut pour l'image suivante.
Juste pour être clair, je ne pose pas de question sur le programme qui contrôle l'affichage. Je pose des questions sur le programme de démonstration. J'ai besoin d'un programme de démonstration qui donne ses graphiques une ligne à la fois, que mon contrôleur d'affichage jettera ensuite sur l'écran.
J'essaie d'imaginer quel genre de programme graphique pourrait fonctionner de cette façon? Vraisemblablement l'affichage d'une sorte de formule mathématique.
Quelqu'un peut-il suggérer des techniques de programmation graphique qui fonctionnent de cette manière, dans lesquelles une fonction peut être appelée, qui calcule et renvoie une ligne du cadre, uniquement sur demande? Existe-t-il des programmes existants qui fonctionnent comme celui-ci que je pourrais examiner?
J'espère que la question a du sens. Tous les pointeurs dans la bonne direction ou des indices seraient appréciés.
Réponses
C'est un cas où il serait raisonnable d' examiner le fonctionnement d'un matériel plus rétro . Le matériel plus ancien présentait de fortes limitations en termes de mémoire et de puissance de traitement. Votre puce 100 + Mhz est bien plus rapide que la plupart des puces grand public des années 80 et avant. Ainsi, bien que ces processeurs aient besoin de puces graphiques dédiées pour pouvoir effectuer le rendu, votre processeur beaucoup plus rapide peut probablement gérer la tâche de manière adéquate.
Je suggérerais de commencer par quelque chose comme l'architecture tilemap-and-sprite de la NES. Ces architectures sont conçues pour être à la fois efficaces en mémoire (en tirant beaucoup sur un espace de stockage limité) et en calcul dans une sortie ligne par ligne, car elles ont généré des données de pixels et les ont envoyées directement au périphérique d'affichage à la vitesse attendue d'un CRT.
L'idée générale est la suivante. Un tilemap se compose de deux parties: une série de tuiles et un tableau 2D d'indices dans la carte de tuiles qui représente l'image en cours de création. Les tuiles sont généralement 8x8, et sur le NES étaient 2 bits par pixel et les tuiles utilisaient des palettes. Ou plus spécifiquement, l'index dans la carte de tuiles inclut non seulement l'index d'une tuile mais également l'index de la palette à utiliser avec cette tuile. Ainsi, une tuile n'est pas intrinsèquement associée à une palette; l'association se fait au point d'utilisation (techniquement, sur la NES, la carte de tuiles et la carte de palette étaient séparées, car chaque bloc de 2x2 de tuiles devait tous utiliser la même palette).
Pour permettre le défilement, la carte de tuiles est plus grande que l'écran visible, et il y a un décalage qui représente l'endroit où le coin supérieur gauche de l'écran visible se trouve dans la tuile. Soit ce décalage à la position Xoff
et Yoff
.
Vous pouvez voir comment cela rend le traitement ligne par ligne trivial. Pour générer une ligne horizontale pour la position horizontale Ypos
(dans l'espace écran), vous devez obtenir le pixel de départ dans le tilemap. Cela nécessite de transformer la position XY (0, Ypos)
de l'espace écran en espace tilemap. Vous y ajoutez donc le vecteur (Xoff, Yoff)
, en masquant le vecteur `` Xoff, Yoff + Ypos) `.
Notez que lorsque vous effectuez un mappage de l'espace écran à l'espace tilemap, l'espace tilemap doit s'enrouler dans les axes X et Y. Ainsi, chaque fois que vous calculez un nouveau pixel dans l'espace tilemap, vous devez l'enrouler autour de la taille de l'espace tilemap.
Maintenant, nous devons décomposer ce pixel de tilemap en deux composants 2D: l'index de tuile dans le tilemap qui nous donne ce pixel, et le pixel dans cette tuile que nous devons récupérer pour ce pixel. L'index de tuile est juste le pixel de l'espace tilemap-space divisé par la taille de tuile. La coordonnée de pixel est l'entier de pixel de l'espace de tilemap modifié par la taille de la tuile. Étant donné une taille de tuile 8x8, vous faites ceci:
ivec2 tilemap_pixel = ...; //Compute the tilemap starting pixel as above.
ivec2 tilemap_tile = ivec2(tilemap_pixel.x & ~0x7, tilemap_pixel.y & ~0x7); //Mask off the lower 3 bits.
ivec2 pixel_in_tile = ivec2(tilemap_pixel.x & 0x7, tilemap_pixel.y & 0x7); //Mask off all but the lower 3 bits.
Ainsi tilemap_tile
, nous avons maintenant l'index de la tuile dans laquelle nous travaillons actuellement. Et pixel_in_tile
nous donne le pixel dans lequel nous travaillons. Nous pouvons donc récupérer ce pixel, dont la valeur peut être mappée dans une palette pour produire la couleur finale de ce pixel. Ou nous pouvons l'utiliser directement.
Obtenir le pixel suivant est assez trivial. Nous incrémentons simplement pixel_in_tile.x
de 1, modulo la taille de la tuile. Si l'incrément a dépassé la taille de la tuile, alors nous incrémentons également tilemap_tile.x
de 1, modulo la taille du tilemap. Et vous continuez jusqu'à ce que vous ayez rempli la rangée de pixels.
Il existe de nombreuses possibilités d'optimisation des performances d'un tel algorithme.
Les tuiles représentent probablement les données les plus importantes. Un jeu de tuiles de 128 éléments de 8x8 tuiles, même à 2bpp, équivaut à 2K. Mais le fait est que ceux-ci peuvent être stockés dans la ROM , car vous ne changez probablement pas les tuiles elles-mêmes. La taille de la mappe de tuiles (en supposant qu'elle doit être en RAM) dépend de la résolution de sortie souhaitée et de la taille des tuiles. Une carte de tuiles pour des tuiles 8x8 pouvant couvrir un écran de 320x240 est de 1 200 octets. Pas vraiment minuscule. Vous aurez besoin de plus de mémoire si vous voulez de la place pour un défilement fluide (et donc une carte de tuiles plus grande).
Cela étant dit, il est également important de noter que la "taille de sortie" ne doit pas nécessairement être la taille réelle du périphérique d'affichage . Par exemple, si vous avez l'intention de dessiner sur un périphérique d'affichage 1080p, vous pouvez toujours effectuer le rendu à une résolution interne qui est, par exemple, 8 fois plus petite, 240x135. Il est facile de modifier cet algorithme pour générer essentiellement la même valeur de pixel 8 fois de suite, ainsi que de réutiliser la même ligne 8 fois par ligne 1080p. En effet, il ne serait pas trop difficile de faire en sorte que cet algorithme permette le défilement des sous-pixels (défilement dans l'espace 1080p, pas dans l'espace 135p) et même l'ajout d'un filtrage entre les valeurs de pixel. Un tilemap de 240x135 "taille de sortie" ne nécessiterait que 510 octets pour la région visible, vous auriez donc plus de place pour une zone de défilement plus grande.