Commit 6c0dc4ba authored by Keith Whitwell's avatar Keith Whitwell

draw: specialized cliptesting routines

parent 4cef3087
/**************************************************************************
*
* Copyright 2010, VMware, inc.
* 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 VMWARE 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.
*
**************************************************************************/
static boolean TAG(do_cliptest)( struct pt_post_vs *pvs,
struct draw_vertex_info *info )
{
struct vertex_header *out = info->verts;
const float *scale = pvs->draw->viewport.scale;
const float *trans = pvs->draw->viewport.translate;
/* const */ float (*plane)[4] = pvs->draw->plane;
const unsigned pos = draw_current_shader_position_output(pvs->draw);
const unsigned ef = pvs->draw->vs.edgeflag_output;
const unsigned nr = pvs->draw->nr_planes;
const unsigned flags = (FLAGS);
unsigned need_pipeline = 0;
unsigned j;
for (j = 0; j < info->count; j++) {
float *position = out->data[pos];
unsigned mask = 0x0;
initialize_vertex_header(out);
if (flags & (DO_CLIP_XY | DO_CLIP_FULL_Z | DO_CLIP_HALF_Z | DO_CLIP_USER)) {
out->clip[0] = position[0];
out->clip[1] = position[1];
out->clip[2] = position[2];
out->clip[3] = position[3];
/* Do the hardwired planes first:
*/
if (flags & DO_CLIP_XY) {
if (-position[0] + position[3] < 0) mask |= (1<<0);
if ( position[0] + position[3] < 0) mask |= (1<<1);
if (-position[1] + position[3] < 0) mask |= (1<<2);
if ( position[1] + position[3] < 0) mask |= (1<<3);
}
/* Clip Z planes according to full cube, half cube or none.
*/
if (flags & DO_CLIP_FULL_Z) {
if ( position[2] + position[3] < 0) mask |= (1<<4);
if (-position[2] + position[3] < 0) mask |= (1<<5);
}
else if (flags & DO_CLIP_HALF_Z) {
if ( position[2] < 0) mask |= (1<<4);
if (-position[2] + position[3] < 0) mask |= (1<<5);
}
if (flags & DO_CLIP_USER) {
unsigned i;
for (i = 6; i < nr; i++) {
if (dot4(position, plane[i]) < 0)
mask |= (1<<i);
}
}
out->clipmask = mask;
need_pipeline |= out->clipmask;
}
if ((flags & DO_VIEWPORT) && mask == 0)
{
/* divide by w */
float w = 1.0f / position[3];
/* Viewport mapping */
position[0] = position[0] * w * scale[0] + trans[0];
position[1] = position[1] * w * scale[1] + trans[1];
position[2] = position[2] * w * scale[2] + trans[2];
position[3] = w;
}
if ((flags & DO_EDGEFLAG) && ef) {
const float *edgeflag = out->data[ef];
out->edgeflag = !(edgeflag[0] != 1.0f);
need_pipeline |= !out->edgeflag;
}
out = (struct vertex_header *)( (char *)out + info->stride );
}
return need_pipeline != 0;
}
#undef FLAGS
#undef TAG
......@@ -106,6 +106,8 @@ boolean draw_init(struct draw_context *draw)
ASSIGN_4V( draw->plane[4], 0, 0, 1, 1 ); /* yes these are correct */
ASSIGN_4V( draw->plane[5], 0, 0, -1, 1 ); /* mesa's a bit wonky */
draw->nr_planes = 6;
draw->clip_xy = 1;
draw->clip_z = 1;
draw->reduced_prim = ~0; /* != any of PIPE_PRIM_x */
......@@ -186,6 +188,14 @@ void draw_set_mrd(struct draw_context *draw, double mrd)
}
static void update_clip_flags( struct draw_context *draw )
{
draw->clip_xy = !draw->driver.bypass_clip_xy;
draw->clip_z = (!draw->driver.bypass_clip_z &&
!draw->depth_clamp);
draw->clip_user = (draw->nr_planes > 6);
}
/**
* Register new primitive rasterization/rendering state.
* This causes the drawing pipeline to be rebuilt.
......@@ -200,18 +210,25 @@ void draw_set_rasterizer_state( struct draw_context *draw,
draw->rasterizer = raster;
draw->rast_handle = rast_handle;
draw->bypass_clipping = draw->driver.bypass_clipping;
}
}
}
/* With a little more work, llvmpipe will be able to turn this off and
* do its own x/y clipping.
*
* Some hardware can turn off clipping altogether - in particular any
* hardware with a TNL unit can do its own clipping, even if it is
* relying on the draw module for some other reason.
*/
void draw_set_driver_clipping( struct draw_context *draw,
boolean bypass_clipping )
boolean bypass_clip_xy,
boolean bypass_clip_z )
{
draw_do_flush( draw, DRAW_FLUSH_STATE_CHANGE );
draw->driver.bypass_clipping = bypass_clipping;
draw->bypass_clipping = draw->driver.bypass_clipping;
draw->driver.bypass_clip_xy = bypass_clip_xy;
draw->driver.bypass_clip_z = bypass_clip_z;
update_clip_flags(draw);
}
......@@ -241,6 +258,8 @@ void draw_set_clip_state( struct draw_context *draw,
memcpy(&draw->plane[6], clip->ucp, clip->nr * sizeof(clip->ucp[0]));
draw->nr_planes = 6 + clip->nr;
draw->depth_clamp = clip->depth_clamp;
update_clip_flags(draw);
}
......
......@@ -212,7 +212,8 @@ void draw_set_render( struct draw_context *draw,
struct vbuf_render *render );
void draw_set_driver_clipping( struct draw_context *draw,
boolean bypass_clipping );
boolean bypass_clip_xy,
boolean bypass_clip_z );
void draw_set_force_passthrough( struct draw_context *draw,
boolean enable );
......
......@@ -265,7 +265,7 @@ static struct draw_stage *validate_pipeline( struct draw_stage *stage )
/* Clip stage
*/
if (!draw->bypass_clipping)
if (draw->clip_xy || draw->clip_z || draw->clip_user)
{
draw->pipeline.clip->next = next;
next = draw->pipeline.clip;
......
......@@ -176,13 +176,19 @@ struct draw_context
} pt;
struct {
boolean bypass_clipping;
boolean bypass_vs;
boolean bypass_clip_xy;
boolean bypass_clip_z;
} driver;
boolean flushing; /**< debugging/sanity */
boolean suspend_flushing; /**< internally set */
boolean bypass_clipping; /**< set if either api or driver bypass_clipping true */
/* Flags set if API requires clipping in these planes and the
* driver doesn't indicate that it can do it for us.
*/
boolean clip_xy;
boolean clip_z;
boolean clip_user;
boolean force_passthrough; /**< never clip or shade */
......
......@@ -86,7 +86,9 @@ draw_pt_arrays(struct draw_context *draw,
opt |= PT_PIPELINE;
}
if (!draw->bypass_clipping && !draw->pt.test_fse) {
if ((draw->clip_xy ||
draw->clip_z ||
draw->clip_user) && !draw->pt.test_fse) {
opt |= PT_CLIPTEST;
}
......
......@@ -221,7 +221,9 @@ boolean draw_pt_post_vs_run( struct pt_post_vs *pvs,
struct draw_vertex_info *info );
void draw_pt_post_vs_prepare( struct pt_post_vs *pvs,
boolean bypass_clipping,
boolean clip_xy,
boolean clip_z,
boolean clip_user,
boolean bypass_viewport,
boolean opengl,
boolean need_edgeflags );
......
......@@ -102,7 +102,7 @@ static void fse_prepare( struct draw_pt_middle_end *middle,
fse->key.nr_inputs); /* inputs - fetch from api format */
fse->key.viewport = !draw->identity_viewport;
fse->key.clip = !draw->bypass_clipping;
fse->key.clip = draw->clip_xy || draw->clip_z || draw->clip_user;
fse->key.const_vbuffers = 0;
memset(fse->key.element, 0,
......
......@@ -100,8 +100,10 @@ static void fetch_pipeline_prepare( struct draw_pt_middle_end *middle,
* but gl vs dx9 clip spaces.
*/
draw_pt_post_vs_prepare( fpme->post_vs,
(boolean)draw->bypass_clipping,
(boolean)draw->identity_viewport,
draw->clip_xy,
draw->clip_z,
draw->clip_user,
draw->identity_viewport,
(boolean)draw->rasterizer->gl_rasterization_rules,
(draw->vs.edgeflag_output ? TRUE : FALSE) );
......
......@@ -107,8 +107,10 @@ llvm_middle_end_prepare( struct draw_pt_middle_end *middle,
* but gl vs dx9 clip spaces.
*/
draw_pt_post_vs_prepare( fpme->post_vs,
(boolean)draw->bypass_clipping,
(boolean)(draw->identity_viewport),
draw->clip_xy,
draw->clip_z,
draw->clip_user,
draw->identity_viewport,
(boolean)draw->rasterizer->gl_rasterization_rules,
(draw->vs.edgeflag_output ? TRUE : FALSE) );
......
......@@ -26,14 +26,26 @@
**************************************************************************/
#include "util/u_memory.h"
#include "util/u_math.h"
#include "pipe/p_context.h"
#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "draw/draw_pt.h"
#define DO_CLIP_XY 0x1
#define DO_CLIP_FULL_Z 0x2
#define DO_CLIP_HALF_Z 0x4
#define DO_CLIP_USER 0x8
#define DO_VIEWPORT 0x10
#define DO_EDGEFLAG 0x20
struct pt_post_vs {
struct draw_context *draw;
unsigned flags;
boolean (*run)( struct pt_post_vs *pvs,
struct draw_vertex_info *info );
};
......@@ -56,186 +68,47 @@ dot4(const float *a, const float *b)
a[3]*b[3]);
}
static INLINE unsigned
compute_clipmask_gl(const float *clip, /*const*/ float plane[][4], unsigned nr,
boolean clip_depth)
{
unsigned mask = 0x0;
unsigned i;
#define FLAGS (0)
#define TAG(x) x##_none
#include "draw_cliptest_tmp.h"
#if 0
debug_printf("compute clipmask %f %f %f %f\n",
clip[0], clip[1], clip[2], clip[3]);
assert(clip[3] != 0.0);
#endif
#define FLAGS (DO_CLIP_XY | DO_CLIP_FULL_Z | DO_VIEWPORT)
#define TAG(x) x##_xy_fullz_viewport
#include "draw_cliptest_tmp.h"
/* Do the hardwired planes first:
*/
if (-clip[0] + clip[3] < 0) mask |= (1<<0);
if ( clip[0] + clip[3] < 0) mask |= (1<<1);
if (-clip[1] + clip[3] < 0) mask |= (1<<2);
if ( clip[1] + clip[3] < 0) mask |= (1<<3);
if (clip_depth) {
if ( clip[2] + clip[3] < 0) mask |= (1<<4); /* match mesa clipplane numbering - for now */
if (-clip[2] + clip[3] < 0) mask |= (1<<5); /* match mesa clipplane numbering - for now */
}
#define FLAGS (DO_CLIP_XY | DO_CLIP_HALF_Z | DO_VIEWPORT)
#define TAG(x) x##_xy_halfz_viewport
#include "draw_cliptest_tmp.h"
/* Followed by any remaining ones:
*/
for (i = 6; i < nr; i++) {
if (dot4(clip, plane[i]) < 0)
mask |= (1<<i);
}
#define FLAGS (DO_CLIP_FULL_Z | DO_VIEWPORT)
#define TAG(x) x##_fullz_viewport
#include "draw_cliptest_tmp.h"
return mask;
}
#define FLAGS (DO_CLIP_HALF_Z | DO_VIEWPORT)
#define TAG(x) x##_halfz_viewport
#include "draw_cliptest_tmp.h"
#define FLAGS (DO_CLIP_XY | DO_CLIP_FULL_Z | DO_CLIP_USER | DO_VIEWPORT)
#define TAG(x) x##_xy_fullz_user_viewport
#include "draw_cliptest_tmp.h"
/* The normal case - cliptest, rhw divide, viewport transform.
*
* Also handle identity viewport here at the expense of a few wasted
* instructions
*/
static boolean post_vs_cliptest_viewport_gl( struct pt_post_vs *pvs,
struct draw_vertex_info *info )
{
struct vertex_header *out = info->verts;
const float *scale = pvs->draw->viewport.scale;
const float *trans = pvs->draw->viewport.translate;
const unsigned pos = draw_current_shader_position_output(pvs->draw);
unsigned clipped = 0;
unsigned j;
if (0) debug_printf("%s count, %d\n", __FUNCTION__, info->count);
for (j = 0; j < info->count; j++) {
float *position = out->data[pos];
initialize_vertex_header(out);
#if 0
debug_printf("%d) io = %p, data = %p = [%f, %f, %f, %f]\n",
j, out, position, position[0], position[1], position[2], position[3]);
#endif
out->clip[0] = position[0];
out->clip[1] = position[1];
out->clip[2] = position[2];
out->clip[3] = position[3];
out->vertex_id = 0xffff;
/* Disable depth clipping if depth clamping is enabled. */
out->clipmask = compute_clipmask_gl(out->clip,
pvs->draw->plane,
pvs->draw->nr_planes,
!pvs->draw->depth_clamp);
clipped += out->clipmask;
if (out->clipmask == 0)
{
/* divide by w */
float w = 1.0f / position[3];
/* Viewport mapping */
position[0] = position[0] * w * scale[0] + trans[0];
position[1] = position[1] * w * scale[1] + trans[1];
position[2] = position[2] * w * scale[2] + trans[2];
position[3] = w;
#if 0
debug_printf("post viewport: %f %f %f %f\n",
position[0],
position[1],
position[2],
position[3]);
#endif
}
out = (struct vertex_header *)( (char *)out + info->stride );
}
return clipped != 0;
}
#define FLAGS (DO_CLIP_XY | DO_CLIP_FULL_Z | DO_CLIP_USER | DO_VIEWPORT | DO_EDGEFLAG)
#define TAG(x) x##_xy_fullz_user_viewport_edgeflag
#include "draw_cliptest_tmp.h"
/* As above plus edgeflags
/* Don't want to create 64 versions of this function, so catch the
* less common ones here. This is looking like something which should
* be code-generated, perhaps appended to the end of the vertex
* shader.
*/
static boolean
post_vs_cliptest_viewport_gl_edgeflag(struct pt_post_vs *pvs,
struct draw_vertex_info *info)
{
unsigned j;
boolean needpipe;
needpipe = post_vs_cliptest_viewport_gl(pvs, info);
/* If present, copy edgeflag VS output into vertex header.
* Otherwise, leave header as is.
*/
if (pvs->draw->vs.edgeflag_output) {
struct vertex_header *out = info->verts;
int ef = pvs->draw->vs.edgeflag_output;
for (j = 0; j < info->count; j++) {
const float *edgeflag = out->data[ef];
out->edgeflag = !(edgeflag[0] != 1.0f);
needpipe |= !out->edgeflag;
out = (struct vertex_header *)( (char *)out + info->stride );
}
}
return needpipe;
}
#define FLAGS (pvs->flags)
#define TAG(x) x##_generic
#include "draw_cliptest_tmp.h"
/* If bypass_clipping is set, skip cliptest and rhw divide.
*/
static boolean post_vs_viewport( struct pt_post_vs *pvs,
struct draw_vertex_info *info )
{
struct vertex_header *out = info->verts;
const float *scale = pvs->draw->viewport.scale;
const float *trans = pvs->draw->viewport.translate;
const unsigned pos = draw_current_shader_position_output(pvs->draw);
unsigned j;
if (0) debug_printf("%s\n", __FUNCTION__);
for (j = 0; j < info->count; j++) {
float *position = out->data[pos];
initialize_vertex_header(out);
/* Viewport mapping only, no cliptest/rhw divide
*/
position[0] = position[0] * scale[0] + trans[0];
position[1] = position[1] * scale[1] + trans[1];
position[2] = position[2] * scale[2] + trans[2];
out = (struct vertex_header *)((char *)out + info->stride);
}
return FALSE;
}
/* If bypass_clipping is set and we have an identity viewport, nothing
* to do.
*/
static boolean post_vs_none( struct pt_post_vs *pvs,
struct draw_vertex_info *info )
{
struct vertex_header *out = info->verts;
unsigned j;
if (0) debug_printf("%s\n", __FUNCTION__);
/* just initialize the vertex_id in all headers */
for (j = 0; j < info->count; j++) {
initialize_vertex_header(out);
out = (struct vertex_header *)((char *)out + info->stride);
}
return FALSE;
}
boolean draw_pt_post_vs_run( struct pt_post_vs *pvs,
struct draw_vertex_info *info )
{
......@@ -244,31 +117,72 @@ boolean draw_pt_post_vs_run( struct pt_post_vs *pvs,
void draw_pt_post_vs_prepare( struct pt_post_vs *pvs,
boolean bypass_clipping,
boolean clip_xy,
boolean clip_z,
boolean clip_user,
boolean bypass_viewport,
boolean opengl,
boolean need_edgeflags )
{
if (!need_edgeflags) {
if (bypass_clipping) {
if (bypass_viewport)
pvs->run = post_vs_none;
else
pvs->run = post_vs_viewport;
}
else {
/* if (opengl) */
pvs->run = post_vs_cliptest_viewport_gl;
}
pvs->flags = 0;
if (clip_xy)
pvs->flags |= DO_CLIP_XY;
if (clip_z && opengl) {
pvs->flags |= DO_CLIP_FULL_Z;
ASSIGN_4V( pvs->draw->plane[4], 0, 0, 1, 1 );
}
if (clip_z && !opengl) {
pvs->flags |= DO_CLIP_HALF_Z;
ASSIGN_4V( pvs->draw->plane[4], 0, 0, 1, 0 );
}
else {
/* If we need to copy edgeflags to the vertex header, it should
* mean we're running the primitive pipeline. Hence the bypass
* flags should be false.
*/
assert(!bypass_clipping);
assert(!bypass_viewport);
pvs->run = post_vs_cliptest_viewport_gl_edgeflag;
if (clip_user)
pvs->flags |= DO_CLIP_USER;
if (!bypass_viewport)
pvs->flags |= DO_VIEWPORT;
if (need_edgeflags)
pvs->flags |= DO_EDGEFLAG;
/* Now select the relevant function:
*/
switch (pvs->flags) {
case 0:
pvs->run = do_cliptest_none;
break;
case DO_CLIP_XY | DO_CLIP_FULL_Z | DO_VIEWPORT:
pvs->run = do_cliptest_xy_fullz_viewport;
break;
case DO_CLIP_XY | DO_CLIP_HALF_Z | DO_VIEWPORT:
pvs->run = do_cliptest_xy_halfz_viewport;
break;
case DO_CLIP_FULL_Z | DO_VIEWPORT:
pvs->run = do_cliptest_fullz_viewport;
break;
case DO_CLIP_HALF_Z | DO_VIEWPORT:
pvs->run = do_cliptest_halfz_viewport;
break;
case DO_CLIP_XY | DO_CLIP_FULL_Z | DO_CLIP_USER | DO_VIEWPORT:
pvs->run = do_cliptest_xy_fullz_user_viewport;
break;
case (DO_CLIP_XY | DO_CLIP_FULL_Z | DO_CLIP_USER |
DO_VIEWPORT | DO_EDGEFLAG):
pvs->run = do_cliptest_xy_fullz_user_viewport_edgeflag;
break;
default:
pvs->run = do_cliptest_generic;
break;
}
}
......
......@@ -431,8 +431,6 @@ struct pipe_context* r300_create_context(struct pipe_screen* screen,
r300->draw = draw_create(&r300->context);
/* Enable our renderer. */
draw_set_rasterize_stage(r300->draw, r300_draw_stage(r300));
/* Enable Draw's clipping. */
draw_set_driver_clipping(r300->draw, FALSE);
/* Disable converting points/lines to triangles. */
draw_wide_line_threshold(r300->draw, 10000000.f);