\documentclass[9pt]{article}
\input{template}

\begin{document}
\title{Floating Point and CPU vs GPU Rendering}
\author{Sam Moore, David Gow}
\maketitle

\section*{Abstract}

We qualitatively illustrate differences between floating point operations on the x86-64 CPU and several GPUs by rendering filled ellipses.

\section{Introduction}

The IEEE Standard for Floating-Point Arithmetic \cite{ieee2008-754} has been widely adopted by hardware manufacturers of CPUs and programming language standards.

Although it is well known that the behaviour of GPU drivers is inconsistent, there is little formal academic research into the behaviour of floating point operations using such drivers. 

In 2004 Hillesland and Lastra adapted Kahan's well known program for testing floating point arithmetic on CPUs during the 1980s ``Paranoia'' for GPUs and found that many GPUs did not appear to be compliant with IEEE-754\cite{hillesland2004paranoia}.

Given the recent interest in use of the GPU for vector graphics\cite{kilgard2012gpu} the behaviour of GPUs when performing floating point operations is worthy of closer investigation.

Using a straight forward filled ellipse rendering algorithm implemented in C/C++ and GLSL we show inconsistent floating point behaviour when comparing the x86-64 CPU, an nVidia GPU\footnote{??? using the nVidia driver}, an intel GPU\footnote{??? using the intel driver} and an AMD/ATI GPU\footnote{Whistler LE (Radeon HD 6610M/7610M) using the fglrx driver}.

\section{Algorithm}

For each pixel position $(x,y)$ normalised relative to the bounding rectangle, if $x^2 + y^2 \leq 1$ then $(x,y)$ should be filled.

Although $x$ and $y$ may be treated as integers on the CPU, since the OpenGL API requires floating point vertex coordinates, our CPU implementation also normalises the coordinates relative to the bounding rectangle; this way we can compare the performance of floating point operations on the CPU and GPU(s).

\pagebreak
\subsection{GLSL Fragment Shader}
\begin{minted}{glsl}
#version 140
// Fragment shader (others omitted)

in vec2 objcoords; // Coordinates x, y, relative to bounding rectangle (from other shaders)
out vec4 output_colour;

uniform vec4 colour;

void main()
{
	if ((objcoords.x)*(objcoords.x) + (objcoords.y)*(objcoords.y) > 1.0)
	{
		discard;
	}
	output_colour = colour;
}
\end{minted}

\subsection{CPU Rendering Algorithm (simplified)}
\begin{minted}{c++}
// where bounds = {x,y,w,h} gives the bounding rectangle in integer pixel positions
// and centre = {x,y} is the centre of the circle
// and pixels[][] is the display buffer
for (int x = bounds.x; x < bounds.x+bounds.w; ++x)
{
	for (int y = bounds.y; y < bounds.y+bounds.h; ++y)
	{
		float dx = 2.0*(float)(x - centre.x)/(float)(bounds.w);
		float dy = 2.0*(float)(y - centre.y)/(float)(bounds.h);
		if (dx*dx + dy*dy <= 1.0)
		{
			pixels[x][y] = true;
		}
	}
}
\end{minted}

Note: The \verb/pixels/ buffer is uploaded directly to the GPU after CPU rendering is completed.

\section{Results}

Figure \ref{comparison.pdf} shows the edge of a unit radius circle viewed under a magnification of approximately $5\times10^6$ as rendered using the CPU.



\section{Conclusion}

nVidia looks qualitatively similar to the CPU rendering. Frankly I was just happy fglrx didn't segfault. Wierd shit happens with intel. If anyone isn't obeying IEEE-754 here, it is probably intel.

\bibliographystyle{unsrt}
\bibliography{papers}

\pagebreak

\begin{figure}[H]
\centering
\includegraphics[width=0.8\textwidth]{figures/circles_gpu_vs_cpu/comparison.pdf}
\caption{The edges of a unit circle viewed through bounds (x,y,w,h) =  (0.0869386,0.634194,2.63295e-07,2.63295e-07)}\label{comparison.pdf}
\end{figure}





\end{document}
