Skip to content

Commit 50f0621

Browse files
zelenskiCS107E BOT
authored andcommitted
Release lab6, add speed exercise (move from lab7)
commit 43311f66382183cc7a0eb4c744b47f8772fe773d Author: Julie Zelenski <[email protected]> Date: Sun Feb 16 17:12:41 2025 -0800 Release lab6, add speed exercise (move from lab7)
1 parent ad3d0ff commit 50f0621

File tree

4 files changed

+308
-1
lines changed

4 files changed

+308
-1
lines changed

_data/unreleased.csv

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ permalink,title,released
22
"/assignments/assign6/","Assignment 6: Graphics Library and Console",false
33
"/assignments/assign7/","Assignment 7: System Monitor with Interrupts",false
44
"/project/","Final Project",false
5-
"/labs/lab6/","Lab 6: Drawing into the Framebuffer",false
65
"/labs/lab7/","Lab 7: Mango Pi, Interrupted",false
76
"/labs/projectlab1/","Lab 8: Project Team Meeting 1",false
87
"/labs/projectlab2/","Lab 9: Project Team Meeting 2",false

labs/lab6/README.md

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
---
2+
released: true
3+
permalink: /labs/lab6/
4+
title: 'Lab 6: Drawing into the Framebuffer'
5+
attribution: Lab written by Philip Levis and Pat Hanrahan, updated by Julie Zelenski
6+
toc: true
7+
readings: |
8+
[Prelab](/labs/lab6/#prelab-preparation) to prep before lab.
9+
---
10+
{% comment %}
11+
Task list to copy/paste when creating PR for this lab:
12+
13+
__Before releasing lab6:__
14+
- [ ] Review writeup/code/checkin questions (instructor)
15+
- [ ] Walk through (SL)
16+
- [ ] Followup on issues from previous quarter postmortem (issue #403)
17+
18+
__To prep for lab6:__
19+
- [ ] Confirm lab has working monitors with HDMI cables (aim for 1 for every 2 students)
20+
21+
{% endcomment %}
22+
23+
24+
## Goals
25+
26+
For your next assignment, you will implement library of simple graphics routines and use them to implement a text console for your shell. The goal of this lab is to review concepts and code in preparation.
27+
28+
During this lab you will:
29+
30+
- Connect your Pi's HDMI port to a monitor and generate video
31+
- Write code to allocate framebuffer memory and make changes to the pixel data
32+
- Review C syntax for pointers to multi-dimensional arrays
33+
- Read and understand fonts and the representation of characters
34+
35+
## Prelab preparation
36+
To prepare for lab, do the following:
37+
38+
1. Be up to date on recent lectures: __Graphics and 2-d arrays__
39+
1. Organize your supplies to bring to lab
40+
- Bring your laptop (with full charge) and full parts kit.
41+
- You do not need your PS/2 keyboard for this lab, it can stay home.
42+
- You will need a HDMI monitor to complete this lab. We have monitors and cables available in lab for shared use.
43+
44+
## Lab exercises
45+
46+
### 0. Pull lab starter code
47+
48+
Change to your local `mycode` repo and pull in the lab starter code:
49+
50+
```console
51+
$ cd ~/cs107e_home/mycode
52+
$ git checkout dev
53+
$ git pull code-mirror lab6-starter
54+
```
55+
### 1. Confirm video
56+
57+
Find an available monitor in lab, connect the monitor to power, and turn it on.
58+
Connect its HDMI cable to the mini-HDMI port on your Mango Pi.
59+
60+
Change to the directory `lab6/grid` and build and run the grid program using
61+
`make run`. The monitor should display a grid of white lines on a black background.
62+
63+
Everyone should connect their Pi to a monitor and confirm the grid display.
64+
For the rest of the lab, 2-3 partners can share one Pi/monitor.
65+
66+
### 2. The `fb` module
67+
68+
You will implement the `fb` module to handle allocating the framebuffer memory
69+
and coordinating with the lower-level `de` and `hdmi` modules to control
70+
the hardware peripherals that display the framebuffer pixels on the screen.
71+
72+
#### Review code of modules `de` and `hdmi`
73+
Our reference libmango provides the implementation of the `de` (Display Engine)
74+
and `hdmi` modules.
75+
Peruse the module interfaces [$CS107E/include/de.h](/header#de) and [$CS107E/include/hdmi.h](/header#hdmi). You
76+
'll note that the modules export a tiny number of functions.
77+
We wrote those modules to support exactly and only the very specific features needed for our system.
78+
79+
Do a brief skim over the the implementation in files
80+
[$CS107E/src/de.c](/src#de) and [$CS107E/src/hdmi.c](/src#hdmi). You will not modify this code and
81+
do not need to make deep dive into it. Your `fb` module will interact with
82+
the `de` and `hdmi` modules through limited public functions documented in the header files.
83+
84+
#### Initializing the framebuffer
85+
86+
Change to the directory `lab6/fb`. The directory contains these files:
87+
88+
```console
89+
$ ls
90+
Makefile fb.c main.c
91+
```
92+
The file `fb.c` contains code to initialize the framebuffer. The `fb` module
93+
declares a struct to hold the module-level state. This struct has four fields:
94+
95+
96+
- `width`, `height`: width/height of framebuffer in pixels
97+
- `depth`: number of bytes per pixel (always 4 in our system)
98+
- `framebuffer`: base address of the framebuffer memory
99+
100+
Review the code in the `fb_init` function. Discuss with your tablemates and
101+
try to answer the questions below.
102+
103+
1. How are total bytes calculated when allocating the framebuffer?
104+
2. What is the color of the pixels in the newly created framebuffer?
105+
3. How does the framebuffer coordinate with the `hdmi` module to set the sceen size?
106+
4. How does the framebuffer communicate with the `de` module to draw the pixels on screen?
107+
5. What kind of changes will be needed for the `fb` module to support double-buffering (i.e. separate front/back buffers)?
108+
109+
If you do not feel confident about your answers or do not agree, please ask us to help resolve!
110+
You're ready for this check-in question.[^1]
111+
112+
113+
### 2. Draw pixels
114+
In directory `lab6/grid`, open the `grid.c` file in your text editor and modify the program in the following ways:
115+
116+
1. Change the grid code to draw horizontal lines in red and vertical lines in yellow.
117+
Remember from lecture that the B (blue) in BGRA is the lowest byte.
118+
119+
2. Change the grid code to draw a checkerboard pattern
120+
(alternating filled red and yellow squares). Pro-tip: define a helper function
121+
that draws a filled rectangle.
122+
123+
> __Here be dragons__ One critical fact to keep in mind when working with the framebuffer memory is that C does no bounds-checking on array indexes.
124+
Writing to an index
125+
outside the bounds of the framebuffer will simply overwrite data at the inappropriate location.
126+
Such as transgression can create a strange variety of symptoms, including corrupting the framebuffer image
127+
and/or locking up the pipeline to the display hardware. Should you encounter seemingly bizarre results
128+
when writing the framebuffer, review how your code accesses its memory. On a related note, redzone
129+
protection can be lifesaver in these situations. The malloc module of the reference library has it enabled, if you did not do the extension yourself.
130+
{: .callout-danger }
131+
132+
Check-in with us and show off your checkerboard. [^2]
133+
134+
### 3. Multi-dimensional arrays
135+
136+
Pointers are ubiquitous in systems programming in C and one of the most
137+
difficult concepts to master. Just when you are getting the hang of pointers
138+
and one-dimensional arrays, now we're adding multi-dimensioned arrays into the
139+
mix. The goal of this lab exercise is to review pointers and multi-dimensional
140+
arrays in preparation for your next assignment.
141+
142+
One convenient way to represent images is with a two-dimensional array.
143+
Treating it as a 2D array can be easier than explicitly calculating offsets
144+
into a one-dimensional array.
145+
146+
To start, here is a quick self-test:
147+
148+
* What is the difference between the following two declarations?
149+
Think about what operations are valid on `a` versus `b`. Also
150+
think about what memory is allocated.
151+
152+
char *a = "Hello, world\n";
153+
char b[] = "Hello, world\n";
154+
155+
* What is the difference between the following two declarations?
156+
157+
int *p[2];
158+
int (*c)[2];
159+
160+
You may find the __[cdecl tool](http://cdecl.org/)__ helpful in demystifying a complex C declaration.
161+
162+
Inspect the code in `lab6/pointers/pointers.c`. Compile the program using
163+
`make`, run it on your Pi, and interpret the results. Ask questions about
164+
anything that doesn't make sense to you and check-in with us to confirm your
165+
understanding. [^3]
166+
167+
### 4. Fonts
168+
169+
A major part of your assignment will be to draw text on the screen.
170+
In order to do this, you need a *font*. Each character in the font is a little
171+
picture that represents the glyph to draw.
172+
173+
![Font](images/Apple2e.bmp){: .zoom .w-100}
174+
175+
This famous font was used by the original Apple IIe. We chose it to
176+
provide that extra-special retro touch for your graphical console.
177+
178+
Review the file [$CS107E/src/font.c](/src#font). It declares a `font_t` struct for representing a font and
179+
defines the variable `font_default`:
180+
181+
```
182+
/* from font.c */
183+
typedef struct {
184+
unsigned char first_char, last_char;
185+
int glyph_width, glyph_height;
186+
uint8_t pixel_data[];
187+
} font_t;
188+
189+
static const font_t font_default = {
190+
.first_char = 0x21, .last_char = 0x7F,
191+
.glyph_width = 14, .glyph_height = 16,
192+
.pixel_data = {
193+
0x03, 0x00, 0x33, 0x00, 0xcc, 0x00, 0xc0, 0x3c,
194+
0x00, 0x30, 0x00, 0x30, 0x00, 0xc0, 0x03, 0x00,
195+
...
196+
0x3f, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
197+
0x00, 0x00, 0x00, 0x00
198+
}
199+
};
200+
```
201+
202+
The pixel data for the character glyphs is stored as a bitmap. In a bitmap,
203+
each pixel is represented by a single bit. If the bit is 'on', the pixel is to
204+
be drawn in the foreground color; if 'off', the pixel is set to the background
205+
color. We use a bitmap rather than full RGBA because it takes much less (32
206+
times less) memory. This makes the font data much smaller, and hence faster to
207+
upload to your Pi.
208+
209+
Below is a pictorial representation of `font_default` using green to display
210+
each 'on' pixel in the bitmap. (click the image to see larger version):
211+
212+
![Font](images/apple2e-line.bmp){: .zoom .w-100}
213+
214+
The font bitmap stores the character glyphs in a single line. The leftmost glyph is for the
215+
character '`!`', whose ASCII value is 33 (0x21), the neighboring glyph is for
216+
character '`"`' (ASCII 34) and so on ending with rightmost character ASCII
217+
value 127 (0x7f) Delete, whose glyph is a little checkerboard. The font bitmap
218+
contains glyphs for 95 total characters, from ASCII 33 to 127.
219+
220+
Each glyph is the same size: 14 pixels wide and 16 pixels tall. This is termed
221+
a *fixed-width* font.
222+
223+
The bitmap is stored using an array of `uint8_t`
224+
values. For example, the first two bytes in the array are `0x03, 0x00`. Group
225+
the 8 bits from the first byte and 6 bits from the second into the 14-bit
226+
sequence `0b00000011000000`. These 14 bits correspond to the top row of the
227+
first glyph, the exclamation point. The bits indicate the vertical line for
228+
the exclamation point is 2 pixels wide and positioned in the center.
229+
230+
Each line of the bitmap is 1330 pixels long (95 characters * 14 pixels wide),
231+
and requires 1330 bits. If each glyph is 16 pixels tall, how many total
232+
bytes are in the `pixel_data` array?
233+
234+
Look carefully at the function `font_get_glyph()` in `font.c` which copies a
235+
single glyph from the font bitmap into a buffer.
236+
237+
* Trace the operation of `font_get_glyph` for ASCII character `&` (ascii hex
238+
0x26)? At what locations in `pixel_data` does it look to find the appropriate
239+
bits?
240+
241+
Now change to the directory `lab6/banner` and review the code in `banner.c`.
242+
This program calls `font_get_glyph` to store the glyph image into `buf`. The
243+
code then wants to go on to access `buf` as a 2-d array through the variable
244+
named `img`, but `img` is missing its declaration and initialization. Read and
245+
follow the instructions marked `TODO:` to fix this issue. Compile and run and
246+
you'll get an ascii banner that prints letters to the terminal using your Pi's
247+
font - neat!
248+
249+
You're ready for this check-in question.[^4]
250+
251+
### 5. Drawing performance
252+
After you have gotten the functionality of your graphics modules working correctly, you can take things to the next level by turning your attention to optimization. Small targeted changes to a heavy-travelled tight loop can reap stunningly big gains. This exercise is a fun exploration in small tweaks to get your graphics code moving wickedly fast.
253+
254+
Change directory to `lab6/speed` and review the source in the `speed.c`
255+
file. The program defines a `redraw` operation and uses the timer to count the ticks during the function's execution. The purpose of `redraw` is to draw every pixel in the screen in the same color (i.e. operation akin to `gl_clear` ).
256+
257+
The `redraw0` function works correctly, but is naive to a fault. Build and run the
258+
code as given and it will print the number of ticks to execute it. Write down this tick count, it will be the baseline to beat. It is possible to gain a __speedup of 100x__ or even a __1000x__ over the redraw0 baseline!
259+
260+
Follow a stepwise process so you can see and measure the effect of each
261+
modification in isolation:
262+
263+
1. Duplicate the code from `redrawN` to make function `redrawN+1`, i.e. `redraw0` -> `redraw1`. Make a small change to the code in `redraw1` that you think will result in performance gain. Edit `main` to add a time trial for `redraw1`.
264+
265+
2. Make a rough prediction about the expected effect on runtime.
266+
267+
3. Build and run the new version to see whether the observed change in tick count matches your intuition. Where the results surprise you, try to figure out why the effect is different than expected. Poke around in the generated assembly or ask us questions.
268+
269+
Repeat this process, each time advancing from the best version so far and
270+
making another small change.
271+
272+
Below are some suggested avenues to explore:
273+
274+
+ `redraw0` is making a __lot__ of calls to `gl_draw_pixel`. Each is incurring the overhead of a function call and the (redundant) bounds checking within the call. The function `redraw1` bypasses `gl` instead getting the draw buffer from `fb` and writing the color components directly to the framebuffer memory. How much of an effect does this have?
275+
+ Is it faster to write the pixel BGRA components as 4 single bytes or a single write of 4 bytes?
276+
+ Does changing the order the pixels are accessed make a difference, i.e. instead of looping row by column, what if you loop column by row? What about looping over the pixels as a 1-d array instead of nested loop in 2-d?
277+
+ Hoisting a repeated operation in the loop body to outside the loop is always good idea. Those calls to `gl_get_width` and `gl_get_height` on each loop iteration? Yeet!
278+
+ Here's something that can be tried without changing the code at all: edit the Makefile to enable various levels of compiler optimization. Do a `make clean` to remove previous build products, then rebuild and re-run. What difference do you observe between `-O0`, `-Og`, `-O2` and `-Ofast`?
279+
+ Think about where the function spends time. Recall that every instruction
280+
contribute a cost: are there ways to change the function so that it does the
281+
same work with fewer instructions? Use `make speed.list` to get a dump of the assembly to see where the effort is going. It takes just a few instructions to write a pixel but each loop iteration adds the overhead cost to increment, compare, branch. How could you change the loop to issue fewer of these
282+
overhead instructions and focus more on doing the meaty work of writing pixels?
283+
284+
Share about the improvement you were able to achieve.[^5] Which changes surprised you by how effective they are? Which changes didn't seem to move the needle much at all? Are you able to make some sense of which changes are most effective and why? When you are writing `gl_clear` for your assignment 6, try to apply what you learned here to help it run briskly.
285+
286+
## Check in with TA
287+
288+
The key goals for this lab are to leave with a good understanding of the layout of the framebuffer memory and feel ready for starting on Assignment 6.[^6]
289+
290+
You will need access to an HDMI monitor for assignments 6 and 7. This can be any computer monitor, tv, or projector that accepts HDMI input. We have monitors available in lab for shared use. If you have access to an HDMI display elsewhere, feel free to give it a try. We have confirmed the reference library's support for HDMI output on a smattering of monitors. We expect it should work on others as well, but are a little wary about possibility of lurking bug or corner case that has yet to surface. Please let us know if you run into trouble and we can help diagnose.
291+
292+
Our HDMI monitors and cables are to stay in lab for all to use. __Please do not take our cables or monitors out of the lab room__.
293+
294+
295+
[^1]: Talk us through your plan for extending the `fb` module to support double-buffering.
296+
297+
[^2]: How does your checkerboard look? Show us your crisp looking squares!
298+
299+
[^3]: What are the differences between the following two lines of code?
300+
```c
301+
char *a = "Hello, world\n";
302+
char b[] = "Hello, world\n";
303+
```
304+
[^4]: Show the declaration for `img` needed for the `banner.c` program.
305+
306+
[^5]: How big of a speedup over the baseline were you able to achieve overall? What changes gave the biggest gains?
307+
308+
[^6]: Were you able to complete all of the lab exercises? Do you need followup assistance? How can we help?

labs/lab6/images/Apple2e.bmp

149 KB
Binary file not shown.

labs/lab6/images/apple2e-line.bmp

203 KB
Binary file not shown.

0 commit comments

Comments
 (0)