This commit is contained in:
elwrcl committed 2026-06-06 13:25:15 +03:00
1 parent 04d4dc8b09
commit 818cab85dc
12511 files changed
+6156641

No files matched your search

@@ -0,0 +1,199 @@
/**************************************************************************
*
* 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.
*
**************************************************************************/
#include "util/u_bitcast.h"
#include <math.h>
static bool
TAG(do_cliptest)(struct pt_post_vs *pvs,
struct draw_vertex_info *info,
const struct draw_prim_info *prim_info)
{
struct vertex_header *out = info->verts;
/* const */ float (*plane)[4] = pvs->draw->plane;
const unsigned pos = draw_current_shader_position_output(pvs->draw);
const unsigned cv = draw_current_shader_clipvertex_output(pvs->draw);
const unsigned ef = pvs->draw->vs.edgeflag_output;
unsigned ucp_enable = pvs->draw->rasterizer->clip_plane_enable;
unsigned flags = (FLAGS);
unsigned need_pipeline = 0;
bool have_cd = false;
bool uses_vp_idx = draw_current_shader_uses_viewport_index(pvs->draw);
unsigned viewport_index_output =
draw_current_shader_viewport_index_output(pvs->draw);
int viewport_index = 0;
int num_written_clipdistance =
draw_current_shader_num_written_clipdistances(pvs->draw);
if (uses_vp_idx) {
viewport_index = u_bitcast_f2u(out->data[viewport_index_output][0]);
viewport_index = draw_clamp_viewport_idx(viewport_index);
}
unsigned cd[2];
cd[0] = draw_current_shader_ccdistance_output(pvs->draw, 0);
cd[1] = draw_current_shader_ccdistance_output(pvs->draw, 1);
if (cd[0] != pos || cd[1] != pos)
have_cd = true;
/* If clipdistance semantic has been written by the shader
* that means we're expected to do 'user plane clipping' */
if (num_written_clipdistance && !(flags & DO_CLIP_USER)) {
flags |= DO_CLIP_USER;
ucp_enable = (1 << num_written_clipdistance) - 1;
}
assert(pos != -1);
unsigned prim_idx = 0, prim_vert_idx = 0;
for (unsigned j = 0; j < info->count; j++) {
float *position = out->data[pos];
unsigned mask = 0x0;
if (uses_vp_idx) {
/* only change the viewport_index for the leading vertex */
if (prim_vert_idx == (prim_info->primitive_lengths[prim_idx])) {
prim_idx++;
prim_vert_idx = 0;
viewport_index = u_bitcast_f2u(out->data[viewport_index_output][0]);
viewport_index = draw_clamp_viewport_idx(viewport_index);
}
prim_vert_idx++;
}
float *scale = pvs->draw->viewports[viewport_index].scale;
float *trans = pvs->draw->viewports[viewport_index].translate;
initialize_vertex_header(out);
if (flags & (DO_CLIP_XY | DO_CLIP_XY_GUARD_BAND |
DO_CLIP_FULL_Z | DO_CLIP_HALF_Z | DO_CLIP_USER)) {
float *clipvertex = position;
if ((flags & DO_CLIP_USER) && cv != pos) {
assert(cv != -1);
clipvertex = out->data[cv];
}
for (unsigned i = 0; i < 4; i++) {
out->clip_pos[i] = position[i];
}
/* Be careful with NaNs. Comparisons must be true for them. */
/* Do the hardwired planes first:
*/
if (flags & DO_CLIP_XY_GUARD_BAND) {
if (!(-0.50 * position[0] + position[3] >= 0)) mask |= (1<<0);
if (!( 0.50 * position[0] + position[3] >= 0)) mask |= (1<<1);
if (!(-0.50 * position[1] + position[3] >= 0)) mask |= (1<<2);
if (!( 0.50 * position[1] + position[3] >= 0)) mask |= (1<<3);
} else 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 ucp_mask = ucp_enable;
while (ucp_mask) {
unsigned plane_idx = ffs(ucp_mask)-1;
ucp_mask &= ~(1 << plane_idx);
plane_idx += 6;
/*
* for user clipping check if we have a clip distance output
* and the shader has written to it, otherwise use clipvertex
* to decide when the plane is clipping.
*/
if (have_cd && num_written_clipdistance) {
float clipdist;
unsigned i = plane_idx - 6;
/* first four clip distance in first vector etc. */
if (i < 4)
clipdist = out->data[cd[0]][i];
else
clipdist = out->data[cd[1]][i-4];
if (clipdist < 0 || util_is_inf_or_nan(clipdist))
mask |= 1 << plane_idx;
} else {
if (!(dot4(clipvertex, plane[plane_idx]) >= 0))
mask |= 1 << plane_idx;
}
}
}
out->clipmask = mask;
need_pipeline |= out->clipmask;
}
/* For the window transformation, the first 4 (xy) clip planes should
* normally be handled by guardband clipping in clip_line_guard_xy, but
* it hasn't been fully implemented yet, so we'll have to do it here.
* Otherwise we'd skip it for being redundant.
*/
if (flags & DO_CLIP_XY_GUARD_BAND)
mask &= 0xfffffff0;
/*
* Transform the vertex position from clip coords to window coords,
* if the vertex is unclipped.
*/
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
File diff suppressed because it is too large. Load diff
+432
View File
@@ -0,0 +1,432 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/**
* \brief Public interface into the drawing module.
*/
/* Authors: Keith Whitwell <[email protected]>
*/
#ifndef DRAW_CONTEXT_H
#define DRAW_CONTEXT_H
#include "pipe/p_state.h"
#include "pipe/p_shader_tokens.h"
#include "util/mesa-blake3.h"
#include "nir.h"
struct pipe_context;
struct draw_context;
struct draw_stage;
struct draw_vertex_shader;
struct draw_geometry_shader;
struct draw_tess_ctrl_shader;
struct draw_tess_eval_shader;
struct draw_mesh_shader;
struct draw_fragment_shader;
struct tgsi_sampler;
struct tgsi_image;
struct tgsi_buffer;
struct lp_cached_code;
/*
* structure to contain driver internal information
* for stream out support. mapping stores the pointer
* to the buffer contents, and internal offset stores
* an internal counter to how much of the stream
* out buffer is used (in bytes).
*/
struct draw_so_target {
struct pipe_stream_output_target target;
void *mapping;
int internal_offset;
};
struct draw_vertex_info {
struct vertex_header *verts;
unsigned vertex_size;
unsigned stride;
unsigned count;
};
struct draw_prim_info {
bool linear;
unsigned start;
const uint16_t *elts;
unsigned count;
enum mesa_prim prim;
unsigned flags;
unsigned *primitive_lengths;
unsigned primitive_count;
};
struct draw_context *draw_create(struct pipe_context *pipe);
#if DRAW_LLVM_AVAILABLE
struct draw_context *draw_create_with_llvm_context(struct pipe_context *pipe,
void *context);
#endif
struct draw_context *draw_create_no_llvm(struct pipe_context *pipe);
void draw_destroy(struct draw_context *draw);
void draw_flush(struct draw_context *draw);
void draw_set_viewport_states(struct draw_context *draw,
unsigned start_slot,
unsigned num_viewports,
const struct pipe_viewport_state *viewports);
void draw_set_clip_state(struct draw_context *pipe,
const struct pipe_clip_state *clip);
/**
* Sets the rasterization state used by the draw module.
* The rast_handle is used to pass the driver specific representation
* of the rasterization state. It's going to be used when the
* draw module sets the state back on the driver itself using the
* pipe::bind_rasterizer_state method.
*
* NOTE: if you're calling this function from within the pipe's
* bind_rasterizer_state you should always call it before binding
* the actual state - that's because the draw module can try to
* bind its own rasterizer state which would reset your newly
* set state. i.e. always do
* draw_set_rasterizer_state(driver->draw, state->pipe_state, state);
* driver->state.raster = state;
*/
void draw_set_rasterizer_state(struct draw_context *draw,
const struct pipe_rasterizer_state *raster,
void *rast_handle);
void draw_set_rasterize_stage(struct draw_context *draw,
struct draw_stage *stage);
void draw_wide_point_threshold(struct draw_context *draw, float threshold);
void draw_wide_point_sprites(struct draw_context *draw, bool draw_sprite);
void draw_wide_line_threshold(struct draw_context *draw, float threshold);
void draw_enable_line_stipple(struct draw_context *draw, bool enable);
void draw_enable_point_sprites(struct draw_context *draw, bool enable);
void draw_set_zs_format(struct draw_context *draw, enum pipe_format format);
/* for TGSI constants are 4 * sizeof(float), but for NIR they need to be sizeof(float); */
void draw_set_constant_buffer_stride(struct draw_context *draw, unsigned num_bytes);
bool
draw_install_aaline_stage(struct draw_context *draw, struct pipe_context *pipe);
bool
draw_install_aapoint_stage(struct draw_context *draw, struct pipe_context *pipe,
nir_alu_type bool_type);
bool
draw_install_pstipple_stage(struct draw_context *draw, struct pipe_context *pipe);
struct tgsi_shader_info *
draw_get_shader_info(const struct draw_context *draw);
void
draw_prepare_shader_outputs(struct draw_context *draw);
int
draw_find_shader_output(const struct draw_context *draw,
enum tgsi_semantic semantic_name,
unsigned semantic_index);
bool
draw_will_inject_frontface(const struct draw_context *draw);
unsigned
draw_num_shader_outputs(const struct draw_context *draw);
unsigned
draw_total_vs_outputs(const struct draw_context *draw);
unsigned
draw_total_gs_outputs(const struct draw_context *draw);
unsigned
draw_total_tcs_outputs(const struct draw_context *draw);
unsigned
draw_total_tes_outputs(const struct draw_context *draw);
void
draw_texture_sampler(struct draw_context *draw,
mesa_shader_stage shader_type,
struct tgsi_sampler *sampler);
void
draw_image(struct draw_context *draw,
mesa_shader_stage shader_type,
struct tgsi_image *image);
void
draw_buffer(struct draw_context *draw,
mesa_shader_stage shader_type,
struct tgsi_buffer *buffer);
void
draw_set_sampler_views(struct draw_context *draw,
mesa_shader_stage shader_stage,
struct pipe_sampler_view **views,
unsigned num);
void
draw_set_samplers(struct draw_context *draw,
mesa_shader_stage shader_stage,
struct pipe_sampler_state **samplers,
unsigned num);
void
draw_set_images(struct draw_context *draw,
mesa_shader_stage shader_stage,
struct pipe_image_view *images,
unsigned num);
void
draw_set_mapped_texture(struct draw_context *draw,
mesa_shader_stage shader_stage,
unsigned sview_idx,
uint32_t width, uint32_t height, uint32_t depth,
uint32_t first_level, uint32_t last_level,
uint32_t num_samples,
uint32_t sample_stride,
const void *base,
uint32_t row_stride[PIPE_MAX_TEXTURE_LEVELS],
uint32_t img_stride[PIPE_MAX_TEXTURE_LEVELS],
uint32_t mip_offsets[PIPE_MAX_TEXTURE_LEVELS]);
void
draw_set_mapped_image(struct draw_context *draw,
mesa_shader_stage shader_stage,
unsigned idx,
uint32_t width, uint32_t height, uint32_t depth,
const void *base_ptr,
uint32_t row_stride,
uint32_t img_stride,
uint32_t num_samples,
uint32_t sample_stride);
/*
* Vertex shader functions
*/
struct draw_vertex_shader *
draw_create_vertex_shader(struct draw_context *draw,
const struct pipe_shader_state *shader);
void draw_bind_vertex_shader(struct draw_context *draw,
struct draw_vertex_shader *dvs);
void draw_delete_vertex_shader(struct draw_context *draw,
struct draw_vertex_shader *dvs);
void draw_vs_attach_so(struct draw_vertex_shader *dvs,
const struct pipe_stream_output_info *info);
void draw_vs_reset_so(struct draw_vertex_shader *dvs);
/*
* Fragment shader functions
*/
struct draw_fragment_shader *
draw_create_fragment_shader(struct draw_context *draw,
const struct pipe_shader_state *shader);
void draw_bind_fragment_shader(struct draw_context *draw,
struct draw_fragment_shader *dvs);
void draw_delete_fragment_shader(struct draw_context *draw,
struct draw_fragment_shader *dvs);
/*
* Geometry shader functions
*/
struct draw_geometry_shader *
draw_create_geometry_shader(struct draw_context *draw,
const struct pipe_shader_state *shader);
void draw_bind_geometry_shader(struct draw_context *draw,
struct draw_geometry_shader *dvs);
void draw_delete_geometry_shader(struct draw_context *draw,
struct draw_geometry_shader *dvs);
/*
* Tess shader functions
*/
struct draw_tess_ctrl_shader *
draw_create_tess_ctrl_shader(struct draw_context *draw,
const struct pipe_shader_state *shader);
void draw_bind_tess_ctrl_shader(struct draw_context *draw,
struct draw_tess_ctrl_shader *dvs);
void draw_delete_tess_ctrl_shader(struct draw_context *draw,
struct draw_tess_ctrl_shader *dvs);
struct draw_tess_eval_shader *
draw_create_tess_eval_shader(struct draw_context *draw,
const struct pipe_shader_state *shader);
void draw_bind_tess_eval_shader(struct draw_context *draw,
struct draw_tess_eval_shader *dvs);
void draw_delete_tess_eval_shader(struct draw_context *draw,
struct draw_tess_eval_shader *dvs);
void draw_set_tess_state(struct draw_context *draw,
const float default_outer_level[4],
const float default_inner_level[2]);
/*
* Mesh shader functions
*/
struct draw_mesh_shader *
draw_create_mesh_shader(struct draw_context *draw,
const struct pipe_shader_state *shader);
void draw_bind_mesh_shader(struct draw_context *draw,
struct draw_mesh_shader *dvs);
void draw_delete_mesh_shader(struct draw_context *draw,
struct draw_mesh_shader *dvs);
/*
* Vertex data functions
*/
void draw_set_vertex_buffers(struct draw_context *draw,
unsigned count,
const struct pipe_vertex_buffer *buffers);
void draw_set_vertex_elements(struct draw_context *draw,
unsigned count,
const struct pipe_vertex_element *elements);
void
draw_set_viewmask(struct draw_context *draw, uint8_t viewmask);
void draw_set_indexes(struct draw_context *draw,
const void *elements, unsigned elem_size,
unsigned available_space);
void draw_set_mapped_vertex_buffer(struct draw_context *draw,
unsigned attr, const void *buffer,
size_t size);
void
draw_set_mapped_constant_buffer(struct draw_context *draw,
mesa_shader_stage shader_type,
unsigned slot,
const void *buffer,
unsigned size);
void
draw_set_mapped_shader_buffer(struct draw_context *draw,
mesa_shader_stage shader_type,
unsigned slot,
const void *buffer,
unsigned size);
void
draw_set_mapped_so_targets(struct draw_context *draw,
unsigned num_targets,
struct draw_so_target *targets[PIPE_MAX_SO_BUFFERS]);
/***********************************************************************
* draw_pt.c
*/
void draw_vbo(struct draw_context *draw,
const struct pipe_draw_info *info,
unsigned drawid_offset,
const struct pipe_draw_indirect_info *indirect,
const struct pipe_draw_start_count_bias *draws,
unsigned num_draws,
uint8_t patch_vertices);
void
draw_mesh(struct draw_context *draw,
struct draw_vertex_info *vert_info,
struct draw_prim_info *prim_info);
/*******************************************************************************
* Driver backend interface
*/
struct vbuf_render;
void
draw_set_render(struct draw_context *draw,
struct vbuf_render *render);
void
draw_set_driver_clipping(struct draw_context *draw,
bool bypass_clip_xy,
bool bypass_clip_z,
bool guard_band_xy,
bool bypass_clip_points_lines);
/*******************************************************************************
* Draw statistics
*/
void
draw_collect_pipeline_statistics(struct draw_context *draw,
bool enable);
void
draw_collect_primitives_generated(struct draw_context *draw,
bool eanble);
/*******************************************************************************
* Draw pipeline
*/
bool
draw_need_pipeline(const struct draw_context *draw,
const struct pipe_rasterizer_state *rasterizer,
enum mesa_prim prim);
void
draw_init_shader_caps(struct pipe_shader_caps *caps);
bool
draw_get_option_use_llvm(void);
void
draw_set_disk_cache_callbacks(struct draw_context *draw,
void *data_cookie,
void (*find_shader)(void *cookie,
struct lp_cached_code *cache,
unsigned char ir_blake3_cache_key[BLAKE3_KEY_LEN]),
void (*insert_shader)(void *cookie,
struct lp_cached_code *cache,
unsigned char ir_blake3_cache_key[BLAKE3_KEY_LEN]));
#endif /* DRAW_CONTEXT_H */
@@ -0,0 +1,458 @@
/*
* Mesa 3-D graphics library
*
* Copyright 2008 VMware, Inc.
* Copyright (C) 2010 LunarG Inc.
*
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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 <[email protected]>
* Chia-I Wu <[email protected]>
*/
/* these macros are optional */
#ifndef LOCAL_VARS
#define LOCAL_VARS
#endif
#ifndef FUNC_ENTER
#define FUNC_ENTER do {} while (0)
#endif
#ifndef FUNC_EXIT
#define FUNC_EXIT do {} while (0)
#endif
#ifndef LINE_ADJ
#define LINE_ADJ(flags, a0, i0, i1, a1) LINE(flags, i0, i1)
#endif
#ifndef TRIANGLE_ADJ
#define TRIANGLE_ADJ(flags, i0, a0, i1, a1, i2, a2) TRIANGLE(flags, i0, i1, i2)
#endif
static void
FUNC(FUNC_VARS)
{
unsigned idx[6], i;
uint16_t flags;
LOCAL_VARS
FUNC_ENTER;
/* prim, prim_flags, count, and last_vertex_last should have been defined */
if (0) {
debug_printf("%s: prim 0x%x, prim_flags 0x%x, count %d, last_vertex_last %d\n",
__func__, prim, prim_flags, count, last_vertex_last);
}
switch (prim) {
case MESA_PRIM_POINTS:
for (i = 0; i < count; i++) {
idx[0] = GET_ELT(i);
POINT(idx[0]);
}
break;
case MESA_PRIM_LINES:
flags = DRAW_PIPE_RESET_STIPPLE;
for (i = 0; i + 1 < count; i += 2) {
idx[0] = GET_ELT(i);
idx[1] = GET_ELT(i + 1);
LINE(flags, idx[0], idx[1]);
}
break;
case MESA_PRIM_LINE_LOOP:
case MESA_PRIM_LINE_STRIP:
if (count >= 2) {
flags = (prim_flags & DRAW_SPLIT_BEFORE) ? 0 : DRAW_PIPE_RESET_STIPPLE;
idx[1] = GET_ELT(0);
idx[2] = idx[1];
for (i = 1; i < count; i++, flags = 0) {
idx[0] = idx[1];
idx[1] = GET_ELT(i);
LINE(flags, idx[0], idx[1]);
}
/* close the loop */
if (prim == MESA_PRIM_LINE_LOOP && !prim_flags)
LINE(flags, idx[1], idx[2]);
}
break;
case MESA_PRIM_TRIANGLES:
flags = DRAW_PIPE_RESET_STIPPLE | DRAW_PIPE_EDGE_FLAG_ALL;
for (i = 0; i + 2 < count; i += 3) {
idx[0] = GET_ELT(i);
idx[1] = GET_ELT(i + 1);
idx[2] = GET_ELT(i + 2);
TRIANGLE(flags, idx[0], idx[1], idx[2]);
}
break;
case MESA_PRIM_TRIANGLE_STRIP:
if (count >= 3) {
flags = DRAW_PIPE_RESET_STIPPLE | DRAW_PIPE_EDGE_FLAG_ALL;
idx[1] = GET_ELT(0);
idx[2] = GET_ELT(1);
if (last_vertex_last) {
for (i = 0; i + 2 < count; i++) {
idx[0] = idx[1];
idx[1] = idx[2];
idx[2] = GET_ELT(i + 2);
/* always emit idx[2] last */
if (i & 1)
TRIANGLE(flags, idx[1], idx[0], idx[2]);
else
TRIANGLE(flags, idx[0], idx[1], idx[2]);
}
}
else {
for (i = 0; i + 2 < count; i++) {
idx[0] = idx[1];
idx[1] = idx[2];
idx[2] = GET_ELT(i + 2);
/* always emit idx[0] first */
if (i & 1)
TRIANGLE(flags, idx[0], idx[2], idx[1]);
else
TRIANGLE(flags, idx[0], idx[1], idx[2]);
}
}
}
break;
case MESA_PRIM_TRIANGLE_FAN:
if (count >= 3) {
flags = DRAW_PIPE_RESET_STIPPLE | DRAW_PIPE_EDGE_FLAG_ALL;
idx[0] = GET_ELT(0);
idx[2] = GET_ELT(1);
/* idx[0] is neither the first nor the last vertex */
if (last_vertex_last) {
for (i = 0; i + 2 < count; i++) {
idx[1] = idx[2];
idx[2] = GET_ELT(i + 2);
/* always emit idx[2] last */
TRIANGLE(flags, idx[0], idx[1], idx[2]);
}
}
else {
for (i = 0; i + 2 < count; i++) {
idx[1] = idx[2];
idx[2] = GET_ELT(i + 2);
/* always emit idx[1] first */
TRIANGLE(flags, idx[1], idx[2], idx[0]);
}
}
}
break;
case MESA_PRIM_QUADS:
if (last_vertex_last) {
for (i = 0; i + 3 < count; i += 4) {
idx[0] = GET_ELT(i);
idx[1] = GET_ELT(i + 1);
idx[2] = GET_ELT(i + 2);
idx[3] = GET_ELT(i + 3);
#ifdef PASS_QUADS
QUAD(0, idx[0], idx[1],
idx[2], idx[3]);
#else
flags = DRAW_PIPE_RESET_STIPPLE |
DRAW_PIPE_EDGE_FLAG_0 |
DRAW_PIPE_EDGE_FLAG_2;
/* always emit idx[3] last */
TRIANGLE(flags, idx[0], idx[1], idx[3]);
flags = DRAW_PIPE_EDGE_FLAG_0 |
DRAW_PIPE_EDGE_FLAG_1;
TRIANGLE(flags, idx[1], idx[2], idx[3]);
#endif
}
}
else {
for (i = 0; i + 3 < count; i += 4) {
idx[0] = GET_ELT(i);
idx[1] = GET_ELT(i + 1);
idx[2] = GET_ELT(i + 2);
idx[3] = GET_ELT(i + 3);
#ifdef PASS_QUADS
QUAD(0, idx[0], idx[1],
idx[2], idx[3]);
#else
flags = DRAW_PIPE_RESET_STIPPLE |
DRAW_PIPE_EDGE_FLAG_0 |
DRAW_PIPE_EDGE_FLAG_1;
/* always emit idx[3] / idx[0] first */
if (quads_flatshade_last)
TRIANGLE(flags, idx[3], idx[0], idx[1]);
else
TRIANGLE(flags, idx[0], idx[1], idx[2]);
flags = DRAW_PIPE_EDGE_FLAG_1 |
DRAW_PIPE_EDGE_FLAG_2;
if (quads_flatshade_last)
TRIANGLE(flags, idx[3], idx[1], idx[2]);
else
TRIANGLE(flags, idx[0], idx[2], idx[3]);
#endif
}
}
break;
case MESA_PRIM_QUAD_STRIP:
if (count >= 4) {
idx[2] = GET_ELT(0);
idx[3] = GET_ELT(1);
if (last_vertex_last) {
for (i = 0; i + 3 < count; i += 2) {
idx[0] = idx[2];
idx[1] = idx[3];
idx[2] = GET_ELT(i + 2);
idx[3] = GET_ELT(i + 3);
#ifdef PASS_QUADS
QUAD(0, idx[2], idx[0],
idx[1], idx[3]);
#else
/* always emit idx[3] last */
flags = DRAW_PIPE_RESET_STIPPLE |
DRAW_PIPE_EDGE_FLAG_0 |
DRAW_PIPE_EDGE_FLAG_2;
TRIANGLE(flags, idx[2], idx[0], idx[3]);
flags = DRAW_PIPE_EDGE_FLAG_0 |
DRAW_PIPE_EDGE_FLAG_1;
TRIANGLE(flags, idx[0], idx[1], idx[3]);
#endif
}
}
else {
for (i = 0; i + 3 < count; i += 2) {
idx[0] = idx[2];
idx[1] = idx[3];
idx[2] = GET_ELT(i + 2);
idx[3] = GET_ELT(i + 3);
#ifdef PASS_QUADS
QUAD(0, idx[3], idx[2],
idx[0], idx[1]);
#else
flags = DRAW_PIPE_RESET_STIPPLE |
DRAW_PIPE_EDGE_FLAG_0 |
DRAW_PIPE_EDGE_FLAG_1;
/* always emit idx[3] / idx[0 first */
if (quads_flatshade_last)
TRIANGLE(flags, idx[3], idx[2], idx[0]);
else
TRIANGLE(flags, idx[0], idx[3], idx[2]);
flags = DRAW_PIPE_EDGE_FLAG_1 |
DRAW_PIPE_EDGE_FLAG_2;
if (quads_flatshade_last)
TRIANGLE(flags, idx[3], idx[0], idx[1]);
else
TRIANGLE(flags, idx[0], idx[1], idx[3]);
#endif
}
}
}
break;
case MESA_PRIM_POLYGON:
if (count >= 3) {
uint16_t edge_next, edge_finish;
if (last_vertex_last) {
flags = (DRAW_PIPE_RESET_STIPPLE |
DRAW_PIPE_EDGE_FLAG_0);
if (!(prim_flags & DRAW_SPLIT_BEFORE))
flags |= DRAW_PIPE_EDGE_FLAG_2;
edge_next = DRAW_PIPE_EDGE_FLAG_0;
edge_finish =
(prim_flags & DRAW_SPLIT_AFTER) ? 0 : DRAW_PIPE_EDGE_FLAG_1;
}
else {
flags = (DRAW_PIPE_RESET_STIPPLE |
DRAW_PIPE_EDGE_FLAG_1);
if (!(prim_flags & DRAW_SPLIT_BEFORE))
flags |= DRAW_PIPE_EDGE_FLAG_0;
edge_next = DRAW_PIPE_EDGE_FLAG_1;
edge_finish =
(prim_flags & DRAW_SPLIT_AFTER) ? 0 : DRAW_PIPE_EDGE_FLAG_2;
}
idx[0] = GET_ELT(0);
idx[2] = GET_ELT(1);
for (i = 0; i + 2 < count; i++, flags = edge_next) {
idx[1] = idx[2];
idx[2] = GET_ELT(i + 2);
if (i + 3 == count)
flags |= edge_finish;
/* idx[0] is both the first and the last vertex */
if (last_vertex_last)
TRIANGLE(flags, idx[1], idx[2], idx[0]);
else
TRIANGLE(flags, idx[0], idx[1], idx[2]);
}
}
break;
case MESA_PRIM_LINES_ADJACENCY:
flags = DRAW_PIPE_RESET_STIPPLE;
for (i = 0; i + 3 < count; i += 4) {
idx[0] = GET_ELT(i);
idx[1] = GET_ELT(i + 1);
idx[2] = GET_ELT(i + 2);
idx[3] = GET_ELT(i + 3);
LINE_ADJ(flags, idx[0], idx[1], idx[2], idx[3]);
}
break;
case MESA_PRIM_LINE_STRIP_ADJACENCY:
if (count >= 4) {
flags = (prim_flags & DRAW_SPLIT_BEFORE) ? 0 : DRAW_PIPE_RESET_STIPPLE;
idx[1] = GET_ELT(0);
idx[2] = GET_ELT(1);
idx[3] = GET_ELT(2);
for (i = 1; i + 2 < count; i++, flags = 0) {
idx[0] = idx[1];
idx[1] = idx[2];
idx[2] = idx[3];
idx[3] = GET_ELT(i + 2);
LINE_ADJ(flags, idx[0], idx[1], idx[2], idx[3]);
}
}
break;
case MESA_PRIM_TRIANGLES_ADJACENCY:
flags = DRAW_PIPE_RESET_STIPPLE | DRAW_PIPE_EDGE_FLAG_ALL;
for (i = 0; i + 5 < count; i += 6) {
idx[0] = GET_ELT(i);
idx[1] = GET_ELT(i + 1);
idx[2] = GET_ELT(i + 2);
idx[3] = GET_ELT(i + 3);
idx[4] = GET_ELT(i + 4);
idx[5] = GET_ELT(i + 5);
TRIANGLE_ADJ(flags, idx[0], idx[1], idx[2], idx[3], idx[4], idx[5]);
}
break;
case MESA_PRIM_TRIANGLE_STRIP_ADJACENCY:
if (count >= 6) {
flags = DRAW_PIPE_RESET_STIPPLE | DRAW_PIPE_EDGE_FLAG_ALL;
idx[0] = GET_ELT(1);
idx[2] = GET_ELT(0);
idx[4] = GET_ELT(2);
idx[3] = GET_ELT(4);
/*
* The vertices of the i-th triangle are stored in
* idx[0,2,4] = { 2*i, 2*i+2, 2*i+4 };
*
* The adjacent vertices are stored in
* idx[1,3,5] = { 2*i-2, 2*i+6, 2*i+3 }.
*
* However, there are two exceptions:
*
* For the first triangle, idx[1] = 1;
* For the last triangle, idx[3] = 2*i+5.
*/
if (last_vertex_last) {
for (i = 0; i + 5 < count; i += 2) {
idx[1] = idx[0];
idx[0] = idx[2];
idx[2] = idx[4];
idx[4] = idx[3];
idx[3] = GET_ELT(i + ((i + 7 < count) ? 6 : 5));
idx[5] = GET_ELT(i + 3);
/*
* alternate the first two vertices (idx[0] and idx[2]) and the
* corresponding adjacent vertices (idx[3] and idx[5]) to have
* the correct orientation
*/
if (i & 2) {
TRIANGLE_ADJ(flags,
idx[2], idx[1], idx[0], idx[5], idx[4], idx[3]);
}
else {
TRIANGLE_ADJ(flags,
idx[0], idx[1], idx[2], idx[3], idx[4], idx[5]);
}
}
}
else {
for (i = 0; i + 5 < count; i += 2) {
idx[1] = idx[0];
idx[0] = idx[2];
idx[2] = idx[4];
idx[4] = idx[3];
idx[3] = GET_ELT(i + ((i + 7 < count) ? 6 : 5));
idx[5] = GET_ELT(i + 3);
/*
* alternate the last two vertices (idx[2] and idx[4]) and the
* corresponding adjacent vertices (idx[1] and idx[5]) to have
* the correct orientation
*/
if (i & 2) {
TRIANGLE_ADJ(flags,
idx[0], idx[5], idx[4], idx[3], idx[2], idx[1]);
}
else {
TRIANGLE_ADJ(flags,
idx[0], idx[1], idx[2], idx[3], idx[4], idx[5]);
}
}
}
}
break;
default:
assert(0);
break;
}
FUNC_EXIT;
}
#undef LOCAL_VARS
#undef FUNC_ENTER
#undef FUNC_EXIT
#undef LINE_ADJ
#undef TRIANGLE_ADJ
#undef FUNC
#undef FUNC_VARS
#undef GET_ELT
#undef POINT
#undef LINE
#undef TRIANGLE
+76
View File
@@ -0,0 +1,76 @@
/**************************************************************************
*
* 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.
*
**************************************************************************/
#include "pipe/p_shader_tokens.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/u_prim.h"
#include "nir/nir_to_tgsi_info.h"
#include "draw_fs.h"
#include "draw_private.h"
#include "draw_context.h"
struct draw_fragment_shader *
draw_create_fragment_shader(struct draw_context *draw,
const struct pipe_shader_state *shader)
{
struct draw_fragment_shader *dfs;
dfs = CALLOC_STRUCT(draw_fragment_shader);
if (dfs) {
dfs->base = *shader;
if (shader->type == PIPE_SHADER_IR_TGSI)
tgsi_scan_shader(shader->tokens, &dfs->info);
else
nir_tgsi_scan_shader(shader->ir.nir, &dfs->info, true);
}
return dfs;
}
void
draw_bind_fragment_shader(struct draw_context *draw,
struct draw_fragment_shader *dfs)
{
draw_do_flush(draw, DRAW_FLUSH_STATE_CHANGE);
draw->fs.fragment_shader = dfs;
}
void
draw_delete_fragment_shader(struct draw_context *draw,
struct draw_fragment_shader *dfs)
{
FREE(dfs);
}
+42
View File
@@ -0,0 +1,42 @@
/**************************************************************************
*
* 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.
*
**************************************************************************/
#ifndef DRAW_FS_H
#define DRAW_FS_H
#include "tgsi/tgsi_scan.h"
struct draw_fragment_shader
{
struct pipe_shader_state base;
struct tgsi_shader_info info;
};
#endif /* DRAW_FS_H */
File diff suppressed because it is too large. Load diff
+165
View File
@@ -0,0 +1,165 @@
/**************************************************************************
*
* Copyright 2009 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.
*
**************************************************************************/
#ifndef DRAW_GS_H
#define DRAW_GS_H
#include "draw_context.h"
#include "tgsi/tgsi_exec.h"
#include "tgsi/tgsi_scan.h"
#include "draw_private.h"
#define MAX_TGSI_PRIMITIVES 4
struct draw_context;
#if DRAW_LLVM_AVAILABLE
struct draw_gs_jit_context;
struct draw_gs_llvm_variant;
/**
* Structure holding the inputs to the geometry shader. It uses SOA layout.
* The dimensions are as follows:
* - maximum number of vertices for a geometry shader input primitive
* (6 for triangle_adjacency)
* - maximum number of attributes for each vertex
* - four channels per each attribute (x,y,z,w)
* - number of input primitives equal to the SOA vector length
*/
struct draw_gs_inputs {
float data[6][PIPE_MAX_SHADER_INPUTS][TGSI_NUM_CHANNELS][TGSI_NUM_CHANNELS];
};
#endif
/**
* Private version of the compiled geometry shader
*/
struct draw_vertex_stream {
unsigned *primitive_lengths;
unsigned emitted_vertices;
unsigned emitted_primitives;
float (*tmp_output)[4];
};
struct draw_geometry_shader {
struct draw_context *draw;
struct tgsi_exec_machine *machine;
/* This member will disappear shortly:*/
struct pipe_shader_state state;
struct tgsi_shader_info info;
unsigned position_output;
unsigned viewport_index_output;
unsigned clipvertex_output;
unsigned ccdistance_output[PIPE_MAX_CLIP_OR_CULL_DISTANCE_ELEMENT_COUNT];
unsigned max_output_vertices;
unsigned primitive_boundary;
enum mesa_prim input_primitive;
enum mesa_prim output_primitive;
unsigned vertex_size;
struct draw_vertex_stream stream[TGSI_MAX_VERTEX_STREAMS];
unsigned num_vertex_streams;
unsigned in_prim_idx;
unsigned tess_prim_idx;
const uint32_t *next_patch_length;
unsigned input_vertex_stride;
unsigned fetched_prim_count;
const float (*input)[4];
const struct tgsi_shader_info *input_info;
unsigned vector_length;
unsigned max_out_prims;
unsigned num_invocations;
unsigned invocation_id;
#if DRAW_LLVM_AVAILABLE
struct draw_gs_inputs *gs_input;
struct draw_gs_jit_context *jit_context;
struct lp_jit_resources *jit_resources;
struct draw_gs_llvm_variant *current_variant;
struct vertex_header *gs_output[PIPE_MAX_VERTEX_STREAMS];
int **llvm_prim_lengths;
int *llvm_emitted_primitives;
int *llvm_emitted_vertices;
int *llvm_prim_ids;
#endif
void (*fetch_inputs)(struct draw_geometry_shader *shader,
unsigned *indices,
unsigned num_vertices,
unsigned prim_idx);
void (*fetch_outputs)(struct draw_geometry_shader *shader,
unsigned vertex_stream,
unsigned num_primitives,
float (**p_output)[4]);
void (*prepare)(struct draw_geometry_shader *shader,
const struct draw_buffer_info *constants);
void (*run)(struct draw_geometry_shader *shader,
unsigned input_primitives, unsigned *out_prims);
};
void
draw_geometry_shader_new_instance(struct draw_geometry_shader *gs);
/*
* Returns the number of vertices emitted.
* The vertex shader can emit any number of vertices as long as it's
* smaller than the GS_MAX_OUTPUT_VERTICES shader property.
*/
void
draw_geometry_shader_run(struct draw_geometry_shader *shader,
const struct draw_buffer_info *constants,
const struct draw_vertex_info *input_verts,
const struct draw_prim_info *input_prim,
const struct tgsi_shader_info *input_info,
uint32_t *const *patch_lengths,
struct draw_vertex_info *output_verts,
struct draw_prim_info *output_prims);
void
draw_geometry_shader_prepare(struct draw_geometry_shader *shader,
struct draw_context *draw);
int
draw_gs_max_output_vertices(struct draw_geometry_shader *shader,
unsigned pipe_prim);
#if DRAW_LLVM_AVAILABLE
void
draw_gs_set_current_variant(struct draw_geometry_shader *shader,
struct draw_gs_llvm_variant *variant);
#endif
#endif
+32
View File
@@ -0,0 +1,32 @@
#define FUNC_VARS struct draw_geometry_shader *gs, \
const struct draw_prim_info *input_prims, \
const struct draw_vertex_info *input_verts, \
struct draw_prim_info *output_prims, \
struct draw_vertex_info *output_verts
#define FUNC_ENTER \
/* declare more local vars */ \
const unsigned prim = input_prims->prim; \
const unsigned prim_flags = input_prims->flags; \
const unsigned count = input_prims->count; \
const bool quads_flatshade_last = false; \
const bool last_vertex_last = !gs->draw->rasterizer->flatshade_first; \
do { \
switch (prim) { \
case MESA_PRIM_QUADS: \
case MESA_PRIM_QUAD_STRIP: \
case MESA_PRIM_POLYGON: \
assert(!"unexpected primitive type in GS"); \
return; \
default: \
break; \
} \
} while (0)
#define POINT(i0) gs_point(gs,i0)
#define LINE(flags,i0,i1) gs_line(gs,i0,i1)
#define TRIANGLE(flags,i0,i1,i2) gs_tri(gs,i0,i1,i2)
#define LINE_ADJ(flags,i0,i1,i2,i3) gs_line_adj(gs,i0,i1,i2,i3)
#define TRIANGLE_ADJ(flags,i0,i1,i2,i3,i4,i5) gs_tri_adj(gs,i0,i1,i2,i3,i4,i5)
#define QUAD(flags,i0,i1,i2,i3)
#include "draw_decompose_tmp.h"
File diff suppressed because it is too large. Load diff
+693
View File
@@ -0,0 +1,693 @@
/**************************************************************************
*
* 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.
*
**************************************************************************/
#ifndef DRAW_LLVM_H
#define DRAW_LLVM_H
#include "draw/draw_private.h"
#include "draw/draw_vs.h"
#include "draw/draw_gs.h"
#include "draw/draw_tess.h"
#include "gallivm/lp_bld.h"
#include "gallivm/lp_bld_sample.h"
#include "gallivm/lp_bld_jit_types.h"
#include "gallivm/lp_bld_jit_sample.h"
#include "pipe/p_context.h"
#include "util/list.h"
struct draw_llvm;
struct llvm_vertex_shader;
struct llvm_geometry_shader;
struct llvm_tess_ctrl_shader;
struct llvm_tess_eval_shader;
/**
* This structure is passed directly to the generated vertex shader.
*
* It contains the derived state.
*
* Changes here must be reflected in the draw_jit_context_* macros.
* Changes to the ordering should be avoided.
*
* Only use types with a clear size and padding here, in particular prefer the
* stdint.h types to the basic integer types.
*/
struct draw_vs_jit_context
{
float (*planes) [DRAW_TOTAL_CLIP_PLANES][4];
struct pipe_viewport_state *viewports;
};
enum {
DRAW_VS_JIT_CTX_PLANES = 0,
DRAW_VS_JIT_CTX_VIEWPORT = 1,
DRAW_VS_JIT_CTX_NUM_FIELDS
};
#define draw_vs_jit_context_planes(_gallivm, _type, _ptr) \
lp_build_struct_get2(_gallivm, _type, _ptr, DRAW_VS_JIT_CTX_PLANES, "planes")
#define draw_vs_jit_context_viewports(_variant, _ptr) \
lp_build_struct_get2(_variant->gallivm, _variant->context_type, _ptr, DRAW_VS_JIT_CTX_VIEWPORT, "viewports")
#define draw_jit_vbuffer_offset(_gallivm, _type, _ptr) \
lp_build_struct_get2(_gallivm, _type, _ptr, 1, "buffer_offset")
enum {
DRAW_JIT_DVBUFFER_MAP = 0,
DRAW_JIT_DVBUFFER_SIZE,
DRAW_JIT_DVBUFFER_NUM_FIELDS /* number of fields above */
};
#define draw_jit_dvbuffer_map(_gallivm, _type, _ptr) \
lp_build_struct_get2(_gallivm, _type, _ptr, DRAW_JIT_DVBUFFER_MAP, "map")
#define draw_jit_dvbuffer_size(_gallivm, _type, _ptr) \
lp_build_struct_get2(_gallivm, _type, _ptr, DRAW_JIT_DVBUFFER_SIZE, "size")
/**
* This structure is passed directly to the generated geometry shader.
*
* It contains the derived state.
*
* Changes here must be reflected in the draw_gs_jit_context_* macros.
* Changes to the ordering should be avoided.
*
* Only use types with a clear size and padding here, in particular prefer the
* stdint.h types to the basic integer types.
*/
struct draw_gs_jit_context
{
float (*planes) [DRAW_TOTAL_CLIP_PLANES][4];
struct pipe_viewport_state *viewports;
int **prim_lengths;
int *emitted_vertices;
int *emitted_prims;
};
enum {
DRAW_GS_JIT_CTX_PLANES = 0,
DRAW_GS_JIT_CTX_VIEWPORT = 1,
DRAW_GS_JIT_CTX_PRIM_LENGTHS = 2,
DRAW_GS_JIT_CTX_EMITTED_VERTICES = 3,
DRAW_GS_JIT_CTX_EMITTED_PRIMS = 4,
DRAW_GS_JIT_CTX_NUM_FIELDS = 5
};
#define draw_gs_jit_context_planes(_gallivm, _ptr) \
lp_build_struct_get(_gallivm, _ptr, DRAW_GS_JIT_CTX_PLANES, "planes")
#define draw_gs_jit_context_viewports(_gallivm, _ptr) \
lp_build_struct_get(_gallivm, _ptr, DRAW_GS_JIT_CTX_VIEWPORT, "viewports")
#define draw_gs_jit_prim_lengths(_variant, _ptr) \
lp_build_struct_get2(_variant->gallivm, _variant->context_type, _ptr, DRAW_GS_JIT_CTX_PRIM_LENGTHS, "prim_lengths")
#define draw_gs_jit_emitted_vertices(_variant, _ptr) \
lp_build_struct_get2(_variant->gallivm, _variant->context_type, _ptr, DRAW_GS_JIT_CTX_EMITTED_VERTICES, "emitted_vertices")
#define draw_gs_jit_emitted_prims(_variant, _ptr) \
lp_build_struct_get2(_variant->gallivm, _variant->context_type, _ptr, DRAW_GS_JIT_CTX_EMITTED_PRIMS, "emitted_prims")
typedef bool
(*draw_jit_vert_func)(struct draw_vs_jit_context *context,
const struct lp_jit_resources *resources,
struct vertex_header *io,
const struct draw_vertex_buffer vbuffers[PIPE_MAX_ATTRIBS],
unsigned count,
unsigned start,
unsigned stride,
struct pipe_vertex_buffer *vertex_buffers,
unsigned instance_id,
unsigned vertex_id_offset,
unsigned start_instance,
const unsigned *fetch_elts,
unsigned draw_id, unsigned view_id);
typedef int
(*draw_gs_jit_func)(struct draw_gs_jit_context *context,
const struct lp_jit_resources *resources,
float inputs[6][PIPE_MAX_SHADER_INPUTS][TGSI_NUM_CHANNELS][TGSI_NUM_CHANNELS],
struct vertex_header **output,
unsigned num_prims,
unsigned instance_id,
int *prim_ids,
unsigned invocation_id,
unsigned view_id);
typedef int
(*draw_tcs_jit_func)(const struct lp_jit_resources *resources,
float inputs[32][NUM_TCS_INPUTS][TGSI_NUM_CHANNELS],
float outputs[32][PIPE_MAX_SHADER_INPUTS][TGSI_NUM_CHANNELS],
uint32_t prim_id, uint32_t patch_vertices_in,
unsigned view_id);
typedef int
(*draw_tes_jit_func)(const struct lp_jit_resources *resources,
float inputs[32][PIPE_MAX_SHADER_INPUTS][TGSI_NUM_CHANNELS],
struct vertex_header *io,
uint32_t prim_id, uint32_t num_tess_coord,
float *tess_coord_x, float *tess_coord_y, float *tess_outer,
float *tess_inner, uint32_t patch_vertices_in,
unsigned view_id);
struct draw_llvm_variant_key
{
unsigned nr_vertex_elements:8;
unsigned nr_samplers:8;
unsigned nr_sampler_views:8;
unsigned nr_images:8;
unsigned clamp_vertex_color:1;
unsigned clip_xy:1;
unsigned clip_z:1;
unsigned clip_user:1;
unsigned clip_halfz:1;
unsigned bypass_viewport:1;
unsigned need_edgeflags:1;
unsigned has_gs_or_tes:1;
unsigned num_outputs:8;
unsigned ucp_enable:PIPE_MAX_CLIP_PLANES;
/* note padding here - must use memset */
/* Variable number of vertex elements:
*/
struct pipe_vertex_element vertex_element[1];
/* Followed by variable number of samplers:
*/
/* struct draw_sampler_static_state sampler; */
/* Followed by variable number of images
*/
};
struct draw_gs_llvm_variant_key
{
unsigned nr_samplers:8;
unsigned nr_sampler_views:8;
unsigned nr_images:8;
unsigned num_outputs:8;
/* note padding here - must use memset */
unsigned clamp_vertex_color:1;
struct lp_sampler_static_state samplers[1];
/* Followed by variable number of images.*/
};
struct draw_tcs_llvm_variant_key
{
unsigned nr_samplers:8;
unsigned nr_sampler_views:8;
unsigned nr_images:8;
struct lp_sampler_static_state samplers[1];
/* Followed by variable number of images.*/
};
struct draw_tes_llvm_variant_key
{
unsigned nr_samplers:8;
unsigned nr_sampler_views:8;
unsigned nr_images:8;
unsigned primid_output:7;
unsigned primid_needed:1;
unsigned clamp_vertex_color:1;
struct lp_sampler_static_state samplers[1];
/* Followed by variable number of images.*/
};
#define DRAW_LLVM_MAX_VARIANT_KEY_SIZE \
(sizeof(struct draw_llvm_variant_key) + \
PIPE_MAX_SHADER_SAMPLER_VIEWS * sizeof(struct lp_sampler_static_state) + \
PIPE_MAX_SHADER_IMAGES * sizeof(struct lp_image_static_state) + \
(PIPE_MAX_ATTRIBS-1) * sizeof(struct pipe_vertex_element))
#define DRAW_GS_LLVM_MAX_VARIANT_KEY_SIZE \
(sizeof(struct draw_gs_llvm_variant_key) + \
PIPE_MAX_SHADER_IMAGES * sizeof(struct lp_image_static_state) + \
PIPE_MAX_SHADER_SAMPLER_VIEWS * sizeof(struct lp_sampler_static_state))
#define DRAW_TCS_LLVM_MAX_VARIANT_KEY_SIZE \
(sizeof(struct draw_tcs_llvm_variant_key) + \
PIPE_MAX_SHADER_IMAGES * sizeof(struct lp_image_static_state) + \
PIPE_MAX_SHADER_SAMPLER_VIEWS * sizeof(struct lp_sampler_static_state))
#define DRAW_TES_LLVM_MAX_VARIANT_KEY_SIZE \
(sizeof(struct draw_tes_llvm_variant_key) + \
PIPE_MAX_SHADER_IMAGES * sizeof(struct lp_image_static_state) + \
PIPE_MAX_SHADER_SAMPLER_VIEWS * sizeof(struct lp_sampler_static_state))
static inline size_t
draw_llvm_variant_key_size(unsigned nr_vertex_elements,
unsigned nr_samplers,
unsigned nr_sampler_views,
unsigned nr_images)
{
return (sizeof(struct draw_llvm_variant_key) +
(nr_vertex_elements - 1) * sizeof(struct pipe_vertex_element) +
MAX2(nr_samplers, nr_sampler_views) *
sizeof(struct lp_sampler_static_state) +
nr_images * sizeof(struct lp_image_static_state));
}
static inline size_t
draw_gs_llvm_variant_key_size(unsigned nr_samplers,
unsigned nr_sampler_views,
unsigned nr_images)
{
return (sizeof(struct draw_gs_llvm_variant_key) +
(MAX2(nr_samplers, nr_sampler_views) - 1) *
sizeof(struct lp_sampler_static_state) +
nr_images * sizeof(struct lp_sampler_static_state));
}
static inline size_t
draw_tcs_llvm_variant_key_size(unsigned nr_samplers,
unsigned nr_sampler_views,
unsigned nr_images)
{
return (sizeof(struct draw_tcs_llvm_variant_key) +
(MAX2(nr_samplers, nr_sampler_views) - 1) *
sizeof(struct lp_sampler_static_state) +
nr_images * sizeof(struct lp_sampler_static_state));
}
static inline size_t
draw_tes_llvm_variant_key_size(unsigned nr_samplers,
unsigned nr_sampler_views,
unsigned nr_images)
{
return (sizeof(struct draw_tes_llvm_variant_key) +
(MAX2(nr_samplers, nr_sampler_views) - 1) *
sizeof(struct lp_sampler_static_state) +
nr_images * sizeof(struct lp_sampler_static_state));
}
static inline struct lp_sampler_static_state *
draw_llvm_variant_key_samplers(struct draw_llvm_variant_key *key)
{
return (struct lp_sampler_static_state *)
&key->vertex_element[key->nr_vertex_elements];
}
static inline struct lp_image_static_state *
draw_llvm_variant_key_images(struct draw_llvm_variant_key *key)
{
struct lp_sampler_static_state *samplers = (struct lp_sampler_static_state *)
(&key->vertex_element[key->nr_vertex_elements]);
return (struct lp_image_static_state *)
&samplers[MAX2(key->nr_samplers, key->nr_sampler_views)];
}
static inline struct lp_image_static_state *
draw_gs_llvm_variant_key_images(struct draw_gs_llvm_variant_key *key)
{
return (struct lp_image_static_state *)
&key->samplers[MAX2(key->nr_samplers, key->nr_sampler_views)];
}
static inline struct lp_image_static_state *
draw_tcs_llvm_variant_key_images(struct draw_tcs_llvm_variant_key *key)
{
return (struct lp_image_static_state *)
&key->samplers[MAX2(key->nr_samplers, key->nr_sampler_views)];
}
static inline struct lp_image_static_state *
draw_tes_llvm_variant_key_images(struct draw_tes_llvm_variant_key *key)
{
return (struct lp_image_static_state *)
&key->samplers[MAX2(key->nr_samplers, key->nr_sampler_views)];
}
struct draw_llvm_variant_list_item
{
struct list_head list;
struct draw_llvm_variant *base;
};
struct draw_gs_llvm_variant_list_item
{
struct list_head list;
struct draw_gs_llvm_variant *base;
};
struct draw_tcs_llvm_variant_list_item
{
struct list_head list;
struct draw_tcs_llvm_variant *base;
};
struct draw_tes_llvm_variant_list_item
{
struct list_head list;
struct draw_tes_llvm_variant *base;
};
struct draw_llvm_variant
{
struct gallivm_state *gallivm;
/* LLVM JIT builder types */
LLVMTypeRef context_type;
LLVMTypeRef context_ptr_type;
LLVMTypeRef resources_type;
LLVMTypeRef resources_ptr_type;
LLVMTypeRef buffer_type;
LLVMTypeRef buffer_ptr_type;
LLVMTypeRef vb_type;
LLVMTypeRef vb_ptr_type;
LLVMTypeRef vertex_header_type;
LLVMTypeRef vertex_header_ptr_type;
LLVMValueRef function;
char *function_name;
draw_jit_vert_func jit_func;
struct llvm_vertex_shader *shader;
struct draw_llvm *llvm;
struct draw_llvm_variant_list_item list_item_global;
struct draw_llvm_variant_list_item list_item_local;
/* key is variable-sized, must be last */
struct draw_llvm_variant_key key;
};
struct draw_gs_llvm_variant
{
struct gallivm_state *gallivm;
/* LLVM JIT builder types */
LLVMTypeRef context_type;
LLVMTypeRef context_ptr_type;
LLVMTypeRef resources_type;
LLVMTypeRef resources_ptr_type;
LLVMTypeRef vertex_header_type;
LLVMTypeRef vertex_header_ptr_type;
LLVMTypeRef input_array_type;
LLVMValueRef context_ptr;
LLVMValueRef io_ptr;
LLVMValueRef num_prims;
LLVMValueRef function;
char *function_name;
draw_gs_jit_func jit_func;
struct llvm_geometry_shader *shader;
struct draw_llvm *llvm;
struct draw_gs_llvm_variant_list_item list_item_global;
struct draw_gs_llvm_variant_list_item list_item_local;
/* key is variable-sized, must be last */
struct draw_gs_llvm_variant_key key;
};
struct draw_tcs_llvm_variant
{
struct gallivm_state *gallivm;
/* LLVM JIT builder types */
LLVMTypeRef resources_type;
LLVMTypeRef resources_ptr_type;
LLVMTypeRef input_array_type;
LLVMTypeRef output_array_type;
LLVMValueRef context_ptr;
/* LLVMValueRef io_ptr; */
LLVMValueRef num_prims;
LLVMValueRef function;
char *function_name;
draw_tcs_jit_func jit_func;
struct llvm_tess_ctrl_shader *shader;
struct draw_llvm *llvm;
struct draw_tcs_llvm_variant_list_item list_item_global;
struct draw_tcs_llvm_variant_list_item list_item_local;
/* key is variable-sized, must be last */
struct draw_tcs_llvm_variant_key key;
};
struct draw_tes_llvm_variant
{
struct gallivm_state *gallivm;
/* LLVM JIT builder types */
LLVMTypeRef resources_type;
LLVMTypeRef resources_ptr_type;
LLVMTypeRef vertex_header_ptr_type;
LLVMTypeRef input_array_type;
LLVMTypeRef patch_input_array_type;
LLVMTypeRef input_array_deref_type;
LLVMTypeRef vertex_header_type;
LLVMValueRef context_ptr;
LLVMValueRef io_ptr;
LLVMValueRef num_prims;
LLVMValueRef function;
char *function_name;
draw_tes_jit_func jit_func;
struct llvm_tess_eval_shader *shader;
struct draw_llvm *llvm;
struct draw_tes_llvm_variant_list_item list_item_global;
struct draw_tes_llvm_variant_list_item list_item_local;
/* key is variable-sized, must be last */
struct draw_tes_llvm_variant_key key;
};
struct llvm_vertex_shader {
struct draw_vertex_shader base;
unsigned variant_key_size;
struct draw_llvm_variant_list_item variants;
unsigned variants_created;
unsigned variants_cached;
};
struct llvm_geometry_shader {
struct draw_geometry_shader base;
unsigned variant_key_size;
struct draw_gs_llvm_variant_list_item variants;
unsigned variants_created;
unsigned variants_cached;
};
struct llvm_tess_ctrl_shader {
struct draw_tess_ctrl_shader base;
unsigned variant_key_size;
struct draw_tcs_llvm_variant_list_item variants;
unsigned variants_created;
unsigned variants_cached;
};
struct llvm_tess_eval_shader {
struct draw_tess_eval_shader base;
unsigned variant_key_size;
struct draw_tes_llvm_variant_list_item variants;
unsigned variants_created;
unsigned variants_cached;
};
struct draw_llvm {
struct draw_context *draw;
lp_context_ref context;
struct draw_vs_jit_context vs_jit_context;
struct draw_gs_jit_context gs_jit_context;
struct lp_jit_resources jit_resources[DRAW_MAX_SHADER_STAGE];
struct draw_llvm_variant_list_item vs_variants_list;
int nr_variants;
struct draw_gs_llvm_variant_list_item gs_variants_list;
int nr_gs_variants;
struct draw_tcs_llvm_variant_list_item tcs_variants_list;
int nr_tcs_variants;
struct draw_tes_llvm_variant_list_item tes_variants_list;
int nr_tes_variants;
};
static inline struct llvm_vertex_shader *
llvm_vertex_shader(struct draw_vertex_shader *vs)
{
return (struct llvm_vertex_shader *)vs;
}
static inline struct llvm_geometry_shader *
llvm_geometry_shader(struct draw_geometry_shader *gs)
{
return (struct llvm_geometry_shader *)gs;
}
static inline struct llvm_tess_ctrl_shader *
llvm_tess_ctrl_shader(struct draw_tess_ctrl_shader *tcs)
{
return (struct llvm_tess_ctrl_shader *)tcs;
}
static inline struct llvm_tess_eval_shader *
llvm_tess_eval_shader(struct draw_tess_eval_shader *tes)
{
return (struct llvm_tess_eval_shader *)tes;
}
struct draw_llvm *
draw_llvm_create(struct draw_context *draw, lp_context_ref *llvm_context);
void
draw_llvm_destroy(struct draw_llvm *llvm);
struct draw_llvm_variant *
draw_llvm_create_variant(struct draw_llvm *llvm,
unsigned num_vertex_header_attribs,
const struct draw_llvm_variant_key *key);
void
draw_llvm_destroy_variant(struct draw_llvm_variant *variant);
struct draw_llvm_variant_key *
draw_llvm_make_variant_key(struct draw_llvm *llvm, char *store);
void
draw_llvm_dump_variant_key(struct draw_llvm_variant_key *key);
struct draw_gs_llvm_variant *
draw_gs_llvm_create_variant(struct draw_llvm *llvm,
unsigned num_vertex_header_attribs,
const struct draw_gs_llvm_variant_key *key);
void
draw_gs_llvm_destroy_variant(struct draw_gs_llvm_variant *variant);
struct draw_gs_llvm_variant_key *
draw_gs_llvm_make_variant_key(struct draw_llvm *llvm, char *store);
void
draw_gs_llvm_dump_variant_key(struct draw_gs_llvm_variant_key *key);
struct draw_tcs_llvm_variant *
draw_tcs_llvm_create_variant(struct draw_llvm *llvm,
unsigned num_vertex_header_attribs,
const struct draw_tcs_llvm_variant_key *key);
void
draw_tcs_llvm_destroy_variant(struct draw_tcs_llvm_variant *variant);
struct draw_tcs_llvm_variant_key *
draw_tcs_llvm_make_variant_key(struct draw_llvm *llvm, char *store);
void
draw_tcs_llvm_dump_variant_key(struct draw_tcs_llvm_variant_key *key);
struct draw_tes_llvm_variant *
draw_tes_llvm_create_variant(struct draw_llvm *llvm,
unsigned num_vertex_header_attribs,
const struct draw_tes_llvm_variant_key *key);
void
draw_tes_llvm_destroy_variant(struct draw_tes_llvm_variant *variant);
struct draw_tes_llvm_variant_key *
draw_tes_llvm_make_variant_key(struct draw_llvm *llvm, char *store);
void
draw_tes_llvm_dump_variant_key(struct draw_tes_llvm_variant_key *key);
void
draw_llvm_set_sampler_state(struct draw_context *draw,
mesa_shader_stage shader_stage);
void
draw_llvm_set_mapped_texture(struct draw_context *draw,
mesa_shader_stage shader_stage,
unsigned sview_idx,
uint32_t width, uint32_t height, uint32_t depth,
uint32_t first_level, uint32_t last_level,
uint32_t num_samples,
uint32_t sample_stride,
const void *base_ptr,
uint32_t row_stride[PIPE_MAX_TEXTURE_LEVELS],
uint32_t img_stride[PIPE_MAX_TEXTURE_LEVELS],
uint32_t mip_offsets[PIPE_MAX_TEXTURE_LEVELS]);
void
draw_llvm_set_mapped_image(struct draw_context *draw,
mesa_shader_stage shader_stage,
unsigned idx,
uint32_t width, uint32_t height, uint32_t depth,
const void *base_ptr,
uint32_t row_stride,
uint32_t img_stride,
uint32_t num_samples,
uint32_t sample_stride);
void
draw_store_aos_array(struct gallivm_state *gallivm,
struct lp_type soa_type,
LLVMTypeRef io_type,
LLVMValueRef io_ptr,
LLVMValueRef *indices,
LLVMValueRef* aos,
int attrib,
LLVMValueRef clipmask,
bool need_edgeflag,
bool per_prim);
#endif
+101
View File
@@ -0,0 +1,101 @@
/**************************************************************************
*
* Copyright 2023 Red Hat.
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
*
**************************************************************************/
#include "draw_mesh.h"
#include "draw_context.h"
#include "tgsi/tgsi_scan.h"
#include "nir/nir_to_tgsi_info.h"
#include "pipe/p_shader_tokens.h"
struct draw_mesh_shader *
draw_create_mesh_shader(struct draw_context *draw,
const struct pipe_shader_state *state)
{
struct draw_mesh_shader *ms;
ms = CALLOC_STRUCT(draw_mesh_shader);
if (!ms)
return NULL;
ms->draw = draw;
nir_tgsi_scan_shader(state->ir.nir, &ms->info, true);
nir_shader *nir = state->ir.nir;
ms->output_primitive = nir->info.mesh.primitive_type;
ms->position_output = -1;
bool found_clipvertex = false;
for (unsigned i = 0; i < ms->info.num_outputs; i++) {
if (ms->info.output_semantic_name[i] == TGSI_SEMANTIC_POSITION &&
ms->info.output_semantic_index[i] == 0)
ms->position_output = i;
if (ms->info.output_semantic_name[i] == TGSI_SEMANTIC_VIEWPORT_INDEX)
ms->viewport_index_output = i;
if (ms->info.output_semantic_name[i] == TGSI_SEMANTIC_CLIPVERTEX &&
ms->info.output_semantic_index[i] == 0) {
found_clipvertex = true;
ms->clipvertex_output = i;
}
if (ms->info.output_semantic_name[i] == TGSI_SEMANTIC_CLIPDIST) {
assert(ms->info.output_semantic_index[i] <
PIPE_MAX_CLIP_OR_CULL_DISTANCE_ELEMENT_COUNT);
ms->ccdistance_output[ms->info.output_semantic_index[i]] = i;
}
}
if (!found_clipvertex)
ms->clipvertex_output = ms->position_output;
return ms;
}
void
draw_bind_mesh_shader(struct draw_context *draw,
struct draw_mesh_shader *dms)
{
draw_do_flush(draw, DRAW_FLUSH_STATE_CHANGE);
if (dms) {
draw->ms.mesh_shader = dms;
draw->ms.num_ms_outputs = dms->info.num_outputs;
draw->ms.position_output = dms->position_output;
draw->ms.clipvertex_output = dms->clipvertex_output;
} else {
draw->ms.mesh_shader = NULL;
draw->ms.num_ms_outputs = 0;
}
}
void
draw_delete_mesh_shader(struct draw_context *draw,
struct draw_mesh_shader *dms)
{
if (!dms)
return;
FREE(dms);
}
+48
View File
@@ -0,0 +1,48 @@
/**************************************************************************
*
* Copyright 2023 Red Hat.
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
*
**************************************************************************/
#ifndef DRAW_MESH_H
#define DRAW_MESH_H
#include "draw_context.h"
#include "draw_private.h"
#include "tgsi/tgsi_exec.h"
#include "tgsi/tgsi_scan.h"
struct draw_context;
struct draw_mesh_shader {
struct draw_context *draw;
struct tgsi_shader_info info;
unsigned position_output;
unsigned viewport_index_output;
unsigned clipvertex_output;
unsigned ccdistance_output[PIPE_MAX_CLIP_OR_CULL_DISTANCE_ELEMENT_COUNT];
unsigned output_primitive;
};
#endif
+229
View File
@@ -0,0 +1,229 @@
/**************************************************************************
*
* Copyright 2023 Red Hat.
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
*
**************************************************************************/
/*
* Mesh primitive assembler
*
* This takes vertex and per-primitive data, and assembles a linear set
* of draw compatible vertices.
*/
#include "draw_mesh_prim.h"
#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "util/u_debug.h"
#include "util/u_memory.h"
#include "pipe/p_defines.h"
struct draw_mesh_prim
{
struct draw_context *draw;
struct draw_prim_info *output_prims;
struct draw_vertex_info *output_verts;
const struct draw_prim_info *input_prims;
const struct draw_vertex_info *input_verts;
unsigned num_prims;
const char *per_prim;
uint32_t num_per_prim;
uint32_t added_prim_size;
int cull_prim_idx;
};
static void
add_prim(struct draw_mesh_prim *asmblr, unsigned length)
{
struct draw_prim_info *output_prims = asmblr->output_prims;
output_prims->primitive_lengths = realloc(output_prims->primitive_lengths, sizeof(unsigned) * (output_prims->primitive_count + 1));
output_prims->primitive_lengths[output_prims->primitive_count] = length;
output_prims->primitive_count++;
}
/*
* Copy the vertex header along with its data from the current
* vertex buffer into a buffer holding vertices arranged
* into decomposed primitives (i.e. buffer without the
* adjacency vertices)
*/
static void
copy_verts(struct draw_mesh_prim *asmblr,
unsigned *indices, unsigned num_indices)
{
char *output = (char*)asmblr->output_verts->verts;
const char *input = (const char*)asmblr->input_verts->verts;
for (unsigned i = 0; i < num_indices; ++i) {
unsigned idx = indices[i];
unsigned output_offset =
asmblr->output_verts->count * asmblr->output_verts->stride;
unsigned input_offset = asmblr->input_verts->stride * idx;
memcpy(output + output_offset, input + input_offset,
asmblr->input_verts->vertex_size);
memcpy(output + output_offset + asmblr->input_verts->vertex_size,
asmblr->per_prim + (asmblr->num_prims * asmblr->added_prim_size * 8),
asmblr->added_prim_size);
asmblr->output_verts->count += 1;
}
++asmblr->num_prims;
}
static bool
cull_prim(struct draw_mesh_prim *asmblr)
{
if (asmblr->cull_prim_idx == -1)
return false;
const uint32_t *cull_prim_ptr = (uint32_t *)(asmblr->per_prim + (asmblr->num_prims * asmblr->added_prim_size * 8));
cull_prim_ptr += (asmblr->cull_prim_idx * 4);
return (*cull_prim_ptr) ? true : false;
}
static void
prim_point(struct draw_mesh_prim *asmblr,
unsigned idx)
{
unsigned indices[1];
indices[0] = idx;
if (cull_prim(asmblr)) {
++asmblr->num_prims;
return;
}
add_prim(asmblr, 1);
copy_verts(asmblr, indices, 1);
}
static void
prim_line(struct draw_mesh_prim *asmblr,
unsigned i0, unsigned i1)
{
unsigned indices[2];
indices[0] = i0;
indices[1] = i1;
if (cull_prim(asmblr)) {
++asmblr->num_prims;
return;
}
add_prim(asmblr, 2);
copy_verts(asmblr, indices, 2);
}
static void
prim_tri(struct draw_mesh_prim *asmblr,
unsigned i0, unsigned i1, unsigned i2)
{
unsigned indices[3];
indices[0] = i0;
indices[1] = i1;
indices[2] = i2;
if (cull_prim(asmblr)) {
++asmblr->num_prims;
return;
}
add_prim(asmblr, 3);
copy_verts(asmblr, indices, 3);
}
#define FUNC assembler_run_linear
#define GET_ELT(idx) (start + (idx))
#include "draw_mesh_prim_tmp.h"
#define FUNC assembler_run_elts
#define LOCAL_VARS const uint16_t *elts = input_prims->elts;
#define GET_ELT(idx) (elts[start + (idx)])
#include "draw_mesh_prim_tmp.h"
void
draw_mesh_prim_run(struct draw_context *draw,
unsigned num_per_prim_inputs,
void *per_prim_inputs,
int cull_prim_idx,
const struct draw_prim_info *input_prims,
const struct draw_vertex_info *input_verts,
struct draw_prim_info *output_prims,
struct draw_vertex_info *output_verts)
{
struct draw_mesh_prim asmblr_mesh;
struct draw_mesh_prim *asmblr = &asmblr_mesh;
unsigned start, i;
unsigned max_primitives = input_prims->primitive_count;
unsigned max_verts = mesa_vertices_per_prim(input_prims->prim) * max_primitives;
asmblr->output_prims = output_prims;
asmblr->output_verts = output_verts;
asmblr->input_prims = input_prims;
asmblr->input_verts = input_verts;
asmblr->num_prims = 0;
asmblr->num_per_prim = num_per_prim_inputs;
asmblr->per_prim = per_prim_inputs;
asmblr->cull_prim_idx = cull_prim_idx;
output_prims->linear = true;
output_prims->elts = NULL;
output_prims->start = 0;
output_prims->prim = input_prims->prim;
output_prims->flags = 0x0;
output_prims->primitive_lengths = MALLOC(sizeof(unsigned));
output_prims->primitive_lengths[0] = 0;
output_prims->primitive_count = 0;
asmblr->added_prim_size = asmblr->num_per_prim * (4 * sizeof(float));
output_verts->vertex_size = input_verts->vertex_size + asmblr->added_prim_size;
output_verts->stride = output_verts->vertex_size;
output_verts->verts = (struct vertex_header*)MALLOC(
output_verts->vertex_size * max_verts);
output_verts->count = 0;
for (start = i = 0; i < input_prims->primitive_count;
start += input_prims->primitive_lengths[i], i++) {
unsigned count = input_prims->primitive_lengths[i];
if (input_prims->linear) {
assembler_run_linear(asmblr, input_prims, input_verts,
start, count);
} else {
assembler_run_elts(asmblr, input_prims, input_verts,
start, count);
}
}
output_prims->count = output_verts->count;
}
@@ -0,0 +1,42 @@
/**************************************************************************
*
* Copyright 2023 Red Hat.
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
*
**************************************************************************/
#ifndef DRAW_MESH_PRIM_H
#define DRAW_MESH_PRIM_H
struct draw_context;
struct draw_prim_info;
struct draw_vertex_info;
void
draw_mesh_prim_run(struct draw_context *draw,
unsigned num_per_prim_inputs,
void *per_prim_inputs,
int cull_prim_idx,
const struct draw_prim_info *in_prim_info,
const struct draw_vertex_info *in_vert_info,
struct draw_prim_info *out_prim_info,
struct draw_vertex_info *out_vert_info);
#endif
@@ -0,0 +1,30 @@
#define FUNC_VARS \
struct draw_mesh_prim *asmblr, \
const struct draw_prim_info *input_prims, \
const struct draw_vertex_info *input_verts, \
unsigned start, \
unsigned count
#define FUNC_ENTER \
/* declare more local vars */ \
const enum mesa_prim prim = input_prims->prim; \
const unsigned prim_flags = input_prims->flags; \
const bool last_vertex_last = false; \
switch (prim) { \
case MESA_PRIM_POINTS: \
case MESA_PRIM_LINES: \
case MESA_PRIM_TRIANGLES: \
break; \
default: \
assert(!"unexpected primitive type in prim assembler"); \
return; \
}
#define POINT(i0) prim_point(asmblr, i0)
#define LINE(flags, i0, i1) prim_line(asmblr, i0, i1)
#define TRIANGLE(flags, i0, i1, i2) prim_tri(asmblr, i0, i1, i2)
#define QUAD(flags, i0, i1, i2, i3)
#define PASS_QUADS
#include "draw_decompose_tmp.h"
+357
View File
@@ -0,0 +1,357 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Authors:
* Keith Whitwell <[email protected]>
*/
#include "draw/draw_private.h"
#include "draw/draw_pipe.h"
#include "util/u_debug.h"
#include "util/u_math.h"
bool
draw_pipeline_init(struct draw_context *draw)
{
/* create pipeline stages */
draw->pipeline.wide_line = draw_wide_line_stage(draw);
draw->pipeline.wide_point = draw_wide_point_stage(draw);
draw->pipeline.stipple = draw_stipple_stage(draw);
draw->pipeline.unfilled = draw_unfilled_stage(draw);
draw->pipeline.twoside = draw_twoside_stage(draw);
draw->pipeline.offset = draw_offset_stage(draw);
draw->pipeline.clip = draw_clip_stage(draw);
draw->pipeline.flatshade = draw_flatshade_stage(draw);
draw->pipeline.cull = draw_cull_stage(draw);
draw->pipeline.user_cull = draw_user_cull_stage(draw);
draw->pipeline.validate = draw_validate_stage(draw);
draw->pipeline.first = draw->pipeline.validate;
if (!draw->pipeline.wide_line ||
!draw->pipeline.wide_point ||
!draw->pipeline.stipple ||
!draw->pipeline.unfilled ||
!draw->pipeline.twoside ||
!draw->pipeline.offset ||
!draw->pipeline.clip ||
!draw->pipeline.flatshade ||
!draw->pipeline.cull ||
!draw->pipeline.user_cull ||
!draw->pipeline.validate)
return false;
/* these defaults are oriented toward the needs of softpipe */
draw->pipeline.wide_point_threshold = 1000000.0f; /* infinity */
draw->pipeline.wide_line_threshold = 1.0f;
draw->pipeline.wide_point_sprites = false;
draw->pipeline.line_stipple = true;
draw->pipeline.point_sprite = true;
return true;
}
void
draw_pipeline_destroy(struct draw_context *draw)
{
if (draw->pipeline.wide_line)
draw->pipeline.wide_line->destroy(draw->pipeline.wide_line);
if (draw->pipeline.wide_point)
draw->pipeline.wide_point->destroy(draw->pipeline.wide_point);
if (draw->pipeline.stipple)
draw->pipeline.stipple->destroy(draw->pipeline.stipple);
if (draw->pipeline.unfilled)
draw->pipeline.unfilled->destroy(draw->pipeline.unfilled);
if (draw->pipeline.twoside)
draw->pipeline.twoside->destroy(draw->pipeline.twoside);
if (draw->pipeline.offset)
draw->pipeline.offset->destroy(draw->pipeline.offset);
if (draw->pipeline.clip)
draw->pipeline.clip->destroy(draw->pipeline.clip);
if (draw->pipeline.flatshade)
draw->pipeline.flatshade->destroy(draw->pipeline.flatshade);
if (draw->pipeline.cull)
draw->pipeline.cull->destroy(draw->pipeline.cull);
if (draw->pipeline.user_cull)
draw->pipeline.user_cull->destroy(draw->pipeline.user_cull);
if (draw->pipeline.validate)
draw->pipeline.validate->destroy(draw->pipeline.validate);
if (draw->pipeline.aaline)
draw->pipeline.aaline->destroy(draw->pipeline.aaline);
if (draw->pipeline.aapoint)
draw->pipeline.aapoint->destroy(draw->pipeline.aapoint);
if (draw->pipeline.pstipple)
draw->pipeline.pstipple->destroy(draw->pipeline.pstipple);
if (draw->pipeline.rasterize)
draw->pipeline.rasterize->destroy(draw->pipeline.rasterize);
}
/**
* Build primitive to render a point with vertex at v0.
*/
static void
do_point(struct draw_context *draw,
const char *v0)
{
struct prim_header prim;
prim.flags = 0;
prim.pad = 0;
prim.v[0] = (struct vertex_header *)v0;
draw->pipeline.first->point(draw->pipeline.first, &prim);
}
/**
* Build primitive to render a line with vertices at v0, v1.
* \param flags bitmask of DRAW_PIPE_EDGE_x, DRAW_PIPE_RESET_STIPPLE
*/
static void
do_line(struct draw_context *draw,
uint16_t flags,
const char *v0,
const char *v1)
{
struct prim_header prim;
prim.flags = flags;
prim.pad = 0;
prim.v[0] = (struct vertex_header *)v0;
prim.v[1] = (struct vertex_header *)v1;
draw->pipeline.first->line(draw->pipeline.first, &prim);
}
/**
* Build primitive to render a triangle with vertices at v0, v1, v2.
* \param flags bitmask of DRAW_PIPE_EDGE_x, DRAW_PIPE_RESET_STIPPLE
*/
static void
do_triangle(struct draw_context *draw,
uint16_t flags,
char *v0,
char *v1,
char *v2)
{
struct prim_header prim;
prim.v[0] = (struct vertex_header *)v0;
prim.v[1] = (struct vertex_header *)v1;
prim.v[2] = (struct vertex_header *)v2;
prim.flags = flags;
prim.pad = 0;
draw->pipeline.first->tri(draw->pipeline.first, &prim);
}
/*
* Set up macros for draw_pt_decompose.h template code.
* This code uses vertex indexes / elements.
*/
#define TRIANGLE(flags,i0,i1,i2) \
do { \
do_triangle(draw, \
flags, \
verts + stride * (i0), \
verts + stride * (i1), \
verts + stride * (i2)); \
} while (0)
#define LINE(flags,i0,i1) \
do { \
do_line(draw, \
flags, \
verts + stride * (i0), \
verts + stride * (i1)); \
} while (0)
#define POINT(i0) \
do { \
do_point(draw, verts + stride * (i0)); \
} while (0)
#define GET_ELT(idx) (MIN2(elts[idx], max_index))
#define FUNC pipe_run_elts
#define FUNC_VARS \
struct draw_context *draw, \
enum mesa_prim prim, \
unsigned prim_flags, \
struct vertex_header *vertices, \
unsigned stride, \
const uint16_t *elts, \
unsigned count, \
unsigned max_index
#include "draw_pt_decompose.h"
/**
* Code to run the pipeline on a fairly arbitrary collection of vertices.
* For drawing indexed primitives.
*
* Vertex headers must be pre-initialized with the
* UNDEFINED_VERTEX_ID, this code will cause that id to become
* overwritten, so it may have to be reset if there is the intention
* to reuse the vertices.
*
* This code provides a callback to reset the vertex id's which the
* draw_vbuf.c code uses when it has to perform a flush.
*/
void
draw_pipeline_run(struct draw_context *draw,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info)
{
draw->pipeline.verts = (char *)vert_info->verts;
draw->pipeline.vertex_stride = vert_info->stride;
draw->pipeline.vertex_count = vert_info->count;
unsigned i, start;
for (start = i = 0;
i < prim_info->primitive_count;
start += prim_info->primitive_lengths[i], i++) {
const unsigned count = prim_info->primitive_lengths[i];
#if MESA_DEBUG
/* Warn if one of the element indexes go outside the vertex buffer */
{
unsigned max_index = 0x0;
/* find the largest element index */
for (unsigned i = 0; i < count; i++) {
unsigned int index = prim_info->elts[start + i];
if (index > max_index)
max_index = index;
}
if (max_index >= vert_info->count) {
debug_printf("%s: max_index (%u) outside vertex buffer (%u)\n",
__func__,
max_index,
vert_info->count);
}
}
#endif
pipe_run_elts(draw,
prim_info->prim,
prim_info->flags,
vert_info->verts,
vert_info->stride,
prim_info->elts + start,
count,
vert_info->count - 1);
}
draw->pipeline.verts = NULL;
draw->pipeline.vertex_count = 0;
}
/*
* Set up macros for draw_pt_decompose.h template code.
* This code is for non-indexed (aka linear) rendering (no elts).
*/
#define TRIANGLE(flags,i0,i1,i2) \
do_triangle(draw, flags, \
verts + stride * (i0), \
verts + stride * (i1), \
verts + stride * (i2))
#define LINE(flags,i0,i1) \
do_line(draw, flags, \
verts + stride * (i0), \
verts + stride * (i1))
#define POINT(i0) \
do_point(draw, verts + stride * (i0))
#define GET_ELT(idx) (idx)
#define FUNC pipe_run_linear
#define FUNC_VARS \
struct draw_context *draw, \
enum mesa_prim prim, \
unsigned prim_flags, \
struct vertex_header *vertices, \
unsigned stride, \
unsigned count
#include "draw_pt_decompose.h"
/*
* For drawing non-indexed primitives.
*/
void
draw_pipeline_run_linear(struct draw_context *draw,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info)
{
for (unsigned start = 0, i = 0;
i < prim_info->primitive_count;
start += prim_info->primitive_lengths[i], i++) {
unsigned count = prim_info->primitive_lengths[i];
char *verts = ((char*)vert_info->verts) +
(start * vert_info->stride);
draw->pipeline.verts = verts;
draw->pipeline.vertex_stride = vert_info->stride;
draw->pipeline.vertex_count = count;
assert(count <= vert_info->count);
pipe_run_linear(draw,
prim_info->prim,
prim_info->flags,
(struct vertex_header*)verts,
vert_info->stride,
count);
}
draw->pipeline.verts = NULL;
draw->pipeline.vertex_count = 0;
}
void
draw_pipeline_flush(struct draw_context *draw,
unsigned flags)
{
draw->pipeline.first->flush(draw->pipeline.first, flags);
if (flags & DRAW_FLUSH_STATE_CHANGE)
draw->pipeline.first = draw->pipeline.validate;
}
+128
View File
@@ -0,0 +1,128 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Authors:
* Keith Whitwell <[email protected]>
*/
#ifndef DRAW_PIPE_H
#define DRAW_PIPE_H
#include "util/compiler.h"
#include "draw_private.h" /* for sizeof(vertex_header) */
#include "draw_context.h"
/**
* Basic info for a point/line/triangle primitive.
*/
struct prim_header {
float det; /**< front/back face determinant */
uint16_t flags;
uint16_t pad;
struct vertex_header *v[3]; /**< 1 to 3 vertex pointers */
};
/**
* Base class for all primitive drawing stages.
*/
struct draw_stage
{
struct draw_context *draw; /**< parent context */
struct draw_stage *next; /**< next stage in pipeline */
const char *name; /**< for debugging */
struct vertex_header **tmp; /**< temp vert storage, such as for clipping */
unsigned nr_tmps;
void (*point)(struct draw_stage *, struct prim_header *);
void (*line)(struct draw_stage *, struct prim_header *);
void (*tri)(struct draw_stage *, struct prim_header *);
void (*flush)(struct draw_stage *, unsigned flags);
void (*reset_stipple_counter)(struct draw_stage *);
void (*destroy)(struct draw_stage *);
};
struct draw_stage *draw_unfilled_stage(struct draw_context *context);
struct draw_stage *draw_twoside_stage(struct draw_context *context);
struct draw_stage *draw_offset_stage(struct draw_context *context);
struct draw_stage *draw_clip_stage(struct draw_context *context);
struct draw_stage *draw_flatshade_stage(struct draw_context *context);
struct draw_stage *draw_cull_stage(struct draw_context *context);
struct draw_stage *draw_user_cull_stage(struct draw_context *draw);
struct draw_stage *draw_stipple_stage(struct draw_context *context);
struct draw_stage *draw_wide_line_stage(struct draw_context *context);
struct draw_stage *draw_wide_point_stage(struct draw_context *context);
struct draw_stage *draw_validate_stage(struct draw_context *context);
void draw_free_temp_verts(struct draw_stage *stage);
bool draw_alloc_temp_verts(struct draw_stage *stage, unsigned nr);
void draw_reset_vertex_ids(struct draw_context *draw);
void draw_pipe_passthrough_tri(struct draw_stage *stage, struct prim_header *header);
void draw_pipe_passthrough_line(struct draw_stage *stage, struct prim_header *header);
void draw_pipe_passthrough_point(struct draw_stage *stage, struct prim_header *header);
void draw_aapoint_prepare_outputs(struct draw_context *context,
struct draw_stage *stage);
void draw_aaline_prepare_outputs(struct draw_context *context,
struct draw_stage *stage);
void draw_unfilled_prepare_outputs(struct draw_context *context,
struct draw_stage *stage);
/**
* Get a writeable copy of a vertex.
* \param stage drawing stage info
* \param vert the vertex to copy (source)
* \param idx index into stage's tmp[] array to put the copy (dest)
* \return pointer to the copied vertex
*/
static inline struct vertex_header *
dup_vert(struct draw_stage *stage,
const struct vertex_header *vert,
unsigned idx)
{
struct vertex_header *tmp = stage->tmp[idx];
const unsigned vsize = sizeof(struct vertex_header)
+ draw_num_shader_outputs(stage->draw) * 4 * sizeof(float);
memcpy(tmp, vert, vsize);
tmp->vertex_id = UNDEFINED_VERTEX_ID;
return tmp;
}
#endif
@@ -0,0 +1,763 @@
/**************************************************************************
*
* Copyright 2007-2018 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.
*
**************************************************************************/
/**
* AA line stage: AA lines are converted triangles (with extra generic)
*
* Authors: Brian Paul
*/
#include "pipe/p_context.h"
#include "pipe/p_defines.h"
#include "pipe/p_shader_tokens.h"
#include "util/u_inlines.h"
#include "util/format/u_format.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "tgsi/tgsi_transform.h"
#include "tgsi/tgsi_dump.h"
#include "draw_context.h"
#include "draw_private.h"
#include "draw_pipe.h"
#include "nir.h"
#include "nir/nir_draw_helpers.h"
/** Approx number of new tokens for instructions in aa_transform_inst() */
#define NUM_NEW_TOKENS 53
/**
* Subclass of pipe_shader_state to carry extra fragment shader info.
*/
struct aaline_fragment_shader
{
struct pipe_shader_state state;
void *driver_fs;
void *aaline_fs;
int generic_attrib; /**< generic used for distance */
};
/**
* Subclass of draw_stage
*/
struct aaline_stage
{
struct draw_stage stage;
float half_line_width;
/** For AA lines, this is the vertex attrib slot for new generic */
unsigned coord_slot;
/** position, not necessarily output zero */
unsigned pos_slot;
/*
* Currently bound state
*/
struct aaline_fragment_shader *fs;
/*
* Driver interface/override functions
*/
void * (*driver_create_fs_state)(struct pipe_context *,
const struct pipe_shader_state *);
void (*driver_bind_fs_state)(struct pipe_context *, void *);
void (*driver_delete_fs_state)(struct pipe_context *, void *);
};
/**
* Subclass of tgsi_transform_context, used for transforming the
* user's fragment shader to add the special AA instructions.
*/
struct aa_transform_context {
struct tgsi_transform_context base;
uint64_t tempsUsed; /**< bitmask */
int colorOutput; /**< which output is the primary color */
int maxInput, maxGeneric; /**< max input index found */
int numImm; /**< number of immediate regsters */
int colorTemp, aaTemp; /**< temp registers */
};
/**
* TGSI declaration transform callback.
* Look for a free input attrib, and two free temp regs.
*/
static void
aa_transform_decl(struct tgsi_transform_context *ctx,
struct tgsi_full_declaration *decl)
{
struct aa_transform_context *aactx = (struct aa_transform_context *)ctx;
if (decl->Declaration.File == TGSI_FILE_OUTPUT &&
decl->Semantic.Name == TGSI_SEMANTIC_COLOR &&
decl->Semantic.Index == 0) {
aactx->colorOutput = decl->Range.First;
}
else if (decl->Declaration.File == TGSI_FILE_INPUT) {
if ((int) decl->Range.Last > aactx->maxInput)
aactx->maxInput = decl->Range.Last;
if (decl->Semantic.Name == TGSI_SEMANTIC_GENERIC &&
(int) decl->Semantic.Index > aactx->maxGeneric) {
aactx->maxGeneric = decl->Semantic.Index;
}
}
else if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {
unsigned i;
for (i = decl->Range.First;
i <= decl->Range.Last; i++) {
/*
* XXX this bitfield doesn't really cut it...
*/
aactx->tempsUsed |= UINT64_C(1) << i;
}
}
ctx->emit_declaration(ctx, decl);
}
/**
* TGSI immediate declaration transform callback.
*/
static void
aa_immediate(struct tgsi_transform_context *ctx,
struct tgsi_full_immediate *imm)
{
struct aa_transform_context *aactx = (struct aa_transform_context *)ctx;
ctx->emit_immediate(ctx, imm);
aactx->numImm++;
}
/**
* Find the lowest zero bit, or -1 if bitfield is all ones.
*/
static int
free_bit(uint64_t bitfield)
{
return ffsll(~bitfield) - 1;
}
/**
* TGSI transform prolog callback.
*/
static void
aa_transform_prolog(struct tgsi_transform_context *ctx)
{
struct aa_transform_context *aactx = (struct aa_transform_context *) ctx;
uint64_t usedTemps = aactx->tempsUsed;
/* find two free temp regs */
aactx->colorTemp = free_bit(usedTemps);
usedTemps |= UINT64_C(1) << aactx->colorTemp;
aactx->aaTemp = free_bit(usedTemps);
assert(aactx->colorTemp >= 0);
assert(aactx->aaTemp >= 0);
/* declare new generic input/texcoord */
tgsi_transform_input_decl(ctx, aactx->maxInput + 1,
TGSI_SEMANTIC_GENERIC, aactx->maxGeneric + 1,
TGSI_INTERPOLATE_LINEAR);
/* declare new temp regs */
tgsi_transform_temp_decl(ctx, aactx->aaTemp);
tgsi_transform_temp_decl(ctx, aactx->colorTemp);
/* declare new immediate reg */
tgsi_transform_immediate_decl(ctx, 2.0, -1.0, 0.0, 0.25);
}
/**
* TGSI transform epilog callback.
*/
static void
aa_transform_epilog(struct tgsi_transform_context *ctx)
{
struct aa_transform_context *aactx = (struct aa_transform_context *) ctx;
if (aactx->colorOutput != -1) {
struct tgsi_full_instruction inst;
/* insert distance-based coverage code for antialiasing. */
/* saturate(linewidth - fabs(interpx), linelength - fabs(interpz) */
inst = tgsi_default_full_instruction();
inst.Instruction.Saturate = true;
inst.Instruction.Opcode = TGSI_OPCODE_ADD;
inst.Instruction.NumDstRegs = 1;
tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,
aactx->aaTemp, TGSI_WRITEMASK_XZ);
inst.Instruction.NumSrcRegs = 2;
tgsi_transform_src_reg(&inst.Src[1], TGSI_FILE_INPUT, aactx->maxInput + 1,
TGSI_SWIZZLE_X, TGSI_SWIZZLE_X,
TGSI_SWIZZLE_Z, TGSI_SWIZZLE_Z);
tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_INPUT, aactx->maxInput + 1,
TGSI_SWIZZLE_Y, TGSI_SWIZZLE_Y,
TGSI_SWIZZLE_W, TGSI_SWIZZLE_W);
inst.Src[1].Register.Absolute = true;
inst.Src[1].Register.Negate = true;
ctx->emit_instruction(ctx, &inst);
/* linelength * 2 - 1 */
tgsi_transform_op3_swz_inst(ctx, TGSI_OPCODE_MAD,
TGSI_FILE_TEMPORARY, aactx->aaTemp,
TGSI_WRITEMASK_Y,
TGSI_FILE_INPUT, aactx->maxInput + 1,
TGSI_SWIZZLE_W, false,
TGSI_FILE_IMMEDIATE, aactx->numImm,
TGSI_SWIZZLE_X,
TGSI_FILE_IMMEDIATE, aactx->numImm,
TGSI_SWIZZLE_Y);
/* MIN height alpha */
tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_MIN,
TGSI_FILE_TEMPORARY, aactx->aaTemp,
TGSI_WRITEMASK_Z,
TGSI_FILE_TEMPORARY, aactx->aaTemp,
TGSI_SWIZZLE_Z,
TGSI_FILE_TEMPORARY, aactx->aaTemp,
TGSI_SWIZZLE_Y, false);
/* MUL width / height alpha */
tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_MUL,
TGSI_FILE_TEMPORARY, aactx->aaTemp,
TGSI_WRITEMASK_W,
TGSI_FILE_TEMPORARY, aactx->aaTemp,
TGSI_SWIZZLE_X,
TGSI_FILE_TEMPORARY, aactx->aaTemp,
TGSI_SWIZZLE_Z, false);
/* MOV rgb */
tgsi_transform_op1_inst(ctx, TGSI_OPCODE_MOV,
TGSI_FILE_OUTPUT, aactx->colorOutput,
TGSI_WRITEMASK_XYZ,
TGSI_FILE_TEMPORARY, aactx->colorTemp);
/* MUL alpha */
tgsi_transform_op2_inst(ctx, TGSI_OPCODE_MUL,
TGSI_FILE_OUTPUT, aactx->colorOutput,
TGSI_WRITEMASK_W,
TGSI_FILE_TEMPORARY, aactx->colorTemp,
TGSI_FILE_TEMPORARY, aactx->aaTemp, false);
}
}
/**
* TGSI instruction transform callback.
* Replace writes to result.color w/ a temp reg.
*/
static void
aa_transform_inst(struct tgsi_transform_context *ctx,
struct tgsi_full_instruction *inst)
{
struct aa_transform_context *aactx = (struct aa_transform_context *) ctx;
unsigned i;
/*
* Look for writes to result.color and replace with colorTemp reg.
*/
for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
struct tgsi_full_dst_register *dst = &inst->Dst[i];
if (dst->Register.File == TGSI_FILE_OUTPUT &&
dst->Register.Index == aactx->colorOutput) {
dst->Register.File = TGSI_FILE_TEMPORARY;
dst->Register.Index = aactx->colorTemp;
}
}
ctx->emit_instruction(ctx, inst);
}
/**
* Generate the frag shader we'll use for drawing AA lines.
* This will be the user's shader plus some arithmetic instructions.
*/
static bool
generate_aaline_fs(struct aaline_stage *aaline)
{
struct pipe_context *pipe = aaline->stage.draw->pipe;
const struct pipe_shader_state *orig_fs = &aaline->fs->state;
struct pipe_shader_state aaline_fs;
struct aa_transform_context transform;
const unsigned newLen = tgsi_num_tokens(orig_fs->tokens) + NUM_NEW_TOKENS;
aaline_fs = *orig_fs; /* copy to init */
memset(&transform, 0, sizeof(transform));
transform.colorOutput = -1;
transform.maxInput = -1;
transform.maxGeneric = -1;
transform.colorTemp = -1;
transform.aaTemp = -1;
transform.base.prolog = aa_transform_prolog;
transform.base.epilog = aa_transform_epilog;
transform.base.transform_instruction = aa_transform_inst;
transform.base.transform_declaration = aa_transform_decl;
transform.base.transform_immediate = aa_immediate;
aaline_fs.tokens = tgsi_transform_shader(orig_fs->tokens, newLen, &transform.base);
if (!aaline_fs.tokens)
return false;
#if 0 /* debug */
debug_printf("draw_aaline, orig shader:\n");
tgsi_dump(orig_fs->tokens, 0);
debug_printf("draw_aaline, new shader:\n");
tgsi_dump(aaline_fs.tokens, 0);
#endif
aaline->fs->aaline_fs = aaline->driver_create_fs_state(pipe, &aaline_fs);
if (aaline->fs->aaline_fs != NULL)
aaline->fs->generic_attrib = transform.maxGeneric + 1;
FREE((void *)aaline_fs.tokens);
return aaline->fs->aaline_fs != NULL;
}
static bool
generate_aaline_fs_nir(struct aaline_stage *aaline)
{
struct pipe_context *pipe = aaline->stage.draw->pipe;
const struct pipe_shader_state *orig_fs = &aaline->fs->state;
struct pipe_shader_state aaline_fs;
aaline_fs = *orig_fs; /* copy to init */
aaline_fs.ir.nir = nir_shader_clone(NULL, orig_fs->ir.nir);
if (!aaline_fs.ir.nir)
return false;
nir_lower_aaline_fs(aaline_fs.ir.nir, &aaline->fs->generic_attrib, NULL, NULL);
aaline->fs->aaline_fs = aaline->driver_create_fs_state(pipe, &aaline_fs);
if (aaline->fs->aaline_fs == NULL)
return false;
return true;
}
/**
* When we're about to draw our first AA line in a batch, this function is
* called to tell the driver to bind our modified fragment shader.
*/
static bool
bind_aaline_fragment_shader(struct aaline_stage *aaline)
{
struct draw_context *draw = aaline->stage.draw;
struct pipe_context *pipe = draw->pipe;
if (!aaline->fs->aaline_fs) {
if (aaline->fs->state.type == PIPE_SHADER_IR_NIR) {
if (!generate_aaline_fs_nir(aaline))
return false;
} else
if (!generate_aaline_fs(aaline))
return false;
}
draw->suspend_flushing = true;
aaline->driver_bind_fs_state(pipe, aaline->fs->aaline_fs);
draw->suspend_flushing = false;
return true;
}
static inline struct aaline_stage *
aaline_stage(struct draw_stage *stage)
{
return (struct aaline_stage *) stage;
}
/**
* Draw a wide line by drawing a quad, using geometry which will
* fullfill GL's antialiased line requirements.
*/
static void
aaline_line(struct draw_stage *stage, struct prim_header *header)
{
const struct aaline_stage *aaline = aaline_stage(stage);
const float half_width = aaline->half_line_width;
struct prim_header tri;
struct vertex_header *v[8];
unsigned coordPos = aaline->coord_slot;
unsigned posPos = aaline->pos_slot;
float *pos, *tex;
float dx = header->v[1]->data[posPos][0] - header->v[0]->data[posPos][0];
float dy = header->v[1]->data[posPos][1] - header->v[0]->data[posPos][1];
float length = sqrtf(dx * dx + dy * dy);
float c_a = dx / length, s_a = dy / length;
float half_length = 0.5 * length;
float t_l, t_w;
unsigned i;
half_length = half_length + 0.5f;
t_w = half_width;
t_l = 0.5f;
/* allocate/dup new verts */
for (i = 0; i < 4; i++) {
v[i] = dup_vert(stage, header->v[i/2], i);
}
/*
* Quad strip for line from v0 to v1 (*=endpoints):
*
* 1 3
* +-----------------------------+
* | |
* | *v0 v1* |
* | |
* +-----------------------------+
* 0 2
*/
/*
* We increase line length by 0.5 pixels (at each endpoint),
* and calculate the tri endpoints by moving them half-width
* distance away perpendicular to the line.
* XXX: since we change line endpoints (by 0.5 pixel), should
* actually re-interpolate all other values?
*/
/* new verts */
pos = v[0]->data[posPos];
pos[0] += (-t_l * c_a - t_w * s_a);
pos[1] += (-t_l * s_a + t_w * c_a);
pos = v[1]->data[posPos];
pos[0] += (-t_l * c_a - -t_w * s_a);
pos[1] += (-t_l * s_a + -t_w * c_a);
pos = v[2]->data[posPos];
pos[0] += (t_l * c_a - t_w * s_a);
pos[1] += (t_l * s_a + t_w * c_a);
pos = v[3]->data[posPos];
pos[0] += (t_l * c_a - -t_w * s_a);
pos[1] += (t_l * s_a + -t_w * c_a);
/* new texcoords */
tex = v[0]->data[coordPos];
ASSIGN_4V(tex, -half_width, half_width, -half_length, half_length);
tex = v[1]->data[coordPos];
ASSIGN_4V(tex, half_width, half_width, -half_length, half_length);
tex = v[2]->data[coordPos];
ASSIGN_4V(tex, -half_width, half_width, half_length, half_length);
tex = v[3]->data[coordPos];
ASSIGN_4V(tex, half_width, half_width, half_length, half_length);
tri.v[0] = v[2]; tri.v[1] = v[1]; tri.v[2] = v[0];
stage->next->tri(stage->next, &tri);
tri.v[0] = v[3]; tri.v[1] = v[1]; tri.v[2] = v[2];
stage->next->tri(stage->next, &tri);
}
static void
aaline_first_line(struct draw_stage *stage, struct prim_header *header)
{
auto struct aaline_stage *aaline = aaline_stage(stage);
struct draw_context *draw = stage->draw;
struct pipe_context *pipe = draw->pipe;
const struct pipe_rasterizer_state *rast = draw->rasterizer;
void *r;
assert(draw->rasterizer->line_smooth && !draw->rasterizer->multisample);
if (draw->rasterizer->line_width <= 1.0)
aaline->half_line_width = 1.0;
else
aaline->half_line_width = 0.5f * draw->rasterizer->line_width + 0.5f;
if (!draw->rasterizer->half_pixel_center)
/*
* The tex coords probably would need adjustments?
*/
debug_printf("aa lines without half pixel center may be wrong\n");
/*
* Bind (generate) our fragprog
*/
if (!bind_aaline_fragment_shader(aaline)) {
stage->line = draw_pipe_passthrough_line;
stage->line(stage, header);
return;
}
draw_aaline_prepare_outputs(draw, draw->pipeline.aaline);
draw->suspend_flushing = true;
/* Disable triangle culling, stippling, unfilled mode etc. */
r = draw_get_rasterizer_no_cull(draw, rast);
pipe->bind_rasterizer_state(pipe, r);
draw->suspend_flushing = false;
/* now really draw first line */
stage->line = aaline_line;
stage->line(stage, header);
}
static void
aaline_flush(struct draw_stage *stage, unsigned flags)
{
struct draw_context *draw = stage->draw;
struct aaline_stage *aaline = aaline_stage(stage);
struct pipe_context *pipe = draw->pipe;
stage->line = aaline_first_line;
stage->next->flush(stage->next, flags);
/* restore original frag shader */
draw->suspend_flushing = true;
aaline->driver_bind_fs_state(pipe, aaline->fs ? aaline->fs->driver_fs : NULL);
/* restore original rasterizer state */
if (draw->rast_handle) {
pipe->bind_rasterizer_state(pipe, draw->rast_handle);
}
draw->suspend_flushing = false;
draw_remove_extra_vertex_attribs(draw);
}
static void
aaline_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
aaline_destroy(struct draw_stage *stage)
{
struct aaline_stage *aaline = aaline_stage(stage);
struct pipe_context *pipe = stage->draw->pipe;
draw_free_temp_verts(stage);
/* restore the old entry points */
pipe->create_fs_state = aaline->driver_create_fs_state;
pipe->bind_fs_state = aaline->driver_bind_fs_state;
pipe->delete_fs_state = aaline->driver_delete_fs_state;
FREE(stage);
}
static struct aaline_stage *
draw_aaline_stage(struct draw_context *draw)
{
struct aaline_stage *aaline = CALLOC_STRUCT(aaline_stage);
if (!aaline)
return NULL;
aaline->stage.draw = draw;
aaline->stage.name = "aaline";
aaline->stage.next = NULL;
aaline->stage.point = draw_pipe_passthrough_point;
aaline->stage.line = aaline_first_line;
aaline->stage.tri = draw_pipe_passthrough_tri;
aaline->stage.flush = aaline_flush;
aaline->stage.reset_stipple_counter = aaline_reset_stipple_counter;
aaline->stage.destroy = aaline_destroy;
if (!draw_alloc_temp_verts(&aaline->stage, 8)) {
aaline->stage.destroy(&aaline->stage);
return NULL;
}
return aaline;
}
static struct aaline_stage *
aaline_stage_from_pipe(struct pipe_context *pipe)
{
struct draw_context *draw = (struct draw_context *) pipe->draw;
if (draw) {
return aaline_stage(draw->pipeline.aaline);
} else {
return NULL;
}
}
/**
* This function overrides the driver's create_fs_state() function and
* will typically be called by the gallium frontend.
*/
static void *
aaline_create_fs_state(struct pipe_context *pipe,
const struct pipe_shader_state *fs)
{
struct aaline_stage *aaline = aaline_stage_from_pipe(pipe);
struct aaline_fragment_shader *aafs = NULL;
if (!aaline)
return NULL;
aafs = CALLOC_STRUCT(aaline_fragment_shader);
if (!aafs)
return NULL;
aafs->state.type = fs->type;
if (fs->type == PIPE_SHADER_IR_TGSI)
aafs->state.tokens = tgsi_dup_tokens(fs->tokens);
else
aafs->state.ir.nir = nir_shader_clone(NULL, fs->ir.nir);
/* pass-through */
aafs->driver_fs = aaline->driver_create_fs_state(pipe, fs);
return aafs;
}
static void
aaline_bind_fs_state(struct pipe_context *pipe, void *fs)
{
struct aaline_stage *aaline = aaline_stage_from_pipe(pipe);
struct aaline_fragment_shader *aafs = (struct aaline_fragment_shader *) fs;
if (!aaline) {
return;
}
/* save current */
aaline->fs = aafs;
/* pass-through */
aaline->driver_bind_fs_state(pipe, (aafs ? aafs->driver_fs : NULL));
}
static void
aaline_delete_fs_state(struct pipe_context *pipe, void *fs)
{
struct aaline_stage *aaline = aaline_stage_from_pipe(pipe);
struct aaline_fragment_shader *aafs = (struct aaline_fragment_shader *) fs;
if (!aafs) {
return;
}
if (aaline) {
/* pass-through */
aaline->driver_delete_fs_state(pipe, aafs->driver_fs);
if (aafs->aaline_fs)
aaline->driver_delete_fs_state(pipe, aafs->aaline_fs);
}
if (aafs->state.type == PIPE_SHADER_IR_TGSI)
FREE((void*)aafs->state.tokens);
else
ralloc_free(aafs->state.ir.nir);
FREE(aafs);
}
void
draw_aaline_prepare_outputs(struct draw_context *draw,
struct draw_stage *stage)
{
struct aaline_stage *aaline = aaline_stage(stage);
const struct pipe_rasterizer_state *rast = draw->rasterizer;
/* update vertex attrib info */
aaline->pos_slot = draw_current_shader_position_output(draw);
if (!rast->line_smooth || rast->multisample)
return;
/* allocate the extra post-transformed vertex attribute */
if (aaline->fs && aaline->fs->aaline_fs)
aaline->coord_slot = draw_alloc_extra_vertex_attrib(draw,
TGSI_SEMANTIC_GENERIC,
aaline->fs->generic_attrib);
else
aaline->coord_slot = -1;
}
/**
* Called by drivers that want to install this AA line prim stage
* into the draw module's pipeline. This will not be used if the
* hardware has native support for AA lines.
*/
bool
draw_install_aaline_stage(struct draw_context *draw, struct pipe_context *pipe)
{
struct aaline_stage *aaline;
pipe->draw = (void *) draw;
/*
* Create / install AA line drawing / prim stage
*/
aaline = draw_aaline_stage(draw);
if (!aaline)
return false;
/* save original driver functions */
aaline->driver_create_fs_state = pipe->create_fs_state;
aaline->driver_bind_fs_state = pipe->bind_fs_state;
aaline->driver_delete_fs_state = pipe->delete_fs_state;
/* override the driver's functions */
pipe->create_fs_state = aaline_create_fs_state;
pipe->bind_fs_state = aaline_bind_fs_state;
pipe->delete_fs_state = aaline_delete_fs_state;
/* Install once everything is known to be OK:
*/
draw->pipeline.aaline = &aaline->stage;
return true;
}
@@ -0,0 +1,827 @@
/**************************************************************************
*
* Copyright 2008 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.
*
**************************************************************************/
/**
* AA point stage: AA points are converted to quads and rendered with a
* special fragment shader. Another approach would be to use a texture
* map image of a point, but experiments indicate the quality isn't nearly
* as good as this approach.
*
* Note: this looks a lot like draw_aaline.c but there's actually little
* if any code that can be shared.
*
* Authors: Brian Paul
*/
#include "pipe/p_context.h"
#include "pipe/p_defines.h"
#include "pipe/p_shader_tokens.h"
#include "tgsi/tgsi_transform.h"
#include "tgsi/tgsi_dump.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "draw_context.h"
#include "draw_vs.h"
#include "draw_pipe.h"
#include "nir.h"
#include "nir/nir_draw_helpers.h"
/** Approx number of new tokens for instructions in aa_transform_inst() */
#define NUM_NEW_TOKENS 200
/*
* Enabling NORMALIZE might give _slightly_ better results.
* Basically, it controls whether we compute distance as d=sqrt(x*x+y*y) or
* d=x*x+y*y. Since we're working with a unit circle, the later seems
* close enough and saves some costly instructions.
*/
#define NORMALIZE 0
/**
* Subclass of pipe_shader_state to carry extra fragment shader info.
*/
struct aapoint_fragment_shader
{
struct pipe_shader_state state;
void *driver_fs; /**< the regular shader */
void *aapoint_fs; /**< the aa point-augmented shader */
int generic_attrib; /**< The generic input attrib/texcoord we'll use */
};
/**
* Subclass of draw_stage
*/
struct aapoint_stage
{
struct draw_stage stage;
/** half of pipe_rasterizer_state::point_size */
float radius;
/** vertex attrib slot containing point size */
int psize_slot;
/** this is the vertex attrib slot for the new texcoords */
unsigned tex_slot;
/** vertex attrib slot containing position */
unsigned pos_slot;
/** Type of Boolean variables on this hardware. */
nir_alu_type bool_type;
/** Currently bound fragment shader */
struct aapoint_fragment_shader *fs;
/*
* Driver interface/override functions
*/
void * (*driver_create_fs_state)(struct pipe_context *,
const struct pipe_shader_state *);
void (*driver_bind_fs_state)(struct pipe_context *, void *);
void (*driver_delete_fs_state)(struct pipe_context *, void *);
};
/**
* Subclass of tgsi_transform_context, used for transforming the
* user's fragment shader to add the special AA instructions.
*/
struct aa_transform_context {
struct tgsi_transform_context base;
uint32_t tempsUsed; /**< bitmask */
int colorOutput; /**< which output is the primary color */
int maxInput, maxGeneric; /**< max input index found */
int tmp0, colorTemp; /**< temp registers */
};
/**
* TGSI declaration transform callback.
* Look for two free temp regs and available input reg for new texcoords.
*/
static void
aa_transform_decl(struct tgsi_transform_context *ctx,
struct tgsi_full_declaration *decl)
{
struct aa_transform_context *aactx = (struct aa_transform_context *) ctx;
if (decl->Declaration.File == TGSI_FILE_OUTPUT &&
decl->Semantic.Name == TGSI_SEMANTIC_COLOR &&
decl->Semantic.Index == 0) {
aactx->colorOutput = decl->Range.First;
}
else if (decl->Declaration.File == TGSI_FILE_INPUT) {
if ((int) decl->Range.Last > aactx->maxInput)
aactx->maxInput = decl->Range.Last;
if (decl->Semantic.Name == TGSI_SEMANTIC_GENERIC &&
(int) decl->Semantic.Index > aactx->maxGeneric) {
aactx->maxGeneric = decl->Semantic.Index;
}
}
else if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {
unsigned i;
for (i = decl->Range.First;
i <= decl->Range.Last; i++) {
aactx->tempsUsed |= 1u << i;
}
}
ctx->emit_declaration(ctx, decl);
}
/**
* TGSI transform callback.
* Insert new declarations and instructions before first instruction.
*/
static void
aa_transform_prolog(struct tgsi_transform_context *ctx)
{
/* emit our new declarations before the first instruction */
struct aa_transform_context *aactx = (struct aa_transform_context *) ctx;
struct tgsi_full_instruction newInst;
const int texInput = aactx->maxInput + 1;
int tmp0;
unsigned i;
/* find two free temp regs */
for (i = 0; i < 32; i++) {
if ((aactx->tempsUsed & (1u << i)) == 0) {
/* found a free temp */
if (aactx->tmp0 < 0)
aactx->tmp0 = i;
else if (aactx->colorTemp < 0)
aactx->colorTemp = i;
else
break;
}
}
assert(aactx->colorTemp != aactx->tmp0);
tmp0 = aactx->tmp0;
/* declare new generic input/texcoord */
tgsi_transform_input_decl(ctx, texInput,
TGSI_SEMANTIC_GENERIC, aactx->maxGeneric + 1,
TGSI_INTERPOLATE_LINEAR);
/* declare new temp regs */
tgsi_transform_temp_decl(ctx, tmp0);
tgsi_transform_temp_decl(ctx, aactx->colorTemp);
/*
* Emit code to compute fragment coverage, kill if outside point radius
*
* Temp reg0 usage:
* t0.x = distance of fragment from center point
* t0.y = boolean, is t0.x > 1.0, also misc temp usage
* t0.z = temporary for computing 1/(1-k) value
* t0.w = final coverage value
*/
/* MUL t0.xy, tex, tex; # compute x^2, y^2 */
tgsi_transform_op2_inst(ctx, TGSI_OPCODE_MUL,
TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_XY,
TGSI_FILE_INPUT, texInput,
TGSI_FILE_INPUT, texInput, false);
/* ADD t0.x, t0.x, t0.y; # x^2 + y^2 */
tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_ADD,
TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_X,
TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_X,
TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_Y, false);
#if NORMALIZE /* OPTIONAL normalization of length */
/* RSQ t0.x, t0.x; */
tgsi_transform_op1_inst(ctx, TGSI_OPCODE_RSQ,
TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_X,
TGSI_FILE_TEMPORARY, tmp0);
/* RCP t0.x, t0.x; */
tgsi_transform_op1_inst(ctx, TGSI_OPCODE_RCP,
TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_X,
TGSI_FILE_TEMPORARY, tmp0);
#endif
/* SGT t0.y, t0.xxxx, tex.wwww; # bool b = d > 1 (NOTE tex.w == 1) */
tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_SGT,
TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_Y,
TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_X,
TGSI_FILE_INPUT, texInput, TGSI_SWIZZLE_W, false);
/* KILL_IF -tmp0.yyyy; # if -tmp0.y < 0, KILL */
tgsi_transform_kill_inst(ctx, TGSI_FILE_TEMPORARY, tmp0,
TGSI_SWIZZLE_Y, true);
/* compute coverage factor = (1-d)/(1-k) */
/* SUB t0.z, tex.w, tex.z; # m = 1 - k */
tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_ADD,
TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_Z,
TGSI_FILE_INPUT, texInput, TGSI_SWIZZLE_W,
TGSI_FILE_INPUT, texInput, TGSI_SWIZZLE_Z, true);
/* RCP t0.z, t0.z; # t0.z = 1 / m */
newInst = tgsi_default_full_instruction();
newInst.Instruction.Opcode = TGSI_OPCODE_RCP;
newInst.Instruction.NumDstRegs = 1;
newInst.Dst[0].Register.File = TGSI_FILE_TEMPORARY;
newInst.Dst[0].Register.Index = tmp0;
newInst.Dst[0].Register.WriteMask = TGSI_WRITEMASK_Z;
newInst.Instruction.NumSrcRegs = 1;
newInst.Src[0].Register.File = TGSI_FILE_TEMPORARY;
newInst.Src[0].Register.Index = tmp0;
newInst.Src[0].Register.SwizzleX = TGSI_SWIZZLE_Z;
ctx->emit_instruction(ctx, &newInst);
/* SUB t0.y, 1, t0.x; # d = 1 - d */
tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_ADD,
TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_Y,
TGSI_FILE_INPUT, texInput, TGSI_SWIZZLE_W,
TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_X, true);
/* MUL t0.w, t0.y, t0.z; # coverage = d * m */
tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_MUL,
TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_W,
TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_Y,
TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_Z, false);
/* SLE t0.y, t0.x, tex.z; # bool b = distance <= k */
tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_SLE,
TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_Y,
TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_X,
TGSI_FILE_INPUT, texInput, TGSI_SWIZZLE_Z, false);
/* CMP t0.w, -t0.y, tex.w, t0.w;
* # if -t0.y < 0 then
* t0.w = 1
* else
* t0.w = t0.w
*/
tgsi_transform_op3_swz_inst(ctx, TGSI_OPCODE_CMP,
TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_W,
TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_Y, 1,
TGSI_FILE_INPUT, texInput, TGSI_SWIZZLE_W,
TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_W);
}
/**
* TGSI transform callback.
* Insert new instructions before the END instruction.
*/
static void
aa_transform_epilog(struct tgsi_transform_context *ctx)
{
struct aa_transform_context *aactx = (struct aa_transform_context *) ctx;
/* add alpha modulation code at tail of program */
/* MOV result.color.xyz, colorTemp; */
tgsi_transform_op1_inst(ctx, TGSI_OPCODE_MOV,
TGSI_FILE_OUTPUT, aactx->colorOutput,
TGSI_WRITEMASK_XYZ,
TGSI_FILE_TEMPORARY, aactx->colorTemp);
/* MUL result.color.w, colorTemp, tmp0.w; */
tgsi_transform_op2_inst(ctx, TGSI_OPCODE_MUL,
TGSI_FILE_OUTPUT, aactx->colorOutput,
TGSI_WRITEMASK_W,
TGSI_FILE_TEMPORARY, aactx->colorTemp,
TGSI_FILE_TEMPORARY, aactx->tmp0, false);
}
/**
* TGSI transform callback.
* Called per instruction.
* Replace writes to result.color w/ a temp reg.
*/
static void
aa_transform_inst(struct tgsi_transform_context *ctx,
struct tgsi_full_instruction *inst)
{
struct aa_transform_context *aactx = (struct aa_transform_context *) ctx;
unsigned i;
/* Not an END instruction.
* Look for writes to result.color and replace with colorTemp reg.
*/
for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
struct tgsi_full_dst_register *dst = &inst->Dst[i];
if (dst->Register.File == TGSI_FILE_OUTPUT &&
dst->Register.Index == aactx->colorOutput) {
dst->Register.File = TGSI_FILE_TEMPORARY;
dst->Register.Index = aactx->colorTemp;
}
}
ctx->emit_instruction(ctx, inst);
}
/**
* Generate the frag shader we'll use for drawing AA points.
* This will be the user's shader plus some texture/modulate instructions.
*/
static bool
generate_aapoint_fs(struct aapoint_stage *aapoint)
{
const struct pipe_shader_state *orig_fs = &aapoint->fs->state;
struct pipe_shader_state aapoint_fs;
struct aa_transform_context transform;
const unsigned newLen = tgsi_num_tokens(orig_fs->tokens) + NUM_NEW_TOKENS;
struct pipe_context *pipe = aapoint->stage.draw->pipe;
aapoint_fs = *orig_fs; /* copy to init */
assert(aapoint_fs.type == PIPE_SHADER_IR_TGSI);
memset(&transform, 0, sizeof(transform));
transform.colorOutput = -1;
transform.maxInput = -1;
transform.maxGeneric = -1;
transform.colorTemp = -1;
transform.tmp0 = -1;
transform.base.prolog = aa_transform_prolog;
transform.base.epilog = aa_transform_epilog;
transform.base.transform_instruction = aa_transform_inst;
transform.base.transform_declaration = aa_transform_decl;
aapoint_fs.tokens = tgsi_transform_shader(orig_fs->tokens, newLen, &transform.base);
if (!aapoint_fs.tokens)
return false;
#if 0 /* debug */
debug_printf("draw_aapoint, orig shader:\n");
tgsi_dump(orig_fs->tokens, 0);
debug_printf("draw_aapoint, new shader:\n");
tgsi_dump(aapoint_fs.tokens, 0);
#endif
aapoint->fs->aapoint_fs
= aapoint->driver_create_fs_state(pipe, &aapoint_fs);
if (aapoint->fs->aapoint_fs == NULL)
goto fail;
aapoint->fs->generic_attrib = transform.maxGeneric + 1;
FREE((void *)aapoint_fs.tokens);
return true;
fail:
FREE((void *)aapoint_fs.tokens);
return false;
}
static bool
generate_aapoint_fs_nir(struct aapoint_stage *aapoint)
{
struct pipe_context *pipe = aapoint->stage.draw->pipe;
const struct pipe_shader_state *orig_fs = &aapoint->fs->state;
struct pipe_shader_state aapoint_fs;
aapoint_fs = *orig_fs; /* copy to init */
aapoint_fs.ir.nir = nir_shader_clone(NULL, orig_fs->ir.nir);
if (!aapoint_fs.ir.nir)
return false;
nir_lower_aapoint_fs(aapoint_fs.ir.nir, &aapoint->fs->generic_attrib, aapoint->bool_type);
aapoint->fs->aapoint_fs = aapoint->driver_create_fs_state(pipe, &aapoint_fs);
if (aapoint->fs->aapoint_fs == NULL)
goto fail;
return true;
fail:
return false;
}
/**
* When we're about to draw our first AA point in a batch, this function is
* called to tell the driver to bind our modified fragment shader.
*/
static bool
bind_aapoint_fragment_shader(struct aapoint_stage *aapoint)
{
struct draw_context *draw = aapoint->stage.draw;
struct pipe_context *pipe = draw->pipe;
if (!aapoint->fs->aapoint_fs) {
if (aapoint->fs->state.type == PIPE_SHADER_IR_NIR) {
if (!generate_aapoint_fs_nir(aapoint))
return false;
} else if (!generate_aapoint_fs(aapoint))
return false;
}
draw->suspend_flushing = true;
aapoint->driver_bind_fs_state(pipe, aapoint->fs->aapoint_fs);
draw->suspend_flushing = false;
return true;
}
static inline struct aapoint_stage *
aapoint_stage(struct draw_stage *stage)
{
return (struct aapoint_stage *) stage;
}
/**
* Draw an AA point by drawing a quad.
*/
static void
aapoint_point(struct draw_stage *stage, struct prim_header *header)
{
const struct aapoint_stage *aapoint = aapoint_stage(stage);
struct prim_header tri;
struct vertex_header *v[4];
const unsigned tex_slot = aapoint->tex_slot;
const unsigned pos_slot = aapoint->pos_slot;
float radius, *pos, *tex;
float k;
if (aapoint->psize_slot >= 0) {
radius = 0.5f * header->v[0]->data[aapoint->psize_slot][0];
}
else {
radius = aapoint->radius;
}
/*
* Note: the texcoords (generic attrib, really) we use are special:
* The S and T components simply vary from -1 to +1.
* The R component is k, below.
* The Q component is 1.0 and will used as a handy constant in the
* fragment shader.
*/
/*
* k is the threshold distance from the point's center at which
* we begin alpha attenuation (the coverage value).
* Operating within a unit circle, we'll compute the fragment's
* distance 'd' from the center point using the texcoords.
* IF d > 1.0 THEN
* KILL fragment
* ELSE IF d > k THEN
* compute coverage in [0,1] proportional to d in [k, 1].
* ELSE
* coverage = 1.0; // full coverage
* ENDIF
*
* Note: the ELSEIF and ELSE clauses are actually implemented with CMP to
* avoid using IF/ELSE/ENDIF TGSI opcodes.
*/
#if !NORMALIZE
k = 1.0f / radius;
k = 1.0f - 2.0f * k + k * k;
#else
k = 1.0f - 1.0f / radius;
#endif
/* allocate/dup new verts */
for (unsigned i = 0; i < 4; i++) {
v[i] = dup_vert(stage, header->v[0], i);
}
/* new verts */
pos = v[0]->data[pos_slot];
pos[0] -= radius;
pos[1] -= radius;
pos = v[1]->data[pos_slot];
pos[0] += radius;
pos[1] -= radius;
pos = v[2]->data[pos_slot];
pos[0] += radius;
pos[1] += radius;
pos = v[3]->data[pos_slot];
pos[0] -= radius;
pos[1] += radius;
/* new texcoords */
tex = v[0]->data[tex_slot];
ASSIGN_4V(tex, -1, -1, k, 1);
tex = v[1]->data[tex_slot];
ASSIGN_4V(tex, 1, -1, k, 1);
tex = v[2]->data[tex_slot];
ASSIGN_4V(tex, 1, 1, k, 1);
tex = v[3]->data[tex_slot];
ASSIGN_4V(tex, -1, 1, k, 1);
/* emit 2 tris for the quad strip */
tri.v[0] = v[0];
tri.v[1] = v[1];
tri.v[2] = v[2];
stage->next->tri(stage->next, &tri);
tri.v[0] = v[0];
tri.v[1] = v[2];
tri.v[2] = v[3];
stage->next->tri(stage->next, &tri);
}
static void
aapoint_first_point(struct draw_stage *stage, struct prim_header *header)
{
auto struct aapoint_stage *aapoint = aapoint_stage(stage);
struct draw_context *draw = stage->draw;
struct pipe_context *pipe = draw->pipe;
const struct pipe_rasterizer_state *rast = draw->rasterizer;
void *r;
assert(draw->rasterizer->point_smooth && !draw->rasterizer->multisample);
if (draw->rasterizer->point_size <= 2.0)
aapoint->radius = 1.0;
else
aapoint->radius = 0.5f * draw->rasterizer->point_size;
/*
* Bind (generate) our fragprog.
*/
bind_aapoint_fragment_shader(aapoint);
draw_aapoint_prepare_outputs(draw, draw->pipeline.aapoint);
draw->suspend_flushing = true;
/* Disable triangle culling, stippling, unfilled mode etc. */
r = draw_get_rasterizer_no_cull(draw, rast);
pipe->bind_rasterizer_state(pipe, r);
draw->suspend_flushing = false;
/* now really draw first point */
stage->point = aapoint_point;
stage->point(stage, header);
}
static void
aapoint_flush(struct draw_stage *stage, unsigned flags)
{
struct draw_context *draw = stage->draw;
struct aapoint_stage *aapoint = aapoint_stage(stage);
struct pipe_context *pipe = draw->pipe;
stage->point = aapoint_first_point;
stage->next->flush(stage->next, flags);
/* restore original frag shader */
draw->suspend_flushing = true;
aapoint->driver_bind_fs_state(pipe, aapoint->fs ? aapoint->fs->driver_fs : NULL);
/* restore original rasterizer state */
if (draw->rast_handle) {
pipe->bind_rasterizer_state(pipe, draw->rast_handle);
}
draw->suspend_flushing = false;
draw_remove_extra_vertex_attribs(draw);
}
static void
aapoint_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
aapoint_destroy(struct draw_stage *stage)
{
struct aapoint_stage* aapoint = aapoint_stage(stage);
struct pipe_context *pipe = stage->draw->pipe;
draw_free_temp_verts(stage);
/* restore the old entry points */
pipe->create_fs_state = aapoint->driver_create_fs_state;
pipe->bind_fs_state = aapoint->driver_bind_fs_state;
pipe->delete_fs_state = aapoint->driver_delete_fs_state;
FREE(stage);
}
void
draw_aapoint_prepare_outputs(struct draw_context *draw,
struct draw_stage *stage)
{
struct aapoint_stage *aapoint = aapoint_stage(stage);
const struct pipe_rasterizer_state *rast = draw->rasterizer;
/* update vertex attrib info */
aapoint->pos_slot = draw_current_shader_position_output(draw);
if (!rast->point_smooth || rast->multisample)
return;
if (aapoint->fs && aapoint->fs->aapoint_fs) {
/* allocate the extra post-transformed vertex attribute */
aapoint->tex_slot = draw_alloc_extra_vertex_attrib(draw,
TGSI_SEMANTIC_GENERIC,
aapoint->fs->generic_attrib);
assert(aapoint->tex_slot > 0); /* output[0] is vertex pos */
} else {
aapoint->tex_slot = -1;
}
/* find psize slot in post-transform vertex */
aapoint->psize_slot = -1;
if (draw->rasterizer->point_size_per_vertex) {
const struct tgsi_shader_info *info = draw_get_shader_info(draw);
/* find PSIZ vertex output */
for (unsigned i = 0; i < info->num_outputs; i++) {
if (info->output_semantic_name[i] == TGSI_SEMANTIC_PSIZE) {
aapoint->psize_slot = i;
break;
}
}
}
}
static struct aapoint_stage *
draw_aapoint_stage(struct draw_context *draw, nir_alu_type bool_type)
{
struct aapoint_stage *aapoint = CALLOC_STRUCT(aapoint_stage);
if (!aapoint)
goto fail;
aapoint->stage.draw = draw;
aapoint->stage.name = "aapoint";
aapoint->stage.next = NULL;
aapoint->stage.point = aapoint_first_point;
aapoint->stage.line = draw_pipe_passthrough_line;
aapoint->stage.tri = draw_pipe_passthrough_tri;
aapoint->stage.flush = aapoint_flush;
aapoint->stage.reset_stipple_counter = aapoint_reset_stipple_counter;
aapoint->stage.destroy = aapoint_destroy;
aapoint->bool_type = bool_type;
if (!draw_alloc_temp_verts(&aapoint->stage, 4))
goto fail;
return aapoint;
fail:
if (aapoint)
aapoint->stage.destroy(&aapoint->stage);
return NULL;
}
static struct aapoint_stage *
aapoint_stage_from_pipe(struct pipe_context *pipe)
{
struct draw_context *draw = (struct draw_context *) pipe->draw;
return aapoint_stage(draw->pipeline.aapoint);
}
/**
* This function overrides the driver's create_fs_state() function and
* will typically be called by the gallium frontend.
*/
static void *
aapoint_create_fs_state(struct pipe_context *pipe,
const struct pipe_shader_state *fs)
{
struct aapoint_stage *aapoint = aapoint_stage_from_pipe(pipe);
struct aapoint_fragment_shader *aafs = CALLOC_STRUCT(aapoint_fragment_shader);
if (!aafs)
return NULL;
aafs->state.type = fs->type;
if (fs->type == PIPE_SHADER_IR_TGSI)
aafs->state.tokens = tgsi_dup_tokens(fs->tokens);
else
aafs->state.ir.nir = nir_shader_clone(NULL, fs->ir.nir);
/* pass-through */
aafs->driver_fs = aapoint->driver_create_fs_state(pipe, fs);
return aafs;
}
static void
aapoint_bind_fs_state(struct pipe_context *pipe, void *fs)
{
struct aapoint_stage *aapoint = aapoint_stage_from_pipe(pipe);
struct aapoint_fragment_shader *aafs = (struct aapoint_fragment_shader *) fs;
/* save current */
aapoint->fs = aafs;
/* pass-through */
aapoint->driver_bind_fs_state(pipe,
(aafs ? aafs->driver_fs : NULL));
}
static void
aapoint_delete_fs_state(struct pipe_context *pipe, void *fs)
{
struct aapoint_stage *aapoint = aapoint_stage_from_pipe(pipe);
struct aapoint_fragment_shader *aafs = (struct aapoint_fragment_shader *) fs;
/* pass-through */
aapoint->driver_delete_fs_state(pipe, aafs->driver_fs);
if (aafs->aapoint_fs)
aapoint->driver_delete_fs_state(pipe, aafs->aapoint_fs);
if (aafs->state.type == PIPE_SHADER_IR_TGSI)
FREE((void*)aafs->state.tokens);
else
ralloc_free(aafs->state.ir.nir);
FREE(aafs);
}
/**
* Called by drivers that want to install this AA point prim stage
* into the draw module's pipeline. This will not be used if the
* hardware has native support for AA points.
*/
bool
draw_install_aapoint_stage(struct draw_context *draw,
struct pipe_context *pipe,
nir_alu_type bool_type)
{
struct aapoint_stage *aapoint;
pipe->draw = (void *) draw;
/*
* Create / install AA point drawing / prim stage
*/
aapoint = draw_aapoint_stage(draw, bool_type);
if (!aapoint)
return false;
/* save original driver functions */
aapoint->driver_create_fs_state = pipe->create_fs_state;
aapoint->driver_bind_fs_state = pipe->bind_fs_state;
aapoint->driver_delete_fs_state = pipe->delete_fs_state;
/* override the driver's functions */
pipe->create_fs_state = aapoint_create_fs_state;
pipe->bind_fs_state = aapoint_bind_fs_state;
pipe->delete_fs_state = aapoint_delete_fs_state;
draw->pipeline.aapoint = &aapoint->stage;
return true;
}
+1000
View File
@@ -0,0 +1,1000 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/**
* \brief Clipping stage
*
* \author Keith Whitwell <[email protected]>
*/
#include "util/u_bitcast.h"
#include "util/u_memory.h"
#include "util/u_math.h"
#include "pipe/p_shader_tokens.h"
#include "draw_vs.h"
#include "draw_pipe.h"
#include "draw_fs.h"
#include "draw_gs.h"
/** Set to 1 to enable printing of coords before/after clipping */
#define DEBUG_CLIP 0
#define MAX_CLIPPED_VERTICES ((2 * (6 + PIPE_MAX_CLIP_PLANES))+1)
struct clip_stage {
struct draw_stage stage; /**< base class */
unsigned pos_attr;
bool have_clipdist;
int cv_attr;
/* List of the attributes to be constant interpolated. */
unsigned num_const_attribs;
uint8_t const_attribs[PIPE_MAX_SHADER_OUTPUTS];
/* List of the attributes to be linear interpolated. */
unsigned num_linear_attribs;
uint8_t linear_attribs[PIPE_MAX_SHADER_OUTPUTS];
/* List of the attributes to be perspective interpolated. */
unsigned num_perspect_attribs;
uint8_t perspect_attribs[PIPE_MAX_SHADER_OUTPUTS];
float (*plane)[4];
};
/** Cast wrapper */
static inline struct clip_stage *
clip_stage(struct draw_stage *stage)
{
return (struct clip_stage *) stage;
}
static inline unsigned
draw_viewport_index(struct draw_context *draw,
const struct vertex_header *leading_vertex)
{
if (draw_current_shader_uses_viewport_index(draw)) {
unsigned viewport_index_output =
draw_current_shader_viewport_index_output(draw);
unsigned viewport_index =
u_bitcast_f2u(leading_vertex->data[viewport_index_output][0]);
return draw_clamp_viewport_idx(viewport_index);
} else {
return 0;
}
}
#define LINTERP(T, OUT, IN) ((OUT) + (T) * ((IN) - (OUT)))
/* All attributes are float[4], so this is easy:
*/
static void
interp_attr(float dst[4],
float t,
const float in[4],
const float out[4])
{
dst[0] = LINTERP(t, out[0], in[0]);
dst[1] = LINTERP(t, out[1], in[1]);
dst[2] = LINTERP(t, out[2], in[2]);
dst[3] = LINTERP(t, out[3], in[3]);
}
/**
* Copy flat shaded attributes src vertex to dst vertex.
*/
static void
copy_flat(struct draw_stage *stage,
struct vertex_header *dst,
const struct vertex_header *src)
{
const struct clip_stage *clipper = clip_stage(stage);
for (unsigned i = 0; i < clipper->num_const_attribs; i++) {
const unsigned attr = clipper->const_attribs[i];
COPY_4FV(dst->data[attr], src->data[attr]);
}
}
/* Interpolate between two vertices to produce a third.
*/
static void
interp(const struct clip_stage *clip,
struct vertex_header *dst,
float t,
const struct vertex_header *out,
const struct vertex_header *in,
unsigned viewport_index)
{
const unsigned pos_attr = clip->pos_attr;
/* Vertex header.
*/
dst->clipmask = 0;
dst->edgeflag = 0; /* will get overwritten later */
dst->pad = 0;
dst->vertex_id = UNDEFINED_VERTEX_ID;
/* Interpolate the clip-space coords.
*/
if (clip->cv_attr >= 0) {
interp_attr(dst->data[clip->cv_attr], t,
in->data[clip->cv_attr], out->data[clip->cv_attr]);
}
/* interpolate the clip-space position */
interp_attr(dst->clip_pos, t, in->clip_pos, out->clip_pos);
/* Do the projective divide and viewport transformation to get
* new window coordinates:
*/
{
const float *pos = dst->clip_pos;
const float *scale =
clip->stage.draw->viewports[viewport_index].scale;
const float *trans =
clip->stage.draw->viewports[viewport_index].translate;
const float oow = 1.0f / pos[3];
dst->data[pos_attr][0] = pos[0] * oow * scale[0] + trans[0];
dst->data[pos_attr][1] = pos[1] * oow * scale[1] + trans[1];
dst->data[pos_attr][2] = pos[2] * oow * scale[2] + trans[2];
dst->data[pos_attr][3] = oow;
}
/* interp perspective attribs */
for (unsigned j = 0; j < clip->num_perspect_attribs; j++) {
const unsigned attr = clip->perspect_attribs[j];
interp_attr(dst->data[attr], t, in->data[attr], out->data[attr]);
}
/**
* Compute the t in screen-space instead of 3d space to use
* for noperspective interpolation.
*
* The points can be aligned with the X axis, so in that case try
* the Y. When both points are at the same screen position, we can
* pick whatever value (the interpolated point won't be in front
* anyway), so just use the 3d t.
*/
if (clip->num_linear_attribs) {
float t_nopersp = t;
/* find either in.x != out.x or in.y != out.y */
for (int k = 0; k < 2; k++) {
if (in->clip_pos[k] != out->clip_pos[k]) {
/* do divide by W, then compute linear interpolation factor */
float in_coord = in->clip_pos[k] / in->clip_pos[3];
float out_coord = out->clip_pos[k] / out->clip_pos[3];
float dst_coord = dst->clip_pos[k] / dst->clip_pos[3];
t_nopersp = (dst_coord - out_coord) / (in_coord - out_coord);
break;
}
}
for (unsigned j = 0; j < clip->num_linear_attribs; j++) {
const unsigned attr = clip->linear_attribs[j];
interp_attr(dst->data[attr], t_nopersp, in->data[attr], out->data[attr]);
}
}
}
/**
* Emit a post-clip polygon to the next pipeline stage. The polygon
* will be convex and the provoking vertex will always be vertex[0].
*/
static void
emit_poly(struct draw_stage *stage,
struct vertex_header **inlist,
const bool *edgeflags,
unsigned n,
const struct prim_header *origPrim)
{
const struct clip_stage *clipper = clip_stage(stage);
uint16_t edge_first, edge_middle, edge_last;
if (stage->draw->rasterizer->flatshade_first) {
edge_first = DRAW_PIPE_EDGE_FLAG_0;
edge_middle = DRAW_PIPE_EDGE_FLAG_1;
edge_last = DRAW_PIPE_EDGE_FLAG_2;
} else {
edge_first = DRAW_PIPE_EDGE_FLAG_2;
edge_middle = DRAW_PIPE_EDGE_FLAG_0;
edge_last = DRAW_PIPE_EDGE_FLAG_1;
}
if (!edgeflags[0])
edge_first = 0;
/* later stages may need the determinant, but only the sign matters */
struct prim_header header;
header.det = origPrim->det;
header.flags = DRAW_PIPE_RESET_STIPPLE | edge_first | edge_middle;
header.pad = 0;
for (unsigned i = 2; i < n; i++, header.flags = edge_middle) {
/* order the triangle verts to respect the provoking vertex mode */
if (stage->draw->rasterizer->flatshade_first) {
header.v[0] = inlist[0]; /* the provoking vertex */
header.v[1] = inlist[i-1];
header.v[2] = inlist[i];
} else {
header.v[0] = inlist[i-1];
header.v[1] = inlist[i];
header.v[2] = inlist[0]; /* the provoking vertex */
}
if (!edgeflags[i-1]) {
header.flags &= ~edge_middle;
}
if (i == n - 1 && edgeflags[i])
header.flags |= edge_last;
if (DEBUG_CLIP) {
debug_printf("Clipped tri: (flat-shade-first = %d)\n",
stage->draw->rasterizer->flatshade_first);
for (unsigned j = 0; j < 3; j++) {
debug_printf(" Vert %d: clip pos: %f %f %f %f\n", j,
header.v[j]->clip_pos[0],
header.v[j]->clip_pos[1],
header.v[j]->clip_pos[2],
header.v[j]->clip_pos[3]);
if (clipper->cv_attr >= 0) {
debug_printf(" Vert %d: cv: %f %f %f %f\n", j,
header.v[j]->data[clipper->cv_attr][0],
header.v[j]->data[clipper->cv_attr][1],
header.v[j]->data[clipper->cv_attr][2],
header.v[j]->data[clipper->cv_attr][3]);
}
for (unsigned k = 0; k < draw_num_shader_outputs(stage->draw); k++) {
debug_printf(" Vert %d: Attr %d: %f %f %f %f\n", j, k,
header.v[j]->data[k][0],
header.v[j]->data[k][1],
header.v[j]->data[k][2],
header.v[j]->data[k][3]);
}
}
}
stage->next->tri(stage->next, &header);
}
}
static inline float
dot4(const float *a, const float *b)
{
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
}
/*
* this function extracts the clip distance for the current plane,
* it first checks if the shader provided a clip distance, otherwise
* it works out the value using the clipvertex
*/
static inline float
getclipdist(const struct clip_stage *clipper,
struct vertex_header *vert,
int plane_idx)
{
const float *plane;
float dp;
if (plane_idx < 6) {
/* ordinary xyz view volume clipping uses pos output */
plane = clipper->plane[plane_idx];
dp = dot4(vert->clip_pos, plane);
}
else if (clipper->have_clipdist) {
/* pick the correct clipdistance element from the output vectors */
int _idx = plane_idx - 6;
int cdi = _idx >= 4;
int vidx = cdi ? _idx - 4 : _idx;
dp = vert->data[draw_current_shader_ccdistance_output(clipper->stage.draw, cdi)][vidx];
} else {
/*
* legacy user clip planes or gl_ClipVertex
*/
plane = clipper->plane[plane_idx];
if (clipper->cv_attr >= 0) {
dp = dot4(vert->data[clipper->cv_attr], plane);
}
else {
dp = dot4(vert->clip_pos, plane);
}
}
return dp;
}
/* Clip a triangle against the viewport and user clip planes.
*/
static void
do_clip_tri(struct draw_stage *stage,
struct prim_header *header,
unsigned clipmask)
{
struct clip_stage *clipper = clip_stage(stage);
struct vertex_header *a[MAX_CLIPPED_VERTICES];
struct vertex_header *b[MAX_CLIPPED_VERTICES];
struct vertex_header **inlist = a;
struct vertex_header **outlist = b;
struct vertex_header *prov_vertex;
unsigned tmpnr = 0;
unsigned n = 3;
bool aEdges[MAX_CLIPPED_VERTICES];
bool bEdges[MAX_CLIPPED_VERTICES];
bool *inEdges = aEdges;
bool *outEdges = bEdges;
int viewport_index = 0;
inlist[0] = header->v[0];
inlist[1] = header->v[1];
inlist[2] = header->v[2];
/*
* For d3d10, we need to take this from the leading (first) vertex.
* For GL, we could do anything (as long as we advertize
* GL_UNDEFINED_VERTEX for the VIEWPORT_INDEX_PROVOKING_VERTEX query),
* but it needs to be consistent with what other parts (i.e. driver)
* will do, and that seems easier with GL_PROVOKING_VERTEX logic.
*/
if (stage->draw->rasterizer->flatshade_first) {
prov_vertex = inlist[0];
} else {
prov_vertex = inlist[2];
}
viewport_index = draw_viewport_index(clipper->stage.draw, prov_vertex);
if (DEBUG_CLIP) {
const float *v0 = header->v[0]->clip_pos;
const float *v1 = header->v[1]->clip_pos;
const float *v2 = header->v[2]->clip_pos;
debug_printf("Clip triangle pos:\n");
debug_printf(" %f, %f, %f, %f\n", v0[0], v0[1], v0[2], v0[3]);
debug_printf(" %f, %f, %f, %f\n", v1[0], v1[1], v1[2], v1[3]);
debug_printf(" %f, %f, %f, %f\n", v2[0], v2[1], v2[2], v2[3]);
if (clipper->cv_attr >= 0) {
const float *v0 = header->v[0]->data[clipper->cv_attr];
const float *v1 = header->v[1]->data[clipper->cv_attr];
const float *v2 = header->v[2]->data[clipper->cv_attr];
debug_printf("Clip triangle cv:\n");
debug_printf(" %f, %f, %f, %f\n", v0[0], v0[1], v0[2], v0[3]);
debug_printf(" %f, %f, %f, %f\n", v1[0], v1[1], v1[2], v1[3]);
debug_printf(" %f, %f, %f, %f\n", v2[0], v2[1], v2[2], v2[3]);
}
}
/*
* Note: at this point we can't just use the per-vertex edge flags.
* We have to observe the edge flag bits set in header->flags which
* were set during primitive decomposition. Put those flags into
* an edge flags array which parallels the vertex array.
* Later, in the 'unfilled' pipeline stage we'll draw the edge if both
* the header.flags bit is set AND the per-vertex edgeflag field is set.
*/
inEdges[0] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_0);
inEdges[1] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_1);
inEdges[2] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_2);
while (clipmask && n >= 3) {
const unsigned plane_idx = ffs(clipmask)-1;
const bool is_user_clip_plane = plane_idx >= 6;
struct vertex_header *vert_prev = inlist[0];
bool *edge_prev = &inEdges[0];
float dp_prev;
unsigned outcount = 0;
dp_prev = getclipdist(clipper, vert_prev, plane_idx);
clipmask &= ~(1<<plane_idx);
if (util_is_inf_or_nan(dp_prev))
return; //discard nan
assert(n < MAX_CLIPPED_VERTICES);
if (n >= MAX_CLIPPED_VERTICES)
return;
inlist[n] = inlist[0]; /* prevent rotation of vertices */
inEdges[n] = inEdges[0];
for (unsigned i = 1; i <= n; i++) {
struct vertex_header *vert = inlist[i];
bool *edge = &inEdges[i];
bool different_sign;
float dp = getclipdist(clipper, vert, plane_idx);
if (util_is_inf_or_nan(dp))
return; //discard nan
if (dp_prev >= 0.0f) {
assert(outcount < MAX_CLIPPED_VERTICES);
if (outcount >= MAX_CLIPPED_VERTICES)
return;
outEdges[outcount] = *edge_prev;
outlist[outcount++] = vert_prev;
different_sign = dp < 0.0f;
} else {
different_sign = !(dp < 0.0f);
}
if (different_sign) {
struct vertex_header *new_vert;
bool *new_edge;
assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
if (tmpnr >= MAX_CLIPPED_VERTICES + 1)
return;
new_vert = clipper->stage.tmp[tmpnr++];
assert(outcount < MAX_CLIPPED_VERTICES);
if (outcount >= MAX_CLIPPED_VERTICES)
return;
new_edge = &outEdges[outcount];
outlist[outcount++] = new_vert;
float denom = dp - dp_prev;
if (dp < 0.0f) {
/* Going out of bounds. Avoid division by zero as we
* know dp != dp_prev from different_sign, above.
*/
if (-dp < dp_prev) {
float t = dp / denom;
interp(clipper, new_vert, t, vert, vert_prev, viewport_index);
} else {
float t = -dp_prev / denom;
interp(clipper, new_vert, t, vert_prev, vert, viewport_index);
}
/* Whether or not to set edge flag for the new vert depends
* on whether it's a user-defined clipping plane. We're
* copying NVIDIA's behaviour here.
*/
if (is_user_clip_plane) {
/* we want to see an edge along the clip plane */
*new_edge = true;
new_vert->edgeflag = true;
}
else {
/* we don't want to see an edge along the frustum clip plane */
*new_edge = *edge_prev;
new_vert->edgeflag = false;
}
}
else {
/* Coming back in.
*/
if (-dp_prev < dp) {
float t = -dp_prev / denom;
interp(clipper, new_vert, t, vert_prev, vert, viewport_index);
} else {
float t = dp / denom;
interp(clipper, new_vert, t, vert, vert_prev, viewport_index);
}
/* Copy starting vert's edgeflag:
*/
new_vert->edgeflag = vert_prev->edgeflag;
*new_edge = *edge_prev;
}
}
vert_prev = vert;
edge_prev = edge;
dp_prev = dp;
}
/* swap in/out lists */
SWAP(inlist, outlist);
SWAP(inEdges, outEdges);
n = outcount;
}
/* If constant interpolated, copy provoking vertex attrib to polygon vertex[0]
*/
if (n >= 3) {
if (clipper->num_const_attribs) {
if (stage->draw->rasterizer->flatshade_first) {
if (inlist[0] != header->v[0]) {
assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
if (tmpnr >= MAX_CLIPPED_VERTICES + 1)
return;
inlist[0] = dup_vert(stage, inlist[0], tmpnr++);
copy_flat(stage, inlist[0], header->v[0]);
}
}
else {
if (inlist[0] != header->v[2]) {
assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
if (tmpnr >= MAX_CLIPPED_VERTICES + 1)
return;
inlist[0] = dup_vert(stage, inlist[0], tmpnr++);
copy_flat(stage, inlist[0], header->v[2]);
}
}
}
/* Emit the polygon as triangles to the setup stage:
*/
emit_poly(stage, inlist, inEdges, n, header);
}
}
/* Clip a line against the viewport and user clip planes.
*/
static void
do_clip_line(struct draw_stage *stage,
struct prim_header *header,
unsigned clipmask)
{
const struct clip_stage *clipper = clip_stage(stage);
struct vertex_header *v0 = header->v[0];
struct vertex_header *v1 = header->v[1];
struct vertex_header *prov_vertex;
float t0 = 0.0F;
float t1 = 0.0F;
struct prim_header newprim;
int viewport_index;
newprim.flags = header->flags;
if (stage->draw->rasterizer->flatshade_first) {
prov_vertex = v0;
}
else {
prov_vertex = v1;
}
viewport_index = draw_viewport_index(clipper->stage.draw, prov_vertex);
while (clipmask) {
const unsigned plane_idx = ffs(clipmask)-1;
const float dp0 = getclipdist(clipper, v0, plane_idx);
const float dp1 = getclipdist(clipper, v1, plane_idx);
if (util_is_inf_or_nan(dp0) || util_is_inf_or_nan(dp1))
return; //discard nan
if (dp1 < 0.0F) {
float t = dp1 / (dp1 - dp0);
t1 = MAX2(t1, t);
}
if (dp0 < 0.0F) {
float t = dp0 / (dp0 - dp1);
t0 = MAX2(t0, t);
}
if (t0 + t1 >= 1.0F)
return; /* discard */
clipmask &= ~(1 << plane_idx); /* turn off this plane's bit */
}
if (v0->clipmask) {
interp(clipper, stage->tmp[0], t0, v0, v1, viewport_index);
if (stage->draw->rasterizer->flatshade_first) {
copy_flat(stage, stage->tmp[0], v0); /* copy v0 color to tmp[0] */
}
else {
copy_flat(stage, stage->tmp[0], v1); /* copy v1 color to tmp[0] */
}
newprim.v[0] = stage->tmp[0];
}
else {
newprim.v[0] = v0;
}
if (v1->clipmask) {
interp(clipper, stage->tmp[1], t1, v1, v0, viewport_index);
if (stage->draw->rasterizer->flatshade_first) {
copy_flat(stage, stage->tmp[1], v0); /* copy v0 color to tmp[1] */
}
else {
copy_flat(stage, stage->tmp[1], v1); /* copy v1 color to tmp[1] */
}
newprim.v[1] = stage->tmp[1];
}
else {
newprim.v[1] = v1;
}
stage->next->line(stage->next, &newprim);
}
static void
clip_point(struct draw_stage *stage, struct prim_header *header)
{
if (header->v[0]->clipmask == 0)
stage->next->point(stage->next, header);
}
/*
* Clip points but ignore the first 4 (xy) clip planes.
* (Because the generated clip mask is completely unaffacted by guard band,
* we still need to manually evaluate the x/y planes if they are outside
* the guard band and not just outside the vp.)
*/
static void
clip_point_guard_xy(struct draw_stage *stage, struct prim_header *header)
{
unsigned clipmask = header->v[0]->clipmask;
if ((clipmask & 0xffffffff) == 0)
stage->next->point(stage->next, header);
else if ((clipmask & 0xfffffff0) == 0) {
while (clipmask) {
const unsigned plane_idx = ffs(clipmask)-1;
clipmask &= ~(1 << plane_idx); /* turn off this plane's bit */
/* TODO: this should really do proper guardband clipping,
* currently just throw out infs/nans.
* Also note that vertices with negative w values MUST be tossed
* out (not sure if proper guardband clipping would do this
* automatically). These would usually be captured by depth clip
* too but this can be disabled.
*/
if (header->v[0]->clip_pos[3] <= 0.0f ||
util_is_inf_or_nan(header->v[0]->clip_pos[0]) ||
util_is_inf_or_nan(header->v[0]->clip_pos[1]))
return;
}
stage->next->point(stage->next, header);
}
}
static void
clip_first_point(struct draw_stage *stage, struct prim_header *header)
{
stage->point = stage->draw->guard_band_points_lines_xy ? clip_point_guard_xy : clip_point;
stage->point(stage, header);
}
static void
clip_line(struct draw_stage *stage, struct prim_header *header)
{
unsigned clipmask = (header->v[0]->clipmask |
header->v[1]->clipmask);
if (clipmask == 0) {
/* no clipping needed */
stage->next->line(stage->next, header);
}
else if ((header->v[0]->clipmask &
header->v[1]->clipmask) == 0) {
do_clip_line(stage, header, clipmask);
}
/* else, totally clipped */
}
static void
clip_line_guard_xy(struct draw_stage *stage, struct prim_header *header)
{
unsigned clipmask = (header->v[0]->clipmask |
header->v[1]->clipmask);
if ((clipmask & 0xffffffff) == 0) {
stage->next->line(stage->next, header);
}
else if ((clipmask & 0xfffffff0) == 0) {
while (clipmask) {
const unsigned plane_idx = ffs(clipmask)-1;
clipmask &= ~(1 << plane_idx); /* turn off this plane's bit */
/* TODO: this should really do proper guardband clipping,
* currently just throw out infs/nans.
* Also note that vertices with negative w values MUST be tossed
* out (not sure if proper guardband clipping would do this
* automatically). These would usually be captured by depth clip
* too but this can be disabled.
*/
if ((header->v[0]->clip_pos[3] <= 0.0f &&
header->v[1]->clip_pos[3] <= 0.0f) ||
util_is_nan(header->v[0]->clip_pos[0]) ||
util_is_nan(header->v[0]->clip_pos[1]) ||
util_is_nan(header->v[1]->clip_pos[0]) ||
util_is_nan(header->v[1]->clip_pos[1]))
return;
}
stage->next->line(stage->next, header);
} else if ((header->v[0]->clipmask &
header->v[1]->clipmask) == 0) {
do_clip_line(stage, header, clipmask & 0xfffffff0);
}
}
static void
clip_tri(struct draw_stage *stage, struct prim_header *header)
{
unsigned clipmask = (header->v[0]->clipmask |
header->v[1]->clipmask |
header->v[2]->clipmask);
if (clipmask == 0) {
/* no clipping needed */
stage->next->tri(stage->next, header);
}
else if ((header->v[0]->clipmask &
header->v[1]->clipmask &
header->v[2]->clipmask) == 0) {
do_clip_tri(stage, header, clipmask);
}
}
static enum tgsi_interpolate_mode
find_interp(const struct draw_fragment_shader *fs,
enum tgsi_interpolate_mode *indexed_interp,
enum tgsi_semantic semantic_name, unsigned semantic_index)
{
enum tgsi_interpolate_mode interp;
/* If it's gl_{Front,Back}{,Secondary}Color, pick up the mode
* from the array we've filled before. */
if ((semantic_name == TGSI_SEMANTIC_COLOR ||
semantic_name == TGSI_SEMANTIC_BCOLOR) &&
semantic_index < 2) {
interp = indexed_interp[semantic_index];
} else if (semantic_name == TGSI_SEMANTIC_POSITION ||
semantic_name == TGSI_SEMANTIC_CLIPVERTEX) {
/* these inputs are handled specially always */
return -1;
} else {
/* Otherwise, search in the FS inputs, with a decent default
* if we don't find it.
* This probably only matters for layer, vpindex, culldist, maybe
* front_face.
*/
unsigned j;
if (semantic_name == TGSI_SEMANTIC_LAYER ||
semantic_name == TGSI_SEMANTIC_VIEWPORT_INDEX) {
interp = TGSI_INTERPOLATE_CONSTANT;
}
else {
interp = TGSI_INTERPOLATE_PERSPECTIVE;
}
if (fs) {
for (j = 0; j < fs->info.num_inputs; j++) {
if (semantic_name == fs->info.input_semantic_name[j] &&
semantic_index == fs->info.input_semantic_index[j]) {
interp = fs->info.input_interpolate[j];
break;
}
}
}
}
return interp;
}
/* Update state. Could further delay this until we hit the first
* primitive that really requires clipping.
*/
static void
clip_init_state(struct draw_stage *stage)
{
struct clip_stage *clipper = clip_stage(stage);
const struct draw_context *draw = stage->draw;
const struct draw_fragment_shader *fs = draw->fs.fragment_shader;
const struct tgsi_shader_info *info = draw_get_shader_info(draw);
clipper->pos_attr = draw_current_shader_position_output(draw);
clipper->have_clipdist = draw_current_shader_num_written_clipdistances(draw) > 0;
if (draw_current_shader_clipvertex_output(draw) != clipper->pos_attr) {
clipper->cv_attr = (int)draw_current_shader_clipvertex_output(draw);
}
else {
clipper->cv_attr = -1;
}
/* We need to know for each attribute what kind of interpolation is
* done on it (flat, smooth or noperspective). But the information
* is not directly accessible for outputs, only for inputs. So we
* have to match semantic name and index between the VS (or GS/ES)
* outputs and the FS inputs to get to the interpolation mode.
*
* The only hitch is with gl_FrontColor/gl_BackColor which map to
* gl_Color, and their Secondary versions. First there are (up to)
* two outputs for one input, so we tuck the information in a
* specific array. Second if they don't have qualifiers, the
* default value has to be picked from the global shade mode.
*
* Of course, if we don't have a fragment shader in the first
* place, defaults should be used.
*/
/* First pick up the interpolation mode for
* gl_Color/gl_SecondaryColor, with the correct default.
*/
enum tgsi_interpolate_mode indexed_interp[2];
indexed_interp[0] = indexed_interp[1] = draw->rasterizer->flatshade ?
TGSI_INTERPOLATE_CONSTANT : TGSI_INTERPOLATE_PERSPECTIVE;
if (fs) {
for (unsigned i = 0; i < fs->info.num_inputs; i++) {
if (fs->info.input_semantic_name[i] == TGSI_SEMANTIC_COLOR &&
fs->info.input_semantic_index[i] < 2) {
if (fs->info.input_interpolate[i] != TGSI_INTERPOLATE_COLOR)
indexed_interp[fs->info.input_semantic_index[i]] = fs->info.input_interpolate[i];
}
}
}
/* Then resolve the interpolation mode for every output attribute. */
clipper->num_const_attribs = 0;
clipper->num_linear_attribs = 0;
clipper->num_perspect_attribs = 0;
unsigned i;
for (i = 0; i < info->num_outputs; i++) {
/* Find the interpolation mode for a specific attribute */
int interp = find_interp(fs, indexed_interp,
info->output_semantic_name[i],
info->output_semantic_index[i]);
switch (interp) {
case TGSI_INTERPOLATE_CONSTANT:
clipper->const_attribs[clipper->num_const_attribs] = i;
clipper->num_const_attribs++;
break;
case TGSI_INTERPOLATE_LINEAR:
clipper->linear_attribs[clipper->num_linear_attribs] = i;
clipper->num_linear_attribs++;
break;
case TGSI_INTERPOLATE_PERSPECTIVE:
clipper->perspect_attribs[clipper->num_perspect_attribs] = i;
clipper->num_perspect_attribs++;
break;
case TGSI_INTERPOLATE_COLOR:
if (draw->rasterizer->flatshade) {
clipper->const_attribs[clipper->num_const_attribs] = i;
clipper->num_const_attribs++;
} else {
clipper->perspect_attribs[clipper->num_perspect_attribs] = i;
clipper->num_perspect_attribs++;
}
break;
default:
assert(interp == -1);
break;
}
}
/* Search the extra vertex attributes */
for (unsigned j = 0; j < draw->extra_shader_outputs.num; j++) {
/* Find the interpolation mode for a specific attribute */
enum tgsi_interpolate_mode interp =
find_interp(fs, indexed_interp,
draw->extra_shader_outputs.semantic_name[j],
draw->extra_shader_outputs.semantic_index[j]);
switch (interp) {
case TGSI_INTERPOLATE_CONSTANT:
clipper->const_attribs[clipper->num_const_attribs] = i + j;
clipper->num_const_attribs++;
break;
case TGSI_INTERPOLATE_LINEAR:
clipper->linear_attribs[clipper->num_linear_attribs] = i + j;
clipper->num_linear_attribs++;
break;
case TGSI_INTERPOLATE_PERSPECTIVE:
clipper->perspect_attribs[clipper->num_perspect_attribs] = i + j;
clipper->num_perspect_attribs++;
break;
default:
assert(interp == -1);
break;
}
}
stage->tri = clip_tri;
}
static void
clip_first_tri(struct draw_stage *stage,
struct prim_header *header)
{
clip_init_state(stage);
stage->tri(stage, header);
}
static void
clip_first_line(struct draw_stage *stage,
struct prim_header *header)
{
clip_init_state(stage);
stage->line = stage->draw->guard_band_points_lines_xy ? clip_line_guard_xy : clip_line;
stage->line(stage, header);
}
static void
clip_flush(struct draw_stage *stage, unsigned flags)
{
stage->tri = clip_first_tri;
stage->line = clip_first_line;
stage->next->flush(stage->next, flags);
}
static void
clip_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
clip_destroy(struct draw_stage *stage)
{
draw_free_temp_verts(stage);
FREE(stage);
}
/**
* Allocate a new clipper stage.
* \return pointer to new stage object
*/
struct draw_stage *
draw_clip_stage(struct draw_context *draw)
{
struct clip_stage *clipper = CALLOC_STRUCT(clip_stage);
if (!clipper)
goto fail;
clipper->stage.draw = draw;
clipper->stage.name = "clipper";
clipper->stage.point = clip_first_point;
clipper->stage.line = clip_first_line;
clipper->stage.tri = clip_first_tri;
clipper->stage.flush = clip_flush;
clipper->stage.reset_stipple_counter = clip_reset_stipple_counter;
clipper->stage.destroy = clip_destroy;
clipper->plane = draw->plane;
if (!draw_alloc_temp_verts(&clipper->stage, MAX_CLIPPED_VERTICES+1))
goto fail;
return &clipper->stage;
fail:
if (clipper)
clipper->stage.destroy(&clipper->stage);
return NULL;
}
+171
View File
@@ -0,0 +1,171 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/**
* \brief Drawing stage for polygon culling
*/
/* Authors: Keith Whitwell <[email protected]>
*/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "pipe/p_defines.h"
#include "draw_pipe.h"
struct cull_stage {
struct draw_stage stage;
unsigned cull_face; /**< which face(s) to cull (one of PIPE_FACE_x) */
unsigned front_ccw;
};
static inline struct cull_stage *
cull_stage(struct draw_stage *stage)
{
return (struct cull_stage *)stage;
}
/*
* Triangles can be culled using regular face cull.
*/
static void
cull_tri(struct draw_stage *stage,
struct prim_header *header)
{
const unsigned pos = draw_current_shader_position_output(stage->draw);
/* Window coords: */
const float *v0 = header->v[0]->data[pos];
const float *v1 = header->v[1]->data[pos];
const float *v2 = header->v[2]->data[pos];
/* edge vectors: e = v0 - v2, f = v1 - v2 */
const float ex = v0[0] - v2[0];
const float ey = v0[1] - v2[1];
const float fx = v1[0] - v2[0];
const float fy = v1[1] - v2[1];
/* det = cross(e,f).z */
header->det = ex * fy - ey * fx;
if (header->det != 0) {
/* if det < 0 then Z points toward the camera and the triangle is
* counter-clockwise winding.
*/
unsigned ccw = (header->det < 0);
unsigned face = ((ccw == cull_stage(stage)->front_ccw) ?
PIPE_FACE_FRONT :
PIPE_FACE_BACK);
if ((face & cull_stage(stage)->cull_face) == 0) {
/* triangle is not culled, pass to next stage */
stage->next->tri(stage->next, header);
}
} else {
/*
* With zero area, this is back facing (because the spec says
* it's front facing if sign is positive?).
* Some apis apparently do not allow us to cull zero area tris
* here, in case of fill mode line (which is rather lame).
*/
if ((PIPE_FACE_BACK & cull_stage(stage)->cull_face) == 0) {
stage->next->tri(stage->next, header);
}
}
}
static void
cull_first_tri(struct draw_stage *stage,
struct prim_header *header)
{
struct cull_stage *cull = cull_stage(stage);
cull->cull_face = stage->draw->rasterizer->cull_face;
cull->front_ccw = stage->draw->rasterizer->front_ccw;
stage->tri = cull_tri;
stage->tri(stage, header);
}
static void
cull_flush(struct draw_stage *stage, unsigned flags)
{
stage->tri = cull_first_tri;
stage->next->flush(stage->next, flags);
}
static void
cull_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
cull_destroy(struct draw_stage *stage)
{
draw_free_temp_verts(stage);
FREE(stage);
}
/**
* Create a new polygon culling stage.
*/
struct draw_stage *
draw_cull_stage(struct draw_context *draw)
{
struct cull_stage *cull = CALLOC_STRUCT(cull_stage);
if (!cull)
goto fail;
cull->stage.draw = draw;
cull->stage.name = "cull";
cull->stage.next = NULL;
cull->stage.point = draw_pipe_passthrough_point;
cull->stage.line = draw_pipe_passthrough_line;
cull->stage.tri = cull_first_tri;
cull->stage.flush = cull_flush;
cull->stage.reset_stipple_counter = cull_reset_stipple_counter;
cull->stage.destroy = cull_destroy;
if (!draw_alloc_temp_verts(&cull->stage, 0))
goto fail;
return &cull->stage;
fail:
if (cull)
cull->stage.destroy(&cull->stage);
return NULL;
}
@@ -0,0 +1,350 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/* Authors: Keith Whitwell <[email protected]>
*/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "pipe/p_shader_tokens.h"
#include "draw_vs.h"
#include "draw_fs.h"
#include "draw_pipe.h"
/** subclass of draw_stage */
struct flat_stage
{
struct draw_stage stage;
unsigned num_flat_attribs;
unsigned flat_attribs[PIPE_MAX_SHADER_OUTPUTS]; /* flatshaded attribs */
};
static inline struct flat_stage *
flat_stage(struct draw_stage *stage)
{
return (struct flat_stage *) stage;
}
/** Copy all the constant attributes from 'src' vertex to 'dst' vertex */
static inline void
copy_flats(struct draw_stage *stage,
struct vertex_header *dst,
const struct vertex_header *src)
{
const struct flat_stage *flat = flat_stage(stage);
for (unsigned i = 0; i < flat->num_flat_attribs; i++) {
const unsigned attr = flat->flat_attribs[i];
COPY_4FV(dst->data[attr], src->data[attr]);
}
}
/** Copy all the color attributes from src vertex to dst0 & dst1 vertices */
static inline void
copy_flats2(struct draw_stage *stage,
struct vertex_header *dst0,
struct vertex_header *dst1,
const struct vertex_header *src)
{
const struct flat_stage *flat = flat_stage(stage);
for (unsigned i = 0; i < flat->num_flat_attribs; i++) {
const unsigned attr = flat->flat_attribs[i];
COPY_4FV(dst0->data[attr], src->data[attr]);
COPY_4FV(dst1->data[attr], src->data[attr]);
}
}
/**
* Flatshade tri. Not required for clipping which handles this on its own,
* but required for unfilled tris and other primitive-changing stages
* (like widelines). If no such stages are active, handled by hardware.
*/
static void
flatshade_tri_0(struct draw_stage *stage,
struct prim_header *header)
{
struct prim_header tmp;
tmp.det = header->det;
tmp.flags = header->flags;
tmp.pad = header->pad;
tmp.v[0] = header->v[0];
tmp.v[1] = dup_vert(stage, header->v[1], 0);
tmp.v[2] = dup_vert(stage, header->v[2], 1);
copy_flats2(stage, tmp.v[1], tmp.v[2], tmp.v[0]);
stage->next->tri(stage->next, &tmp);
}
static void
flatshade_tri_2(struct draw_stage *stage,
struct prim_header *header)
{
struct prim_header tmp;
tmp.det = header->det;
tmp.flags = header->flags;
tmp.pad = header->pad;
tmp.v[0] = dup_vert(stage, header->v[0], 0);
tmp.v[1] = dup_vert(stage, header->v[1], 1);
tmp.v[2] = header->v[2];
copy_flats2(stage, tmp.v[0], tmp.v[1], tmp.v[2]);
stage->next->tri(stage->next, &tmp);
}
/**
* Flatshade line.
*/
static void
flatshade_line_0(struct draw_stage *stage,
struct prim_header *header)
{
struct prim_header tmp;
tmp.det = header->det;
tmp.flags = header->flags;
tmp.pad = header->pad;
tmp.v[0] = header->v[0];
tmp.v[1] = dup_vert(stage, header->v[1], 0);
copy_flats(stage, tmp.v[1], tmp.v[0]);
stage->next->line(stage->next, &tmp);
}
static void
flatshade_line_1(struct draw_stage *stage,
struct prim_header *header)
{
struct prim_header tmp;
tmp.det = header->det;
tmp.flags = header->flags;
tmp.pad = header->pad;
tmp.v[0] = dup_vert(stage, header->v[0], 0);
tmp.v[1] = header->v[1];
copy_flats(stage, tmp.v[0], tmp.v[1]);
stage->next->line(stage->next, &tmp);
}
static int
find_interp(const struct draw_fragment_shader *fs, int *indexed_interp,
enum tgsi_semantic semantic_name, unsigned semantic_index)
{
int interp;
/* If it's gl_{Front,Back}{,Secondary}Color, pick up the mode
* from the array we've filled before. */
if ((semantic_name == TGSI_SEMANTIC_COLOR ||
semantic_name == TGSI_SEMANTIC_BCOLOR) &&
semantic_index < 2) {
interp = indexed_interp[semantic_index];
} else {
/* Otherwise, search in the FS inputs, with a decent default
* if we don't find it.
*/
interp = TGSI_INTERPOLATE_PERSPECTIVE;
if (fs) {
for (unsigned j = 0; j < fs->info.num_inputs; j++) {
if (semantic_name == fs->info.input_semantic_name[j] &&
semantic_index == fs->info.input_semantic_index[j]) {
interp = fs->info.input_interpolate[j];
break;
}
}
}
}
return interp;
}
static void
flatshade_init_state(struct draw_stage *stage)
{
struct flat_stage *flat = flat_stage(stage);
const struct draw_context *draw = stage->draw;
const struct draw_fragment_shader *fs = draw->fs.fragment_shader;
const struct tgsi_shader_info *info = draw_get_shader_info(draw);
unsigned i, j;
/* Find which vertex shader outputs need constant interpolation, make a list */
/* XXX: this code is a near exact copy of the one in clip_init_state.
* The latter also cares about perspective though.
*/
/* First pick up the interpolation mode for
* gl_Color/gl_SecondaryColor, with the correct default.
*/
int indexed_interp[2];
indexed_interp[0] = indexed_interp[1] = draw->rasterizer->flatshade ?
TGSI_INTERPOLATE_CONSTANT : TGSI_INTERPOLATE_PERSPECTIVE;
if (fs) {
for (i = 0; i < fs->info.num_inputs; i++) {
if (fs->info.input_semantic_name[i] == TGSI_SEMANTIC_COLOR &&
fs->info.input_semantic_index[i] < 2) {
if (fs->info.input_interpolate[i] != TGSI_INTERPOLATE_COLOR)
indexed_interp[fs->info.input_semantic_index[i]] = fs->info.input_interpolate[i];
}
}
}
/* Then resolve the interpolation mode for every output attribute.
*
* Given how the rest of the code, the most efficient way is to
* have a vector of flat-mode attributes.
*/
flat->num_flat_attribs = 0;
for (i = 0; i < info->num_outputs; i++) {
/* Find the interpolation mode for a specific attribute */
int interp = find_interp(fs, indexed_interp,
info->output_semantic_name[i],
info->output_semantic_index[i]);
/* If it's flat, add it to the flat vector. */
if (interp == TGSI_INTERPOLATE_CONSTANT ||
(interp == TGSI_INTERPOLATE_COLOR && draw->rasterizer->flatshade)) {
flat->flat_attribs[flat->num_flat_attribs] = i;
flat->num_flat_attribs++;
}
}
/* Search the extra vertex attributes */
for (j = 0; j < draw->extra_shader_outputs.num; j++) {
/* Find the interpolation mode for a specific attribute */
int interp = find_interp(fs, indexed_interp,
draw->extra_shader_outputs.semantic_name[j],
draw->extra_shader_outputs.semantic_index[j]);
/* If it's flat, add it to the flat vector. */
if (interp == TGSI_INTERPOLATE_CONSTANT) {
flat->flat_attribs[flat->num_flat_attribs] = i + j;
flat->num_flat_attribs++;
}
}
/* Choose flatshade routine according to provoking vertex:
*/
if (draw->rasterizer->flatshade_first) {
stage->line = flatshade_line_0;
stage->tri = flatshade_tri_0;
} else {
stage->line = flatshade_line_1;
stage->tri = flatshade_tri_2;
}
}
static void
flatshade_first_tri(struct draw_stage *stage,
struct prim_header *header)
{
flatshade_init_state(stage);
stage->tri(stage, header);
}
static void
flatshade_first_line(struct draw_stage *stage,
struct prim_header *header)
{
flatshade_init_state(stage);
stage->line(stage, header);
}
static void
flatshade_flush(struct draw_stage *stage,
unsigned flags)
{
stage->tri = flatshade_first_tri;
stage->line = flatshade_first_line;
stage->next->flush(stage->next, flags);
}
static void
flatshade_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
flatshade_destroy(struct draw_stage *stage)
{
draw_free_temp_verts(stage);
FREE(stage);
}
/**
* Create flatshading drawing stage.
*/
struct draw_stage *
draw_flatshade_stage(struct draw_context *draw)
{
struct flat_stage *flatshade = CALLOC_STRUCT(flat_stage);
if (!flatshade)
goto fail;
flatshade->stage.draw = draw;
flatshade->stage.name = "flatshade";
flatshade->stage.next = NULL;
flatshade->stage.point = draw_pipe_passthrough_point;
flatshade->stage.line = flatshade_first_line;
flatshade->stage.tri = flatshade_first_tri;
flatshade->stage.flush = flatshade_flush;
flatshade->stage.reset_stipple_counter = flatshade_reset_stipple_counter;
flatshade->stage.destroy = flatshade_destroy;
if (!draw_alloc_temp_verts(&flatshade->stage, 2))
goto fail;
return &flatshade->stage;
fail:
if (flatshade)
flatshade->stage.destroy(&flatshade->stage);
return NULL;
}
@@ -0,0 +1,255 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/**
* \brief polygon offset state
*
* \author Keith Whitwell <[email protected]>
* \author Brian Paul
*/
#include "util/format/u_format.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "draw_pipe.h"
struct offset_stage {
struct draw_stage stage;
float scale;
float units;
float clamp;
};
static inline struct offset_stage *
offset_stage(struct draw_stage *stage)
{
return (struct offset_stage *) stage;
}
/**
* Offset tri Z. Some hardware can handle this, but not usually when
* doing unfilled rendering.
*/
static void
do_offset_tri(struct draw_stage *stage,
struct prim_header *header)
{
const unsigned pos = draw_current_shader_position_output(stage->draw);
struct offset_stage *offset = offset_stage(stage);
float inv_det = 1.0f / header->det;
/* Window coords:
*/
float *v0 = header->v[0]->data[pos];
float *v1 = header->v[1]->data[pos];
float *v2 = header->v[2]->data[pos];
/* edge vectors e = v0 - v2, f = v1 - v2 */
float ex = v0[0] - v2[0];
float ey = v0[1] - v2[1];
float ez = v0[2] - v2[2];
float fx = v1[0] - v2[0];
float fy = v1[1] - v2[1];
float fz = v1[2] - v2[2];
/* (a,b) = cross(e,f).xy */
float a = ey*fz - ez*fy;
float b = ez*fx - ex*fz;
float dzdx = fabsf(a * inv_det);
float dzdy = fabsf(b * inv_det);
float mult = MAX2(dzdx, dzdy) * offset->scale;
float zoffset;
if (stage->draw->floating_point_depth) {
float bias;
union fi maxz;
maxz.f = MAX3(fabs(v0[2]), fabs(v1[2]), fabs(v2[2]));
/* just do the math directly on shifted number */
maxz.ui &= 0xff << 23;
maxz.i -= 23 << 23;
/* Clamping to zero means mrd will be zero for very small numbers,
* but specs do not indicate this should be prevented by clamping
* mrd to smallest normal number instead. */
maxz.i = MAX2(maxz.i, 0);
bias = offset->units * maxz.f;
zoffset = bias + mult;
} else {
zoffset = offset->units + mult;
}
if (offset->clamp)
zoffset = (offset->clamp < 0.0f) ? MAX2(zoffset, offset->clamp) :
MIN2(zoffset, offset->clamp);
/*
* Note: we're applying the offset and clamping per-vertex.
* Ideally, the offset is applied per-fragment prior to fragment shading.
*/
v0[2] = SATURATE(v0[2] + zoffset);
v1[2] = SATURATE(v1[2] + zoffset);
v2[2] = SATURATE(v2[2] + zoffset);
stage->next->tri(stage->next, header);
}
static void
offset_tri(struct draw_stage *stage,
struct prim_header *header)
{
struct prim_header tmp;
tmp.det = header->det;
tmp.flags = header->flags;
tmp.pad = header->pad;
tmp.v[0] = dup_vert(stage, header->v[0], 0);
tmp.v[1] = dup_vert(stage, header->v[1], 1);
tmp.v[2] = dup_vert(stage, header->v[2], 2);
do_offset_tri(stage, &tmp);
}
static void
offset_first_tri(struct draw_stage *stage,
struct prim_header *header)
{
struct offset_stage *offset = offset_stage(stage);
const struct pipe_rasterizer_state *rast = stage->draw->rasterizer;
unsigned fill_mode = rast->fill_front;
bool do_offset;
if (rast->fill_back != rast->fill_front) {
/* Need to check for back-facing triangle */
bool ccw = header->det < 0.0f;
if (ccw != rast->front_ccw)
fill_mode = rast->fill_back;
}
/* Now determine if we need to do offsetting for the point/line/fill mode */
switch (fill_mode) {
case PIPE_POLYGON_MODE_FILL:
do_offset = rast->offset_tri;
break;
case PIPE_POLYGON_MODE_LINE:
do_offset = rast->offset_line;
break;
case PIPE_POLYGON_MODE_POINT:
do_offset = rast->offset_point;
break;
default:
assert(!"invalid fill_mode in offset_first_tri()");
do_offset = rast->offset_tri;
}
if (do_offset) {
offset->scale = rast->offset_scale;
offset->clamp = rast->offset_clamp;
/*
* If depth is floating point, depth bias is calculated with respect
* to the primitive's maximum Z value. Retain the original depth bias
* value until that stage.
*/
if (stage->draw->floating_point_depth) {
offset->units = (float) rast->offset_units;
} else {
offset->units = (float) (rast->offset_units * stage->draw->mrd * 2);
}
} else {
offset->scale = 0.0f;
offset->clamp = 0.0f;
offset->units = 0.0f;
}
stage->tri = offset_tri;
stage->tri(stage, header);
}
static void
offset_flush(struct draw_stage *stage,
unsigned flags)
{
stage->tri = offset_first_tri;
stage->next->flush(stage->next, flags);
}
static void
offset_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
offset_destroy(struct draw_stage *stage)
{
draw_free_temp_verts(stage);
FREE(stage);
}
/**
* Create polygon offset drawing stage.
*/
struct draw_stage *
draw_offset_stage(struct draw_context *draw)
{
struct offset_stage *offset = CALLOC_STRUCT(offset_stage);
if (!offset)
goto fail;
offset->stage.draw = draw;
offset->stage.name = "offset";
offset->stage.next = NULL;
offset->stage.point = draw_pipe_passthrough_point;
offset->stage.line = draw_pipe_passthrough_line;
offset->stage.tri = offset_first_tri;
offset->stage.flush = offset_flush;
offset->stage.reset_stipple_counter = offset_reset_stipple_counter;
offset->stage.destroy = offset_destroy;
if (!draw_alloc_temp_verts(&offset->stage, 3))
goto fail;
return &offset->stage;
fail:
if (offset)
offset->stage.destroy(&offset->stage);
return NULL;
}
@@ -0,0 +1,516 @@
/**************************************************************************
*
* Copyright 2008 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.
*
**************************************************************************/
/**
* Polygon stipple stage: implement polygon stipple with texture map and
* fragment program. The fragment program samples the texture using the
* fragment window coordinate register and does a fragment kill for the
* stipple-failing fragments.
*
* Authors: Brian Paul
*/
#include "pipe/p_context.h"
#include "pipe/p_defines.h"
#include "pipe/p_shader_tokens.h"
#include "util/u_inlines.h"
#include "util/format/u_format.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/u_pstipple.h"
#include "util/u_sampler.h"
#include "tgsi/tgsi_parse.h"
#include "draw_context.h"
#include "draw_pipe.h"
#include "nir.h"
#include "nir/nir_draw_helpers.h"
/** Approx number of new tokens for instructions in pstip_transform_inst() */
#define NUM_NEW_TOKENS 53
/**
* Subclass of pipe_shader_state to carry extra fragment shader info.
*/
struct pstip_fragment_shader
{
struct pipe_shader_state state;
void *driver_fs;
void *pstip_fs;
unsigned sampler_unit;
};
/**
* Subclass of draw_stage
*/
struct pstip_stage
{
struct draw_stage stage;
void *sampler_cso;
struct pipe_resource *texture;
struct pipe_sampler_view *sampler_view;
unsigned num_samplers;
unsigned num_sampler_views;
/*
* Currently bound state
*/
struct pstip_fragment_shader *fs;
struct {
void *samplers[PIPE_MAX_SAMPLERS];
struct pipe_sampler_view *sampler_views[PIPE_MAX_SHADER_SAMPLER_VIEWS];
const struct pipe_poly_stipple *stipple;
} state;
/*
* Driver interface/override functions
*/
void * (*driver_create_fs_state)(struct pipe_context *,
const struct pipe_shader_state *);
void (*driver_bind_fs_state)(struct pipe_context *, void *);
void (*driver_delete_fs_state)(struct pipe_context *, void *);
void (*driver_bind_sampler_states)(struct pipe_context *,
mesa_shader_stage,
unsigned, unsigned, void **);
void (*driver_set_sampler_views)(struct pipe_context *,
mesa_shader_stage shader,
unsigned start, unsigned count,
unsigned unbind_num_trailing_slots,
struct pipe_sampler_view **);
void (*driver_set_polygon_stipple)(struct pipe_context *,
const struct pipe_poly_stipple *);
struct pipe_context *pipe;
};
/**
* Generate the frag shader we'll use for doing polygon stipple.
* This will be the user's shader prefixed with a TEX and KIL instruction.
*/
static bool
generate_pstip_fs(struct pstip_stage *pstip)
{
struct pipe_context *pipe = pstip->pipe;
struct pipe_screen *screen = pipe->screen;
const struct pipe_shader_state *orig_fs = &pstip->fs->state;
/*struct draw_context *draw = pstip->stage.draw;*/
struct pipe_shader_state pstip_fs;
enum tgsi_file_type wincoord_file;
wincoord_file = screen->caps.fs_position_is_sysval ?
TGSI_FILE_SYSTEM_VALUE : TGSI_FILE_INPUT;
pstip_fs = *orig_fs; /* copy to init */
if (orig_fs->type == PIPE_SHADER_IR_TGSI) {
pstip_fs.tokens = util_pstipple_create_fragment_shader(orig_fs->tokens,
&pstip->fs->sampler_unit,
0,
wincoord_file);
if (pstip_fs.tokens == NULL)
return false;
} else {
pstip_fs.ir.nir = nir_shader_clone(NULL, orig_fs->ir.nir);
nir_lower_pstipple_fs(pstip_fs.ir.nir,
&pstip->fs->sampler_unit, 0, wincoord_file == TGSI_FILE_SYSTEM_VALUE,
nir_type_bool1);
}
assert(pstip->fs->sampler_unit < PIPE_MAX_SAMPLERS);
pstip->fs->pstip_fs = pstip->driver_create_fs_state(pipe, &pstip_fs);
FREE((void *)pstip_fs.tokens);
if (!pstip->fs->pstip_fs)
return false;
return true;
}
/**
* When we're about to draw our first stipple polygon in a batch, this function
* is called to tell the driver to bind our modified fragment shader.
*/
static bool
bind_pstip_fragment_shader(struct pstip_stage *pstip)
{
struct draw_context *draw = pstip->stage.draw;
if (!pstip->fs->pstip_fs &&
!generate_pstip_fs(pstip))
return false;
draw->suspend_flushing = true;
pstip->driver_bind_fs_state(pstip->pipe, pstip->fs->pstip_fs);
draw->suspend_flushing = false;
return true;
}
static inline struct pstip_stage *
pstip_stage(struct draw_stage *stage)
{
return (struct pstip_stage *) stage;
}
static void
pstip_first_tri(struct draw_stage *stage, struct prim_header *header)
{
struct pstip_stage *pstip = pstip_stage(stage);
struct pipe_context *pipe = pstip->pipe;
struct draw_context *draw = stage->draw;
unsigned num_samplers;
unsigned num_sampler_views;
assert(stage->draw->rasterizer->poly_stipple_enable);
/* bind our fragprog */
if (!bind_pstip_fragment_shader(pstip)) {
stage->tri = draw_pipe_passthrough_tri;
stage->tri(stage, header);
return;
}
/* how many samplers? */
/* we'll use sampler/texture[pstip->sampler_unit] for the stipple */
num_samplers = MAX2(pstip->num_samplers, pstip->fs->sampler_unit + 1);
num_sampler_views = MAX2(pstip->num_sampler_views, num_samplers);
/* plug in our sampler, texture */
pstip->state.samplers[pstip->fs->sampler_unit] = pstip->sampler_cso;
pstip->state.sampler_views[pstip->fs->sampler_unit] = pstip->sampler_view;
assert(num_samplers <= PIPE_MAX_SAMPLERS);
draw->suspend_flushing = true;
pstip->driver_bind_sampler_states(pipe, MESA_SHADER_FRAGMENT, 0,
num_samplers, pstip->state.samplers);
pstip->driver_set_sampler_views(pipe, MESA_SHADER_FRAGMENT, 0,
num_sampler_views, 0,
pstip->state.sampler_views);
draw->suspend_flushing = false;
/* now really draw first triangle */
stage->tri = draw_pipe_passthrough_tri;
stage->tri(stage, header);
}
static void
pstip_flush(struct draw_stage *stage, unsigned flags)
{
struct draw_context *draw = stage->draw;
struct pstip_stage *pstip = pstip_stage(stage);
struct pipe_context *pipe = pstip->pipe;
stage->tri = pstip_first_tri;
stage->next->flush(stage->next, flags);
/* restore original frag shader, texture, sampler state */
draw->suspend_flushing = true;
pstip->driver_bind_fs_state(pipe, pstip->fs ? pstip->fs->driver_fs : NULL);
pstip->driver_bind_sampler_states(pipe, MESA_SHADER_FRAGMENT, 0,
pstip->num_samplers,
pstip->state.samplers);
pstip->driver_set_sampler_views(pipe, MESA_SHADER_FRAGMENT, 0,
pstip->num_sampler_views, 0,
pstip->state.sampler_views);
draw->suspend_flushing = false;
}
static void
pstip_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
pstip_destroy(struct draw_stage *stage)
{
struct pstip_stage *pstip = pstip_stage(stage);
for (unsigned i = 0; i < PIPE_MAX_SHADER_SAMPLER_VIEWS; i++) {
pipe_sampler_view_reference(&pstip->state.sampler_views[i], NULL);
}
pstip->pipe->delete_sampler_state(pstip->pipe, pstip->sampler_cso);
pipe_resource_reference(&pstip->texture, NULL);
pipe_sampler_view_release_ptr(&pstip->sampler_view);
draw_free_temp_verts(stage);
FREE(stage);
}
/** Create a new polygon stipple drawing stage object */
static struct pstip_stage *
draw_pstip_stage(struct draw_context *draw, struct pipe_context *pipe)
{
struct pstip_stage *pstip = CALLOC_STRUCT(pstip_stage);
if (!pstip)
goto fail;
pstip->pipe = pipe;
pstip->stage.draw = draw;
pstip->stage.name = "pstip";
pstip->stage.next = NULL;
pstip->stage.point = draw_pipe_passthrough_point;
pstip->stage.line = draw_pipe_passthrough_line;
pstip->stage.tri = pstip_first_tri;
pstip->stage.flush = pstip_flush;
pstip->stage.reset_stipple_counter = pstip_reset_stipple_counter;
pstip->stage.destroy = pstip_destroy;
if (!draw_alloc_temp_verts(&pstip->stage, 8))
goto fail;
return pstip;
fail:
if (pstip)
pstip->stage.destroy(&pstip->stage);
return NULL;
}
static struct pstip_stage *
pstip_stage_from_pipe(struct pipe_context *pipe)
{
struct draw_context *draw = (struct draw_context *) pipe->draw;
return pstip_stage(draw->pipeline.pstipple);
}
/**
* This function overrides the driver's create_fs_state() function and
* will typically be called by the gallium frontend.
*/
static void *
pstip_create_fs_state(struct pipe_context *pipe,
const struct pipe_shader_state *fs)
{
struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
struct pstip_fragment_shader *pstipfs = CALLOC_STRUCT(pstip_fragment_shader);
if (pstipfs) {
pstipfs->state.type = fs->type;
if (fs->type == PIPE_SHADER_IR_TGSI)
pstipfs->state.tokens = tgsi_dup_tokens(fs->tokens);
else
pstipfs->state.ir.nir = nir_shader_clone(NULL, fs->ir.nir);
/* pass-through */
pstipfs->driver_fs = pstip->driver_create_fs_state(pstip->pipe, fs);
}
return pstipfs;
}
static void
pstip_bind_fs_state(struct pipe_context *pipe, void *fs)
{
struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
struct pstip_fragment_shader *pstipfs = (struct pstip_fragment_shader *) fs;
/* save current */
pstip->fs = pstipfs;
/* pass-through */
pstip->driver_bind_fs_state(pstip->pipe,
(pstipfs ? pstipfs->driver_fs : NULL));
}
static void
pstip_delete_fs_state(struct pipe_context *pipe, void *fs)
{
struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
struct pstip_fragment_shader *pstipfs = (struct pstip_fragment_shader *) fs;
/* pass-through */
pstip->driver_delete_fs_state(pstip->pipe, pstipfs->driver_fs);
if (pstipfs->pstip_fs)
pstip->driver_delete_fs_state(pstip->pipe, pstipfs->pstip_fs);
if (pstipfs->state.type == PIPE_SHADER_IR_TGSI)
FREE((void*)pstipfs->state.tokens);
else
ralloc_free(pstipfs->state.ir.nir);
FREE(pstipfs);
}
static void
pstip_bind_sampler_states(struct pipe_context *pipe,
mesa_shader_stage shader,
unsigned start, unsigned num, void **sampler)
{
struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
assert(start == 0);
if (shader == MESA_SHADER_FRAGMENT) {
/* save current */
memcpy(pstip->state.samplers, sampler, num * sizeof(void *));
for (unsigned i = num; i < PIPE_MAX_SAMPLERS; i++) {
pstip->state.samplers[i] = NULL;
}
pstip->num_samplers = num;
}
/* pass-through */
pstip->driver_bind_sampler_states(pstip->pipe, shader, start, num, sampler);
}
static void
pstip_set_sampler_views(struct pipe_context *pipe,
mesa_shader_stage shader,
unsigned start, unsigned num,
unsigned unbind_num_trailing_slots,
struct pipe_sampler_view **views)
{
struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
if (shader == MESA_SHADER_FRAGMENT) {
/* save current */
unsigned i;
for (i = 0; i < num; i++) {
pstip->state.sampler_views[start + i] = views[i];
}
for (; i < num + unbind_num_trailing_slots; i++) {
pstip->state.sampler_views[start + i] = NULL;
}
pstip->num_sampler_views = num;
}
/* pass-through */
pstip->driver_set_sampler_views(pstip->pipe, shader, start, num,
unbind_num_trailing_slots, views);
}
static void
pstip_set_polygon_stipple(struct pipe_context *pipe,
const struct pipe_poly_stipple *stipple)
{
struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
/* save current */
pstip->state.stipple = stipple;
/* pass-through */
pstip->driver_set_polygon_stipple(pstip->pipe, stipple);
util_pstipple_update_stipple_texture(pstip->pipe, pstip->texture,
pstip->state.stipple->stipple);
}
/**
* Called by drivers that want to install this polygon stipple stage
* into the draw module's pipeline. This will not be used if the
* hardware has native support for polygon stipple.
*/
bool
draw_install_pstipple_stage(struct draw_context *draw,
struct pipe_context *pipe)
{
pipe->draw = (void *) draw;
/*
* Create / install pgon stipple drawing / prim stage
*/
struct pstip_stage *pstip = draw_pstip_stage(draw, pipe);
if (!pstip)
goto fail;
draw->pipeline.pstipple = &pstip->stage;
/* save original driver functions */
pstip->driver_create_fs_state = pipe->create_fs_state;
pstip->driver_bind_fs_state = pipe->bind_fs_state;
pstip->driver_delete_fs_state = pipe->delete_fs_state;
pstip->driver_bind_sampler_states = pipe->bind_sampler_states;
pstip->driver_set_sampler_views = pipe->set_sampler_views;
pstip->driver_set_polygon_stipple = pipe->set_polygon_stipple;
/* create special texture, sampler state */
pstip->texture = util_pstipple_create_stipple_texture(pipe, NULL);
if (!pstip->texture)
goto fail;
pstip->sampler_view = util_pstipple_create_sampler_view(pipe,
pstip->texture);
if (!pstip->sampler_view)
goto fail;
pstip->sampler_cso = util_pstipple_create_sampler(pipe);
if (!pstip->sampler_cso)
goto fail;
/* override the driver's functions */
pipe->create_fs_state = pstip_create_fs_state;
pipe->bind_fs_state = pstip_bind_fs_state;
pipe->delete_fs_state = pstip_delete_fs_state;
pipe->bind_sampler_states = pstip_bind_sampler_states;
pipe->set_sampler_views = pstip_set_sampler_views;
pipe->set_polygon_stipple = pstip_set_polygon_stipple;
return true;
fail:
if (pstip)
pstip->stage.destroy(&pstip->stage);
return false;
}
@@ -0,0 +1,272 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/* Authors: Keith Whitwell <[email protected]>
*/
/* Implement line stipple by cutting lines up into smaller lines.
* There are hundreds of ways to implement line stipple, this is one
* choice that should work in all situations, requires no state
* manipulations, but with a penalty in terms of large amounts of
* generated geometry.
*/
#include "pipe/p_defines.h"
#include "pipe/p_shader_tokens.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "draw/draw_pipe.h"
/** Subclass of draw_stage */
struct stipple_stage {
struct draw_stage stage;
unsigned counter;
uint16_t pattern;
uint16_t factor;
bool rectangular;
};
static inline struct stipple_stage *
stipple_stage(struct draw_stage *stage)
{
return (struct stipple_stage *) stage;
}
/**
* Compute interpolated vertex attributes for 'dst' at position 't'
* between 'v0' and 'v1'.
* XXX using linear interpolation for all attribs at this time.
*/
static void
screen_interp(struct draw_context *draw,
struct vertex_header *dst,
float t,
const struct vertex_header *v0,
const struct vertex_header *v1)
{
unsigned attr;
unsigned num_outputs = draw_current_shader_outputs(draw);
for (attr = 0; attr < num_outputs; attr++) {
const float *val0 = v0->data[attr];
const float *val1 = v1->data[attr];
float *newv = dst->data[attr];
unsigned i;
for (i = 0; i < 4; i++) {
newv[i] = val0[i] + t * (val1[i] - val0[i]);
}
}
}
static void
emit_segment(struct draw_stage *stage, struct prim_header *header,
float t0, float t1)
{
struct vertex_header *v0new = dup_vert(stage, header->v[0], 0);
struct vertex_header *v1new = dup_vert(stage, header->v[1], 1);
struct prim_header newprim = *header;
if (t0 > 0.0) {
screen_interp(stage->draw, v0new, t0, header->v[0], header->v[1]);
newprim.v[0] = v0new;
}
if (t1 < 1.0) {
screen_interp(stage->draw, v1new, t1, header->v[0], header->v[1]);
newprim.v[1] = v1new;
}
stage->next->line(stage->next, &newprim);
}
static inline bool
stipple_test(unsigned counter, uint16_t pattern, uint16_t factor)
{
unsigned b = (counter / factor) & 0xf;
return !!((1 << b) & pattern);
}
static void
stipple_line(struct draw_stage *stage, struct prim_header *header)
{
struct stipple_stage *stipple = stipple_stage(stage);
struct vertex_header *v0 = header->v[0];
struct vertex_header *v1 = header->v[1];
const unsigned pos = draw_current_shader_position_output(stage->draw);
const float *pos0 = v0->data[pos];
const float *pos1 = v1->data[pos];
float start = 0;
bool state = 0;
float x0 = pos0[0];
float x1 = pos1[0];
float y0 = pos0[1];
float y1 = pos1[1];
float length;
int i;
int intlength;
if (header->flags & DRAW_PIPE_RESET_STIPPLE)
stipple->counter = 0;
if (stipple->rectangular) {
float dx = x1 - x0;
float dy = y1 - y0;
length = sqrtf(dx*dx + dy*dy);
} else {
float dx = x0 > x1 ? x0 - x1 : x1 - x0;
float dy = y0 > y1 ? y0 - y1 : y1 - y0;
length = MAX2(dx, dy);
}
if (util_is_inf_or_nan(length))
intlength = 0;
else
intlength = ceilf(length);
/* XXX ToDo: instead of iterating pixel-by-pixel, use a look-up table.
*/
for (i = 0; i < intlength; i++) {
bool result = stipple_test(stipple->counter + i,
stipple->pattern, stipple->factor);
if (result != state) {
/* changing from "off" to "on" or vice versa */
if (state) {
/* finishing an "on" segment */
emit_segment(stage, header, start / length, i / length);
}
else {
/* starting an "on" segment */
start = (float)i;
}
state = result;
}
}
if (state && start < length)
emit_segment(stage, header, start / length, 1.0);
stipple->counter += intlength;
}
static void
reset_stipple_counter(struct draw_stage *stage)
{
struct stipple_stage *stipple = stipple_stage(stage);
stipple->counter = 0;
stage->next->reset_stipple_counter(stage->next);
}
static void
stipple_reset_point(struct draw_stage *stage, struct prim_header *header)
{
struct stipple_stage *stipple = stipple_stage(stage);
stipple->counter = 0;
stage->next->point(stage->next, header);
}
static void
stipple_reset_tri(struct draw_stage *stage, struct prim_header *header)
{
struct stipple_stage *stipple = stipple_stage(stage);
stipple->counter = 0;
stage->next->tri(stage->next, header);
}
static void
stipple_first_line(struct draw_stage *stage,
struct prim_header *header)
{
struct stipple_stage *stipple = stipple_stage(stage);
struct draw_context *draw = stage->draw;
stipple->pattern = draw->rasterizer->line_stipple_pattern;
stipple->factor = draw->rasterizer->line_stipple_factor + 1;
stipple->rectangular = draw->rasterizer->line_rectangular;
stage->line = stipple_line;
stage->line(stage, header);
}
static void
stipple_flush(struct draw_stage *stage, unsigned flags)
{
stage->line = stipple_first_line;
stage->next->flush(stage->next, flags);
}
static void
stipple_destroy(struct draw_stage *stage)
{
draw_free_temp_verts(stage);
FREE(stage);
}
/**
* Create line stippler stage
*/
struct draw_stage *
draw_stipple_stage(struct draw_context *draw)
{
struct stipple_stage *stipple = CALLOC_STRUCT(stipple_stage);
if (!stipple)
goto fail;
stipple->stage.draw = draw;
stipple->stage.name = "stipple";
stipple->stage.next = NULL;
stipple->stage.point = stipple_reset_point;
stipple->stage.line = stipple_first_line;
stipple->stage.tri = stipple_reset_tri;
stipple->stage.reset_stipple_counter = reset_stipple_counter;
stipple->stage.flush = stipple_flush;
stipple->stage.destroy = stipple_destroy;
if (!draw_alloc_temp_verts(&stipple->stage, 2))
goto fail;
return &stipple->stage;
fail:
if (stipple)
stipple->stage.destroy(&stipple->stage);
return NULL;
}
@@ -0,0 +1,199 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/* Authors: Keith Whitwell <[email protected]>
*/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "pipe/p_defines.h"
#include "pipe/p_shader_tokens.h"
#include "draw_vs.h"
#include "draw_pipe.h"
struct twoside_stage {
struct draw_stage stage;
float sign; /**< +1 or -1 */
int attrib_front0, attrib_back0;
int attrib_front1, attrib_back1;
};
static inline struct twoside_stage *
twoside_stage(struct draw_stage *stage)
{
return (struct twoside_stage *) stage;
}
/**
* Copy back color(s) to front color(s).
*/
static inline struct vertex_header *
copy_bfc(struct twoside_stage *twoside,
const struct vertex_header *v,
unsigned idx)
{
struct vertex_header *tmp = dup_vert(&twoside->stage, v, idx);
if (twoside->attrib_back0 >= 0 && twoside->attrib_front0 >= 0) {
COPY_4FV(tmp->data[twoside->attrib_front0],
tmp->data[twoside->attrib_back0]);
}
if (twoside->attrib_back1 >= 0 && twoside->attrib_front1 >= 0) {
COPY_4FV(tmp->data[twoside->attrib_front1],
tmp->data[twoside->attrib_back1]);
}
return tmp;
}
/* Twoside tri:
*/
static void
twoside_tri(struct draw_stage *stage,
struct prim_header *header)
{
struct twoside_stage *twoside = twoside_stage(stage);
if (header->det * twoside->sign < 0.0) {
/* this is a back-facing triangle */
struct prim_header tmp;
tmp.det = header->det;
tmp.flags = header->flags;
tmp.pad = header->pad;
/* copy back attribs to front attribs */
tmp.v[0] = copy_bfc(twoside, header->v[0], 0);
tmp.v[1] = copy_bfc(twoside, header->v[1], 1);
tmp.v[2] = copy_bfc(twoside, header->v[2], 2);
stage->next->tri(stage->next, &tmp);
} else {
stage->next->tri(stage->next, header);
}
}
static void
twoside_first_tri(struct draw_stage *stage,
struct prim_header *header)
{
struct twoside_stage *twoside = twoside_stage(stage);
const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader;
twoside->attrib_front0 = -1;
twoside->attrib_front1 = -1;
twoside->attrib_back0 = -1;
twoside->attrib_back1 = -1;
/*
* Find which vertex shader outputs are front/back colors
* (only first two can be front or back).
*/
for (unsigned i = 0; i < vs->info.num_outputs; i++) {
if (vs->info.output_semantic_name[i] == TGSI_SEMANTIC_COLOR) {
if (vs->info.output_semantic_index[i] == 0)
twoside->attrib_front0 = i;
else if (vs->info.output_semantic_index[i] == 1)
twoside->attrib_front1 = i;
}
if (vs->info.output_semantic_name[i] == TGSI_SEMANTIC_BCOLOR) {
if (vs->info.output_semantic_index[i] == 0)
twoside->attrib_back0 = i;
else if (vs->info.output_semantic_index[i] == 1)
twoside->attrib_back1 = i;
}
}
/*
* We'll multiply the primitive's determinant by this sign to determine
* if the triangle is back-facing (negative).
* sign = -1 for CCW, +1 for CW
*/
twoside->sign = stage->draw->rasterizer->front_ccw ? -1.0f : 1.0f;
stage->tri = twoside_tri;
stage->tri(stage, header);
}
static void
twoside_flush(struct draw_stage *stage, unsigned flags)
{
stage->tri = twoside_first_tri;
stage->next->flush(stage->next, flags);
}
static void
twoside_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
twoside_destroy(struct draw_stage *stage)
{
draw_free_temp_verts(stage);
FREE(stage);
}
/**
* Create twoside pipeline stage.
*/
struct draw_stage *
draw_twoside_stage(struct draw_context *draw)
{
struct twoside_stage *twoside = CALLOC_STRUCT(twoside_stage);
if (!twoside)
goto fail;
twoside->stage.draw = draw;
twoside->stage.name = "twoside";
twoside->stage.next = NULL;
twoside->stage.point = draw_pipe_passthrough_point;
twoside->stage.line = draw_pipe_passthrough_line;
twoside->stage.tri = twoside_first_tri;
twoside->stage.flush = twoside_flush;
twoside->stage.reset_stipple_counter = twoside_reset_stipple_counter;
twoside->stage.destroy = twoside_destroy;
if (!draw_alloc_temp_verts(&twoside->stage, 3))
goto fail;
return &twoside->stage;
fail:
if (twoside)
twoside->stage.destroy(&twoside->stage);
return NULL;
}
@@ -0,0 +1,315 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/**
* \brief Drawing stage for handling glPolygonMode(line/point).
* Convert triangles to points or lines as needed.
*/
/* Authors: Keith Whitwell <[email protected]>
*/
#include "util/u_memory.h"
#include "pipe/p_defines.h"
#include "draw_private.h"
#include "draw_pipe.h"
#include "draw_fs.h"
struct unfilled_stage {
struct draw_stage stage;
/** [0] = front face, [1] = back face.
* legal values: PIPE_POLYGON_MODE_FILL, PIPE_POLYGON_MODE_LINE,
* and PIPE_POLYGON_MODE_POINT,
*/
unsigned mode[2];
int face_slot;
};
static inline struct
unfilled_stage *unfilled_stage(struct draw_stage *stage)
{
return (struct unfilled_stage *) stage;
}
static void
inject_front_face_info(struct draw_stage *stage,
struct prim_header *header)
{
struct unfilled_stage *unfilled = unfilled_stage(stage);
bool is_front_face = (
(stage->draw->rasterizer->front_ccw && header->det < 0.0f) ||
(!stage->draw->rasterizer->front_ccw && header->det > 0.0f));
int slot = unfilled->face_slot;
/* In case the backend doesn't care about it */
if (slot < 0) {
return;
}
for (unsigned i = 0; i < 3; ++i) {
struct vertex_header *v = header->v[i];
v->data[slot][0] = is_front_face;
v->data[slot][1] = is_front_face;
v->data[slot][2] = is_front_face;
v->data[slot][3] = is_front_face;
v->vertex_id = UNDEFINED_VERTEX_ID;
}
}
static void
point(struct draw_stage *stage,
struct prim_header *header,
struct vertex_header *v0)
{
struct prim_header tmp;
tmp.det = header->det;
tmp.flags = 0;
tmp.v[0] = v0;
stage->next->point(stage->next, &tmp);
}
static void
line(struct draw_stage *stage,
struct prim_header *header,
struct vertex_header *v0,
struct vertex_header *v1)
{
struct prim_header tmp;
tmp.det = header->det;
tmp.flags = 0;
tmp.v[0] = v0;
tmp.v[1] = v1;
stage->next->line(stage->next, &tmp);
}
static void
points(struct draw_stage *stage,
struct prim_header *header)
{
struct vertex_header *v0 = header->v[0];
struct vertex_header *v1 = header->v[1];
struct vertex_header *v2 = header->v[2];
inject_front_face_info(stage, header);
if ((header->flags & DRAW_PIPE_EDGE_FLAG_0) && v0->edgeflag)
point(stage, header, v0);
if ((header->flags & DRAW_PIPE_EDGE_FLAG_1) && v1->edgeflag)
point(stage, header, v1);
if ((header->flags & DRAW_PIPE_EDGE_FLAG_2) && v2->edgeflag)
point(stage, header, v2);
}
static void
lines(struct draw_stage *stage,
struct prim_header *header)
{
struct vertex_header *v0 = header->v[0];
struct vertex_header *v1 = header->v[1];
struct vertex_header *v2 = header->v[2];
if (header->flags & DRAW_PIPE_RESET_STIPPLE)
/*
* XXX could revisit this. The only stage which cares is the line
* stipple stage. Could just emit correct reset flags here and not
* bother about all the calling through reset_stipple_counter
* stages. Though technically it is necessary if line stipple is
* handled by the driver, but this is not actually hooked up when
* using vbuf (vbuf stage reset_stipple_counter does nothing).
*/
stage->next->reset_stipple_counter(stage->next);
inject_front_face_info(stage, header);
if ((header->flags & DRAW_PIPE_EDGE_FLAG_2) && v2->edgeflag)
line(stage, header, v2, v0);
if ((header->flags & DRAW_PIPE_EDGE_FLAG_0) && v0->edgeflag)
line(stage, header, v0, v1);
if ((header->flags & DRAW_PIPE_EDGE_FLAG_1) && v1->edgeflag)
line(stage, header, v1, v2);
}
/** For debugging */
static void
print_header_flags(unsigned flags)
{
debug_printf("header->flags = ");
if (flags & DRAW_PIPE_RESET_STIPPLE)
debug_printf("RESET_STIPPLE ");
if (flags & DRAW_PIPE_EDGE_FLAG_0)
debug_printf("EDGE_FLAG_0 ");
if (flags & DRAW_PIPE_EDGE_FLAG_1)
debug_printf("EDGE_FLAG_1 ");
if (flags & DRAW_PIPE_EDGE_FLAG_2)
debug_printf("EDGE_FLAG_2 ");
debug_printf("\n");
}
/* Unfilled tri:
*
* Note edgeflags in the vertex struct is not sufficient as we will
* need to manipulate them when decomposing primitives.
*
* We currently keep the vertex edgeflag and primitive edgeflag mask
* separate until the last possible moment.
*/
static void
unfilled_tri(struct draw_stage *stage,
struct prim_header *header)
{
struct unfilled_stage *unfilled = unfilled_stage(stage);
unsigned cw = header->det >= 0.0;
unsigned mode = unfilled->mode[cw];
if (0)
print_header_flags(header->flags);
switch (mode) {
case PIPE_POLYGON_MODE_FILL:
stage->next->tri(stage->next, header);
break;
case PIPE_POLYGON_MODE_LINE:
lines(stage, header);
break;
case PIPE_POLYGON_MODE_POINT:
points(stage, header);
break;
default:
assert(0);
}
}
static void
unfilled_first_tri(struct draw_stage *stage,
struct prim_header *header)
{
struct unfilled_stage *unfilled = unfilled_stage(stage);
const struct pipe_rasterizer_state *rast = stage->draw->rasterizer;
unfilled->mode[0] = rast->front_ccw ? rast->fill_front : rast->fill_back;
unfilled->mode[1] = rast->front_ccw ? rast->fill_back : rast->fill_front;
stage->tri = unfilled_tri;
stage->tri(stage, header);
}
static void
unfilled_flush(struct draw_stage *stage,
unsigned flags)
{
stage->next->flush(stage->next, flags);
stage->tri = unfilled_first_tri;
}
static void
unfilled_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
unfilled_destroy(struct draw_stage *stage)
{
draw_free_temp_verts(stage);
FREE(stage);
}
/*
* Try to allocate an output slot which we can use
* to preserve the front face information.
*/
void
draw_unfilled_prepare_outputs(struct draw_context *draw,
struct draw_stage *stage)
{
struct unfilled_stage *unfilled = unfilled_stage(stage);
const struct pipe_rasterizer_state *rast = draw ? draw->rasterizer : NULL;
bool is_unfilled = (rast &&
(rast->fill_front != PIPE_POLYGON_MODE_FILL ||
rast->fill_back != PIPE_POLYGON_MODE_FILL));
const struct draw_fragment_shader *fs = draw ? draw->fs.fragment_shader : NULL;
if (is_unfilled && fs && fs->info.uses_frontface) {
unfilled->face_slot = draw_alloc_extra_vertex_attrib(
stage->draw, TGSI_SEMANTIC_FACE, 0);
} else {
unfilled->face_slot = -1;
}
}
/**
* Create unfilled triangle stage.
*/
struct draw_stage *
draw_unfilled_stage(struct draw_context *draw)
{
struct unfilled_stage *unfilled = CALLOC_STRUCT(unfilled_stage);
if (!unfilled)
goto fail;
unfilled->stage.draw = draw;
unfilled->stage.name = "unfilled";
unfilled->stage.next = NULL;
unfilled->stage.tmp = NULL;
unfilled->stage.point = draw_pipe_passthrough_point;
unfilled->stage.line = draw_pipe_passthrough_line;
unfilled->stage.tri = unfilled_first_tri;
unfilled->stage.flush = unfilled_flush;
unfilled->stage.reset_stipple_counter = unfilled_reset_stipple_counter;
unfilled->stage.destroy = unfilled_destroy;
unfilled->face_slot = -1;
if (!draw_alloc_temp_verts(&unfilled->stage, 0))
goto fail;
return &unfilled->stage;
fail:
if (unfilled)
unfilled->stage.destroy(&unfilled->stage);
return NULL;
}
@@ -0,0 +1,206 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/**
* \brief Drawing stage for user culling
*/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "pipe/p_defines.h"
#include "draw_pipe.h"
struct user_cull_stage {
struct draw_stage stage;
};
static inline bool
cull_distance_is_out(float dist)
{
return (dist < 0.0f) || util_is_inf_or_nan(dist);
}
/*
* If the shader writes the culldistance then we can
* perform distance based culling. Distance based
* culling doesn't require a face and can be performed
* on primitives without faces (e.g. points and lines)
*/
static void
user_cull_point(struct draw_stage *stage,
struct prim_header *header)
{
const unsigned num_written_culldistances =
draw_current_shader_num_written_culldistances(stage->draw);
const unsigned num_written_clipdistances =
draw_current_shader_num_written_clipdistances(stage->draw);
assert(num_written_culldistances);
for (unsigned i = 0; i < num_written_culldistances; ++i) {
unsigned cull_idx = (num_written_clipdistances + i) / 4;
unsigned out_idx =
draw_current_shader_ccdistance_output(stage->draw, cull_idx);
unsigned idx = (num_written_clipdistances + i) % 4;
float cull1 = header->v[0]->data[out_idx][idx];
bool vert1_out = cull_distance_is_out(cull1);
if (vert1_out)
return;
}
stage->next->point(stage->next, header);
}
/*
* If the shader writes the culldistance then we can
* perform distance based culling. Distance based
* culling doesn't require a face and can be performed
* on primitives without faces (e.g. points and lines)
*/
static void
user_cull_line(struct draw_stage *stage,
struct prim_header *header)
{
const unsigned num_written_culldistances =
draw_current_shader_num_written_culldistances(stage->draw);
const unsigned num_written_clipdistances =
draw_current_shader_num_written_clipdistances(stage->draw);
assert(num_written_culldistances);
for (unsigned i = 0; i < num_written_culldistances; ++i) {
unsigned cull_idx = (num_written_clipdistances + i) / 4;
unsigned out_idx =
draw_current_shader_ccdistance_output(stage->draw, cull_idx);
unsigned idx = (num_written_clipdistances + i) % 4;
float cull1 = header->v[0]->data[out_idx][idx];
float cull2 = header->v[1]->data[out_idx][idx];
bool vert1_out = cull_distance_is_out(cull1);
bool vert2_out = cull_distance_is_out(cull2);
if (vert1_out && vert2_out)
return;
}
stage->next->line(stage->next, header);
}
/*
* Triangles can be culled either using the cull distance
* shader outputs or the regular face culling. If required
* this function performs both, starting with distance culling.
*/
static void
user_cull_tri(struct draw_stage *stage,
struct prim_header *header)
{
const unsigned num_written_culldistances =
draw_current_shader_num_written_culldistances(stage->draw);
const unsigned num_written_clipdistances =
draw_current_shader_num_written_clipdistances(stage->draw);
unsigned i;
assert(num_written_culldistances);
/* Do the distance culling */
for (i = 0; i < num_written_culldistances; ++i) {
unsigned cull_idx = (num_written_clipdistances + i) / 4;
unsigned out_idx =
draw_current_shader_ccdistance_output(stage->draw, cull_idx);
unsigned idx = (num_written_clipdistances + i) % 4;
float cull1 = header->v[0]->data[out_idx][idx];
float cull2 = header->v[1]->data[out_idx][idx];
float cull3 = header->v[2]->data[out_idx][idx];
bool vert1_out = cull_distance_is_out(cull1);
bool vert2_out = cull_distance_is_out(cull2);
bool vert3_out = cull_distance_is_out(cull3);
if (vert1_out && vert2_out && vert3_out) {
return;
}
}
stage->next->tri(stage->next, header);
}
static void
user_cull_flush(struct draw_stage *stage, unsigned flags)
{
stage->point = user_cull_point;
stage->line = user_cull_line;
stage->tri = user_cull_tri;
stage->next->flush(stage->next, flags);
}
static void
user_cull_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
user_cull_destroy(struct draw_stage *stage)
{
draw_free_temp_verts(stage);
FREE(stage);
}
/**
* Create a new polygon culling stage.
*/
struct draw_stage *
draw_user_cull_stage(struct draw_context *draw)
{
struct user_cull_stage *user_cull = CALLOC_STRUCT(user_cull_stage);
if (!user_cull)
goto fail;
user_cull->stage.draw = draw;
user_cull->stage.name = "user_cull";
user_cull->stage.next = NULL;
user_cull->stage.point = user_cull_point;
user_cull->stage.line = user_cull_line;
user_cull->stage.tri = user_cull_tri;
user_cull->stage.flush = user_cull_flush;
user_cull->stage.reset_stipple_counter = user_cull_reset_stipple_counter;
user_cull->stage.destroy = user_cull_destroy;
if (!draw_alloc_temp_verts(&user_cull->stage, 0))
goto fail;
return &user_cull->stage;
fail:
if (user_cull)
user_cull->stage.destroy(&user_cull->stage);
return NULL;
}
+132
View File
@@ -0,0 +1,132 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Authors:
* Keith Whitwell <[email protected]>
*/
#include "util/u_memory.h"
#include "draw/draw_private.h"
#include "draw/draw_pipe.h"
void
draw_pipe_passthrough_point(struct draw_stage *stage, struct prim_header *header)
{
stage->next->point(stage->next, header);
}
void
draw_pipe_passthrough_line(struct draw_stage *stage, struct prim_header *header)
{
stage->next->line(stage->next, header);
}
void
draw_pipe_passthrough_tri(struct draw_stage *stage, struct prim_header *header)
{
stage->next->tri(stage->next, header);
}
/* This is only used for temporary verts.
*/
#define MAX_VERTEX_SIZE ((2 + PIPE_MAX_SHADER_OUTPUTS) * 4 * sizeof(float))
/**
* Allocate space for temporary post-transform vertices, such as for clipping.
*/
bool
draw_alloc_temp_verts(struct draw_stage *stage, unsigned nr)
{
assert(!stage->tmp);
stage->tmp = NULL;
stage->nr_tmps = nr;
if (nr != 0) {
uint8_t *store = (uint8_t *) MALLOC(MAX_VERTEX_SIZE * nr +
DRAW_EXTRA_VERTICES_PADDING);
if (!store)
return false;
stage->tmp = (struct vertex_header **) MALLOC(sizeof(struct vertex_header *) * nr);
if (stage->tmp == NULL) {
FREE(store);
return false;
}
for (unsigned i = 0; i < nr; i++)
stage->tmp[i] = (struct vertex_header *)(store + i * MAX_VERTEX_SIZE);
}
return true;
}
void
draw_free_temp_verts(struct draw_stage *stage)
{
if (stage->tmp) {
FREE(stage->tmp[0]);
FREE(stage->tmp);
stage->tmp = NULL;
}
}
/* Reset vertex ids. This is basically a type of flush.
*
* Called only from draw_pipe_vbuf.c
*/
void
draw_reset_vertex_ids(struct draw_context *draw)
{
struct draw_stage *stage = draw->pipeline.first;
while (stage) {
for (unsigned i = 0; i < stage->nr_tmps; i++)
stage->tmp[i]->vertex_id = UNDEFINED_VERTEX_ID;
stage = stage->next;
}
if (draw->pipeline.verts) {
char *verts = draw->pipeline.verts;
unsigned stride = draw->pipeline.vertex_stride;
for (unsigned i = 0; i < draw->pipeline.vertex_count; i++) {
((struct vertex_header *)verts)->vertex_id = UNDEFINED_VERTEX_ID;
verts += stride;
}
}
}
@@ -0,0 +1,357 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/* Authors: Keith Whitwell <[email protected]>
*/
#include "util/u_memory.h"
#include "util/u_math.h"
#include "util/u_prim.h"
#include "pipe/p_defines.h"
#include "draw_private.h"
#include "draw_pipe.h"
#include "draw_context.h"
#include "draw_vbuf.h"
/**
* Default version of a function to check if we need any special
* pipeline stages, or whether prims/verts can go through untouched.
* Don't test for bypass clipping or vs modes, this function is just
* about the primitive pipeline stages.
*
* This can be overridden by the driver.
*/
bool
draw_need_pipeline(const struct draw_context *draw,
const struct pipe_rasterizer_state *rasterizer,
enum mesa_prim prim)
{
unsigned reduced_prim = u_reduced_prim(prim);
/* If the driver has overridden this, use that version:
*/
if (draw->render && draw->render->need_pipeline) {
return draw->render->need_pipeline(draw->render, rasterizer, prim);
}
/* Don't have to worry about triangles turning into lines/points
* and triggering the pipeline, because we have to trigger the
* pipeline *anyway* if unfilled mode is active.
*/
if (reduced_prim == MESA_PRIM_LINES) {
/* line stipple */
if (rasterizer->line_stipple_enable && draw->pipeline.line_stipple)
return true;
/* wide lines */
if (roundf(rasterizer->line_width) > draw->pipeline.wide_line_threshold)
return true;
/* AA lines */
if ((!rasterizer->multisample && rasterizer->line_smooth) && draw->pipeline.aaline)
return true;
if (draw_current_shader_num_written_culldistances(draw))
return true;
} else if (reduced_prim == MESA_PRIM_POINTS) {
/* large points */
if (rasterizer->point_size > draw->pipeline.wide_point_threshold)
return true;
/* sprite points */
if (rasterizer->point_quad_rasterization
&& draw->pipeline.wide_point_sprites)
return true;
/* AA points */
if ((!rasterizer->multisample && rasterizer->point_smooth) && draw->pipeline.aapoint)
return true;
/* point sprites */
if (rasterizer->sprite_coord_enable && draw->pipeline.point_sprite)
return true;
if (draw_current_shader_num_written_culldistances(draw))
return true;
} else if (reduced_prim == MESA_PRIM_TRIANGLES) {
/* polygon stipple */
if (rasterizer->poly_stipple_enable && draw->pipeline.pstipple)
return true;
/* unfilled polygons */
if (rasterizer->fill_front != PIPE_POLYGON_MODE_FILL ||
rasterizer->fill_back != PIPE_POLYGON_MODE_FILL)
return true;
/* polygon offset */
if (rasterizer->offset_point ||
rasterizer->offset_line ||
rasterizer->offset_tri)
return true;
/* two-side lighting */
if (rasterizer->light_twoside)
return true;
if (draw_current_shader_num_written_culldistances(draw))
return true;
}
/* polygon cull - this is difficult - hardware can cull just fine
* most of the time (though sometimes CULL_NEITHER is unsupported.
*
* Generally this isn't a reason to require the pipeline, though.
*
if (rasterizer->cull_mode)
return TRUE;
*/
return false;
}
/**
* Rebuild the rendering pipeline.
*/
static struct draw_stage *
validate_pipeline(struct draw_stage *stage)
{
struct draw_context *draw = stage->draw;
struct draw_stage *next = draw->pipeline.rasterize;
bool need_det = false;
bool precalc_flat = false;
bool wide_lines, wide_points;
const struct pipe_rasterizer_state *rast = draw->rasterizer;
/* Set the validate's next stage to the rasterize stage, so that it
* can be found later if needed for flushing.
*/
stage->next = next;
/* drawing wide, non-AA lines? */
wide_lines = rast->line_width != 1.0f &&
roundf(rast->line_width) > draw->pipeline.wide_line_threshold &&
(!rast->line_smooth || rast->multisample);
/* drawing large/sprite points (but not AA points)? */
if (rast->sprite_coord_enable && draw->pipeline.point_sprite)
wide_points = true;
else if ((!rast->multisample && rast->point_smooth) && draw->pipeline.aapoint)
wide_points = false;
else if (rast->point_size > draw->pipeline.wide_point_threshold)
wide_points = true;
else if (rast->point_quad_rasterization && draw->pipeline.wide_point_sprites)
wide_points = true;
else
wide_points = false;
/*
* NOTE: we build up the pipeline in end-to-start order.
*
* TODO: make the current primitive part of the state and build
* shorter pipelines for lines & points.
*/
if ((!rast->multisample && rast->line_smooth) && draw->pipeline.aaline) {
draw->pipeline.aaline->next = next;
next = draw->pipeline.aaline;
precalc_flat = true;
}
if ((!rast->multisample && rast->point_smooth) && draw->pipeline.aapoint) {
draw->pipeline.aapoint->next = next;
next = draw->pipeline.aapoint;
}
if (wide_lines) {
draw->pipeline.wide_line->next = next;
next = draw->pipeline.wide_line;
precalc_flat = true;
}
if (wide_points) {
draw->pipeline.wide_point->next = next;
next = draw->pipeline.wide_point;
}
if (rast->line_stipple_enable && draw->pipeline.line_stipple) {
draw->pipeline.stipple->next = next;
next = draw->pipeline.stipple;
precalc_flat = true; /* only needed for lines really */
}
if (rast->poly_stipple_enable
&& draw->pipeline.pstipple) {
draw->pipeline.pstipple->next = next;
next = draw->pipeline.pstipple;
}
if (rast->fill_front != PIPE_POLYGON_MODE_FILL ||
rast->fill_back != PIPE_POLYGON_MODE_FILL) {
draw->pipeline.unfilled->next = next;
next = draw->pipeline.unfilled;
precalc_flat = true; /* only needed for triangles really */
need_det = true;
}
if (precalc_flat) {
/*
* could only run the stage if either rast->flatshade is true
* or there's constant interpolated values.
*/
draw->pipeline.flatshade->next = next;
next = draw->pipeline.flatshade;
}
if (rast->offset_point ||
rast->offset_line ||
rast->offset_tri) {
draw->pipeline.offset->next = next;
next = draw->pipeline.offset;
need_det = true;
}
if (rast->light_twoside) {
draw->pipeline.twoside->next = next;
next = draw->pipeline.twoside;
need_det = true;
}
/* Always run the cull stage as we calculate determinant there
* also.
*
* This can actually be a win as culling out the triangles can lead
* to less work emitting vertices, smaller vertex buffers, etc.
* It's difficult to say whether this will be true in general.
*/
if (need_det || rast->cull_face != PIPE_FACE_NONE) {
draw->pipeline.cull->next = next;
next = draw->pipeline.cull;
}
/* Clip stage
*/
if (draw->clip_xy || draw->clip_z || draw->clip_user) {
draw->pipeline.clip->next = next;
next = draw->pipeline.clip;
}
if (draw_current_shader_num_written_culldistances(draw)) {
draw->pipeline.user_cull->next = next;
next = draw->pipeline.user_cull;
}
draw->pipeline.first = next;
if (0) {
debug_printf("draw pipeline:\n");
for (next = draw->pipeline.first; next ; next = next->next)
debug_printf(" %s\n", next->name);
debug_printf("\n");
}
return draw->pipeline.first;
}
static void
validate_tri(struct draw_stage *stage,
struct prim_header *header)
{
struct draw_stage *pipeline = validate_pipeline(stage);
pipeline->tri(pipeline, header);
}
static void
validate_line(struct draw_stage *stage,
struct prim_header *header)
{
struct draw_stage *pipeline = validate_pipeline(stage);
pipeline->line(pipeline, header);
}
static void
validate_point(struct draw_stage *stage,
struct prim_header *header)
{
struct draw_stage *pipeline = validate_pipeline(stage);
pipeline->point(pipeline, header);
}
static void
validate_reset_stipple_counter(struct draw_stage *stage)
{
struct draw_stage *pipeline = validate_pipeline(stage);
pipeline->reset_stipple_counter(pipeline);
}
static void
validate_flush(struct draw_stage *stage,
unsigned flags)
{
/* May need to pass a backend flush on to the rasterize stage.
*/
if (stage->next)
stage->next->flush(stage->next, flags);
}
static void
validate_destroy(struct draw_stage *stage)
{
FREE(stage);
}
/**
* Create validate pipeline stage.
*/
struct draw_stage *
draw_validate_stage(struct draw_context *draw)
{
struct draw_stage *stage = CALLOC_STRUCT(draw_stage);
if (!stage)
return NULL;
stage->draw = draw;
stage->name = "validate";
stage->next = NULL;
stage->point = validate_point;
stage->line = validate_line;
stage->tri = validate_tri;
stage->flush = validate_flush;
stage->reset_stipple_counter = validate_reset_stipple_counter;
stage->destroy = validate_destroy;
return stage;
}
+452
View File
@@ -0,0 +1,452 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/**
* \file
* Vertex buffer drawing stage.
*
* \author Jose Fonseca <[email protected]>
* \author Keith Whitwell <[email protected]>
*/
#include "util/u_debug.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "draw_vbuf.h"
#include "draw_private.h"
#include "draw_vertex.h"
#include "draw_pipe.h"
#include "translate/translate.h"
#include "translate/translate_cache.h"
/**
* Vertex buffer emit stage.
*/
struct vbuf_stage {
struct draw_stage stage; /**< This must be first (base class) */
struct vbuf_render *render;
const struct vertex_info *vinfo;
/** Vertex size in bytes */
unsigned vertex_size;
struct translate *translate;
/* FIXME: we have no guarantee that 'unsigned' is 32bit */
/** Vertices in hardware format */
uint8_t *vertices;
uint8_t *vertex_ptr;
unsigned max_vertices;
unsigned nr_vertices;
/** Indices */
uint16_t *indices;
unsigned max_indices;
unsigned nr_indices;
/* Cache point size somewhere its address won't change:
*/
float point_size;
float zero4[4];
struct translate_cache *cache;
};
/**
* Basically a cast wrapper.
*/
static inline struct vbuf_stage *
vbuf_stage(struct draw_stage *stage)
{
assert(stage);
return (struct vbuf_stage *)stage;
}
static void vbuf_flush_vertices(struct vbuf_stage *vbuf);
static void vbuf_alloc_vertices(struct vbuf_stage *vbuf);
static inline void
check_space(struct vbuf_stage *vbuf, unsigned nr)
{
if (vbuf->nr_vertices + nr > vbuf->max_vertices ||
vbuf->nr_indices + nr > vbuf->max_indices) {
vbuf_flush_vertices(vbuf);
vbuf_alloc_vertices(vbuf);
}
}
/**
* Extract the needed fields from post-transformed vertex and emit
* a hardware(driver) vertex.
* Recall that the vertices are constructed by the 'draw' module and
* have a couple of slots at the beginning (1-dword header, 4-dword
* clip pos) that we ignore here. We only use the vertex->data[] fields.
*/
static inline uint16_t
emit_vertex(struct vbuf_stage *vbuf, struct vertex_header *vertex)
{
if (vertex->vertex_id == UNDEFINED_VERTEX_ID && vbuf->vertex_ptr) {
/* Hmm - vertices are emitted one at a time - better make sure
* set_buffer is efficient. Consider a special one-shot mode for
* translate.
*/
/* Note: we really do want data[0] here, not data[pos]:
*/
vbuf->translate->set_buffer(vbuf->translate, 0, vertex->data[0], 0, ~0);
vbuf->translate->run(vbuf->translate, 0, 1, 0, 0, vbuf->vertex_ptr);
if (0) draw_dump_emitted_vertex(vbuf->vinfo, (uint8_t *)vbuf->vertex_ptr);
vbuf->vertex_ptr += vbuf->vertex_size;
vertex->vertex_id = vbuf->nr_vertices++;
}
return (uint16_t)vertex->vertex_id;
}
static void
vbuf_tri(struct draw_stage *stage, struct prim_header *prim)
{
struct vbuf_stage *vbuf = vbuf_stage(stage);
unsigned i;
check_space(vbuf, 3);
for (i = 0; i < 3; i++) {
vbuf->indices[vbuf->nr_indices++] = emit_vertex(vbuf, prim->v[i]);
}
}
static void
vbuf_line(struct draw_stage *stage, struct prim_header *prim)
{
struct vbuf_stage *vbuf = vbuf_stage(stage);
unsigned i;
check_space(vbuf, 2);
for (i = 0; i < 2; i++) {
vbuf->indices[vbuf->nr_indices++] = emit_vertex(vbuf, prim->v[i]);
}
}
static void
vbuf_point(struct draw_stage *stage, struct prim_header *prim)
{
struct vbuf_stage *vbuf = vbuf_stage(stage);
check_space(vbuf, 1);
vbuf->indices[vbuf->nr_indices++] = emit_vertex(vbuf, prim->v[0]);
}
/**
* Set the prim type for subsequent vertices.
* This may result in a new vertex size. The existing vbuffer (if any)
* will be flushed if needed and a new one allocated.
*/
static void
vbuf_start_prim(struct vbuf_stage *vbuf, enum mesa_prim prim)
{
struct translate_key hw_key;
unsigned dst_offset;
unsigned i;
const struct vertex_info *vinfo;
vbuf->render->set_primitive(vbuf->render, prim);
if (vbuf->render->set_view_index)
vbuf->render->set_view_index(vbuf->render, vbuf->stage.draw->pt.user.viewid);
/* Must do this after set_primitive() above:
*
* XXX: need some state managment to track when this needs to be
* recalculated. The driver should tell us whether there was a
* state change.
*/
vbuf->vinfo = vbuf->render->get_vertex_info(vbuf->render);
vinfo = vbuf->vinfo;
vbuf->vertex_size = vinfo->size * sizeof(float);
/* Translate from pipeline vertices to hw vertices.
*/
dst_offset = 0;
for (i = 0; i < vinfo->num_attribs; i++) {
unsigned emit_sz = 0;
unsigned src_buffer = 0;
enum pipe_format output_format;
unsigned src_offset = (vinfo->attrib[i].src_index * 4 * sizeof(float));
output_format = draw_translate_vinfo_format(vinfo->attrib[i].emit);
emit_sz = draw_translate_vinfo_size(vinfo->attrib[i].emit);
/* doesn't handle EMIT_OMIT */
assert(emit_sz != 0);
if (vinfo->attrib[i].emit == EMIT_1F_PSIZE) {
src_buffer = 1;
src_offset = 0;
}
else if (vinfo->attrib[i].src_index == DRAW_ATTR_NONEXIST) {
/* elements which don't exist will get assigned zeros */
src_buffer = 2;
src_offset = 0;
}
hw_key.element[i].type = TRANSLATE_ELEMENT_NORMAL;
hw_key.element[i].input_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
hw_key.element[i].input_buffer = src_buffer;
hw_key.element[i].input_offset = src_offset;
hw_key.element[i].instance_divisor = 0;
hw_key.element[i].output_format = output_format;
hw_key.element[i].output_offset = dst_offset;
dst_offset += emit_sz;
}
hw_key.nr_elements = vinfo->num_attribs;
hw_key.output_stride = vbuf->vertex_size;
/* Don't bother with caching at this stage:
*/
if (!vbuf->translate ||
translate_key_compare(&vbuf->translate->key, &hw_key) != 0) {
translate_key_sanitize(&hw_key);
vbuf->translate = translate_cache_find(vbuf->cache, &hw_key);
vbuf->translate->set_buffer(vbuf->translate, 1, &vbuf->point_size, 0, ~0);
vbuf->translate->set_buffer(vbuf->translate, 2, &vbuf->zero4[0], 0, ~0);
}
vbuf->point_size = vbuf->stage.draw->rasterizer->point_size;
/* Allocate new buffer?
*/
assert(vbuf->vertices == NULL);
vbuf_alloc_vertices(vbuf);
}
static void
vbuf_first_tri(struct draw_stage *stage, struct prim_header *prim)
{
struct vbuf_stage *vbuf = vbuf_stage(stage);
vbuf_flush_vertices(vbuf);
vbuf_start_prim(vbuf, MESA_PRIM_TRIANGLES);
stage->tri = vbuf_tri;
stage->tri(stage, prim);
}
static void
vbuf_first_line(struct draw_stage *stage, struct prim_header *prim)
{
struct vbuf_stage *vbuf = vbuf_stage(stage);
vbuf_flush_vertices(vbuf);
vbuf_start_prim(vbuf, MESA_PRIM_LINES);
stage->line = vbuf_line;
stage->line(stage, prim);
}
static void
vbuf_first_point(struct draw_stage *stage, struct prim_header *prim)
{
struct vbuf_stage *vbuf = vbuf_stage(stage);
vbuf_flush_vertices(vbuf);
vbuf_start_prim(vbuf, MESA_PRIM_POINTS);
stage->point = vbuf_point;
stage->point(stage, prim);
}
/**
* Flush existing vertex buffer and allocate a new one.
*/
static void
vbuf_flush_vertices(struct vbuf_stage *vbuf)
{
if (vbuf->vertices) {
vbuf->render->unmap_vertices(vbuf->render, 0, vbuf->nr_vertices - 1);
if (vbuf->nr_indices) {
vbuf->render->draw_elements(vbuf->render,
vbuf->indices,
vbuf->nr_indices);
vbuf->nr_indices = 0;
}
/* Reset temporary vertices ids */
if (vbuf->nr_vertices)
draw_reset_vertex_ids(vbuf->stage.draw);
/* Free the vertex buffer */
vbuf->render->release_vertices(vbuf->render);
vbuf->max_vertices = vbuf->nr_vertices = 0;
vbuf->vertex_ptr = vbuf->vertices = NULL;
}
/* Reset point/line/tri function pointers.
* If (for example) we transition from points to tris and back to points
* again, we need to call the vbuf_first_point() function again to flush
* the triangles before drawing more points. This can happen when drawing
* with front polygon mode = filled and back polygon mode = line or point.
*/
vbuf->stage.point = vbuf_first_point;
vbuf->stage.line = vbuf_first_line;
vbuf->stage.tri = vbuf_first_tri;
}
static void
vbuf_alloc_vertices(struct vbuf_stage *vbuf)
{
if (vbuf->vertex_ptr) {
assert(!vbuf->nr_indices);
assert(!vbuf->vertices);
}
/* Allocate a new vertex buffer */
vbuf->max_vertices =
vbuf->render->max_vertex_buffer_bytes / vbuf->vertex_size;
if (vbuf->max_vertices >= UNDEFINED_VERTEX_ID)
vbuf->max_vertices = UNDEFINED_VERTEX_ID - 1;
/* Must always succeed -- driver gives us a
* 'max_vertex_buffer_bytes' which it guarantees it can allocate,
* and it will flush itself if necessary to do so. If this does
* fail, we are basically without usable hardware.
*/
vbuf->render->allocate_vertices(vbuf->render,
(uint16_t) vbuf->vertex_size,
(uint16_t) vbuf->max_vertices);
vbuf->vertex_ptr = vbuf->vertices =
vbuf->render->map_vertices(vbuf->render);
}
static void
vbuf_flush(struct draw_stage *stage, unsigned flags)
{
struct vbuf_stage *vbuf = vbuf_stage(stage);
vbuf_flush_vertices(vbuf);
}
static void
vbuf_reset_stipple_counter(struct draw_stage *stage)
{
/* XXX: Need to do something here for hardware with linestipple.
*/
(void) stage;
}
static void
vbuf_destroy(struct draw_stage *stage)
{
struct vbuf_stage *vbuf = vbuf_stage(stage);
if (vbuf->indices)
align_free(vbuf->indices);
if (vbuf->render)
vbuf->render->destroy(vbuf->render);
if (vbuf->cache)
translate_cache_destroy(vbuf->cache);
FREE(stage);
}
/**
* Create a new primitive vbuf/render stage.
*/
struct draw_stage *
draw_vbuf_stage(struct draw_context *draw, struct vbuf_render *render)
{
struct vbuf_stage *vbuf = CALLOC_STRUCT(vbuf_stage);
if (!vbuf)
goto fail;
vbuf->stage.draw = draw;
vbuf->stage.name = "vbuf";
vbuf->stage.point = vbuf_first_point;
vbuf->stage.line = vbuf_first_line;
vbuf->stage.tri = vbuf_first_tri;
vbuf->stage.flush = vbuf_flush;
vbuf->stage.reset_stipple_counter = vbuf_reset_stipple_counter;
vbuf->stage.destroy = vbuf_destroy;
vbuf->render = render;
vbuf->max_indices = MIN2(render->max_indices, UNDEFINED_VERTEX_ID-1);
vbuf->indices = (uint16_t *) align_malloc(vbuf->max_indices *
sizeof(vbuf->indices[0]),
16);
if (!vbuf->indices)
goto fail;
vbuf->cache = translate_cache_create();
if (!vbuf->cache)
goto fail;
vbuf->vertex_ptr = vbuf->vertices = NULL;
vbuf->zero4[0] = vbuf->zero4[1] = vbuf->zero4[2] = vbuf->zero4[3] = 0.0f;
return &vbuf->stage;
fail:
if (vbuf)
vbuf_destroy(&vbuf->stage);
return NULL;
}
@@ -0,0 +1,220 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/* Authors: Keith Whitwell <[email protected]>
*/
#include "pipe/p_context.h"
#include "pipe/p_defines.h"
#include "pipe/p_shader_tokens.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "draw_private.h"
#include "draw_pipe.h"
struct wideline_stage {
struct draw_stage stage;
};
/**
* Draw a wide line by drawing a quad (two triangles).
*/
static void
wideline_line(struct draw_stage *stage,
struct prim_header *header)
{
const unsigned pos = draw_current_shader_position_output(stage->draw);
const float half_width = 0.5f * stage->draw->rasterizer->line_width;
struct prim_header tri;
struct vertex_header *v0 = dup_vert(stage, header->v[0], 0);
struct vertex_header *v1 = dup_vert(stage, header->v[0], 1);
struct vertex_header *v2 = dup_vert(stage, header->v[1], 2);
struct vertex_header *v3 = dup_vert(stage, header->v[1], 3);
float *pos0 = v0->data[pos];
float *pos1 = v1->data[pos];
float *pos2 = v2->data[pos];
float *pos3 = v3->data[pos];
const float dx = fabsf(pos0[0] - pos2[0]);
const float dy = fabsf(pos0[1] - pos2[1]);
const bool half_pixel_center =
stage->draw->rasterizer->half_pixel_center;
/* small tweak to meet GL specification */
const float bias = half_pixel_center ? 0.125f : 0.0f;
/*
* Draw wide line as a quad (two tris) by "stretching" the line along
* X or Y.
* We need to tweak coords in several ways to be conformant here.
*/
if (dx > dy) {
/* x-major line */
pos0[1] = pos0[1] - half_width - bias;
pos1[1] = pos1[1] + half_width - bias;
pos2[1] = pos2[1] - half_width - bias;
pos3[1] = pos3[1] + half_width - bias;
if (half_pixel_center) {
if (pos0[0] < pos2[0]) {
/* left to right line */
pos0[0] -= 0.5f;
pos1[0] -= 0.5f;
pos2[0] -= 0.5f;
pos3[0] -= 0.5f;
} else {
/* right to left line */
pos0[0] += 0.5f;
pos1[0] += 0.5f;
pos2[0] += 0.5f;
pos3[0] += 0.5f;
}
}
} else {
/* y-major line */
pos0[0] = pos0[0] - half_width + bias;
pos1[0] = pos1[0] + half_width + bias;
pos2[0] = pos2[0] - half_width + bias;
pos3[0] = pos3[0] + half_width + bias;
if (half_pixel_center) {
if (pos0[1] < pos2[1]) {
/* top to bottom line */
pos0[1] -= 0.5f;
pos1[1] -= 0.5f;
pos2[1] -= 0.5f;
pos3[1] -= 0.5f;
} else {
/* bottom to top line */
pos0[1] += 0.5f;
pos1[1] += 0.5f;
pos2[1] += 0.5f;
pos3[1] += 0.5f;
}
}
}
tri.det = header->det; /* only the sign matters */
tri.v[0] = v0;
tri.v[1] = v2;
tri.v[2] = v3;
stage->next->tri(stage->next, &tri);
tri.v[0] = v0;
tri.v[1] = v3;
tri.v[2] = v1;
stage->next->tri(stage->next, &tri);
}
static void
wideline_first_line(struct draw_stage *stage,
struct prim_header *header)
{
struct draw_context *draw = stage->draw;
struct pipe_context *pipe = draw->pipe;
const struct pipe_rasterizer_state *rast = draw->rasterizer;
void *r;
/* Disable triangle culling, stippling, unfilled mode etc. */
r = draw_get_rasterizer_no_cull(draw, rast);
draw->suspend_flushing = true;
pipe->bind_rasterizer_state(pipe, r);
draw->suspend_flushing = false;
stage->line = wideline_line;
wideline_line(stage, header);
}
static void
wideline_flush(struct draw_stage *stage, unsigned flags)
{
struct draw_context *draw = stage->draw;
struct pipe_context *pipe = draw->pipe;
stage->line = wideline_first_line;
stage->next->flush(stage->next, flags);
/* restore original rasterizer state */
if (draw->rast_handle) {
draw->suspend_flushing = true;
pipe->bind_rasterizer_state(pipe, draw->rast_handle);
draw->suspend_flushing = false;
}
}
static void
wideline_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
wideline_destroy(struct draw_stage *stage)
{
draw_free_temp_verts(stage);
FREE(stage);
}
struct draw_stage *
draw_wide_line_stage(struct draw_context *draw)
{
struct wideline_stage *wide = CALLOC_STRUCT(wideline_stage);
if (!wide)
goto fail;
wide->stage.draw = draw;
wide->stage.name = "wide-line";
wide->stage.next = NULL;
wide->stage.point = draw_pipe_passthrough_point;
wide->stage.line = wideline_first_line;
wide->stage.tri = draw_pipe_passthrough_tri;
wide->stage.flush = wideline_flush;
wide->stage.reset_stipple_counter = wideline_reset_stipple_counter;
wide->stage.destroy = wideline_destroy;
if (!draw_alloc_temp_verts(&wide->stage, 4))
goto fail;
return &wide->stage;
fail:
if (wide)
wide->stage.destroy(&wide->stage);
return NULL;
}
@@ -0,0 +1,342 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/* Authors: Keith Whitwell <[email protected]>
*/
/**
* Notes on wide points and sprite mode:
*
* In wide point/sprite mode we effectively need to convert each incoming
* vertex into four outgoing vertices specifying the corners of a quad.
* Since we don't (yet) have geometry shaders, we have to handle this here
* in the draw module.
*
* For sprites, it also means that this is where we have to handle texcoords
* for the vertices of the quad. OpenGL's GL_COORD_REPLACE state specifies
* if/how enabled texcoords are automatically generated for sprites. We pass
* that info through gallium in the pipe_rasterizer_state::sprite_coord_mode
* array.
*
* Additionally, GLSL's gl_PointCoord fragment attribute has to be handled
* here as well. This is basically an additional texture/generic attribute
* that varies .x from 0 to 1 horizontally across the point and varies .y
* vertically from 0 to 1 down the sprite.
*
* With geometry shaders, the gallium frontends could create a GS to do
* most/all of this.
*/
#include "pipe/p_screen.h"
#include "pipe/p_context.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "pipe/p_defines.h"
#include "pipe/p_shader_tokens.h"
#include "draw_fs.h"
#include "draw_vs.h"
#include "draw_pipe.h"
struct widepoint_stage {
struct draw_stage stage; /**< base class */
float half_point_size;
float xbias;
float ybias;
/** for automatic texcoord generation/replacement */
unsigned num_texcoord_gen;
unsigned texcoord_gen_slot[PIPE_MAX_SHADER_OUTPUTS];
/* TGSI_SEMANTIC to which sprite_coord_enable applies */
enum tgsi_semantic sprite_coord_semantic;
int psize_slot;
};
static inline struct widepoint_stage *
widepoint_stage(struct draw_stage *stage)
{
return (struct widepoint_stage *)stage;
}
/**
* Set the vertex texcoords for sprite mode.
* Coords may be left untouched or set to a right-side-up or upside-down
* orientation.
*/
static void
set_texcoords(const struct widepoint_stage *wide,
struct vertex_header *v, const float tc[4])
{
const struct draw_context *draw = wide->stage.draw;
const struct pipe_rasterizer_state *rast = draw->rasterizer;
const unsigned texcoord_mode = rast->sprite_coord_mode;
for (unsigned i = 0; i < wide->num_texcoord_gen; i++) {
const unsigned slot = wide->texcoord_gen_slot[i];
v->data[slot][0] = tc[0];
if (texcoord_mode == PIPE_SPRITE_COORD_LOWER_LEFT)
v->data[slot][1] = 1.0f - tc[1];
else
v->data[slot][1] = tc[1];
v->data[slot][2] = tc[2];
v->data[slot][3] = tc[3];
}
}
/* If there are lots of sprite points (and why wouldn't there be?) it
* would probably be more sensible to change hardware setup to
* optimize this rather than doing the whole thing in software like
* this.
*/
static void
widepoint_point(struct draw_stage *stage,
struct prim_header *header)
{
const struct widepoint_stage *wide = widepoint_stage(stage);
const unsigned pos = draw_current_shader_position_output(stage->draw);
const bool sprite = (bool) stage->draw->rasterizer->point_quad_rasterization;
float half_size;
float left_adj, right_adj, bot_adj, top_adj;
struct prim_header tri;
/* four dups of original vertex */
struct vertex_header *v0 = dup_vert(stage, header->v[0], 0);
struct vertex_header *v1 = dup_vert(stage, header->v[0], 1);
struct vertex_header *v2 = dup_vert(stage, header->v[0], 2);
struct vertex_header *v3 = dup_vert(stage, header->v[0], 3);
float *pos0 = v0->data[pos];
float *pos1 = v1->data[pos];
float *pos2 = v2->data[pos];
float *pos3 = v3->data[pos];
/* point size is either per-vertex or fixed size */
if (wide->psize_slot >= 0) {
half_size = header->v[0]->data[wide->psize_slot][0];
half_size *= 0.5f;
}
else {
half_size = wide->half_point_size;
}
left_adj = -half_size + wide->xbias;
right_adj = half_size + wide->xbias;
bot_adj = half_size + wide->ybias;
top_adj = -half_size + wide->ybias;
pos0[0] += left_adj;
pos0[1] += top_adj;
pos1[0] += left_adj;
pos1[1] += bot_adj;
pos2[0] += right_adj;
pos2[1] += top_adj;
pos3[0] += right_adj;
pos3[1] += bot_adj;
if (sprite) {
static const float tex00[4] = { 0, 0, 0, 1 };
static const float tex01[4] = { 0, 1, 0, 1 };
static const float tex11[4] = { 1, 1, 0, 1 };
static const float tex10[4] = { 1, 0, 0, 1 };
set_texcoords(wide, v0, tex00);
set_texcoords(wide, v1, tex01);
set_texcoords(wide, v2, tex10);
set_texcoords(wide, v3, tex11);
}
tri.det = header->det; /* only the sign matters */
tri.v[0] = v0;
tri.v[1] = v2;
tri.v[2] = v3;
stage->next->tri(stage->next, &tri);
tri.v[0] = v0;
tri.v[1] = v3;
tri.v[2] = v1;
stage->next->tri(stage->next, &tri);
}
static void
widepoint_first_point(struct draw_stage *stage,
struct prim_header *header)
{
struct widepoint_stage *wide = widepoint_stage(stage);
struct draw_context *draw = stage->draw;
struct pipe_context *pipe = draw->pipe;
const struct pipe_rasterizer_state *rast = draw->rasterizer;
void *r;
wide->half_point_size = 0.5f * rast->point_size;
wide->xbias = 0.0;
wide->ybias = 0.0;
if (rast->half_pixel_center) {
wide->xbias = 0.125;
wide->ybias = -0.125;
}
/* Disable triangle culling, stippling, unfilled mode etc. */
r = draw_get_rasterizer_no_cull(draw, rast);
draw->suspend_flushing = true;
pipe->bind_rasterizer_state(pipe, r);
draw->suspend_flushing = false;
/* XXX we won't know the real size if it's computed by the vertex shader! */
if ((rast->point_size > draw->pipeline.wide_point_threshold) ||
(rast->point_quad_rasterization && draw->pipeline.point_sprite)) {
stage->point = widepoint_point;
}
else {
stage->point = draw_pipe_passthrough_point;
}
draw_remove_extra_vertex_attribs(draw);
if (rast->point_quad_rasterization) {
const struct draw_fragment_shader *fs = draw->fs.fragment_shader;
assert(fs);
wide->num_texcoord_gen = 0;
/* Loop over fragment shader inputs looking for the PCOORD input or inputs
* for which bit 'k' in sprite_coord_enable is set.
*/
for (unsigned i = 0; i < fs->info.num_inputs; i++) {
int slot;
const enum tgsi_semantic sn = fs->info.input_semantic_name[i];
const unsigned si = fs->info.input_semantic_index[i];
if (sn == wide->sprite_coord_semantic) {
/* Note that sprite_coord_enable is a bitfield of 32 bits. */
if (si >= 32 || !(rast->sprite_coord_enable & (1 << si)))
continue;
} else if (sn != TGSI_SEMANTIC_PCOORD) {
continue;
}
/* OK, this generic attribute needs to be replaced with a
* sprite coord (see above).
*/
slot = draw_alloc_extra_vertex_attrib(draw, sn, si);
/* add this slot to the texcoord-gen list */
wide->texcoord_gen_slot[wide->num_texcoord_gen++] = slot;
}
}
wide->psize_slot = -1;
if (rast->point_size_per_vertex) {
/* find PSIZ vertex output */
wide->psize_slot = draw_find_shader_output(draw, TGSI_SEMANTIC_PSIZE, 0);
}
stage->point(stage, header);
}
static void
widepoint_flush(struct draw_stage *stage, unsigned flags)
{
struct draw_context *draw = stage->draw;
struct pipe_context *pipe = draw->pipe;
stage->point = widepoint_first_point;
stage->next->flush(stage->next, flags);
draw_remove_extra_vertex_attribs(draw);
/* restore original rasterizer state */
if (draw->rast_handle) {
draw->suspend_flushing = true;
pipe->bind_rasterizer_state(pipe, draw->rast_handle);
draw->suspend_flushing = false;
}
}
static void
widepoint_reset_stipple_counter(struct draw_stage *stage)
{
stage->next->reset_stipple_counter(stage->next);
}
static void
widepoint_destroy(struct draw_stage *stage)
{
draw_free_temp_verts(stage);
FREE(stage);
}
struct draw_stage *draw_wide_point_stage(struct draw_context *draw)
{
struct widepoint_stage *wide = CALLOC_STRUCT(widepoint_stage);
if (!wide)
goto fail;
wide->stage.draw = draw;
wide->stage.name = "wide-point";
wide->stage.next = NULL;
wide->stage.point = widepoint_first_point;
wide->stage.line = draw_pipe_passthrough_line;
wide->stage.tri = draw_pipe_passthrough_tri;
wide->stage.flush = widepoint_flush;
wide->stage.reset_stipple_counter = widepoint_reset_stipple_counter;
wide->stage.destroy = widepoint_destroy;
if (!draw_alloc_temp_verts(&wide->stage, 4))
goto fail;
wide->sprite_coord_semantic =
draw->pipe->screen->caps.tgsi_texcoord
?
TGSI_SEMANTIC_TEXCOORD : TGSI_SEMANTIC_GENERIC;
return &wide->stage;
fail:
if (wide)
wide->stage.destroy(&wide->stage);
return NULL;
}
@@ -0,0 +1,346 @@
/**************************************************************************
*
* Copyright 2013 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.
*
**************************************************************************/
#include "draw_prim_assembler.h"
#include "draw_fs.h"
#include "draw_gs.h"
#include "draw_tess.h"
#include "util/u_debug.h"
#include "util/u_memory.h"
#include "util/u_prim.h"
#include "pipe/p_defines.h"
struct draw_assembler
{
struct draw_context *draw;
struct draw_prim_info *output_prims;
struct draw_vertex_info *output_verts;
const struct draw_prim_info *input_prims;
const struct draw_vertex_info *input_verts;
bool needs_primid;
int primid_slot;
unsigned primid;
unsigned num_prims;
};
static bool
needs_primid(const struct draw_context *draw)
{
const struct draw_fragment_shader *fs = draw->fs.fragment_shader;
const struct draw_geometry_shader *gs = draw->gs.geometry_shader;
const struct draw_tess_eval_shader *tes = draw->tes.tess_eval_shader;
if (fs && fs->info.uses_primid) {
if (gs)
return !gs->info.uses_primid;
else if (tes)
return !tes->info.uses_primid;
else
return true;
}
return false;
}
bool
draw_prim_assembler_is_required(const struct draw_context *draw,
const struct draw_prim_info *prim_info,
const struct draw_vertex_info *vert_info)
{
/* viewport index requires primitive boundaries to get correct vertex */
if (draw_current_shader_uses_viewport_index(draw))
return true;
switch (prim_info->prim) {
case MESA_PRIM_LINES_ADJACENCY:
case MESA_PRIM_LINE_STRIP_ADJACENCY:
case MESA_PRIM_TRIANGLES_ADJACENCY:
case MESA_PRIM_TRIANGLE_STRIP_ADJACENCY:
return true;
default:
return needs_primid(draw);
}
}
static void
add_prim(struct draw_assembler *asmblr, unsigned length)
{
struct draw_prim_info *output_prims = asmblr->output_prims;
output_prims->primitive_lengths = realloc(output_prims->primitive_lengths, sizeof(unsigned) * (output_prims->primitive_count + 1));
output_prims->primitive_lengths[output_prims->primitive_count] = length;
output_prims->primitive_count++;
}
/*
* Copy the vertex header along with its data from the current
* vertex buffer into a buffer holding vertices arranged
* into decomposed primitives (i.e. buffer without the
* adjacency vertices)
*/
static void
copy_verts(struct draw_assembler *asmblr,
unsigned *indices, unsigned num_indices)
{
char *output = (char*)asmblr->output_verts->verts;
const char *input = (const char*)asmblr->input_verts->verts;
for (unsigned i = 0; i < num_indices; ++i) {
unsigned idx = indices[i];
unsigned output_offset =
asmblr->output_verts->count * asmblr->output_verts->stride;
unsigned input_offset = asmblr->input_verts->stride * idx;
memcpy(output + output_offset, input + input_offset,
asmblr->input_verts->vertex_size);
asmblr->output_verts->count += 1;
}
++asmblr->num_prims;
}
static void
inject_primid(struct draw_assembler *asmblr,
unsigned idx,
unsigned primid)
{
int slot = asmblr->primid_slot;
char *input = (char*)asmblr->input_verts->verts;
unsigned input_offset = asmblr->input_verts->stride * idx;
struct vertex_header *v = (struct vertex_header*)(input + input_offset);
/* In case the backend doesn't care about it */
if (slot < 0) {
return;
}
memcpy(&v->data[slot][0], &primid, sizeof(primid));
memcpy(&v->data[slot][1], &primid, sizeof(primid));
memcpy(&v->data[slot][2], &primid, sizeof(primid));
memcpy(&v->data[slot][3], &primid, sizeof(primid));
}
static void
prim_point(struct draw_assembler *asmblr,
unsigned idx)
{
unsigned indices[1];
if (asmblr->needs_primid) {
inject_primid(asmblr, idx, asmblr->primid++);
}
indices[0] = idx;
add_prim(asmblr, 1);
copy_verts(asmblr, indices, 1);
}
static void
prim_line(struct draw_assembler *asmblr,
unsigned i0, unsigned i1)
{
unsigned indices[2];
if (asmblr->needs_primid) {
inject_primid(asmblr, i0, asmblr->primid);
inject_primid(asmblr, i1, asmblr->primid++);
}
indices[0] = i0;
indices[1] = i1;
add_prim(asmblr, 2);
copy_verts(asmblr, indices, 2);
}
static void
prim_tri(struct draw_assembler *asmblr,
unsigned i0, unsigned i1, unsigned i2)
{
unsigned indices[3];
if (asmblr->needs_primid) {
inject_primid(asmblr, i0, asmblr->primid);
inject_primid(asmblr, i1, asmblr->primid);
inject_primid(asmblr, i2, asmblr->primid++);
}
indices[0] = i0;
indices[1] = i1;
indices[2] = i2;
add_prim(asmblr, 3);
copy_verts(asmblr, indices, 3);
}
static void
prim_quad(struct draw_assembler *asmblr,
unsigned i0, unsigned i1,
unsigned i2, unsigned i3)
{
unsigned indices[4];
if (asmblr->needs_primid) {
inject_primid(asmblr, i0, asmblr->primid);
inject_primid(asmblr, i1, asmblr->primid);
inject_primid(asmblr, i2, asmblr->primid);
inject_primid(asmblr, i3, asmblr->primid++);
}
indices[0] = i0;
indices[1] = i1;
indices[2] = i2;
indices[3] = i3;
add_prim(asmblr, 4);
copy_verts(asmblr, indices, 4);
}
void
draw_prim_assembler_prepare_outputs(struct draw_assembler *ia)
{
struct draw_context *draw = ia->draw;
if (needs_primid(draw)) {
ia->primid_slot = draw_alloc_extra_vertex_attrib(
ia->draw, TGSI_SEMANTIC_PRIMID, 0);
} else {
ia->primid_slot = -1;
}
}
#define FUNC assembler_run_linear
#define GET_ELT(idx) (start + (idx))
#include "draw_prim_assembler_tmp.h"
#define FUNC assembler_run_elts
#define LOCAL_VARS const uint16_t *elts = input_prims->elts;
#define GET_ELT(idx) (elts[start + (idx)])
#include "draw_prim_assembler_tmp.h"
/*
* Primitive assembler breaks up adjacency primitives and assembles
* the base primitives they represent, e.g. vertices forming
* MESA_PRIM_TRIANGLE_STRIP_ADJACENCY
* become vertices forming MESA_PRIM_TRIANGLES
* This is needed because specification says that the adjacency
* primitives are only visible in the geometry shader so we need
* to get rid of them so that the rest of the pipeline can
* process the inputs.
*/
void
draw_prim_assembler_run(struct draw_context *draw,
const struct draw_prim_info *input_prims,
const struct draw_vertex_info *input_verts,
struct draw_prim_info *output_prims,
struct draw_vertex_info *output_verts)
{
struct draw_assembler *asmblr = draw->ia;
unsigned start, i;
unsigned assembled_prim = (input_prims->prim == MESA_PRIM_QUADS ||
input_prims->prim == MESA_PRIM_QUAD_STRIP) ?
MESA_PRIM_QUADS : u_reduced_prim(input_prims->prim);
unsigned max_primitives = u_decomposed_prims_for_vertices(
input_prims->prim, input_prims->count);
unsigned max_verts = mesa_vertices_per_prim(assembled_prim) * max_primitives;
asmblr->output_prims = output_prims;
asmblr->output_verts = output_verts;
asmblr->input_prims = input_prims;
asmblr->input_verts = input_verts;
asmblr->needs_primid = needs_primid(asmblr->draw);
asmblr->num_prims = 0;
output_prims->linear = true;
output_prims->elts = NULL;
output_prims->start = 0;
output_prims->prim = assembled_prim;
output_prims->flags = 0x0;
output_prims->primitive_lengths = MALLOC(sizeof(unsigned));
output_prims->primitive_lengths[0] = 0;
output_prims->primitive_count = 1;
output_verts->vertex_size = input_verts->vertex_size;
output_verts->stride = input_verts->stride;
output_verts->verts = (struct vertex_header*)MALLOC(
input_verts->vertex_size * max_verts + DRAW_EXTRA_VERTICES_PADDING);
output_verts->count = 0;
for (start = i = 0; i < input_prims->primitive_count;
start += input_prims->primitive_lengths[i], i++) {
unsigned count = input_prims->primitive_lengths[i];
if (input_prims->linear) {
assembler_run_linear(asmblr, input_prims, input_verts,
start, count);
} else {
assembler_run_elts(asmblr, input_prims, input_verts,
start, count);
}
}
output_prims->count = output_verts->count;
}
struct draw_assembler *
draw_prim_assembler_create(struct draw_context *draw)
{
struct draw_assembler *ia = CALLOC_STRUCT(draw_assembler);
ia->draw = draw;
return ia;
}
void
draw_prim_assembler_destroy(struct draw_assembler *ia)
{
FREE(ia);
}
/*
* Called at the very begin of the draw call with a new instance
* Used to reset state that should persist between primitive restart.
*/
void
draw_prim_assembler_new_instance(struct draw_assembler *asmblr)
{
asmblr->primid = 0;
}
@@ -0,0 +1,77 @@
/**************************************************************************
*
* Copyright 2013 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.
*
**************************************************************************/
/*
* Input assembler needs to be able to decompose adjacency primitives
* into something that can be understood by the rest of the pipeline.
* The specs say that the adjacency primitives are *only* visible
* in the geometry shader, for everything else they need to be
* decomposed. Which in most of the cases is not an issue, because the
* geometry shader always decomposes them for us, but without geometry
* shader we were passing unchanged adjacency primitives to the
* rest of the pipeline and causing crashes everywhere.
* If geometry shader is missing and the input primitive is one of
* the adjacency primitives we use the code from this file to
* decompose them into something that the rest of the pipeline can
* understand.
*
*/
#ifndef DRAW_PRIM_ASSEMBLER_H
#define DRAW_PRIM_ASSEMBLER_H
#include "draw/draw_private.h"
struct draw_assembler;
struct draw_assembler *
draw_prim_assembler_create(struct draw_context *draw);
void
draw_prim_assembler_destroy(struct draw_assembler *ia);
bool
draw_prim_assembler_is_required(const struct draw_context *draw,
const struct draw_prim_info *prim_info,
const struct draw_vertex_info *vert_info);
void
draw_prim_assembler_run(struct draw_context *draw,
const struct draw_prim_info *in_prim_info,
const struct draw_vertex_info *in_vert_info,
struct draw_prim_info *out_prim_info,
struct draw_vertex_info *out_vert_info);
void
draw_prim_assembler_prepare_outputs(struct draw_assembler *ia);
void
draw_prim_assembler_new_instance(struct draw_assembler *ia);
#endif
@@ -0,0 +1,28 @@
#define FUNC_VARS \
struct draw_assembler *asmblr, \
const struct draw_prim_info *input_prims, \
const struct draw_vertex_info *input_verts, \
unsigned start, \
unsigned count
#define FUNC_ENTER \
/* declare more local vars */ \
const enum mesa_prim prim = input_prims->prim; \
const unsigned prim_flags = input_prims->flags; \
const bool last_vertex_last = !asmblr->draw->rasterizer->flatshade_first; \
switch (prim) { \
case MESA_PRIM_POLYGON: \
assert(!"unexpected primitive type in prim assembler"); \
return; \
default: \
break; \
}
#define PASS_QUADS
#define POINT(i0) prim_point(asmblr, i0)
#define LINE(flags, i0, i1) prim_line(asmblr, i0, i1)
#define TRIANGLE(flags, i0, i1, i2) prim_tri(asmblr, i0, i1, i2)
#define QUAD(flags, i0, i1, i2, i3) prim_quad(asmblr, i0, i1, i2, i3)
#include "draw_decompose_tmp.h"
+549
View File
@@ -0,0 +1,549 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/**
* Private data structures, etc for the draw module.
*/
/**
* Authors:
* Keith Whitwell <[email protected]>
* Brian Paul
*/
#ifndef DRAW_PRIVATE_H
#define DRAW_PRIVATE_H
#include "pipe/p_state.h"
#include "pipe/p_defines.h"
#include "pipe/p_shader_tokens.h"
#include "util/mesa-blake3.h"
#include "draw_vertex_header.h"
#if DRAW_LLVM_AVAILABLE
struct gallivm_state;
#endif
/**
* The max stage the draw stores resources for.
* i.e. vs, tcs, tes, gs. no fs/cs/ms/ts.
*/
#define DRAW_MAX_SHADER_STAGE (MESA_SHADER_GEOMETRY + 1)
/**
* The largest possible index of a vertex that can be fetched.
*/
#define DRAW_MAX_FETCH_IDX 0xffffffff
/**
* Maximum number of extra shader outputs. These are allocated by:
* - draw_pipe_aaline.c (1)
* - draw_pipe_aapoint.c (1)
* - draw_pipe_unfilled.c (1)
* - draw_pipe_wide_point.c (up to 32)
* - draw_prim_assembler.c (1)
*/
#define DRAW_MAX_EXTRA_SHADER_OUTPUTS 32
/**
* Despite some efforts to determine the number of extra shader outputs ahead
* of time, the matter of fact is that this number will vary as primitives
* flow through the draw pipeline. In particular, aaline/aapoint stages
* only allocate their extra shader outputs on the first line/point.
*
* Consequently dup_vert() ends up copying vertices larger than those
* allocated.
*
* Ideally we'd keep track of incoming/outgoing vertex sizes (and strides)
* throughout the draw pipeline, but unfortunately we recompute these all over
* the place, so preemptively expanding the vertex stride/size does not work
* as mismatches ensue.
*
* As stopgap to prevent buffer read overflows, we allocate an extra bit of
* padding at the end of temporary vertex buffers, allowing dup_vert() to copy
* more vertex attributes than allocated.
*/
#define DRAW_EXTRA_VERTICES_PADDING \
(DRAW_MAX_EXTRA_SHADER_OUTPUTS * sizeof(float[4]))
struct pipe_context;
struct draw_vertex_shader;
struct draw_stage;
struct draw_pt_front_end;
struct draw_assembler;
struct draw_llvm;
struct vbuf_render;
struct tgsi_exec_machine;
struct tgsi_sampler;
struct tgsi_image;
struct tgsi_buffer;
struct lp_cached_code;
struct draw_vertex_info;
struct draw_prim_info;
/**
* Represents the mapped vertex buffer.
*/
struct draw_vertex_buffer {
const void *map;
uint32_t size;
};
/* NOTE: It should match vertex_id size above */
#define UNDEFINED_VERTEX_ID 0xffff
/* maximum number of shader variants we can cache */
#define DRAW_MAX_SHADER_VARIANTS 512
struct draw_buffer_info {
const void *ptr;
unsigned size;
};
/**
* Private context for the drawing module.
*/
struct draw_context
{
struct pipe_context *pipe;
/** Drawing/primitive pipeline stages */
struct {
struct draw_stage *first; /**< one of the following */
struct draw_stage *validate;
/* stages (in logical order) */
struct draw_stage *flatshade;
struct draw_stage *clip;
struct draw_stage *cull;
struct draw_stage *user_cull;
struct draw_stage *twoside;
struct draw_stage *offset;
struct draw_stage *unfilled;
struct draw_stage *stipple;
struct draw_stage *aapoint;
struct draw_stage *aaline;
struct draw_stage *pstipple;
struct draw_stage *wide_line;
struct draw_stage *wide_point;
struct draw_stage *rasterize;
float wide_point_threshold; /**< convert pnts to tris if larger than this */
float wide_line_threshold; /**< convert lines to tris if wider than this */
bool wide_point_sprites; /**< convert points to tris for sprite mode */
bool line_stipple; /**< do line stipple? */
bool point_sprite; /**< convert points to quads for sprites? */
/* Temporary storage while the pipeline is being run:
*/
char *verts;
unsigned vertex_stride;
unsigned vertex_count;
} pipeline;
struct vbuf_render *render;
/* Support prototype passthrough path:
*/
struct {
/* Current active frontend */
struct draw_pt_front_end *frontend;
enum mesa_prim prim;
uint8_t vertices_per_patch;
bool rebind_parameters;
unsigned opt; /**< bitmask of PT_x flags */
unsigned eltSize; /* saved eltSize for flushing */
unsigned viewid; /* saved viewid for flushing */
struct {
struct draw_pt_middle_end *fetch_shade_emit;
struct draw_pt_middle_end *general;
struct draw_pt_middle_end *llvm;
struct draw_pt_middle_end *mesh;
} middle;
struct {
struct draw_pt_front_end *vsplit;
} front;
struct pipe_vertex_buffer vertex_buffer[PIPE_MAX_ATTRIBS];
unsigned nr_vertex_buffers;
/*
* This is the largest legal index value for the current set of
* bound vertex buffers. Regardless of any other consideration,
* all vertex lookups need to be clamped to 0..max_index to
* prevent out-of-bound access.
*/
unsigned max_index;
unsigned vertex_strides[PIPE_MAX_ATTRIBS];
struct pipe_vertex_element vertex_element[PIPE_MAX_ATTRIBS];
unsigned nr_vertex_elements;
bool test_fse; /* enable FSE even though its not correct (eg for softpipe) */
bool no_fse; /* disable FSE even when it is correct */
/* user-space vertex data, buffers */
struct {
/** vertex element/index buffer (ex: glDrawElements) */
const void *elts;
/** bytes per index (0, 1, 2 or 4) */
unsigned eltSizeIB;
unsigned eltSize;
unsigned eltMax;
int eltBias;
unsigned min_index;
unsigned max_index;
unsigned drawid;
bool increment_draw_id;
unsigned viewid;
/** vertex arrays */
struct draw_vertex_buffer vbuffer[PIPE_MAX_ATTRIBS];
/** constant buffers for each shader stage */
struct draw_buffer_info constants[DRAW_MAX_SHADER_STAGE][PIPE_MAX_CONSTANT_BUFFERS];
struct draw_buffer_info ssbos[DRAW_MAX_SHADER_STAGE][PIPE_MAX_SHADER_BUFFERS];
/* pointer to planes */
float (*planes)[DRAW_TOTAL_CLIP_PLANES][4];
} user;
} pt;
struct {
bool bypass_clip_xy;
bool bypass_clip_z;
bool guard_band_xy;
bool bypass_clip_points_lines;
} driver;
unsigned fpstate; /**< saved FP state */
bool quads_always_flatshade_last;
bool flushing; /**< debugging/sanity */
bool suspend_flushing; /**< internally set */
/* Flags set if API requires clipping in these planes and the
* driver doesn't indicate that it can do it for us.
*/
bool clip_xy;
bool clip_z;
bool clip_user;
bool guard_band_xy;
bool guard_band_points_lines_xy;
bool dump_vs;
bool identity_viewport;
bool bypass_viewport;
/** Depth format and bias related settings. */
bool floating_point_depth;
double mrd; /**< minimum resolvable depth value, for polygon offset */
/** Current rasterizer state given to us by the driver */
const struct pipe_rasterizer_state *rasterizer;
/** Driver CSO handle for the current rasterizer state */
void *rast_handle;
/** Rasterizer CSOs without culling/stipple/etc */
void *rasterizer_no_cull[2][2][2];
struct pipe_viewport_state viewports[PIPE_MAX_VIEWPORTS];
/** Vertex shader state */
struct {
struct draw_vertex_shader *vertex_shader;
unsigned num_vs_outputs; /**< convenience, from vertex_shader */
unsigned position_output;
unsigned edgeflag_output;
unsigned clipvertex_output;
unsigned ccdistance_output[2];
/** Fields for TGSI interpreter / execution */
struct {
struct tgsi_exec_machine *machine;
struct tgsi_sampler *sampler;
struct tgsi_image *image;
struct tgsi_buffer *buffer;
} tgsi;
struct translate *fetch;
struct translate_cache *fetch_cache;
struct translate *emit;
struct translate_cache *emit_cache;
} vs;
/** Geometry shader state */
struct {
struct draw_geometry_shader *geometry_shader;
unsigned num_gs_outputs; /**< convenience, from geometry_shader */
unsigned position_output;
unsigned clipvertex_output;
/** Fields for TGSI interpreter / execution */
struct {
struct tgsi_exec_machine *machine;
struct tgsi_sampler *sampler;
struct tgsi_image *image;
struct tgsi_buffer *buffer;
} tgsi;
} gs;
/* Tessellation state */
struct {
struct draw_tess_ctrl_shader *tess_ctrl_shader;
} tcs;
struct {
struct draw_tess_eval_shader *tess_eval_shader;
unsigned num_tes_outputs; /**< convenience, from tess_eval_shader */
unsigned position_output;
unsigned clipvertex_output;
} tes;
/** Fragment shader state */
struct {
struct draw_fragment_shader *fragment_shader;
} fs;
struct {
struct draw_mesh_shader *mesh_shader;
unsigned num_ms_outputs; /**< convenience, from geometry_shader */
unsigned position_output;
unsigned clipvertex_output;
} ms;
/** Stream output (vertex feedback) state */
struct {
struct draw_so_target *targets[PIPE_MAX_SO_BUFFERS];
unsigned num_targets;
} so;
/* Clip derived state:
*/
float plane[DRAW_TOTAL_CLIP_PLANES][4];
uint32_t viewmask;
/* If a prim stage introduces new vertex attributes, they'll be stored here
*/
struct {
unsigned num;
enum tgsi_semantic semantic_name[DRAW_MAX_EXTRA_SHADER_OUTPUTS];
unsigned semantic_index[DRAW_MAX_EXTRA_SHADER_OUTPUTS];
unsigned slot[DRAW_MAX_EXTRA_SHADER_OUTPUTS];
} extra_shader_outputs;
unsigned instance_id;
unsigned start_instance;
unsigned start_index;
unsigned constant_buffer_stride;
struct draw_llvm *llvm;
/** Texture sampler and sampler view state.
* Note that we have arrays indexed by shader type. At this time
* we only handle vertex and geometry shaders in the draw module, but
* there may be more in the future (ex: hull and tessellation).
*/
struct pipe_sampler_view *sampler_views[DRAW_MAX_SHADER_STAGE][PIPE_MAX_SHADER_SAMPLER_VIEWS];
unsigned num_sampler_views[DRAW_MAX_SHADER_STAGE];
const struct pipe_sampler_state *samplers[DRAW_MAX_SHADER_STAGE][PIPE_MAX_SAMPLERS];
unsigned num_samplers[DRAW_MAX_SHADER_STAGE];
struct pipe_image_view *images[DRAW_MAX_SHADER_STAGE][PIPE_MAX_SHADER_IMAGES];
unsigned num_images[DRAW_MAX_SHADER_STAGE];
struct pipe_query_data_pipeline_statistics statistics;
bool collect_statistics;
bool collect_primgen;
float default_outer_tess_level[4];
float default_inner_tess_level[2];
struct draw_assembler *ia;
void *disk_cache_cookie;
void (*disk_cache_find_shader)(void *cookie,
struct lp_cached_code *cache,
unsigned char ir_blake3_cache_key[BLAKE3_KEY_LEN]);
void (*disk_cache_insert_shader)(void *cookie,
struct lp_cached_code *cache,
unsigned char ir_blake3_cache_key[BLAKE3_KEY_LEN]);
void *driver_private;
};
struct draw_fetch_info {
bool linear;
unsigned start;
const unsigned *elts;
unsigned count;
};
/* these flags are set if the primitive is a segment of a larger one */
#define DRAW_SPLIT_BEFORE 0x1
#define DRAW_SPLIT_AFTER 0x2
#define DRAW_LINE_LOOP_AS_STRIP 0x4
/*******************************************************************************
* Draw common initialization code
*/
bool draw_init(struct draw_context *draw);
void draw_new_instance(struct draw_context *draw);
/*******************************************************************************
* Vertex shader code:
*/
bool draw_vs_init(struct draw_context *draw);
void draw_vs_destroy(struct draw_context *draw);
/*******************************************************************************
* Geometry shading code:
*/
bool draw_gs_init(struct draw_context *draw);
void draw_gs_destroy(struct draw_context *draw);
/*******************************************************************************
* Common shading code:
*/
unsigned draw_current_shader_outputs(const struct draw_context *draw);
unsigned draw_current_shader_position_output(const struct draw_context *draw);
unsigned draw_current_shader_viewport_index_output(const struct draw_context *draw);
unsigned draw_current_shader_clipvertex_output(const struct draw_context *draw);
unsigned draw_current_shader_ccdistance_output(const struct draw_context *draw, int index);
unsigned draw_current_shader_num_written_clipdistances(const struct draw_context *draw);
unsigned draw_current_shader_num_written_culldistances(const struct draw_context *draw);
int draw_alloc_extra_vertex_attrib(struct draw_context *draw,
enum tgsi_semantic semantic_name,
unsigned semantic_index);
void draw_remove_extra_vertex_attribs(struct draw_context *draw);
bool draw_current_shader_uses_viewport_index(
const struct draw_context *draw);
/*******************************************************************************
* Vertex processing (was passthrough) code:
*/
bool draw_pt_init(struct draw_context *draw);
void draw_pt_destroy(struct draw_context *draw);
void draw_pt_reset_vertex_ids(struct draw_context *draw);
void draw_pt_flush(struct draw_context *draw, unsigned flags);
/*******************************************************************************
* Primitive processing (pipeline) code:
*/
bool draw_pipeline_init(struct draw_context *draw);
void draw_pipeline_destroy(struct draw_context *draw);
/*
* These flags are used by the pipeline when unfilled and/or line stipple modes
* are operational.
*/
#define DRAW_PIPE_EDGE_FLAG_0 0x1
#define DRAW_PIPE_EDGE_FLAG_1 0x2
#define DRAW_PIPE_EDGE_FLAG_2 0x4
#define DRAW_PIPE_EDGE_FLAG_ALL 0x7
#define DRAW_PIPE_RESET_STIPPLE 0x8
void
draw_pipeline_run(struct draw_context *draw,
const struct draw_vertex_info *vert,
const struct draw_prim_info *prim);
void
draw_pipeline_run_linear(struct draw_context *draw,
const struct draw_vertex_info *vert,
const struct draw_prim_info *prim);
void
draw_pipeline_flush(struct draw_context *draw,
unsigned flags);
/*
* Flushing
*/
#define DRAW_FLUSH_PARAMETER_CHANGE 0x1 /**< Constants, viewport, etc */
#define DRAW_FLUSH_STATE_CHANGE 0x2 /**< Other/heavy state changes */
#define DRAW_FLUSH_BACKEND 0x4 /**< Flush the output buffer */
void
draw_do_flush(struct draw_context *draw, unsigned flags);
void *
draw_get_rasterizer_no_cull(struct draw_context *draw,
const struct pipe_rasterizer_state *rast);
void
draw_stats_clipper_primitives(struct draw_context *draw,
const struct draw_prim_info *prim_info);
void
draw_update_clip_flags(struct draw_context *draw);
void
draw_update_viewport_flags(struct draw_context *draw);
/**
* Return index i from the index buffer.
* If the index buffer would overflow we return index 0.
*/
#define DRAW_GET_IDX(_elts, _i) \
(((_i) >= draw->pt.user.eltMax) ? 0 : (_elts)[_i])
/**
* Return index of the given viewport clamping it
* to be between 0 <= and < PIPE_MAX_VIEWPORTS
*/
static inline unsigned
draw_clamp_viewport_idx(int idx)
{
return ((PIPE_MAX_VIEWPORTS > idx && idx >= 0) ? idx : 0);
}
#endif /* DRAW_PRIVATE_H */
+655
View File
@@ -0,0 +1,655 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Authors:
* Keith Whitwell <[email protected]>
*/
#include "draw/draw_context.h"
#include "draw/draw_gs.h"
#include "draw/draw_tess.h"
#include "draw/draw_private.h"
#include "draw/draw_pt.h"
#include "draw/draw_vbuf.h"
#include "draw/draw_vs.h"
#include "tgsi/tgsi_dump.h"
#include "util/u_math.h"
#include "util/u_prim.h"
#include "util/format/u_format.h"
#include "util/u_draw.h"
DEBUG_GET_ONCE_BOOL_OPTION(draw_fse, "DRAW_FSE", false)
DEBUG_GET_ONCE_BOOL_OPTION(draw_no_fse, "DRAW_NO_FSE", false)
/* Overall we split things into:
* - frontend -- prepare fetch_elts, draw_elts - eg vsplit
* - middle -- fetch, shade, cliptest, viewport
* - pipeline -- the prim pipeline: clipping, wide lines, etc
* - backend -- the vbuf_render provided by the driver.
*/
static bool
draw_pt_arrays(struct draw_context *draw,
enum mesa_prim prim,
bool index_bias_varies,
const struct pipe_draw_start_count_bias *draw_info,
unsigned num_draws)
{
enum mesa_prim out_prim = prim;
if (draw->gs.geometry_shader)
out_prim = draw->gs.geometry_shader->output_primitive;
else if (draw->tes.tess_eval_shader)
out_prim = get_tes_output_prim(draw->tes.tess_eval_shader);
unsigned opt = PT_SHADE;
if (!draw->render) {
opt |= PT_PIPELINE;
}
if (draw_need_pipeline(draw, draw->rasterizer, out_prim)) {
opt |= PT_PIPELINE;
}
if ((draw->clip_xy ||
draw->clip_z ||
draw->clip_user) && !draw->pt.test_fse) {
opt |= PT_CLIPTEST;
}
struct draw_pt_middle_end *middle;
if (draw->pt.middle.llvm) {
middle = draw->pt.middle.llvm;
} else {
if (opt == PT_SHADE && !draw->pt.no_fse)
middle = draw->pt.middle.fetch_shade_emit;
else
middle = draw->pt.middle.general;
}
struct draw_pt_front_end *frontend = draw->pt.frontend;
if (frontend) {
if (draw->pt.prim != prim || draw->pt.opt != opt) {
/* In certain conditions switching primitives requires us to flush
* and validate the different stages. One example is when smooth
* lines are active but first drawn with triangles and then with
* lines.
*/
draw_do_flush(draw, DRAW_FLUSH_STATE_CHANGE);
frontend = NULL;
} else if (draw->pt.eltSize != draw->pt.user.eltSize || draw->pt.viewid != draw->pt.user.viewid) {
/* Flush draw state if eltSize or viewid changed.
* eltSize changes could be improved so only the frontend is flushed since it
* converts all indices to ushorts and the fetch part of the middle
* always prepares both linear and indexed.
*/
frontend->flush(frontend, DRAW_FLUSH_STATE_CHANGE);
frontend = NULL;
}
}
if (!frontend) {
frontend = draw->pt.front.vsplit;
frontend->prepare(frontend, prim, middle, opt);
draw->pt.frontend = frontend;
draw->pt.eltSize = draw->pt.user.eltSize;
draw->pt.viewid = draw->pt.user.viewid;
draw->pt.prim = prim;
draw->pt.opt = opt;
}
if (draw->pt.rebind_parameters) {
/* update constants, viewport dims, clip planes, etc */
middle->bind_parameters(middle);
draw->pt.rebind_parameters = false;
}
for (unsigned i = 0; i < num_draws; i++) {
/* Sanitize primitive length:
*/
unsigned first, incr;
if (prim == MESA_PRIM_PATCHES) {
first = draw->pt.vertices_per_patch;
incr = draw->pt.vertices_per_patch;
} else {
draw_pt_split_prim(prim, &first, &incr);
}
unsigned count = draw_pt_trim_count(draw_info[i].count, first, incr);
if (draw->pt.user.eltSize) {
if (index_bias_varies) {
draw->pt.user.eltBias = draw_info[i].index_bias;
} else {
draw->pt.user.eltBias = draw_info[0].index_bias;
}
} else {
draw->pt.user.eltBias = 0;
}
draw->start_index = draw_info[i].start;
if (count >= first)
frontend->run(frontend, draw_info[i].start, count);
if (num_draws > 1 && draw->pt.user.increment_draw_id)
draw->pt.user.drawid++;
}
return true;
}
void
draw_pt_flush(struct draw_context *draw, unsigned flags)
{
assert(flags);
if (draw->pt.frontend) {
draw->pt.frontend->flush(draw->pt.frontend, flags);
/* don't prepare if we only are flushing the backend */
if (flags & DRAW_FLUSH_STATE_CHANGE)
draw->pt.frontend = NULL;
}
if (flags & DRAW_FLUSH_PARAMETER_CHANGE) {
draw->pt.rebind_parameters = true;
}
}
bool
draw_pt_init(struct draw_context *draw)
{
draw->pt.test_fse = debug_get_option_draw_fse();
draw->pt.no_fse = debug_get_option_draw_no_fse();
draw->pt.front.vsplit = draw_pt_vsplit(draw);
if (!draw->pt.front.vsplit)
return false;
draw->pt.middle.fetch_shade_emit = draw_pt_middle_fse(draw);
if (!draw->pt.middle.fetch_shade_emit)
return false;
draw->pt.middle.general = draw_pt_fetch_pipeline_or_emit(draw);
if (!draw->pt.middle.general)
return false;
#if DRAW_LLVM_AVAILABLE
if (draw->llvm) {
draw->pt.middle.llvm = draw_pt_fetch_pipeline_or_emit_llvm(draw);
draw->pt.middle.mesh = draw_pt_mesh_pipeline_or_emit(draw);
}
#endif
return true;
}
void
draw_pt_destroy(struct draw_context *draw)
{
if (draw->pt.middle.mesh) {
draw->pt.middle.mesh->destroy(draw->pt.middle.mesh);
draw->pt.middle.mesh = NULL;
}
if (draw->pt.middle.llvm) {
draw->pt.middle.llvm->destroy(draw->pt.middle.llvm);
draw->pt.middle.llvm = NULL;
}
if (draw->pt.middle.general) {
draw->pt.middle.general->destroy(draw->pt.middle.general);
draw->pt.middle.general = NULL;
}
if (draw->pt.middle.fetch_shade_emit) {
draw->pt.middle.fetch_shade_emit->destroy(draw->pt.middle.fetch_shade_emit);
draw->pt.middle.fetch_shade_emit = NULL;
}
if (draw->pt.front.vsplit) {
draw->pt.front.vsplit->destroy(draw->pt.front.vsplit);
draw->pt.front.vsplit = NULL;
}
}
/**
* Debug- print the first 'count' vertices.
*/
static void
draw_print_arrays(struct draw_context *draw, enum mesa_prim prim,
int start, unsigned count, int index_bias)
{
debug_printf("Draw arrays(prim = %u, start = %u, count = %u)\n",
prim, start, count);
for (unsigned i = 0; i < count; i++) {
unsigned ii = 0;
if (draw->pt.user.eltSize) {
/* indexed arrays */
switch (draw->pt.user.eltSize) {
case 1:
{
const uint8_t *elem = (const uint8_t *) draw->pt.user.elts;
ii = elem[start + i];
}
break;
case 2:
{
const uint16_t *elem = (const uint16_t *) draw->pt.user.elts;
ii = elem[start + i];
}
break;
case 4:
{
const uint32_t *elem = (const uint32_t *) draw->pt.user.elts;
ii = elem[start + i];
}
break;
default:
assert(0);
return;
}
ii += index_bias;
debug_printf("Element[%u + %u] + %i -> Vertex %u:\n", start, i,
index_bias, ii);
}
else {
/* non-indexed arrays */
ii = start + i;
debug_printf("Vertex %u:\n", ii);
}
for (unsigned j = 0; j < draw->pt.nr_vertex_elements; j++) {
unsigned buf = draw->pt.vertex_element[j].vertex_buffer_index;
uint8_t *ptr = (uint8_t *) draw->pt.user.vbuffer[buf].map;
if (draw->pt.vertex_element[j].instance_divisor) {
ii = draw->instance_id / draw->pt.vertex_element[j].instance_divisor;
}
ptr += draw->pt.vertex_buffer[buf].buffer_offset;
ptr += draw->pt.vertex_element[j].src_stride * ii;
ptr += draw->pt.vertex_element[j].src_offset;
debug_printf(" Attr %u: ", j);
switch (draw->pt.vertex_element[j].src_format) {
case PIPE_FORMAT_R32_FLOAT:
{
float *v = (float *) ptr;
debug_printf("R %f @ %p\n", v[0], (void *) v);
}
break;
case PIPE_FORMAT_R32G32_FLOAT:
{
float *v = (float *) ptr;
debug_printf("RG %f %f @ %p\n", v[0], v[1], (void *) v);
}
break;
case PIPE_FORMAT_R32G32B32_FLOAT:
{
float *v = (float *) ptr;
debug_printf("RGB %f %f %f @ %p\n", v[0], v[1], v[2], (void *) v);
}
break;
case PIPE_FORMAT_R32G32B32A32_FLOAT:
{
float *v = (float *) ptr;
debug_printf("RGBA %f %f %f %f @ %p\n", v[0], v[1], v[2], v[3],
(void *) v);
}
break;
case PIPE_FORMAT_B8G8R8A8_UNORM:
{
uint8_t *u = (uint8_t *) ptr;
debug_printf("BGRA %d %d %d %d @ %p\n", u[0], u[1], u[2], u[3],
(void *) u);
}
break;
case PIPE_FORMAT_A8R8G8B8_UNORM:
{
uint8_t *u = (uint8_t *) ptr;
debug_printf("ARGB %d %d %d %d @ %p\n", u[0], u[1], u[2], u[3],
(void *) u);
}
break;
default:
debug_printf("other format %s (fix me)\n",
util_format_name(draw->pt.vertex_element[j].src_format));
}
}
}
}
/** Helper code for below */
static inline void
prim_restart_loop(struct draw_context *draw,
const struct pipe_draw_info *info,
const struct pipe_draw_start_count_bias *draw_info,
const void *elements)
{
const unsigned elt_max = draw->pt.user.eltMax;
struct pipe_draw_start_count_bias cur = *draw_info;
cur.count = 0;
for (unsigned j = 0; j < draw_info->count; j++) {
unsigned index = 0;
unsigned i = util_clamped_uadd(draw_info->start, j);
if (i < elt_max) {
switch (draw->pt.user.eltSize) {
case 1:
index = ((const uint8_t*)elements)[i];
break;
case 2:
index = ((const uint16_t*)elements)[i];
break;
case 4:
index = ((const uint32_t*)elements)[i];
break;
default:
assert(0 && "bad eltSize in draw_arrays()");
}
}
if (index == info->restart_index) {
if (cur.count > 0) {
/* draw elts up to prev pos */
draw_pt_arrays(draw, info->mode, info->index_bias_varies, &cur, 1);
}
/* begin new prim at next elt */
cur.start = i + 1;
cur.count = 0;
}
else {
cur.count++;
}
}
if (cur.count > 0) {
draw_pt_arrays(draw, info->mode, info->index_bias_varies, &cur, 1);
}
}
/**
* For drawing prims with primitive restart enabled.
* Scan for restart indexes and draw the runs of elements/vertices between
* the restarts.
*/
static void
draw_pt_arrays_restart(struct draw_context *draw,
const struct pipe_draw_info *info,
const struct pipe_draw_start_count_bias *draw_info,
unsigned num_draws)
{
assert(info->primitive_restart);
if (draw->pt.user.eltSize) {
/* indexed prims (draw_elements) */
for (unsigned i = 0; i < num_draws; i++)
prim_restart_loop(draw, info, &draw_info[i], draw->pt.user.elts);
} else {
/* Non-indexed prims (draw_arrays).
* Primitive restart should have been handled in gallium frontends.
*/
draw_pt_arrays(draw, info->mode, info->index_bias_varies, draw_info, num_draws);
}
}
/**
* Resolve true values within pipe_draw_info.
* If we're rendering from transform feedback/stream output
* buffers both the count and max_index need to be computed
* from the attached stream output target.
*/
static void
resolve_draw_info(const struct pipe_draw_info *raw_info,
const struct pipe_draw_indirect_info *indirect,
const struct pipe_draw_start_count_bias *raw_draw,
struct pipe_draw_info *info,
struct pipe_draw_start_count_bias *draw,
struct pipe_vertex_buffer *vertex_buffer,
struct pipe_vertex_element *vertex_element)
{
*info = *raw_info;
*draw = *raw_draw;
struct draw_so_target *target =
(struct draw_so_target *)indirect->count_from_stream_output;
assert(vertex_buffer != NULL);
draw->count = vertex_element->src_stride == 0 ? 0 :
target->internal_offset / vertex_element->src_stride;
/* Stream output draw can not be indexed */
assert(!info->index_size);
info->max_index = draw->count - 1;
}
/*
* Loop over all instances and execute draws for them.
*/
static void
draw_instances(struct draw_context *draw,
unsigned drawid_offset,
const struct pipe_draw_info *info,
const struct pipe_draw_start_count_bias *draws,
unsigned num_draws)
{
draw->start_instance = info->start_instance;
for (unsigned instance = 0; instance < info->instance_count; instance++) {
unsigned instance_idx = instance + info->start_instance;
draw->instance_id = instance;
/* check for overflow */
if (instance_idx < instance ||
instance_idx < draw->start_instance) {
/* if we overflown just set the instance id to the max */
draw->instance_id = 0xffffffff;
}
draw->pt.user.drawid = drawid_offset;
draw_new_instance(draw);
if (info->primitive_restart) {
draw_pt_arrays_restart(draw, info, draws, num_draws);
} else {
draw_pt_arrays(draw, info->mode, info->index_bias_varies,
draws, num_draws);
}
}
}
/**
* Draw vertex arrays.
* This is the main entrypoint into the drawing module. If drawing an indexed
* primitive, the draw_set_indexes() function should have already been called
* to specify the element/index buffer information.
*/
void
draw_vbo(struct draw_context *draw,
const struct pipe_draw_info *info,
unsigned drawid_offset,
const struct pipe_draw_indirect_info *indirect,
const struct pipe_draw_start_count_bias *draws,
unsigned num_draws,
uint8_t patch_vertices)
{
struct pipe_draw_info resolved_info;
struct pipe_draw_start_count_bias resolved_draw;
const struct pipe_draw_info *use_info;
const struct pipe_draw_start_count_bias *use_draws;
if (info->instance_count == 0)
return;
/* Make sure that denorms are treated like zeros. This is
* the behavior required by D3D10. OpenGL doesn't care.
*/
draw->fpstate = util_fpstate_get();
util_fpstate_set_denorms_to_zero(draw->fpstate);
if (indirect && indirect->count_from_stream_output) {
resolve_draw_info(info, indirect, &draws[0], &resolved_info,
&resolved_draw, &(draw->pt.vertex_buffer[0]),
&(draw->pt.vertex_element[0]));
use_info = &resolved_info;
use_draws = &resolved_draw;
num_draws = 1;
} else {
use_info = info;
use_draws = draws;
}
if (info->index_size) {
assert(draw->pt.user.elts);
draw->pt.user.min_index = use_info->index_bounds_valid ? use_info->min_index : 0;
draw->pt.user.max_index = use_info->index_bounds_valid ? use_info->max_index : ~0;
} else {
draw->pt.user.min_index = 0;
draw->pt.user.max_index = ~0;
}
draw->pt.user.eltSize = use_info->index_size ? draw->pt.user.eltSizeIB : 0;
draw->pt.user.drawid = drawid_offset;
draw->pt.user.increment_draw_id = use_info->increment_draw_id;
draw->pt.user.viewid = 0;
draw->pt.vertices_per_patch = patch_vertices;
if (0) {
for (unsigned i = 0; i < num_draws; i++)
debug_printf("draw_vbo(mode=%u start=%u count=%u):\n",
use_info->mode, use_draws[i].start, use_draws[i].count);
}
if (0)
tgsi_dump(draw->vs.vertex_shader->state.tokens, 0);
if (0) {
debug_printf("Elements:\n");
for (unsigned i = 0; i < draw->pt.nr_vertex_elements; i++) {
debug_printf(" %u: src_offset=%u src_stride=%u inst_div=%u vbuf=%u format=%s\n",
i,
draw->pt.vertex_element[i].src_offset,
draw->pt.vertex_element[i].src_stride,
draw->pt.vertex_element[i].instance_divisor,
draw->pt.vertex_element[i].vertex_buffer_index,
util_format_name(draw->pt.vertex_element[i].src_format));
}
debug_printf("Buffers:\n");
for (unsigned i = 0; i < draw->pt.nr_vertex_buffers; i++) {
debug_printf(" %u: offset=%u size=%d ptr=%p\n",
i,
draw->pt.vertex_buffer[i].buffer_offset,
(int) draw->pt.user.vbuffer[i].size,
draw->pt.user.vbuffer[i].map);
}
}
if (0) {
for (unsigned i = 0; i < num_draws; i++)
draw_print_arrays(draw, use_info->mode, use_draws[i].start,
MIN2(use_draws[i].count, 20),
use_info->index_bias_varies
? use_draws[i].index_bias
: use_draws[0].index_bias);
}
unsigned index_limit = util_draw_max_index(draw->pt.vertex_buffer,
draw->pt.vertex_element,
draw->pt.nr_vertex_elements,
use_info);
#if DRAW_LLVM_AVAILABLE
if (!draw->llvm)
#endif
{
if (index_limit == 0) {
/* one of the buffers is too small to do any valid drawing */
debug_warning("draw: VBO too small to draw anything\n");
util_fpstate_set(draw->fpstate);
return;
}
}
/* If we're collecting stats then make sure we start from scratch */
if (draw->collect_statistics) {
memset(&draw->statistics, 0, sizeof(draw->statistics));
}
draw->pt.max_index = index_limit - 1;
/*
* TODO: We could use draw->pt.max_index to further narrow
* the min_index/max_index hints given by gallium frontends.
*/
if (draw->viewmask) {
u_foreach_bit(i, draw->viewmask) {
draw->pt.user.viewid = i;
draw_instances(draw, drawid_offset, use_info, use_draws, num_draws);
}
} else {
draw_instances(draw, drawid_offset, use_info, use_draws, num_draws);
}
/* If requested emit the pipeline statistics for this run */
if (draw->collect_statistics) {
draw->render->pipeline_statistics(draw->render, &draw->statistics);
}
util_fpstate_set(draw->fpstate);
}
/* to be called after a mesh shader is run */
void
draw_mesh(struct draw_context *draw,
struct draw_vertex_info *vert_info,
struct draw_prim_info *prim_info)
{
struct draw_pt_middle_end *middle = draw->pt.middle.mesh;
draw->pt.user.eltSize = 0;
draw->pt.user.viewid = 0;
draw->pt.user.drawid = 0;
draw->pt.user.increment_draw_id = false;
draw->pt.vertices_per_patch = 0;
middle->prepare(middle, 0, 0, NULL);
draw_mesh_middle_end_run(middle, vert_info, prim_info);
}
+276
View File
@@ -0,0 +1,276 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Authors:
* Keith Whitwell <[email protected]>
*/
#ifndef DRAW_PT_H
#define DRAW_PT_H
#include "util/compiler.h"
struct draw_pt_middle_end;
struct draw_context;
struct draw_prim_info;
struct draw_vertex_info;
#define PT_SHADE 0x1
#define PT_CLIPTEST 0x2
#define PT_PIPELINE 0x4
#define PT_MAX_MIDDLE 0x8
/* The "front end" - prepare sets of fetch, draw elements for the
* middle end.
*
* The fetch elements are indices to the vertices. The draw elements are
* indices to the fetched vertices. When both arrays of elements are both
* linear, middle->run_linear is called; When only the fetch elements are
* linear, middle->run_linear_elts is called; Otherwise, middle->run is
* called.
*
* When the number of the draw elements exceeds max_vertex of the middle end,
* the draw elements (as well as the fetch elements) are splitted and the
* middle end is called multiple times.
*
* Currenly there is:
* - vsplit - catchall implementation, splits big prims
*/
struct draw_pt_front_end {
void (*prepare)(struct draw_pt_front_end *,
enum mesa_prim prim,
struct draw_pt_middle_end *,
unsigned opt);
void (*run)(struct draw_pt_front_end *,
unsigned start,
unsigned count);
void (*flush)(struct draw_pt_front_end *, unsigned flags);
void (*destroy)(struct draw_pt_front_end *);
};
/* The "middle end" - prepares actual hardware vertices for the
* hardware backend.
*
* prim_flags is as defined by pipe_draw_info::flags.
*
* Currently two versions of this:
* - fetch, vertex shade, cliptest, prim-pipeline
* - fetch, emit (ie passthrough)
*/
struct draw_pt_middle_end {
void (*prepare)(struct draw_pt_middle_end *,
enum mesa_prim prim,
unsigned opt,
unsigned *max_vertices);
/**
* Bind/update parameter state such as constants, viewport dims
* and clip planes. Basically, stuff which isn't "baked" into the
* shader or pipeline state.
*/
void (*bind_parameters)(struct draw_pt_middle_end *);
void (*run)(struct draw_pt_middle_end *,
unsigned start,
const unsigned *fetch_elts,
unsigned fetch_count,
const uint16_t *draw_elts,
unsigned draw_count,
unsigned prim_flags);
void (*run_linear)(struct draw_pt_middle_end *,
unsigned start,
unsigned count,
unsigned prim_flags);
/* Transform all vertices in a linear range and then draw them with
* the supplied element list. May fail and return FALSE.
*/
bool (*run_linear_elts)(struct draw_pt_middle_end *,
unsigned fetch_start,
unsigned fetch_count,
const uint16_t *draw_elts,
unsigned draw_start,
unsigned draw_count);
int (*get_max_vertex_count)(struct draw_pt_middle_end *);
void (*finish)(struct draw_pt_middle_end *);
void (*destroy)(struct draw_pt_middle_end *);
};
/* The "back end" - supplied by the driver, defined in draw_vbuf.h.
*/
struct vbuf_render;
struct vertex_header;
/* Frontends:
*
* Currently only the general-purpose vsplit implementation.
*/
struct draw_pt_front_end *draw_pt_vsplit(struct draw_context *draw);
/* Middle-ends:
*
* Currently one general-purpose case which can do all possibilities,
* at the slight expense of creating a vertex_header in some cases
* unecessarily.
*/
struct draw_pt_middle_end *draw_pt_middle_fse(struct draw_context *draw);
struct draw_pt_middle_end *draw_pt_fetch_pipeline_or_emit(struct draw_context *draw);
struct draw_pt_middle_end *draw_pt_fetch_pipeline_or_emit_llvm(struct draw_context *draw);
struct draw_pt_middle_end *draw_pt_mesh_pipeline_or_emit(struct draw_context *draw);
/*******************************************************************************
* HW vertex emit:
*/
struct pt_emit;
void
draw_pt_emit_prepare(struct pt_emit *emit,
enum mesa_prim prim,
unsigned *max_vertices);
void
draw_pt_emit(struct pt_emit *emit,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info);
void
draw_pt_emit_linear(struct pt_emit *emit,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info);
void
draw_pt_emit_destroy(struct pt_emit *emit);
struct pt_emit *
draw_pt_emit_create(struct draw_context *draw);
/*******************************************************************************
* HW stream output emit:
*/
struct pt_so_emit;
void
draw_pt_so_emit_prepare(struct pt_so_emit *emit, bool use_pre_clip_pos);
void
draw_pt_so_emit(struct pt_so_emit *emit,
int num_vertex_streams,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info);
void
draw_pt_so_emit_destroy(struct pt_so_emit *emit);
struct pt_so_emit *
draw_pt_so_emit_create(struct draw_context *draw);
/*******************************************************************************
* API vertex fetch:
*/
struct pt_fetch;
void
draw_pt_fetch_prepare(struct pt_fetch *fetch,
unsigned vertex_input_count,
unsigned vertex_size,
unsigned instance_id_index);
void
draw_pt_fetch_run(struct pt_fetch *fetch,
const unsigned *elts,
unsigned count,
char *verts);
void
draw_pt_fetch_run_linear(struct pt_fetch *fetch,
unsigned start,
unsigned count,
char *verts);
void
draw_pt_fetch_destroy(struct pt_fetch *fetch);
struct pt_fetch *
draw_pt_fetch_create(struct draw_context *draw);
/*******************************************************************************
* Post-VS: cliptest, rhw, viewport
*/
struct pt_post_vs;
bool
draw_pt_post_vs_run(struct pt_post_vs *pvs,
struct draw_vertex_info *info,
const struct draw_prim_info *prim_info);
void
draw_pt_post_vs_prepare(struct pt_post_vs *pvs,
bool clip_xy,
bool clip_z,
bool clip_user,
bool guard_band,
bool bypass_viewport,
bool clip_halfz,
bool need_edgeflags);
struct pt_post_vs *
draw_pt_post_vs_create(struct draw_context *draw);
void
draw_pt_post_vs_destroy(struct pt_post_vs *pvs);
/*******************************************************************************
* Utils:
*/
void
draw_pt_split_prim(enum mesa_prim prim, unsigned *first, unsigned *incr);
unsigned
draw_pt_trim_count(unsigned count, unsigned first, unsigned incr);
void
draw_mesh_middle_end_run(struct draw_pt_middle_end *middle,
struct draw_vertex_info *vert_info,
struct draw_prim_info *prim_info);
#endif
@@ -0,0 +1,8 @@
#define LOCAL_VARS \
char *verts = (char *) vertices; \
const bool quads_flatshade_last = \
draw->quads_always_flatshade_last; \
const bool last_vertex_last = \
!draw->rasterizer->flatshade_first;
#include "draw_decompose_tmp.h"
+323
View File
@@ -0,0 +1,323 @@
/**************************************************************************
*
* Copyright 2008 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.
*
**************************************************************************/
#include "util/u_memory.h"
#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "draw/draw_vbuf.h"
#include "draw/draw_vertex.h"
#include "draw/draw_pt.h"
#include "translate/translate.h"
#include "translate/translate_cache.h"
#include "util/u_prim.h"
struct pt_emit {
struct draw_context *draw;
struct translate *translate;
struct translate_cache *cache;
unsigned prim;
const struct vertex_info *vinfo;
float zero4[4];
};
void
draw_pt_emit_prepare(struct pt_emit *emit,
enum mesa_prim prim,
unsigned *max_vertices)
{
struct draw_context *draw = emit->draw;
const struct vertex_info *vinfo;
struct translate_key hw_key;
/* XXX: need to flush to get prim_vbuf.c to release its allocation??
*/
draw_do_flush(draw, DRAW_FLUSH_BACKEND);
/* XXX: may need to defensively reset this later on as clipping can
* clobber this state in the render backend.
*/
emit->prim = prim;
draw->render->set_primitive(draw->render, emit->prim);
if (draw->render->set_view_index)
draw->render->set_view_index(draw->render, draw->pt.user.viewid);
/* Must do this after set_primitive() above:
*/
emit->vinfo = vinfo = draw->render->get_vertex_info(draw->render);
/* Translate from pipeline vertices to hw vertices.
*/
unsigned dst_offset = 0;
for (unsigned i = 0; i < vinfo->num_attribs; i++) {
unsigned emit_sz = 0;
unsigned src_buffer = 0;
unsigned output_format;
unsigned src_offset = vinfo->attrib[i].src_index * 4 * sizeof(float);
output_format = draw_translate_vinfo_format(vinfo->attrib[i].emit);
emit_sz = draw_translate_vinfo_size(vinfo->attrib[i].emit);
/* doesn't handle EMIT_OMIT */
assert(emit_sz != 0);
if (vinfo->attrib[i].emit == EMIT_1F_PSIZE) {
src_buffer = 1;
src_offset = 0;
} else if (vinfo->attrib[i].src_index == DRAW_ATTR_NONEXIST) {
/* elements which don't exist will get assigned zeros */
src_buffer = 2;
src_offset = 0;
}
hw_key.element[i].type = TRANSLATE_ELEMENT_NORMAL;
hw_key.element[i].input_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
hw_key.element[i].input_buffer = src_buffer;
hw_key.element[i].input_offset = src_offset;
hw_key.element[i].instance_divisor = 0;
hw_key.element[i].output_format = output_format;
hw_key.element[i].output_offset = dst_offset;
dst_offset += emit_sz;
}
hw_key.nr_elements = vinfo->num_attribs;
hw_key.output_stride = vinfo->size * 4;
if (!emit->translate ||
translate_key_compare(&emit->translate->key, &hw_key) != 0) {
translate_key_sanitize(&hw_key);
emit->translate = translate_cache_find(emit->cache, &hw_key);
emit->translate->set_buffer(emit->translate, 2, &emit->zero4[0], 0, ~0);
}
if (!vinfo->size)
*max_vertices = 0;
else
*max_vertices = (draw->render->max_vertex_buffer_bytes /
(vinfo->size * 4));
}
void
draw_pt_emit(struct pt_emit *emit,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info)
{
const float (*vertex_data)[4] = (const float (*)[4])vert_info->verts->data;
unsigned vertex_count = vert_info->count;
unsigned stride = vert_info->stride;
const uint16_t *elts = prim_info->elts;
struct draw_context *draw = emit->draw;
struct translate *translate = emit->translate;
struct vbuf_render *render = draw->render;
unsigned start, i;
void *hw_verts;
/* XXX: need to flush to get prim_vbuf.c to release its allocation??
*/
draw_do_flush(draw, DRAW_FLUSH_BACKEND);
if (vertex_count == 0)
return;
/* XXX: and work out some way to coordinate the render primitive
* between vbuf.c and here...
*/
render->set_primitive(draw->render, prim_info->prim);
if (draw->render->set_view_index)
draw->render->set_view_index(draw->render, draw->pt.user.viewid);
assert(vertex_count <= 65535);
render->allocate_vertices(render,
(uint16_t)translate->key.output_stride,
(uint16_t)vertex_count);
hw_verts = render->map_vertices(render);
if (!hw_verts) {
debug_warn_once("map of vertex buffer failed (out of memory?)");
return;
}
translate->set_buffer(translate,
0,
vertex_data,
stride,
~0);
translate->set_buffer(translate,
1,
&draw->rasterizer->point_size,
0,
~0);
/* fetch/translate vertex attribs to fill hw_verts[] */
translate->run(translate,
0,
vertex_count,
0,
0,
hw_verts);
if (0) {
for (unsigned i = 0; i < vertex_count; i++) {
debug_printf("\n\n%s vertex %d:\n", __func__, i);
draw_dump_emitted_vertex(emit->vinfo,
(const uint8_t *)hw_verts +
translate->key.output_stride * i);
}
}
render->unmap_vertices(render, 0, vertex_count - 1);
for (start = i = 0;
i < prim_info->primitive_count;
start += prim_info->primitive_lengths[i], i++) {
render->draw_elements(render,
elts + start,
prim_info->primitive_lengths[i]);
}
render->release_vertices(render);
}
void
draw_pt_emit_linear(struct pt_emit *emit,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info)
{
const float (*vertex_data)[4] = (const float (*)[4])vert_info->verts->data;
unsigned stride = vert_info->stride;
unsigned count = vert_info->count;
struct draw_context *draw = emit->draw;
struct translate *translate = emit->translate;
struct vbuf_render *render = draw->render;
void *hw_verts;
unsigned start, i;
#if 0
debug_printf("Linear emit\n");
#endif
/* XXX: need to flush to get prim_vbuf.c to release its allocation??
*/
draw_do_flush(draw, DRAW_FLUSH_BACKEND);
/* XXX: and work out some way to coordinate the render primitive
* between vbuf.c and here...
*/
render->set_primitive(draw->render, prim_info->prim);
if (draw->render->set_view_index)
draw->render->set_view_index(draw->render, draw->pt.user.viewid);
assert(count <= 65535);
if (!render->allocate_vertices(render,
(uint16_t)translate->key.output_stride,
(uint16_t)count))
goto fail;
hw_verts = render->map_vertices(render);
if (!hw_verts)
goto fail;
translate->set_buffer(translate, 0,
vertex_data, stride, count - 1);
translate->set_buffer(translate, 1,
&draw->rasterizer->point_size,
0, ~0);
translate->run(translate,
0,
count,
0,
0,
hw_verts);
if (0) {
for (unsigned i = 0; i < count; i++) {
debug_printf("\n\n%s vertex %d:\n", __func__, i);
draw_dump_emitted_vertex(emit->vinfo,
(const uint8_t *)hw_verts +
translate->key.output_stride * i);
}
}
render->unmap_vertices(render, 0, count - 1);
for (start = i = 0;
i < prim_info->primitive_count;
start += prim_info->primitive_lengths[i], i++) {
render->draw_arrays(render,
start,
prim_info->primitive_lengths[i]);
}
render->release_vertices(render);
return;
fail:
debug_warn_once("allocate or map of vertex buffer failed (out of memory?)");
return;
}
struct pt_emit *
draw_pt_emit_create(struct draw_context *draw)
{
struct pt_emit *emit = CALLOC_STRUCT(pt_emit);
if (!emit)
return NULL;
emit->draw = draw;
emit->cache = translate_cache_create();
if (!emit->cache) {
FREE(emit);
return NULL;
}
emit->zero4[0] = emit->zero4[1] = emit->zero4[2] = emit->zero4[3] = 0.0f;
return emit;
}
void
draw_pt_emit_destroy(struct pt_emit *emit)
{
if (emit->cache)
translate_cache_destroy(emit->cache);
FREE(emit);
}
+219
View File
@@ -0,0 +1,219 @@
/**************************************************************************
*
* Copyright 2008 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.
*
**************************************************************************/
#include "util/u_memory.h"
#include "util/u_math.h"
#include "util/format/u_format.h"
#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "draw/draw_pt.h"
#include "translate/translate.h"
#include "translate/translate_cache.h"
struct pt_fetch {
struct draw_context *draw;
struct translate *translate;
unsigned vertex_size;
struct translate_cache *cache;
};
/**
* Perform the fetch from API vertex elements & vertex buffers, to a
* contiguous set of float[4] attributes as required for the
* vertex_shader->run_linear() method.
*/
void
draw_pt_fetch_prepare(struct pt_fetch *fetch,
unsigned vs_input_count,
unsigned vertex_size,
unsigned instance_id_index)
{
struct draw_context *draw = fetch->draw;
unsigned nr_inputs;
unsigned nr = 0, ei = 0;
unsigned dst_offset = 0;
unsigned num_extra_inputs = 0;
struct translate_key key;
fetch->vertex_size = vertex_size;
/* Leave the clipmask/edgeflags/pad/vertex_id,
* clip[] and whatever else in the header untouched.
*/
dst_offset = offsetof(struct vertex_header, data);
if (instance_id_index != ~0) {
num_extra_inputs++;
}
assert(draw->pt.nr_vertex_elements + num_extra_inputs >= vs_input_count);
nr_inputs = MIN2(vs_input_count, draw->pt.nr_vertex_elements + num_extra_inputs);
for (unsigned i = 0; i < nr_inputs; i++) {
if (i == instance_id_index) {
key.element[nr].type = TRANSLATE_ELEMENT_INSTANCE_ID;
key.element[nr].input_format = PIPE_FORMAT_R32_USCALED;
key.element[nr].output_format = PIPE_FORMAT_R32_USCALED;
key.element[nr].output_offset = dst_offset;
dst_offset += sizeof(uint32_t);
} else if (util_format_is_pure_sint(draw->pt.vertex_element[i].src_format)) {
key.element[nr].type = TRANSLATE_ELEMENT_NORMAL;
key.element[nr].input_format = draw->pt.vertex_element[ei].src_format;
key.element[nr].input_buffer = draw->pt.vertex_element[ei].vertex_buffer_index;
key.element[nr].input_offset = draw->pt.vertex_element[ei].src_offset;
key.element[nr].instance_divisor = draw->pt.vertex_element[ei].instance_divisor;
key.element[nr].output_format = PIPE_FORMAT_R32G32B32A32_SINT;
key.element[nr].output_offset = dst_offset;
ei++;
dst_offset += 4 * sizeof(int32_t);
} else if (util_format_is_pure_uint(draw->pt.vertex_element[i].src_format)) {
key.element[nr].type = TRANSLATE_ELEMENT_NORMAL;
key.element[nr].input_format = draw->pt.vertex_element[ei].src_format;
key.element[nr].input_buffer = draw->pt.vertex_element[ei].vertex_buffer_index;
key.element[nr].input_offset = draw->pt.vertex_element[ei].src_offset;
key.element[nr].instance_divisor = draw->pt.vertex_element[ei].instance_divisor;
key.element[nr].output_format = PIPE_FORMAT_R32G32B32A32_UINT;
key.element[nr].output_offset = dst_offset;
ei++;
dst_offset += 4 * sizeof(uint32_t);
} else {
key.element[nr].type = TRANSLATE_ELEMENT_NORMAL;
key.element[nr].input_format = draw->pt.vertex_element[ei].src_format;
key.element[nr].input_buffer = draw->pt.vertex_element[ei].vertex_buffer_index;
key.element[nr].input_offset = draw->pt.vertex_element[ei].src_offset;
key.element[nr].instance_divisor = draw->pt.vertex_element[ei].instance_divisor;
key.element[nr].output_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
key.element[nr].output_offset = dst_offset;
ei++;
dst_offset += 4 * sizeof(float);
}
nr++;
}
assert(dst_offset <= vertex_size);
key.nr_elements = nr;
key.output_stride = vertex_size;
if (!fetch->translate ||
translate_key_compare(&fetch->translate->key, &key) != 0) {
translate_key_sanitize(&key);
fetch->translate = translate_cache_find(fetch->cache, &key);
}
}
void
draw_pt_fetch_run(struct pt_fetch *fetch,
const unsigned *elts,
unsigned count,
char *verts)
{
struct draw_context *draw = fetch->draw;
struct translate *translate = fetch->translate;
for (unsigned i = 0; i < draw->pt.nr_vertex_buffers; i++) {
translate->set_buffer(translate,
i,
((char *)draw->pt.user.vbuffer[i].map +
draw->pt.vertex_buffer[i].buffer_offset),
draw->pt.vertex_strides[i],
draw->pt.max_index);
}
translate->run_elts(translate,
elts,
count,
draw->start_instance,
draw->instance_id,
verts);
}
void
draw_pt_fetch_run_linear(struct pt_fetch *fetch,
unsigned start,
unsigned count,
char *verts)
{
struct draw_context *draw = fetch->draw;
struct translate *translate = fetch->translate;
for (unsigned i = 0; i < draw->pt.nr_vertex_buffers; i++) {
translate->set_buffer(translate,
i,
((char *)draw->pt.user.vbuffer[i].map +
draw->pt.vertex_buffer[i].buffer_offset),
draw->pt.vertex_strides[i],
draw->pt.max_index);
}
translate->run(translate,
start,
count,
draw->start_instance,
draw->instance_id,
verts);
}
struct pt_fetch *
draw_pt_fetch_create(struct draw_context *draw)
{
struct pt_fetch *fetch = CALLOC_STRUCT(pt_fetch);
if (!fetch)
return NULL;
fetch->draw = draw;
fetch->cache = translate_cache_create();
if (!fetch->cache) {
FREE(fetch);
return NULL;
}
return fetch;
}
void
draw_pt_fetch_destroy(struct pt_fetch *fetch)
{
if (fetch->cache)
translate_cache_destroy(fetch->cache);
FREE(fetch);
}
@@ -0,0 +1,366 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Authors:
* Keith Whitwell <[email protected]>
*/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "draw/draw_vbuf.h"
#include "draw/draw_vertex.h"
#include "draw/draw_pt.h"
#include "draw/draw_vs.h"
struct fetch_shade_emit;
/* Prototype fetch, shade, emit-hw-verts all in one go.
*/
struct fetch_shade_emit {
struct draw_pt_middle_end base;
struct draw_context *draw;
/* Temporaries:
*/
const float *constants;
unsigned pitch[PIPE_MAX_ATTRIBS];
const uint8_t *src[PIPE_MAX_ATTRIBS];
unsigned prim;
struct draw_vs_variant_key key;
struct draw_vs_variant *active;
const struct vertex_info *vinfo;
};
static void
fse_prepare(struct draw_pt_middle_end *middle,
enum mesa_prim prim,
unsigned opt,
unsigned *max_vertices)
{
struct fetch_shade_emit *fse = (struct fetch_shade_emit *)middle;
struct draw_context *draw = fse->draw;
unsigned num_vs_inputs = draw->vs.vertex_shader->info.num_inputs;
const struct vertex_info *vinfo;
unsigned nr_vbs = 0;
/* Can't support geometry shader on this path.
*/
assert(!draw->gs.geometry_shader);
draw->render->set_primitive(draw->render, prim);
/* Must do this after set_primitive() above:
*/
fse->vinfo = vinfo = draw->render->get_vertex_info(draw->render);
fse->key.output_stride = vinfo->size * 4;
fse->key.nr_outputs = vinfo->num_attribs;
fse->key.nr_inputs = num_vs_inputs;
fse->key.nr_elements = MAX2(fse->key.nr_outputs, /* outputs - translate to hw format */
fse->key.nr_inputs); /* inputs - fetch from api format */
fse->key.viewport = !draw->bypass_viewport;
fse->key.clip = draw->clip_xy || draw->clip_z || draw->clip_user;
fse->key.const_vbuffers = 0;
memset(fse->key.element, 0,
fse->key.nr_elements * sizeof(fse->key.element[0]));
for (unsigned i = 0; i < num_vs_inputs; i++) {
const struct pipe_vertex_element *src = &draw->pt.vertex_element[i];
fse->key.element[i].in.format = src->src_format;
/* Consider ignoring these, ie make generated programs
* independent of this state:
*/
fse->key.element[i].in.buffer = src->vertex_buffer_index;
fse->key.element[i].in.offset = src->src_offset;
if (src->src_stride == 0)
fse->key.const_vbuffers |= (1<<src->vertex_buffer_index);
nr_vbs = MAX2(nr_vbs, src->vertex_buffer_index + 1);
}
if (0) debug_printf("%s: lookup const_vbuffers: %x\n",
__func__, fse->key.const_vbuffers);
{
unsigned dst_offset = 0;
for (unsigned i = 0; i < vinfo->num_attribs; i++) {
unsigned emit_sz = draw_translate_vinfo_size(vinfo->attrib[i].emit);
/* doesn't handle EMIT_OMIT */
assert(emit_sz != 0);
/* The elements in the key correspond to vertex shader output
* numbers, not to positions in the hw vertex description --
* that's handled by the output_offset field.
*/
fse->key.element[i].out.format = vinfo->attrib[i].emit;
fse->key.element[i].out.vs_output = vinfo->attrib[i].src_index;
fse->key.element[i].out.offset = dst_offset;
dst_offset += emit_sz;
assert(fse->key.output_stride >= dst_offset);
}
}
fse->active = draw_vs_lookup_variant(draw->vs.vertex_shader, &fse->key);
if (!fse->active) {
assert(0);
return ;
}
if (0) debug_printf("%s: found const_vbuffers: %x\n", __func__,
fse->active->key.const_vbuffers);
/* Now set buffer pointers:
*/
for (unsigned i = 0; i < draw->pt.nr_vertex_buffers; i++) {
fse->active->set_buffer(fse->active,
i,
((const uint8_t *) draw->pt.user.vbuffer[i].map +
draw->pt.vertex_buffer[i].buffer_offset),
draw->pt.vertex_strides[i],
draw->pt.max_index);
}
*max_vertices = (draw->render->max_vertex_buffer_bytes /
(vinfo->size * 4));
/* Probably need to do this somewhere (or fix exec shader not to
* need it):
*/
if (1) {
struct draw_vertex_shader *vs = draw->vs.vertex_shader;
vs->prepare(vs, draw);
}
}
static void
fse_bind_parameters(struct draw_pt_middle_end *middle)
{
/* No-op? */
}
static void
fse_run_linear(struct draw_pt_middle_end *middle,
unsigned start,
unsigned count,
unsigned prim_flags)
{
struct fetch_shade_emit *fse = (struct fetch_shade_emit *)middle;
struct draw_context *draw = fse->draw;
char *hw_verts;
/* XXX: need to flush to get prim_vbuf.c to release its allocation??
*/
draw_do_flush(draw, DRAW_FLUSH_BACKEND);
if (!draw->render->allocate_vertices(draw->render,
(uint16_t) fse->key.output_stride,
(uint16_t) count))
goto fail;
hw_verts = draw->render->map_vertices(draw->render);
if (!hw_verts)
goto fail;
/* Single routine to fetch vertices, run shader and emit HW verts.
* Clipping is done elsewhere -- either by the API or on hardware,
* or for some other reason not required...
*/
fse->active->run_linear(fse->active, start, count, hw_verts);
if (0) {
for (unsigned i = 0; i < count; i++) {
debug_printf("\n\n%s vertex %d: (stride %d, offset %d)\n",
__func__, i,
fse->key.output_stride,
fse->key.output_stride * i);
draw_dump_emitted_vertex(fse->vinfo,
(const uint8_t *) hw_verts
+ fse->key.output_stride * i);
}
}
draw->render->unmap_vertices(draw->render, 0, (uint16_t) (count - 1));
/* Draw arrays path to avoid re-emitting index list again and
* again.
*/
draw->render->draw_arrays(draw->render, 0, count);
draw->render->release_vertices(draw->render);
return;
fail:
debug_warn_once("allocate or map of vertex buffer failed (out of memory?)");
return;
}
static void
fse_run(struct draw_pt_middle_end *middle,
unsigned start,
const unsigned *fetch_elts,
unsigned fetch_count,
const uint16_t *draw_elts,
unsigned draw_count,
unsigned prim_flags)
{
struct fetch_shade_emit *fse = (struct fetch_shade_emit *)middle;
struct draw_context *draw = fse->draw;
void *hw_verts;
/* XXX: need to flush to get prim_vbuf.c to release its allocation??
*/
draw_do_flush(draw, DRAW_FLUSH_BACKEND);
if (!draw->render->allocate_vertices(draw->render,
(uint16_t) fse->key.output_stride,
(uint16_t) fetch_count))
goto fail;
hw_verts = draw->render->map_vertices(draw->render);
if (!hw_verts)
goto fail;
/* Single routine to fetch vertices, run shader and emit HW verts.
*/
fse->active->run_elts(fse->active, fetch_elts, fetch_count, hw_verts);
if (0) {
for (unsigned i = 0; i < fetch_count; i++) {
debug_printf("\n\n%s vertex %d:\n", __func__, i);
draw_dump_emitted_vertex(fse->vinfo,
(const uint8_t *)hw_verts +
fse->key.output_stride * i);
}
}
draw->render->unmap_vertices(draw->render, 0, (uint16_t)(fetch_count - 1));
draw->render->draw_elements(draw->render, draw_elts, draw_count);
draw->render->release_vertices(draw->render);
return;
fail:
debug_warn_once("allocate or map of vertex buffer failed (out of memory?)");
return;
}
static bool
fse_run_linear_elts(struct draw_pt_middle_end *middle,
unsigned start,
unsigned count,
const uint16_t *draw_elts,
unsigned draw_start,
unsigned draw_count)
{
struct fetch_shade_emit *fse = (struct fetch_shade_emit *)middle;
struct draw_context *draw = fse->draw;
char *hw_verts;
/* XXX: need to flush to get prim_vbuf.c to release its allocation??
*/
draw_do_flush(draw, DRAW_FLUSH_BACKEND);
if (!draw->render->allocate_vertices(draw->render,
(uint16_t) fse->key.output_stride,
(uint16_t) count))
return false;
hw_verts = draw->render->map_vertices(draw->render);
if (!hw_verts)
return false;
/* Single routine to fetch vertices, run shader and emit HW verts.
* Clipping is done elsewhere -- either by the API or on hardware,
* or for some other reason not required...
*/
fse->active->run_linear(fse->active, start, count, hw_verts);
draw->render->draw_elements(draw->render, draw_elts, draw_count);
draw->render->unmap_vertices(draw->render, 0, (uint16_t)(count - 1));
draw->render->release_vertices(draw->render);
return true;
}
static void
fse_finish(struct draw_pt_middle_end *middle)
{
}
static void
fse_destroy(struct draw_pt_middle_end *middle)
{
FREE(middle);
}
struct draw_pt_middle_end *
draw_pt_middle_fse(struct draw_context *draw)
{
struct fetch_shade_emit *fse = CALLOC_STRUCT(fetch_shade_emit);
if (!fse)
return NULL;
fse->base.prepare = fse_prepare;
fse->base.bind_parameters = fse_bind_parameters;
fse->base.run = fse_run;
fse->base.run_linear = fse_run_linear;
fse->base.run_linear_elts = fse_run_linear_elts;
fse->base.finish = fse_finish;
fse->base.destroy = fse_destroy;
fse->draw = draw;
return &fse->base;
}
@@ -0,0 +1,552 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/u_prim.h"
#include "draw/draw_context.h"
#include "draw/draw_vbuf.h"
#include "draw/draw_vertex.h"
#include "draw/draw_prim_assembler.h"
#include "draw/draw_pt.h"
#include "draw/draw_vs.h"
#include "draw/draw_gs.h"
struct fetch_pipeline_middle_end {
struct draw_pt_middle_end base;
struct draw_context *draw;
struct pt_emit *emit;
struct pt_so_emit *so_emit;
struct pt_fetch *fetch;
struct pt_post_vs *post_vs;
unsigned vertex_data_offset;
unsigned vertex_size;
unsigned input_prim;
unsigned opt;
};
/** cast wrapper */
static inline struct fetch_pipeline_middle_end *
fetch_pipeline_middle_end(struct draw_pt_middle_end *middle)
{
return (struct fetch_pipeline_middle_end *) middle;
}
/**
* Prepare/validate middle part of the vertex pipeline.
* NOTE: if you change this function, also look at the LLVM
* function llvm_middle_end_prepare() for similar changes.
*/
static void
fetch_pipeline_prepare(struct draw_pt_middle_end *middle,
enum mesa_prim prim,
unsigned opt,
unsigned *max_vertices)
{
struct fetch_pipeline_middle_end *fpme = fetch_pipeline_middle_end(middle);
struct draw_context *draw = fpme->draw;
struct draw_vertex_shader *vs = draw->vs.vertex_shader;
struct draw_geometry_shader *gs = draw->gs.geometry_shader;
unsigned instance_id_index = ~0;
const unsigned gs_out_prim = (gs ? gs->output_primitive :
u_assembled_prim(prim));
unsigned nr_vs_outputs = draw_total_vs_outputs(draw);
unsigned nr = MAX2(vs->info.num_inputs, nr_vs_outputs);
unsigned point_line_clip = draw->rasterizer->fill_front == PIPE_POLYGON_MODE_POINT ||
draw->rasterizer->fill_front == PIPE_POLYGON_MODE_LINE ||
gs_out_prim == MESA_PRIM_POINTS ||
gs_out_prim == MESA_PRIM_LINE_STRIP;
if (gs) {
nr = MAX2(nr, gs->info.num_outputs + 1);
}
/* Scan for instanceID system value.
*/
for (unsigned i = 0; i < vs->info.num_inputs; i++) {
if (vs->info.input_semantic_name[i] == TGSI_SEMANTIC_INSTANCEID) {
instance_id_index = i;
break;
}
}
fpme->input_prim = prim;
fpme->opt = opt;
/* Always leave room for the vertex header whether we need it or
* not. It's hard to get rid of it in particular because of the
* viewport code in draw_pt_post_vs.c.
*/
fpme->vertex_size = sizeof(struct vertex_header) + nr * 4 * sizeof(float);
draw_pt_fetch_prepare(fpme->fetch,
vs->info.num_inputs,
fpme->vertex_size,
instance_id_index);
draw_pt_post_vs_prepare(fpme->post_vs,
draw->clip_xy,
draw->clip_z,
draw->clip_user,
point_line_clip ? draw->guard_band_points_lines_xy :
draw->guard_band_xy,
draw->bypass_viewport,
draw->rasterizer->clip_halfz,
(draw->vs.edgeflag_output ? true : false));
draw_pt_so_emit_prepare(fpme->so_emit, false);
if (!(opt & PT_PIPELINE)) {
draw_pt_emit_prepare(fpme->emit, gs_out_prim, max_vertices);
*max_vertices = MAX2(*max_vertices, 4096);
}
else {
/* limit max fetches by limiting max_vertices */
*max_vertices = 4096;
}
/* No need to prepare the shader.
*/
vs->prepare(vs, draw);
/* Make sure that the vertex size didn't change at any point above */
assert(nr_vs_outputs == draw_total_vs_outputs(draw));
}
static void
fetch_pipeline_bind_parameters(struct draw_pt_middle_end *middle)
{
/* No-op since the vertex shader executor and drawing pipeline
* just grab the constants, viewport, etc. from the draw context state.
*/
}
static void
fetch(struct pt_fetch *fetch,
const struct draw_fetch_info *fetch_info,
char *output)
{
if (fetch_info->linear) {
draw_pt_fetch_run_linear(fetch, fetch_info->start,
fetch_info->count, output);
}
else {
draw_pt_fetch_run(fetch, fetch_info->elts, fetch_info->count, output);
}
}
static void
pipeline(struct fetch_pipeline_middle_end *fpme,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info)
{
if (prim_info->linear)
draw_pipeline_run_linear(fpme->draw, vert_info, prim_info);
else
draw_pipeline_run(fpme->draw, vert_info, prim_info);
}
static void
emit(struct pt_emit *emit,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info)
{
if (prim_info->linear) {
draw_pt_emit_linear(emit, vert_info, prim_info);
}
else {
draw_pt_emit(emit, vert_info, prim_info);
}
}
static void
draw_vertex_shader_run(struct draw_vertex_shader *vshader,
const struct draw_buffer_info *constants,
const struct draw_fetch_info *fetch_info,
const struct draw_vertex_info *input_verts,
struct draw_vertex_info *output_verts)
{
output_verts->vertex_size = input_verts->vertex_size;
output_verts->stride = input_verts->vertex_size;
output_verts->count = input_verts->count;
output_verts->verts =
(struct vertex_header *)MALLOC(output_verts->vertex_size *
align(output_verts->count, 4) +
DRAW_EXTRA_VERTICES_PADDING);
vshader->run_linear(vshader,
(const float (*)[4])input_verts->verts->data,
( float (*)[4])output_verts->verts->data,
constants,
input_verts->count,
input_verts->vertex_size,
input_verts->vertex_size,
fetch_info->elts);
}
static void
fetch_pipeline_generic(struct draw_pt_middle_end *middle,
const struct draw_fetch_info *fetch_info,
const struct draw_prim_info *in_prim_info)
{
struct fetch_pipeline_middle_end *fpme = fetch_pipeline_middle_end(middle);
struct draw_context *draw = fpme->draw;
struct draw_vertex_shader *vshader = draw->vs.vertex_shader;
struct draw_geometry_shader *gshader = draw->gs.geometry_shader;
struct draw_prim_info gs_prim_info[TGSI_MAX_VERTEX_STREAMS];
struct draw_vertex_info fetched_vert_info;
struct draw_vertex_info vs_vert_info;
struct draw_vertex_info gs_vert_info[TGSI_MAX_VERTEX_STREAMS];
struct draw_vertex_info *vert_info;
struct draw_prim_info ia_prim_info;
struct draw_vertex_info ia_vert_info;
const struct draw_prim_info *prim_info = in_prim_info;
bool free_prim_info = false;
unsigned opt = fpme->opt;
int num_vertex_streams = 1;
fetched_vert_info.count = fetch_info->count;
fetched_vert_info.vertex_size = fpme->vertex_size;
fetched_vert_info.stride = fpme->vertex_size;
fetched_vert_info.verts =
(struct vertex_header *)MALLOC(fpme->vertex_size *
align(fetch_info->count, 4) +
DRAW_EXTRA_VERTICES_PADDING);
if (!fetched_vert_info.verts) {
assert(0);
return;
}
if (draw->collect_statistics) {
draw->statistics.ia_vertices += prim_info->count;
draw->statistics.ia_primitives +=
u_decomposed_prims_for_vertices(prim_info->prim, fetch_info->count);
draw->statistics.vs_invocations += fetch_info->count;
}
/* Fetch into our vertex buffer.
*/
fetch(fpme->fetch, fetch_info, (char *)fetched_vert_info.verts);
vert_info = &fetched_vert_info;
/* Run the shader, note that this overwrites the data[] parts of
* the pipeline verts.
* Need fetch info to get vertex id correct.
*/
if (fpme->opt & PT_SHADE) {
draw_vertex_shader_run(vshader,
draw->pt.user.constants[MESA_SHADER_VERTEX],
fetch_info,
vert_info,
&vs_vert_info);
FREE(vert_info->verts);
vert_info = &vs_vert_info;
}
/* Finished with fetch:
*/
fetch_info = NULL;
if ((fpme->opt & PT_SHADE) && gshader) {
draw_geometry_shader_run(gshader,
draw->pt.user.constants[MESA_SHADER_GEOMETRY],
vert_info,
prim_info,
&vshader->info,
NULL,
gs_vert_info,
gs_prim_info);
FREE(vert_info->verts);
vert_info = &gs_vert_info[0];
prim_info = &gs_prim_info[0];
num_vertex_streams = gshader->num_vertex_streams;
/*
* pt emit can only handle ushort number of vertices (see
* render->allocate_vertices).
* vsplit guarantees there's never more than 4096, however GS can
* easily blow this up (by a factor of 256 (or even 1024) max).
*/
if (vert_info->count > 65535) {
opt |= PT_PIPELINE;
}
} else {
if (draw_prim_assembler_is_required(draw, prim_info, vert_info)) {
draw_prim_assembler_run(draw, prim_info, vert_info,
&ia_prim_info, &ia_vert_info);
if (ia_vert_info.count) {
FREE(vert_info->verts);
vert_info = &ia_vert_info;
prim_info = &ia_prim_info;
free_prim_info = true;
}
}
}
if (prim_info->count == 0) {
// debug_printf("GS/IA didn't emit any vertices!\n");
FREE(vert_info->verts);
if (free_prim_info) {
FREE(prim_info->primitive_lengths);
}
return;
}
/* Stream output needs to be done before clipping.
*
* XXX: Stream output surely needs to respect the prim_info->elt
* lists.
*/
draw_pt_so_emit(fpme->so_emit, num_vertex_streams, vert_info, prim_info);
/* rasterization stream selection */
if ((fpme->opt & PT_SHADE) && gshader) {
unsigned rs = draw->rasterizer->rasterization_stream;
if (rs < gshader->num_vertex_streams) {
vert_info = &gs_vert_info[rs];
prim_info = &gs_prim_info[rs];
}
}
draw_stats_clipper_primitives(draw, prim_info);
/*
* if there's no position, need to stop now, or the latter stages
* will try to access non-existent position output.
*/
if (draw_current_shader_position_output(draw) != -1) {
if (draw_pt_post_vs_run(fpme->post_vs, vert_info, prim_info)) {
opt |= PT_PIPELINE;
}
/* Do we need to run the pipeline?
*/
if (opt & PT_PIPELINE) {
pipeline(fpme, vert_info, prim_info);
}
else {
emit(fpme->emit, vert_info, prim_info);
}
}
if ((fpme->opt & PT_SHADE) && gshader) {
for (unsigned i = 0; i < gshader->num_vertex_streams; i++)
FREE(gs_vert_info[i].verts);
} else {
FREE(vert_info->verts);
}
if (free_prim_info) {
FREE(prim_info->primitive_lengths);
}
}
static inline unsigned
prim_type(unsigned prim, unsigned flags)
{
if (flags & DRAW_LINE_LOOP_AS_STRIP)
return MESA_PRIM_LINE_STRIP;
else
return prim;
}
static void
fetch_pipeline_run(struct draw_pt_middle_end *middle,
unsigned start,
const unsigned *fetch_elts,
unsigned fetch_count,
const uint16_t *draw_elts,
unsigned draw_count,
unsigned prim_flags)
{
struct fetch_pipeline_middle_end *fpme = fetch_pipeline_middle_end(middle);
struct draw_fetch_info fetch_info;
struct draw_prim_info prim_info;
fetch_info.linear = false;
fetch_info.start = start;
fetch_info.elts = fetch_elts;
fetch_info.count = fetch_count;
prim_info.linear = false;
prim_info.start = start - fpme->draw->start_index;
prim_info.count = draw_count;
prim_info.elts = draw_elts;
prim_info.prim = prim_type(fpme->input_prim, prim_flags);
prim_info.flags = prim_flags;
prim_info.primitive_count = 1;
prim_info.primitive_lengths = &draw_count;
fetch_pipeline_generic(middle, &fetch_info, &prim_info);
}
static void
fetch_pipeline_linear_run(struct draw_pt_middle_end *middle,
unsigned start,
unsigned count,
unsigned prim_flags)
{
struct fetch_pipeline_middle_end *fpme = fetch_pipeline_middle_end(middle);
struct draw_fetch_info fetch_info;
struct draw_prim_info prim_info;
fetch_info.linear = true;
fetch_info.start = start;
fetch_info.count = count;
fetch_info.elts = NULL;
prim_info.linear = true;
prim_info.start = start - fpme->draw->start_index;
prim_info.count = count;
prim_info.elts = NULL;
prim_info.prim = prim_type(fpme->input_prim, prim_flags);
prim_info.flags = prim_flags;
prim_info.primitive_count = 1;
prim_info.primitive_lengths = &count;
fetch_pipeline_generic(middle, &fetch_info, &prim_info);
}
static bool
fetch_pipeline_linear_run_elts(struct draw_pt_middle_end *middle,
unsigned start,
unsigned count,
const uint16_t *draw_elts,
unsigned draw_start,
unsigned draw_count)
{
struct fetch_pipeline_middle_end *fpme = fetch_pipeline_middle_end(middle);
struct draw_fetch_info fetch_info;
struct draw_prim_info prim_info;
fetch_info.linear = true;
fetch_info.start = start;
fetch_info.count = count;
fetch_info.elts = NULL;
prim_info.linear = false;
prim_info.start = draw_start - fpme->draw->start_index;
prim_info.count = draw_count;
prim_info.elts = draw_elts;
prim_info.prim = fpme->input_prim;
prim_info.flags = 0;
prim_info.primitive_count = 1;
prim_info.primitive_lengths = &draw_count;
fetch_pipeline_generic(middle, &fetch_info, &prim_info);
return true;
}
static void
fetch_pipeline_finish(struct draw_pt_middle_end *middle)
{
/* nothing to do */
}
static void
fetch_pipeline_destroy(struct draw_pt_middle_end *middle)
{
struct fetch_pipeline_middle_end *fpme = fetch_pipeline_middle_end(middle);
if (fpme->fetch)
draw_pt_fetch_destroy(fpme->fetch);
if (fpme->emit)
draw_pt_emit_destroy(fpme->emit);
if (fpme->so_emit)
draw_pt_so_emit_destroy(fpme->so_emit);
if (fpme->post_vs)
draw_pt_post_vs_destroy(fpme->post_vs);
FREE(middle);
}
struct draw_pt_middle_end *
draw_pt_fetch_pipeline_or_emit(struct draw_context *draw)
{
struct fetch_pipeline_middle_end *fpme =
CALLOC_STRUCT(fetch_pipeline_middle_end);
if (!fpme)
goto fail;
fpme->base.prepare = fetch_pipeline_prepare;
fpme->base.bind_parameters = fetch_pipeline_bind_parameters;
fpme->base.run = fetch_pipeline_run;
fpme->base.run_linear = fetch_pipeline_linear_run;
fpme->base.run_linear_elts = fetch_pipeline_linear_run_elts;
fpme->base.finish = fetch_pipeline_finish;
fpme->base.destroy = fetch_pipeline_destroy;
fpme->draw = draw;
fpme->fetch = draw_pt_fetch_create(draw);
if (!fpme->fetch)
goto fail;
fpme->post_vs = draw_pt_post_vs_create(draw);
if (!fpme->post_vs)
goto fail;
fpme->emit = draw_pt_emit_create(draw);
if (!fpme->emit)
goto fail;
fpme->so_emit = draw_pt_so_emit_create(draw);
if (!fpme->so_emit)
goto fail;
return &fpme->base;
fail:
if (fpme)
fetch_pipeline_destroy(&fpme->base);
return NULL;
}
@@ -0,0 +1,931 @@
/**************************************************************************
*
* 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.
*
**************************************************************************/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/u_prim.h"
#include "draw/draw_context.h"
#include "draw/draw_gs.h"
#include "draw/draw_tess.h"
#include "draw/draw_vbuf.h"
#include "draw/draw_vertex.h"
#include "draw/draw_pt.h"
#include "draw/draw_prim_assembler.h"
#include "draw/draw_vs.h"
#include "draw/draw_llvm.h"
#include "gallivm/lp_bld_init.h"
#include "gallivm/lp_bld_debug.h"
struct llvm_middle_end {
struct draw_pt_middle_end base;
struct draw_context *draw;
struct pt_emit *emit;
struct pt_so_emit *so_emit;
struct pt_fetch *fetch;
struct pt_post_vs *post_vs;
unsigned vertex_data_offset;
unsigned vertex_size;
enum mesa_prim input_prim;
unsigned opt;
struct draw_llvm *llvm;
struct draw_llvm_variant *current_variant;
};
/** cast wrapper */
static inline struct llvm_middle_end *
llvm_middle_end(struct draw_pt_middle_end *middle)
{
return (struct llvm_middle_end *) middle;
}
static void
llvm_middle_end_prepare_gs(struct llvm_middle_end *fpme)
{
struct draw_context *draw = fpme->draw;
struct draw_llvm *llvm = fpme->llvm;
struct draw_geometry_shader *gs = draw->gs.geometry_shader;
struct draw_gs_llvm_variant_list_item *li;
struct llvm_geometry_shader *shader = llvm_geometry_shader(gs);
char store[DRAW_GS_LLVM_MAX_VARIANT_KEY_SIZE];
struct draw_gs_llvm_variant_key *key = draw_gs_llvm_make_variant_key(llvm, store);
/* Search shader's list of variants for the key */
struct draw_gs_llvm_variant *variant = NULL;
LIST_FOR_EACH_ENTRY(li, &shader->variants.list, list) {
if (memcmp(&li->base->key, key, shader->variant_key_size) == 0) {
variant = li->base;
break;
}
}
if (variant) {
/* found the variant, move to head of global list (for LRU) */
list_move_to(&variant->list_item_global.list, &llvm->gs_variants_list.list);
} else {
/* Need to create new variant */
/* First check if we've created too many variants. If so, free
* 3.125% of the LRU to avoid using too much memory.
*/
if (llvm->nr_gs_variants >= DRAW_MAX_SHADER_VARIANTS) {
if (gallivm_debug & GALLIVM_DEBUG_PERF) {
debug_printf("Evicting GS: %u gs variants,\t%u total variants\n",
shader->variants_cached, llvm->nr_gs_variants);
}
/*
* XXX: should we flush here ?
*/
struct draw_gs_llvm_variant_list_item *item;
for (unsigned i = 0; i < DRAW_MAX_SHADER_VARIANTS / 32; i++) {
if (list_is_empty(&llvm->gs_variants_list.list)) {
break;
}
item = list_last_entry(&llvm->gs_variants_list.list,
struct draw_gs_llvm_variant_list_item, list);
assert(item);
assert(item->base);
draw_gs_llvm_destroy_variant(item->base);
}
}
variant = draw_gs_llvm_create_variant(llvm, draw_total_gs_outputs(draw), key);
if (variant) {
list_add(&variant->list_item_local.list, &shader->variants.list);
list_add(&variant->list_item_global.list, &llvm->gs_variants_list.list);
llvm->nr_gs_variants++;
shader->variants_cached++;
}
}
gs->current_variant = variant;
}
static void
llvm_middle_end_prepare_tcs(struct llvm_middle_end *fpme)
{
struct draw_context *draw = fpme->draw;
struct draw_llvm *llvm = fpme->llvm;
struct draw_tess_ctrl_shader *tcs = draw->tcs.tess_ctrl_shader;
struct draw_tcs_llvm_variant_list_item *li;
struct llvm_tess_ctrl_shader *shader = llvm_tess_ctrl_shader(tcs);
char store[DRAW_TCS_LLVM_MAX_VARIANT_KEY_SIZE];
const struct draw_tcs_llvm_variant_key *key =
draw_tcs_llvm_make_variant_key(llvm, store);
/* Search shader's list of variants for the key */
struct draw_tcs_llvm_variant *variant = NULL;
LIST_FOR_EACH_ENTRY(li, &shader->variants.list, list) {
if (memcmp(&li->base->key, key, shader->variant_key_size) == 0) {
variant = li->base;
break;
}
}
if (variant) {
/* found the variant, move to head of global list (for LRU) */
list_move_to(&variant->list_item_global.list,
&llvm->tcs_variants_list.list);
} else {
/* Need to create new variant */
/* First check if we've created too many variants. If so, free
* 3.125% of the LRU to avoid using too much memory.
*/
if (llvm->nr_tcs_variants >= DRAW_MAX_SHADER_VARIANTS) {
if (gallivm_debug & GALLIVM_DEBUG_PERF) {
debug_printf("Evicting TCS: %u tcs variants,\t%u total variants\n",
shader->variants_cached, llvm->nr_tcs_variants);
}
/*
* XXX: should we flush here ?
*/
for (unsigned i = 0; i < DRAW_MAX_SHADER_VARIANTS / 32; i++) {
struct draw_tcs_llvm_variant_list_item *item;
if (list_is_empty(&llvm->tcs_variants_list.list)) {
break;
}
item = list_last_entry(&llvm->tcs_variants_list.list,
struct draw_tcs_llvm_variant_list_item, list);
assert(item);
assert(item->base);
draw_tcs_llvm_destroy_variant(item->base);
}
}
variant = draw_tcs_llvm_create_variant(llvm, 0, key);
if (variant) {
list_add(&variant->list_item_local.list, &shader->variants.list);
list_add(&variant->list_item_global.list, &llvm->tcs_variants_list.list);
llvm->nr_tcs_variants++;
shader->variants_cached++;
}
}
tcs->current_variant = variant;
}
static void
llvm_middle_end_prepare_tes(struct llvm_middle_end *fpme)
{
struct draw_context *draw = fpme->draw;
struct draw_llvm *llvm = fpme->llvm;
struct draw_tess_eval_shader *tes = draw->tes.tess_eval_shader;
struct draw_tes_llvm_variant *variant = NULL;
struct draw_tes_llvm_variant_list_item *li;
struct llvm_tess_eval_shader *shader = llvm_tess_eval_shader(tes);
char store[DRAW_TES_LLVM_MAX_VARIANT_KEY_SIZE];
const struct draw_tes_llvm_variant_key *key =
draw_tes_llvm_make_variant_key(llvm, store);
/* Search shader's list of variants for the key */
LIST_FOR_EACH_ENTRY(li, &shader->variants.list, list) {
if (memcmp(&li->base->key, key, shader->variant_key_size) == 0) {
variant = li->base;
break;
}
}
if (variant) {
/* found the variant, move to head of global list (for LRU) */
list_move_to(&variant->list_item_global.list,
&llvm->tes_variants_list.list);
} else {
/* Need to create new variant */
/* First check if we've created too many variants. If so, free
* 3.125% of the LRU to avoid using too much memory.
*/
if (llvm->nr_tes_variants >= DRAW_MAX_SHADER_VARIANTS) {
if (gallivm_debug & GALLIVM_DEBUG_PERF) {
debug_printf("Evicting TES: %u tes variants,\t%u total variants\n",
shader->variants_cached, llvm->nr_tes_variants);
}
/*
* XXX: should we flush here ?
*/
for (unsigned i = 0; i < DRAW_MAX_SHADER_VARIANTS / 32; i++) {
struct draw_tes_llvm_variant_list_item *item;
if (list_is_empty(&llvm->tes_variants_list.list)) {
break;
}
item = list_last_entry(&llvm->tes_variants_list.list,
struct draw_tes_llvm_variant_list_item, list);
assert(item);
assert(item->base);
draw_tes_llvm_destroy_variant(item->base);
}
}
variant = draw_tes_llvm_create_variant(llvm, draw_total_tes_outputs(draw), key);
if (variant) {
list_add(&variant->list_item_local.list, &shader->variants.list);
list_add(&variant->list_item_global.list, &llvm->tes_variants_list.list);
llvm->nr_tes_variants++;
shader->variants_cached++;
}
}
tes->current_variant = variant;
}
/**
* Prepare/validate middle part of the vertex pipeline.
* NOTE: if you change this function, also look at the non-LLVM
* function fetch_pipeline_prepare() for similar changes.
*/
static void
llvm_middle_end_prepare(struct draw_pt_middle_end *middle,
enum mesa_prim in_prim,
unsigned opt,
unsigned *max_vertices)
{
struct llvm_middle_end *fpme = llvm_middle_end(middle);
struct draw_context *draw = fpme->draw;
struct draw_llvm *llvm = fpme->llvm;
struct draw_vertex_shader *vs = draw->vs.vertex_shader;
struct draw_geometry_shader *gs = draw->gs.geometry_shader;
struct draw_tess_ctrl_shader *tcs = draw->tcs.tess_ctrl_shader;
struct draw_tess_eval_shader *tes = draw->tes.tess_eval_shader;
const enum mesa_prim out_prim =
gs ? gs->output_primitive : tes ? get_tes_output_prim(tes) :
u_assembled_prim(in_prim);
unsigned point_line_clip = draw->rasterizer->fill_front == PIPE_POLYGON_MODE_POINT ||
draw->rasterizer->fill_front == PIPE_POLYGON_MODE_LINE ||
out_prim == MESA_PRIM_POINTS ||
u_reduced_prim(out_prim) == MESA_PRIM_LINES;
fpme->input_prim = in_prim;
fpme->opt = opt;
draw_pt_post_vs_prepare(fpme->post_vs,
draw->clip_xy,
draw->clip_z,
draw->clip_user,
point_line_clip ? draw->guard_band_points_lines_xy :
draw->guard_band_xy,
draw->bypass_viewport,
draw->rasterizer->clip_halfz,
(draw->vs.edgeflag_output ? true : false));
draw_pt_so_emit_prepare(fpme->so_emit, (gs == NULL && tes == NULL));
if (!(opt & PT_PIPELINE)) {
draw_pt_emit_prepare(fpme->emit, out_prim, max_vertices);
*max_vertices = MAX2(*max_vertices, 4096);
} else {
/* limit max fetches by limiting max_vertices */
*max_vertices = 4096;
}
/* Get the number of float[4] attributes per vertex.
* Note: this must be done after draw_pt_emit_prepare() since that
* can effect the vertex size.
*/
unsigned nr = MAX2(vs->info.num_inputs, draw_total_vs_outputs(draw));
/* Always leave room for the vertex header whether we need it or
* not. It's hard to get rid of it in particular because of the
* viewport code in draw_pt_post_vs.c.
*/
fpme->vertex_size = sizeof(struct vertex_header) + nr * 4 * sizeof(float);
/* return even number */
*max_vertices = *max_vertices & ~1;
/* Find/create the vertex shader variant */
{
struct draw_llvm_variant *variant = NULL;
struct draw_llvm_variant_list_item *li;
struct llvm_vertex_shader *shader = llvm_vertex_shader(vs);
char store[DRAW_LLVM_MAX_VARIANT_KEY_SIZE];
struct draw_llvm_variant_key *key = draw_llvm_make_variant_key(llvm, store);
/* Search shader's list of variants for the key */
LIST_FOR_EACH_ENTRY(li, &shader->variants.list, list) {
if (memcmp(&li->base->key, key, shader->variant_key_size) == 0) {
variant = li->base;
break;
}
}
if (variant) {
/* found the variant, move to head of global list (for LRU) */
list_move_to(&variant->list_item_global.list, &llvm->vs_variants_list.list);
} else {
/* Need to create new variant */
/* First check if we've created too many variants. If so, free
* 3.125% of the LRU to avoid using too much memory.
*/
if (llvm->nr_variants >= DRAW_MAX_SHADER_VARIANTS) {
if (gallivm_debug & GALLIVM_DEBUG_PERF) {
debug_printf("Evicting VS: %u vs variants,\t%u total variants\n",
shader->variants_cached, llvm->nr_variants);
}
/*
* XXX: should we flush here ?
*/
for (unsigned i = 0; i < DRAW_MAX_SHADER_VARIANTS / 32; i++) {
struct draw_llvm_variant_list_item *item;
if (list_is_empty(&llvm->vs_variants_list.list)) {
break;
}
item = list_last_entry(&llvm->vs_variants_list.list,
struct draw_llvm_variant_list_item, list);
assert(item);
assert(item->base);
draw_llvm_destroy_variant(item->base);
}
}
variant = draw_llvm_create_variant(llvm, nr, key);
if (variant) {
list_add(&variant->list_item_local.list, &shader->variants.list);
list_add(&variant->list_item_global.list, &llvm->vs_variants_list.list);
llvm->nr_variants++;
shader->variants_cached++;
}
}
fpme->current_variant = variant;
}
if (gs) {
llvm_middle_end_prepare_gs(fpme);
}
if (tcs) {
llvm_middle_end_prepare_tcs(fpme);
}
if (tes) {
llvm_middle_end_prepare_tes(fpme);
}
}
static unsigned
get_num_consts_robust(struct draw_context *draw, struct draw_buffer_info *bufs, unsigned idx)
{
uint64_t const_bytes = bufs[idx].size;
if (const_bytes < sizeof(float))
return 0;
return DIV_ROUND_UP(const_bytes, draw->constant_buffer_stride);
}
/**
* Bind/update constant buffer pointers, clip planes and viewport dims.
* These are "light weight" parameters which aren't baked into the
* generated code. Updating these items is much cheaper than revalidating
* and rebuilding the generated pipeline code.
*/
static void
llvm_middle_end_bind_parameters(struct draw_pt_middle_end *middle)
{
static const float fake_const_buf[4];
struct llvm_middle_end *fpme = llvm_middle_end(middle);
struct draw_context *draw = fpme->draw;
struct draw_llvm *llvm = fpme->llvm;
unsigned i;
for (mesa_shader_stage shader_type = MESA_SHADER_VERTEX; shader_type <= MESA_SHADER_GEOMETRY; shader_type++) {
for (i = 0; i < ARRAY_SIZE(llvm->jit_resources[shader_type].constants); ++i) {
/*
* There could be a potential issue with rounding this up, as the
* shader expects 16-byte allocations, the fix is likely to move
* to LOAD intrinsic in the future and remove the vec4 constraint.
*/
int num_consts = get_num_consts_robust(draw, draw->pt.user.constants[shader_type], i);
llvm->jit_resources[shader_type].constants[i].f = draw->pt.user.constants[shader_type][i].ptr;
llvm->jit_resources[shader_type].constants[i].num_elements = num_consts;
if (num_consts == 0) {
llvm->jit_resources[shader_type].constants[i].f = fake_const_buf;
}
}
for (i = 0; i < ARRAY_SIZE(llvm->jit_resources[shader_type].ssbos); ++i) {
int num_ssbos = draw->pt.user.ssbos[shader_type][i].size;
llvm->jit_resources[shader_type].ssbos[i].u = draw->pt.user.ssbos[shader_type][i].ptr;
llvm->jit_resources[shader_type].ssbos[i].num_elements = num_ssbos;
if (num_ssbos == 0) {
llvm->jit_resources[shader_type].ssbos[i].u = (const uint32_t *)fake_const_buf;
}
}
}
llvm->vs_jit_context.planes =
(float (*)[DRAW_TOTAL_CLIP_PLANES][4]) draw->pt.user.planes[0];
llvm->gs_jit_context.planes =
(float (*)[DRAW_TOTAL_CLIP_PLANES][4]) draw->pt.user.planes[0];
llvm->vs_jit_context.viewports = draw->viewports;
llvm->gs_jit_context.viewports = draw->viewports;
}
static void
pipeline(struct llvm_middle_end *llvm,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info)
{
if (prim_info->linear)
draw_pipeline_run_linear(llvm->draw, vert_info, prim_info);
else
draw_pipeline_run(llvm->draw, vert_info, prim_info);
}
static void
emit(struct pt_emit *emit,
const struct draw_vertex_info *vert_info,
const struct draw_prim_info *prim_info)
{
if (prim_info->linear)
draw_pt_emit_linear(emit, vert_info, prim_info);
else
draw_pt_emit(emit, vert_info, prim_info);
}
static void
llvm_pipeline_generic(struct draw_pt_middle_end *middle,
const struct draw_fetch_info *fetch_info,
const struct draw_prim_info *in_prim_info)
{
struct llvm_middle_end *fpme = llvm_middle_end(middle);
struct draw_context *draw = fpme->draw;
struct draw_geometry_shader *gshader = draw->gs.geometry_shader;
struct draw_tess_ctrl_shader *tcs_shader = draw->tcs.tess_ctrl_shader;
struct draw_tess_eval_shader *tes_shader = draw->tes.tess_eval_shader;
struct draw_prim_info tcs_prim_info;
struct draw_prim_info tes_prim_info;
struct draw_prim_info gs_prim_info[TGSI_MAX_VERTEX_STREAMS];
struct draw_vertex_info llvm_vert_info;
struct draw_vertex_info tcs_vert_info;
struct draw_vertex_info tes_vert_info;
struct draw_vertex_info *vert_info;
struct draw_prim_info ia_prim_info;
struct draw_vertex_info ia_vert_info;
const struct draw_prim_info *prim_info = in_prim_info;
bool free_prim_info = false;
unsigned opt = fpme->opt;
bool clipped = 0;
uint16_t *tes_elts_out = NULL;
assert(fetch_info->count > 0);
llvm_vert_info.count = fetch_info->count;
llvm_vert_info.vertex_size = fpme->vertex_size;
llvm_vert_info.stride = fpme->vertex_size;
llvm_vert_info.verts = (struct vertex_header *)
MALLOC(fpme->vertex_size *
align(fetch_info->count, lp_native_vector_width / 32) +
DRAW_EXTRA_VERTICES_PADDING);
if (!llvm_vert_info.verts) {
assert(0);
return;
}
if (draw->collect_statistics) {
draw->statistics.ia_vertices += prim_info->count;
if (prim_info->prim == MESA_PRIM_PATCHES)
draw->statistics.ia_primitives +=
prim_info->count / draw->pt.vertices_per_patch;
else
draw->statistics.ia_primitives +=
u_decomposed_prims_for_vertices(prim_info->prim, prim_info->count);
draw->statistics.vs_invocations += fetch_info->count;
}
{
unsigned start, vertex_id_offset;
const unsigned *elts;
if (fetch_info->linear) {
start = fetch_info->start;
vertex_id_offset = draw->start_index;
elts = NULL;
} else {
start = draw->pt.user.eltMax;
vertex_id_offset = draw->pt.user.eltBias;
elts = fetch_info->elts;
}
/* Run vertex fetch shader */
clipped = fpme->current_variant->jit_func(&fpme->llvm->vs_jit_context,
&fpme->llvm->jit_resources[MESA_SHADER_VERTEX],
llvm_vert_info.verts,
draw->pt.user.vbuffer,
fetch_info->count,
start,
fpme->vertex_size,
draw->pt.vertex_buffer,
draw->instance_id,
vertex_id_offset,
draw->start_instance,
elts,
draw->pt.user.drawid,
draw->pt.user.viewid);
/* Finished with fetch and vs */
fetch_info = NULL;
vert_info = &llvm_vert_info;
}
/* Keep track of the patch lengths if we have a geometry shader, this way we can increment
* gl_PrimitiveID once per patch, instead of per tessellation output primitive.
* The Vulkan and OpenGL specs say:
* In a geometry shader, it (gl_PrimitiveID) will contain the number of primitives presented
* as input to the shader since the current set of rendering primitives was started.
* This sounds like gl_PrimitiveID will be the index of each output primitive from the
* tessellation shader, so multiple per patch. But drivers implement the more specific language
* from ARB_tessellation_shader:
* In geometry language the input variable gl_PrimitiveIDIn behaves identically to the
* tessellation control and evaluation shader input variable gl_PrimitiveID.
* Which implies on primitive index per patch. DirectX is also more specific:
* The geometry-shader stage can consume the SV_PrimitiveID system-generated value that is
* auto-generated by the IA. This allows per-primitive data to be fetched or computed if desired.
* The input-assembler (IA) stage happens before the tessellation, so we would expect one index
* per patch.
*/
uint32_t *patch_lengths = NULL;
if (opt & PT_SHADE) {
struct draw_vertex_shader *vshader = draw->vs.vertex_shader;
if (tcs_shader) {
draw_tess_ctrl_shader_run(tcs_shader,
vert_info,
prim_info,
&vshader->info,
&tcs_vert_info,
&tcs_prim_info);
FREE(vert_info->verts);
vert_info = &tcs_vert_info;
prim_info = &tcs_prim_info;
} else if (tes_shader) {
unsigned num_prims = prim_info->count / draw->pt.vertices_per_patch;
tcs_prim_info = *prim_info;
tcs_prim_info.primitive_count = num_prims;
prim_info = &tcs_prim_info;
}
if (tes_shader) {
draw_tess_eval_shader_run(tes_shader,
tcs_shader ? tcs_shader->vertices_out : draw->pt.vertices_per_patch,
vert_info,
prim_info,
tcs_shader ? &tcs_shader->info : &vshader->info,
&tes_vert_info,
&tes_prim_info,
gshader ? &patch_lengths : NULL,
&tes_elts_out);
FREE(vert_info->verts);
vert_info = &tes_vert_info;
prim_info = &tes_prim_info;
free_prim_info = true;
/*
* pt emit can only handle ushort number of vertices (see
* render->allocate_vertices).
* vsplit guarantees there's never more than 4096, however GS can
* easily blow this up (by a factor of 256 (or even 1024) max).
*/
if (vert_info->count > 65535) {
opt |= PT_PIPELINE;
}
}
}
struct draw_vertex_info gs_vert_info[TGSI_MAX_VERTEX_STREAMS];
memset(&gs_vert_info, 0, sizeof(gs_vert_info));
if ((opt & PT_SHADE) && gshader) {
struct draw_vertex_shader *vshader = draw->vs.vertex_shader;
draw_geometry_shader_run(gshader,
draw->pt.user.constants[MESA_SHADER_GEOMETRY],
vert_info,
prim_info,
tes_shader ? &tes_shader->info : &vshader->info,
tes_shader ? &patch_lengths : NULL,
gs_vert_info,
gs_prim_info);
FREE(vert_info->verts);
if (free_prim_info) {
FREE(prim_info->primitive_lengths);
FREE(tes_elts_out);
}
vert_info = &gs_vert_info[0];
prim_info = &gs_prim_info[0];
free_prim_info = false;
/*
* pt emit can only handle ushort number of vertices (see
* render->allocate_vertices).
* vsplit guarantees there's never more than 4096, however GS can
* easily blow this up (by a factor of 256 (or even 1024) max).
*/
if (vert_info->count > 65535) {
opt |= PT_PIPELINE;
}
} else {
if (!tes_shader &&
draw_prim_assembler_is_required(draw, prim_info, vert_info)) {
draw_prim_assembler_run(draw, prim_info, vert_info,
&ia_prim_info, &ia_vert_info);
if (ia_vert_info.count) {
FREE(vert_info->verts);
if (free_prim_info) {
FREE(prim_info->primitive_lengths);
FREE(tes_elts_out);
tes_elts_out = NULL;
}
vert_info = &ia_vert_info;
prim_info = &ia_prim_info;
free_prim_info = true;
}
}
}
/* stream output needs to be done before clipping */
draw_pt_so_emit(fpme->so_emit,
gshader ? gshader->num_vertex_streams : 1,
vert_info, prim_info);
/* rasterization stream selection */
if ((opt & PT_SHADE) && gshader) {
unsigned rs = draw->rasterizer->rasterization_stream;
if (rs < gshader->num_vertex_streams) {
vert_info = &gs_vert_info[rs];
prim_info = &gs_prim_info[rs];
}
}
if (prim_info->count == 0) {
// debug_printf("GS/IA didn't emit any vertices!\n");
} else {
draw_stats_clipper_primitives(draw, prim_info);
/*
* if there's no position, need to stop now, or the latter stages
* will try to access non-existent position output.
*/
if (draw_current_shader_position_output(draw) != -1) {
if ((opt & PT_SHADE) &&
(gshader || tes_shader ||
draw->vs.vertex_shader->info.writes_viewport_index)) {
clipped = draw_pt_post_vs_run(fpme->post_vs, vert_info, prim_info);
}
/* "clipped" also includes non-one edgeflag */
if (clipped) {
opt |= PT_PIPELINE;
}
/* Do we need to run the pipeline? Now will come here if clipped */
if (opt & PT_PIPELINE) {
pipeline(fpme, vert_info, prim_info);
} else {
emit(fpme->emit, vert_info, prim_info);
}
}
}
if ((opt & PT_SHADE) && gshader) {
for (unsigned i = 0; i < gshader->num_vertex_streams; i++)
FREE(gs_vert_info[i].verts);
} else {
FREE(vert_info->verts);
}
FREE(patch_lengths);
if (free_prim_info) {
FREE(tes_elts_out);
FREE(prim_info->primitive_lengths);
}
}
static inline enum mesa_prim
prim_type(enum mesa_prim prim, unsigned flags)
{
if (flags & DRAW_LINE_LOOP_AS_STRIP)
return MESA_PRIM_LINE_STRIP;
else
return prim;
}
static void
llvm_middle_end_run(struct draw_pt_middle_end *middle,
unsigned start,
const unsigned *fetch_elts,
unsigned fetch_count,
const uint16_t *draw_elts,
unsigned draw_count,
unsigned prim_flags)
{
struct llvm_middle_end *fpme = llvm_middle_end(middle);
struct draw_fetch_info fetch_info;
struct draw_prim_info prim_info;
fetch_info.linear = false;
fetch_info.start = start;
fetch_info.elts = fetch_elts;
fetch_info.count = fetch_count;
prim_info.linear = false;
prim_info.start = start - fpme->draw->start_index;
prim_info.count = draw_count;
prim_info.elts = draw_elts;
prim_info.prim = prim_type(fpme->input_prim, prim_flags);
prim_info.flags = prim_flags;
prim_info.primitive_count = 1;
prim_info.primitive_lengths = &draw_count;
llvm_pipeline_generic(middle, &fetch_info, &prim_info);
}
static void
llvm_middle_end_linear_run(struct draw_pt_middle_end *middle,
unsigned start,
unsigned count,
unsigned prim_flags)
{
struct llvm_middle_end *fpme = llvm_middle_end(middle);
struct draw_fetch_info fetch_info;
struct draw_prim_info prim_info;
fetch_info.linear = true;
fetch_info.start = start;
fetch_info.count = count;
fetch_info.elts = NULL;
prim_info.linear = true;
prim_info.start = start - fpme->draw->start_index;
prim_info.count = count;
prim_info.elts = NULL;
prim_info.prim = prim_type(fpme->input_prim, prim_flags);
prim_info.flags = prim_flags;
prim_info.primitive_count = 1;
prim_info.primitive_lengths = &count;
llvm_pipeline_generic(middle, &fetch_info, &prim_info);
}
static bool
llvm_middle_end_linear_run_elts(struct draw_pt_middle_end *middle,
unsigned start,
unsigned count,
const uint16_t *draw_elts,
unsigned draw_start,
unsigned draw_count)
{
struct llvm_middle_end *fpme = llvm_middle_end(middle);
struct draw_fetch_info fetch_info;
struct draw_prim_info prim_info;
fetch_info.linear = true;
fetch_info.start = start;
fetch_info.count = count;
fetch_info.elts = NULL;
prim_info.linear = false;
prim_info.start = draw_start - fpme->draw->start_index;
prim_info.count = draw_count;
prim_info.elts = draw_elts;
prim_info.prim = fpme->input_prim;
prim_info.flags = 0;
prim_info.primitive_count = 1;
prim_info.primitive_lengths = &draw_count;
llvm_pipeline_generic(middle, &fetch_info, &prim_info);
return true;
}
static void
llvm_middle_end_finish(struct draw_pt_middle_end *middle)
{
/* nothing to do */
}
static void
llvm_middle_end_destroy(struct draw_pt_middle_end *middle)
{
struct llvm_middle_end *fpme = llvm_middle_end(middle);
if (fpme->fetch)
draw_pt_fetch_destroy(fpme->fetch);
if (fpme->emit)
draw_pt_emit_destroy(fpme->emit);
if (fpme->so_emit)
draw_pt_so_emit_destroy(fpme->so_emit);
if (fpme->post_vs)
draw_pt_post_vs_destroy(fpme->post_vs);
FREE(middle);
}
struct draw_pt_middle_end *
draw_pt_fetch_pipeline_or_emit_llvm(struct draw_context *draw)
{
struct llvm_middle_end *fpme = 0;
if (!draw->llvm)
return NULL;
fpme = CALLOC_STRUCT(llvm_middle_end);
if (!fpme)
goto fail;
fpme->base.prepare = llvm_middle_end_prepare;
fpme->base.bind_parameters = llvm_middle_end_bind_parameters;
fpme->base.run = llvm_middle_end_run;
fpme->base.run_linear = llvm_middle_end_linear_run;
fpme->base.run_linear_elts = llvm_middle_end_linear_run_elts;
fpme->base.finish = llvm_middle_end_finish;
fpme->base.destroy = llvm_middle_end_destroy;
fpme->draw = draw;
fpme->fetch = draw_pt_fetch_create(draw);
if (!fpme->fetch)
goto fail;
fpme->post_vs = draw_pt_post_vs_create(draw);
if (!fpme->post_vs)
goto fail;
fpme->emit = draw_pt_emit_create(draw);
if (!fpme->emit)
goto fail;
fpme->so_emit = draw_pt_so_emit_create(draw);
if (!fpme->so_emit)
goto fail;
fpme->llvm = draw->llvm;
if (!fpme->llvm)
goto fail;
fpme->current_variant = NULL;
return &fpme->base;
fail:
if (fpme)
llvm_middle_end_destroy(&fpme->base);
return NULL;
}
@@ -0,0 +1,131 @@
/**************************************************************************
*
* Copyright 2023 Red Hat.
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
*
**************************************************************************/
#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "draw/draw_pt.h"
#include "draw/draw_mesh.h"
/*
* Mesh shader middle end,
* This doesn't need a frontend as mesh shader frontend
* is done with compute shaders in llvmpipe.
*/
struct mesh_pipeline_middle_end {
struct draw_pt_middle_end base;
struct draw_context *draw;
struct pt_so_emit *so_emit;
struct pt_post_vs *post_vs;
};
/** cast wrapper */
static inline struct mesh_pipeline_middle_end *
mesh_pipeline_middle_end(struct draw_pt_middle_end *middle)
{
return (struct mesh_pipeline_middle_end *) middle;
}
static void
mesh_pipeline_destroy(struct draw_pt_middle_end *middle)
{
struct mesh_pipeline_middle_end *mpme = mesh_pipeline_middle_end(middle);
if (mpme->so_emit)
draw_pt_so_emit_destroy(mpme->so_emit);
if (mpme->post_vs)
draw_pt_post_vs_destroy(mpme->post_vs);
FREE(middle);
}
static void
mesh_middle_end_prepare(struct draw_pt_middle_end *middle,
enum mesa_prim prim,
unsigned opt,
unsigned *max_vertices)
{
struct mesh_pipeline_middle_end *mpme = mesh_pipeline_middle_end(middle);
struct draw_context *draw = mpme->draw;
enum mesa_prim out_prim = draw->ms.mesh_shader->output_primitive;
unsigned point_clip = draw->rasterizer->fill_front == PIPE_POLYGON_MODE_POINT ||
out_prim == MESA_PRIM_POINTS;
draw_pt_post_vs_prepare(mpme->post_vs,
draw->clip_xy,
draw->clip_z,
draw->clip_user,
point_clip ? draw->guard_band_points_lines_xy :
draw->guard_band_xy,
draw->bypass_viewport,
draw->rasterizer->clip_halfz,
false);
draw_pt_so_emit_prepare(mpme->so_emit, false);
draw_do_flush(draw, DRAW_FLUSH_BACKEND);
}
void
draw_mesh_middle_end_run(struct draw_pt_middle_end *middle,
struct draw_vertex_info *vert_info,
struct draw_prim_info *prim_info)
{
struct mesh_pipeline_middle_end *mpme = mesh_pipeline_middle_end(middle);
draw_pt_post_vs_run(mpme->post_vs, vert_info, prim_info);
/* just for primgen query */
draw_pt_so_emit(mpme->so_emit, 1, vert_info, prim_info);
draw_pipeline_run_linear(mpme->draw, vert_info, prim_info);
}
struct draw_pt_middle_end *
draw_pt_mesh_pipeline_or_emit(struct draw_context *draw)
{
struct mesh_pipeline_middle_end *mpme =
CALLOC_STRUCT(mesh_pipeline_middle_end);
if (!mpme)
goto fail;
mpme->base.prepare = mesh_middle_end_prepare;
mpme->base.destroy = mesh_pipeline_destroy;
mpme->draw = draw;
mpme->post_vs = draw_pt_post_vs_create(draw);
if (!mpme->post_vs)
goto fail;
mpme->so_emit = draw_pt_so_emit_create(draw);
if (!mpme->so_emit)
goto fail;
return &mpme->base;
fail:
if (mpme)
mesh_pipeline_destroy(&mpme->base);
return NULL;
}
@@ -0,0 +1,234 @@
/**************************************************************************
*
* Copyright 2008 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.
*
**************************************************************************/
#include "util/u_memory.h"
#include "util/u_math.h"
#include "util/u_prim.h"
#include "pipe/p_context.h"
#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "draw/draw_pt.h"
#include "draw/draw_vs.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
#define DO_CLIP_XY_GUARD_BAND 0x40
struct pt_post_vs {
struct draw_context *draw;
unsigned flags;
bool (*run)(struct pt_post_vs *pvs,
struct draw_vertex_info *info,
const struct draw_prim_info *prim_info);
};
static inline void
initialize_vertex_header(struct vertex_header *header)
{
header->clipmask = 0;
header->edgeflag = 1;
header->pad = 0;
header->vertex_id = UNDEFINED_VERTEX_ID;
}
static inline float
dot4(const float *a, const float *b)
{
return (a[0] * b[0] +
a[1] * b[1] +
a[2] * b[2] +
a[3] * b[3]);
}
#define FLAGS (0)
#define TAG(x) x##_none
#include "draw_cliptest_tmp.h"
#define FLAGS (DO_CLIP_XY | DO_CLIP_FULL_Z | DO_VIEWPORT)
#define TAG(x) x##_xy_fullz_viewport
#include "draw_cliptest_tmp.h"
#define FLAGS (DO_CLIP_XY | DO_CLIP_HALF_Z | DO_VIEWPORT)
#define TAG(x) x##_xy_halfz_viewport
#include "draw_cliptest_tmp.h"
#define FLAGS (DO_CLIP_XY_GUARD_BAND | DO_CLIP_HALF_Z | DO_VIEWPORT)
#define TAG(x) x##_xy_gb_halfz_viewport
#include "draw_cliptest_tmp.h"
#define FLAGS (DO_CLIP_XY_GUARD_BAND | DO_CLIP_FULL_Z | DO_VIEWPORT)
#define TAG(x) x##_xy_gb_fullz_viewport
#include "draw_cliptest_tmp.h"
#define FLAGS (DO_CLIP_FULL_Z | DO_VIEWPORT)
#define TAG(x) x##_fullz_viewport
#include "draw_cliptest_tmp.h"
#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"
#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"
/* 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.
*/
#define FLAGS (pvs->flags)
#define TAG(x) x##_generic
#include "draw_cliptest_tmp.h"
bool
draw_pt_post_vs_run(struct pt_post_vs *pvs,
struct draw_vertex_info *info,
const struct draw_prim_info *prim_info)
{
return pvs->run(pvs, info, prim_info);
}
void
draw_pt_post_vs_prepare(struct pt_post_vs *pvs,
bool clip_xy,
bool clip_z,
bool clip_user,
bool guard_band,
bool bypass_viewport,
bool clip_halfz,
bool need_edgeflags)
{
pvs->flags = 0;
if (clip_xy && !guard_band) {
pvs->flags |= DO_CLIP_XY;
ASSIGN_4V(pvs->draw->plane[0], -1, 0, 0, 1);
ASSIGN_4V(pvs->draw->plane[1], 1, 0, 0, 1);
ASSIGN_4V(pvs->draw->plane[2], 0, -1, 0, 1);
ASSIGN_4V(pvs->draw->plane[3], 0, 1, 0, 1);
} else if (clip_xy && guard_band) {
pvs->flags |= DO_CLIP_XY_GUARD_BAND;
ASSIGN_4V(pvs->draw->plane[0], -0.5, 0, 0, 1);
ASSIGN_4V(pvs->draw->plane[1], 0.5, 0, 0, 1);
ASSIGN_4V(pvs->draw->plane[2], 0, -0.5, 0, 1);
ASSIGN_4V(pvs->draw->plane[3], 0, 0.5, 0, 1);
}
if (clip_z) {
if (clip_halfz) {
pvs->flags |= DO_CLIP_HALF_Z;
ASSIGN_4V(pvs->draw->plane[4], 0, 0, 1, 0);
} else {
pvs->flags |= DO_CLIP_FULL_Z;
ASSIGN_4V(pvs->draw->plane[4], 0, 0, 1, 1);
}
}
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_XY_GUARD_BAND | DO_CLIP_HALF_Z | DO_VIEWPORT:
pvs->run = do_cliptest_xy_gb_halfz_viewport;
break;
case DO_CLIP_XY_GUARD_BAND | DO_CLIP_FULL_Z | DO_VIEWPORT:
pvs->run = do_cliptest_xy_gb_fullz_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;
}
}
struct pt_post_vs *
draw_pt_post_vs_create(struct draw_context *draw)
{
struct pt_post_vs *pvs = CALLOC_STRUCT(pt_post_vs);
if (!pvs)
return NULL;
pvs->draw = draw;
return pvs;
}
void
draw_pt_post_vs_destroy(struct pt_post_vs *pvs)
{
FREE(pvs);
}
@@ -0,0 +1,347 @@
/**************************************************************************
*
* 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.
*
**************************************************************************/
#include "draw/draw_private.h"
#include "draw/draw_vs.h"
#include "draw/draw_gs.h"
#include "draw/draw_tess.h"
#include "draw/draw_context.h"
#include "draw/draw_vbuf.h"
#include "draw/draw_vertex.h"
#include "draw/draw_pt.h"
#include "pipe/p_state.h"
#include "util/u_math.h"
#include "util/u_prim.h"
#include "util/u_memory.h"
struct pt_so_emit {
struct draw_context *draw;
unsigned input_vertex_stride;
const float (*inputs)[4];
const float *pre_clip_pos;
bool has_so;
bool use_pre_clip_pos;
int pos_idx;
unsigned emitted_primitives;
unsigned generated_primitives;
unsigned stream;
};
static const struct pipe_stream_output_info *
draw_so_info(const struct draw_context *draw)
{
const struct pipe_stream_output_info *state = NULL;
if (draw->ms.mesh_shader)
return state;
if (draw->gs.geometry_shader) {
state = &draw->gs.geometry_shader->state.stream_output;
} else if (draw->tes.tess_eval_shader) {
state = &draw->tes.tess_eval_shader->state.stream_output;
} else {
state = &draw->vs.vertex_shader->state.stream_output;
}
return state;
}
static inline bool
draw_has_so(const struct draw_context *draw)
{
const struct pipe_stream_output_info *state = draw_so_info(draw);
if (state && state->num_outputs > 0)
return true;
return false;
}
void
draw_pt_so_emit_prepare(struct pt_so_emit *emit, bool use_pre_clip_pos)
{
struct draw_context *draw = emit->draw;
emit->use_pre_clip_pos = use_pre_clip_pos;
emit->has_so = draw_has_so(draw);
if (use_pre_clip_pos)
emit->pos_idx = draw_current_shader_position_output(draw);
/* if we have a state with outputs make sure we have
* buffers to output to */
if (emit->has_so) {
bool has_valid_buffer = false;
for (unsigned i = 0; i < draw->so.num_targets; ++i) {
if (draw->so.targets[i]) {
has_valid_buffer = true;
break;
}
}
emit->has_so = has_valid_buffer;
}
if (!emit->has_so)
return;
/* XXX: need to flush to get prim_vbuf.c to release its allocation??
*/
draw_do_flush(draw, DRAW_FLUSH_BACKEND);
}
static void
so_emit_prim(struct pt_so_emit *so,
unsigned *indices,
unsigned num_vertices)
{
unsigned input_vertex_stride = so->input_vertex_stride;
struct draw_context *draw = so->draw;
const float (*input_ptr)[4];
const float *pcp_ptr = NULL;
const struct pipe_stream_output_info *state = draw_so_info(draw);
int buffer_total_bytes[PIPE_MAX_SO_BUFFERS];
bool buffer_written[PIPE_MAX_SO_BUFFERS] = {0};
input_ptr = so->inputs;
if (so->use_pre_clip_pos)
pcp_ptr = so->pre_clip_pos;
++so->generated_primitives;
for (unsigned i = 0; i < draw->so.num_targets; i++) {
struct draw_so_target *target = draw->so.targets[i];
if (target) {
buffer_total_bytes[i] = target->internal_offset;
} else {
buffer_total_bytes[i] = 0;
}
}
/* check have we space to emit prim first - if not don't do anything */
for (unsigned slot = 0; slot < state->num_outputs; ++slot) {
int ob = state->output[slot].output_buffer;
if (state->output[slot].stream != so->stream)
continue;
/* If a buffer is missing then that's equivalent to an overflow */
if (!draw->so.targets[ob]) {
return;
}
buffer_written[ob] = true;
}
for (unsigned ob = 0; ob < draw->so.num_targets; ++ob) {
if (buffer_written[ob]) {
unsigned size_req = num_vertices * state->stride[ob] * sizeof(float);
if (buffer_total_bytes[ob] + size_req > draw->so.targets[ob]->target.buffer_size) {
return;
}
}
}
for (unsigned i = 0; i < num_vertices; ++i) {
const float (*input)[4];
const float *pre_clip_pos = NULL;
input = (const float (*)[4])(
(const char *)input_ptr + (indices[i] * input_vertex_stride));
if (pcp_ptr)
pre_clip_pos = (const float *)(
(const char *)pcp_ptr + (indices[i] * input_vertex_stride));
for (unsigned slot = 0; slot < state->num_outputs; ++slot) {
unsigned idx = state->output[slot].register_index;
unsigned start_comp = state->output[slot].start_component;
unsigned num_comps = state->output[slot].num_components;
unsigned stream = state->output[slot].stream;
if (stream != so->stream)
continue;
unsigned ob = state->output[slot].output_buffer;
float *buffer = (float *)((char *)draw->so.targets[ob]->mapping +
draw->so.targets[ob]->target.buffer_offset +
draw->so.targets[ob]->internal_offset) +
state->output[slot].dst_offset;
if (idx == so->pos_idx && pcp_ptr && so->stream == 0) {
memcpy(buffer, &pre_clip_pos[start_comp],
num_comps * sizeof(float));
} else {
memcpy(buffer, &input[idx][start_comp],
num_comps * sizeof(float));
}
#if 0
{
debug_printf("VERT[%d], stream = %d, offset = %d, slot[%d] sc = %d, num_c = %d, idx = %d = [",
i, stream,
draw->so.targets[ob]->internal_offset + (4 * state->output[slot].dst_offset),
slot, start_comp, num_comps, idx);
for (unsigned j = 0; j < num_comps; ++j) {
unsigned *ubuffer = (unsigned*)buffer;
debug_printf("%d (0x%x), ", ubuffer[j], ubuffer[j]);
}
debug_printf("]\n");
}
#endif
}
for (unsigned ob = 0; ob < draw->so.num_targets; ++ob) {
struct draw_so_target *target = draw->so.targets[ob];
if (target && buffer_written[ob]) {
target->internal_offset += state->stride[ob] * sizeof(float);
}
}
}
++so->emitted_primitives;
}
static void
so_point(struct pt_so_emit *so, int idx)
{
unsigned indices[1];
indices[0] = idx;
so_emit_prim(so, indices, 1);
}
static void
so_line(struct pt_so_emit *so, int i0, int i1)
{
unsigned indices[2];
indices[0] = i0;
indices[1] = i1;
so_emit_prim(so, indices, 2);
}
static void
so_tri(struct pt_so_emit *so, int i0, int i1, int i2)
{
unsigned indices[3];
indices[0] = i0;
indices[1] = i1;
indices[2] = i2;
so_emit_prim(so, indices, 3);
}
#define FUNC so_run_linear
#define GET_ELT(idx) (start + (idx))
#include "draw_so_emit_tmp.h"
#define FUNC so_run_elts
#define LOCAL_VARS const uint16_t *elts = input_prims->elts;
#define GET_ELT(idx) (elts[start + (idx)])
#include "draw_so_emit_tmp.h"
void
draw_pt_so_emit(struct pt_so_emit *emit,
int num_vertex_streams,
const struct draw_vertex_info *input_verts,
const struct draw_prim_info *input_prims)
{
struct draw_context *draw = emit->draw;
struct vbuf_render *render = draw->render;
if (!emit->has_so && num_vertex_streams == 1) {
if (draw->collect_primgen) {
unsigned total = 0;
for (unsigned i = 0; i < input_prims->primitive_count; i++) {
total +=
u_decomposed_prims_for_vertices(input_prims->prim,
input_prims->primitive_lengths[i]);
}
render->set_stream_output_info(render, 0, 0, total);
}
return;
}
if (!emit->has_so && !draw->collect_primgen)
return;
/* XXX: need to flush to get prim_vbuf.c to release its allocation??*/
draw_do_flush(draw, DRAW_FLUSH_BACKEND);
for (unsigned stream = 0; stream < num_vertex_streams; stream++) {
emit->emitted_primitives = 0;
emit->generated_primitives = 0;
if (emit->use_pre_clip_pos)
emit->pre_clip_pos = input_verts[stream].verts->clip_pos;
emit->input_vertex_stride = input_verts[stream].stride;
emit->inputs = (const float (*)[4])input_verts[stream].verts->data;
emit->stream = stream;
unsigned start, i;
for (start = i = 0; i < input_prims[stream].primitive_count;
start += input_prims[stream].primitive_lengths[i], i++) {
unsigned count = input_prims[stream].primitive_lengths[i];
if (input_prims->linear) {
so_run_linear(emit, &input_prims[stream], &input_verts[stream],
start, count);
} else {
so_run_elts(emit, &input_prims[stream], &input_verts[stream],
start, count);
}
}
render->set_stream_output_info(render,
stream,
emit->has_so ? emit->emitted_primitives : 0,
emit->generated_primitives);
}
}
struct pt_so_emit *
draw_pt_so_emit_create(struct draw_context *draw)
{
struct pt_so_emit *emit = CALLOC_STRUCT(pt_so_emit);
if (!emit)
return NULL;
emit->draw = draw;
return emit;
}
void
draw_pt_so_emit_destroy(struct pt_so_emit *emit)
{
FREE(emit);
}
+105
View File
@@ -0,0 +1,105 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Authors:
* Keith Whitwell <[email protected]>
*/
#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "draw/draw_pt.h"
#include "util/u_debug.h"
void
draw_pt_split_prim(enum mesa_prim prim, unsigned *first, unsigned *incr)
{
switch (prim) {
case MESA_PRIM_POINTS:
*first = 1;
*incr = 1;
break;
case MESA_PRIM_LINES:
*first = 2;
*incr = 2;
break;
case MESA_PRIM_LINE_STRIP:
case MESA_PRIM_LINE_LOOP:
*first = 2;
*incr = 1;
break;
case MESA_PRIM_LINES_ADJACENCY:
*first = 4;
*incr = 4;
break;
case MESA_PRIM_LINE_STRIP_ADJACENCY:
*first = 4;
*incr = 1;
break;
case MESA_PRIM_TRIANGLES:
*first = 3;
*incr = 3;
break;
case MESA_PRIM_TRIANGLES_ADJACENCY:
*first = 6;
*incr = 6;
break;
case MESA_PRIM_TRIANGLE_STRIP:
case MESA_PRIM_TRIANGLE_FAN:
case MESA_PRIM_POLYGON:
*first = 3;
*incr = 1;
break;
case MESA_PRIM_TRIANGLE_STRIP_ADJACENCY:
*first = 6;
*incr = 2;
break;
case MESA_PRIM_QUADS:
*first = 4;
*incr = 4;
break;
case MESA_PRIM_QUAD_STRIP:
*first = 4;
*incr = 2;
break;
default:
assert(0);
*first = 0;
*incr = 1; /* set to one so that count % incr works */
break;
}
}
unsigned
draw_pt_trim_count(unsigned count, unsigned first, unsigned incr)
{
if (count < first)
return 0;
return count - (count - first) % incr;
}
+277
View File
@@ -0,0 +1,277 @@
/*
* Mesa 3-D graphics library
*
* Copyright 2007-2008 VMware, Inc.
* Copyright (C) 2010 LunarG Inc.
*
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
*/
#include <stdbool.h>
#include "util/macros.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "draw/draw_pt.h"
#define SEGMENT_SIZE 1024
#define MAP_SIZE 256
struct vsplit_frontend {
struct draw_pt_front_end base;
struct draw_context *draw;
enum mesa_prim prim;
struct draw_pt_middle_end *middle;
unsigned max_vertices;
uint16_t segment_size;
/* buffers for splitting */
unsigned fetch_elts[SEGMENT_SIZE];
uint16_t draw_elts[SEGMENT_SIZE];
uint16_t identity_draw_elts[SEGMENT_SIZE];
struct {
/* map a fetch element to a draw element */
unsigned fetches[MAP_SIZE];
uint16_t draws[MAP_SIZE];
bool has_max_fetch;
uint16_t num_fetch_elts;
uint16_t num_draw_elts;
} cache;
};
static void
vsplit_clear_cache(struct vsplit_frontend *vsplit)
{
memset(vsplit->cache.fetches, 0xff, sizeof(vsplit->cache.fetches));
vsplit->cache.has_max_fetch = false;
vsplit->cache.num_fetch_elts = 0;
vsplit->cache.num_draw_elts = 0;
}
static void
vsplit_flush_cache(struct vsplit_frontend *vsplit, unsigned start, unsigned flags)
{
vsplit->middle->run(vsplit->middle, start,
vsplit->fetch_elts, vsplit->cache.num_fetch_elts,
vsplit->draw_elts, vsplit->cache.num_draw_elts, flags);
}
/**
* Add a fetch element and add it to the draw elements.
*/
static inline void
vsplit_add_cache(struct vsplit_frontend *vsplit, unsigned fetch)
{
unsigned hash;
hash = fetch % MAP_SIZE;
/* If the value isn't in the cache or it's an overflow due to the
* element bias */
if (vsplit->cache.fetches[hash] != fetch) {
/* update cache */
vsplit->cache.fetches[hash] = fetch;
vsplit->cache.draws[hash] = vsplit->cache.num_fetch_elts;
/* add fetch */
assert(vsplit->cache.num_fetch_elts < vsplit->segment_size);
vsplit->fetch_elts[vsplit->cache.num_fetch_elts++] = fetch;
}
vsplit->draw_elts[vsplit->cache.num_draw_elts++] = vsplit->cache.draws[hash];
}
/**
* Returns the base index to the elements array.
* The value is checked for integer overflow (not sure it can happen?).
*/
static inline unsigned
vsplit_get_base_idx(unsigned start, unsigned fetch)
{
return util_clamped_uadd(start, fetch);
}
static inline void
vsplit_add_cache_uint8(struct vsplit_frontend *vsplit, const uint8_t *elts,
unsigned start, unsigned fetch, int elt_bias)
{
struct draw_context *draw = vsplit->draw;
unsigned elt_idx;
elt_idx = vsplit_get_base_idx(start, fetch);
elt_idx = (unsigned)((int)(DRAW_GET_IDX(elts, elt_idx)) + elt_bias);
/* unlike the uint32_t case this can only happen with elt_bias */
if (elt_bias && elt_idx == DRAW_MAX_FETCH_IDX && !vsplit->cache.has_max_fetch) {
unsigned hash = elt_idx % MAP_SIZE;
vsplit->cache.fetches[hash] = 0;
vsplit->cache.has_max_fetch = true;
}
vsplit_add_cache(vsplit, elt_idx);
}
static inline void
vsplit_add_cache_uint16(struct vsplit_frontend *vsplit, const uint16_t *elts,
unsigned start, unsigned fetch, int elt_bias)
{
struct draw_context *draw = vsplit->draw;
unsigned elt_idx;
elt_idx = vsplit_get_base_idx(start, fetch);
elt_idx = (unsigned)((int)(DRAW_GET_IDX(elts, elt_idx)) + elt_bias);
/* unlike the uint32_t case this can only happen with elt_bias */
if (elt_bias && elt_idx == DRAW_MAX_FETCH_IDX && !vsplit->cache.has_max_fetch) {
unsigned hash = elt_idx % MAP_SIZE;
vsplit->cache.fetches[hash] = 0;
vsplit->cache.has_max_fetch = true;
}
vsplit_add_cache(vsplit, elt_idx);
}
/**
* Add a fetch element and add it to the draw elements. The fetch element is
* in full range (uint32_t).
*/
static inline void
vsplit_add_cache_uint32(struct vsplit_frontend *vsplit, const uint32_t *elts,
unsigned start, unsigned fetch, int elt_bias)
{
struct draw_context *draw = vsplit->draw;
unsigned elt_idx;
/*
* The final element index is just element index plus element bias.
*/
elt_idx = vsplit_get_base_idx(start, fetch);
elt_idx = (unsigned)((int)(DRAW_GET_IDX(elts, elt_idx)) + elt_bias);
/* Take care for DRAW_MAX_FETCH_IDX (since cache is initialized to -1). */
if (elt_idx == DRAW_MAX_FETCH_IDX && !vsplit->cache.has_max_fetch) {
unsigned hash = elt_idx % MAP_SIZE;
/* force update - any value will do except DRAW_MAX_FETCH_IDX */
vsplit->cache.fetches[hash] = 0;
vsplit->cache.has_max_fetch = true;
}
vsplit_add_cache(vsplit, elt_idx);
}
#define FUNC vsplit_run_linear
#include "draw_pt_vsplit_tmp.h"
#define FUNC vsplit_run_uint8
#define ELT_TYPE uint8_t
#define ADD_CACHE(vsplit, ib, start, fetch, bias) vsplit_add_cache_uint8(vsplit,ib,start,fetch,bias)
#include "draw_pt_vsplit_tmp.h"
#define FUNC vsplit_run_uint16
#define ELT_TYPE uint16_t
#define ADD_CACHE(vsplit, ib, start, fetch, bias) vsplit_add_cache_uint16(vsplit,ib,start,fetch, bias)
#include "draw_pt_vsplit_tmp.h"
#define FUNC vsplit_run_uint32
#define ELT_TYPE uint32_t
#define ADD_CACHE(vsplit, ib, start, fetch, bias) vsplit_add_cache_uint32(vsplit, ib, start, fetch, bias)
#include "draw_pt_vsplit_tmp.h"
static void
vsplit_prepare(struct draw_pt_front_end *frontend,
enum mesa_prim in_prim,
struct draw_pt_middle_end *middle,
unsigned opt)
{
struct vsplit_frontend *vsplit = (struct vsplit_frontend *) frontend;
switch (vsplit->draw->pt.user.eltSize) {
case 0:
vsplit->base.run = vsplit_run_linear;
break;
case 1:
vsplit->base.run = vsplit_run_uint8;
break;
case 2:
vsplit->base.run = vsplit_run_uint16;
break;
case 4:
vsplit->base.run = vsplit_run_uint32;
break;
default:
assert(0);
break;
}
/* split only */
vsplit->prim = in_prim;
vsplit->middle = middle;
middle->prepare(middle, vsplit->prim, opt, &vsplit->max_vertices);
vsplit->segment_size = MIN2(SEGMENT_SIZE, vsplit->max_vertices);
}
static void
vsplit_flush(struct draw_pt_front_end *frontend, unsigned flags)
{
struct vsplit_frontend *vsplit = (struct vsplit_frontend *) frontend;
if (flags & DRAW_FLUSH_STATE_CHANGE) {
vsplit->middle->finish(vsplit->middle);
vsplit->middle = NULL;
}
}
static void
vsplit_destroy(struct draw_pt_front_end *frontend)
{
FREE(frontend);
}
struct draw_pt_front_end *
draw_pt_vsplit(struct draw_context *draw)
{
struct vsplit_frontend *vsplit = CALLOC_STRUCT(vsplit_frontend);
if (!vsplit)
return NULL;
vsplit->base.prepare = vsplit_prepare;
vsplit->base.run = NULL;
vsplit->base.flush = vsplit_flush;
vsplit->base.destroy = vsplit_destroy;
vsplit->draw = draw;
for (unsigned i = 0; i < SEGMENT_SIZE; i++)
vsplit->identity_draw_elts[i] = i;
return &vsplit->base;
}
@@ -0,0 +1,305 @@
/*
* Mesa 3-D graphics library
*
* Copyright 2007-2008 VMware, Inc.
* Copyright (C) 2010 LunarG Inc.
*
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
*/
#ifdef ELT_TYPE
/**
* Fetch all elements in [min_index, max_index] with bias, and use the
* (rebased) index buffer as the draw elements.
*/
static bool
CONCAT2(vsplit_primitive_, ELT_TYPE)(struct vsplit_frontend *vsplit,
unsigned istart, unsigned icount)
{
struct draw_context *draw = vsplit->draw;
const ELT_TYPE *ib = (const ELT_TYPE *) draw->pt.user.elts;
const unsigned min_index = draw->pt.user.min_index;
const unsigned max_index = draw->pt.user.max_index;
const int elt_bias = draw->pt.user.eltBias;
unsigned fetch_start, fetch_count;
const uint16_t *draw_elts = NULL;
const unsigned start = istart;
const unsigned end = istart + icount;
/* If the index buffer overflows we'll need to run
* through the normal paths */
if (end >= draw->pt.user.eltMax ||
end < istart)
return false;
/* use the ib directly */
if (min_index == 0 && sizeof(ib[0]) == sizeof(draw_elts[0])) {
if (icount > vsplit->max_vertices)
return false;
for (unsigned i = 0; i < icount; i++) {
ELT_TYPE idx = DRAW_GET_IDX(ib, start + i);
if (idx < min_index || idx > max_index) {
debug_printf("warning: index out of range\n");
}
}
draw_elts = (const uint16_t *) (ib + istart);
} else {
/* have to go through vsplit->draw_elts */
if (icount > vsplit->segment_size)
return false;
}
/* this is faster only when we fetch less elements than the normal path */
if (max_index - min_index > icount - 1)
return false;
if (elt_bias < 0 && (int) min_index < -elt_bias)
return false;
/* why this check? */
for (unsigned i = 0; i < draw->pt.nr_vertex_elements; i++) {
if (draw->pt.vertex_element[i].instance_divisor)
return false;
}
fetch_start = min_index + elt_bias;
fetch_count = max_index - min_index + 1;
/* Check for overflow in the fetch_start */
if (fetch_start < min_index || fetch_start < elt_bias)
return false;
if (!draw_elts) {
if (min_index == 0) {
for (unsigned i = 0; i < icount; i++) {
ELT_TYPE idx = DRAW_GET_IDX(ib, i + start);
if (idx < min_index || idx > max_index) {
debug_printf("warning: index out of range\n");
}
vsplit->draw_elts[i] = (uint16_t) idx;
}
} else {
for (unsigned i = 0; i < icount; i++) {
ELT_TYPE idx = DRAW_GET_IDX(ib, i + start);
if (idx < min_index || idx > max_index) {
debug_printf("warning: index out of range\n");
}
vsplit->draw_elts[i] = (uint16_t) (idx - min_index);
}
}
draw_elts = vsplit->draw_elts;
}
return vsplit->middle->run_linear_elts(vsplit->middle,
fetch_start, fetch_count,
draw_elts, istart, icount);
}
/**
* Use the cache to prepare the fetch and draw elements, and flush.
*
* When spoken is TRUE, ispoken replaces istart; When close is TRUE, iclose is
* appended.
*/
static inline void
CONCAT2(vsplit_segment_cache_, ELT_TYPE)(struct vsplit_frontend *vsplit,
unsigned flags,
unsigned istart, unsigned icount,
bool spoken, unsigned ispoken,
bool close, unsigned iclose)
{
struct draw_context *draw = vsplit->draw;
const ELT_TYPE *ib = (const ELT_TYPE *) draw->pt.user.elts;
const int ibias = draw->pt.user.eltBias;
assert(icount + !!close <= vsplit->segment_size);
vsplit_clear_cache(vsplit);
spoken = !!spoken;
if (ibias == 0) {
if (spoken)
ADD_CACHE(vsplit, ib, 0, ispoken, 0);
for (unsigned i = spoken; i < icount; i++) {
ADD_CACHE(vsplit, ib, istart, i, 0);
}
if (close)
ADD_CACHE(vsplit, ib, 0, iclose, 0);
} else {
if (spoken)
ADD_CACHE(vsplit, ib, 0, ispoken, ibias);
for (unsigned i = spoken; i < icount; i++)
ADD_CACHE(vsplit, ib, istart, i, ibias);
if (close)
ADD_CACHE(vsplit, ib, 0, iclose, ibias);
}
vsplit_flush_cache(vsplit, istart, flags);
}
static void
CONCAT2(vsplit_segment_simple_, ELT_TYPE)(struct vsplit_frontend *vsplit,
unsigned flags,
unsigned istart,
unsigned icount)
{
CONCAT2(vsplit_segment_cache_, ELT_TYPE)(vsplit,
flags, istart, icount, false, 0, false, 0);
}
static void
CONCAT2(vsplit_segment_loop_, ELT_TYPE)(struct vsplit_frontend *vsplit,
unsigned flags,
unsigned istart,
unsigned icount,
unsigned i0)
{
const bool close_loop = ((flags) == DRAW_SPLIT_BEFORE);
CONCAT2(vsplit_segment_cache_, ELT_TYPE)(vsplit,
flags, istart, icount, false, 0, close_loop, i0);
}
static void
CONCAT2(vsplit_segment_fan_, ELT_TYPE)(struct vsplit_frontend *vsplit,
unsigned flags,
unsigned istart,
unsigned icount,
unsigned i0)
{
const bool use_spoken = (((flags) & DRAW_SPLIT_BEFORE) != 0);
CONCAT2(vsplit_segment_cache_, ELT_TYPE)(vsplit,
flags, istart, icount, use_spoken, i0, false, 0);
}
#define LOCAL_VARS \
struct vsplit_frontend *vsplit = (struct vsplit_frontend *) frontend; \
const enum mesa_prim prim = vsplit->prim; \
const unsigned max_count_simple = vsplit->segment_size; \
const unsigned max_count_loop = vsplit->segment_size - 1; \
const unsigned max_count_fan = vsplit->segment_size;
#define PRIMITIVE(istart, icount) \
CONCAT2(vsplit_primitive_, ELT_TYPE)(vsplit, istart, icount)
#else /* ELT_TYPE */
static void
vsplit_segment_simple_linear(struct vsplit_frontend *vsplit, unsigned flags,
unsigned istart, unsigned icount)
{
assert(icount <= vsplit->max_vertices);
vsplit->middle->run_linear(vsplit->middle, istart, icount, flags);
}
static void
vsplit_segment_loop_linear(struct vsplit_frontend *vsplit, unsigned flags,
unsigned istart, unsigned icount, unsigned i0)
{
bool close_loop = (flags == DRAW_SPLIT_BEFORE);
unsigned nr;
assert(icount + !!close_loop <= vsplit->segment_size);
/* need to draw the sections of the line loop as line strips */
flags |= DRAW_LINE_LOOP_AS_STRIP;
if (close_loop) {
for (nr = 0; nr < icount; nr++)
vsplit->fetch_elts[nr] = istart + nr;
vsplit->fetch_elts[nr++] = i0;
vsplit->middle->run(vsplit->middle, istart, vsplit->fetch_elts, nr,
vsplit->identity_draw_elts, nr, flags);
} else {
vsplit->middle->run_linear(vsplit->middle, istart, icount, flags);
}
}
static void
vsplit_segment_fan_linear(struct vsplit_frontend *vsplit, unsigned flags,
unsigned istart, unsigned icount, unsigned i0)
{
bool use_spoken = ((flags & DRAW_SPLIT_BEFORE) != 0);
unsigned nr = 0;
assert(icount <= vsplit->segment_size);
if (use_spoken) {
/* replace istart by i0 */
vsplit->fetch_elts[nr++] = i0;
for (unsigned i = 1 ; i < icount; i++)
vsplit->fetch_elts[nr++] = istart + i;
vsplit->middle->run(vsplit->middle, istart, vsplit->fetch_elts, nr,
vsplit->identity_draw_elts, nr, flags);
} else {
vsplit->middle->run_linear(vsplit->middle, istart, icount, flags);
}
}
#define LOCAL_VARS \
struct vsplit_frontend *vsplit = (struct vsplit_frontend *) frontend; \
const enum mesa_prim prim = vsplit->prim; \
const unsigned max_count_simple = vsplit->max_vertices; \
const unsigned max_count_loop = vsplit->segment_size - 1; \
const unsigned max_count_fan = vsplit->segment_size;
#define PRIMITIVE(istart, icount) false
#define ELT_TYPE linear
#endif /* ELT_TYPE */
#define FUNC_VARS \
struct draw_pt_front_end *frontend, \
unsigned start, \
unsigned count
#define SEGMENT_SIMPLE(flags, istart, icount) \
CONCAT2(vsplit_segment_simple_, ELT_TYPE)(vsplit, flags, istart, icount)
#define SEGMENT_LOOP(flags, istart, icount, i0) \
CONCAT2(vsplit_segment_loop_, ELT_TYPE)(vsplit, flags, istart, icount, i0)
#define SEGMENT_FAN(flags, istart, icount, i0) \
CONCAT2(vsplit_segment_fan_, ELT_TYPE)(vsplit, flags, istart, icount, i0)
#include "draw_split_tmp.h"
#undef ELT_TYPE
#undef ADD_CACHE
@@ -0,0 +1,31 @@
#define FUNC_VARS \
struct pt_so_emit *so, \
const struct draw_prim_info *input_prims, \
const struct draw_vertex_info *input_verts, \
unsigned start, \
unsigned count
#define FUNC_ENTER \
/* declare more local vars */ \
const enum mesa_prim prim = input_prims->prim; \
const unsigned prim_flags = input_prims->flags; \
const bool quads_flatshade_last = false; \
const bool last_vertex_last = !so->draw->rasterizer->flatshade_first; \
do { \
switch (prim) { \
case MESA_PRIM_LINES_ADJACENCY: \
case MESA_PRIM_LINE_STRIP_ADJACENCY: \
case MESA_PRIM_TRIANGLES_ADJACENCY: \
case MESA_PRIM_TRIANGLE_STRIP_ADJACENCY: \
assert(!"unexpected primitive type in stream output"); \
return; \
default: \
break; \
} \
} while (0)
#define POINT(i0) so_point(so,i0)
#define LINE(flags,i0,i1) so_line(so,i0,i1)
#define TRIANGLE(flags,i0,i1,i2) so_tri(so,i0,i1,i2)
#include "draw_decompose_tmp.h"
+176
View File
@@ -0,0 +1,176 @@
/*
* Mesa 3-D graphics library
*
* Copyright 2008 VMware, Inc.
* Copyright (C) 2010 LunarG Inc.
*
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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 void
FUNC(FUNC_VARS)
{
unsigned first, incr;
LOCAL_VARS
/*
* prim, start, count, and max_count_{simple,loop,fan} should have been
* defined
*/
if (0) {
debug_printf("%s: prim 0x%x, start %d, count %d, max_count_simple %d, "
"max_count_loop %d, max_count_fan %d\n",
__func__, prim, start, count, max_count_simple,
max_count_loop, max_count_fan);
}
if (prim == MESA_PRIM_PATCHES) {
first = vsplit->draw->pt.vertices_per_patch;
incr = vsplit->draw->pt.vertices_per_patch;
} else
draw_pt_split_prim(prim, &first, &incr);
/* sanitize primitive length */
count = draw_pt_trim_count(count, first, incr);
if (count < first)
return;
/* try flushing the entire primitive */
if (PRIMITIVE(start, count))
return;
/* must be able to at least flush two complete primitives */
assert(max_count_simple >= first + incr &&
max_count_loop >= first + incr &&
max_count_fan >= first + incr);
/* no splitting required */
if (count <= max_count_simple) {
SEGMENT_SIMPLE(0x0, start, count);
} else {
const unsigned rollback = first - incr;
unsigned flags = DRAW_SPLIT_AFTER, seg_start = 0, seg_max;
/*
* Both count and seg_max below are explicitly trimmed. Because
*
* seg_start = N * (seg_max - rollback) = N' * incr,
*
* we have
*
* remaining = count - seg_start = first + N'' * incr.
*
* That is, remaining is implicitly trimmed.
*/
switch (prim) {
case MESA_PRIM_PATCHES:
case MESA_PRIM_POINTS:
case MESA_PRIM_LINES:
case MESA_PRIM_LINE_STRIP:
case MESA_PRIM_TRIANGLES:
case MESA_PRIM_TRIANGLE_STRIP:
case MESA_PRIM_QUADS:
case MESA_PRIM_QUAD_STRIP:
case MESA_PRIM_LINES_ADJACENCY:
case MESA_PRIM_LINE_STRIP_ADJACENCY:
case MESA_PRIM_TRIANGLES_ADJACENCY:
case MESA_PRIM_TRIANGLE_STRIP_ADJACENCY:
seg_max =
draw_pt_trim_count(MIN2(max_count_simple, count), first, incr);
if (prim == MESA_PRIM_TRIANGLE_STRIP ||
prim == MESA_PRIM_TRIANGLE_STRIP_ADJACENCY) {
/* make sure we flush even number of triangles at a time */
if (seg_max < count && !(((seg_max - first) / incr) & 1))
seg_max -= incr;
}
do {
const unsigned remaining = count - seg_start;
if (remaining > seg_max) {
SEGMENT_SIMPLE(flags, start + seg_start, seg_max);
seg_start += seg_max - rollback;
flags |= DRAW_SPLIT_BEFORE;
} else {
flags &= ~DRAW_SPLIT_AFTER;
SEGMENT_SIMPLE(flags, start + seg_start, remaining);
seg_start += remaining;
}
} while (seg_start < count);
break;
case MESA_PRIM_LINE_LOOP:
seg_max =
draw_pt_trim_count(MIN2(max_count_loop, count), first, incr);
do {
const unsigned remaining = count - seg_start;
if (remaining > seg_max) {
SEGMENT_LOOP(flags, start + seg_start, seg_max, start);
seg_start += seg_max - rollback;
flags |= DRAW_SPLIT_BEFORE;
} else {
flags &= ~DRAW_SPLIT_AFTER;
SEGMENT_LOOP(flags, start + seg_start, remaining, start);
seg_start += remaining;
}
} while (seg_start < count);
break;
case MESA_PRIM_TRIANGLE_FAN:
case MESA_PRIM_POLYGON:
seg_max =
draw_pt_trim_count(MIN2(max_count_fan, count), first, incr);
do {
const unsigned remaining = count - seg_start;
if (remaining > seg_max) {
SEGMENT_FAN(flags, start + seg_start, seg_max, start);
seg_start += seg_max - rollback;
flags |= DRAW_SPLIT_BEFORE;
} else {
flags &= ~DRAW_SPLIT_AFTER;
SEGMENT_FAN(flags, start + seg_start, remaining, start);
seg_start += remaining;
}
} while (seg_start < count);
break;
default:
assert(0);
break;
}
}
}
#undef FUNC
#undef FUNC_VARS
#undef LOCAL_VARS
#undef PRIMITIVE
#undef SEGMENT_SIMPLE
#undef SEGMENT_LOOP
#undef SEGMENT_FAN
+667
View File
@@ -0,0 +1,667 @@
/**************************************************************************
*
* Copyright 2020 Red Hat.
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
*
**************************************************************************/
#include "draw_tess.h"
#if DRAW_LLVM_AVAILABLE
#include "draw_llvm.h"
#endif
#include "tessellator/p_tessellator.h"
#include "nir/nir_to_tgsi_info.h"
#include "util/u_prim.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/ralloc.h"
#if DRAW_LLVM_AVAILABLE
static inline int
draw_tes_get_input_index(int semantic, int index,
const struct tgsi_shader_info *input_info)
{
int i;
const uint8_t *input_semantic_names = input_info->output_semantic_name;
const uint8_t *input_semantic_indices = input_info->output_semantic_index;
for (i = 0; i < PIPE_MAX_SHADER_OUTPUTS; i++) {
if (input_semantic_names[i] == semantic &&
input_semantic_indices[i] == index)
return i;
}
return -1;
}
#define DEBUG_INPUTS 0
static void
llvm_fetch_tcs_input(struct draw_tess_ctrl_shader *shader,
const struct draw_prim_info *input_prim_info,
unsigned prim_id,
unsigned num_vertices)
{
const float (*input_ptr)[4];
float (*input_data)[32][NUM_TCS_INPUTS][TGSI_NUM_CHANNELS] = &shader->tcs_input->data;
unsigned slot, i;
int vs_slot;
unsigned input_vertex_stride = shader->input_vertex_stride;
input_ptr = shader->input;
for (i = 0; i < num_vertices; i++) {
const float (*input)[4];
int vertex_idx = prim_id * num_vertices + i;
if (input_prim_info->linear == false)
vertex_idx = input_prim_info->elts[vertex_idx];
#if DEBUG_INPUTS
debug_printf("%d) tcs vertex index = %d (prim idx = %d)\n",
i, prim_id, 0);
#endif
input = (const float (*)[4])((const char *)input_ptr + (vertex_idx * input_vertex_stride));
for (slot = 0, vs_slot = 0; slot < shader->info.num_inputs; ++slot) {
vs_slot = draw_tes_get_input_index(
shader->info.input_semantic_name[slot],
shader->info.input_semantic_index[slot],
shader->input_info);
if (vs_slot < 0) {
debug_printf("VS/TCS signature mismatch!\n");
(*input_data)[i][slot][0] = 0;
(*input_data)[i][slot][1] = 0;
(*input_data)[i][slot][2] = 0;
(*input_data)[i][slot][3] = 0;
} else {
(*input_data)[i][slot][0] = input[vs_slot][0];
(*input_data)[i][slot][1] = input[vs_slot][1];
(*input_data)[i][slot][2] = input[vs_slot][2];
(*input_data)[i][slot][3] = input[vs_slot][3];
#if DEBUG_INPUTS
debug_printf("\t\t%p = %f %f %f %f\n", &(*input_data)[i][slot][0],
(*input_data)[i][slot][0],
(*input_data)[i][slot][1],
(*input_data)[i][slot][2],
(*input_data)[i][slot][3]);
#endif
++vs_slot;
}
}
}
}
#define DEBUG_OUTPUTS 0
static void
llvm_store_tcs_output(struct draw_tess_ctrl_shader *shader,
unsigned prim_id,
struct draw_vertex_info *output_verts,
unsigned vert_start)
{
float (*output_ptr)[4];
float (*output_data)[32][PIPE_MAX_SHADER_INPUTS][TGSI_NUM_CHANNELS] = &shader->tcs_output->data;
unsigned slot, i;
unsigned num_vertices = shader->vertices_out;
char *output = (char *)output_verts->verts->data;
output += vert_start * output_verts->stride;
for (i = 0; i < num_vertices; i++) {
#if DEBUG_OUTPUTS
debug_printf("%d) tcs store vertex index = %d (prim idx = %d)\n",
i, prim_id, 0);
#endif
output_ptr = (float(*)[4])(output + (i * output_verts->stride));
for (slot = 0; slot < shader->info.num_outputs; ++slot) {
output_ptr[slot][0] = (*output_data)[i][slot][0];
output_ptr[slot][1] = (*output_data)[i][slot][1];
output_ptr[slot][2] = (*output_data)[i][slot][2];
output_ptr[slot][3] = (*output_data)[i][slot][3];
#if DEBUG_OUTPUTS
debug_printf("\t\t%p = %f %f %f %f\n",
&output_ptr[slot][0],
output_ptr[slot][0],
output_ptr[slot][1],
output_ptr[slot][2],
output_ptr[slot][3]);
#endif
}
}
}
static void
llvm_tcs_run(struct draw_tess_ctrl_shader *shader, uint32_t prim_id)
{
shader->current_variant->jit_func(shader->jit_resources,
shader->tcs_input->data, shader->tcs_output->data, prim_id,
shader->draw->pt.vertices_per_patch, shader->draw->pt.user.viewid);
}
#endif
/**
* Execute tess ctrl shader.
*/
int draw_tess_ctrl_shader_run(struct draw_tess_ctrl_shader *shader,
const struct draw_vertex_info *input_verts,
const struct draw_prim_info *input_prims,
const struct tgsi_shader_info *input_info,
struct draw_vertex_info *output_verts,
struct draw_prim_info *output_prims)
{
const float (*input)[4] = (const float (*)[4])input_verts->verts->data;
unsigned num_outputs = draw_total_tcs_outputs(shader->draw);
unsigned input_stride = input_verts->vertex_size;
unsigned vertex_size = sizeof(struct vertex_header) + num_outputs * 4 * sizeof(float);
unsigned num_patches = input_prims->count / shader->draw->pt.vertices_per_patch;
output_verts->vertex_size = vertex_size;
output_verts->stride = output_verts->vertex_size;
output_verts->verts = NULL;
output_verts->count = 0;
shader->input = input;
shader->input_vertex_stride = input_stride;
shader->input_info = input_info;
output_prims->linear = true;
output_prims->start = input_prims->start;
output_prims->elts = NULL;
output_prims->count = 0;
output_prims->prim = MESA_PRIM_PATCHES;
output_prims->flags = 0;
output_prims->primitive_lengths = NULL;
output_prims->primitive_count = 0;
if (shader->draw->collect_statistics) {
shader->draw->statistics.hs_invocations += num_patches;
}
#if DRAW_LLVM_AVAILABLE
unsigned first_patch = input_prims->start / shader->draw->pt.vertices_per_patch;
for (unsigned i = 0; i < num_patches; i++) {
uint32_t vert_start = output_verts->count;
output_verts->count += shader->vertices_out;
llvm_fetch_tcs_input(shader, input_prims, i, shader->draw->pt.vertices_per_patch);
llvm_tcs_run(shader, first_patch + i);
uint32_t old_verts = util_align_npot(vert_start, 16);
uint32_t new_verts = util_align_npot(output_verts->count, 16);
uint32_t old_size = output_verts->vertex_size * old_verts;
uint32_t new_size = output_verts->vertex_size * new_verts;
output_verts->verts = REALLOC(output_verts->verts, old_size, new_size);
llvm_store_tcs_output(shader, i, output_verts, vert_start);
}
#endif
output_prims->primitive_count = num_patches;
return 0;
}
#if DRAW_LLVM_AVAILABLE
#define DEBUG_INPUTS 0
static void
llvm_fetch_tes_input(struct draw_tess_eval_shader *shader,
const struct draw_prim_info *input_prim_info,
unsigned prim_id,
unsigned num_vertices)
{
const float (*input_ptr)[4];
float (*input_data)[32][PIPE_MAX_SHADER_INPUTS][TGSI_NUM_CHANNELS] = &shader->tes_input->data;
unsigned slot, i;
int vs_slot;
unsigned input_vertex_stride = shader->input_vertex_stride;
input_ptr = shader->input;
for (i = 0; i < num_vertices; i++) {
const float (*input)[4];
int vertex_idx = prim_id * num_vertices + i;
if (input_prim_info->linear == false)
vertex_idx = input_prim_info->elts[vertex_idx];
#if DEBUG_INPUTS
debug_printf("%d) tes vertex index = %d (prim idx = %d)\n",
i, prim_id, 0);
#endif
input = (const float (*)[4])((const char *)input_ptr + (vertex_idx * input_vertex_stride));
for (slot = 0, vs_slot = 0; slot < shader->info.num_inputs; ++slot) {
vs_slot = draw_tes_get_input_index(
shader->info.input_semantic_name[slot],
shader->info.input_semantic_index[slot],
shader->input_info);
if (vs_slot < 0) {
debug_printf("TCS/TES signature mismatch!\n");
(*input_data)[i][slot][0] = 0;
(*input_data)[i][slot][1] = 0;
(*input_data)[i][slot][2] = 0;
(*input_data)[i][slot][3] = 0;
} else {
(*input_data)[i][slot][0] = input[vs_slot][0];
(*input_data)[i][slot][1] = input[vs_slot][1];
(*input_data)[i][slot][2] = input[vs_slot][2];
(*input_data)[i][slot][3] = input[vs_slot][3];
#if DEBUG_INPUTS
debug_printf("\t\t%p = %f %f %f %f\n",
&input[vs_slot][0],
(*input_data)[i][slot][0],
(*input_data)[i][slot][1],
(*input_data)[i][slot][2],
(*input_data)[i][slot][3]);
#endif
++vs_slot;
}
}
}
}
static void
llvm_fetch_tess_factors(struct draw_tess_eval_shader *shader,
unsigned patch_id,
unsigned num_vertices,
struct pipe_tessellation_factors *factors)
{
int outer_slot = draw_tes_get_input_index(
TGSI_SEMANTIC_TESSOUTER, 0, shader->input_info);
int inner_slot = draw_tes_get_input_index(
TGSI_SEMANTIC_TESSINNER, 0, shader->input_info);
const float (*input_ptr)[4];
const float (*input)[4];
input_ptr = shader->input;
input = (const float (*)[4])((const char *)input_ptr + ((patch_id * num_vertices) * shader->input_vertex_stride));
if (outer_slot != -1) {
for (unsigned i = 0; i < 4; i++)
factors->outer_tf[i] = input[outer_slot][i];
} else {
for (unsigned i = 0; i < 4; i++)
factors->outer_tf[i] = shader->draw->default_outer_tess_level[i];
}
if (inner_slot != -1) {
for (unsigned i = 0; i < 2; i++)
factors->inner_tf[i] = input[inner_slot][i];
} else {
for (unsigned i = 0; i < 2; i++)
factors->inner_tf[i] = shader->draw->default_inner_tess_level[i];
}
}
static void
llvm_tes_run(struct draw_tess_eval_shader *shader,
uint32_t prim_id,
uint32_t patch_vertices_in,
struct pipe_tessellator_data *tess_data,
struct pipe_tessellation_factors *tess_factors,
struct vertex_header *output)
{
shader->current_variant->jit_func(shader->jit_resources,
shader->tes_input->data, output, prim_id,
tess_data->num_domain_points, tess_data->domain_points_u, tess_data->domain_points_v,
tess_factors->outer_tf, tess_factors->inner_tf, patch_vertices_in,
shader->draw->pt.user.viewid);
}
#endif
/**
* Execute tess eval shader.
*/
int draw_tess_eval_shader_run(struct draw_tess_eval_shader *shader,
unsigned num_input_vertices_per_patch,
const struct draw_vertex_info *input_verts,
const struct draw_prim_info *input_prims,
const struct tgsi_shader_info *input_info,
struct draw_vertex_info *output_verts,
struct draw_prim_info *output_prims,
uint32_t **patch_lengths,
uint16_t **elts_out)
{
const float (*input)[4] = (const float (*)[4])input_verts->verts->data;
unsigned num_outputs = draw_total_tes_outputs(shader->draw);
unsigned input_stride = input_verts->vertex_size;
unsigned vertex_size = sizeof(struct vertex_header) + num_outputs * 4 * sizeof(float);
uint16_t *elts = NULL;
output_verts->vertex_size = vertex_size;
output_verts->stride = output_verts->vertex_size;
output_verts->count = 0;
output_verts->verts = NULL;
output_prims->linear = false;
output_prims->start = 0;
output_prims->elts = NULL;
output_prims->count = 0;
output_prims->prim = get_tes_output_prim(shader);
output_prims->flags = 0;
output_prims->primitive_lengths = NULL;
output_prims->primitive_count = 0;
if (patch_lengths) {
*patch_lengths = MALLOC(input_prims->primitive_count * sizeof(uint32_t));
}
shader->input = input;
shader->input_vertex_stride = input_stride;
shader->input_info = input_info;
#if DRAW_LLVM_AVAILABLE
struct pipe_tessellation_factors factors;
struct pipe_tessellator_data data = { 0 };
struct pipe_tessellator *ptess = p_tess_init(shader->prim_mode,
shader->spacing,
!shader->vertex_order_cw,
shader->point_mode);
unsigned first_patch = input_prims->start / shader->draw->pt.vertices_per_patch;
for (unsigned i = 0; i < input_prims->primitive_count; i++) {
uint32_t vert_start = output_verts->count;
uint32_t prim_start = output_prims->primitive_count;
uint32_t elt_start = output_prims->count;
llvm_fetch_tess_factors(shader, i, num_input_vertices_per_patch, &factors);
/**
* Tessellate with the factors for this primitive.
* Make sure subnormals are not flushed to zero during tessellation.
* This is the behavior required by D3D11. OpenGL doesn't care.
*/
unsigned fpstate = util_fpstate_get();
util_fpstate_set(shader->draw->fpstate); /* do not flush subnormals */
p_tessellate(ptess, &factors, &data);
util_fpstate_set(fpstate); /* flush subnormals again */
if (data.num_domain_points == 0)
continue;
uint32_t old_verts = vert_start;
uint32_t new_verts = vert_start + util_align_npot(data.num_domain_points, 4);
uint32_t old_size = output_verts->vertex_size * old_verts;
uint32_t new_size = output_verts->vertex_size * new_verts;
output_verts->verts = REALLOC(output_verts->verts, old_size, new_size);
output_verts->count += data.num_domain_points;
output_prims->count += data.num_indices;
elts = REALLOC(elts, elt_start * sizeof(uint16_t),
output_prims->count * sizeof(uint16_t));
for (uint32_t i = 0; i < data.num_indices; i++)
elts[elt_start + i] = vert_start + data.indices[i];
llvm_fetch_tes_input(shader, input_prims, i, num_input_vertices_per_patch);
/* run once per primitive? */
char *output = (char *)output_verts->verts;
output += vert_start * vertex_size;
llvm_tes_run(shader, first_patch + i, num_input_vertices_per_patch, &data, &factors, (struct vertex_header *)output);
if (shader->draw->collect_statistics) {
shader->draw->statistics.ds_invocations += data.num_domain_points;
}
uint32_t prim_len = u_prim_vertex_count(output_prims->prim)->min;
uint32_t prims_per_patch = data.num_indices / prim_len;
output_prims->primitive_count += prims_per_patch;
if (patch_lengths) {
(*patch_lengths)[i] = prims_per_patch;
}
output_prims->primitive_lengths = REALLOC(output_prims->primitive_lengths, prim_start * sizeof(uint32_t),
output_prims->primitive_count * sizeof(uint32_t));
for (uint32_t i = prim_start; i < output_prims->primitive_count; i++) {
output_prims->primitive_lengths[i] = prim_len;
}
}
p_tess_destroy(ptess);
#endif
*elts_out = elts;
output_prims->elts = elts;
return 0;
}
struct draw_tess_ctrl_shader *
draw_create_tess_ctrl_shader(struct draw_context *draw,
const struct pipe_shader_state *state)
{
#if DRAW_LLVM_AVAILABLE
bool use_llvm = draw->llvm != NULL;
struct llvm_tess_ctrl_shader *llvm_tcs = NULL;
#endif
struct draw_tess_ctrl_shader *tcs;
#if DRAW_LLVM_AVAILABLE
if (use_llvm) {
llvm_tcs = CALLOC_STRUCT(llvm_tess_ctrl_shader);
if (!llvm_tcs)
return NULL;
tcs = &llvm_tcs->base;
list_inithead(&llvm_tcs->variants.list);
} else
#endif
{
tcs = CALLOC_STRUCT(draw_tess_ctrl_shader);
}
if (!tcs)
return NULL;
tcs->draw = draw;
tcs->state = *state;
nir_tgsi_scan_shader(state->ir.nir, &tcs->info, true);
tcs->vector_length = 4;
tcs->vertices_out = tcs->info.properties[TGSI_PROPERTY_TCS_VERTICES_OUT];
#if DRAW_LLVM_AVAILABLE
if (use_llvm) {
tcs->tcs_input = align_malloc(sizeof(struct draw_tcs_inputs), 16);
memset(tcs->tcs_input, 0, sizeof(struct draw_tcs_inputs));
tcs->tcs_output = align_malloc(sizeof(struct draw_tcs_outputs), 16);
memset(tcs->tcs_output, 0, sizeof(struct draw_tcs_outputs));
tcs->jit_resources = &draw->llvm->jit_resources[MESA_SHADER_TESS_CTRL];
llvm_tcs->variant_key_size =
draw_tcs_llvm_variant_key_size(
tcs->info.file_max[TGSI_FILE_SAMPLER]+1,
tcs->info.file_max[TGSI_FILE_SAMPLER_VIEW]+1,
tcs->info.file_max[TGSI_FILE_IMAGE]+1);
}
#endif
return tcs;
}
void draw_bind_tess_ctrl_shader(struct draw_context *draw,
struct draw_tess_ctrl_shader *dtcs)
{
draw_do_flush(draw, DRAW_FLUSH_STATE_CHANGE);
if (dtcs) {
draw->tcs.tess_ctrl_shader = dtcs;
} else {
draw->tcs.tess_ctrl_shader = NULL;
}
}
void draw_delete_tess_ctrl_shader(struct draw_context *draw,
struct draw_tess_ctrl_shader *dtcs)
{
if (!dtcs)
return;
#if DRAW_LLVM_AVAILABLE
if (draw->llvm) {
struct llvm_tess_ctrl_shader *shader = llvm_tess_ctrl_shader(dtcs);
struct draw_tcs_llvm_variant_list_item *li, *next;
LIST_FOR_EACH_ENTRY_SAFE(li, next, &shader->variants.list, list) {
draw_tcs_llvm_destroy_variant(li->base);
}
assert(shader->variants_cached == 0);
align_free(dtcs->tcs_input);
align_free(dtcs->tcs_output);
}
#endif
if (dtcs->state.type == PIPE_SHADER_IR_NIR && dtcs->state.ir.nir)
ralloc_free(dtcs->state.ir.nir);
FREE(dtcs);
}
#if DRAW_LLVM_AVAILABLE
void draw_tcs_set_current_variant(struct draw_tess_ctrl_shader *shader,
struct draw_tcs_llvm_variant *variant)
{
shader->current_variant = variant;
}
#endif
struct draw_tess_eval_shader *
draw_create_tess_eval_shader(struct draw_context *draw,
const struct pipe_shader_state *state)
{
#if DRAW_LLVM_AVAILABLE
bool use_llvm = draw->llvm != NULL;
struct llvm_tess_eval_shader *llvm_tes = NULL;
#endif
struct draw_tess_eval_shader *tes;
#if DRAW_LLVM_AVAILABLE
if (use_llvm) {
llvm_tes = CALLOC_STRUCT(llvm_tess_eval_shader);
if (!llvm_tes)
return NULL;
tes = &llvm_tes->base;
list_inithead(&llvm_tes->variants.list);
} else
#endif
{
tes = CALLOC_STRUCT(draw_tess_eval_shader);
}
if (!tes)
return NULL;
tes->draw = draw;
tes->state = *state;
nir_tgsi_scan_shader(state->ir.nir, &tes->info, true);
tes->prim_mode = tes->info.properties[TGSI_PROPERTY_TES_PRIM_MODE];
tes->spacing = tes->info.properties[TGSI_PROPERTY_TES_SPACING];
tes->vertex_order_cw = tes->info.properties[TGSI_PROPERTY_TES_VERTEX_ORDER_CW];
tes->point_mode = tes->info.properties[TGSI_PROPERTY_TES_POINT_MODE];
tes->vector_length = 4;
tes->position_output = -1;
bool found_clipvertex = false;
for (unsigned i = 0; i < tes->info.num_outputs; i++) {
if (tes->info.output_semantic_name[i] == TGSI_SEMANTIC_POSITION &&
tes->info.output_semantic_index[i] == 0)
tes->position_output = i;
if (tes->info.output_semantic_name[i] == TGSI_SEMANTIC_VIEWPORT_INDEX)
tes->viewport_index_output = i;
if (tes->info.output_semantic_name[i] == TGSI_SEMANTIC_CLIPVERTEX &&
tes->info.output_semantic_index[i] == 0) {
found_clipvertex = true;
tes->clipvertex_output = i;
}
if (tes->info.output_semantic_name[i] == TGSI_SEMANTIC_CLIPDIST) {
assert(tes->info.output_semantic_index[i] <
PIPE_MAX_CLIP_OR_CULL_DISTANCE_ELEMENT_COUNT);
tes->ccdistance_output[tes->info.output_semantic_index[i]] = i;
}
}
if (!found_clipvertex)
tes->clipvertex_output = tes->position_output;
#if DRAW_LLVM_AVAILABLE
if (use_llvm) {
tes->tes_input = align_malloc(sizeof(struct draw_tes_inputs), 16);
memset(tes->tes_input, 0, sizeof(struct draw_tes_inputs));
tes->jit_resources = &draw->llvm->jit_resources[MESA_SHADER_TESS_EVAL];
llvm_tes->variant_key_size =
draw_tes_llvm_variant_key_size(
tes->info.file_max[TGSI_FILE_SAMPLER]+1,
tes->info.file_max[TGSI_FILE_SAMPLER_VIEW]+1,
tes->info.file_max[TGSI_FILE_IMAGE]+1);
}
#endif
return tes;
}
void draw_bind_tess_eval_shader(struct draw_context *draw,
struct draw_tess_eval_shader *dtes)
{
draw_do_flush(draw, DRAW_FLUSH_STATE_CHANGE);
if (dtes) {
draw->tes.tess_eval_shader = dtes;
draw->tes.num_tes_outputs = dtes->info.num_outputs;
draw->tes.position_output = dtes->position_output;
draw->tes.clipvertex_output = dtes->clipvertex_output;
} else {
draw->tes.tess_eval_shader = NULL;
}
}
void draw_delete_tess_eval_shader(struct draw_context *draw,
struct draw_tess_eval_shader *dtes)
{
if (!dtes)
return;
#if DRAW_LLVM_AVAILABLE
if (draw->llvm) {
struct llvm_tess_eval_shader *shader = llvm_tess_eval_shader(dtes);
struct draw_tes_llvm_variant_list_item *li, *next;
LIST_FOR_EACH_ENTRY_SAFE(li, next, &shader->variants.list, list) {
draw_tes_llvm_destroy_variant(li->base);
}
assert(shader->variants_cached == 0);
align_free(dtes->tes_input);
}
#endif
if (dtes->state.type == PIPE_SHADER_IR_NIR && dtes->state.ir.nir)
ralloc_free(dtes->state.ir.nir);
FREE(dtes);
}
#if DRAW_LLVM_AVAILABLE
void draw_tes_set_current_variant(struct draw_tess_eval_shader *shader,
struct draw_tes_llvm_variant *variant)
{
shader->current_variant = variant;
}
#endif
enum mesa_prim get_tes_output_prim(struct draw_tess_eval_shader *shader)
{
if (shader->point_mode)
return MESA_PRIM_POINTS;
else if (shader->prim_mode == MESA_PRIM_LINES)
return MESA_PRIM_LINES;
else
return MESA_PRIM_TRIANGLES;
}
+130
View File
@@ -0,0 +1,130 @@
/**************************************************************************
*
* Copyright 2020 Red Hat.
* 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, sublicense,
* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
*
**************************************************************************/
#ifndef DRAW_TESS_H
#define DRAW_TESS_H
#include "draw_context.h"
#include "draw_private.h"
#include "tgsi/tgsi_scan.h"
struct draw_context;
#if DRAW_LLVM_AVAILABLE
#define NUM_PATCH_INPUTS 32
#define NUM_TCS_INPUTS (PIPE_MAX_SHADER_INPUTS - NUM_PATCH_INPUTS)
struct draw_tcs_inputs {
/* num vertices per prim */
float data[32][NUM_TCS_INPUTS][4];
};
struct draw_tcs_outputs {
/* num vertices per prim */
float data[32][PIPE_MAX_SHADER_INPUTS][4];
};
struct draw_tes_inputs {
/* num vertices per prim */
float data[32][PIPE_MAX_SHADER_INPUTS][4];
};
#endif
struct draw_tess_ctrl_shader {
struct draw_context *draw;
struct pipe_shader_state state;
struct tgsi_shader_info info;
unsigned vector_length;
unsigned vertices_out;
unsigned input_vertex_stride;
const float (*input)[4];
const struct tgsi_shader_info *input_info;
#if DRAW_LLVM_AVAILABLE
struct draw_tcs_inputs *tcs_input;
struct draw_tcs_outputs *tcs_output;
struct lp_jit_resources *jit_resources;
struct draw_tcs_llvm_variant *current_variant;
#endif
};
struct draw_tess_eval_shader {
struct draw_context *draw;
struct pipe_shader_state state;
struct tgsi_shader_info info;
enum mesa_prim prim_mode;
unsigned spacing;
unsigned vertex_order_cw;
unsigned point_mode;
unsigned position_output;
unsigned viewport_index_output;
unsigned clipvertex_output;
unsigned ccdistance_output[PIPE_MAX_CLIP_OR_CULL_DISTANCE_ELEMENT_COUNT];
unsigned vector_length;
unsigned input_vertex_stride;
const float (*input)[4];
const struct tgsi_shader_info *input_info;
#if DRAW_LLVM_AVAILABLE
struct draw_tes_inputs *tes_input;
struct lp_jit_resources *jit_resources;
struct draw_tes_llvm_variant *current_variant;
#endif
};
enum mesa_prim get_tes_output_prim(struct draw_tess_eval_shader *shader);
int draw_tess_ctrl_shader_run(struct draw_tess_ctrl_shader *shader,
const struct draw_vertex_info *input_verts,
const struct draw_prim_info *input_prim,
const struct tgsi_shader_info *input_info,
struct draw_vertex_info *output_verts,
struct draw_prim_info *output_prims );
int draw_tess_eval_shader_run(struct draw_tess_eval_shader *shader,
unsigned num_input_vertices_per_patch,
const struct draw_vertex_info *input_verts,
const struct draw_prim_info *input_prim,
const struct tgsi_shader_info *input_info,
struct draw_vertex_info *output_verts,
struct draw_prim_info *output_prims,
uint32_t **patch_lengths,
uint16_t **elts_out);
#if DRAW_LLVM_AVAILABLE
void draw_tcs_set_current_variant(struct draw_tess_ctrl_shader *shader,
struct draw_tcs_llvm_variant *variant);
void draw_tes_set_current_variant(struct draw_tess_eval_shader *shader,
struct draw_tes_llvm_variant *variant);
#endif
#endif
+152
View File
@@ -0,0 +1,152 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/**
* \file
* Vertex buffer drawing stage.
*
* \author Keith Whitwell <[email protected]>
* \author Jose Fonseca <[email protected]>
*/
#ifndef DRAW_VBUF_H_
#define DRAW_VBUF_H_
#include "util/compiler.h"
#include "pipe/p_defines.h"
struct pipe_rasterizer_state;
struct draw_context;
struct vertex_info;
struct pipe_query_data_pipeline_statistics;
/**
* Interface for hardware vertex buffer rendering.
*/
struct vbuf_render {
/**
* Driver limits. May be tuned lower to improve cache hits on
* index list.
*/
unsigned max_indices;
unsigned max_vertex_buffer_bytes;
/**
* Query if the hardware driver needs assistance for a particular
* combination of rasterizer state and primitive.
*
* Currently optional.
*/
bool (*need_pipeline)(const struct vbuf_render *render,
const struct pipe_rasterizer_state *rasterizer,
unsigned int prim);
/**
* Get the hardware vertex format.
*
* XXX: have this in draw_context instead?
*/
const struct vertex_info *(*get_vertex_info)(struct vbuf_render *);
/**
* Request a destination for vertices.
* Hardware renderers will use ttm memory, others will just malloc
* something.
*/
bool (*allocate_vertices)(struct vbuf_render *,
uint16_t vertex_size,
uint16_t nr_vertices);
void *(*map_vertices)(struct vbuf_render *);
void (*unmap_vertices)(struct vbuf_render *,
uint16_t min_index,
uint16_t max_index);
/**
* Notify the renderer of the current primitive when it changes.
* Must succeed for TRIANGLES, LINES and POINTS. Other prims at
* the discretion of the driver, for the benefit of the passthrough
* path.
*/
void (*set_primitive)(struct vbuf_render *, enum mesa_prim prim);
/**
* Notify the renderer of the current view index.
*/
void (*set_view_index)(struct vbuf_render *, unsigned view_index);
/**
* Draw indexed primitives. Note that indices are ushort. The driver
* must complete this call, if necessary splitting the index list itself.
*/
void (*draw_elements)(struct vbuf_render *,
const uint16_t *indices,
unsigned nr_indices);
/* Draw non-indexed primitives.
*/
void (*draw_arrays)(struct vbuf_render *,
unsigned start,
unsigned nr);
/**
* Called when vbuf is done with this set of vertices:
*/
void (*release_vertices)(struct vbuf_render *);
void (*destroy)(struct vbuf_render *);
/**
* Called after writing data to the stream out buffers
*/
void (*set_stream_output_info)(struct vbuf_render *vbufr,
unsigned stream,
unsigned primitive_count,
unsigned primitive_generated);
/**
* Called after all relevant statistics have been accumulated.
*/
void (*pipeline_statistics)(
struct vbuf_render *vbufr,
const struct pipe_query_data_pipeline_statistics *stats);
};
struct draw_stage *
draw_vbuf_stage(struct draw_context *draw,
struct vbuf_render *render);
#endif /*DRAW_VBUF_H_*/
+113
View File
@@ -0,0 +1,113 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Functions for specifying the post-transformation vertex layout.
*
* Author:
* Brian Paul
* Keith Whitwell
*/
#include "draw/draw_private.h"
#include "draw/draw_vertex.h"
/**
* Compute the size of a vertex, in dwords/floats, to update the
* vinfo->size field.
*/
void
draw_compute_vertex_size(struct vertex_info *vinfo)
{
vinfo->size = 0;
for (unsigned i = 0; i < vinfo->num_attribs; i++)
vinfo->size += draw_translate_vinfo_size(vinfo->attrib[i].emit);
assert(vinfo->size % 4 == 0);
/* in dwords */
vinfo->size /= 4;
}
void
draw_dump_emitted_vertex(const struct vertex_info *vinfo, const uint8_t *data)
{
for (unsigned i = 0; i < vinfo->num_attribs; i++) {
switch (vinfo->attrib[i].emit) {
case EMIT_OMIT:
debug_printf("EMIT_OMIT:");
break;
case EMIT_1F:
debug_printf("EMIT_1F:\t");
debug_printf("%f ", *(float *)data); data += sizeof(float);
break;
case EMIT_1F_PSIZE:
debug_printf("EMIT_1F_PSIZE:\t");
debug_printf("%f ", *(float *)data); data += sizeof(float);
break;
case EMIT_2F:
debug_printf("EMIT_2F:\t");
debug_printf("%f ", *(float *)data); data += sizeof(float);
debug_printf("%f ", *(float *)data); data += sizeof(float);
break;
case EMIT_3F:
debug_printf("EMIT_3F:\t");
debug_printf("%f ", *(float *)data); data += sizeof(float);
debug_printf("%f ", *(float *)data); data += sizeof(float);
debug_printf("%f ", *(float *)data); data += sizeof(float);
data += sizeof(float);
break;
case EMIT_4F:
debug_printf("EMIT_4F:\t");
debug_printf("%f ", *(float *)data); data += sizeof(float);
debug_printf("%f ", *(float *)data); data += sizeof(float);
debug_printf("%f ", *(float *)data); data += sizeof(float);
debug_printf("%f ", *(float *)data); data += sizeof(float);
break;
case EMIT_4UB:
debug_printf("EMIT_4UB:\t");
debug_printf("%u ", *data++);
debug_printf("%u ", *data++);
debug_printf("%u ", *data++);
debug_printf("%u ", *data++);
break;
case EMIT_4UB_BGRA:
debug_printf("EMIT_4UB_BGRA:\t");
debug_printf("%u ", *data++);
debug_printf("%u ", *data++);
debug_printf("%u ", *data++);
debug_printf("%u ", *data++);
break;
default:
assert(0);
}
debug_printf("\n");
}
debug_printf("\n");
}
+195
View File
@@ -0,0 +1,195 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/**
* Post-transform vertex format info. The vertex_info struct is used by
* the draw_vbuf code to emit hardware-specific vertex layouts into hw
* vertex buffers.
*
* Author:
* Brian Paul
*/
#ifndef DRAW_VERTEX_H
#define DRAW_VERTEX_H
#include "util/compiler.h"
#include "pipe/p_state.h"
#include "util/u_debug.h"
#include "util/u_memory.h"
#define DRAW_ATTR_NONEXIST 255
/**
* Vertex attribute emit modes
*/
enum attrib_emit {
EMIT_OMIT, /**< don't emit the attribute */
EMIT_1F,
EMIT_1F_PSIZE, /**< insert constant point size */
EMIT_2F,
EMIT_3F,
EMIT_4F,
EMIT_4UB, /**< is RGBA like the rest */
EMIT_4UB_BGRA
};
/**
* Information about hardware/rasterization vertex layout.
*/
struct vertex_info
{
unsigned num_attribs;
unsigned size; /**< total vertex size in dwords */
/* Keep this small and at the end of the struct to allow quick
* memcmp() comparisons.
*/
struct {
unsigned emit:8; /**< EMIT_x */
unsigned src_index:8; /**< map to post-xform attribs */
} attrib[PIPE_MAX_SHADER_OUTPUTS];
};
static inline size_t
draw_vinfo_size(const struct vertex_info *a)
{
return offsetof(const struct vertex_info, attrib[a->num_attribs]);
}
static inline int
draw_vinfo_compare(const struct vertex_info *a,
const struct vertex_info *b)
{
size_t sizea = draw_vinfo_size(a);
return memcmp(a, b, sizea);
}
static inline void
draw_vinfo_copy(struct vertex_info *dst,
const struct vertex_info *src)
{
size_t size = draw_vinfo_size(src);
memcpy(dst, src, size);
}
/**
* Add another attribute to the given vertex_info object.
* \param src_index indicates which post-transformed vertex attrib slot
* corresponds to this attribute.
* \return slot in which the attribute was added
*/
static inline uint
draw_emit_vertex_attr(struct vertex_info *vinfo,
enum attrib_emit emit,
int src_index)
{
const unsigned n = vinfo->num_attribs;
/* If the src_index is negative, meaning it hasn't been found
* we'll assign it all zeros later - set to DRAW_ATTR_NONEXIST */
if (src_index < 0) {
src_index = DRAW_ATTR_NONEXIST;
}
assert(n < ARRAY_SIZE(vinfo->attrib));
vinfo->attrib[n].emit = emit;
vinfo->attrib[n].src_index = src_index;
vinfo->num_attribs++;
return n;
}
void
draw_compute_vertex_size(struct vertex_info *vinfo);
void
draw_dump_emitted_vertex(const struct vertex_info *vinfo,
const uint8_t *data);
static inline enum pipe_format
draw_translate_vinfo_format(enum attrib_emit emit)
{
switch (emit) {
case EMIT_OMIT:
return PIPE_FORMAT_NONE;
case EMIT_1F:
case EMIT_1F_PSIZE:
return PIPE_FORMAT_R32_FLOAT;
case EMIT_2F:
return PIPE_FORMAT_R32G32_FLOAT;
case EMIT_3F:
return PIPE_FORMAT_R32G32B32_FLOAT;
case EMIT_4F:
return PIPE_FORMAT_R32G32B32A32_FLOAT;
case EMIT_4UB:
return PIPE_FORMAT_R8G8B8A8_UNORM;
case EMIT_4UB_BGRA:
return PIPE_FORMAT_B8G8R8A8_UNORM;
default:
assert(!"unexpected format");
return PIPE_FORMAT_NONE;
}
}
static inline unsigned
draw_translate_vinfo_size(enum attrib_emit emit)
{
switch (emit) {
case EMIT_OMIT:
return 0;
case EMIT_1F:
case EMIT_1F_PSIZE:
return 1 * sizeof(float);
case EMIT_2F:
return 2 * sizeof(float);
case EMIT_3F:
return 3 * sizeof(float);
case EMIT_4F:
return 4 * sizeof(float);
case EMIT_4UB:
return 4 * sizeof(unsigned char);
case EMIT_4UB_BGRA:
return 4 * sizeof(unsigned char);
default:
assert(!"unexpected format");
return 0;
}
}
#endif /* DRAW_VERTEX_H */
@@ -0,0 +1,48 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
#ifndef DRAW_VERTEX_HEADER_H
#define DRAW_VERTEX_HEADER_H
/** Sum of frustum planes and user-defined planes */
#define DRAW_TOTAL_CLIP_PLANES (6 + PIPE_MAX_CLIP_PLANES)
/**
* Basic vertex info. Used to represent vertices after VS (through GS, TESS,
* etc.) to vbuf output.
*/
struct vertex_header {
unsigned clipmask:DRAW_TOTAL_CLIP_PLANES;
unsigned edgeflag:1;
unsigned pad:1;
unsigned vertex_id:16;
float clip_pos[4];
float data[][4]; // the vertex attributes
};
#endif
+268
View File
@@ -0,0 +1,268 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Authors:
* Keith Whitwell <[email protected]>
* Brian Paul
*/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "pipe/p_shader_tokens.h"
#include "pipe/p_context.h"
#include "pipe/p_screen.h"
#include "draw_private.h"
#include "draw_context.h"
#include "draw_vs.h"
#include "translate/translate.h"
#include "translate/translate_cache.h"
#include "tgsi/tgsi_dump.h"
#include "tgsi/tgsi_exec.h"
#include "tgsi/tgsi_ureg.h"
#include "nir/nir_to_tgsi.h"
DEBUG_GET_ONCE_BOOL_OPTION(gallium_dump_vs, "GALLIUM_DUMP_VS", false)
struct draw_vertex_shader *
draw_create_vertex_shader(struct draw_context *draw,
const struct pipe_shader_state *shader)
{
struct draw_vertex_shader *vs = NULL;
struct pipe_shader_state state = *shader;
if (draw->dump_vs) {
tgsi_dump(shader->tokens, 0);
}
#if DRAW_LLVM_AVAILABLE
bool is_allocated = false;
if (draw->pt.middle.llvm) {
struct pipe_screen *screen = draw->pipe->screen;
if (shader->type == PIPE_SHADER_IR_NIR &&
!screen->shader_caps[MESA_SHADER_VERTEX].integers) {
state.type = PIPE_SHADER_IR_TGSI;
state.tokens = nir_to_tgsi(shader->ir.nir, screen);
is_allocated = true;
}
vs = draw_create_vs_llvm(draw, &state);
}
#endif
if (!vs) {
vs = draw_create_vs_exec(draw, &state);
}
#if DRAW_LLVM_AVAILABLE
if (is_allocated) {
ureg_free_tokens(state.tokens);
}
#endif
if (vs) {
bool found_clipvertex = false;
vs->position_output = -1;
for (unsigned i = 0; i < vs->info.num_outputs; i++) {
if (vs->info.output_semantic_name[i] == TGSI_SEMANTIC_POSITION &&
vs->info.output_semantic_index[i] == 0) {
vs->position_output = i;
} else if (vs->info.output_semantic_name[i] == TGSI_SEMANTIC_EDGEFLAG &&
vs->info.output_semantic_index[i] == 0) {
vs->edgeflag_output = i;
} else if (vs->info.output_semantic_name[i] == TGSI_SEMANTIC_CLIPVERTEX &&
vs->info.output_semantic_index[i] == 0) {
found_clipvertex = true;
vs->clipvertex_output = i;
} else if (vs->info.output_semantic_name[i] == TGSI_SEMANTIC_VIEWPORT_INDEX) {
vs->viewport_index_output = i;
} else if (vs->info.output_semantic_name[i] == TGSI_SEMANTIC_CLIPDIST) {
assert(vs->info.output_semantic_index[i] <
PIPE_MAX_CLIP_OR_CULL_DISTANCE_ELEMENT_COUNT);
vs->ccdistance_output[vs->info.output_semantic_index[i]] = i;
}
}
if (!found_clipvertex)
vs->clipvertex_output = vs->position_output;
}
assert(vs);
return vs;
}
void
draw_bind_vertex_shader(struct draw_context *draw,
struct draw_vertex_shader *dvs)
{
draw_do_flush(draw, DRAW_FLUSH_STATE_CHANGE);
if (dvs) {
draw->vs.vertex_shader = dvs;
draw->vs.num_vs_outputs = dvs->info.num_outputs;
draw->vs.position_output = dvs->position_output;
draw->vs.edgeflag_output = dvs->edgeflag_output;
draw->vs.clipvertex_output = dvs->clipvertex_output;
draw->vs.ccdistance_output[0] = dvs->ccdistance_output[0];
draw->vs.ccdistance_output[1] = dvs->ccdistance_output[1];
dvs->prepare(dvs, draw);
draw_update_clip_flags(draw);
draw_update_viewport_flags(draw);
} else {
draw->vs.vertex_shader = NULL;
draw->vs.num_vs_outputs = 0;
}
}
void
draw_delete_vertex_shader(struct draw_context *draw,
struct draw_vertex_shader *dvs)
{
for (unsigned i = 0; i < dvs->nr_variants; i++)
dvs->variant[i]->destroy(dvs->variant[i]);
dvs->nr_variants = 0;
dvs->delete(dvs);
}
bool
draw_vs_init(struct draw_context *draw)
{
draw->dump_vs = debug_get_option_gallium_dump_vs();
if (!draw->llvm) {
draw->vs.tgsi.machine = tgsi_exec_machine_create(MESA_SHADER_VERTEX);
if (!draw->vs.tgsi.machine)
return false;
}
draw->vs.emit_cache = translate_cache_create();
if (!draw->vs.emit_cache)
return false;
draw->vs.fetch_cache = translate_cache_create();
if (!draw->vs.fetch_cache)
return false;
return true;
}
void
draw_vs_destroy(struct draw_context *draw)
{
if (draw->vs.fetch_cache)
translate_cache_destroy(draw->vs.fetch_cache);
if (draw->vs.emit_cache)
translate_cache_destroy(draw->vs.emit_cache);
if (!draw->llvm)
tgsi_exec_machine_destroy(draw->vs.tgsi.machine);
}
struct draw_vs_variant *
draw_vs_lookup_variant(struct draw_vertex_shader *vs,
const struct draw_vs_variant_key *key)
{
/* Lookup existing variant:
*/
for (unsigned i = 0; i < vs->nr_variants; i++)
if (draw_vs_variant_key_compare(key, &vs->variant[i]->key) == 0)
return vs->variant[i];
/* Else have to create a new one:
*/
struct draw_vs_variant *variant = vs->create_variant(vs, key);
if (!variant)
return NULL;
/* Add it to our list, could be smarter:
*/
if (vs->nr_variants < ARRAY_SIZE(vs->variant)) {
vs->variant[vs->nr_variants++] = variant;
} else {
vs->last_variant++;
vs->last_variant %= ARRAY_SIZE(vs->variant);
vs->variant[vs->last_variant]->destroy(vs->variant[vs->last_variant]);
vs->variant[vs->last_variant] = variant;
}
return variant;
}
struct translate *
draw_vs_get_fetch(struct draw_context *draw,
struct translate_key *key)
{
if (!draw->vs.fetch ||
translate_key_compare(&draw->vs.fetch->key, key) != 0) {
translate_key_sanitize(key);
draw->vs.fetch = translate_cache_find(draw->vs.fetch_cache, key);
}
return draw->vs.fetch;
}
struct translate *
draw_vs_get_emit(struct draw_context *draw,
struct translate_key *key)
{
if (!draw->vs.emit ||
translate_key_compare(&draw->vs.emit->key, key) != 0) {
translate_key_sanitize(key);
draw->vs.emit = translate_cache_find(draw->vs.emit_cache, key);
}
return draw->vs.emit;
}
void
draw_vs_attach_so(struct draw_vertex_shader *dvs,
const struct pipe_stream_output_info *info)
{
dvs->state.stream_output = *info;
}
void
draw_vs_reset_so(struct draw_vertex_shader *dvs)
{
memset(&dvs->state.stream_output, 0, sizeof(dvs->state.stream_output));
}
+203
View File
@@ -0,0 +1,203 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/* Authors: Keith Whitwell <[email protected]>
*/
#ifndef DRAW_VS_H
#define DRAW_VS_H
#include "draw_context.h"
#include "draw_private.h"
#include "draw_vertex.h"
#include "tgsi/tgsi_scan.h"
struct draw_context;
struct pipe_shader_state;
struct draw_variant_input {
enum pipe_format format;
unsigned buffer;
unsigned offset;
unsigned instance_divisor;
};
struct draw_variant_output {
enum attrib_emit format; /* output format */
unsigned vs_output:8; /* which vertex shader output is this? */
unsigned offset:24; /* offset into output vertex */
};
struct draw_variant_element {
struct draw_variant_input in;
struct draw_variant_output out;
};
struct draw_vs_variant_key {
unsigned output_stride;
unsigned nr_elements:8; /* max2(nr_inputs, nr_outputs) */
unsigned nr_inputs:8;
unsigned nr_outputs:8;
unsigned viewport:1;
unsigned clip:1;
unsigned const_vbuffers:5;
struct draw_variant_element element[PIPE_MAX_ATTRIBS];
};
struct draw_vs_variant {
struct draw_vs_variant_key key;
struct draw_vertex_shader *vs;
void (*set_buffer)(struct draw_vs_variant *,
unsigned i,
const void *ptr,
unsigned stride,
unsigned max_stride);
void (UTIL_CDECL *run_linear)(struct draw_vs_variant *shader,
unsigned start,
unsigned count,
void *output_buffer);
void (UTIL_CDECL *run_elts)(struct draw_vs_variant *shader,
const unsigned *elts,
unsigned count,
void *output_buffer);
void (*destroy)(struct draw_vs_variant *);
};
/**
* Private version of the compiled vertex_shader
*/
struct draw_vertex_shader {
struct draw_context *draw;
/* This member will disappear shortly:
*/
struct pipe_shader_state state;
struct tgsi_shader_info info;
unsigned position_output;
unsigned viewport_index_output;
unsigned edgeflag_output;
unsigned clipvertex_output;
unsigned ccdistance_output[PIPE_MAX_CLIP_OR_CULL_DISTANCE_ELEMENT_COUNT];
/* Extracted from shader:
*/
const float (*immediates)[4];
struct draw_vs_variant *variant[16];
unsigned nr_variants;
unsigned last_variant;
struct draw_vs_variant *(*create_variant)(struct draw_vertex_shader *shader,
const struct draw_vs_variant_key *key);
void (*prepare)(struct draw_vertex_shader *shader,
struct draw_context *draw);
/* Run the shader - this interface will get cleaned up in the
* future:
*/
void (*run_linear)(struct draw_vertex_shader *shader,
const float (*input)[4],
float (*output)[4],
const struct draw_buffer_info *constants,
unsigned count,
unsigned input_stride,
unsigned output_stride,
const unsigned *fetch_elts);
void (*delete)(struct draw_vertex_shader *);
};
struct draw_vs_variant *
draw_vs_lookup_variant(struct draw_vertex_shader *base,
const struct draw_vs_variant_key *key);
/********************************************************************************
* Internal functions:
*/
struct draw_vertex_shader *
draw_create_vs_exec(struct draw_context *draw,
const struct pipe_shader_state *templ);
#if DRAW_LLVM_AVAILABLE
struct draw_vertex_shader *
draw_create_vs_llvm(struct draw_context *draw,
const struct pipe_shader_state *state);
#endif
/********************************************************************************
* Helpers for vs implementations that don't do their own fetch/emit variants.
* Means these can be shared between shaders.
*/
struct translate;
struct translate_key;
struct translate *
draw_vs_get_fetch(struct draw_context *draw,
struct translate_key *key);
struct translate *
draw_vs_get_emit(struct draw_context *draw,
struct translate_key *key);
struct draw_vs_variant *
draw_vs_create_variant_generic(struct draw_vertex_shader *vs,
const struct draw_vs_variant_key *key);
static inline int
draw_vs_variant_keysize(const struct draw_vs_variant_key *key)
{
return 2 * sizeof(int) + key->nr_elements * sizeof(struct draw_variant_element);
}
static inline int
draw_vs_variant_key_compare(const struct draw_vs_variant_key *a,
const struct draw_vs_variant_key *b)
{
int keysize = draw_vs_variant_keysize(a);
return memcmp(a, b, keysize);
}
#define MAX_TGSI_VERTICES 4
#endif
+252
View File
@@ -0,0 +1,252 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Authors:
* Keith Whitwell <[email protected]>
* Brian Paul
*/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/ralloc.h"
#include "pipe/p_shader_tokens.h"
#include "pipe/p_context.h"
#include "nir/nir_to_tgsi.h"
#include "draw_private.h"
#include "draw_context.h"
#include "draw_vs.h"
#include "tgsi/tgsi_parse.h"
#include "tgsi/tgsi_scan.h"
#include "tgsi/tgsi_exec.h"
struct exec_vertex_shader {
struct draw_vertex_shader base;
struct tgsi_exec_machine *machine;
};
static struct exec_vertex_shader *
exec_vertex_shader(struct draw_vertex_shader *vs)
{
return (struct exec_vertex_shader *)vs;
}
/* Not required for run_linear.
*/
static void
vs_exec_prepare(struct draw_vertex_shader *shader,
struct draw_context *draw)
{
struct exec_vertex_shader *evs = exec_vertex_shader(shader);
assert(!draw->llvm);
/* Specify the vertex program to interpret/execute.
* Avoid rebinding when possible.
*/
if (evs->machine->Tokens != shader->state.tokens) {
tgsi_exec_machine_bind_shader(evs->machine,
shader->state.tokens,
draw->vs.tgsi.sampler,
draw->vs.tgsi.image,
draw->vs.tgsi.buffer);
}
}
/**
* Simplified vertex shader interface for the pt paths. Given the
* complexity of code-generating all the above operations together,
* it's time to try doing all the other stuff separately.
*/
static void
vs_exec_run_linear(struct draw_vertex_shader *shader,
const float (*input)[4],
float (*output)[4],
const struct draw_buffer_info *constants,
unsigned count,
unsigned input_stride,
unsigned output_stride,
const unsigned *fetch_elts)
{
struct exec_vertex_shader *evs = exec_vertex_shader(shader);
struct tgsi_exec_machine *machine = evs->machine;
unsigned int i, j;
unsigned slot;
bool clamp_vertex_color = shader->draw->rasterizer->clamp_vertex_color;
assert(!shader->draw->llvm);
tgsi_exec_set_constant_buffers(machine, PIPE_MAX_CONSTANT_BUFFERS,
(const struct tgsi_exec_consts_info *)constants);
if (shader->info.uses_instanceid) {
unsigned i = machine->SysSemanticToIndex[TGSI_SEMANTIC_INSTANCEID];
assert(i < ARRAY_SIZE(machine->SystemValue));
for (j = 0; j < TGSI_QUAD_SIZE; j++)
machine->SystemValue[i].xyzw[0].i[j] = shader->draw->instance_id;
}
for (i = 0; i < count; i += MAX_TGSI_VERTICES) {
unsigned int max_vertices = MIN2(MAX_TGSI_VERTICES, count - i);
/* Swizzle inputs.
*/
for (j = 0; j < max_vertices; j++) {
#if 0
debug_printf("%d) Input vert:\n", i + j);
for (slot = 0; slot < shader->info.num_inputs; slot++) {
debug_printf("\t%d: %f %f %f %f\n", slot,
input[slot][0],
input[slot][1],
input[slot][2],
input[slot][3]);
}
#endif
int basevertex = shader->draw->pt.user.eltSize ? shader->draw->pt.user.eltBias : shader->draw->start_index;
if (shader->info.uses_vertexid) {
unsigned vid = machine->SysSemanticToIndex[TGSI_SEMANTIC_VERTEXID];
assert(vid < ARRAY_SIZE(machine->SystemValue));
machine->SystemValue[vid].xyzw[0].i[j] = fetch_elts ? fetch_elts[i + j] : (i + j + basevertex);
}
if (shader->info.uses_basevertex) {
unsigned vid = machine->SysSemanticToIndex[TGSI_SEMANTIC_BASEVERTEX];
assert(vid < ARRAY_SIZE(machine->SystemValue));
machine->SystemValue[vid].xyzw[0].i[j] = basevertex;
}
if (shader->info.uses_vertexid_nobase) {
unsigned vid = machine->SysSemanticToIndex[TGSI_SEMANTIC_VERTEXID_NOBASE];
assert(vid < ARRAY_SIZE(machine->SystemValue));
machine->SystemValue[vid].xyzw[0].i[j] = fetch_elts ? (fetch_elts[i + j] - basevertex) : (i + j);
}
for (slot = 0; slot < shader->info.num_inputs; slot++) {
#if 0
assert(!util_is_inf_or_nan(input[slot][0]));
assert(!util_is_inf_or_nan(input[slot][1]));
assert(!util_is_inf_or_nan(input[slot][2]));
assert(!util_is_inf_or_nan(input[slot][3]));
#endif
machine->Inputs[slot].xyzw[0].f[j] = input[slot][0];
machine->Inputs[slot].xyzw[1].f[j] = input[slot][1];
machine->Inputs[slot].xyzw[2].f[j] = input[slot][2];
machine->Inputs[slot].xyzw[3].f[j] = input[slot][3];
}
input = (const float (*)[4])((const char *)input + input_stride);
}
machine->NonHelperMask = (1 << max_vertices) - 1;
/* run interpreter */
tgsi_exec_machine_run(machine, 0);
/* Unswizzle all output results.
*/
for (j = 0; j < max_vertices; j++) {
for (slot = 0; slot < shader->info.num_outputs; slot++) {
enum tgsi_semantic name = shader->info.output_semantic_name[slot];
if (clamp_vertex_color &&
(name == TGSI_SEMANTIC_COLOR || name == TGSI_SEMANTIC_BCOLOR)) {
output[slot][0] = SATURATE(machine->Outputs[slot].xyzw[0].f[j]);
output[slot][1] = SATURATE(machine->Outputs[slot].xyzw[1].f[j]);
output[slot][2] = SATURATE(machine->Outputs[slot].xyzw[2].f[j]);
output[slot][3] = SATURATE(machine->Outputs[slot].xyzw[3].f[j]);
} else {
output[slot][0] = machine->Outputs[slot].xyzw[0].f[j];
output[slot][1] = machine->Outputs[slot].xyzw[1].f[j];
output[slot][2] = machine->Outputs[slot].xyzw[2].f[j];
output[slot][3] = machine->Outputs[slot].xyzw[3].f[j];
}
}
#if 0
debug_printf("%d) Post xform vert:\n", i + j);
for (slot = 0; slot < shader->info.num_outputs; slot++) {
debug_printf("\t%d: %f %f %f %f\n", slot,
output[slot][0],
output[slot][1],
output[slot][2],
output[slot][3]);
assert(!util_is_inf_or_nan(output[slot][0]));
}
#endif
output = (float (*)[4])((char *)output + output_stride);
}
}
}
static void
vs_exec_delete(struct draw_vertex_shader *dvs)
{
FREE((void*) dvs->state.tokens);
FREE(dvs);
}
struct draw_vertex_shader *
draw_create_vs_exec(struct draw_context *draw,
const struct pipe_shader_state *state)
{
struct exec_vertex_shader *vs = CALLOC_STRUCT(exec_vertex_shader);
if (!vs)
return NULL;
if (state->type == PIPE_SHADER_IR_NIR) {
vs->base.state.type = PIPE_SHADER_IR_TGSI;
vs->base.state.tokens = nir_to_tgsi(state->ir.nir, draw->pipe->screen);
} else {
assert(state->type == PIPE_SHADER_IR_TGSI);
vs->base.state.type = state->type;
/* we need to keep a local copy of the tokens */
vs->base.state.tokens = tgsi_dup_tokens(state->tokens);
if (!vs->base.state.tokens) {
FREE(vs);
return NULL;
}
}
tgsi_scan_shader(vs->base.state.tokens, &vs->base.info);
vs->base.state.stream_output = state->stream_output;
vs->base.draw = draw;
vs->base.prepare = vs_exec_prepare;
vs->base.run_linear = vs_exec_run_linear;
vs->base.delete = vs_exec_delete;
vs->base.create_variant = draw_vs_create_variant_generic;
vs->machine = draw->vs.tgsi.machine;
return &vs->base;
}
+129
View File
@@ -0,0 +1,129 @@
/**************************************************************************
*
* 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.
*
**************************************************************************/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "pipe/p_shader_tokens.h"
#include "pipe/p_screen.h"
#include "draw_private.h"
#include "draw_context.h"
#include "draw_vs.h"
#include "draw_llvm.h"
#include "tgsi/tgsi_parse.h"
#include "tgsi/tgsi_scan.h"
#include "nir/nir_to_tgsi_info.h"
#include "nir.h"
static void
vs_llvm_prepare(struct draw_vertex_shader *shader,
struct draw_context *draw)
{
/*struct llvm_vertex_shader *evs = llvm_vertex_shader(shader);*/
}
static void
vs_llvm_run_linear(struct draw_vertex_shader *shader,
const float (*input)[4],
float (*output)[4],
const struct draw_buffer_info *constants,
unsigned count,
unsigned input_stride,
unsigned output_stride,
const unsigned *elts)
{
/* we should never get here since the entire pipeline is
* generated in draw_pt_fetch_shade_pipeline_llvm.c */
assert(0);
}
static void
vs_llvm_delete(struct draw_vertex_shader *dvs)
{
struct llvm_vertex_shader *shader = llvm_vertex_shader(dvs);
struct draw_llvm_variant_list_item *li, *next;
LIST_FOR_EACH_ENTRY_SAFE(li, next, &shader->variants.list, list) {
draw_llvm_destroy_variant(li->base);
}
assert(shader->variants_cached == 0);
ralloc_free(dvs->state.ir.nir);
FREE((void*) dvs->state.tokens);
FREE(dvs);
}
struct draw_vertex_shader *
draw_create_vs_llvm(struct draw_context *draw,
const struct pipe_shader_state *state)
{
struct llvm_vertex_shader *vs = CALLOC_STRUCT(llvm_vertex_shader);
if (!vs)
return NULL;
if (state->type == PIPE_SHADER_IR_NIR) {
vs->base.state.ir.nir = state->ir.nir;
nir_shader *nir = state->ir.nir;
if (!nir->options->lower_uniforms_to_ubo)
NIR_PASS(_, state->ir.nir, nir_lower_uniforms_to_ubo, false, false);
nir_tgsi_scan_shader(state->ir.nir, &vs->base.info, true);
} else {
/* we make a private copy of the tokens */
vs->base.state.tokens = tgsi_dup_tokens(state->tokens);
if (!vs->base.state.tokens) {
FREE(vs);
return NULL;
}
tgsi_scan_shader(state->tokens, &vs->base.info);
}
vs->variant_key_size =
draw_llvm_variant_key_size(
vs->base.info.file_max[TGSI_FILE_INPUT]+1,
vs->base.info.file_max[TGSI_FILE_SAMPLER]+1,
vs->base.info.file_max[TGSI_FILE_SAMPLER_VIEW]+1,
vs->base.info.file_max[TGSI_FILE_IMAGE]+1);
vs->base.state.type = state->type;
vs->base.state.stream_output = state->stream_output;
vs->base.draw = draw;
vs->base.prepare = vs_llvm_prepare;
vs->base.run_linear = vs_llvm_run_linear;
vs->base.delete = vs_llvm_delete;
vs->base.create_variant = draw_vs_create_variant_generic;
list_inithead(&vs->variants.list);
return &vs->base;
}
@@ -0,0 +1,348 @@
/**************************************************************************
*
* Copyright 2007 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.
*
**************************************************************************/
/*
* Authors:
* Keith Whitwell <[email protected]>
*/
#include "util/u_memory.h"
#include "util/u_math.h"
#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "draw/draw_vbuf.h"
#include "draw/draw_vertex.h"
#include "draw/draw_vs.h"
#include "translate/translate.h"
/* A first pass at incorporating vertex fetch/emit functionality into
*/
struct draw_vs_variant_generic {
struct draw_vs_variant base;
struct draw_vertex_shader *shader;
struct draw_context *draw;
/* Basic plan is to run these two translate functions before/after
* the vertex shader's existing run_linear() routine to simulate
* the inclusion of this functionality into the shader...
*
* Next will look at actually including it.
*/
struct translate *fetch;
struct translate *emit;
unsigned temp_vertex_stride;
};
static void
vsvg_set_buffer(struct draw_vs_variant *variant,
unsigned buffer,
const void *ptr,
unsigned stride,
unsigned max_index)
{
struct draw_vs_variant_generic *vsvg = (struct draw_vs_variant_generic *)variant;
vsvg->fetch->set_buffer(vsvg->fetch, buffer, ptr, stride, max_index);
}
static const struct pipe_viewport_state *
find_viewport(struct draw_context *draw,
char *buffer,
unsigned vertex_idx,
unsigned stride)
{
int viewport_index_output =
draw_current_shader_viewport_index_output(draw);
const char *ptr = buffer + vertex_idx * stride;
const unsigned *data = (const unsigned *) ptr;
int viewport_index =
draw_current_shader_uses_viewport_index(draw) ?
data[viewport_index_output * 4] : 0;
viewport_index = draw_clamp_viewport_idx(viewport_index);
return &draw->viewports[viewport_index];
}
/* Mainly for debug at this stage:
*/
static void
do_rhw_viewport(struct draw_vs_variant_generic *vsvg,
unsigned count,
void *output_buffer)
{
char *ptr = (char *)output_buffer;
unsigned stride = vsvg->temp_vertex_stride;
ptr += vsvg->base.vs->position_output * 4 * sizeof(float);
for (unsigned j = 0; j < count; j++, ptr += stride) {
const struct pipe_viewport_state *viewport =
find_viewport(vsvg->base.vs->draw, (char*)output_buffer,
j, stride);
const float *scale = viewport->scale;
const float *trans = viewport->translate;
float *data = (float *)ptr;
float w = 1.0f / data[3];
data[0] = data[0] * w * scale[0] + trans[0];
data[1] = data[1] * w * scale[1] + trans[1];
data[2] = data[2] * w * scale[2] + trans[2];
data[3] = w;
}
}
static void
do_viewport(struct draw_vs_variant_generic *vsvg,
unsigned count,
void *output_buffer)
{
char *ptr = (char *)output_buffer;
unsigned stride = vsvg->temp_vertex_stride;
ptr += vsvg->base.vs->position_output * 4 * sizeof(float);
for (unsigned j = 0; j < count; j++, ptr += stride) {
const struct pipe_viewport_state *viewport =
find_viewport(vsvg->base.vs->draw, (char*)output_buffer,
j, stride);
const float *scale = viewport->scale;
const float *trans = viewport->translate;
float *data = (float *)ptr;
data[0] = data[0] * scale[0] + trans[0];
data[1] = data[1] * scale[1] + trans[1];
data[2] = data[2] * scale[2] + trans[2];
}
}
static void UTIL_CDECL
vsvg_run_elts(struct draw_vs_variant *variant,
const unsigned *elts,
unsigned count,
void *output_buffer)
{
struct draw_vs_variant_generic *vsvg = (struct draw_vs_variant_generic *)variant;
unsigned temp_vertex_stride = vsvg->temp_vertex_stride;
void *temp_buffer = MALLOC(align(count,4) * temp_vertex_stride +
DRAW_EXTRA_VERTICES_PADDING);
if (0) debug_printf("%s %d \n", __func__, count);
/* Want to do this in small batches for cache locality?
*/
vsvg->fetch->run_elts(vsvg->fetch,
elts,
count,
vsvg->draw->start_instance,
vsvg->draw->instance_id,
temp_buffer);
vsvg->base.vs->run_linear(vsvg->base.vs,
temp_buffer,
temp_buffer,
vsvg->base.vs->draw->pt.user.constants[MESA_SHADER_VERTEX],
count,
temp_vertex_stride,
temp_vertex_stride, NULL);
/* FIXME: geometry shading? */
if (vsvg->base.key.clip) {
/* not really handling clipping, just do the rhw so we can
* see the results...
*/
do_rhw_viewport(vsvg, count, temp_buffer);
} else if (vsvg->base.key.viewport) {
do_viewport(vsvg, count, temp_buffer);
}
vsvg->emit->set_buffer(vsvg->emit,
0,
temp_buffer,
temp_vertex_stride,
~0);
vsvg->emit->set_buffer(vsvg->emit,
1,
&vsvg->draw->rasterizer->point_size,
0,
~0);
vsvg->emit->run(vsvg->emit,
0, count,
vsvg->draw->start_instance,
vsvg->draw->instance_id,
output_buffer);
FREE(temp_buffer);
}
static void UTIL_CDECL
vsvg_run_linear(struct draw_vs_variant *variant,
unsigned start,
unsigned count,
void *output_buffer)
{
struct draw_vs_variant_generic *vsvg = (struct draw_vs_variant_generic *)variant;
unsigned temp_vertex_stride = vsvg->temp_vertex_stride;
void *temp_buffer = MALLOC(align(count,4) * temp_vertex_stride +
DRAW_EXTRA_VERTICES_PADDING);
if (0) debug_printf("%s %d %d (sz %d, %d)\n", __func__, start, count,
vsvg->base.key.output_stride,
temp_vertex_stride);
vsvg->fetch->run(vsvg->fetch,
start,
count,
vsvg->draw->start_instance,
vsvg->draw->instance_id,
temp_buffer);
vsvg->base.vs->run_linear(vsvg->base.vs,
temp_buffer,
temp_buffer,
vsvg->base.vs->draw->pt.user.constants[MESA_SHADER_VERTEX],
count,
temp_vertex_stride,
temp_vertex_stride, NULL);
if (vsvg->base.key.clip) {
/* not really handling clipping, just do the rhw so we can
* see the results...
*/
do_rhw_viewport(vsvg, count, temp_buffer);
} else if (vsvg->base.key.viewport) {
do_viewport(vsvg, count, temp_buffer);
}
vsvg->emit->set_buffer(vsvg->emit,
0,
temp_buffer,
temp_vertex_stride,
~0);
vsvg->emit->set_buffer(vsvg->emit,
1,
&vsvg->draw->rasterizer->point_size,
0,
~0);
vsvg->emit->run(vsvg->emit,
0, count,
vsvg->draw->start_instance,
vsvg->draw->instance_id,
output_buffer);
FREE(temp_buffer);
}
static void
vsvg_destroy(struct draw_vs_variant *variant)
{
FREE(variant);
}
struct draw_vs_variant *
draw_vs_create_variant_generic(struct draw_vertex_shader *vs,
const struct draw_vs_variant_key *key)
{
struct draw_vs_variant_generic *vsvg = CALLOC_STRUCT(draw_vs_variant_generic);
if (!vsvg)
return NULL;
vsvg->base.key = *key;
vsvg->base.vs = vs;
vsvg->base.set_buffer = vsvg_set_buffer;
vsvg->base.run_elts = vsvg_run_elts;
vsvg->base.run_linear = vsvg_run_linear;
vsvg->base.destroy = vsvg_destroy;
vsvg->draw = vs->draw;
vsvg->temp_vertex_stride = MAX2(key->nr_inputs,
draw_total_vs_outputs(vs->draw)) * 4 * sizeof(float);
/* Build free-standing fetch and emit functions:
*/
struct translate_key fetch;
fetch.nr_elements = key->nr_inputs;
fetch.output_stride = vsvg->temp_vertex_stride;
for (unsigned i = 0; i < key->nr_inputs; i++) {
fetch.element[i].type = TRANSLATE_ELEMENT_NORMAL;
fetch.element[i].input_format = key->element[i].in.format;
fetch.element[i].input_buffer = key->element[i].in.buffer;
fetch.element[i].input_offset = key->element[i].in.offset;
fetch.element[i].instance_divisor = 0;
fetch.element[i].output_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
fetch.element[i].output_offset = i * 4 * sizeof(float);
assert(fetch.element[i].output_offset < fetch.output_stride);
}
struct translate_key emit;
emit.nr_elements = key->nr_outputs;
emit.output_stride = key->output_stride;
for (unsigned i = 0; i < key->nr_outputs; i++) {
if (key->element[i].out.format != EMIT_1F_PSIZE) {
emit.element[i].type = TRANSLATE_ELEMENT_NORMAL;
emit.element[i].input_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
emit.element[i].input_buffer = 0;
emit.element[i].input_offset = key->element[i].out.vs_output * 4 * sizeof(float);
emit.element[i].instance_divisor = 0;
emit.element[i].output_format = draw_translate_vinfo_format(key->element[i].out.format);
emit.element[i].output_offset = key->element[i].out.offset;
assert(emit.element[i].input_offset <= fetch.output_stride);
} else {
emit.element[i].type = TRANSLATE_ELEMENT_NORMAL;
emit.element[i].input_format = PIPE_FORMAT_R32_FLOAT;
emit.element[i].input_buffer = 1;
emit.element[i].input_offset = 0;
emit.element[i].instance_divisor = 0;
emit.element[i].output_format = PIPE_FORMAT_R32_FLOAT;
emit.element[i].output_offset = key->element[i].out.offset;
}
}
vsvg->fetch = draw_vs_get_fetch(vs->draw, &fetch);
vsvg->emit = draw_vs_get_emit(vs->draw, &emit);
return &vsvg->base;
}