| /************************************************************************** |
| * |
| * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas. |
| * All Rights Reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sub license, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the |
| * next paragraph) shall be included in all copies or substantial portions |
| * of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. |
| * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR |
| * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| * |
| **************************************************************************/ |
| |
| /* |
| * Authors: |
| * Keith Whitwell <keith@tungstengraphics.com> |
| */ |
| |
| #include "util/u_memory.h" |
| #include "util/u_prim.h" |
| #include "draw/draw_context.h" |
| #include "draw/draw_private.h" |
| #include "draw/draw_pt.h" |
| |
| |
| #define CACHE_MAX 256 |
| #define FETCH_MAX 256 |
| #define DRAW_MAX (16*1024) |
| |
| struct vcache_frontend { |
| struct draw_pt_front_end base; |
| struct draw_context *draw; |
| |
| unsigned in[CACHE_MAX]; |
| ushort out[CACHE_MAX]; |
| |
| ushort draw_elts[DRAW_MAX]; |
| unsigned fetch_elts[FETCH_MAX]; |
| |
| unsigned draw_count; |
| unsigned fetch_count; |
| unsigned fetch_max; |
| |
| struct draw_pt_middle_end *middle; |
| |
| unsigned input_prim; |
| unsigned output_prim; |
| |
| unsigned middle_prim; |
| unsigned opt; |
| }; |
| |
| static INLINE void |
| vcache_flush( struct vcache_frontend *vcache ) |
| { |
| if (vcache->middle_prim != vcache->output_prim) { |
| vcache->middle_prim = vcache->output_prim; |
| vcache->middle->prepare( vcache->middle, |
| vcache->middle_prim, |
| vcache->opt, |
| &vcache->fetch_max ); |
| } |
| |
| if (vcache->draw_count) { |
| vcache->middle->run( vcache->middle, |
| vcache->fetch_elts, |
| vcache->fetch_count, |
| vcache->draw_elts, |
| vcache->draw_count ); |
| } |
| |
| memset(vcache->in, ~0, sizeof(vcache->in)); |
| vcache->fetch_count = 0; |
| vcache->draw_count = 0; |
| } |
| |
| static INLINE void |
| vcache_check_flush( struct vcache_frontend *vcache ) |
| { |
| if ( vcache->draw_count + 6 >= DRAW_MAX || |
| vcache->fetch_count + 4 >= FETCH_MAX ) |
| { |
| vcache_flush( vcache ); |
| } |
| } |
| |
| |
| static INLINE void |
| vcache_elt( struct vcache_frontend *vcache, |
| unsigned felt, |
| ushort flags ) |
| { |
| unsigned idx = felt % CACHE_MAX; |
| |
| if (vcache->in[idx] != felt) { |
| assert(vcache->fetch_count < FETCH_MAX); |
| |
| vcache->in[idx] = felt; |
| vcache->out[idx] = (ushort)vcache->fetch_count; |
| vcache->fetch_elts[vcache->fetch_count++] = felt; |
| } |
| |
| vcache->draw_elts[vcache->draw_count++] = vcache->out[idx] | flags; |
| } |
| |
| |
| |
| static INLINE void |
| vcache_triangle( struct vcache_frontend *vcache, |
| unsigned i0, |
| unsigned i1, |
| unsigned i2 ) |
| { |
| vcache_elt(vcache, i0, 0); |
| vcache_elt(vcache, i1, 0); |
| vcache_elt(vcache, i2, 0); |
| vcache_check_flush(vcache); |
| } |
| |
| |
| static INLINE void |
| vcache_triangle_flags( struct vcache_frontend *vcache, |
| ushort flags, |
| unsigned i0, |
| unsigned i1, |
| unsigned i2 ) |
| { |
| vcache_elt(vcache, i0, flags); |
| vcache_elt(vcache, i1, 0); |
| vcache_elt(vcache, i2, 0); |
| vcache_check_flush(vcache); |
| } |
| |
| static INLINE void |
| vcache_line( struct vcache_frontend *vcache, |
| unsigned i0, |
| unsigned i1 ) |
| { |
| vcache_elt(vcache, i0, 0); |
| vcache_elt(vcache, i1, 0); |
| vcache_check_flush(vcache); |
| } |
| |
| |
| static INLINE void |
| vcache_line_flags( struct vcache_frontend *vcache, |
| ushort flags, |
| unsigned i0, |
| unsigned i1 ) |
| { |
| vcache_elt(vcache, i0, flags); |
| vcache_elt(vcache, i1, 0); |
| vcache_check_flush(vcache); |
| } |
| |
| |
| static INLINE void |
| vcache_point( struct vcache_frontend *vcache, |
| unsigned i0 ) |
| { |
| vcache_elt(vcache, i0, 0); |
| vcache_check_flush(vcache); |
| } |
| |
| static INLINE void |
| vcache_quad( struct vcache_frontend *vcache, |
| unsigned i0, |
| unsigned i1, |
| unsigned i2, |
| unsigned i3 ) |
| { |
| vcache_triangle( vcache, i0, i1, i3 ); |
| vcache_triangle( vcache, i1, i2, i3 ); |
| } |
| |
| static INLINE void |
| vcache_ef_quad( struct vcache_frontend *vcache, |
| unsigned i0, |
| unsigned i1, |
| unsigned i2, |
| unsigned i3 ) |
| { |
| if (vcache->draw->rasterizer->flatshade_first) { |
| vcache_triangle_flags( vcache, |
| ( DRAW_PIPE_RESET_STIPPLE | |
| DRAW_PIPE_EDGE_FLAG_0 | |
| DRAW_PIPE_EDGE_FLAG_1 ), |
| i0, i1, i2 ); |
| |
| vcache_triangle_flags( vcache, |
| ( DRAW_PIPE_EDGE_FLAG_2 | |
| DRAW_PIPE_EDGE_FLAG_1 ), |
| i0, i2, i3 ); |
| } |
| else { |
| vcache_triangle_flags( vcache, |
| ( DRAW_PIPE_RESET_STIPPLE | |
| DRAW_PIPE_EDGE_FLAG_0 | |
| DRAW_PIPE_EDGE_FLAG_2 ), |
| i0, i1, i3 ); |
| |
| vcache_triangle_flags( vcache, |
| ( DRAW_PIPE_EDGE_FLAG_0 | |
| DRAW_PIPE_EDGE_FLAG_1 ), |
| i1, i2, i3 ); |
| } |
| } |
| |
| /* At least for now, we're back to using a template include file for |
| * this. The two paths aren't too different though - it may be |
| * possible to reunify them. |
| */ |
| #define TRIANGLE(vc,flags,i0,i1,i2) vcache_triangle_flags(vc,flags,i0,i1,i2) |
| #define QUAD(vc,i0,i1,i2,i3) vcache_ef_quad(vc,i0,i1,i2,i3) |
| #define LINE(vc,flags,i0,i1) vcache_line_flags(vc,flags,i0,i1) |
| #define POINT(vc,i0) vcache_point(vc,i0) |
| #define FUNC vcache_run_extras |
| #include "draw_pt_vcache_tmp.h" |
| |
| #define TRIANGLE(vc,flags,i0,i1,i2) vcache_triangle(vc,i0,i1,i2) |
| #define QUAD(vc,i0,i1,i2,i3) vcache_quad(vc,i0,i1,i2,i3) |
| #define LINE(vc,flags,i0,i1) vcache_line(vc,i0,i1) |
| #define POINT(vc,i0) vcache_point(vc,i0) |
| #define FUNC vcache_run |
| #include "draw_pt_vcache_tmp.h" |
| |
| static INLINE void |
| rebase_uint_elts( const unsigned *src, |
| unsigned count, |
| int delta, |
| ushort *dest ) |
| { |
| unsigned i; |
| |
| for (i = 0; i < count; i++) |
| dest[i] = (ushort)(src[i] + delta); |
| } |
| |
| static INLINE void |
| rebase_ushort_elts( const ushort *src, |
| unsigned count, |
| int delta, |
| ushort *dest ) |
| { |
| unsigned i; |
| |
| for (i = 0; i < count; i++) |
| dest[i] = (ushort)(src[i] + delta); |
| } |
| |
| static INLINE void |
| rebase_ubyte_elts( const ubyte *src, |
| unsigned count, |
| int delta, |
| ushort *dest ) |
| { |
| unsigned i; |
| |
| for (i = 0; i < count; i++) |
| dest[i] = (ushort)(src[i] + delta); |
| } |
| |
| |
| |
| static INLINE void |
| translate_uint_elts( const unsigned *src, |
| unsigned count, |
| ushort *dest ) |
| { |
| unsigned i; |
| |
| for (i = 0; i < count; i++) |
| dest[i] = (ushort)(src[i]); |
| } |
| |
| static INLINE void |
| translate_ushort_elts( const ushort *src, |
| unsigned count, |
| ushort *dest ) |
| { |
| unsigned i; |
| |
| for (i = 0; i < count; i++) |
| dest[i] = (ushort)(src[i]); |
| } |
| |
| static INLINE void |
| translate_ubyte_elts( const ubyte *src, |
| unsigned count, |
| ushort *dest ) |
| { |
| unsigned i; |
| |
| for (i = 0; i < count; i++) |
| dest[i] = (ushort)(src[i]); |
| } |
| |
| |
| |
| |
| #if 0 |
| static INLINE enum pipe_format |
| format_from_get_elt( pt_elt_func get_elt ) |
| { |
| switch (draw->pt.user.eltSize) { |
| case 1: return PIPE_FORMAT_R8_UNORM; |
| case 2: return PIPE_FORMAT_R16_UNORM; |
| case 4: return PIPE_FORMAT_R32_UNORM; |
| default: return PIPE_FORMAT_NONE; |
| } |
| } |
| #endif |
| |
| static INLINE void |
| vcache_check_run( struct draw_pt_front_end *frontend, |
| pt_elt_func get_elt, |
| const void *elts, |
| unsigned draw_count ) |
| { |
| struct vcache_frontend *vcache = (struct vcache_frontend *)frontend; |
| struct draw_context *draw = vcache->draw; |
| unsigned min_index = draw->pt.user.min_index; |
| unsigned max_index = draw->pt.user.max_index; |
| unsigned index_size = draw->pt.user.eltSize; |
| unsigned fetch_count = max_index + 1 - min_index; |
| const ushort *transformed_elts; |
| ushort *storage = NULL; |
| boolean ok = FALSE; |
| |
| |
| if (0) debug_printf("fetch_count %d fetch_max %d draw_count %d\n", fetch_count, |
| vcache->fetch_max, |
| draw_count); |
| |
| if (max_index == 0xffffffff || |
| fetch_count > draw_count) { |
| if (0) debug_printf("fail\n"); |
| goto fail; |
| } |
| |
| if (vcache->middle_prim != vcache->input_prim) { |
| vcache->middle_prim = vcache->input_prim; |
| vcache->middle->prepare( vcache->middle, |
| vcache->middle_prim, |
| vcache->opt, |
| &vcache->fetch_max ); |
| } |
| |
| |
| if (min_index == 0 && |
| index_size == 2) |
| { |
| transformed_elts = (const ushort *)elts; |
| } |
| else |
| { |
| storage = MALLOC( draw_count * sizeof(ushort) ); |
| if (!storage) |
| goto fail; |
| |
| if (min_index == 0) { |
| switch(index_size) { |
| case 1: |
| translate_ubyte_elts( (const ubyte *)elts, |
| draw_count, |
| storage ); |
| break; |
| |
| case 2: |
| translate_ushort_elts( (const ushort *)elts, |
| draw_count, |
| storage ); |
| break; |
| |
| case 4: |
| translate_uint_elts( (const uint *)elts, |
| draw_count, |
| storage ); |
| break; |
| |
| default: |
| assert(0); |
| FREE(storage); |
| return; |
| } |
| } |
| else { |
| switch(index_size) { |
| case 1: |
| rebase_ubyte_elts( (const ubyte *)elts, |
| draw_count, |
| 0 - (int)min_index, |
| storage ); |
| break; |
| |
| case 2: |
| rebase_ushort_elts( (const ushort *)elts, |
| draw_count, |
| 0 - (int)min_index, |
| storage ); |
| break; |
| |
| case 4: |
| rebase_uint_elts( (const uint *)elts, |
| draw_count, |
| 0 - (int)min_index, |
| storage ); |
| break; |
| |
| default: |
| assert(0); |
| FREE(storage); |
| return; |
| } |
| } |
| transformed_elts = storage; |
| } |
| |
| if (fetch_count < UNDEFINED_VERTEX_ID) |
| ok = vcache->middle->run_linear_elts( vcache->middle, |
| min_index, /* start */ |
| fetch_count, |
| transformed_elts, |
| draw_count ); |
| |
| FREE(storage); |
| |
| if (ok) |
| return; |
| |
| debug_printf("failed to execute atomic draw elts for %d/%d, splitting up\n", |
| fetch_count, draw_count); |
| |
| fail: |
| vcache_run( frontend, get_elt, elts, draw_count ); |
| } |
| |
| |
| |
| |
| static void |
| vcache_prepare( struct draw_pt_front_end *frontend, |
| unsigned prim, |
| struct draw_pt_middle_end *middle, |
| unsigned opt ) |
| { |
| struct vcache_frontend *vcache = (struct vcache_frontend *)frontend; |
| |
| if (opt & PT_PIPELINE) |
| { |
| vcache->base.run = vcache_run_extras; |
| } |
| else |
| { |
| vcache->base.run = vcache_check_run; |
| } |
| |
| vcache->input_prim = prim; |
| vcache->output_prim = u_reduced_prim(prim); |
| |
| vcache->middle = middle; |
| vcache->opt = opt; |
| |
| /* Have to run prepare here, but try and guess a good prim for |
| * doing so: |
| */ |
| vcache->middle_prim = (opt & PT_PIPELINE) ? vcache->output_prim : vcache->input_prim; |
| middle->prepare( middle, vcache->middle_prim, opt, &vcache->fetch_max ); |
| } |
| |
| |
| |
| |
| static void |
| vcache_finish( struct draw_pt_front_end *frontend ) |
| { |
| struct vcache_frontend *vcache = (struct vcache_frontend *)frontend; |
| vcache->middle->finish( vcache->middle ); |
| vcache->middle = NULL; |
| } |
| |
| static void |
| vcache_destroy( struct draw_pt_front_end *frontend ) |
| { |
| FREE(frontend); |
| } |
| |
| |
| struct draw_pt_front_end *draw_pt_vcache( struct draw_context *draw ) |
| { |
| struct vcache_frontend *vcache = CALLOC_STRUCT( vcache_frontend ); |
| if (vcache == NULL) |
| return NULL; |
| |
| vcache->base.prepare = vcache_prepare; |
| vcache->base.run = NULL; |
| vcache->base.finish = vcache_finish; |
| vcache->base.destroy = vcache_destroy; |
| vcache->draw = draw; |
| |
| memset(vcache->in, ~0, sizeof(vcache->in)); |
| |
| return &vcache->base; |
| } |