[Mesa-dev] [PATCH] swrast: initial multi-threaded span rendering
Keith Whitwell
keithw at vmware.com
Wed Aug 10 02:16:30 PDT 2011
I'm not sure it makes a lot of sense to be optimizing swrast at this
stage. Take a look at llvmpipe and perhaps consider improving the
multithreading already in place in that rasterizer, which is far better
optimized than swrast already.
Keith
On Wed, 2011-08-10 at 08:07 +0000, Andreas Fänger wrote:
> Optional parallel rendering of spans using OpenMP.
> Initial implementation for aa triangles. A new option for scons is
> also provided to activate the openmp support (off by default).
> ---
> common.py | 1 +
> scons/gallium.py | 12 +++++++
> src/mesa/swrast/s_aatritemp.h | 68 ++++++++++++++++++++++-----------------
> src/mesa/swrast/s_context.c | 26 ++++++++++++---
> src/mesa/swrast/s_texcombine.c | 4 ++
> src/mesa/tnl/t_pipeline.c | 12 +++++++
> 6 files changed, 87 insertions(+), 36 deletions(-)
>
> diff --git a/common.py b/common.py
> index 8657030..cfee1b5 100644
> --- a/common.py
> +++ b/common.py
> @@ -88,6 +88,7 @@ def AddOptions(opts):
> opts.Add('toolchain', 'compiler toolchain', default_toolchain)
> opts.Add(BoolOption('gles', 'EXPERIMENTAL: enable OpenGL ES support', 'no'))
> opts.Add(BoolOption('llvm', 'use LLVM', default_llvm))
> + opts.Add(BoolOption('openmp', 'EXPERIMENTAL: compile with openmp (swrast)', 'no'))
> opts.Add(BoolOption('debug', 'DEPRECATED: debug build', 'yes'))
> opts.Add(BoolOption('profile', 'DEPRECATED: profile build', 'no'))
> opts.Add(BoolOption('quiet', 'DEPRECATED: profile build', 'yes'))
> diff --git a/scons/gallium.py b/scons/gallium.py
> index 8cd3bc7..7135251 100755
> --- a/scons/gallium.py
> +++ b/scons/gallium.py
> @@ -596,6 +596,18 @@ def generate(env):
> libs += ['m', 'pthread', 'dl']
> env.Append(LIBS = libs)
>
> + # OpenMP
> + if env['openmp']:
> + if env['msvc']:
> + env.Append(CCFLAGS = ['/openmp'])
> + # When building openmp release VS2008 link.exe crashes with LNK1103 error.
> + # Workaround: overwrite PDB flags with empty value as it isn't required anyways
> + if env['build'] == 'release':
> + env['PDB'] = ''
> + if env['gcc']:
> + env.Append(CCFLAGS = ['-fopenmp'])
> + env.Append(LIBS = ['gomp'])
> +
> # Load tools
> env.Tool('lex')
> env.Tool('yacc')
> diff --git a/src/mesa/swrast/s_aatritemp.h b/src/mesa/swrast/s_aatritemp.h
> index 91d4f7a..005d12c 100644
> --- a/src/mesa/swrast/s_aatritemp.h
> +++ b/src/mesa/swrast/s_aatritemp.h
> @@ -181,13 +181,18 @@
> const GLfloat *pMax = vMax->attrib[FRAG_ATTRIB_WPOS];
> const GLfloat dxdy = majDx / majDy;
> const GLfloat xAdj = dxdy < 0.0F ? -dxdy : 0.0F;
> - GLfloat x = pMin[0] - (yMin - iyMin) * dxdy;
> GLint iy;
> - for (iy = iyMin; iy < iyMax; iy++, x += dxdy) {
> + #pragma omp parallel for schedule(dynamic) private(iy) firstprivate(span)
> + for (iy = iyMin; iy < iyMax; iy++) {
> + GLfloat x = pMin[0] - (yMin - iy) * dxdy;
> GLint ix, startX = (GLint) (x - xAdj);
> GLuint count;
> GLfloat coverage = 0.0F;
>
> +#ifdef _OPENMP
> + /* each thread needs to use a different (global) SpanArrays variable */
> + span.array = SWRAST_CONTEXT(ctx)->SpanArrays + omp_get_thread_num();
> +#endif
> /* skip over fragments with zero coverage */
> while (startX < MAX_WIDTH) {
> coverage = compute_coveragef(pMin, pMid, pMax, startX, iy);
> @@ -228,13 +233,12 @@
> coverage = compute_coveragef(pMin, pMid, pMax, ix, iy);
> }
>
> - if (ix <= startX)
> - continue;
> -
> - span.x = startX;
> - span.y = iy;
> - span.end = (GLuint) ix - (GLuint) startX;
> - _swrast_write_rgba_span(ctx, &span);
> + if (ix > startX) {
> + span.x = startX;
> + span.y = iy;
> + span.end = (GLuint) ix - (GLuint) startX;
> + _swrast_write_rgba_span(ctx, &span);
> + }
> }
> }
> else {
> @@ -244,13 +248,18 @@
> const GLfloat *pMax = vMax->attrib[FRAG_ATTRIB_WPOS];
> const GLfloat dxdy = majDx / majDy;
> const GLfloat xAdj = dxdy > 0 ? dxdy : 0.0F;
> - GLfloat x = pMin[0] - (yMin - iyMin) * dxdy;
> GLint iy;
> - for (iy = iyMin; iy < iyMax; iy++, x += dxdy) {
> + #pragma omp parallel for schedule(dynamic) private(iy) firstprivate(span)
> + for (iy = iyMin; iy < iyMax; iy++) {
> + GLfloat x = pMin[0] - (yMin - iy) * dxdy;
> GLint ix, left, startX = (GLint) (x + xAdj);
> GLuint count, n;
> GLfloat coverage = 0.0F;
>
> +#ifdef _OPENMP
> + /* each thread needs to use a different (global) SpanArrays variable */
> + span.array = SWRAST_CONTEXT(ctx)->SpanArrays + omp_get_thread_num();
> +#endif
> /* make sure we're not past the window edge */
> if (startX >= ctx->DrawBuffer->_Xmax) {
> startX = ctx->DrawBuffer->_Xmax - 1;
> @@ -296,31 +305,30 @@
> ATTRIB_LOOP_END
> #endif
>
> - if (startX <= ix)
> - continue;
> + if (startX > ix) {
> + n = (GLuint) startX - (GLuint) ix;
>
> - n = (GLuint) startX - (GLuint) ix;
> + left = ix + 1;
>
> - left = ix + 1;
> -
> - /* shift all values to the left */
> - /* XXX this is temporary */
> - {
> - SWspanarrays *array = span.array;
> - GLint j;
> - for (j = 0; j < (GLint) n; j++) {
> - array->coverage[j] = array->coverage[j + left];
> - COPY_CHAN4(array->rgba[j], array->rgba[j + left]);
> + /* shift all values to the left */
> + /* XXX this is temporary */
> + {
> + SWspanarrays *array = span.array;
> + GLint j;
> + for (j = 0; j < (GLint) n; j++) {
> + array->coverage[j] = array->coverage[j + left];
> + COPY_CHAN4(array->rgba[j], array->rgba[j + left]);
> #ifdef DO_Z
> - array->z[j] = array->z[j + left];
> + array->z[j] = array->z[j + left];
> #endif
> + }
> }
> - }
>
> - span.x = left;
> - span.y = iy;
> - span.end = n;
> - _swrast_write_rgba_span(ctx, &span);
> + span.x = left;
> + span.y = iy;
> + span.end = n;
> + _swrast_write_rgba_span(ctx, &span);
> + }
> }
> }
> }
> diff --git a/src/mesa/swrast/s_context.c b/src/mesa/swrast/s_context.c
> index def1531..4434f11 100644
> --- a/src/mesa/swrast/s_context.c
> +++ b/src/mesa/swrast/s_context.c
> @@ -772,6 +772,11 @@ _swrast_CreateContext( struct gl_context *ctx )
> {
> GLuint i;
> SWcontext *swrast = (SWcontext *)CALLOC(sizeof(SWcontext));
> +#ifdef _OPENMP
> + const GLint maxThreads = omp_get_max_threads();
> +#else
> + const GLint maxThreads = 1;
> +#endif
>
> if (SWRAST_DEBUG) {
> _mesa_debug(ctx, "_swrast_CreateContext\n");
> @@ -806,19 +811,25 @@ _swrast_CreateContext( struct gl_context *ctx )
> for (i = 0; i < MAX_TEXTURE_IMAGE_UNITS; i++)
> swrast->TextureSample[i] = NULL;
>
> - swrast->SpanArrays = MALLOC_STRUCT(sw_span_arrays);
> + /* SpanArrays is global and shared by all SWspan instances. However, when
> + * using multiple threads, it is necessary to have one SpanArrays instance
> + * per thread.
> + */
> + swrast->SpanArrays = (SWspanarrays *) MALLOC(maxThreads * sizeof(SWspanarrays));
> if (!swrast->SpanArrays) {
> FREE(swrast);
> return GL_FALSE;
> }
> - swrast->SpanArrays->ChanType = CHAN_TYPE;
> + for(i = 0; i < maxThreads; i++) {
> + swrast->SpanArrays[i].ChanType = CHAN_TYPE;
> #if CHAN_TYPE == GL_UNSIGNED_BYTE
> - swrast->SpanArrays->rgba = swrast->SpanArrays->rgba8;
> + swrast->SpanArrays[i].rgba = swrast->SpanArrays[i].rgba8;
> #elif CHAN_TYPE == GL_UNSIGNED_SHORT
> - swrast->SpanArrays->rgba = swrast->SpanArrays->rgba16;
> + swrast->SpanArrays[i].rgba = swrast->SpanArrays[i].rgba16;
> #else
> - swrast->SpanArrays->rgba = swrast->SpanArrays->attribs[FRAG_ATTRIB_COL0];
> + swrast->SpanArrays[i].rgba = swrast->SpanArrays[i].attribs[FRAG_ATTRIB_COL0];
> #endif
> + }
>
> /* init point span buffer */
> swrast->PointSpan.primitive = GL_POINT;
> @@ -826,7 +837,10 @@ _swrast_CreateContext( struct gl_context *ctx )
> swrast->PointSpan.facing = 0;
> swrast->PointSpan.array = swrast->SpanArrays;
>
> - swrast->TexelBuffer = (GLfloat *) MALLOC(ctx->Const.MaxTextureImageUnits *
> + /* TexelBuffer is also global and normally shared by all SWspan instances;
> + * when running with multiple threads, create one per thread.
> + */
> + swrast->TexelBuffer = (GLfloat *) MALLOC(ctx->Const.MaxTextureImageUnits * maxThreads *
> MAX_WIDTH * 4 * sizeof(GLfloat));
> if (!swrast->TexelBuffer) {
> FREE(swrast->SpanArrays);
> diff --git a/src/mesa/swrast/s_texcombine.c b/src/mesa/swrast/s_texcombine.c
> index 086ed0b..80b9dff 100644
> --- a/src/mesa/swrast/s_texcombine.c
> +++ b/src/mesa/swrast/s_texcombine.c
> @@ -48,7 +48,11 @@ typedef float (*float4_array)[4];
> static INLINE float4_array
> get_texel_array(SWcontext *swrast, GLuint unit)
> {
> +#ifdef _OPENMP
> + return (float4_array) (swrast->TexelBuffer + unit * MAX_WIDTH * 4 * omp_get_num_threads() + (MAX_WIDTH * 4 * omp_get_thread_num()));
> +#else
> return (float4_array) (swrast->TexelBuffer + unit * MAX_WIDTH * 4);
> +#endif
> }
>
>
> diff --git a/src/mesa/tnl/t_pipeline.c b/src/mesa/tnl/t_pipeline.c
> index 18f095f..881d5d5 100644
> --- a/src/mesa/tnl/t_pipeline.c
> +++ b/src/mesa/tnl/t_pipeline.c
> @@ -146,7 +146,17 @@ void _tnl_run_pipeline( struct gl_context *ctx )
> _tnl_notify_pipeline_output_change( ctx );
> }
>
> +#ifndef _OPENMP
> + /* Don't adjust FPU precision mode in case multiple threads are to be used.
> + * This would require that the additional threads also changed the FPU mode
> + * which is quite a mess as this had to be done in all parallelized sections;
> + * otherwise the master thread and all other threads are running in different
> + * modes, producing inconsistent results.
> + * Note that all x64 implementations don't define/use START_FAST_MATH, so
> + * this is "hack" is only used in i386 mode
> + */
> START_FAST_MATH(__tmp);
> +#endif
>
> for (i = 0; i < tnl->pipeline.nr_stages ; i++) {
> struct tnl_pipeline_stage *s = &tnl->pipeline.stages[i];
> @@ -154,7 +164,9 @@ void _tnl_run_pipeline( struct gl_context *ctx )
> break;
> }
>
> +#ifndef _OPENMP
> END_FAST_MATH(__tmp);
> +#endif
> }
>
>
More information about the mesa-dev
mailing list