You are on page 1of 4

How interfacing with graphics card work with C or C++?

Asked 12 years, 5 months ago Modified 10 years, 2 months ago Viewed 15k times

Libraries such as OpenGL access the graphics card and can produce graphics programs, how does
these libraries access the graphics card since they are implemented using C. According to what i've
24 heard, C and C++ do not provide graphics features built in the language and producing graphics
requires libraries. How then are these libraries written in C? The same question applies for sound also?

Are additional features to C/C++ languages such as graphics, sound, internet access written in lower
level languages and then provided to the C/C++ using libraries?

I would be thankful for any summary which correct my concepts, or any suggested readings on the web
or books.

opengl graphics

Share Improve this question Follow edited Nov 1, 2011 at 13:50 asked Nov 1, 2011 at 13:20
Christian Rau Amir Nasr
45.7k 11 110 188 241 1 2 3

2 Answers Sorted by: Highest score (default)

OpenGL is not really a library. It's a specification. The opengl32.dll or libGL.so you have on your system
are merely very thin layers, that communicate with the GPU driver.
25
According to what i've heard, c and c++ do not provide graphics features built in the language
and producing graphics requires libraries. How then are these libraries written in c?

The operating system offers functions to talk to the hardware. A driver uses those facilities to drive
(hence the name) the hardware. For example writing a sequence A, B, C, D may make some particular
GPU draw a triangle to the framebuffer.

I explained those things already in On Windows, how does OpenGL differ from DirectX? and here How
does OpenGL work at the lowest level? (I'm including a verbatim quote here):

This question is almost impossible to answer because OpenGL by itself is just a front end API,
and as long as an implementations adheres to the specification and the outcome conforms to
this it can be done any way you like.

The question may have been: How does an OpenGL driver work on the lowest level. Now this is
again impossible to answer in general, as a driver is closely tied to some piece of hardware,
which may again do things however the developer designed it.

So the question should have been: "How does it look on average behind the scenes of OpenGL
and the graphics system?". Let's look at this from the bottom up:
1. At the lowest level there's some graphics device. Nowadays these are GPUs which provide
a set of registers controlling their operation (which registers exactly is device dependent)
have some program memory for shaders, bulk memory for input data (vertices, textures,
etc.) and an I/O channel to the rest of the system over which it recieves/sends data and
command streams.

2. The graphics driver keeps track of the GPUs state and all the resources application
programs that make use of the GPU. Also it is responsible for conversion or any other
processing the data sent by applications (convert textures into the pixelformat supported by
the GPU, compile shaders in the machine code of the GPU). Furthermore it provides some
abstract, driver dependent interface to application programs.

3. Then there's the driver dependent OpenGL client library/driver. On Windows this gets
loaded by proxy through opengl32.dll, on Unix systems this resides in two places: * X11
GLX module and driver dependent GLX driver * and /usr/lib/libGL.so may contain some
driver dependent stuff for direct rendering

On MacOS X this happens to be the "OpenGL Framework".

It is this part that translates OpenGL calls how you do it into calls to the driver specific
functions in the part of the driver described in (2).

4. Finally the actual OpenGL API library, opengl32.dll in Windows, and on Unix
/usr/lib/libGL.so; this mostly just passes down the commands to the OpenGL
implementation proper.

How the actual communication happens can not be generalized:

In Unix the 3<->4 connection may happen either over Sockets (yes, it may, and does go over
network if you want to) or through Shared Memory. In Windows the interface library and the
driver client are both loaded into the process address space, so that's no so much
communication but simple function calls and variable/pointer passing. In MacOS X this is similar
to Windows, only that there's no separation between OpenGL interface and driver client (that's
the reason why MacOS X is so slow to keep up with new OpenGL versions, it always requires a
full operating system upgrade to deliver the new framework).

Communication betwen 3<->2 may go through ioctl, read/write, or through mapping some
memory into process address space and configuring the MMU to trigger some driver code
whenever changes to that memory are done. This is quite similar on any operating system since
you always have to cross the kernel/userland boundary: Ultimately you go through some
syscall.

Communication between system and GPU happen through the periphial bus and the access
methods it defines, so PCI, AGP, PCI-E, etc, which work through Port-I/O, Memory Mapped I/O,
DMA, IRQs.

Update

To answer, how one interfaces the actual hardware from a C program say a OS kernel and/or driver
written in C:

The C standard itself treats addresses as something purely abstract. You can cast a pointer to uintptr_t,
but the numerical value you get is only required to adhere to pointer arithmetic if cast back to the pointer.
Otherwise the value may be unrelated to address space. The only safe way to implement hardware
access to C is by writing the lowest level stuff in assembly, following the ABI of the used C
implementation and system.

That's how all proper OS do it. Never addresses are cast into pointers in C! The operating system has
implemented this in assembler, matched to the C compiler of the system. The code written in assembly
is exported as functions callable in C. The kernel provides those functions then to the GPU driver. So the
chain is:

Application Program → [OpenGL API layer → Vendor OpenGL implementation] → GPU driver → OS
kernel → Hardware.

Share Improve this answer Follow edited May 23, 2017 at 12:24 answered Nov 1, 2011 at 13:30
Community Bot datenwolf
1 1 161k 13 188 303

If I am not mistaken, you question boils down to how does one use C to access hardware. The answer,
in a very general and hand wavy fashion, is that peripheral hardware is often mapped into the address
3 space and accessed as if it is normal memory with the caveat that it can change unexpectedly.

C does a great job of accessing memory so it is totally suitable for reading and writing directly with
hardware. The volatile keyword is there to explicitly stop the compiler from taking short cuts and force
querying the addresses in question. This is because memory mapped IO addresses can change
unexpectedly and do not behave like normal RAM.

On a simple system this low level memory access to set addresses will be abstracted into a more easily
usable library. On modern operating systems, the kernel must mediate access to shared resources like a
graphics card. This means that the kernel will implement the memory mapped IO but also put in place a
series of system calls (often using Swiss Army Knife constructs like ioctl ) so that a user process can
gain access to the peripheral. This in turn will be wrapped in a nice user-friendly library like OpenGL.

Share Improve this answer Follow edited Feb 9, 2014 at 4:04 answered Nov 1, 2011 at 13:38
thesquaregroot doron
1,414 1 22 38 28.4k 12 68 106

The C standard itself treats addresses as something purely abstract. You can cast a pointer to uintptr_t, but the
numerical value you get is only required to adhere to pointer arithmetic if cast back to the pointer. Otherwise the
value may be unrelated to address space. The only safe way to implement hardware access to C is by writing the
lowest level stuff in assembly, following the ABI of the used C implementation and system. That's how all proper OS
do it. Never addresses are cast into pointers in C! – datenwolf Nov 1, 2011 at 13:46

@datenwolf: Well, yes, casting ints to pointers is undefined behaviour in C; nonetheless many compilers will to
something "useful" in that case, and programmers will use that fact. So you can (and some people do) "cast
addresses into pointers" - it's just compiler- and OS-dependent how/if this works (just like using assembler).
– sleske Nov 1, 2011 at 14:05

@sleske: Casting ints to pointers is only undefined behaviour if the int is not the result of a previous pointer to int
cast. Anyway, the numerical value of pointers cast to int is not defined being straigtforward. There are for example
architectures, that use a special bit patters for the NIL pointer. The C standard however requires this machine bit
pattern having the numerical value 0 in a C programm (the #define NULL 0 macro is just for lovers of writing
NULL, the numerical value always is 0). And I know of no proper OS where any implementation specifics are
exploited. They all use asm. – datenwolf Nov 1, 2011 at 14:13

A note on the volatile keyword. While pointer values are not required to map to addresses, it is perfectly
possible to return a pointer some Memory Mapped IO (MMIO) address space through C implementation matching
assembly code. Accessing the memory by this C pointer will of course do MMIO, but only if the compiler doesn't
optimizes it away. That's where volatile enters the game. The OS gives you some memory, through a function
called by C, but ultimately ending up in code written in assembly. By this marshalling C code can do MMIO.
– datenwolf Nov 1, 2011 at 14:17

You might also like