OS Graphics
Posted: Mon Jul 29, 2013 12:01 pm
Hi,
It seems people had difficulty understanding what I was trying to describe in another topic; so I thought I'd put together a much easier to understand example.
Imagine you have 2 applications ("foo" and "bar"). The first application creates a window with some text and 2 fancy icons/textures; and looks like this: The second application creates a window with some text and 2 fancy icons/textures; and it looks like this: Both applications send their graphics to the GUI. The GUI adds window decorations to both application's windows; then puts them on top of the desktop/background. The final result (what the user sees) looks like this: The GUI sends its graphics to a "virtual screen" layer. The virtual screen happens to be 2 monitors connected to 2 different video cards; both running at different resolutions and different colour depths. To make it more interesting, the monitor on the right is on its side.
The virtual screen layer sends this to the first video card (which displays it on the left monitor): The virtual screen layer also sends this to the second video card (which displays it on the right monitor): Now, some people (and most existing graphics systems) work on "raw pixels". My proposal is that it's better to work on "lists of commands". Let's see how both approaches work for this example.
Raw Pixels
In this case; the first application draws texture 1 (64 * 32 = 2048 pixels) and texture 2 (40*40 = 1600 pixels). The text is sent to a font engine which converts it into a texture (40 * 16 = 640 pixels). Then the application creates a buffer for its window (white background) and adds all the textures to it, creating another texture (200*100 = 20000 pixels). In total, the first application has drawn 2048+1600+640+20000 = 24288 pixels.
The second application draws texture 3 (120 * 60 = 7200 pixels), texture 4 (50*30 = 1500 pixels) and gets the font engine to create the text for its text (40 * 16 = 640 pixels); then creates a texture for its window's content (200*100 = 20000 pixels). In total, the second application has drawn 7200+1500+640+20000 = 29340 pixels.
The GUI gets the first application's window and has to add window borders; so it creates a larger texture for the first application's "decorated window" (205*105 = 21525 pixels), and the same for the second applications window (another 205*105 = 21525 pixels). It also draws its background/desktop, which is another texture (300*150 = 45000 pixels). Finally the GUI combines all of these into it's output (300*150 = 45000 pixels). In total, the GUI has drawn 21525 + 21525 + 45000 + 45000 = 133050 pixels.
The virtual screen layer gets the GUI's output. It creates one texture for the first video card (150*150 = 22500 pixels) and another texture for the second video card (150*150 = 22500 pixels) and sends the textures to the video cards. In total the virtual screen layer has drawn 22500+22500 = 45000 pixels.
For the complete thing, the total number of pixels drawn by both applications, the GUI and the virtual screen layer is 24288+29340+133050+45000 = 231678 pixels.
Of course all of this work wouldn't need to be done every time anything changes. For example; if the first application changes texture 1, then the first application would need to redraw texture 1 (2048 pixels) and redraw it's window (20000 pixels), the second application wouldn't redraw anything, the GUI would need to redraw the first application's "decorated window" (21525 pixels) and it's own buffer (45000 pixels), and the virtual screen layer would draw the textures for both video cards (45000 pixels). That all adds up to a total of 2048+20000+21525+45000+45000 = 133573 pixels drawn because texture 1 changed.
List of Commands
For this case; the first application has 3 "list of commands" (to describe the first texture, the second texture and its text). Then it creates a fourth "main list of commands" describing its window. It draws nothing and just sends these lists of commands to the GUI.
The second application does the same thing; sending its 4 lists of commands to the GUI.
The GUI gets the first application's "main list of commands" and modifies it to include commands for the window decoration. It does the same for the the second application's "main list of commands". The GUI also has it's own list of commands for its background, plus its "main list of commands". The GUI sends a total of 3+1+3+1+2 = 10 lists of commands to the virtual screen layer; but draws nothing.
The virtual screen layer looks at all these lists of commands and works out what needs to go to each video card. It can easily (and quickly) determine that the first video card needs 8 of them (GUI's background, GUI's main list, app1's main list, app1's text, texture 1, app2's main list, app2's text and texture 3) and determine that the second video card needs 6 of them (GUI's background, GUI's main list, app1's main list, texture 2, app2's main list and texture 4). The virtual screen layer sends the lists of commands to the video drivers (and draws nothing).
The driver for the first video card has to use the lists of commands it received to draw everything for the first monitor (150*150 = 22500 pixels). While doing this drawing it decides to create "cached textures" for texture 3 (7200 pixels) and app2's text (640 pixels). It knows that texture 1 and app1's text isn't visible and doesn't bother creating "cached textures" for them (at this time). For the remaining lists of commands it decides they're so simple that it can draw them directly without using any cached textures. That adds up to 7200+640 = 7840 pixels for cached textures plus 22500 pixels to send to the monitor; or a total of 30340 pixels drawn by the first video card's driver.
The driver for the second video card does something similar; creating 22500 pixels to send to the second monitor; and "cached textures" for both texture 2 (1600 pixels) and texture 4 (1500 pixels). It ends up drawing a total of 1600+1500+22500 = 25600 pixels.
For the complete thing, only the video drivers do any drawing; and the total number of pixels drawn is 22500+25600 = 48100 pixels. This is 20% less than the "raw pixel" approach which drew 231678 pixels, so this is approximately 5 times faster. However, the lists of commands had to be created, decoded and manipulated, and this adds a negligible amount of extra overhead, so it might only be 4.95 times faster.
Of course all of this work wouldn't need to be done every time anything changes. For example; if the first application changes texture 1, then the list of commands for texture 1 (and nothing else) would have to be sent from app1 to GUI to virtual screen layer to the first video card's driver; but the first video card's driver knows that texture 1 isn't visible anyway and draws nothing. For this case, drawing nothing is a lot less than the "raw pixel" approach which drew 231678 pixels; and (accounting for list creating, decoding, etc) this might be a million times faster.
Cheers,
Brendan
It seems people had difficulty understanding what I was trying to describe in another topic; so I thought I'd put together a much easier to understand example.
Imagine you have 2 applications ("foo" and "bar"). The first application creates a window with some text and 2 fancy icons/textures; and looks like this: The second application creates a window with some text and 2 fancy icons/textures; and it looks like this: Both applications send their graphics to the GUI. The GUI adds window decorations to both application's windows; then puts them on top of the desktop/background. The final result (what the user sees) looks like this: The GUI sends its graphics to a "virtual screen" layer. The virtual screen happens to be 2 monitors connected to 2 different video cards; both running at different resolutions and different colour depths. To make it more interesting, the monitor on the right is on its side.
The virtual screen layer sends this to the first video card (which displays it on the left monitor): The virtual screen layer also sends this to the second video card (which displays it on the right monitor): Now, some people (and most existing graphics systems) work on "raw pixels". My proposal is that it's better to work on "lists of commands". Let's see how both approaches work for this example.
Raw Pixels
In this case; the first application draws texture 1 (64 * 32 = 2048 pixels) and texture 2 (40*40 = 1600 pixels). The text is sent to a font engine which converts it into a texture (40 * 16 = 640 pixels). Then the application creates a buffer for its window (white background) and adds all the textures to it, creating another texture (200*100 = 20000 pixels). In total, the first application has drawn 2048+1600+640+20000 = 24288 pixels.
The second application draws texture 3 (120 * 60 = 7200 pixels), texture 4 (50*30 = 1500 pixels) and gets the font engine to create the text for its text (40 * 16 = 640 pixels); then creates a texture for its window's content (200*100 = 20000 pixels). In total, the second application has drawn 7200+1500+640+20000 = 29340 pixels.
The GUI gets the first application's window and has to add window borders; so it creates a larger texture for the first application's "decorated window" (205*105 = 21525 pixels), and the same for the second applications window (another 205*105 = 21525 pixels). It also draws its background/desktop, which is another texture (300*150 = 45000 pixels). Finally the GUI combines all of these into it's output (300*150 = 45000 pixels). In total, the GUI has drawn 21525 + 21525 + 45000 + 45000 = 133050 pixels.
The virtual screen layer gets the GUI's output. It creates one texture for the first video card (150*150 = 22500 pixels) and another texture for the second video card (150*150 = 22500 pixels) and sends the textures to the video cards. In total the virtual screen layer has drawn 22500+22500 = 45000 pixels.
For the complete thing, the total number of pixels drawn by both applications, the GUI and the virtual screen layer is 24288+29340+133050+45000 = 231678 pixels.
Of course all of this work wouldn't need to be done every time anything changes. For example; if the first application changes texture 1, then the first application would need to redraw texture 1 (2048 pixels) and redraw it's window (20000 pixels), the second application wouldn't redraw anything, the GUI would need to redraw the first application's "decorated window" (21525 pixels) and it's own buffer (45000 pixels), and the virtual screen layer would draw the textures for both video cards (45000 pixels). That all adds up to a total of 2048+20000+21525+45000+45000 = 133573 pixels drawn because texture 1 changed.
List of Commands
For this case; the first application has 3 "list of commands" (to describe the first texture, the second texture and its text). Then it creates a fourth "main list of commands" describing its window. It draws nothing and just sends these lists of commands to the GUI.
The second application does the same thing; sending its 4 lists of commands to the GUI.
The GUI gets the first application's "main list of commands" and modifies it to include commands for the window decoration. It does the same for the the second application's "main list of commands". The GUI also has it's own list of commands for its background, plus its "main list of commands". The GUI sends a total of 3+1+3+1+2 = 10 lists of commands to the virtual screen layer; but draws nothing.
The virtual screen layer looks at all these lists of commands and works out what needs to go to each video card. It can easily (and quickly) determine that the first video card needs 8 of them (GUI's background, GUI's main list, app1's main list, app1's text, texture 1, app2's main list, app2's text and texture 3) and determine that the second video card needs 6 of them (GUI's background, GUI's main list, app1's main list, texture 2, app2's main list and texture 4). The virtual screen layer sends the lists of commands to the video drivers (and draws nothing).
The driver for the first video card has to use the lists of commands it received to draw everything for the first monitor (150*150 = 22500 pixels). While doing this drawing it decides to create "cached textures" for texture 3 (7200 pixels) and app2's text (640 pixels). It knows that texture 1 and app1's text isn't visible and doesn't bother creating "cached textures" for them (at this time). For the remaining lists of commands it decides they're so simple that it can draw them directly without using any cached textures. That adds up to 7200+640 = 7840 pixels for cached textures plus 22500 pixels to send to the monitor; or a total of 30340 pixels drawn by the first video card's driver.
The driver for the second video card does something similar; creating 22500 pixels to send to the second monitor; and "cached textures" for both texture 2 (1600 pixels) and texture 4 (1500 pixels). It ends up drawing a total of 1600+1500+22500 = 25600 pixels.
For the complete thing, only the video drivers do any drawing; and the total number of pixels drawn is 22500+25600 = 48100 pixels. This is 20% less than the "raw pixel" approach which drew 231678 pixels, so this is approximately 5 times faster. However, the lists of commands had to be created, decoded and manipulated, and this adds a negligible amount of extra overhead, so it might only be 4.95 times faster.
Of course all of this work wouldn't need to be done every time anything changes. For example; if the first application changes texture 1, then the list of commands for texture 1 (and nothing else) would have to be sent from app1 to GUI to virtual screen layer to the first video card's driver; but the first video card's driver knows that texture 1 isn't visible anyway and draws nothing. For this case, drawing nothing is a lot less than the "raw pixel" approach which drew 231678 pixels; and (accounting for list creating, decoding, etc) this might be a million times faster.
Cheers,
Brendan