llvmpipe: Add a linear rasterizer optimized for 2D rendering.

This change adds:

- an alternative rasterizer, which rasterizes bins in a left->right &
  top->bottom linear fashion;

- triangle -> rectangle detection;

- 1:1 blit detection;

- a special TGSI -> LLVM IR code generation that uses 8-bit SSE integers
  in AoS fashion (as opposed to 32bits floats.)

Altogether these changes yield a 2x to 3x performance improvement for 2D
workloads.  It was designed to render Windows 7 Aero and other Windows
built-in 3D applications (like Windows Media Player, Internet Explorer
11, UWP applications) with minimum CPU utilization, but it should be
generally applicable to other 2D-on-3D applications, like desktop
compositors, HTML browsers, 3D based UI toolkits, etc.

This was mostly the brainchild of Keith Whitwell back in 2010.  I wrote
TGSI -> AoS translation.  And many others added bug-fixes and
enhancements over the years: Roland Scheidegger, Brian Paul, and James
Benton.

Known issues:

- piglit spec@!opengl 1.1@quad-invariance will warn that "left and right
half should match" due to rounding error difference

- These optimized paths to kick in is that depth-buffer must not be
used, so some applications which want to benefit from these improvements
might need to be modified to ensure they use painter's algorithm instead
of depth-buffers.

Reviewed-by: Roland Scheidegger <sroland@vmware.com>
Reviewed-by: Brian Paul <brianp@vmware.com>
Acked-by: Keith Whitwell <keithw@vmware.com>

v2: Incorporate Dave Airlie feedback: cleanup LP_DEBUG_xx; shrink 3+
empty lines.
v3: silence unused var warning, adapt to new upstream code (point setup)

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/11969>
This commit is contained in:
Jose Fonseca
2021-05-07 13:49:07 +01:00
committed by Roland Scheidegger
parent 4dc81cc631
commit 5a1d3bcf26
53 changed files with 7894 additions and 219 deletions
@@ -454,7 +454,7 @@ lp_build_sample_image_nearest(struct lp_build_sample_context *bld,
LLVMValueRef s_float, t_float = NULL, r_float = NULL;
LLVMValueRef x_stride;
LLVMValueRef x_offset, offset;
LLVMValueRef x_subcoord, y_subcoord, z_subcoord;
LLVMValueRef x_subcoord, y_subcoord = NULL, z_subcoord;
lp_build_context_init(&i32, bld->gallivm, lp_type_int_vec(32, bld->vector_width));
@@ -778,7 +778,7 @@ lp_build_sample_image_linear(struct lp_build_sample_context *bld,
LLVMValueRef y_offset0, y_offset1;
LLVMValueRef z_offset0, z_offset1;
LLVMValueRef offset[2][2][2]; /* [z][y][x] */
LLVMValueRef x_subcoord[2], y_subcoord[2], z_subcoord[2];
LLVMValueRef x_subcoord[2], y_subcoord[2] = {NULL, NULL}, z_subcoord[2];
unsigned x, y, z;
lp_build_context_init(&i32, bld->gallivm, lp_type_int_vec(32, bld->vector_width));
+405 -1
View File
@@ -1,6 +1,6 @@
/**************************************************************************
*
* Copyright 2008 VMware, Inc.
* Copyright 2008-2021 VMware, Inc.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -38,6 +38,8 @@
#define U_SSE_H_
#include "pipe/p_config.h"
#include "pipe/p_compiler.h"
#include "util/u_debug.h"
#if defined(PIPE_ARCH_SSE)
@@ -296,6 +298,408 @@ transpose2_64_2_32(const __m128i * restrict a01,
#define SCALAR_EPI32(m, i) _mm_shuffle_epi32((m), _MM_SHUFFLE(i,i,i,i))
/*
* Implements (1-w)*a + w*b = a - wa + wb = w(b-a) + a
* ((b-a)*w >> 8) + a
* The math behind negative sub results (logic shift/mask) is tricky.
*
* w -- weight values
* a -- src0 values
* b -- src1 values
*/
static ALWAYS_INLINE __m128i
util_sse2_lerp_epi16(__m128i w, __m128i a, __m128i b)
{
__m128i res;
res = _mm_sub_epi16(b, a);
res = _mm_mullo_epi16(res, w);
res = _mm_srli_epi16(res, 8);
/* use add_epi8 instead of add_epi16 so no need to mask off upper bits */
res = _mm_add_epi8(res, a);
return res;
}
/* Apply premultiplied-alpha blending on two pixels simultaneously.
* All parameters are packed as 8.8 fixed point values in __m128i SSE
* registers, with the upper 8 bits all zero.
*
* a -- src alpha values
* d -- dst color values
* s -- src color values
*/
static inline __m128i
util_sse2_premul_blend_epi16( __m128i a, __m128i d, __m128i s)
{
__m128i da, d_sub_da, tmp;
tmp = _mm_mullo_epi16(d, a);
da = _mm_srli_epi16(tmp, 8);
d_sub_da = _mm_sub_epi16(d, da);
return _mm_add_epi16(s, d_sub_da);
}
/* Apply premultiplied-alpha blending on four pixels in packed BGRA
* format (one/inv_src_alpha blend mode).
*
* src -- four pixels (bgra8 format)
* dst -- four destination pixels (bgra8)
* return -- blended pixels (bgra8)
*/
static ALWAYS_INLINE __m128i
util_sse2_blend_premul_4(const __m128i src,
const __m128i dst)
{
__m128i al, ah, dl, dh, sl, sh, rl, rh;
__m128i zero = _mm_setzero_si128();
/* Blend first two pixels:
*/
sl = _mm_unpacklo_epi8(src, zero);
dl = _mm_unpacklo_epi8(dst, zero);
al = _mm_shufflehi_epi16(sl, 0xff);
al = _mm_shufflelo_epi16(al, 0xff);
rl = util_sse2_premul_blend_epi16(al, dl, sl);
/* Blend second two pixels:
*/
sh = _mm_unpackhi_epi8(src, zero);
dh = _mm_unpackhi_epi8(dst, zero);
ah = _mm_shufflehi_epi16(sh, 0xff);
ah = _mm_shufflelo_epi16(ah, 0xff);
rh = util_sse2_premul_blend_epi16(ah, dh, sh);
/* Pack the results down to four bgra8 pixels:
*/
return _mm_packus_epi16(rl, rh);
}
/* Apply src-alpha blending on four pixels in packed BGRA
* format (srcalpha/inv_src_alpha blend mode).
*
* src -- four pixels (bgra8 format)
* dst -- four destination pixels (bgra8)
* return -- blended pixels (bgra8)
*/
static ALWAYS_INLINE __m128i
util_sse2_blend_srcalpha_4(const __m128i src,
const __m128i dst)
{
__m128i al, ah, dl, dh, sl, sh, rl, rh;
__m128i zero = _mm_setzero_si128();
/* Blend first two pixels:
*/
sl = _mm_unpacklo_epi8(src, zero);
dl = _mm_unpacklo_epi8(dst, zero);
al = _mm_shufflehi_epi16(sl, 0xff);
al = _mm_shufflelo_epi16(al, 0xff);
rl = util_sse2_lerp_epi16(al, dl, sl);
/* Blend second two pixels:
*/
sh = _mm_unpackhi_epi8(src, zero);
dh = _mm_unpackhi_epi8(dst, zero);
ah = _mm_shufflehi_epi16(sh, 0xff);
ah = _mm_shufflelo_epi16(ah, 0xff);
rh = util_sse2_lerp_epi16(ah, dh, sh);
/* Pack the results down to four bgra8 pixels:
*/
return _mm_packus_epi16(rl, rh);
}
/**
* premultiplies src with constant alpha then
* does one/inv_src_alpha blend.
*
* src 16xi8 (normalized)
* dst 16xi8 (normalized)
* cst_alpha (constant alpha (u8 value))
*/
static ALWAYS_INLINE __m128i
util_sse2_blend_premul_src_4(const __m128i src,
const __m128i dst,
const unsigned cst_alpha)
{
__m128i srca, d, s, rl, rh;
__m128i zero = _mm_setzero_si128();
__m128i cst_alpha_vec = _mm_set1_epi16(cst_alpha);
/* Blend first two pixels:
*/
s = _mm_unpacklo_epi8(src, zero);
s = _mm_mullo_epi16(s, cst_alpha_vec);
/* the shift will cause some precision loss */
s = _mm_srli_epi16(s, 8);
srca = _mm_shufflehi_epi16(s, 0xff);
srca = _mm_shufflelo_epi16(srca, 0xff);
d = _mm_unpacklo_epi8(dst, zero);
rl = util_sse2_premul_blend_epi16(srca, d, s);
/* Blend second two pixels:
*/
s = _mm_unpackhi_epi8(src, zero);
s = _mm_mullo_epi16(s, cst_alpha_vec);
/* the shift will cause some precision loss */
s = _mm_srli_epi16(s, 8);
srca = _mm_shufflehi_epi16(s, 0xff);
srca = _mm_shufflelo_epi16(srca, 0xff);
d = _mm_unpackhi_epi8(dst, zero);
rh = util_sse2_premul_blend_epi16(srca, d, s);
/* Pack the results down to four bgra8 pixels:
*/
return _mm_packus_epi16(rl, rh);
}
/**
* Linear interpolation with SSE2.
*
* dst, src0, src1 are 16 x i8 vectors, with [0..255] normalized values.
*
* weight_lo and weight_hi should be a 8 x i16 vectors, in 8.8 fixed point
* format, for the low and high components.
* We'd want to pass these as values but MSVC limitation forces us to pass these
* as pointers since it will complain if more than 3 __m128 are passed by value.
*/
static ALWAYS_INLINE __m128i
util_sse2_lerp_epi8_fixed88(__m128i src0, __m128i src1,
const __m128i * restrict weight_lo,
const __m128i * restrict weight_hi)
{
const __m128i zero = _mm_setzero_si128();
__m128i src0_lo = _mm_unpacklo_epi8(src0, zero);
__m128i src0_hi = _mm_unpackhi_epi8(src0, zero);
__m128i src1_lo = _mm_unpacklo_epi8(src1, zero);
__m128i src1_hi = _mm_unpackhi_epi8(src1, zero);
__m128i dst_lo;
__m128i dst_hi;
dst_lo = util_sse2_lerp_epi16(*weight_lo, src0_lo, src1_lo);
dst_hi = util_sse2_lerp_epi16(*weight_hi, src0_hi, src1_hi);
return _mm_packus_epi16(dst_lo, dst_hi);
}
/**
* Linear interpolation with SSE2.
*
* dst, src0, src1 are 16 x i8 vectors, with [0..255] normalized values.
*
* weight should be a 16 x i8 vector, in 0.8 fixed point values.
*/
static ALWAYS_INLINE __m128i
util_sse2_lerp_epi8_fixed08(__m128i src0, __m128i src1,
__m128i weight)
{
const __m128i zero = _mm_setzero_si128();
__m128i weight_lo = _mm_unpacklo_epi8(weight, zero);
__m128i weight_hi = _mm_unpackhi_epi8(weight, zero);
return util_sse2_lerp_epi8_fixed88(src0, src1,
&weight_lo, &weight_hi);
}
/**
* Linear interpolation with SSE2.
*
* dst, src0, src1, and weight are 16 x i8 vectors, with [0..255] normalized
* values.
*/
static ALWAYS_INLINE __m128i
util_sse2_lerp_unorm8(__m128i src0, __m128i src1,
__m128i weight)
{
const __m128i zero = _mm_setzero_si128();
__m128i weight_lo = _mm_unpacklo_epi8(weight, zero);
__m128i weight_hi = _mm_unpackhi_epi8(weight, zero);
#if 0
/*
* Rescale from [0..255] to [0..256].
*/
weight_lo = _mm_add_epi16(weight_lo, _mm_srli_epi16(weight_lo, 7));
weight_hi = _mm_add_epi16(weight_hi, _mm_srli_epi16(weight_hi, 7));
#endif
return util_sse2_lerp_epi8_fixed88(src0, src1,
&weight_lo, &weight_hi);
}
/**
* Linear interpolation with SSE2.
*
* dst, src0, src1, src2, src3 are 16 x i8 vectors, with [0..255] normalized
* values.
*
* ws_lo, ws_hi, wt_lo, wt_hi should be a 8 x i16 vectors, in 8.8 fixed point
* format, for the low and high components.
* We'd want to pass these as values but MSVC limitation forces us to pass these
* as pointers since it will complain if more than 3 __m128 are passed by value.
*
* This uses ws_lo, ws_hi to interpolate between src0 and src1, as well as to
* interpolate between src2 and src3, then uses wt_lo and wt_hi to interpolate
* between the resulting vectors.
*/
static ALWAYS_INLINE __m128i
util_sse2_lerp_2d_epi8_fixed88(__m128i src0, __m128i src1,
const __m128i * restrict src2,
const __m128i * restrict src3,
const __m128i * restrict ws_lo,
const __m128i * restrict ws_hi,
const __m128i * restrict wt_lo,
const __m128i * restrict wt_hi)
{
const __m128i zero = _mm_setzero_si128();
__m128i src0_lo = _mm_unpacklo_epi8(src0, zero);
__m128i src0_hi = _mm_unpackhi_epi8(src0, zero);
__m128i src1_lo = _mm_unpacklo_epi8(src1, zero);
__m128i src1_hi = _mm_unpackhi_epi8(src1, zero);
__m128i src2_lo = _mm_unpacklo_epi8(*src2, zero);
__m128i src2_hi = _mm_unpackhi_epi8(*src2, zero);
__m128i src3_lo = _mm_unpacklo_epi8(*src3, zero);
__m128i src3_hi = _mm_unpackhi_epi8(*src3, zero);
__m128i dst_lo, dst01_lo, dst23_lo;
__m128i dst_hi, dst01_hi, dst23_hi;
dst01_lo = util_sse2_lerp_epi16(*ws_lo, src0_lo, src1_lo);
dst01_hi = util_sse2_lerp_epi16(*ws_hi, src0_hi, src1_hi);
dst23_lo = util_sse2_lerp_epi16(*ws_lo, src2_lo, src3_lo);
dst23_hi = util_sse2_lerp_epi16(*ws_hi, src2_hi, src3_hi);
dst_lo = util_sse2_lerp_epi16(*wt_lo, dst01_lo, dst23_lo);
dst_hi = util_sse2_lerp_epi16(*wt_hi, dst01_hi, dst23_hi);
return _mm_packus_epi16(dst_lo, dst_hi);
}
/**
* Stretch a row of pixels using linear filter.
*
* Uses Bresenham's line algorithm using 16.16 fixed point representation for
* the error term.
*
* @param dst_width destination width in pixels
* @param src_x start x0 in 16.16 fixed point format
* @param src_xstep step in 16.16. fixed point format
*
* @return final src_x value (i.e., src_x + dst_width*src_xstep)
*/
static ALWAYS_INLINE int32_t
util_sse2_stretch_row_8unorm(__m128i * restrict dst,
int32_t dst_width,
const uint32_t * restrict src,
int32_t src_x,
int32_t src_xstep)
{
int16_t error0, error1, error2, error3;
__m128i error_lo, error_hi, error_step;
assert(dst_width >= 0);
assert(dst_width % 4 == 0);
error0 = src_x;
error1 = error0 + src_xstep;
error2 = error1 + src_xstep;
error3 = error2 + src_xstep;
error_lo = _mm_setr_epi16(error0, error0, error0, error0,
error1, error1, error1, error1);
error_hi = _mm_setr_epi16(error2, error2, error2, error2,
error3, error3, error3, error3);
error_step = _mm_set1_epi16(src_xstep << 2);
dst_width >>= 2;
while (dst_width) {
uint16_t src_x0;
uint16_t src_x1;
uint16_t src_x2;
uint16_t src_x3;
__m128i src0, src1;
__m128i weight_lo, weight_hi;
/*
* It is faster to re-compute the coordinates in the scalar integer unit here,
* than to fetch the values from the SIMD integer unit.
*/
src_x0 = src_x >> 16;
src_x += src_xstep;
src_x1 = src_x >> 16;
src_x += src_xstep;
src_x2 = src_x >> 16;
src_x += src_xstep;
src_x3 = src_x >> 16;
src_x += src_xstep;
/*
* Fetch pairs of pixels 64bit at a time, and then swizzle them inplace.
*/
{
__m128i src_00_10 = _mm_loadl_epi64((const __m128i *)&src[src_x0]);
__m128i src_01_11 = _mm_loadl_epi64((const __m128i *)&src[src_x1]);
__m128i src_02_12 = _mm_loadl_epi64((const __m128i *)&src[src_x2]);
__m128i src_03_13 = _mm_loadl_epi64((const __m128i *)&src[src_x3]);
__m128i src_00_01_10_11 = _mm_unpacklo_epi32(src_00_10, src_01_11);
__m128i src_02_03_12_13 = _mm_unpacklo_epi32(src_02_12, src_03_13);
src0 = _mm_unpacklo_epi64(src_00_01_10_11, src_02_03_12_13);
src1 = _mm_unpackhi_epi64(src_00_01_10_11, src_02_03_12_13);
}
weight_lo = _mm_srli_epi16(error_lo, 8);
weight_hi = _mm_srli_epi16(error_hi, 8);
*dst = util_sse2_lerp_epi8_fixed88(src0, src1,
&weight_lo, &weight_hi);
error_lo = _mm_add_epi16(error_lo, error_step);
error_hi = _mm_add_epi16(error_hi, error_step);
++dst;
--dst_width;
}
return src_x;
}
#endif /* PIPE_ARCH_SSE */
#endif /* U_SSE_H_ */
+2 -2
View File
@@ -447,7 +447,7 @@ util_test_constant_buffer(struct pipe_context *ctx,
"MOV OUT[0], CONST[0][0]\n"
"END\n";
struct tgsi_token tokens[1000];
struct pipe_shader_state state;
struct pipe_shader_state state = {0};
if (!tgsi_text_translate(text, tokens, ARRAY_SIZE(tokens))) {
puts("Can't compile a fragment shader.");
@@ -743,7 +743,7 @@ test_texture_barrier(struct pipe_context *ctx, bool use_fbfetch,
}
struct tgsi_token tokens[1000];
struct pipe_shader_state state;
struct pipe_shader_state state = {0};
if (!tgsi_text_translate(text, tokens, ARRAY_SIZE(tokens))) {
assert(0);
+3
View File
@@ -38,6 +38,7 @@
#include "lp_setup.h"
#include "lp_query.h"
#include "lp_debug.h"
#include "lp_state.h"
/**
@@ -57,6 +58,8 @@ llvmpipe_clear(struct pipe_context *pipe,
if (!llvmpipe_check_render_cond(llvmpipe))
return;
llvmpipe_update_derived_clear(llvmpipe);
if (LP_PERF & PERF_NO_DEPTH)
buffers &= ~PIPE_CLEAR_DEPTHSTENCIL;
@@ -284,6 +284,9 @@ llvmpipe_create_context(struct pipe_screen *screen, void *priv,
draw_wide_point_threshold(llvmpipe->draw, 10000.0);
draw_wide_line_threshold(llvmpipe->draw, 10000.0);
/* initial state for clipping - enabled, with no guardband */
draw_set_driver_clipping(llvmpipe->draw, FALSE, FALSE, FALSE, TRUE);
lp_reset_counters();
/* If llvmpipe_set_scissor_states() is never called, we still need to
@@ -156,6 +156,9 @@ struct llvmpipe_context {
unsigned nr_fs_variants;
unsigned nr_fs_instrs;
boolean permit_linear_rasterizer;
boolean single_vp;
struct lp_setup_variant_list_item setup_variants_list;
unsigned nr_setup_variants;
+7
View File
@@ -47,6 +47,11 @@
#define DEBUG_CS 0x10000
#define DEBUG_TGSI_IR 0x20000
#define DEBUG_CACHE_STATS 0x40000
#define DEBUG_NO_FASTPATH 0x80000
#define DEBUG_LINEAR 0x100000
#define DEBUG_LINEAR2 0x200000
#define DEBUG_SHOW_DEPTH 0x400000
#define DEBUG_ACCURATE_A0 0x800000 /* verbose */
/* Performance flags. These are active even on release builds.
*/
@@ -58,6 +63,8 @@
#define PERF_NO_BLEND 0x20 /* disable blending */
#define PERF_NO_DEPTH 0x40 /* disable depth buffering entirely */
#define PERF_NO_ALPHATEST 0x80 /* disable alpha testing */
#define PERF_NO_RAST_LINEAR 0x100 /* disable linear rast */
#define PERF_NO_SHADE 0x200 /* disable fragment shaders */
extern int LP_PERF;
+70
View File
@@ -39,6 +39,7 @@
#include "gallivm/lp_bld_debug.h"
#include "gallivm/lp_bld_format.h"
#include "lp_context.h"
#include "lp_screen.h"
#include "lp_jit.h"
static LLVMTypeRef
@@ -189,6 +190,7 @@ lp_jit_create_types(struct lp_fragment_shader_variant *lp)
struct gallivm_state *gallivm = lp->gallivm;
LLVMContextRef lc = gallivm->context;
LLVMTypeRef viewport_type, texture_type, sampler_type, image_type;
LLVMTypeRef linear_elem_type;
/* struct lp_jit_viewport */
{
@@ -314,6 +316,74 @@ lp_jit_create_types(struct lp_fragment_shader_variant *lp)
lp->jit_thread_data_ptr_type = LLVMPointerType(thread_data_type, 0);
}
/*
* lp_linear_elem
*
* XXX: it can be instanced only once due to the use of opaque types, and
* the fact that screen->module is also a global.
*/
{
LLVMTypeRef ret_type;
LLVMTypeRef arg_types[1];
LLVMTypeRef func_type;
ret_type = LLVMPointerType(LLVMVectorType(LLVMInt8TypeInContext(lc), 16), 0);
arg_types[0] = LLVMPointerType(LLVMInt8TypeInContext(lc), 0);
/* lp_linear_func */
func_type = LLVMFunctionType(ret_type, arg_types, ARRAY_SIZE(arg_types), 0);
/*
* We actually define lp_linear_elem not as a structure but simply as a
* lp_linear_func pointer
*/
linear_elem_type = LLVMPointerType(func_type, 0);
}
/* struct lp_jit_linear_context */
{
LLVMTypeRef linear_elem_ptr_type = LLVMPointerType(linear_elem_type, 0);
LLVMTypeRef elem_types[LP_JIT_LINEAR_CTX_COUNT];
LLVMTypeRef linear_context_type;
elem_types[LP_JIT_LINEAR_CTX_CONSTANTS] = LLVMPointerType(LLVMInt8TypeInContext(lc), 0);
elem_types[LP_JIT_LINEAR_CTX_TEX] =
LLVMArrayType(linear_elem_ptr_type, LP_MAX_LINEAR_TEXTURES);
elem_types[LP_JIT_LINEAR_CTX_INPUTS] =
LLVMArrayType(linear_elem_ptr_type, LP_MAX_LINEAR_INPUTS);
elem_types[LP_JIT_LINEAR_CTX_COLOR0] = LLVMPointerType(LLVMInt8TypeInContext(lc), 0);
elem_types[LP_JIT_LINEAR_CTX_BLEND_COLOR] = LLVMInt32TypeInContext(lc);
elem_types[LP_JIT_LINEAR_CTX_ALPHA_REF] = LLVMInt8TypeInContext(lc);
linear_context_type = LLVMStructTypeInContext(lc, elem_types,
ARRAY_SIZE(elem_types), 0);
LP_CHECK_MEMBER_OFFSET(struct lp_jit_linear_context, constants,
gallivm->target, linear_context_type,
LP_JIT_LINEAR_CTX_CONSTANTS);
LP_CHECK_MEMBER_OFFSET(struct lp_jit_linear_context, tex,
gallivm->target, linear_context_type,
LP_JIT_LINEAR_CTX_TEX);
LP_CHECK_MEMBER_OFFSET(struct lp_jit_linear_context, inputs,
gallivm->target, linear_context_type,
LP_JIT_LINEAR_CTX_INPUTS);
LP_CHECK_MEMBER_OFFSET(struct lp_jit_linear_context, color0,
gallivm->target, linear_context_type,
LP_JIT_LINEAR_CTX_COLOR0);
LP_CHECK_MEMBER_OFFSET(struct lp_jit_linear_context, blend_color,
gallivm->target, linear_context_type,
LP_JIT_LINEAR_CTX_BLEND_COLOR);
LP_CHECK_MEMBER_OFFSET(struct lp_jit_linear_context, alpha_ref_value,
gallivm->target, linear_context_type,
LP_JIT_LINEAR_CTX_ALPHA_REF);
LP_CHECK_STRUCT_SIZE(struct lp_jit_linear_context,
gallivm->target, linear_context_type);
lp->jit_linear_context_ptr_type = LLVMPointerType(linear_context_type, 0);
}
if (gallivm_debug & GALLIVM_DEBUG_IR) {
char *str = LLVMPrintModuleToString(gallivm->module);
fprintf(stderr, "%s", str);
+88
View File
@@ -324,6 +324,94 @@ typedef void
unsigned depth_sample_stride);
#define LP_MAX_LINEAR_CONSTANTS 16
#define LP_MAX_LINEAR_TEXTURES 2
#define LP_MAX_LINEAR_INPUTS 8
/**
* This structure is passed directly to the generated fragment shader.
*
* It contains the derived state.
*
* Changes here must be reflected in the lp_jit_linear_context_* macros and
* lp_jit_init_types function. 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 lp_jit_linear_context
{
/**
* Constants in 8bit unorm values.
*/
const uint8_t (*constants)[4];
struct lp_linear_elem *tex[LP_MAX_LINEAR_TEXTURES];
struct lp_linear_elem *inputs[LP_MAX_LINEAR_INPUTS];
uint8_t *color0;
uint32_t blend_color;
uint8_t alpha_ref_value;
};
/**
* These enum values must match the position of the fields in the
* lp_jit_linear_context struct above.
*/
enum {
LP_JIT_LINEAR_CTX_CONSTANTS = 0,
LP_JIT_LINEAR_CTX_TEX,
LP_JIT_LINEAR_CTX_INPUTS,
LP_JIT_LINEAR_CTX_COLOR0,
LP_JIT_LINEAR_CTX_BLEND_COLOR,
LP_JIT_LINEAR_CTX_ALPHA_REF,
LP_JIT_LINEAR_CTX_COUNT
};
#define lp_jit_linear_context_constants(_gallivm, _ptr) \
lp_build_struct_get(_gallivm, _ptr, LP_JIT_LINEAR_CTX_CONSTANTS, "constants")
#define lp_jit_linear_context_tex(_gallivm, _ptr) \
lp_build_struct_get_ptr(_gallivm, _ptr, LP_JIT_LINEAR_CTX_TEX, "tex")
#define lp_jit_linear_context_inputs(_gallivm, _ptr) \
lp_build_struct_get_ptr(_gallivm, _ptr, LP_JIT_LINEAR_CTX_INPUTS, "inputs")
#define lp_jit_linear_context_color0(_gallivm, _ptr) \
lp_build_struct_get_ptr(_gallivm, _ptr, LP_JIT_LINEAR_CTX_COLOR0, "color0")
#define lp_jit_linear_context_blend_color(_gallivm, _ptr) \
lp_build_struct_get_ptr(_gallivm, _ptr, LP_JIT_LINEAR_CTX_BLEND_COLOR, "blend_color")
#define lp_jit_linear_context_alpha_ref(_gallivm, _ptr) \
lp_build_struct_get_ptr(_gallivm, _ptr, LP_JIT_LINEAR_CTX_ALPHA_REF, "alpha_ref_value")
typedef const uint8_t *
(*lp_jit_linear_llvm_func)(struct lp_jit_linear_context *context,
uint32_t x,
uint32_t y,
uint32_t w);
/* We're not really jitting this, but I need to get into the
* rast_state struct to call the function we actually are jitting.
*/
struct lp_rast_state;
typedef boolean
(*lp_jit_linear_func)(const struct lp_rast_state *state,
uint32_t x,
uint32_t y,
uint32_t w,
uint32_t h,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
uint32_t color_stride);
struct lp_jit_cs_thread_data
{
struct lp_build_format_cache *cache;
-5
View File
@@ -69,11 +69,6 @@
#define LP_MAX_THREADS 16
/**
* Max bytes per scene. This may be replaced by a runtime parameter.
*/
#define LP_MAX_SCENE_SIZE (512 * 1024 * 1024)
/**
* Max number of shader variants (for all shaders combined,
* per context) that will be kept around.
+363
View File
@@ -0,0 +1,363 @@
/**************************************************************************
*
* Copyright 2010-2021 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 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
* THE COPYRIGHT HOLDERS, AUTHORS 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.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
**************************************************************************/
#include "pipe/p_config.h"
#include "util/u_math.h"
#include "util/u_cpu_detect.h"
#include "util/u_pack_color.h"
#include "util/u_rect.h"
#include "util/u_sse.h"
#include "lp_jit.h"
#include "lp_rast.h"
#include "lp_debug.h"
#include "lp_state_fs.h"
#include "lp_linear_priv.h"
#if defined(PIPE_ARCH_SSE)
/* For debugging (LP_DEBUG=linear), shade areas of run-time fallback
* purple. Keep blending active so we can see more of what's going
* on.
*/
static boolean
linear_fallback(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
uint8_t *color,
unsigned stride)
{
unsigned col = 0x808000ff;
int i;
for (y = 0; y < height; y++) {
for (i = 0; i < 64; i++) {
*((uint32_t *)(color + y*stride) + x + i) = col;
}
}
return TRUE;
}
/* Run our configurable linear shader pipeline:
*/
static boolean
lp_fs_linear_run(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
const struct lp_fragment_shader_variant *variant = state->variant;
const struct lp_tgsi_info *info = &variant->shader->info;
struct lp_jit_linear_context jit;
lp_jit_linear_llvm_func jit_func = variant->jit_linear_llvm;
struct lp_linear_sampler samp[LP_MAX_LINEAR_TEXTURES];
struct lp_linear_interp interp[LP_MAX_LINEAR_INPUTS];
uint8_t constants[LP_MAX_LINEAR_CONSTANTS][4];
const float w0 = a0[0][3];
float oow = 1.0f/w0;
unsigned input_mask = variant->linear_input_mask;
int nr_consts = info->base.file_max[TGSI_FILE_CONSTANT]+1;
int nr_tex = info->num_texs;
int i, j;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
/* Require constant w in these rectangles:
*/
if (dadx[0][3] != 0.0f ||
dady[0][3] != 0.0f) {
if (LP_DEBUG & DEBUG_LINEAR2)
debug_printf(" -- w not constant\n");
goto fail;
}
/* XXX: Per statechange:
*/
for (i = 0; i < nr_consts; i++) {
for (j = 0; j < 4; j++) {
float val = state->jit_context.constants[0][i*4+j];
if (val < 0.0f || val > 1.0f) {
if (LP_DEBUG & DEBUG_LINEAR2)
debug_printf(" -- const[%d] out of range\n", i);
goto fail;
}
constants[i][j] = (uint8_t)(val * 255.0f);
}
}
jit.constants = (const uint8_t (*)[4])constants;
/* We assume BGRA ordering */
assert(variant->key.cbuf_format[0] == PIPE_FORMAT_B8G8R8X8_UNORM ||
variant->key.cbuf_format[0] == PIPE_FORMAT_B8G8R8A8_UNORM);
jit.blend_color =
state->jit_context.u8_blend_color[32] +
(state->jit_context.u8_blend_color[16] << 8) +
(state->jit_context.u8_blend_color[0] << 16) +
(state->jit_context.u8_blend_color[48] << 24);
jit.alpha_ref_value = float_to_ubyte(state->jit_context.alpha_ref_value);
/* XXX: Per primitive:
*/
while (input_mask) {
int i = u_bit_scan(&input_mask);
unsigned usage_mask = info->base.input_usage_mask[i];
boolean perspective =
info->base.input_interpolate[i] == TGSI_INTERPOLATE_PERSPECTIVE ||
(info->base.input_interpolate[i] == TGSI_INTERPOLATE_COLOR &&
!variant->key.flatshade);
if (!lp_linear_init_interp(&interp[i],
x, y, width, height,
usage_mask,
perspective,
oow,
a0[i+1],
dadx[i+1],
dady[i+1])) {
if (LP_DEBUG & DEBUG_LINEAR2)
debug_printf(" -- init_interp(%d) failed\n", i);
goto fail;
}
jit.inputs[i] = &interp[i].base;
}
/* XXX: Per primitive: Initialize linear or nearest samplers:
*/
for (i = 0; i < nr_tex; i++) {
const struct lp_tgsi_texture_info *tex_info = &info->tex[i];
unsigned unit = tex_info->sampler_unit;
/* XXX: some texture coordinates are linear!
*/
//boolean perspective = (info->base.input_interpolate[i] ==
// TGSI_INTERPOLATE_PERSPECTIVE);
if (!lp_linear_init_sampler(&samp[i],
tex_info,
&variant->key.samplers[unit],
&state->jit_context.textures[unit],
x, y, width, height,
a0, dadx, dady)) {
if (LP_DEBUG & DEBUG_LINEAR2)
debug_printf(" -- init_sampler(%d) failed\n", i);
goto fail;
}
jit.tex[i] = &samp[i].base;
}
/* JIT function already does blending */
jit.color0 = color + x * 4 + y * stride;
for (y = 0; y < height; y++) {
jit_func(&jit, 0, 0, width);
jit.color0 += stride;
}
return TRUE;
fail:
/* Visually distinguish this from other fallbacks:
*/
if (LP_DEBUG & DEBUG_LINEAR) {
return linear_fallback(state, x, y, width, height, color, stride);
}
return FALSE;
}
static void
check_linear_interp_mask_a(struct lp_fragment_shader_variant *variant)
{
const struct lp_tgsi_info *info = &variant->shader->info;
struct lp_jit_linear_context jit;
struct lp_linear_sampler samp[LP_MAX_LINEAR_TEXTURES];
struct lp_linear_interp interp[LP_MAX_LINEAR_INPUTS];
uint8_t constants[LP_MAX_LINEAR_CONSTANTS][4];
PIPE_ALIGN_VAR(16) uint8_t color0[TILE_SIZE*4];
int nr_inputs = info->base.file_max[TGSI_FILE_INPUT]+1;
int nr_tex = info->num_texs;
int i;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
jit.constants = (const uint8_t (*)[4])constants;
for (i = 0; i < nr_tex; i++) {
lp_linear_init_noop_sampler(&samp[i]);
jit.tex[i] = &samp[i].base;
}
for (i = 0; i < nr_inputs; i++) {
lp_linear_init_noop_interp(&interp[i]);
jit.inputs[i] = &interp[i].base;
}
jit.color0 = color0;
(void)variant->jit_linear_llvm(&jit, 0, 0, 0);
/* Find out which interpolators were called, and store this as a
* mask:
*/
for (i = 0; i < nr_inputs; i++)
variant->linear_input_mask |= (interp[i].row[0] << i);
}
/* Until the above is working, look at texture information and guess
* that any input used as a texture coordinate is not used for
* anything else.
*/
static void
check_linear_interp_mask_b(struct lp_fragment_shader_variant *variant)
{
const struct lp_tgsi_info *info = &variant->shader->info;
int nr_inputs = info->base.file_max[TGSI_FILE_INPUT]+1;
int nr_tex = info->num_texs;
unsigned tex_mask = 0;
int i;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
for (i = 0; i < nr_tex; i++) {
const struct lp_tgsi_texture_info *tex_info = &info->tex[i];
const struct lp_tgsi_channel_info *schan = &tex_info->coord[0];
const struct lp_tgsi_channel_info *tchan = &tex_info->coord[1];
tex_mask |= 1 << schan->u.index;
tex_mask |= 1 << tchan->u.index;
}
variant->linear_input_mask = ((1 << nr_inputs) - 1) & ~tex_mask;
}
void
lp_linear_check_variant(struct lp_fragment_shader_variant *variant)
{
const struct lp_fragment_shader_variant_key *key = &variant->key;
const struct lp_fragment_shader *shader = variant->shader;
const struct lp_tgsi_info *info = &shader->info;
int i;
if (info->base.file_max[TGSI_FILE_CONSTANT] >= LP_MAX_LINEAR_CONSTANTS ||
info->base.file_max[TGSI_FILE_INPUT] >= LP_MAX_LINEAR_INPUTS) {
if (LP_DEBUG & DEBUG_LINEAR)
debug_printf(" -- too many inputs/constants\n");
goto fail;
}
/* If we have a fastpath which implements the entire varient, use
* that.
*/
if (lp_linear_check_fastpath(variant)) {
return;
}
/* Otherwise, can we build up a spanline-based linear path for this
* variant?
*/
/* Check static sampler state.
*/
for (i = 0; i < info->num_texs; i++) {
const struct lp_tgsi_texture_info *tex_info = &info->tex[i];
unsigned unit = tex_info->sampler_unit;
/* XXX: Relax this once setup premultiplies by oow:
*/
if (info->base.input_interpolate[unit] != TGSI_INTERPOLATE_PERSPECTIVE) {
if (LP_DEBUG & DEBUG_LINEAR)
debug_printf(" -- samp[%d]: texcoord not perspective\n", i);
goto fail;
}
if (!lp_linear_check_sampler(&key->samplers[unit], tex_info)) {
if (LP_DEBUG & DEBUG_LINEAR)
debug_printf(" -- samp[%d]: check_sampler failed\n", i);
goto fail;
}
}
/* Check shader. May not have been jitted.
*/
if (variant->linear_function == NULL) {
if (LP_DEBUG & DEBUG_LINEAR)
debug_printf(" -- no linear shader\n");
goto fail;
}
/* Hook in the catchall shader runner:
*/
variant->jit_linear = lp_fs_linear_run;
/* Figure out which inputs we don't need to interpolate (because
* they are only used as texture coordinates). This is important
* as we can cope with texture coordinates which exceed 1.0, but
* cannot do so for regular inputs.
*/
if (1)
check_linear_interp_mask_a(variant);
else
check_linear_interp_mask_b(variant);
if (0) {
lp_debug_fs_variant(variant);
debug_printf("linear input mask: 0x%x\n", variant->linear_input_mask);
}
return;
fail:
if (LP_DEBUG & DEBUG_LINEAR) {
lp_debug_fs_variant(variant);
debug_printf(" ----> no linear path for this variant\n");
}
}
#endif
@@ -0,0 +1,237 @@
/**************************************************************************
*
* Copyright 2010-2021 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 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
* THE COPYRIGHT HOLDERS, AUTHORS 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.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
**************************************************************************/
#include "pipe/p_config.h"
#include "util/u_math.h"
#include "util/u_cpu_detect.h"
#include "util/u_pack_color.h"
#include "util/u_surface.h"
#include "util/u_sse.h"
#include "lp_jit.h"
#include "lp_rast.h"
#include "lp_debug.h"
#include "lp_state_fs.h"
#include "lp_linear_priv.h"
#if defined(PIPE_ARCH_SSE)
/* This file contains various special-case fastpaths which implement
* the entire linear pipeline in a single funciton.
*
* These include simple blits and some debug code.
*
* These functions fully implement the linear path and do not need to
* be combined with blending, interpolation or sampling routines.
*/
/* Linear shader which implements the BLIT_RGBA shader with the
* additional constraints imposed by lp_setup_is_blit().
*/
static boolean
lp_linear_blit_rgba_blit(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
const struct lp_jit_context *context = &state->jit_context;
const struct lp_jit_texture *texture = &context->textures[0];
const uint8_t *src;
unsigned src_stride;
int src_x, src_y;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
/* Require w==1.0:
*/
if (a0[0][3] != 1.0 ||
dadx[0][3] != 0.0 ||
dady[0][3] != 0.0)
return FALSE;
src_x = x + util_iround(a0[1][0]*texture->width - 0.5f);
src_y = y + util_iround(a0[1][1]*texture->height - 0.5f);
src = texture->base;
src_stride = texture->row_stride[0];
/* Fall back to blit_rgba() if clamping required:
*/
if (src_x < 0 ||
src_y < 0 ||
src_x + width > texture->width ||
src_y + height > texture->height)
return FALSE;
util_copy_rect(color, PIPE_FORMAT_B8G8R8A8_UNORM, stride,
x, y,
width, height,
src, src_stride,
src_x, src_y);
return TRUE;
}
/* Linear shader which implements the BLIT_RGB1 shader, with the
* additional constraints imposed by lp_setup_is_blit().
*/
static boolean
lp_linear_blit_rgb1_blit(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
const struct lp_jit_context *context = &state->jit_context;
const struct lp_jit_texture *texture = &context->textures[0];
const uint8_t *src;
unsigned src_stride;
int src_x, src_y;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
/* Require w==1.0:
*/
if (a0[0][3] != 1.0 ||
dadx[0][3] != 0.0 ||
dady[0][3] != 0.0)
return FALSE;
color += x * 4 + y * stride;
src_x = x + util_iround(a0[1][0]*texture->width - 0.5f);
src_y = y + util_iround(a0[1][1]*texture->height - 0.5f);
src = texture->base;
src_stride = texture->row_stride[0];
src += src_x * 4;
src += src_y * src_stride;
if (src_x < 0 ||
src_y < 0 ||
src_x + width > texture->width ||
src_y + height > texture->height)
return FALSE;
for (y = 0; y < height; y++) {
const uint32_t *src_row = (const uint32_t *)src;
uint32_t *dst_row = (uint32_t *)color;
for (x = 0; x < width; x++) {
*dst_row++ = *src_row++ | 0xff000000;
}
color += stride;
src += src_stride;
}
return TRUE;
}
/* Linear shader which always emits purple. Used for debugging.
*/
static boolean
lp_linear_purple(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
union util_color uc;
util_pack_color_ub(0xff, 0, 0xff, 0xff,
PIPE_FORMAT_B8G8R8A8_UNORM, &uc);
util_fill_rect(color,
PIPE_FORMAT_B8G8R8A8_UNORM,
stride,
x,
y,
width,
height,
&uc);
return TRUE;
}
/* Examine the fragment shader varient and determine whether we can
* substitute a fastpath linear shader implementation.
*/
boolean
lp_linear_check_fastpath(struct lp_fragment_shader_variant *variant)
{
enum pipe_format tex_format = variant->key.samplers[0].texture_state.format;
if (variant->shader->kind == LP_FS_KIND_BLIT_RGBA &&
tex_format == PIPE_FORMAT_B8G8R8A8_UNORM &&
is_nearest_clamp_sampler(&variant->key.samplers[0]) &&
variant->opaque) {
variant->jit_linear_blit = lp_linear_blit_rgba_blit;
}
if (variant->shader->kind == LP_FS_KIND_BLIT_RGB1 &&
variant->opaque &&
(tex_format == PIPE_FORMAT_B8G8R8A8_UNORM ||
tex_format == PIPE_FORMAT_B8G8R8X8_UNORM) &&
is_nearest_clamp_sampler(&variant->key.samplers[0])) {
variant->jit_linear_blit = lp_linear_blit_rgb1_blit;
}
if (0) {
variant->jit_linear = lp_linear_purple;
}
/* Stop now if jit_linear has been initialized. Otherwise keep
* searching - even if jit_linear_blit has been instantiated.
*/
return variant->jit_linear != NULL;
}
#else
void
boolean
lp_linear_check_fastpath(struct lp_fragment_shader_variant *variant)
{
return FALSE;
}
#endif
@@ -0,0 +1,243 @@
/**************************************************************************
*
* Copyright 2010-2021 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 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
* THE COPYRIGHT HOLDERS, AUTHORS 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.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
**************************************************************************/
#include "pipe/p_config.h"
#include "util/u_math.h"
#include "util/u_cpu_detect.h"
#include "util/u_pack_color.h"
#include "util/u_rect.h"
#include "util/u_sse.h"
#include "lp_jit.h"
#include "lp_rast.h"
#include "lp_debug.h"
#include "lp_state_fs.h"
#include "lp_linear_priv.h"
#if defined(PIPE_ARCH_SSE)
#define FIXED15_ONE 0x7fff
/* Translate floating point value to 1.15 unsigned fixed-point.
*/
static inline ushort
float_to_ufixed_1_15(float f)
{
return CLAMP((unsigned)(f * (float)FIXED15_ONE), 0, FIXED15_ONE);
}
/* Translate floating point value to 1.15 signed fixed-point.
*/
static inline int16_t
float_to_sfixed_1_15(float f)
{
return CLAMP((signed)(f * (float)FIXED15_ONE), -FIXED15_ONE, FIXED15_ONE);
}
/* Interpolate in 1.15 space, but produce a packed row of 0.8 values.
*/
static const uint32_t *
interp_0_8(struct lp_linear_elem *elem)
{
struct lp_linear_interp *interp = (struct lp_linear_interp *)elem;
uint32_t *row = interp->row;
__m128i a0 = interp->a0;
__m128i dadx = interp->dadx;
int width = (interp->width + 3) & ~3;
int i;
for (i = 0; i < width; i += 4) {
__m128i l, h;
l = _mm_srai_epi16(a0, 7);
a0 = _mm_add_epi16(a0, dadx);
h = _mm_srai_epi16(a0, 7);
a0 = _mm_add_epi16(a0, dadx);
*(__m128i *)&row[i] = _mm_packus_epi16(l, h);
}
interp->a0 = _mm_add_epi16(interp->a0, interp->dady);
return interp->row;
}
static const uint32_t *
interp_noop(struct lp_linear_elem *elem)
{
struct lp_linear_interp *interp = (struct lp_linear_interp *)elem;
return interp->row;
}
static const uint32_t *
interp_check(struct lp_linear_elem *elem)
{
struct lp_linear_interp *interp = (struct lp_linear_interp *)elem;
interp->row[0] = 1;
return interp->row;
}
/* Not quite a noop - we use row[0] to track whether this gets called
* or not, so we can optimize which interpolants we care about.
*/
void
lp_linear_init_noop_interp(struct lp_linear_interp *interp)
{
interp->row[0] = 0;
interp->base.fetch = interp_check;
}
boolean
lp_linear_init_interp(struct lp_linear_interp *interp,
int x, int y, int width, int height,
unsigned usage_mask,
boolean perspective,
float oow,
const float *a0,
const float *dadx,
const float *dady)
{
float s0[4];
float dsdx[4];
float dsdy[4];
int16_t s0_fp[8];
int16_t dsdx_fp[4];
int16_t dsdy_fp[4];
int j;
/* Zero coefficients to avoid using uninitialised values */
memset(s0, 0, sizeof(s0));
memset(dsdx, 0, sizeof(dsdx));
memset(dsdy, 0, sizeof(dsdy));
memset(s0_fp, 0, sizeof(s0_fp));
memset(dsdx_fp, 0, sizeof(dsdx_fp));
memset(dsdy_fp, 0, sizeof(dsdy_fp));
if (perspective) {
for (j = 0; j < 4; j++) {
if (usage_mask & (1<<j)) {
s0[j] = a0[j] * oow;
dsdx[j] = dadx[j] * oow;
dsdy[j] = dady[j] * oow;
}
}
} else {
for (j = 0; j < 4; j++) {
if (usage_mask & (1<<j)) {
s0[j] = a0[j];
dsdx[j] = dadx[j];
dsdy[j] = dady[j];
}
}
}
s0[0] += x * dsdx[0] + y * dsdy[0];
s0[1] += x * dsdx[1] + y * dsdy[1];
s0[2] += x * dsdx[2] + y * dsdy[2];
s0[3] += x * dsdx[3] + y * dsdy[3];
/* XXX: lift all of this into the rectangle setup code.
*
* For rectangles with linear shaders, at setup time:
* - if w is constant (else mark as non-fastpath)
* - premultiply perspective interpolants by w
* - set w = 1 in position
* - check all interpolants for min/max 0..1 (else mark as
* non-fastpath)
*/
for (j = 0; j < 4; j++) {
if (usage_mask & (1<<j)) {
float a = s0[j];
float b = s0[j] + (width - 1) * dsdx[j];
float c = s0[j] + (height - 1) * dsdy[j];
float d = s0[j] + (height - 1) * dsdy[j] + (width - 1) * dsdx[j];
if (MIN4(a,b,c,d) < 0.0)
FAIL("min < 0.0");
if (MAX4(a,b,c,d) > 1.0)
FAIL("max > 1.0");
dsdx_fp[j] = float_to_sfixed_1_15(dsdx[j]);
dsdy_fp[j] = float_to_sfixed_1_15(dsdy[j]);
s0_fp[j] = float_to_ufixed_1_15(s0[j]);
s0_fp[j + 4] = s0_fp[j] + dsdx_fp[j];
dsdx_fp[j] *= 2;
}
}
interp->width = align(width, 4);
interp->a0 = _mm_setr_epi16(s0_fp[2], s0_fp[1], s0_fp[0], s0_fp[3],
s0_fp[6], s0_fp[5], s0_fp[4], s0_fp[7]);
interp->dadx = _mm_setr_epi16(dsdx_fp[2], dsdx_fp[1], dsdx_fp[0], dsdx_fp[3],
dsdx_fp[2], dsdx_fp[1], dsdx_fp[0], dsdx_fp[3]);
interp->dady = _mm_setr_epi16(dsdy_fp[2], dsdy_fp[1], dsdy_fp[0], dsdy_fp[3],
dsdy_fp[2], dsdy_fp[1], dsdy_fp[0], dsdy_fp[3]);
/* If the value is y-invariant, eagerly calculate it here and then
* always return the precalculated value.
*/
if (dsdy[0] == 0 &&
dsdy[1] == 0 &&
dsdy[2] == 0 &&
dsdy[3] == 0)
{
interp_0_8(&interp->base);
interp->base.fetch = interp_noop;
}
else {
interp->base.fetch = interp_0_8;
}
return TRUE;
}
#else
boolean
lp_linear_init_interp(struct lp_linear_interp *interp,
int x, int y, int width, int height,
unsigned usage_mask,
boolean perspective,
float oow,
const float *a0,
const float *dadx,
const float *dady)
{
return FALSE;
}
#endif
@@ -0,0 +1,169 @@
#ifndef LP_LINEAR_PRIV_H
#define LP_LINEAR_PRIV_H
struct lp_linear_elem;
typedef const uint32_t *(*lp_linear_func)(struct lp_linear_elem *base);
struct lp_linear_elem {
lp_linear_func fetch;
};
/* "Linear" refers to the fact we're on the linear (non-swizzled)
* rasterization path. Filtering mode may be either linear or
* nearest.
*/
struct lp_linear_sampler {
struct lp_linear_elem base;
const struct lp_jit_texture *texture;
int s; /* 16.16, biased by .5 */
int t; /* 16.16, biased by .5 */
int dsdx; /* 16.16 */
int dsdy; /* 16.16 */
int dtdx; /* 16.16 */
int dtdy; /* 16.16 */
int width;
boolean axis_aligned;
PIPE_ALIGN_VAR(16) uint32_t row[64];
PIPE_ALIGN_VAR(16) uint32_t stretched_row[2][64];
/**
* y coordinate of the rows stored in the stretched_row.
*
* Negative number means no stretched row is cached.
*/
int stretched_row_y[2];
/**
* The index of stretched_row to receive the next stretched row.
*/
int stretched_row_index;
};
/* "Linear" refers to the fact we're on the linear (non-swizzled)
* rasterization path. Interpolation mode may be either constant,
* linear or perspective.
*/
struct lp_linear_interp {
struct lp_linear_elem base;
__m128i a0;
__m128i dadx;
__m128i dady;
int width; /* rounded up to multiple of 4 */
PIPE_ALIGN_VAR(16) uint32_t row[64];
};
/* Check for a sampler variant which matches our fetch_row
* implementation - normalized texcoords, single mipmap with
* nearest filtering.
*/
static inline boolean
is_nearest_sampler(const struct lp_sampler_static_state *sampler)
{
return
sampler->texture_state.target == PIPE_TEXTURE_2D &&
sampler->sampler_state.min_img_filter == PIPE_TEX_FILTER_NEAREST &&
sampler->sampler_state.mag_img_filter == PIPE_TEX_FILTER_NEAREST &&
(sampler->texture_state.level_zero_only ||
sampler->sampler_state.min_mip_filter == PIPE_TEX_MIPFILTER_NONE) &&
sampler->sampler_state.compare_mode == 0 &&
sampler->sampler_state.normalized_coords == 1;
}
/* Check for a sampler variant which matches our fetch_row
* implementation - normalized texcoords, single mipmap with
* linear filtering.
*/
static inline boolean
is_linear_sampler(const struct lp_sampler_static_state *sampler)
{
return
sampler->texture_state.target == PIPE_TEXTURE_2D &&
sampler->sampler_state.min_img_filter == PIPE_TEX_FILTER_LINEAR &&
sampler->sampler_state.mag_img_filter == PIPE_TEX_FILTER_LINEAR &&
(sampler->texture_state.level_zero_only ||
sampler->sampler_state.min_mip_filter == PIPE_TEX_MIPFILTER_NONE) &&
sampler->sampler_state.compare_mode == 0 &&
sampler->sampler_state.normalized_coords == 1;
}
/* Check for a sampler variant which matches is_nearest_sampler
* but has the additional constraints of using clamp wrapping
*/
static inline boolean
is_nearest_clamp_sampler(const struct lp_sampler_static_state *sampler)
{
return
is_nearest_sampler(sampler) &&
sampler->sampler_state.wrap_s == PIPE_TEX_WRAP_CLAMP_TO_EDGE &&
sampler->sampler_state.wrap_t == PIPE_TEX_WRAP_CLAMP_TO_EDGE;
}
/* Check for a sampler variant which matches is_linear_sampler
* but has the additional constraints of using clamp wrapping
*/
static inline boolean
is_linear_clamp_sampler(const struct lp_sampler_static_state *sampler)
{
return
is_linear_sampler(sampler) &&
sampler->sampler_state.wrap_s == PIPE_TEX_WRAP_CLAMP_TO_EDGE &&
sampler->sampler_state.wrap_t == PIPE_TEX_WRAP_CLAMP_TO_EDGE;
}
boolean
lp_linear_init_interp(struct lp_linear_interp *interp,
int x, int y, int width, int height,
unsigned usage_mask,
boolean perspective,
float oow,
const float *a0,
const float *dadx,
const float *dady);
boolean
lp_linear_init_sampler(struct lp_linear_sampler *samp,
const struct lp_tgsi_texture_info *info,
const struct lp_sampler_static_state *sampler_state,
const struct lp_jit_texture *texture,
int x0, int y0, int width, int height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4]);
boolean
lp_linear_check_fastpath(struct lp_fragment_shader_variant *variant);
boolean
lp_linear_check_sampler(const struct lp_sampler_static_state *sampler,
const struct lp_tgsi_texture_info *tex);
void
lp_linear_init_noop_interp(struct lp_linear_interp *interp);
void
lp_linear_init_noop_sampler(struct lp_linear_sampler *samp);
#define FAIL(s) do { \
if (LP_DEBUG & DEBUG_LINEAR) \
debug_printf("%s: %s\n", __FUNCTION__, s); \
return FALSE; \
} while (0)
#endif
File diff suppressed because it is too large Load Diff
+16
View File
@@ -50,6 +50,8 @@ lp_print_counters(void)
debug_printf("llvmpipe: nr_triangles: %9u\n", lp_count.nr_tris);
debug_printf("llvmpipe: nr_culled_triangles: %9u\n", lp_count.nr_culled_tris);
debug_printf("llvmpipe: nr_rectangles: %9u\n", lp_count.nr_rects);
debug_printf("llvmpipe: nr_culled_rectangles: %9u\n", lp_count.nr_culled_rects);
total_64 = (lp_count.nr_empty_64 +
lp_count.nr_fully_covered_64 +
@@ -58,11 +60,14 @@ lp_print_counters(void)
p1 = 100.0 * (float) lp_count.nr_empty_64 / (float) total_64;
p2 = 100.0 * (float) lp_count.nr_fully_covered_64 / (float) total_64;
p3 = 100.0 * (float) lp_count.nr_partially_covered_64 / (float) total_64;
p4 = 100.0 * (float) lp_count.nr_blit_64 / (float) total_64;
p5 = 100.0 * (float) lp_count.nr_shade_opaque_64 / (float) total_64;
p6 = 100.0 * (float) lp_count.nr_shade_64 / (float) total_64;
debug_printf("llvmpipe: nr_64x64: %9u\n", total_64);
debug_printf("llvmpipe: nr_fully_covered_64x64: %9u (%3.0f%% of %u)\n", lp_count.nr_fully_covered_64, p2, total_64);
debug_printf("llvmpipe: nr_blit_64x64: %9u (%3.0f%% of %u)\n", lp_count.nr_blit_64, p4, total_64);
debug_printf("llvmpipe: nr_pure_blit_64x64: %9u (%3.0f%% of %u)\n", lp_count.nr_pure_blit_64, 0.0, lp_count.nr_blit_64);
debug_printf("llvmpipe: nr_shade_opaque_64x64: %9u (%3.0f%% of %u)\n", lp_count.nr_shade_opaque_64, p5, total_64);
debug_printf("llvmpipe: nr_pure_shade_opaque: %9u (%3.0f%% of %u)\n", lp_count.nr_pure_shade_opaque_64, 0.0, lp_count.nr_shade_opaque_64);
debug_printf("llvmpipe: nr_shade_64x64: %9u (%3.0f%% of %u)\n", lp_count.nr_shade_64, p6, total_64);
@@ -98,6 +103,17 @@ lp_print_counters(void)
debug_printf("llvmpipe: nr_empty_4x4: %9u (%3.0f%% of %u)\n", lp_count.nr_empty_4, p1, total_4);
debug_printf("llvmpipe: nr_non_empty_4x4: %9u (%3.0f%% of %u)\n", lp_count.nr_non_empty_4, p4, total_4);
total_4 = (lp_count.nr_rect_partially_covered_4 +
lp_count.nr_rect_fully_covered_4);
p1 = 100.0 * (float) lp_count.nr_rect_partially_covered_4 / (float) total_4;
p2 = 100.0 * (float) lp_count.nr_rect_fully_covered_4 / (float) total_4;
debug_printf("llvmpipe: nr_rect_4x4: %9u\n", total_4);
debug_printf("llvmpipe: nr_rect_full_4x4: %9u (%3.0f%% of %u)\n", lp_count.nr_rect_fully_covered_4, p1, total_4);
debug_printf("llvmpipe: nr_rect_part_4x4: %9u (%3.0f%% of %u)\n", lp_count.nr_rect_partially_covered_4, p2, total_4);
debug_printf("llvmpipe: nr_color_tile_clear: %9u\n", lp_count.nr_color_tile_clear);
debug_printf("llvmpipe: nr_color_tile_load: %9u\n", lp_count.nr_color_tile_load);
debug_printf("llvmpipe: nr_color_tile_store: %9u\n", lp_count.nr_color_tile_store);
+6
View File
@@ -42,9 +42,13 @@ struct lp_counters
{
unsigned nr_tris;
unsigned nr_culled_tris;
unsigned nr_rects;
unsigned nr_culled_rects;
unsigned nr_empty_64;
unsigned nr_fully_covered_64;
unsigned nr_partially_covered_64;
unsigned nr_blit_64;
unsigned nr_pure_blit_64;
unsigned nr_pure_shade_opaque_64;
unsigned nr_pure_shade_64;
unsigned nr_shade_64;
@@ -55,6 +59,8 @@ struct lp_counters
unsigned nr_empty_4;
unsigned nr_fully_covered_4;
unsigned nr_partially_covered_4;
unsigned nr_rect_fully_covered_4;
unsigned nr_rect_partially_covered_4;
unsigned nr_non_empty_4;
unsigned nr_llvm_compiles;
int64_t llvm_compile_time; /**< total, in microseconds */
+364 -10
View File
@@ -508,6 +508,126 @@ lp_rast_shade_quads_mask(struct lp_rasterizer_task *task,
lp_rast_shade_quads_mask_sample(task, inputs, x, y, new_mask);
}
/**
* Directly copy pixels from a texture to the destination color buffer.
* This is a bin command called during bin processing.
*/
static void
lp_rast_blit_tile_to_dest(struct lp_rasterizer_task *task,
const union lp_rast_cmd_arg arg)
{
const struct lp_scene *scene = task->scene;
const struct lp_rast_shader_inputs *inputs = arg.shade_tile;
const struct lp_rast_state *state = task->state;
struct lp_fragment_shader_variant *variant = state->variant;
const struct lp_jit_texture *texture = &state->jit_context.textures[0];
const uint8_t *src;
uint8_t *dst;
unsigned src_stride;
unsigned dst_stride;
struct pipe_surface *cbuf = scene->fb.cbufs[0];
const unsigned face_slice = cbuf->u.tex.first_layer;
const unsigned level = cbuf->u.tex.level;
struct llvmpipe_resource *lpt = llvmpipe_resource(cbuf->texture);
int src_x, src_y;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
if (inputs->disable) {
/* This command was partially binned and has been disabled */
return;
}
dst = llvmpipe_get_texture_image_address(lpt, face_slice, level);
if (!dst)
return;
dst_stride = lpt->row_stride[level];
src = texture->base;
src_stride = texture->row_stride[0];
src_x = util_iround(GET_A0(inputs)[1][0]*texture->width - 0.5f);
src_y = util_iround(GET_A0(inputs)[1][1]*texture->height - 0.5f);
src_x = src_x + task->x;
src_y = src_y + task->y;
if (0) {
union util_color uc;
uc.ui[0] = 0xff0000ff;
util_fill_rect(dst,
cbuf->format,
dst_stride,
task->x,
task->y,
task->width,
task->height,
&uc);
return;
}
if (src_x >= 0 &&
src_y >= 0 &&
src_x + task->width <= texture->width &&
src_y + task->height <= texture->height) {
if (variant->shader->kind == LP_FS_KIND_BLIT_RGBA ||
(variant->shader->kind == LP_FS_KIND_BLIT_RGB1 &&
cbuf->format == PIPE_FORMAT_B8G8R8X8_UNORM)) {
util_copy_rect(dst,
cbuf->format,
dst_stride,
task->x, task->y,
task->width, task->height,
src, src_stride,
src_x, src_y);
return;
}
if (variant->shader->kind == LP_FS_KIND_BLIT_RGB1) {
if (cbuf->format == PIPE_FORMAT_B8G8R8A8_UNORM) {
int x, y;
dst += task->x * 4;
src += src_x * 4;
dst += task->y * dst_stride;
src += src_y * src_stride;
for (y = 0; y < task->height; ++y) {
const uint32_t *src_row = (const uint32_t *)src;
uint32_t *dst_row = (uint32_t *)dst;
for (x = 0; x < task->width; ++x) {
*dst_row++ = *src_row++ | 0xff000000;
}
dst += dst_stride;
src += src_stride;
}
return;
}
}
}
/*
* Fall back to the jit shaders.
*/
lp_rast_shade_tile_opaque(task, arg);
}
static void
lp_rast_blit_tile(struct lp_rasterizer_task *task,
const union lp_rast_cmd_arg arg)
{
/* This kindof just works, but isn't efficient:
*/
lp_rast_blit_tile_to_dest(task, arg);
}
/**
* Begin a new occlusion query.
* This is a bin command put in all bins.
@@ -597,8 +717,123 @@ lp_rast_tile_end(struct lp_rasterizer_task *task)
task->bin = NULL;
}
static lp_rast_cmd_func dispatch[LP_RAST_OP_MAX] =
{
/* Currently have two rendering paths only - the general case triangle
* path and the super-specialized blit/clear path.
*/
#define TRI ((LP_RAST_FLAGS_TRI <<1)-1) /* general case */
#define RECT ((LP_RAST_FLAGS_RECT<<1)-1) /* direct rectangle rasterizer */
#define BLIT ((LP_RAST_FLAGS_BLIT<<1)-1) /* write direct-to-dest */
static const unsigned
rast_flags[] = {
BLIT, /* clear color */
TRI, /* clear zstencil */
TRI, /* triangle_1 */
TRI, /* triangle_2 */
TRI, /* triangle_3 */
TRI, /* triangle_4 */
TRI, /* triangle_5 */
TRI, /* triangle_6 */
TRI, /* triangle_7 */
TRI, /* triangle_8 */
TRI, /* triangle_3_4 */
TRI, /* triangle_3_16 */
TRI, /* triangle_4_16 */
RECT, /* shade_tile */
RECT, /* shade_tile_opaque */
TRI, /* begin_query */
TRI, /* end_query */
BLIT, /* set_state, */
TRI, /* lp_rast_triangle_32_1 */
TRI, /* lp_rast_triangle_32_2 */
TRI, /* lp_rast_triangle_32_3 */
TRI, /* lp_rast_triangle_32_4 */
TRI, /* lp_rast_triangle_32_5 */
TRI, /* lp_rast_triangle_32_6 */
TRI, /* lp_rast_triangle_32_7 */
TRI, /* lp_rast_triangle_32_8 */
TRI, /* lp_rast_triangle_32_3_4 */
TRI, /* lp_rast_triangle_32_3_16 */
TRI, /* lp_rast_triangle_32_4_16 */
TRI, /* lp_rast_triangle_ms_1 */
TRI, /* lp_rast_triangle_ms_2 */
TRI, /* lp_rast_triangle_ms_3 */
TRI, /* lp_rast_triangle_ms_4 */
TRI, /* lp_rast_triangle_ms_5 */
TRI, /* lp_rast_triangle_ms_6 */
TRI, /* lp_rast_triangle_ms_7 */
TRI, /* lp_rast_triangle_ms_8 */
TRI, /* lp_rast_triangle_ms_3_4 */
TRI, /* lp_rast_triangle_ms_3_16 */
TRI, /* lp_rast_triangle_ms_4_16 */
RECT, /* rectangle */
BLIT, /* blit */
};
/*
*/
static const lp_rast_cmd_func
dispatch_blit[] = {
lp_rast_clear_color,
NULL, /* clear_zstencil */
NULL, /* triangle_1 */
NULL, /* triangle_2 */
NULL, /* triangle_3 */
NULL, /* triangle_4 */
NULL, /* triangle_5 */
NULL, /* triangle_6 */
NULL, /* triangle_7 */
NULL, /* triangle_8 */
NULL, /* triangle_3_4 */
NULL, /* triangle_3_16 */
NULL, /* triangle_4_16 */
NULL, /* shade_tile */
NULL, /* shade_tile_opaque */
NULL, /* begin_query */
NULL, /* end_query */
lp_rast_set_state, /* set_state */
NULL, /* lp_rast_triangle_32_1 */
NULL, /* lp_rast_triangle_32_2 */
NULL, /* lp_rast_triangle_32_3 */
NULL, /* lp_rast_triangle_32_4 */
NULL, /* lp_rast_triangle_32_5 */
NULL, /* lp_rast_triangle_32_6 */
NULL, /* lp_rast_triangle_32_7 */
NULL, /* lp_rast_triangle_32_8 */
NULL, /* lp_rast_triangle_32_3_4 */
NULL, /* lp_rast_triangle_32_3_16 */
NULL, /* lp_rast_triangle_32_4_16 */
NULL, /* lp_rast_triangle_ms_1 */
NULL, /* lp_rast_triangle_ms_2 */
NULL, /* lp_rast_triangle_ms_3 */
NULL, /* lp_rast_triangle_ms_4 */
NULL, /* lp_rast_triangle_ms_5 */
NULL, /* lp_rast_triangle_ms_6 */
NULL, /* lp_rast_triangle_ms_7 */
NULL, /* lp_rast_triangle_ms_8 */
NULL, /* lp_rast_triangle_ms_3_4 */
NULL, /* lp_rast_triangle_ms_3_16 */
NULL, /* lp_rast_triangle_ms_4_16 */
NULL, /* rectangle */
lp_rast_blit_tile_to_dest,
};
/* Triangle and general case rasterization: Use the SOA llvm shdaers,
* an active swizzled tile for each color buf, etc. Don't blit/clear
* directly to destination surface as we know there are swizzled
* operations coming.
*/
static const lp_rast_cmd_func
dispatch_tri[] = {
lp_rast_clear_color,
lp_rast_clear_zstencil,
lp_rast_triangle_1,
@@ -639,27 +874,133 @@ static lp_rast_cmd_func dispatch[LP_RAST_OP_MAX] =
lp_rast_triangle_ms_3_4,
lp_rast_triangle_ms_3_16,
lp_rast_triangle_ms_4_16,
lp_rast_rectangle,
lp_rast_blit_tile,
};
/* Debug rasterization with most fastpaths disabled.
*/
static const lp_rast_cmd_func
dispatch_tri_debug[] =
{
lp_rast_clear_color,
lp_rast_clear_zstencil,
lp_rast_triangle_1,
lp_rast_triangle_2,
lp_rast_triangle_3,
lp_rast_triangle_4,
lp_rast_triangle_5,
lp_rast_triangle_6,
lp_rast_triangle_7,
lp_rast_triangle_8,
lp_rast_triangle_3_4,
lp_rast_triangle_3_16,
lp_rast_triangle_4_16,
lp_rast_shade_tile,
lp_rast_shade_tile,
lp_rast_begin_query,
lp_rast_end_query,
lp_rast_set_state,
lp_rast_triangle_32_1,
lp_rast_triangle_32_2,
lp_rast_triangle_32_3,
lp_rast_triangle_32_4,
lp_rast_triangle_32_5,
lp_rast_triangle_32_6,
lp_rast_triangle_32_7,
lp_rast_triangle_32_8,
lp_rast_triangle_32_3_4,
lp_rast_triangle_32_3_16,
lp_rast_triangle_32_4_16,
lp_rast_triangle_ms_1,
lp_rast_triangle_ms_2,
lp_rast_triangle_ms_3,
lp_rast_triangle_ms_4,
lp_rast_triangle_ms_5,
lp_rast_triangle_ms_6,
lp_rast_triangle_ms_7,
lp_rast_triangle_ms_8,
lp_rast_triangle_ms_3_4,
lp_rast_triangle_ms_3_16,
lp_rast_triangle_ms_4_16,
lp_rast_rectangle,
lp_rast_shade_tile,
};
struct lp_bin_info
lp_characterize_bin(const struct cmd_bin *bin)
{
struct cmd_block *block;
struct lp_bin_info info;
unsigned andflags = ~0;
unsigned k, j = 0;
STATIC_ASSERT(ARRAY_SIZE(rast_flags) == LP_RAST_OP_MAX);
for (block = bin->head; block; block = block->next) {
for (k = 0; k < block->count; k++, j++) {
andflags &= rast_flags[block->cmd[k]];
}
}
info.type = andflags;
info.count = j;
return info;
}
static void
do_rasterize_bin(struct lp_rasterizer_task *task,
const struct cmd_bin *bin,
int x, int y)
blit_rasterize_bin(struct lp_rasterizer_task *task,
const struct cmd_bin *bin)
{
const struct cmd_block *block;
unsigned k;
if (0)
lp_debug_bin(bin, x, y);
STATIC_ASSERT(ARRAY_SIZE(dispatch_blit) == LP_RAST_OP_MAX);
if (0) debug_printf("%s\n", __FUNCTION__);
for (block = bin->head; block; block = block->next) {
for (k = 0; k < block->count; k++) {
dispatch[block->cmd[k]]( task, block->arg[k] );
dispatch_blit[block->cmd[k]]( task, block->arg[k] );
}
}
}
static void
tri_rasterize_bin(struct lp_rasterizer_task *task,
const struct cmd_bin *bin,
int x, int y)
{
const struct cmd_block *block;
unsigned k;
STATIC_ASSERT(ARRAY_SIZE(dispatch_tri) == LP_RAST_OP_MAX);
for (block = bin->head; block; block = block->next) {
for (k = 0; k < block->count; k++) {
dispatch_tri[block->cmd[k]]( task, block->arg[k] );
}
}
}
static void
debug_rasterize_bin(struct lp_rasterizer_task *task,
const struct cmd_bin *bin)
{
const struct cmd_block *block;
unsigned k;
STATIC_ASSERT(ARRAY_SIZE(dispatch_tri_debug) == LP_RAST_OP_MAX);
for (block = bin->head; block; block = block->next) {
for (k = 0; k < block->count; k++) {
dispatch_tri_debug[block->cmd[k]]( task, block->arg[k] );
}
}
}
/**
@@ -672,9 +1013,20 @@ static void
rasterize_bin(struct lp_rasterizer_task *task,
const struct cmd_bin *bin, int x, int y )
{
struct lp_bin_info info = lp_characterize_bin(bin);
lp_rast_tile_begin( task, bin, x, y );
do_rasterize_bin(task, bin, x, y);
if (LP_DEBUG & DEBUG_NO_FASTPATH)
debug_rasterize_bin(task, bin);
else if (info.type & LP_RAST_FLAGS_BLIT)
blit_rasterize_bin(task, bin);
else if (task->scene->permit_linear_rasterizer &&
!(LP_PERF & PERF_NO_RAST_LINEAR) &&
(info.type & LP_RAST_FLAGS_RECT))
lp_linear_rasterize_bin(task, bin);
else
tri_rasterize_bin(task, bin, x, y);
lp_rast_tile_end(task);
@@ -682,7 +1034,9 @@ rasterize_bin(struct lp_rasterizer_task *task,
/* Debug/Perf flags:
*/
if (bin->head->count == 1) {
if (bin->head->cmd[0] == LP_RAST_OP_SHADE_TILE_OPAQUE)
if (bin->head->cmd[0] == LP_RAST_OP_BLIT)
LP_COUNT(nr_pure_blit_64);
else if (bin->head->cmd[0] == LP_RAST_OP_SHADE_TILE_OPAQUE)
LP_COUNT(nr_pure_shade_opaque_64);
else if (bin->head->cmd[0] == LP_RAST_OP_SHADE_TILE)
LP_COUNT(nr_pure_shade_64);
+65 -2
View File
@@ -39,6 +39,7 @@
#include "pipe/p_compiler.h"
#include "util/u_pack_color.h"
#include "util/u_rect.h"
#include "lp_jit.h"
@@ -92,6 +93,15 @@ struct lp_rast_state {
};
/**
* Texture blit offsets.
*/
struct lp_rast_blit {
int16_t x0;
int16_t y0;
};
/**
* Coefficients necessary to run the shader at a given location.
* First coefficient is position.
@@ -101,7 +111,8 @@ struct lp_rast_shader_inputs {
unsigned frontfacing:1; /** True for front-facing */
unsigned disable:1; /** Partially binned, disable this command */
unsigned opaque:1; /** Is opaque */
unsigned pad0:13; /* wasted space */
unsigned is_blit:1; /* blit */
unsigned pad0:12; /* wasted space */
unsigned view_index:16;
unsigned stride; /* how much to advance data between a0, dadx, dady */
unsigned layer; /* the layer to render to (from gs, already clamped) */
@@ -146,6 +157,31 @@ struct lp_rast_triangle {
};
#define RECT_PLANE_LEFT 0x1
#define RECT_PLANE_RIGHT 0x2
#define RECT_PLANE_TOP 0x4
#define RECT_PLANE_BOTTOM 0x8
/**
* Rasterization information for a screen-aligned rectangle known to
* be in this bin, plus inputs to run the shader:
* These fields are tile- and bin-independent.
* Objects of this type are put into the lp_setup_context::data buffer.
*/
struct lp_rast_rectangle {
#ifdef DEBUG
float v[2][2]; /**< diagonal corners */
#endif
/* Rectangle boundaries in integer pixels:
*/
struct u_rect box;
/* inputs for the shader */
struct lp_rast_shader_inputs inputs;
};
struct lp_rast_clear_rb {
union util_color color_val;
unsigned cbuf;
@@ -179,6 +215,7 @@ union lp_rast_cmd_arg {
const struct lp_rast_triangle *tri;
unsigned plane_mask;
} triangle;
const struct lp_rast_rectangle *rectangle;
const struct lp_rast_state *set_state;
const struct lp_rast_clear_rb *clear_rb;
struct {
@@ -227,6 +264,14 @@ lp_rast_arg_triangle_contained( const struct lp_rast_triangle *triangle,
return arg;
}
static inline union lp_rast_cmd_arg
lp_rast_arg_rectangle( const struct lp_rast_rectangle *rectangle )
{
union lp_rast_cmd_arg arg;
arg.rectangle = rectangle;
return arg;
}
static inline union lp_rast_cmd_arg
lp_rast_arg_state( const struct lp_rast_state *state )
{
@@ -317,9 +362,27 @@ lp_rast_arg_null( void )
#define LP_RAST_OP_MS_TRIANGLE_3_4 0x25
#define LP_RAST_OP_MS_TRIANGLE_3_16 0x26
#define LP_RAST_OP_MS_TRIANGLE_4_16 0x27
#define LP_RAST_OP_MAX 0x28
#define LP_RAST_OP_RECTANGLE 0x28 /* Keep at end */
#define LP_RAST_OP_BLIT 0x29 /* Keep at end */
#define LP_RAST_OP_MAX 0x2a
#define LP_RAST_OP_MASK 0xff
/* Returned by characterize_bin:
*/
#define LP_RAST_FLAGS_TRI (0x1)
#define LP_RAST_FLAGS_RECT (0x2)
#define LP_RAST_FLAGS_TILE (0x4)
#define LP_RAST_FLAGS_BLIT (0x8)
struct lp_bin_info {
unsigned type:8;
unsigned count:24;
};
struct lp_bin_info
lp_characterize_bin(const struct cmd_bin *bin);
void
lp_debug_bins( struct lp_scene *scene );
void
+138 -3
View File
@@ -23,7 +23,7 @@ static char get_label( int i )
static const char *cmd_names[LP_RAST_OP_MAX] =
static const char *cmd_names[] =
{
"clear_color",
"clear_zstencil",
@@ -54,10 +54,24 @@ static const char *cmd_names[LP_RAST_OP_MAX] =
"triangle_32_3_4",
"triangle_32_3_16",
"triangle_32_4_16",
"lp_rast_triangle_ms_1",
"lp_rast_triangle_ms_2",
"lp_rast_triangle_ms_3",
"lp_rast_triangle_ms_4",
"lp_rast_triangle_ms_5",
"lp_rast_triangle_ms_6",
"lp_rast_triangle_ms_7",
"lp_rast_triangle_ms_8",
"lp_rast_triangle_ms_3_4",
"lp_rast_triangle_ms_3_16",
"lp_rast_triangle_ms_4_16",
"rectangle",
"blit_tile",
};
static const char *cmd_name(unsigned cmd)
{
STATIC_ASSERT(ARRAY_SIZE(cmd_names) == LP_RAST_OP_MAX);
assert(ARRAY_SIZE(cmd_names) > cmd);
return cmd_names[cmd];
}
@@ -78,7 +92,9 @@ get_variant( const struct lp_rast_state *state,
block->cmd[k] == LP_RAST_OP_TRIANGLE_4 ||
block->cmd[k] == LP_RAST_OP_TRIANGLE_5 ||
block->cmd[k] == LP_RAST_OP_TRIANGLE_6 ||
block->cmd[k] == LP_RAST_OP_TRIANGLE_7)
block->cmd[k] == LP_RAST_OP_TRIANGLE_7 ||
block->cmd[k] == LP_RAST_OP_RECTANGLE ||
block->cmd[k] == LP_RAST_OP_BLIT)
return state->variant;
return NULL;
@@ -98,6 +114,39 @@ is_blend( const struct lp_rast_state *state,
return FALSE;
}
static boolean
is_linear( const struct lp_rast_state *state,
const struct cmd_block *block,
int k )
{
if (block->cmd[k] == LP_RAST_OP_BLIT)
return state->variant->jit_linear_blit != NULL;
if (block->cmd[k] == LP_RAST_OP_SHADE_TILE ||
block->cmd[k] == LP_RAST_OP_SHADE_TILE_OPAQUE)
return state->variant->jit_linear != NULL;
if (block->cmd[k] == LP_RAST_OP_RECTANGLE)
return state->variant->jit_linear != NULL;
return FALSE;
}
static const char *
get_fs_kind( const struct lp_rast_state *state,
const struct cmd_block *block,
int k )
{
const struct lp_fragment_shader_variant *variant = get_variant(state, block, k);
if (variant)
return lp_debug_fs_kind(variant->shader->kind);
return "";
}
static void
@@ -105,9 +154,25 @@ debug_bin( const struct cmd_bin *bin, int x, int y )
{
const struct lp_rast_state *state = NULL;
const struct cmd_block *head = bin->head;
const char *type;
struct lp_bin_info info;
int i, j = 0;
debug_printf("bin %d,%d:\n", x, y);
info = lp_characterize_bin(bin);
if (info.type & LP_RAST_FLAGS_BLIT)
type = "blit";
else if (info.type & LP_RAST_FLAGS_TILE)
type = "tile";
else if (info.type & LP_RAST_FLAGS_RECT)
type = "rect";
else if (info.type & LP_RAST_FLAGS_TRI)
type = "tri";
else
type = "unknown";
debug_printf("bin %d,%d: type %s\n", x, y, type);
while (head) {
for (i = 0; i < head->count; i++, j++) {
@@ -138,7 +203,64 @@ static void plot(struct tile *tile,
/**
* Scan the tile in chunks and figure out which pixels to rasterize
* for this rectangle.
*/
static int
debug_rectangle(int x, int y,
const union lp_rast_cmd_arg arg,
struct tile *tile,
char val)
{
const struct lp_rast_rectangle *rect = arg.rectangle;
boolean blend = tile->state->variant->key.blend.rt[0].blend_enable;
unsigned i,j, count = 0;
/* Check for "disabled" rectangles generated in out-of-memory
* conditions.
*/
if (rect->inputs.disable) {
/* This command was partially binned and has been disabled */
return 0;
}
for (i = 0; i < TILE_SIZE; i++)
{
for (j = 0; j < TILE_SIZE; j++)
{
if (rect->box.x0 <= x + i &&
rect->box.x1 >= x + i &&
rect->box.y0 <= y + j &&
rect->box.y1 >= y + j)
{
plot(tile, i, j, val, blend);
count++;
}
}
}
return count;
}
static int
debug_blit_tile(int x, int y,
const union lp_rast_cmd_arg arg,
struct tile *tile,
char val)
{
const struct lp_rast_shader_inputs *inputs = arg.shade_tile;
unsigned i,j;
if (inputs->disable)
return 0;
for (i = 0; i < TILE_SIZE; i++)
for (j = 0; j < TILE_SIZE; j++)
plot(tile, i, j, val, FALSE);
return TILE_SIZE * TILE_SIZE;
}
static int
@@ -259,6 +381,8 @@ do_debug_bin( struct tile *tile,
for (block = bin->head; block; block = block->next) {
for (k = 0; k < block->count; k++, j++) {
boolean blend = is_blend(tile->state, block, k);
boolean linear = is_linear(tile->state, block, k);
const char *fskind = get_fs_kind(tile->state, block, k);
char val = get_label(j);
int count = 0;
@@ -272,6 +396,9 @@ do_debug_bin( struct tile *tile,
block->cmd[k] == LP_RAST_OP_CLEAR_ZSTENCIL)
count = debug_clear_tile(tx, ty, block->arg[k], tile, val);
if (block->cmd[k] == LP_RAST_OP_BLIT)
count = debug_blit_tile(tx, ty, block->arg[k], tile, val);
if (block->cmd[k] == LP_RAST_OP_SHADE_TILE ||
block->cmd[k] == LP_RAST_OP_SHADE_TILE_OPAQUE)
count = debug_shade_tile(tx, ty, block->arg[k], tile, val);
@@ -285,12 +412,20 @@ do_debug_bin( struct tile *tile,
block->cmd[k] == LP_RAST_OP_TRIANGLE_7)
count = debug_triangle(tx, ty, block->arg[k], tile, val);
if (block->cmd[k] == LP_RAST_OP_RECTANGLE)
count = debug_rectangle(tx, ty, block->arg[k], tile, val);
if (print_cmds) {
debug_printf(" % 5d", count);
debug_printf(" %20s", fskind);
if (blend)
debug_printf(" blended");
if (linear)
debug_printf(" linear");
debug_printf("\n");
}
}
@@ -0,0 +1,263 @@
/**************************************************************************
*
* Copyright 2009-2021 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 <limits.h>
#include "util/u_memory.h"
#include "util/u_math.h"
#include "util/u_rect.h"
#include "util/u_surface.h"
#include "util/u_pack_color.h"
#include "lp_scene_queue.h"
#include "lp_debug.h"
#include "lp_fence.h"
#include "lp_perf.h"
#include "lp_query.h"
#include "lp_rast.h"
#include "lp_rast_priv.h"
#include "lp_scene.h"
static void
lp_rast_linear_clear(struct lp_rasterizer_task *task,
const union lp_rast_cmd_arg arg)
{
const struct lp_scene *scene = task->scene;
union util_color uc;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
uc = arg.clear_rb->color_val;
util_fill_rect(scene->cbufs[0].map,
PIPE_FORMAT_B8G8R8A8_UNORM,
scene->cbufs[0].stride,
task->x,
task->y,
task->width,
task->height,
&uc);
}
/* Run the scanline version of the shader across the whole tile.
*/
static void
lp_rast_linear_tile(struct lp_rasterizer_task *task,
const union lp_rast_cmd_arg arg)
{
const struct lp_rast_shader_inputs *inputs = arg.shade_tile;
const struct lp_rast_state *state;
struct lp_fragment_shader_variant *variant;
const struct lp_scene *scene = task->scene;
if (inputs->disable)
return;
state = task->state;
assert(state);
if (!state) {
return;
}
variant = state->variant;
if (variant->jit_linear_blit &&
inputs->is_blit)
{
if (variant->jit_linear_blit(state,
task->x,
task->y,
task->width,
task->height,
(const float (*)[4])GET_A0(inputs),
(const float (*)[4])GET_DADX(inputs),
(const float (*)[4])GET_DADY(inputs),
scene->cbufs[0].map,
scene->cbufs[0].stride))
return;
}
if (variant->jit_linear) {
if (variant->jit_linear(state,
task->x,
task->y,
task->width,
task->height,
(const float (*)[4])GET_A0(inputs),
(const float (*)[4])GET_DADX(inputs),
(const float (*)[4])GET_DADY(inputs),
scene->cbufs[0].map,
scene->cbufs[0].stride))
return;
}
{
struct u_rect box;
box.x0 = task->x;
box.x1 = task->x + task->width - 1;
box.y0 = task->y;
box.y1 = task->y + task->height - 1;
lp_rast_linear_rect_fallback(task, inputs, &box);
}
}
/* Run the scanline version of the shader on a rectangle within the
* tile.
*/
static void
lp_rast_linear_rect(struct lp_rasterizer_task *task,
const union lp_rast_cmd_arg arg)
{
const struct lp_scene *scene = task->scene;
const struct lp_rast_rectangle *rect = arg.rectangle;
const struct lp_rast_shader_inputs *inputs = &rect->inputs;
const struct lp_rast_state *state = task->state;
struct lp_fragment_shader_variant *variant = state->variant;
struct u_rect box;
int width, height;
if (inputs->disable)
return;
box.x0 = task->x;
box.y0 = task->y;
box.x1 = task->x + task->width - 1;
box.y1 = task->y + task->height - 1;
u_rect_find_intersection(&rect->box, &box);
width = box.x1 - box.x0 + 1;
height = box.y1 - box.y0 + 1;
/* Note that blit primitives can end up in the non-full-tile path,
* the binner currently doesn't try to classify sub-tile
* primitives. Can detect them here though.
*/
if (variant->jit_linear_blit &&
inputs->is_blit)
{
if (variant->jit_linear_blit(state,
box.x0, box.y0,
width, height,
(const float (*)[4])GET_A0(inputs),
(const float (*)[4])GET_DADX(inputs),
(const float (*)[4])GET_DADY(inputs),
scene->cbufs[0].map,
scene->cbufs[0].stride))
return;
}
if (variant->jit_linear)
{
if (variant->jit_linear(state,
box.x0, box.y0,
width, height,
(const float (*)[4])GET_A0(inputs),
(const float (*)[4])GET_DADX(inputs),
(const float (*)[4])GET_DADY(inputs),
scene->cbufs[0].map,
scene->cbufs[0].stride))
return;
}
lp_rast_linear_rect_fallback(task, inputs, &box);
}
static const lp_rast_cmd_func
dispatch_linear[] = {
lp_rast_linear_clear, /* clear_color */
NULL, /* clear_zstencil */
NULL, /* triangle_1 */
NULL, /* triangle_2 */
NULL, /* triangle_3 */
NULL, /* triangle_4 */
NULL, /* triangle_5 */
NULL, /* triangle_6 */
NULL, /* triangle_7 */
NULL, /* triangle_8 */
NULL, /* triangle_3_4 */
NULL, /* triangle_3_16 */
NULL, /* triangle_4_16 */
lp_rast_linear_tile, /* shade_tile */
lp_rast_linear_tile, /* shade_tile_opaque */
NULL, /* begin_query */
NULL, /* end_query */
lp_rast_set_state, /* set_state */
NULL, /* lp_rast_triangle_32_1 */
NULL, /* lp_rast_triangle_32_2 */
NULL, /* lp_rast_triangle_32_3 */
NULL, /* lp_rast_triangle_32_4 */
NULL, /* lp_rast_triangle_32_5 */
NULL, /* lp_rast_triangle_32_6 */
NULL, /* lp_rast_triangle_32_7 */
NULL, /* lp_rast_triangle_32_8 */
NULL, /* lp_rast_triangle_32_3_4 */
NULL, /* lp_rast_triangle_32_3_16 */
NULL, /* lp_rast_triangle_32_4_16 */
NULL, /* lp_rast_triangle_ms_1 */
NULL, /* lp_rast_triangle_ms_2 */
NULL, /* lp_rast_triangle_ms_3 */
NULL, /* lp_rast_triangle_ms_4 */
NULL, /* lp_rast_triangle_ms_5 */
NULL, /* lp_rast_triangle_ms_6 */
NULL, /* lp_rast_triangle_ms_7 */
NULL, /* lp_rast_triangle_ms_8 */
NULL, /* lp_rast_triangle_ms_3_4 */
NULL, /* lp_rast_triangle_ms_3_16 */
NULL, /* lp_rast_triangle_ms_4_16 */
lp_rast_linear_rect, /* rect */
lp_rast_linear_tile, /* blit */
};
/* Assumptions for this path:
* - Single color buffer, PIPE_FORMAT_B8G8R8A8_UNORM
* - No depth buffer
* - All primitives in bins are rect, tile, blit or clear.
* - All shaders have a linear variant.
*/
void
lp_linear_rasterize_bin(struct lp_rasterizer_task *task,
const struct cmd_bin *bin)
{
const struct cmd_block *block;
unsigned k;
STATIC_ASSERT(ARRAY_SIZE(dispatch_linear) == LP_RAST_OP_MAX);
if (0) debug_printf("%s\n", __FUNCTION__);
for (block = bin->head; block; block = block->next) {
for (k = 0; k < block->count; k++) {
assert(dispatch_linear[block->cmd[k]]);
dispatch_linear[block->cmd[k]]( task, block->arg[k] );
}
}
}
@@ -0,0 +1,303 @@
/**************************************************************************
*
* Copyright 2007-2021 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.
*
**************************************************************************/
/*
* Rasterization for binned rectangles within a tile
*/
#include <limits.h>
#include "util/u_math.h"
#include "lp_debug.h"
#include "lp_perf.h"
#include "lp_rast_priv.h"
/* Our 16-pixel stamps are layed out as:
*
* 0 1 2 3
* 4 5 6 7
* 8 9 10 11
* 12 13 14 15
*
* Define bitmasks for each row and column in this layout:
*/
#define COLUMN0 ((1<<0)|(1<<4)|(1<<8) |(1<<12))
#define COLUMN1 ((1<<1)|(1<<5)|(1<<9) |(1<<13))
#define COLUMN2 ((1<<2)|(1<<6)|(1<<10)|(1<<14))
#define COLUMN3 ((1<<3)|(1<<7)|(1<<11)|(1<<15))
#define ROW0 ((1<<0) |(1<<1) |(1<<2) |(1<<3))
#define ROW1 ((1<<4) |(1<<5) |(1<<6) |(1<<7))
#define ROW2 ((1<<8) |(1<<9) |(1<<10)|(1<<11))
#define ROW3 ((1<<12)|(1<<13)|(1<<14)|(1<<15))
#define STAMP_SIZE 4
static unsigned left_mask_tab[STAMP_SIZE] = {
COLUMN0 | COLUMN1 | COLUMN2 | COLUMN3,
COLUMN1 | COLUMN2 | COLUMN3,
COLUMN2 | COLUMN3,
COLUMN3,
};
static unsigned right_mask_tab[STAMP_SIZE] = {
COLUMN0,
COLUMN0 | COLUMN1,
COLUMN0 | COLUMN1 | COLUMN2,
COLUMN0 | COLUMN1 | COLUMN2 | COLUMN3,
};
static unsigned top_mask_tab[STAMP_SIZE] = {
ROW0 | ROW1 | ROW2 | ROW3,
ROW1 | ROW2 | ROW3,
ROW2 | ROW3,
ROW3,
};
static unsigned bottom_mask_tab[STAMP_SIZE] = {
ROW0,
ROW0 | ROW1,
ROW0 | ROW1 | ROW2,
ROW0 | ROW1 | ROW2 | ROW3,
};
/**
* Shade all pixels in a 4x4 block. The fragment code omits the
* triangle in/out tests.
* \param x, y location of 4x4 block in window coords
*/
static void
shade_quads_all( struct lp_rasterizer_task *task,
const struct lp_rast_shader_inputs *inputs,
unsigned x, unsigned y )
{
const struct lp_scene *scene = task->scene;
const struct lp_rast_state *state = task->state;
struct lp_fragment_shader_variant *variant = state->variant;
uint8_t *color = scene->cbufs[0].map;
unsigned stride = scene->cbufs[0].stride;
uint8_t *cbufs[1];
unsigned strides[1];
color += x * 4;
color += y * stride;
cbufs[0] = color;
strides[0] = stride;
assert(!variant->key.depth.enabled);
/* run shader on 4x4 block */
BEGIN_JIT_CALL(state, task);
variant->jit_function[RAST_WHOLE]( &state->jit_context,
x, y,
1,
(const float (*)[4])GET_A0(inputs),
(const float (*)[4])GET_DADX(inputs),
(const float (*)[4])GET_DADY(inputs),
cbufs,
NULL,
0xffff,
&task->thread_data,
strides, 0, 0, 0 );
END_JIT_CALL();
}
static void
shade_quads_mask(struct lp_rasterizer_task *task,
const struct lp_rast_shader_inputs *inputs,
unsigned x, unsigned y,
unsigned mask)
{
const struct lp_rast_state *state = task->state;
struct lp_fragment_shader_variant *variant = state->variant;
const struct lp_scene *scene = task->scene;
uint8_t *color = scene->cbufs[0].map;
unsigned stride = scene->cbufs[0].stride;
uint8_t *cbufs[1];
unsigned strides[1];
color += x * 4;
color += y * stride;
cbufs[0] = color;
strides[0] = stride;
assert(!variant->key.depth.enabled);
/* Propagate non-interpolated raster state */
task->thread_data.raster_state.viewport_index = inputs->viewport_index;
/* run shader on 4x4 block */
BEGIN_JIT_CALL(state, task);
variant->jit_function[RAST_EDGE_TEST](&state->jit_context,
x, y,
1,
(const float (*)[4])GET_A0(inputs),
(const float (*)[4])GET_DADX(inputs),
(const float (*)[4])GET_DADY(inputs),
cbufs,
NULL,
mask,
&task->thread_data,
strides, 0, 0, 0);
END_JIT_CALL();
}
/* Shade a 4x4 stamp completely within the rectangle.
*/
static inline void
full(struct lp_rasterizer_task *task,
const struct lp_rast_shader_inputs *inputs,
unsigned ix, unsigned iy)
{
shade_quads_all(task,
inputs,
ix * STAMP_SIZE,
iy * STAMP_SIZE);
}
/* Shade a 4x4 stamp which may be partially outside the rectangle,
* according to the mask parameter.
*/
static inline void
partial(struct lp_rasterizer_task *task,
const struct lp_rast_shader_inputs *inputs,
unsigned ix, unsigned iy,
unsigned mask)
{
/* Unfortunately we can end up generating full blocks on this path,
* need to catch them.
*/
if (mask == 0xffff)
full(task, inputs, ix, iy);
else {
assert(mask);
shade_quads_mask(task,
inputs,
ix * STAMP_SIZE,
iy * STAMP_SIZE,
mask);
}
}
/**
* Run the full SoA shader.
*/
void
lp_rast_linear_rect_fallback(struct lp_rasterizer_task *task,
const struct lp_rast_shader_inputs *inputs,
const struct u_rect *box)
{
unsigned ix0, ix1, iy0, iy1;
unsigned left_mask;
unsigned right_mask;
unsigned top_mask;
unsigned bottom_mask;
unsigned i,j;
/* The interior of the rectangle (if there is one) will be
* rasterized as full 4x4 stamps.
*
* At each edge of the rectangle, however, there will be a fringe
* of partial blocks where the edge lands somewhere in the middle
* of a 4x4-pixel stamp.
*
* For each edge, precalculate a mask of the pixels inside that
* edge for the first 4x4-pixel stamp.
*
* Note that at the corners, and for narrow rectangles, an
* individual stamp may have two or more edges active. We'll deal
* with that below by combining these masks as appropriate.
*/
left_mask = left_mask_tab [box->x0 & (STAMP_SIZE - 1)];
right_mask = right_mask_tab [box->x1 & (STAMP_SIZE - 1)];
top_mask = top_mask_tab [box->y0 & (STAMP_SIZE - 1)];
bottom_mask = bottom_mask_tab [box->y1 & (STAMP_SIZE - 1)];
ix0 = box->x0 / STAMP_SIZE;
ix1 = box->x1 / STAMP_SIZE;
iy0 = box->y0 / STAMP_SIZE;
iy1 = box->y1 / STAMP_SIZE;
/* Various special cases.
*/
if (ix0 == ix1 && iy0 == iy1) {
/* Rectangle is contained within a single 4x4-pixel stamp:
*/
partial(task, inputs, ix0, iy0,
(left_mask & right_mask &
top_mask & bottom_mask));
}
else if (ix0 == ix1) {
/* Left and right edges fall on the same 4-pixel-wide column:
*/
unsigned mask = left_mask & right_mask;
partial(task, inputs, ix0, iy0, mask & top_mask);
for (i = iy0 + 1; i < iy1; i++)
partial(task, inputs, ix0, i, mask);
partial(task, inputs, ix0, iy1, mask & bottom_mask);
}
else if (iy0 == iy1) {
/* Top and bottom edges fall on the same 4-pixel-wide row:
*/
unsigned mask = top_mask & bottom_mask;
partial(task, inputs, ix0, iy0, mask & left_mask);
for (i = ix0 + 1; i < ix1; i++)
partial(task, inputs, i, iy0, mask);
partial(task, inputs, ix1, iy0, mask & right_mask);
}
else {
/* Each pair of edges falls in a separate 4-pixel-wide
* row/column.
*/
partial(task, inputs, ix0, iy0, left_mask & top_mask);
partial(task, inputs, ix0, iy1, left_mask & bottom_mask);
partial(task, inputs, ix1, iy0, right_mask & top_mask);
partial(task, inputs, ix1, iy1, right_mask & bottom_mask);
for (i = ix0 + 1; i < ix1; i++)
partial(task, inputs, i, iy0, top_mask);
for (i = ix0 + 1; i < ix1; i++)
partial(task, inputs, i, iy1, bottom_mask);
for (i = iy0 + 1; i < iy1; i++)
partial(task, inputs, ix0, i, left_mask);
for (i = iy0 + 1; i < iy1; i++)
partial(task, inputs, ix1, i, right_mask);
/* Full interior blocks
*/
for (j = iy0 + 1; j < iy1; j++) {
for (i = ix0 + 1; i < ix1; i++) {
full(task, inputs, i, j);
}
}
}
}
@@ -176,6 +176,7 @@ lp_rast_get_color_block_pointer(struct lp_rasterizer_task *task,
color = task->color_tiles[buf] + pixel_offset;
if (layer) {
assert(layer <= task->scene->fb_max_layer);
color += layer * task->scene->cbufs[buf].layer_stride;
}
@@ -347,6 +348,10 @@ void lp_rast_triangle_32_3_16( struct lp_rasterizer_task *,
void lp_rast_triangle_32_4_16( struct lp_rasterizer_task *,
const union lp_rast_cmd_arg );
void lp_rast_rectangle( struct lp_rasterizer_task *,
const union lp_rast_cmd_arg );
void lp_rast_triangle_ms_1( struct lp_rasterizer_task *,
const union lp_rast_cmd_arg );
void lp_rast_triangle_ms_2( struct lp_rasterizer_task *,
@@ -406,4 +411,13 @@ lp_rast_set_state(struct lp_rasterizer_task *task,
void
lp_debug_bin( const struct cmd_bin *bin, int x, int y );
void
lp_linear_rasterize_bin(struct lp_rasterizer_task *task,
const struct cmd_bin *bin);
void
lp_rast_linear_rect_fallback(struct lp_rasterizer_task *task,
const struct lp_rast_shader_inputs *inputs,
const struct u_rect *box);
#endif
+255
View File
@@ -0,0 +1,255 @@
/**************************************************************************
*
* Copyright 2007-2021 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.
*
**************************************************************************/
/*
* Rasterization for binned rectangles within a tile
*/
#include <limits.h>
#include "util/u_math.h"
#include "lp_debug.h"
#include "lp_perf.h"
#include "lp_rast_priv.h"
/* Our 16-pixel stamps are layed out as:
*
* 0 1 2 3
* 4 5 6 7
* 8 9 10 11
* 12 13 14 15
*
* Define bitmasks for each row and column in this layout:
*/
#define COLUMN0 ((1<<0)|(1<<4)|(1<<8) |(1<<12))
#define COLUMN1 ((1<<1)|(1<<5)|(1<<9) |(1<<13))
#define COLUMN2 ((1<<2)|(1<<6)|(1<<10)|(1<<14))
#define COLUMN3 ((1<<3)|(1<<7)|(1<<11)|(1<<15))
#define ROW0 ((1<<0) |(1<<1) |(1<<2) |(1<<3))
#define ROW1 ((1<<4) |(1<<5) |(1<<6) |(1<<7))
#define ROW2 ((1<<8) |(1<<9) |(1<<10)|(1<<11))
#define ROW3 ((1<<12)|(1<<13)|(1<<14)|(1<<15))
#define STAMP_SIZE 4
static unsigned left_mask_tab[STAMP_SIZE] = {
COLUMN0 | COLUMN1 | COLUMN2 | COLUMN3,
COLUMN1 | COLUMN2 | COLUMN3,
COLUMN2 | COLUMN3,
COLUMN3,
};
static unsigned right_mask_tab[STAMP_SIZE] = {
COLUMN0,
COLUMN0 | COLUMN1,
COLUMN0 | COLUMN1 | COLUMN2,
COLUMN0 | COLUMN1 | COLUMN2 | COLUMN3,
};
static unsigned top_mask_tab[STAMP_SIZE] = {
ROW0 | ROW1 | ROW2 | ROW3,
ROW1 | ROW2 | ROW3,
ROW2 | ROW3,
ROW3,
};
static unsigned bottom_mask_tab[STAMP_SIZE] = {
ROW0,
ROW0 | ROW1,
ROW0 | ROW1 | ROW2,
ROW0 | ROW1 | ROW2 | ROW3,
};
static inline void
full(struct lp_rasterizer_task *task,
const struct lp_rast_rectangle *rect,
unsigned ix, unsigned iy)
{
LP_COUNT(nr_rect_fully_covered_4);
lp_rast_shade_quads_all(task,
&rect->inputs,
task->x + ix * STAMP_SIZE,
task->y + iy * STAMP_SIZE);
}
static inline void
partial(struct lp_rasterizer_task *task,
const struct lp_rast_rectangle *rect,
unsigned ix, unsigned iy,
unsigned mask)
{
/* Unfortunately we can end up generating full blocks on this path,
* need to catch them.
*/
if (mask == 0xffff)
full(task, rect, ix, iy);
else {
assert(mask);
LP_COUNT(nr_rect_partially_covered_4);
lp_rast_shade_quads_mask(task,
&rect->inputs,
task->x + ix * STAMP_SIZE,
task->y + iy * STAMP_SIZE,
mask);
}
}
static inline void
intersect_rect_and_tile(struct lp_rasterizer_task *task,
const struct lp_rast_rectangle *rect,
struct u_rect *box)
{
box->x0 = task->x;
box->y0 = task->y;
box->x1 = task->x + TILE_SIZE - 1;
box->y1 = task->y + TILE_SIZE - 1;
assert(u_rect_test_intersection(&rect->box, box));
u_rect_find_intersection(&rect->box, box);
box->x0 -= task->x;
box->x1 -= task->x;
box->y0 -= task->y;
box->y1 -= task->y;
}
/**
* Scan the tile in chunks and figure out which pixels to rasterize
* for this rectangle.
*/
void
lp_rast_rectangle(struct lp_rasterizer_task *task,
const union lp_rast_cmd_arg arg)
{
const struct lp_rast_rectangle *rect = arg.rectangle;
struct u_rect box;
unsigned ix0, ix1, iy0, iy1;
unsigned left_mask;
unsigned right_mask;
unsigned top_mask;
unsigned bottom_mask;
unsigned i,j;
/* Check for "disabled" rectangles generated in out-of-memory
* conditions.
*/
if (rect->inputs.disable) {
/* This command was partially binned and has been disabled */
return;
}
/* Intersect the rectangle with this tile.
*/
intersect_rect_and_tile(task, rect, &box);
/* The interior of the rectangle (if there is one) will be
* rasterized as full 4x4 stamps.
*
* At each edge of the rectangle, however, there will be a fringe
* of partial blocks where the edge lands somewhere in the middle
* of a 4-pixel stamp.
*
* For each edge, precalculate a mask of the pixels inside that
* edge for the first 4-pixel stamp.
*
* Note that at the corners, and for narrow rectangles, an
* individual stamp may have two or more edges active. We'll deal
* with that below by combining these masks as appropriate.
*/
left_mask = left_mask_tab [box.x0 & (STAMP_SIZE - 1)];
right_mask = right_mask_tab [box.x1 & (STAMP_SIZE - 1)];
top_mask = top_mask_tab [box.y0 & (STAMP_SIZE - 1)];
bottom_mask = bottom_mask_tab [box.y1 & (STAMP_SIZE - 1)];
ix0 = box.x0 / STAMP_SIZE;
ix1 = box.x1 / STAMP_SIZE;
iy0 = box.y0 / STAMP_SIZE;
iy1 = box.y1 / STAMP_SIZE;
/* Various special cases.
*/
if (ix0 == ix1 && iy0 == iy1) {
/* Rectangle is contained within a single 4x4 stamp:
*/
partial(task, rect, ix0, iy0,
(left_mask & right_mask &
top_mask & bottom_mask));
}
else if (ix0 == ix1) {
/* Left and right edges fall on the same 4-pixel-wide column:
*/
unsigned mask = left_mask & right_mask;
partial(task, rect, ix0, iy0, mask & top_mask);
for (i = iy0 + 1; i < iy1; i++)
partial(task, rect, ix0, i, mask);
partial(task, rect, ix0, iy1, mask & bottom_mask);
}
else if (iy0 == iy1) {
/* Top and bottom edges fall on the same 4-pixel-wide row:
*/
unsigned mask = top_mask & bottom_mask;
partial(task, rect, ix0, iy0, mask & left_mask);
for (i = ix0 + 1; i < ix1; i++)
partial(task, rect, i, iy0, mask);
partial(task, rect, ix1, iy0, mask & right_mask);
}
else {
/* Each pair of edges falls in a separate 4-pixel-wide
* row/column.
*/
partial(task, rect, ix0, iy0, left_mask & top_mask);
partial(task, rect, ix0, iy1, left_mask & bottom_mask);
partial(task, rect, ix1, iy0, right_mask & top_mask);
partial(task, rect, ix1, iy1, right_mask & bottom_mask);
for (i = ix0 + 1; i < ix1; i++)
partial(task, rect, i, iy0, top_mask);
for (i = ix0 + 1; i < ix1; i++)
partial(task, rect, i, iy1, bottom_mask);
for (i = iy0 + 1; i < iy1; i++)
partial(task, rect, ix0, i, left_mask);
for (i = iy0 + 1; i < iy1; i++)
partial(task, rect, ix1, i, right_mask);
/* Full interior blocks
*/
for (j = iy0 + 1; j < iy1; j++) {
for (i = ix0 + 1; i < ix1; i++) {
full(task, rect, i, j);
}
}
}
}
@@ -261,6 +261,31 @@ sign_bits4(const __m128i *cstep, int cdiff)
return _mm_movemask_epi8(result);
}
#define COLUMN0 ((1<<0)|(1<<4)|(1<<8) |(1<<12))
#define COLUMN1 ((1<<1)|(1<<5)|(1<<9) |(1<<13))
#define COLUMN2 ((1<<2)|(1<<6)|(1<<10)|(1<<14))
#define COLUMN3 ((1<<3)|(1<<7)|(1<<11)|(1<<15))
#define ROW0 ((1<<0) |(1<<1) |(1<<2) |(1<<3))
#define ROW1 ((1<<4) |(1<<5) |(1<<6) |(1<<7))
#define ROW2 ((1<<8) |(1<<9) |(1<<10)|(1<<11))
#define ROW3 ((1<<12)|(1<<13)|(1<<14)|(1<<15))
#define STAMP_SIZE 4
static unsigned bottom_mask_tab[STAMP_SIZE] = {
ROW3,
ROW3 | ROW2,
ROW3 | ROW2 | ROW1,
ROW3 | ROW2 | ROW1 | ROW0,
};
static unsigned right_mask_tab[STAMP_SIZE] = {
COLUMN3,
COLUMN3 | COLUMN2,
COLUMN3 | COLUMN2 | COLUMN1,
COLUMN3 | COLUMN2 | COLUMN1 | COLUMN0,
};
#define NR_PLANES 3
@@ -363,6 +363,16 @@ TRI_16(struct lp_rasterizer_task *task,
outmask = 0; /* outside one or more trivial reject planes */
if (x + 12 >= 64) {
int i = ((x + 12) - 64) / 4;
outmask |= right_mask_tab[i];
}
if (y + 12 >= 64) {
int i = ((y + 12) - 64) / 4;
outmask |= bottom_mask_tab[i];
}
x += task->x;
y += task->y;
+10 -24
View File
@@ -68,9 +68,7 @@ lp_scene_create( struct pipe_context *pipe )
return NULL;
scene->pipe = pipe;
scene->data.head =
CALLOC_STRUCT(data_block);
scene->data.head = &scene->data.first;
(void) mtx_init(&scene->mutex, mtx_plain);
@@ -101,8 +99,7 @@ lp_scene_destroy(struct lp_scene *scene)
{
lp_fence_reference(&scene->fence, NULL);
mtx_destroy(&scene->mutex);
assert(scene->data.head->next == NULL);
FREE(scene->data.head);
assert(scene->data.head == &scene->data.first);
FREE(scene);
}
@@ -129,8 +126,8 @@ lp_scene_is_empty(struct lp_scene *scene )
/* Returns true if there has ever been a failed allocation attempt in
* this scene. Used in triangle emit to avoid having to check success
* at each bin.
* this scene. Used in triangle/rectangle emit to avoid having to
* check success at each bin.
*/
boolean
lp_scene_is_oom(struct lp_scene *scene)
@@ -222,7 +219,7 @@ lp_scene_begin_rasterization(struct lp_scene *scene)
void
lp_scene_end_rasterization(struct lp_scene *scene )
{
int i, j;
int i;
/* Unmap color buffers */
for (i = 0; i < scene->fb.nr_cbufs; i++) {
@@ -248,19 +245,7 @@ lp_scene_end_rasterization(struct lp_scene *scene )
/* Reset all command lists:
*/
for (i = 0; i < scene->tiles_x; i++) {
for (j = 0; j < scene->tiles_y; j++) {
struct cmd_bin *bin = lp_scene_get_bin(scene, i, j);
bin->head = NULL;
bin->tail = NULL;
bin->last_state = NULL;
}
}
/* If there are any bins which weren't cleared by the loop above,
* they will be caught (on debug builds at least) by this assert:
*/
assert(lp_scene_is_empty(scene));
memset(scene->tile, 0, sizeof scene->tile);
/* Decrement texture ref counts
*/
@@ -310,13 +295,14 @@ lp_scene_end_rasterization(struct lp_scene *scene )
struct data_block_list *list = &scene->data;
struct data_block *block, *tmp;
for (block = list->head->next; block; block = tmp) {
for (block = list->head; block; block = tmp) {
tmp = block->next;
FREE(block);
if (block != &list->first)
FREE(block);
}
list->head = &list->first;
list->head->next = NULL;
list->head->used = 0;
}
lp_fence_reference(&scene->fence, NULL);
+6 -14
View File
@@ -54,7 +54,8 @@ struct lp_rast_state;
*/
#define CMD_BLOCK_MAX 29
/* Bytes per data block.
/* Bytes per data block. This effectively limits the maximum constant buffer
* size.
*/
#define DATA_BLOCK_SIZE (64 * 1024)
@@ -181,6 +182,8 @@ struct lp_scene {
unsigned resource_reference_size;
boolean alloc_failed;
boolean permit_linear_rasterizer;
/**
* Number of active tiles in each dimension.
* This basically the framebuffer size divided by tile size
@@ -236,7 +239,7 @@ lp_scene_alloc( struct lp_scene *scene, unsigned size)
if (LP_DEBUG & DEBUG_MEM)
debug_printf("alloc %u block %u/%u tot %u/%u\n",
size, block->used, DATA_BLOCK_SIZE,
size, block->used, (unsigned)DATA_BLOCK_SIZE,
scene->scene_size, LP_SCENE_MAX_SIZE);
if (block->used + size > DATA_BLOCK_SIZE) {
@@ -270,7 +273,7 @@ lp_scene_alloc_aligned( struct lp_scene *scene, unsigned size,
if (LP_DEBUG & DEBUG_MEM)
debug_printf("alloc %u block %u/%u tot %u/%u\n",
size + alignment - 1,
block->used, DATA_BLOCK_SIZE,
block->used, (unsigned)DATA_BLOCK_SIZE,
scene->scene_size, LP_SCENE_MAX_SIZE);
if (block->used + size + alignment - 1 > DATA_BLOCK_SIZE) {
@@ -288,17 +291,6 @@ lp_scene_alloc_aligned( struct lp_scene *scene, unsigned size,
}
/* Put back data if we decide not to use it, eg. culled triangles.
*/
static inline void
lp_scene_putback_data( struct lp_scene *scene, unsigned size)
{
struct data_block_list *list = &scene->data;
assert(list->head && list->head->used >= size);
list->head->used -= size;
}
/** Return pointer to a particular tile's bin. */
static inline struct cmd_bin *
lp_scene_get_bin(struct lp_scene *scene, unsigned x, unsigned y)
+7 -1
View File
@@ -70,11 +70,15 @@ static const struct debug_named_value lp_debug_flags[] = {
{ "counters", DEBUG_COUNTERS, NULL },
{ "scene", DEBUG_SCENE, NULL },
{ "fence", DEBUG_FENCE, NULL },
{ "no_fastpath", DEBUG_NO_FASTPATH, NULL },
{ "linear", DEBUG_LINEAR, NULL },
{ "linear2", DEBUG_LINEAR2, NULL },
{ "mem", DEBUG_MEM, NULL },
{ "fs", DEBUG_FS, NULL },
{ "cs", DEBUG_CS, NULL },
{ "tgsi_ir", DEBUG_TGSI_IR, NULL },
{ "cache_stats", DEBUG_CACHE_STATS, NULL },
{ "accurate_a0", DEBUG_ACCURATE_A0 },
DEBUG_NAMED_VALUE_END
};
#endif
@@ -89,6 +93,8 @@ static const struct debug_named_value lp_perf_flags[] = {
{ "no_blend", PERF_NO_BLEND, NULL },
{ "no_depth", PERF_NO_DEPTH, NULL },
{ "no_alphatest", PERF_NO_ALPHATEST, NULL },
{ "no_rast_linear", PERF_NO_RAST_LINEAR, NULL },
{ "no_shade", PERF_NO_SHADE, NULL },
DEBUG_NAMED_VALUE_END
};
@@ -1026,7 +1032,7 @@ llvmpipe_create_screen(struct sw_winsys *winsys)
screen->use_tgsi = (LP_DEBUG & DEBUG_TGSI_IR);
screen->num_threads = util_get_cpu_caps()->nr_cpus > 1 ? util_get_cpu_caps()->nr_cpus : 0;
#ifdef EMBEDDED_DEVICE
screen->num_threads = 0;
screen->num_threads = MIN2(screen->num_threads, 2);
#endif
screen->num_threads = debug_get_num_option("LP_NUM_THREADS", screen->num_threads);
screen->num_threads = MIN2(screen->num_threads, LP_MAX_THREADS);
+98 -3
View File
@@ -39,6 +39,7 @@
#include "util/u_inlines.h"
#include "util/u_memory.h"
#include "util/u_pack_color.h"
#include "util/u_cpu_detect.h"
#include "util/u_viewport.h"
#include "draw/draw_pipe.h"
#include "util/os_time.h"
@@ -53,6 +54,7 @@
#include "lp_setup_context.h"
#include "lp_screen.h"
#include "lp_state.h"
#include "lp_jit.h"
#include "frontend/sw_winsys.h"
#include "draw/draw_context.h"
@@ -84,6 +86,7 @@ lp_setup_get_empty_scene(struct lp_setup_context *setup)
lp_scene_begin_binning(setup->scene, &setup->fb);
setup->scene->permit_linear_rasterizer = setup->permit_linear_rasterizer;
}
@@ -98,6 +101,20 @@ first_triangle( struct lp_setup_context *setup,
setup->triangle( setup, v0, v1, v2 );
}
static boolean
first_rectangle( struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
const float (*v3)[4],
const float (*v4)[4],
const float (*v5)[4])
{
assert(setup->state == SETUP_ACTIVE);
lp_setup_choose_rect( setup );
return setup->rect( setup, v0, v1, v2, v3, v4, v5 );
}
static void
first_line( struct lp_setup_context *setup,
const float (*v0)[4],
@@ -117,7 +134,8 @@ first_point( struct lp_setup_context *setup,
setup->point( setup, v0 );
}
void lp_setup_reset( struct lp_setup_context *setup )
void
lp_setup_reset( struct lp_setup_context *setup )
{
unsigned i;
@@ -145,6 +163,7 @@ void lp_setup_reset( struct lp_setup_context *setup )
setup->line = first_line;
setup->point = first_point;
setup->triangle = first_triangle;
setup->rect = first_rectangle;
}
@@ -576,6 +595,7 @@ lp_setup_set_triangle_state( struct lp_setup_context *setup,
setup->ccw_is_frontface = ccw_is_frontface;
setup->cullmode = cull_mode;
setup->triangle = first_triangle;
setup->rect = first_rectangle;
setup->multisample = multisample;
setup->pixel_offset = half_pixel_center ? 0.5f : 0.0f;
setup->bottom_edge_rule = bottom_edge_rule;
@@ -600,6 +620,7 @@ lp_setup_set_line_state( struct lp_setup_context *setup,
void
lp_setup_set_point_state( struct lp_setup_context *setup,
float point_size,
boolean point_tri_clip,
boolean point_size_per_vertex,
uint sprite_coord_enable,
uint sprite_coord_origin,
@@ -610,6 +631,7 @@ lp_setup_set_point_state( struct lp_setup_context *setup,
setup->point_size = point_size;
setup->sprite_coord_enable = sprite_coord_enable;
setup->sprite_coord_origin = sprite_coord_origin;
setup->point_tri_clip = point_tri_clip;
setup->point_size_per_vertex = point_size_per_vertex;
setup->legacy_points = !point_quad_rasterization;
}
@@ -833,6 +855,7 @@ lp_setup_set_rasterizer_discard(struct lp_setup_context *setup,
setup->line = first_line;
setup->point = first_point;
setup->triangle = first_triangle;
setup->rect = first_rectangle;
}
}
@@ -846,6 +869,24 @@ lp_setup_set_vertex_info(struct lp_setup_context *setup,
}
void
lp_setup_set_linear_mode( struct lp_setup_context *setup,
boolean mode )
{
/* The linear rasterizer requires sse2 both at compile and runtime,
* in particular for the code in lp_rast_linear_fallback.c. This
* is more than ten-year-old technology, so it's a reasonable
* baseline.
*/
#if defined(PIPE_ARCH_SSE)
setup->permit_linear_rasterizer = (mode &&
util_get_cpu_caps()->has_sse2);
#else
setup->permit_linear_rasterizer = FALSE;
#endif
}
/**
* Called during state validation when LP_NEW_VIEWPORT is set.
*/
@@ -855,6 +896,7 @@ lp_setup_set_viewports(struct lp_setup_context *setup,
const struct pipe_viewport_state *viewports)
{
struct llvmpipe_context *lp = llvmpipe_context(setup->pipe);
float half_height, x0, y0;
unsigned i;
LP_DBG(DEBUG_SETUP, "%s\n", __FUNCTION__);
@@ -862,6 +904,26 @@ lp_setup_set_viewports(struct lp_setup_context *setup,
assert(num_viewports <= PIPE_MAX_VIEWPORTS);
assert(viewports);
/*
* Linear rasterizer path for scissor/viewport intersection.
*
* Calculate "scissor" rect from the (first) viewport.
* Just like stored scissor rects need inclusive coords.
* For rounding, assume half pixel center (d3d9 should not end up
* with fractional viewports) - quite obviously for msaa we'd need
* fractional values here (and elsewhere for the point bounding box).
*
* See: lp_setup.c::try_update_scene_state
*/
half_height = fabsf(viewports[0].scale[1]);
x0 = viewports[0].translate[0] - viewports[0].scale[0];
y0 = viewports[0].translate[1] - half_height;
setup->vpwh.x0 = (int)(x0 + 0.5f);
setup->vpwh.x1 = (int)(viewports[0].scale[0] * 2.0f + x0 - 0.5f);
setup->vpwh.y0 = (int)(y0 + 0.5f);
setup->vpwh.y1 = (int)(half_height * 2.0f + y0 - 0.5f);
setup->dirty |= LP_SETUP_NEW_SCISSOR;
/*
* For use in lp_state_fs.c, propagate the viewport values for all viewports.
*/
@@ -882,7 +944,7 @@ lp_setup_set_viewports(struct lp_setup_context *setup,
/**
* Called during state validation when LP_NEW_SAMPLER_VIEW is set.
* Called directly by llvmpipe_set_sampler_views
*/
void
lp_setup_set_fragment_sampler_views(struct lp_setup_context *setup,
@@ -1032,7 +1094,6 @@ lp_setup_set_fragment_sampler_views(struct lp_setup_context *setup,
setup->dirty |= LP_SETUP_NEW_FS;
}
/**
* Called during state validation when LP_NEW_SAMPLER is set.
*/
@@ -1066,6 +1127,8 @@ lp_setup_set_fragment_sampler_state(struct lp_setup_context *setup,
}
/**
* Is the given texture referenced by any scene?
* Note: we have to check all scenes including any scenes currently
@@ -1320,6 +1383,8 @@ try_update_scene_state( struct lp_setup_context *setup )
if (setup->dirty & LP_SETUP_NEW_SCISSOR) {
unsigned i;
setup->scissor_or_vp_clip = setup->scissor_test;
for (i = 0; i < PIPE_MAX_VIEWPORTS; ++i) {
setup->draw_regions[i] = setup->framebuffer;
if (setup->scissor_test) {
@@ -1327,6 +1392,36 @@ try_update_scene_state( struct lp_setup_context *setup )
&setup->draw_regions[i]);
}
}
if (setup->permit_linear_rasterizer) {
/* NOTE: this only takes first vp into account. */
boolean need_vp_scissoring = !!memcmp(&setup->vpwh, &setup->framebuffer,
sizeof(setup->framebuffer));
assert(setup->viewport_index_slot < 0);
setup->scissor_or_vp_clip |= need_vp_scissoring;
if (need_vp_scissoring) {
u_rect_possible_intersection(&setup->vpwh,
&setup->draw_regions[0]);
}
}
else if (setup->point_tri_clip) {
/*
* for d3d-style point clipping, we're going to need
* the fake vp scissor too. Hence do the intersection with vp,
* but don't indicate this. As above this will only work for first vp
* which should be ok because we instruct draw to only skip point
* clipping when there's only one viewport (this works because d3d10
* points are always single pixel).
* (Also note that if we have permit_linear_rasterizer this will
* cause large points to always get vp scissored, regardless the
* point_tri_clip setting.)
*/
boolean need_vp_scissoring = !!memcmp(&setup->vpwh, &setup->framebuffer,
sizeof(setup->framebuffer));
if (need_vp_scissoring) {
u_rect_possible_intersection(&setup->vpwh,
&setup->draw_regions[0]);
}
}
}
setup->dirty = 0;
+6 -1
View File
@@ -88,7 +88,8 @@ lp_setup_set_line_state( struct lp_setup_context *setup,
void
lp_setup_set_point_state( struct lp_setup_context *setup,
float point_size,
float point_size,
boolean point_tri_clip,
boolean point_size_per_vertex,
uint sprite_coord_enable,
uint sprite_coord_origin,
@@ -168,6 +169,10 @@ void
lp_setup_set_vertex_info( struct lp_setup_context *setup,
struct vertex_info *info );
void
lp_setup_set_linear_mode( struct lp_setup_context *setup,
boolean permit_linear_rasterizer );
void
lp_setup_begin_query(struct lp_setup_context *setup,
struct llvmpipe_query *pq);
@@ -0,0 +1,398 @@
/**************************************************************************
*
* Copyright 2010-2021 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.
*
**************************************************************************/
/**
* Look for common topology patterns which can be converted into rectangles.
*/
#include "lp_setup_context.h"
#include "draw/draw_vbuf.h"
#include "draw/draw_vertex.h"
#include "util/u_memory.h"
#include "util/u_math.h"
#include "lp_state_fs.h"
#include "lp_state_setup.h"
#include "lp_perf.h"
/**
* Duplicated from lp_setup_vbuf.c.
*/
typedef const float (*const_float4_ptr)[4];
static inline
const_float4_ptr get_vert(const void *vertex_buffer, int index, int stride)
{
return (const_float4_ptr)((char *)vertex_buffer + index * stride);
}
/* Aero sends these weird zero area triangles. Test for them here.
*/
static boolean
is_zero_area(const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4])
{
/* Specialized test for v0.y == v1.y == v2.y.
*/
return (v0[0][1] == v1[0][1] &&
v0[0][1] == v2[0][1]);
}
/**
* Assuming axis-aligned stretch blit (s a function of x alone, t a
* function of y alone), create a new vertex as in:
*
* vx------+
* | |
* | |
* out-----vy
*/
static void
make_vert(const float (*vx)[4],
const float (*vy)[4],
float (*out)[4])
{
out[0][0] = vx[0][0];
out[0][1] = vy[0][1];
out[0][2] = vx[0][2];
out[0][3] = vx[0][3];
out[1][0] = vx[1][0];
out[1][1] = vy[1][1];
}
/* Calculate axis-aligned interpolant for s as a function of x.
*/
static void
calc_interps(float x0, float x1,
float s0, float s1,
float *a, float *b)
{
assert(x0 != x1);
*a = (s0 - s1) / (x0 - x1);
*b = s0 - *a * x0;
}
/* Validate axis-aligned interpolant for s and t as functions of x and
* y respectively.
*/
static boolean
test_interps(const_float4_ptr v,
float as, float bs,
float at, float bt)
{
float s = as * v[0][0] + bs;
float t = at * v[0][1] + bt;
return (util_is_approx(s, v[1][0], 1/4096.0) &&
util_is_approx(t, v[1][1], 1/4096.0));
}
static void
rect(struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4])
{
ASSERTED int culled = LP_COUNT_GET(nr_culled_rects);
if (0) {
float as, bs, at, bt;
calc_interps(v0[0][0], v2[0][0], v0[1][0], v2[1][0], &as, &bs);
calc_interps(v0[0][1], v2[0][1], v0[1][1], v2[1][1], &at, &bt);
assert(test_interps(v1, as, bs, at, bt));
}
assert(v0[0][0] == v1[0][0]);
assert(v1[0][1] == v2[0][1]);
lp_rect_cw(setup, v0, v1, v2, TRUE);
assert(culled == LP_COUNT_GET(nr_culled_rects));
}
/**
* Check this is an axis-aligned rectangle as in:
*
* v3------v0
* | |
* | |
* v2------v1
*/
static boolean
test_rect(const_float4_ptr v0,
const_float4_ptr v1,
const_float4_ptr v2,
const_float4_ptr v3)
{
if (v0[0][0] != v1[0][0] ||
v1[0][1] != v2[0][1] ||
v2[0][0] != v3[0][0] ||
v3[0][1] != v0[0][1])
return FALSE;
if (v0[0][3] != 1.0 ||
v1[0][3] != 1.0 ||
v2[0][3] != 1.0 ||
v3[0][3] != 1.0)
return FALSE;
return TRUE;
}
/**
* Aero sends the following shape as
*
* 18 12
* +-------------------------------------------------/----+
* |\ /--------- /|
* | \ /--------- / |
* | \ /--------- / |
* | \ /--------- / |
* vA + +--------------------------------------------+ + vC
* | | 9 6 | |
* | /| |\ |
* | || || |
* | / | | \ |
* | | | | | |
* | | | 3 0 | \ |
* vB + / +--------------------------------------------+ | + vD
* | | / ---------/ \ | |
* |/ / ---------/ \ \|
* ||/ ---------/ \||
* |/ ---------/ \|
* +----/-------------------------------------------------+
* 1 2
*
* and in the following decomposition:
* (0, 1, 2)
* (3, 0, 1),
* (6, 0, 2),
* (9, 3, 1),
* (12, 2, 6),
* (12, 6, 9),
* (18, 1, 9),
* (18, 9, 12),
*
* There's no straight-forward way to interpret the existing vertices
* as rectangles. Instead we convert this into four axis-aligned
* rectangles by introducing new vertices at vA, vB, vC and vD, and
* then drawing rectangles.
*/
static boolean
check_elts24(struct lp_setup_context *setup, const void *vb, int stride)
{
const int count = 24;
const int uniq[8] = { 0, 1, 2, 3, 6, 9, 12, 18 };
const int elts[24] = {
0, 1, 2,
3, 0, 1,
6, 0, 2,
9, 3, 1,
12, 2, 6,
12, 6, 9,
18, 1, 9,
18, 9, 12
};
const_float4_ptr v0 = get_vert(vb, stride, 0);
const_float4_ptr v1 = get_vert(vb, stride, 1);
const_float4_ptr v2 = get_vert(vb, stride, 2);
const_float4_ptr v3 = get_vert(vb, stride, 3);
const_float4_ptr v6 = get_vert(vb, stride, 6);
const_float4_ptr v9 = get_vert(vb, stride, 9);
const_float4_ptr v12 = get_vert(vb, stride, 12);
const_float4_ptr v18 = get_vert(vb, stride, 18);
/* Could just calculate a set of interpolants and bin rectangle
* commands for this triangle list directly. Instead, introduce
* some new vertices and feed to the rectangle setup code:
*/
PIPE_ALIGN_VAR(16) float vA[2][4];
PIPE_ALIGN_VAR(16) float vB[2][4];
PIPE_ALIGN_VAR(16) float vC[2][4];
PIPE_ALIGN_VAR(16) float vD[2][4];
float as, bs;
float at, bt;
int i;
if (stride != 32)
return FALSE;
/* Check the shape is two rectangles:
*/
if (!test_rect(v12, v2, v1, v18))
return FALSE;
if (!test_rect(v6, v0, v3, v9))
return FALSE;
/* XXX: check one rect is inside the other?
*/
/* Check our tesselation matches:
*/
for (i = 0; i < count; i++) {
if (memcmp(get_vert(vb, i, stride),
get_vert(vb, elts[i], stride),
6 * sizeof(float)) != 0)
return FALSE;
}
/* Test that this is a stretch blit, meaning we should be able to
* introduce vertices at will.
*/
calc_interps(v0[0][0], v2[0][0], v0[1][0], v2[1][0], &as, &bs);
calc_interps(v0[0][1], v2[0][1], v0[1][1], v2[1][1], &at, &bt);
for (i = 0; i < ARRAY_SIZE(uniq); i++) {
const_float4_ptr v = get_vert(vb, stride, i);
if (!test_interps(v, as, bs, at, bt))
return FALSE;
}
make_vert(v18, v9, vA);
make_vert(v18, v3, vB);
make_vert(v12, v9, vC);
make_vert(v12, v3, vD);
assert(test_interps((const_float4_ptr)vA, as, bs, at, bt));
assert(test_interps((const_float4_ptr)vB, as, bs, at, bt));
assert(test_interps((const_float4_ptr)vC, as, bs, at, bt));
assert(test_interps((const_float4_ptr)vD, as, bs, at, bt));
rect(setup,
(const_float4_ptr)v12,
(const_float4_ptr)vC,
(const_float4_ptr)vA);
rect(setup,
(const_float4_ptr)v9,
(const_float4_ptr)v3,
(const_float4_ptr)vB);
rect(setup,
(const_float4_ptr)vD,
(const_float4_ptr)v2,
(const_float4_ptr)v1);
rect(setup,
(const_float4_ptr)vC,
(const_float4_ptr)vD,
(const_float4_ptr)v0);
return TRUE;
}
boolean
lp_setup_analyse_triangles(struct lp_setup_context *setup,
const void *vb,
int stride,
int nr)
{
int i;
const boolean variant_blit = setup->fs.current.variant->blit;
if (0) {
debug_printf("%s %d\n", __FUNCTION__, nr);
if (variant_blit) {
debug_printf(" - blit variant\n");
}
for (i = 0; i < nr; i += 3) {
const_float4_ptr v0 = get_vert(vb, i, stride);
const_float4_ptr v1 = get_vert(vb, i+1, stride);
const_float4_ptr v2 = get_vert(vb, i+2, stride);
lp_setup_print_triangle(setup, v0, v1, v2);
}
}
/* When drawing some window navigator bars, aero sends a mixed up
* rectangle:
*
* - first triangle ccw
* - second triangle cw
* - third triangle zero area.
*/
if (nr == 9 &&
is_zero_area(get_vert(vb, nr-1, stride),
get_vert(vb, nr-2, stride),
get_vert(vb, nr-3, stride)))
{
const float (*v0)[4] = get_vert(vb, 0, stride);
const float (*v1)[4] = get_vert(vb, 1, stride);
const float (*v2)[4] = get_vert(vb, 2, stride);
const float (*v3)[4] = get_vert(vb, 3, stride);
const float (*v4)[4] = get_vert(vb, 4, stride);
const float (*v5)[4] = get_vert(vb, 5, stride);
/*
* [ v0, v1, v2 ] [ v3, v4, v5 ]
* [(X0, Y0), (X0, Y1), (X1, Y1)] [(X1, Y0), (X1, Y1), (X0, Y0)]
*/
if (v0[0][0] == v1[0][0] && v0[0][0] == v5[0][0] &&
v2[0][0] == v3[0][0] && v2[0][0] == v4[0][0] &&
v0[0][1] == v3[0][1] && v0[0][1] == v5[0][1] &&
v1[0][1] == v2[0][1] && v1[0][1] == v4[0][1]) {
lp_rect_cw(setup, v0, v1, v2, TRUE);
}
return TRUE;
}
/* When highlighting (?) windows, aero sends a window border
* comprised of non-rectangular triangles, but which as a whole can
* be decomposed into rectangles.
*
* Again, with a zero-area trailing triangle.
*
* This requires introducing a couple of new vertices, which are
* luckily easy to compute.
*/
if (nr == 27 &&
variant_blit &&
setup->setup.variant->key.inputs[0].src_index == 1 &&
setup->setup.variant->key.inputs[0].usage_mask == 0x3 &&
is_zero_area(get_vert(vb, nr-1, stride),
get_vert(vb, nr-2, stride),
get_vert(vb, nr-3, stride)) &&
check_elts24(setup, vb, stride))
{
return TRUE;
}
return FALSE;
}
@@ -96,15 +96,18 @@ struct lp_setup_context
struct llvmpipe_query *active_queries[LP_MAX_ACTIVE_BINNED_QUERIES];
unsigned active_binned_queries;
boolean flatshade_first;
boolean ccw_is_frontface;
boolean scissor_test;
boolean point_size_per_vertex;
boolean legacy_points;
boolean rasterizer_discard;
boolean multisample;
boolean rectangular_lines;
unsigned cullmode;
unsigned flatshade_first:1;
unsigned ccw_is_frontface:1;
unsigned scissor_test:1;
unsigned scissor_or_vp_clip:1;
unsigned point_tri_clip:1;
unsigned point_size_per_vertex:1;
unsigned legacy_points:1;
unsigned rasterizer_discard:1;
unsigned permit_linear_rasterizer:1;
unsigned multisample:1;
unsigned rectangular_lines:1;
unsigned cullmode:2; /**< PIPE_FACE_x */
unsigned bottom_edge_rule;
float pixel_offset;
float line_width;
@@ -117,6 +120,7 @@ struct lp_setup_context
struct pipe_framebuffer_state fb;
struct u_rect framebuffer;
struct u_rect scissors[PIPE_MAX_VIEWPORTS];
struct u_rect vpwh;
struct u_rect draw_regions[PIPE_MAX_VIEWPORTS]; /* intersection of fb & scissor */
struct lp_jit_viewport viewports[PIPE_MAX_VIEWPORTS];
@@ -179,6 +183,15 @@ struct lp_setup_context
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4]);
boolean
(*rect)( struct lp_setup_context *,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
const float (*v3)[4],
const float (*v4)[4],
const float (*v5)[4]);
};
static inline void
@@ -199,6 +212,7 @@ scissor_planes_needed(boolean scis_planes[4], const struct u_rect *bbox,
void lp_setup_choose_triangle( struct lp_setup_context *setup );
void lp_setup_choose_line( struct lp_setup_context *setup );
void lp_setup_choose_point( struct lp_setup_context *setup );
void lp_setup_choose_rect( struct lp_setup_context *setup );
void lp_setup_init_vbuf(struct lp_setup_context *setup);
@@ -209,6 +223,15 @@ void lp_setup_destroy( struct lp_setup_context *setup );
boolean lp_setup_flush_and_restart(struct lp_setup_context *setup);
boolean
lp_setup_whole_tile(struct lp_setup_context *setup,
const struct lp_rast_shader_inputs *inputs,
int tx, int ty);
boolean
lp_setup_is_blit(const struct lp_setup_context *setup,
const struct lp_rast_shader_inputs *inputs);
void
lp_setup_print_triangle(struct lp_setup_context *setup,
const float (*v0)[4],
@@ -220,6 +243,19 @@ lp_setup_print_vertex(struct lp_setup_context *setup,
const char *name,
const float (*v)[4]);
void
lp_rect_cw(struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
boolean frontfacing);
void
lp_setup_triangle_ccw( struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
boolean front );
struct lp_rast_triangle *
lp_setup_alloc_triangle(struct lp_scene *scene,
@@ -227,6 +263,16 @@ lp_setup_alloc_triangle(struct lp_scene *scene,
unsigned nr_planes,
unsigned *tri_size);
struct lp_rast_rectangle *
lp_setup_alloc_rectangle(struct lp_scene *scene,
unsigned nr_inputs);
boolean
lp_setup_analyse_triangles(struct lp_setup_context *setup,
const void *vb,
int stride,
int nr);
boolean
lp_setup_bin_triangle(struct lp_setup_context *setup,
struct lp_rast_triangle *tri,
@@ -235,4 +281,9 @@ lp_setup_bin_triangle(struct lp_setup_context *setup,
int nr_planes,
unsigned scissor_index);
boolean
lp_setup_bin_rectangle(struct lp_setup_context *setup,
struct lp_rast_rectangle *rect);
#endif
+6 -11
View File
@@ -586,15 +586,8 @@ try_setup_line( struct lp_setup_context *setup,
bbox.y1--;
}
if (bbox.x1 < bbox.x0 ||
bbox.y1 < bbox.y0) {
if (0) debug_printf("empty bounding box\n");
LP_COUNT(nr_culled_tris);
return TRUE;
}
if (!u_rect_test_intersection(&setup->draw_regions[viewport_index], &bbox)) {
if (0) debug_printf("offscreen\n");
if (0) debug_printf("no intersection\n");
LP_COUNT(nr_culled_tris);
return TRUE;
}
@@ -611,9 +604,11 @@ try_setup_line( struct lp_setup_context *setup,
* Determine how many scissor planes we need, that is drop scissor
* edges if the bounding box of the tri is fully inside that edge.
*/
scissor = &setup->draw_regions[viewport_index];
scissor_planes_needed(s_planes, &bboxpos, scissor);
nr_planes += s_planes[0] + s_planes[1] + s_planes[2] + s_planes[3];
if (setup->scissor_or_vp_clip) {
scissor = &setup->draw_regions[viewport_index];
scissor_planes_needed(s_planes, &bboxpos, scissor);
nr_planes += s_planes[0] + s_planes[1] + s_planes[2] + s_planes[3];
}
line = lp_setup_alloc_triangle(scene,
key->num_inputs,
+93 -41
View File
@@ -352,11 +352,8 @@ try_setup_point( struct lp_setup_context *setup,
int adj = (setup->bottom_edge_rule != 0) ? 1 : 0;
float pixel_offset = setup->multisample ? 0.0 : setup->pixel_offset;
struct lp_scene *scene = setup->scene;
struct lp_rast_triangle *point;
unsigned bytes;
struct u_rect bbox;
int x[2], y[2];
unsigned nr_planes = 4;
struct point_info info;
unsigned viewport_index = 0;
unsigned layer = 0;
@@ -461,56 +458,63 @@ try_setup_point( struct lp_setup_context *setup,
}
if (!u_rect_test_intersection(&setup->draw_regions[viewport_index], &bbox)) {
if (0) debug_printf("offscreen\n");
if (0) debug_printf("no intersection\n");
LP_COUNT(nr_culled_tris);
return TRUE;
}
u_rect_find_intersection(&setup->draw_regions[viewport_index], &bbox);
point = lp_setup_alloc_triangle(scene,
key->num_inputs,
nr_planes,
&bytes);
if (!point)
return FALSE;
/* We can't use rectangle reasterizer for non-legacy points for now. */
if (!setup->legacy_points || setup->multisample) {
struct lp_rast_triangle *point;
struct lp_rast_plane *plane;
unsigned bytes;
unsigned nr_planes = 4;
point = lp_setup_alloc_triangle(scene,
key->num_inputs,
nr_planes,
&bytes);
if (!point)
return FALSE;
#ifdef DEBUG
point->v[0][0] = v0[0][0];
point->v[0][1] = v0[0][1];
point->v[0][0] = v0[0][0];
point->v[0][1] = v0[0][1];
#endif
LP_COUNT(nr_tris);
LP_COUNT(nr_tris);
if (draw_will_inject_frontface(lp_context->draw) &&
setup->face_slot > 0) {
point->inputs.frontfacing = v0[setup->face_slot][0];
} else {
point->inputs.frontfacing = TRUE;
}
if (draw_will_inject_frontface(lp_context->draw) &&
setup->face_slot > 0) {
point->inputs.frontfacing = v0[setup->face_slot][0];
} else {
point->inputs.frontfacing = TRUE;
}
info.v0 = v0;
info.dx01 = 0;
info.dx12 = fixed_width;
info.dy01 = fixed_width;
info.dy12 = 0;
info.a0 = GET_A0(&point->inputs);
info.dadx = GET_DADX(&point->inputs);
info.dady = GET_DADY(&point->inputs);
info.frontfacing = point->inputs.frontfacing;
info.v0 = v0;
info.dx01 = 0;
info.dx12 = fixed_width;
info.dy01 = fixed_width;
info.dy12 = 0;
info.a0 = GET_A0(&point->inputs);
info.dadx = GET_DADX(&point->inputs);
info.dady = GET_DADY(&point->inputs);
info.frontfacing = point->inputs.frontfacing;
/* Setup parameter interpolants:
*/
setup_point_coefficients(setup, &info);
/* Setup parameter interpolants:
*/
setup_point_coefficients(setup, &info);
point->inputs.disable = FALSE;
point->inputs.opaque = FALSE;
point->inputs.layer = layer;
point->inputs.viewport_index = viewport_index;
point->inputs.view_index = setup->view_index;
point->inputs.disable = FALSE;
point->inputs.is_blit = FALSE;
point->inputs.opaque = setup->fs.current.variant->opaque;
point->inputs.layer = layer;
point->inputs.viewport_index = viewport_index;
point->inputs.view_index = setup->view_index;
{
struct lp_rast_plane *plane = GET_PLANES(point);
plane = GET_PLANES(point);
plane[0].dcdx = ~0U << 8;
plane[0].dcdy = 0;
@@ -540,9 +544,57 @@ try_setup_point( struct lp_setup_context *setup,
else
plane[3].c++; /* bottom-left */
}
}
return lp_setup_bin_triangle(setup, point, &bbox, &bbox, nr_planes, viewport_index);
return lp_setup_bin_triangle(setup, point, &bbox, &bbox, nr_planes, viewport_index);
} else {
struct lp_rast_rectangle *point;
point = lp_setup_alloc_rectangle(scene,
key->num_inputs);
if (!point)
return FALSE;
#ifdef DEBUG
point->v[0][0] = v0[0][0];
point->v[0][1] = v0[0][1];
#endif
point->box.x0 = bbox.x0;
point->box.x1 = bbox.x1;
point->box.y0 = bbox.y0;
point->box.y1 = bbox.y1;
LP_COUNT(nr_tris);
if (draw_will_inject_frontface(lp_context->draw) &&
setup->face_slot > 0) {
point->inputs.frontfacing = v0[setup->face_slot][0];
} else {
point->inputs.frontfacing = TRUE;
}
info.v0 = v0;
info.dx01 = 0;
info.dx12 = fixed_width;
info.dy01 = fixed_width;
info.dy12 = 0;
info.a0 = GET_A0(&point->inputs);
info.dadx = GET_DADX(&point->inputs);
info.dady = GET_DADY(&point->inputs);
info.frontfacing = point->inputs.frontfacing;
/* Setup parameter interpolants:
*/
setup_point_coefficients(setup, &info);
point->inputs.disable = FALSE;
point->inputs.is_blit = FALSE;
point->inputs.opaque = setup->fs.current.variant->opaque;
point->inputs.layer = layer;
point->inputs.viewport_index = viewport_index;
point->inputs.view_index = setup->view_index;
return lp_setup_bin_rectangle(setup, point);
}
}
@@ -0,0 +1,900 @@
/**************************************************************************
*
* Copyright 2010-2021 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 THE AUTHORS 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.
*
**************************************************************************/
/**
* Setup/binning code for screen-aligned quads.
*/
#include "util/u_math.h"
#include "util/u_memory.h"
#include "lp_perf.h"
#include "lp_setup_context.h"
#include "lp_rast.h"
#include "lp_state_fs.h"
#include "lp_state_setup.h"
#define NUM_CHANNELS 4
#define UNDETERMINED_BLIT -1
static inline int
subpixel_snap(float a)
{
return util_iround(FIXED_ONE * a);
}
static inline float
fixed_to_float(int a)
{
return a * (1.0f / FIXED_ONE);
}
/**
* Alloc space for a new rectangle plus the input.a0/dadx/dady arrays
* immediately after it.
* The memory is allocated from the per-scene pool, not per-tile.
* \param size returns number of bytes allocated
* \param nr_inputs number of fragment shader inputs
* \return pointer to rectangle space
*/
struct lp_rast_rectangle *
lp_setup_alloc_rectangle(struct lp_scene *scene, unsigned nr_inputs)
{
unsigned input_array_sz = NUM_CHANNELS * (nr_inputs + 1) * sizeof(float);
struct lp_rast_rectangle *rect;
unsigned bytes;
bytes = sizeof(*rect) + (3 * input_array_sz);
rect = lp_scene_alloc_aligned( scene, bytes, 16 );
if (rect == NULL)
return NULL;
rect->inputs.stride = input_array_sz;
return rect;
}
/**
* The rectangle covers the whole tile- shade whole tile.
* XXX no rectangle/triangle dependencies in this file - share it with
* the same code in lp_setup_tri.c
* \param tx, ty the tile position in tiles, not pixels
*/
boolean
lp_setup_whole_tile(struct lp_setup_context *setup,
const struct lp_rast_shader_inputs *inputs,
int tx, int ty)
{
struct lp_scene *scene = setup->scene;
LP_COUNT(nr_fully_covered_64);
/* if variant is opaque and scissor doesn't effect the tile */
if (inputs->opaque) {
/* Several things prevent this optimization from working:
* - For layered rendering we can't determine if this covers the same layer
* as previous rendering (or in case of clears those actually always cover
* all layers so optimization is impossible). Need to use fb_max_layer and
* not setup->layer_slot to determine this since even if there's currently
* no slot assigned previous rendering could have used one.
* - If there were any Begin/End query commands in the scene then those
* would get removed which would be very wrong. Furthermore, if queries
* were just active we also can't do the optimization since to get
* accurate query results we unfortunately need to execute the rendering
* commands.
*/
if (!scene->fb.zsbuf && scene->fb_max_layer == 0 && !scene->had_queries) {
/*
* All previous rendering will be overwritten so reset the bin.
*/
lp_scene_bin_reset( scene, tx, ty );
}
if (inputs->is_blit) {
LP_COUNT(nr_blit_64);
return lp_scene_bin_cmd_with_state( scene, tx, ty,
setup->fs.stored,
LP_RAST_OP_BLIT,
lp_rast_arg_inputs(inputs) );
}
else {
LP_COUNT(nr_shade_opaque_64);
return lp_scene_bin_cmd_with_state( scene, tx, ty,
setup->fs.stored,
LP_RAST_OP_SHADE_TILE_OPAQUE,
lp_rast_arg_inputs(inputs) );
}
}
else {
LP_COUNT(nr_shade_64);
return lp_scene_bin_cmd_with_state( scene, tx, ty,
setup->fs.stored,
LP_RAST_OP_SHADE_TILE,
lp_rast_arg_inputs(inputs) );
}
}
boolean
lp_setup_is_blit(const struct lp_setup_context *setup,
const struct lp_rast_shader_inputs *inputs)
{
const struct lp_fragment_shader_variant *variant =
setup->fs.current.variant;
if (variant->blit) {
/*
* Detect blits.
*/
const struct lp_jit_texture *texture =
&setup->fs.current.jit_context.textures[0];
float dsdx, dsdy, dtdx, dtdy;
/* XXX: dadx vs dady confusion below?
*/
dsdx = GET_DADX(inputs)[1][0]*texture->width;
dsdy = GET_DADX(inputs)[1][1]*texture->width;
dtdx = GET_DADY(inputs)[1][0]*texture->height;
dtdy = GET_DADY(inputs)[1][1]*texture->height;
/*
* We don't need to check s0/t0 tolerances
* as we establish as pre-condition that there is no
* texture filtering.
*/
assert(variant->key.samplers[0].sampler_state.min_img_filter == PIPE_TEX_FILTER_NEAREST);
assert(variant->key.samplers[0].sampler_state.mag_img_filter == PIPE_TEX_FILTER_NEAREST);
/*
* Check for 1:1 match of texels to dest pixels
*/
if (util_is_approx(dsdx, 1.0f, 1.0f/LP_MAX_WIDTH) &&
util_is_approx(dsdy, 0.0f, 1.0f/LP_MAX_HEIGHT) &&
util_is_approx(dtdx, 0.0f, 1.0f/LP_MAX_WIDTH) &&
util_is_approx(dtdy, 1.0f, 1.0f/LP_MAX_HEIGHT)) {
return true;
}
else {
#if 0
debug_printf("dsdx = %f\n", dsdx);
debug_printf("dsdy = %f\n", dsdy);
debug_printf("dtdx = %f\n", dtdx);
debug_printf("dtdy = %f\n", dtdy);
debug_printf("\n");
#endif
return FALSE;
}
}
return FALSE;
}
static inline void
partial(struct lp_setup_context *setup,
const struct lp_rast_rectangle *rect,
unsigned ix, unsigned iy,
unsigned mask)
{
if (mask == 0) {
assert(rect->box.x0 <= ix * TILE_SIZE);
assert(rect->box.y0 <= iy * TILE_SIZE);
assert(rect->box.x1 >= (ix+1) * TILE_SIZE - 1);
assert(rect->box.y1 >= (iy+1) * TILE_SIZE - 1);
lp_setup_whole_tile(setup, &rect->inputs, ix, iy);
}
else {
LP_COUNT(nr_partially_covered_64);
lp_scene_bin_cmd_with_state( setup->scene,
ix, iy,
setup->fs.stored,
LP_RAST_OP_RECTANGLE,
lp_rast_arg_rectangle(rect) );
}
}
/**
* Setup/bin a screen-aligned rect.
* We need three corner vertices in order to correctly setup
* interpolated parameters. We *could* get away with just the
* diagonal vertices but it'd cause ugliness elsewhere.
*
* + -------v0
* | |
* v2 ------ v1
*
* By an unfortunate mixup between GL and D3D coordinate spaces, half
* of this file talks about clockwise rectangles (which were CCW in GL
* coordinate space), while the other half prefers to work with D3D
* CCW rectangles.
*/
static boolean
try_rect_cw(struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
boolean frontfacing)
{
const struct lp_fragment_shader_variant *variant =
setup->fs.current.variant;
const struct lp_setup_variant_key *key = &setup->setup.variant->key;
struct lp_scene *scene = setup->scene;
struct lp_rast_rectangle *rect;
boolean cw;
struct u_rect bbox;
unsigned viewport_index = 0;
unsigned layer = 0;
const float (*pv)[4];
/* x/y positions in fixed point */
int x0 = subpixel_snap(v0[0][0] - setup->pixel_offset);
int x1 = subpixel_snap(v1[0][0] - setup->pixel_offset);
int x2 = subpixel_snap(v2[0][0] - setup->pixel_offset);
int y0 = subpixel_snap(v0[0][1] - setup->pixel_offset);
int y1 = subpixel_snap(v1[0][1] - setup->pixel_offset);
int y2 = subpixel_snap(v2[0][1] - setup->pixel_offset);
LP_COUNT(nr_rects);
/* Cull clockwise rects without overflowing.
*/
cw = (x2 < x1) ^ (y0 < y2);
if (cw) {
LP_COUNT(nr_culled_rects);
return TRUE;
}
if (setup->flatshade_first) {
pv = v0;
}
else {
pv = v2;
}
if (setup->viewport_index_slot > 0) {
unsigned *udata = (unsigned*)pv[setup->viewport_index_slot];
viewport_index = lp_clamp_viewport_idx(*udata);
}
if (setup->layer_slot > 0) {
layer = *(unsigned*)pv[setup->layer_slot];
layer = MIN2(layer, scene->fb_max_layer);
}
/* Bounding rectangle (in pixels) */
{
/* Yes this is necessary to accurately calculate bounding boxes
* with the two fill-conventions we support. GL (normally) ends
* up needing a bottom-left fill convention, which requires
* slightly different rounding.
*/
int adj = (setup->bottom_edge_rule != 0) ? 1 : 0;
bbox.x0 = (MIN3(x0, x1, x2) + (FIXED_ONE-1)) >> FIXED_ORDER;
bbox.x1 = (MAX3(x0, x1, x2) + (FIXED_ONE-1)) >> FIXED_ORDER;
bbox.y0 = (MIN3(y0, y1, y2) + (FIXED_ONE-1) + adj) >> FIXED_ORDER;
bbox.y1 = (MAX3(y0, y1, y2) + (FIXED_ONE-1) + adj) >> FIXED_ORDER;
/* Inclusive coordinates:
*/
bbox.x1--;
bbox.y1--;
}
if (!u_rect_test_intersection(&setup->draw_regions[viewport_index], &bbox)) {
if (0) debug_printf("no intersection\n");
LP_COUNT(nr_culled_rects);
return TRUE;
}
u_rect_find_intersection(&setup->draw_regions[viewport_index], &bbox);
rect = lp_setup_alloc_rectangle(scene, key->num_inputs);
if (!rect)
return FALSE;
#ifdef DEBUG
rect->v[0][0] = v0[0][0];
rect->v[0][1] = v0[0][1];
rect->v[1][0] = v1[0][0];
rect->v[1][1] = v1[0][1];
#endif
rect->box.x0 = bbox.x0;
rect->box.x1 = bbox.x1;
rect->box.y0 = bbox.y0;
rect->box.y1 = bbox.y1;
/* Setup parameter interpolants:
*/
setup->setup.variant->jit_function( v0,
v1,
v2,
frontfacing,
GET_A0(&rect->inputs),
GET_DADX(&rect->inputs),
GET_DADY(&rect->inputs),
&setup->setup.variant->key );
rect->inputs.frontfacing = frontfacing;
rect->inputs.disable = FALSE;
rect->inputs.is_blit = lp_setup_is_blit(setup, &rect->inputs);
rect->inputs.opaque = variant->opaque;
rect->inputs.layer = layer;
rect->inputs.viewport_index = viewport_index;
rect->inputs.view_index = setup->view_index;
return lp_setup_bin_rectangle(setup, rect);
}
boolean
lp_setup_bin_rectangle(struct lp_setup_context *setup,
struct lp_rast_rectangle *rect)
{
struct lp_scene *scene = setup->scene;
unsigned ix0, iy0, ix1, iy1;
unsigned i, j;
unsigned left_mask = 0;
unsigned right_mask = 0;
unsigned top_mask = 0;
unsigned bottom_mask = 0;
/*
* All fields of 'rect' are now set. The remaining code here is
* concerned with binning.
*/
/* Convert to inclusive tile coordinates:
*/
ix0 = rect->box.x0 / TILE_SIZE;
iy0 = rect->box.y0 / TILE_SIZE;
ix1 = rect->box.x1 / TILE_SIZE;
iy1 = rect->box.y1 / TILE_SIZE;
/*
* Clamp to framebuffer size
*/
assert(ix0 == MAX2(ix0, 0));
assert(iy0 == MAX2(iy0, 0));
assert(ix1 == MIN2(ix1, scene->tiles_x - 1));
assert(iy1 == MIN2(iy1, scene->tiles_y - 1));
if (ix0 * TILE_SIZE != rect->box.x0)
left_mask = RECT_PLANE_LEFT;
if (ix1 * TILE_SIZE + TILE_SIZE - 1 != rect->box.x1)
right_mask = RECT_PLANE_RIGHT;
if (iy0 * TILE_SIZE != rect->box.y0)
top_mask = RECT_PLANE_TOP;
if (iy1 * TILE_SIZE + TILE_SIZE - 1 != rect->box.y1)
bottom_mask = RECT_PLANE_BOTTOM;
/* Determine which tile(s) intersect the rectangle's bounding box
*/
if (iy0 == iy1 && ix0 == ix1) {
partial(setup, rect, ix0, iy0,
(left_mask | right_mask | top_mask | bottom_mask));
}
else if (ix0 == ix1) {
unsigned mask = left_mask | right_mask;
partial(setup, rect, ix0, iy0, mask | top_mask);
for (i = iy0 + 1; i < iy1; i++)
partial(setup, rect, ix0, i, mask);
partial(setup, rect, ix0, iy1, mask | bottom_mask);
}
else if (iy0 == iy1) {
unsigned mask = top_mask | bottom_mask;
partial(setup, rect, ix0, iy0, mask | left_mask);
for (i = ix0 + 1; i < ix1; i++)
partial(setup, rect, i, iy0, mask);
partial(setup, rect, ix1, iy0, mask | right_mask);
}
else {
partial(setup, rect, ix0, iy0, left_mask | top_mask);
partial(setup, rect, ix0, iy1, left_mask | bottom_mask);
partial(setup, rect, ix1, iy0, right_mask | top_mask);
partial(setup, rect, ix1, iy1, right_mask | bottom_mask);
/* Top/Bottom fringes
*/
for (i = ix0 + 1; i < ix1; i++) {
partial(setup, rect, i, iy0, top_mask);
partial(setup, rect, i, iy1, bottom_mask);
}
/* Left/Right fringes
*/
for (i = iy0 + 1; i < iy1; i++) {
partial(setup, rect, ix0, i, left_mask);
partial(setup, rect, ix1, i, right_mask);
}
/* Full interior tiles
*/
for (j = iy0 + 1; j < iy1; j++) {
for (i = ix0 + 1; i < ix1; i++) {
lp_setup_whole_tile(setup, &rect->inputs, i, j);
}
}
}
/* Catch any out-of-memory which occurred during binning. Do this
* once here rather than checking all the return values throughout.
*/
if (lp_scene_is_oom(scene)) {
/* Disable rasterization of this partially-binned rectangle.
* We'll flush this scene and re-bin the entire rectangle:
*/
rect->inputs.disable = TRUE;
return FALSE;
}
return TRUE;
}
void
lp_rect_cw(struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
boolean frontfacing)
{
if (!try_rect_cw(setup, v0, v1, v2, frontfacing)) {
if (!lp_setup_flush_and_restart(setup))
return;
if (!try_rect_cw(setup, v0, v1, v2, frontfacing))
return;
}
}
/**
* Take the six vertices for two triangles and try to determine if they
* form a screen-aligned quad/rectangle. If so, draw the rect directly,
* else, draw as two regular triangles.
*/
static boolean
do_rect_ccw(struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
const float (*v3)[4],
const float (*v4)[4],
const float (*v5)[4],
boolean front)
{
const float (*rv0)[4], (*rv1)[4], (*rv2)[4], (*rv3)[4]; /* rect verts */
#define SAME_POS(A, B) (A[0][0] == B[0][0] && \
A[0][1] == B[0][1] && \
A[0][2] == B[0][2] && \
A[0][3] == B[0][3])
/* Only need to consider CCW orientations. There are nine ways
* that two counter-clockwise triangles can join up:
*/
if (SAME_POS(v0, v3)) {
if (SAME_POS(v2, v4)) {
/*
* v5 v4/v2
* +-----+
* | / |
* | / |
* | / |
* +-----+
* v3/v0 v1
*/
rv0 = v5;
rv1 = v0;
rv2 = v1;
rv3 = v2;
}
else if (SAME_POS(v1, v5)) {
/*
* v4 v3/v0
* +-----+
* | / |
* | / |
* | / |
* +-----+
* v5/v1 v2
*/
rv0 = v4;
rv1 = v1;
rv2 = v2;
rv3 = v0;
}
else {
goto emit_triangles;
}
}
else if (SAME_POS(v0, v5)) {
if (SAME_POS(v2, v3)) {
/*
* v4 v3/v2
* +-----+
* | / |
* | / |
* | / |
* +-----+
* v5/v0 v1
*/
rv0 = v4;
rv1 = v0;
rv2 = v1;
rv3 = v2;
}
else if (SAME_POS(v1, v4)) {
/*
* v3 v5/v0
* +-----+
* | / |
* | / |
* | / |
* +-----+
* v4/v1 v2
*/
rv0 = v3;
rv1 = v1;
rv2 = v2;
rv3 = v0;
}
else {
goto emit_triangles;
}
}
else if (SAME_POS(v0, v4)) {
if (SAME_POS(v2, v5)) {
/*
* v3 v5/v2
* +-----+
* | / |
* | / |
* | / |
* +-----+
* v4/v0 v1
*/
rv0 = v3;
rv1 = v0;
rv2 = v1;
rv3 = v2;
}
else if (SAME_POS(v1, v3)) {
/*
* v5 v4/v0
* +-----+
* | / |
* | / |
* | / |
* +-----+
* v3/v1 v2
*/
rv0 = v5;
rv1 = v1;
rv2 = v2;
rv3 = v0;
}
else {
goto emit_triangles;
}
}
else if (SAME_POS(v2, v3)) {
if (SAME_POS(v1, v4)) {
/*
* v5 v4/v1
* +-----+
* | / |
* | / |
* | / |
* +-----+
* v3/v2 v0
*/
rv0 = v5;
rv1 = v2;
rv2 = v0;
rv3 = v1;
}
else {
goto emit_triangles;
}
}
else if (SAME_POS(v2, v5)) {
if (SAME_POS(v1, v3)) {
/*
* v4 v3/v1
* +-----+
* | / |
* | / |
* | / |
* +-----+
* v5/v2 v0
*/
rv0 = v4;
rv1 = v2;
rv2 = v0;
rv3 = v1;
}
else {
goto emit_triangles;
}
}
else if (SAME_POS(v2, v4)) {
if (SAME_POS(v1, v5)) {
/*
* v3 v5/v1
* +-----+
* | / |
* | / |
* | / |
* +-----+
* v4/v2 v0
*/
rv0 = v3;
rv1 = v2;
rv2 = v0;
rv3 = v1;
}
else {
goto emit_triangles;
}
}
else {
goto emit_triangles;
}
#define SAME_X(A, B) (A[0][0] == B[0][0])
#define SAME_Y(A, B) (A[0][1] == B[0][1])
/* The vertices are now counter clockwise, as such:
*
* rv0 -------rv3
* | |
* rv1 ------ rv2
*
* To render as a rectangle,
* * The X values should be the same at v0, v1 and v2, v3.
* * The Y values should be the same at v0, v3 and v1, v2.
*/
if (SAME_Y(rv0, rv1)) {
const float (*tmp)[4];
tmp = rv0;
rv0 = rv1;
rv1 = rv2;
rv2 = rv3;
rv3 = tmp;
}
if (SAME_X(rv0, rv1) && SAME_X(rv2, rv3) &&
SAME_Y(rv0, rv3) && SAME_Y(rv1, rv2)) {
const struct lp_setup_variant_key *key = &setup->setup.variant->key;
const unsigned n = key->num_inputs;
unsigned i, j;
/* We have a rectangle. Check that the other attributes are
* coplanar.
*/
for (i = 0; i < n; i++) {
for (j = 0; j < 4; j++) {
if (key->inputs[i].usage_mask & (1<<j)) {
unsigned k = key->inputs[i].src_index;
float dxdx1, dxdx2, dxdy1, dxdy2;
dxdx1 = rv0[k][j] - rv3[k][j];
dxdx2 = rv1[k][j] - rv2[k][j];
dxdy1 = rv0[k][j] - rv1[k][j];
dxdy2 = rv3[k][j] - rv2[k][j];
if (dxdx1 != dxdx2 ||
dxdy1 != dxdy2) {
goto emit_triangles;
}
}
}
}
/* Note we're changing to clockwise here. Fix this by reworking
* lp_rect_cw to expect/operate on ccw rects. Note that
* function was previously misnamed.
*/
lp_rect_cw(setup, rv0, rv2, rv1, front);
return TRUE;
}
else {
/* setup->quad(setup, rv0, rv1, rv2, rv3); */
}
emit_triangles:
return FALSE;
}
enum winding {
WINDING_NONE = 0,
WINDING_CCW,
WINDING_CW
};
static inline enum winding
winding(const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4])
{
/* edge vectors e = v0 - v2, f = v1 - v2 */
const float ex = v0[0][0] - v2[0][0];
const float ey = v0[0][1] - v2[0][1];
const float fx = v1[0][0] - v2[0][0];
const float fy = v1[0][1] - v2[0][1];
/* det = cross(e,f).z */
const float det = ex * fy - ey * fx;
if (det < 0.0f)
return WINDING_CCW;
else if (det > 0.0f)
return WINDING_CW;
else
return WINDING_NONE;
}
static boolean
setup_rect_cw(struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
const float (*v3)[4],
const float (*v4)[4],
const float (*v5)[4])
{
enum winding winding0 = winding(v0, v1, v2);
enum winding winding1 = winding(v3, v4, v5);
if (winding0 == WINDING_CW &&
winding1 == WINDING_CW) {
return do_rect_ccw(setup, v0, v2, v1, v3, v5, v4, !setup->ccw_is_frontface);
} else if (winding0 == WINDING_CW) {
setup->triangle(setup, v0, v1, v2);
return TRUE;
} else if (winding1 == WINDING_CW) {
setup->triangle(setup, v3, v4, v5);
return TRUE;
} else {
return TRUE;
}
}
static boolean
setup_rect_ccw(struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
const float (*v3)[4],
const float (*v4)[4],
const float (*v5)[4])
{
enum winding winding0 = winding(v0, v1, v2);
enum winding winding1 = winding(v3, v4, v5);
if (winding0 == WINDING_CCW &&
winding1 == WINDING_CCW) {
return do_rect_ccw(setup, v0, v1, v2, v3, v4, v5, setup->ccw_is_frontface);
} else if (winding0 == WINDING_CCW) {
setup->triangle(setup, v0, v1, v2);
return TRUE;
} else if (winding1 == WINDING_CCW) {
return FALSE;
setup->triangle(setup, v3, v4, v5);
return TRUE;
} else {
return TRUE;
}
}
static boolean
setup_rect_noop(struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
const float (*v3)[4],
const float (*v4)[4],
const float (*v5)[4])
{
return TRUE;
}
static boolean
setup_rect_both(struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
const float (*v3)[4],
const float (*v4)[4],
const float (*v5)[4])
{
enum winding winding0 = winding(v0, v1, v2);
enum winding winding1 = winding(v3, v4, v5);
if (winding0 != winding1) {
/* If we knew that the "front" parameter wasn't going to be
* referenced, could rearrange one of the two triangles such
* that they were both CCW. Aero actually does send mixed
* CW/CCW rectangles under some circumstances, but we catch them
* explicitly.
*/
return FALSE;
}
else if (winding0 == WINDING_CCW) {
return do_rect_ccw(setup, v0, v1, v2, v3, v4, v5, setup->ccw_is_frontface);
}
else if (winding0 == WINDING_CW) {
return do_rect_ccw(setup, v0, v2, v1, v3, v5, v4, !setup->ccw_is_frontface);
} else {
return TRUE;
}
}
void
lp_setup_choose_rect( struct lp_setup_context *setup )
{
if (setup->rasterizer_discard) {
setup->rect = setup_rect_noop;
return;
}
switch (setup->cullmode) {
case PIPE_FACE_NONE:
setup->rect = setup_rect_both;
break;
case PIPE_FACE_BACK:
setup->rect = setup->ccw_is_frontface ? setup_rect_ccw : setup_rect_cw;
break;
case PIPE_FACE_FRONT:
setup->rect = setup->ccw_is_frontface ? setup_rect_cw : setup_rect_ccw;
break;
default:
setup->rect = setup_rect_noop;
break;
}
}
+121 -56
View File
@@ -205,6 +205,7 @@ lp_rast_32_tri_tab[MAX_PLANES+1] = {
LP_RAST_OP_TRIANGLE_32_8
};
static unsigned
lp_rast_ms_tri_tab[MAX_PLANES+1] = {
0, /* should be impossible */
@@ -218,56 +219,46 @@ lp_rast_ms_tri_tab[MAX_PLANES+1] = {
LP_RAST_OP_MS_TRIANGLE_8
};
/**
* The primitive covers the whole tile- shade whole tile.
/*
* Detect big primitives drawn with an alpha == 1.0.
*
* \param tx, ty the tile position in tiles, not pixels
* This is used when simulating anti-aliasing primitives in shaders, e.g.,
* when drawing the windows client area in Aero's flip-3d effect.
*/
static boolean
lp_setup_whole_tile(struct lp_setup_context *setup,
const struct lp_rast_shader_inputs *inputs,
int tx, int ty)
check_opaque(struct lp_setup_context *setup,
const float (*v1)[4],
const float (*v2)[4],
const float (*v3)[4])
{
struct lp_scene *scene = setup->scene;
const struct lp_fragment_shader_variant *variant =
setup->fs.current.variant;
const struct lp_tgsi_channel_info *alpha_info = &variant->shader->info.cbuf[0][3];
LP_COUNT(nr_fully_covered_64);
if (variant->opaque)
return TRUE;
if (!variant->potentially_opaque)
return FALSE;
/* if variant is opaque and scissor doesn't effect the tile */
if (inputs->opaque) {
/* Several things prevent this optimization from working:
* - For layered rendering we can't determine if this covers the same layer
* as previous rendering (or in case of clears those actually always cover
* all layers so optimization is impossible). Need to use fb_max_layer and
* not setup->layer_slot to determine this since even if there's currently
* no slot assigned previous rendering could have used one.
* - If there were any Begin/End query commands in the scene then those
* would get removed which would be very wrong. Furthermore, if queries
* were just active we also can't do the optimization since to get
* accurate query results we unfortunately need to execute the rendering
* commands.
*/
if (!scene->fb.zsbuf && scene->fb_max_layer == 0 && !scene->had_queries) {
/*
* All previous rendering will be overwritten so reset the bin.
*/
lp_scene_bin_reset( scene, tx, ty );
}
LP_COUNT(nr_shade_opaque_64);
return lp_scene_bin_cmd_with_state( scene, tx, ty,
setup->fs.stored,
LP_RAST_OP_SHADE_TILE_OPAQUE,
lp_rast_arg_inputs(inputs) );
} else {
LP_COUNT(nr_shade_64);
return lp_scene_bin_cmd_with_state( scene, tx, ty,
setup->fs.stored,
LP_RAST_OP_SHADE_TILE,
lp_rast_arg_inputs(inputs) );
if (alpha_info->file == TGSI_FILE_CONSTANT) {
const float *constants = setup->fs.current.jit_context.constants[0];
float alpha = constants[alpha_info->u.index*4 +
alpha_info->swizzle];
return alpha == 1.0f;
}
if (alpha_info->file == TGSI_FILE_INPUT) {
return (v1[1 + alpha_info->u.index][alpha_info->swizzle] == 1.0f &&
v2[1 + alpha_info->u.index][alpha_info->swizzle] == 1.0f &&
v3[1 + alpha_info->u.index][alpha_info->swizzle] == 1.0f);
}
return FALSE;
}
/**
* Do basic setup for triangle rasterization and determine which
* framebuffer tiles are touched. Put the triangle in the scene's
@@ -333,15 +324,8 @@ do_triangle_ccw(struct lp_setup_context *setup,
bbox.y1 = (MAX3(position->y[0], position->y[1], position->y[2]) - 1 + adj) >> FIXED_ORDER;
}
if (bbox.x1 < bbox.x0 ||
bbox.y1 < bbox.y0) {
if (0) debug_printf("empty bounding box\n");
LP_COUNT(nr_culled_tris);
return TRUE;
}
if (!u_rect_test_intersection(&setup->draw_regions[viewport_index], &bbox)) {
if (0) debug_printf("offscreen\n");
if (0) debug_printf("no intersection\n");
LP_COUNT(nr_culled_tris);
return TRUE;
}
@@ -360,9 +344,11 @@ do_triangle_ccw(struct lp_setup_context *setup,
* Determine how many scissor planes we need, that is drop scissor
* edges if the bounding box of the tri is fully inside that edge.
*/
scissor = &setup->draw_regions[viewport_index];
scissor_planes_needed(s_planes, &bboxpos, scissor);
nr_planes += s_planes[0] + s_planes[1] + s_planes[2] + s_planes[3];
if (setup->scissor_or_vp_clip) {
scissor = &setup->draw_regions[viewport_index];
scissor_planes_needed(s_planes, &bboxpos, scissor);
nr_planes += s_planes[0] + s_planes[1] + s_planes[2] + s_planes[3];
}
tri = lp_setup_alloc_triangle(scene,
key->num_inputs,
@@ -382,17 +368,97 @@ do_triangle_ccw(struct lp_setup_context *setup,
LP_COUNT(nr_tris);
/*
* Rotate the tri such that v0 is closest to the fb origin.
* This can give more accurate a0 value (which is at fb origin)
* when calculating the interpolants.
* It can't work when there's flat shading for instance in one
* of the attributes, hence restrict this to just a single attribute
* which is what causes some test failures.
* (This does not address the problem that interpolation may be
* inaccurate if gradients are relatively steep in small tris far
* away from the origin. It does however fix the (silly) wgf11rasterizer
* Interpolator test.)
* XXX This causes problems with mipgen -EmuTexture for not yet really
* understood reasons (if the vertices would be submitted in a different
* order, we'd also generate the same "wrong" results here without
* rotation). In any case, that we generate different values if a prim
* has the vertices rotated but is otherwise the same (which is due to
* numerical issues) is not a nice property. An additional problem by
* swapping the vertices here (which is possibly worse) is that
* the same primitive coming in twice might generate different values
* (in particular for z) due to the swapping potentially not happening
* both times, if the attributes to be interpolated are different. For now,
* just restrict this to not get used with dx9 (by checking pixel offset),
* could also restrict it further to only trigger with wgf11Interpolator
* Rasterizer test (the only place which needs it, with always the same
* vertices even).
*/
if ((LP_DEBUG & DEBUG_ACCURATE_A0) &&
setup->pixel_offset == 0.5f &&
key->num_inputs == 1 &&
(key->inputs[0].interp == LP_INTERP_LINEAR ||
key->inputs[0].interp == LP_INTERP_PERSPECTIVE)) {
float dist0 = v0[0][0] * v0[0][0] + v0[0][1] * v0[0][1];
float dist1 = v1[0][0] * v1[0][0] + v1[0][1] * v1[0][1];
float dist2 = v2[0][0] * v2[0][0] + v2[0][1] * v2[0][1];
if (dist0 > dist1 && dist1 < dist2) {
const float (*vt)[4];
int x, y;
vt = v0;
v0 = v1;
v1 = v2;
v2 = vt;
x = position->x[0];
y = position->y[0];
position->x[0] = position->x[1];
position->y[0] = position->y[1];
position->x[1] = position->x[2];
position->y[1] = position->y[2];
position->x[2] = x;
position->y[2] = y;
position->dx20 = position->dx01;
position->dy20 = position->dy01;
position->dx01 = position->x[0] - position->x[1];
position->dy01 = position->y[0] - position->y[1];
}
else if (dist0 > dist2) {
const float (*vt)[4];
int x, y;
vt = v0;
v0 = v2;
v2 = v1;
v1 = vt;
x = position->x[0];
y = position->y[0];
position->x[0] = position->x[2];
position->y[0] = position->y[2];
position->x[2] = position->x[1];
position->y[2] = position->y[1];
position->x[1] = x;
position->y[1] = y;
position->dx01 = position->dx20;
position->dy01 = position->dy20;
position->dx20 = position->x[2] - position->x[0];
position->dy20 = position->y[2] - position->y[0];
}
}
/* Setup parameter interpolants:
*/
setup->setup.variant->jit_function(v0, v1, v2,
frontfacing,
GET_A0(&tri->inputs),
GET_DADX(&tri->inputs),
GET_DADY(&tri->inputs));
GET_DADY(&tri->inputs),
&setup->setup.variant->key);
tri->inputs.frontfacing = frontfacing;
tri->inputs.disable = FALSE;
tri->inputs.opaque = setup->fs.current.variant->opaque;
tri->inputs.is_blit = FALSE;
tri->inputs.opaque = check_opaque(setup, v0, v1, v2);
tri->inputs.layer = layer;
tri->inputs.viewport_index = viewport_index;
tri->inputs.view_index = setup->view_index;
@@ -693,7 +759,6 @@ do_triangle_ccw(struct lp_setup_context *setup,
* (easier to evaluate) to ordinary planes.)
*/
if (nr_planes > 3) {
/* why not just use draw_regions */
struct lp_rast_plane *plane_s = &plane[3];
if (s_planes[0]) {
@@ -912,8 +977,8 @@ lp_setup_bin_triangle(struct lp_setup_context *setup,
ystep[i] = ((int64_t)plane[i].dcdy) << TILE_ORDER;
}
tri->inputs.is_blit = lp_setup_is_blit(setup, &tri->inputs);
/* Test tile-sized blocks against the triangle.
* Discard blocks fully outside the tri. If the block is fully
* contained inside the tri, bin an lp_rast_shade_tile command.
+133 -17
View File
@@ -41,9 +41,16 @@
#include "draw/draw_vbuf.h"
#include "draw/draw_vertex.h"
#include "util/u_memory.h"
#include "util/u_math.h"
#include "lp_state_fs.h"
#include "lp_perf.h"
#define LP_MAX_VBUF_INDEXES 1024
/* It should be a multiple of both 6 and 4 (in other words, a multiple of 12)
* to ensure draw splits between a whole number of rectangles.
*/
#define LP_MAX_VBUF_INDEXES 1020
#define LP_MAX_VBUF_SIZE 4096
@@ -135,6 +142,22 @@ static inline const_float4_ptr get_vert( const void *vertex_buffer,
return (const_float4_ptr)((char *)vertex_buffer + index * stride);
}
static inline void
rect(struct lp_setup_context *setup,
const float (*v0)[4],
const float (*v1)[4],
const float (*v2)[4],
const float (*v3)[4],
const float (*v4)[4],
const float (*v5)[4])
{
if (!setup->permit_linear_rasterizer ||
!setup->rect( setup, v0, v1, v2, v3, v4, v5)) {
setup->triangle(setup, v0, v1, v2);
setup->triangle(setup, v3, v4, v5);
}
}
/**
* draw elements / indexed primitives
*/
@@ -145,6 +168,7 @@ lp_setup_draw_elements(struct vbuf_render *vbr, const ushort *indices, uint nr)
const unsigned stride = setup->vertex_info->size * sizeof(float);
const void *vertex_buffer = setup->vertex_buffer;
const boolean flatshade_first = setup->flatshade_first;
boolean uses_constant_interp;
unsigned i;
assert(setup->setup.variant);
@@ -152,6 +176,8 @@ lp_setup_draw_elements(struct vbuf_render *vbr, const ushort *indices, uint nr)
if (!lp_setup_update_state(setup, TRUE))
return;
uses_constant_interp = setup->setup.variant->key.uses_constant_interp;
switch (setup->prim) {
case PIPE_PRIM_POINTS:
for (i = 0; i < nr; i++) {
@@ -190,11 +216,24 @@ lp_setup_draw_elements(struct vbuf_render *vbr, const ushort *indices, uint nr)
break;
case PIPE_PRIM_TRIANGLES:
for (i = 2; i < nr; i += 3) {
setup->triangle( setup,
if (nr % 6 == 0 && !uses_constant_interp) {
for (i = 5; i < nr; i += 6) {
rect( setup,
get_vert(vertex_buffer, indices[i-5], stride),
get_vert(vertex_buffer, indices[i-4], stride),
get_vert(vertex_buffer, indices[i-3], stride),
get_vert(vertex_buffer, indices[i-2], stride),
get_vert(vertex_buffer, indices[i-1], stride),
get_vert(vertex_buffer, indices[i-0], stride) );
}
}
else {
for (i = 2; i < nr; i += 3) {
setup->triangle( setup,
get_vert(vertex_buffer, indices[i-2], stride),
get_vert(vertex_buffer, indices[i-1], stride),
get_vert(vertex_buffer, indices[i-0], stride) );
}
}
break;
@@ -345,11 +384,14 @@ lp_setup_draw_arrays(struct vbuf_render *vbr, uint start, uint nr)
const void *vertex_buffer =
(void *) get_vert(setup->vertex_buffer, start, stride);
const boolean flatshade_first = setup->flatshade_first;
boolean uses_constant_interp;
unsigned i;
if (!lp_setup_update_state(setup, TRUE))
return;
uses_constant_interp = setup->setup.variant->key.uses_constant_interp;
switch (setup->prim) {
case PIPE_PRIM_POINTS:
for (i = 0; i < nr; i++) {
@@ -388,22 +430,74 @@ lp_setup_draw_arrays(struct vbuf_render *vbr, uint start, uint nr)
break;
case PIPE_PRIM_TRIANGLES:
for (i = 2; i < nr; i += 3) {
setup->triangle( setup,
if (nr % 6 == 0 && !uses_constant_interp) {
for (i = 5; i < nr; i += 6) {
rect( setup,
get_vert(vertex_buffer, i-5, stride),
get_vert(vertex_buffer, i-4, stride),
get_vert(vertex_buffer, i-3, stride),
get_vert(vertex_buffer, i-2, stride),
get_vert(vertex_buffer, i-1, stride),
get_vert(vertex_buffer, i-0, stride) );
}
}
else if (!uses_constant_interp &&
lp_setup_analyse_triangles(setup, vertex_buffer, stride, nr)) {
/* If lp_setup_analyse_triangles() returned true, it also
* emitted (setup) the rect or triangles.
*/
}
else {
for (i = 2; i < nr; i += 3) {
setup->triangle( setup,
get_vert(vertex_buffer, i-2, stride),
get_vert(vertex_buffer, i-1, stride),
get_vert(vertex_buffer, i-0, stride) );
}
}
break;
case PIPE_PRIM_TRIANGLE_STRIP:
if (flatshade_first) {
for (i = 2; i < nr; i++) {
/* emit first triangle vertex as first triangle vertex */
setup->triangle( setup,
get_vert(vertex_buffer, i-2, stride),
get_vert(vertex_buffer, i+(i&1)-1, stride),
get_vert(vertex_buffer, i-(i&1), stride) );
if (!uses_constant_interp) {
int j;
i = 2;
j = 3;
while (j < nr) {
/* emit first triangle vertex as first triangle vertex */
const float (*v0)[4] = get_vert(vertex_buffer, i-2, stride);
const float (*v1)[4] = get_vert(vertex_buffer, i+(i&1)-1, stride);
const float (*v2)[4] = get_vert(vertex_buffer, i-(i&1), stride);
const float (*v3)[4] = get_vert(vertex_buffer, j-2, stride);
const float (*v4)[4] = get_vert(vertex_buffer, j+(j&1)-1, stride);
const float (*v5)[4] = get_vert(vertex_buffer, j-(j&1), stride);
if (setup->permit_linear_rasterizer &&
setup->rect(setup, v0, v1, v2, v3, v4, v5)) {
i += 2;
j += 2;
} else {
/* emit one triangle, and retry rectangle in the next one */
setup->triangle(setup, v0, v1, v2);
i += 1;
j += 1;
}
}
if (i < nr) {
/* emit last triangle */
setup->triangle( setup,
get_vert(vertex_buffer, i-2, stride),
get_vert(vertex_buffer, i+(i&1)-1, stride),
get_vert(vertex_buffer, i-(i&1), stride) );
}
}
else {
for (i = 2; i < nr; i++) {
/* emit first triangle vertex as first triangle vertex */
setup->triangle( setup,
get_vert(vertex_buffer, i-2, stride),
get_vert(vertex_buffer, i+(i&1)-1, stride),
get_vert(vertex_buffer, i-(i&1), stride) );
}
}
}
else {
@@ -418,7 +512,16 @@ lp_setup_draw_arrays(struct vbuf_render *vbr, uint start, uint nr)
break;
case PIPE_PRIM_TRIANGLE_FAN:
if (flatshade_first) {
if (nr == 4 && !uses_constant_interp) {
rect( setup,
get_vert(vertex_buffer, 0, stride),
get_vert(vertex_buffer, 1, stride),
get_vert(vertex_buffer, 2, stride),
get_vert(vertex_buffer, 0, stride),
get_vert(vertex_buffer, 2, stride),
get_vert(vertex_buffer, 3, stride) );
}
else if (flatshade_first) {
for (i = 2; i < nr; i += 1) {
/* emit first non-spoke vertex as first vertex */
setup->triangle( setup,
@@ -455,15 +558,28 @@ lp_setup_draw_arrays(struct vbuf_render *vbr, uint start, uint nr)
}
else {
/* emit last quad vertex as last triangle vertex */
for (i = 3; i < nr; i += 4) {
setup->triangle( setup,
if (!uses_constant_interp) {
for (i = 3; i < nr; i += 4) {
rect( setup,
get_vert(vertex_buffer, i-3, stride),
get_vert(vertex_buffer, i-2, stride),
get_vert(vertex_buffer, i-0, stride) );
setup->triangle( setup,
get_vert(vertex_buffer, i-2, stride),
get_vert(vertex_buffer, i-1, stride),
get_vert(vertex_buffer, i-3, stride),
get_vert(vertex_buffer, i-1, stride),
get_vert(vertex_buffer, i-0, stride) );
}
}
else {
for (i = 3; i < nr; i += 4) {
setup->triangle( setup,
get_vert(vertex_buffer, i-3, stride),
get_vert(vertex_buffer, i-2, stride),
get_vert(vertex_buffer, i-0, stride) );
setup->triangle( setup,
get_vert(vertex_buffer, i-2, stride),
get_vert(vertex_buffer, i-1, stride),
get_vert(vertex_buffer, i-0, stride) );
}
}
}
break;
+3
View File
@@ -116,6 +116,9 @@ llvmpipe_update_fs(struct llvmpipe_context *lp);
void
llvmpipe_update_setup(struct llvmpipe_context *lp);
void
llvmpipe_update_derived_clear(struct llvmpipe_context *llvmpipe);
void
llvmpipe_update_derived(struct llvmpipe_context *llvmpipe);
@@ -177,6 +177,8 @@ llvmpipe_set_sample_mask(struct pipe_context *pipe,
struct llvmpipe_context *llvmpipe = llvmpipe_context(pipe);
if (sample_mask != llvmpipe->sample_mask) {
draw_flush(llvmpipe->draw);
llvmpipe->sample_mask = sample_mask;
llvmpipe->dirty |= LP_NEW_SAMPLE_MASK;
@@ -173,6 +173,73 @@ compute_vertex_info(struct llvmpipe_context *llvmpipe)
}
static void
check_linear_rasterizer( struct llvmpipe_context *lp )
{
boolean bgr8;
boolean permit_linear;
boolean single_vp;
boolean clipping_changed = FALSE;
bgr8 = (lp->framebuffer.nr_cbufs == 1 &&
lp->framebuffer.cbufs[0]->texture->target == PIPE_TEXTURE_2D &&
(lp->framebuffer.cbufs[0]->format == PIPE_FORMAT_B8G8R8A8_UNORM ||
lp->framebuffer.cbufs[0]->format == PIPE_FORMAT_B8G8R8X8_UNORM));
/* permit_linear means guardband, hence fake scissor, which we can only
* handle if there's just one vp. */
single_vp = lp->viewport_index_slot < 0;
permit_linear = (!lp->framebuffer.zsbuf &&
bgr8 &&
single_vp);
/* Tell draw that we're happy doing our own x/y clipping.
*/
if (lp->permit_linear_rasterizer != permit_linear) {
lp->permit_linear_rasterizer = permit_linear;
lp_setup_set_linear_mode(lp->setup, permit_linear);
clipping_changed = TRUE;
}
if (lp->single_vp != single_vp) {
lp->single_vp = single_vp;
clipping_changed = TRUE;
}
/* Disable xy clipping in linear mode.
*
* Use a guard band if we don't have zsbuf. Could enable
* guardband always - this just to be conservative.
*
* Because we have a layering violation where the draw module emits
* state changes to the driver while we're already inside a draw
* call, need to be careful about when we make calls back to the
* draw module. Hence the clipping_changed flag which is as much
* to prevent flush recursion as it is to short-circuit noop state
* changes.
*/
if (clipping_changed) {
draw_set_driver_clipping(lp->draw,
FALSE,
FALSE,
permit_linear,
single_vp);
}
}
/**
* Handle state changes before clears.
* Called just prior to clearing (pipe::clear()).
*/
void llvmpipe_update_derived_clear( struct llvmpipe_context *llvmpipe )
{
if (llvmpipe->dirty & (LP_NEW_FS |
LP_NEW_FRAMEBUFFER))
check_linear_rasterizer(llvmpipe);
}
/**
* Handle state changes.
* Called just prior to drawing anything (pipe::draw_arrays(), etc).
@@ -293,6 +360,8 @@ void llvmpipe_update_derived( struct llvmpipe_context *llvmpipe )
llvmpipe->viewports);
}
llvmpipe_update_derived_clear(llvmpipe);
llvmpipe->dirty = 0;
}
+150 -7
View File
@@ -589,7 +589,7 @@ generate_fs_loop(struct gallivm_state *gallivm,
LLVMValueRef stencil_refs[2];
LLVMValueRef outputs[PIPE_MAX_SHADER_OUTPUTS][TGSI_NUM_CHANNELS];
LLVMValueRef zs_samples = lp_build_const_int32(gallivm, key->zsbuf_nr_samples);
struct lp_build_for_loop_state loop_state, sample_loop_state;
struct lp_build_for_loop_state loop_state, sample_loop_state = {0};
struct lp_build_mask_context mask;
/*
* TODO: figure out if simple_shader optimization is really worthwile to
@@ -3441,6 +3441,24 @@ dump_fs_variant_key(struct lp_fragment_shader_variant_key *key)
}
}
const char *
lp_debug_fs_kind(enum lp_fs_kind kind)
{
switch(kind) {
case LP_FS_KIND_GENERAL:
return "GENERAL";
case LP_FS_KIND_BLIT_RGBA:
return "BLIT_RGBA";
case LP_FS_KIND_BLIT_RGB1:
return "BLIT_RGB1";
case LP_FS_KIND_AERO_MINIFICATION:
return "AERO_MINIFICATION";
case LP_FS_KIND_LLVM_LINEAR:
return "LLVM_LINEAR";
default:
return "unknown";
}
}
void
lp_debug_fs_variant(struct lp_fragment_shader_variant *variant)
@@ -3453,6 +3471,9 @@ lp_debug_fs_variant(struct lp_fragment_shader_variant *variant)
nir_print_shader(variant->shader->base.ir.nir, stderr);
dump_fs_variant_key(&variant->key);
debug_printf("variant->opaque = %u\n", variant->opaque);
debug_printf("variant->potentially_opaque = %u\n", variant->potentially_opaque);
debug_printf("variant->blit = %u\n", variant->blit);
debug_printf("shader->kind = %s\n", lp_debug_fs_kind(variant->shader->kind));
debug_printf("\n");
}
@@ -3491,6 +3512,8 @@ generate_variant(struct llvmpipe_context *lp,
struct lp_fragment_shader_variant *variant;
const struct util_format_description *cbuf0_format_desc = NULL;
boolean fullcolormask;
boolean no_kill;
boolean linear;
char module_name[64];
unsigned char ir_sha1_cache_key[20];
struct lp_cached_code cached = { 0 };
@@ -3536,9 +3559,9 @@ generate_variant(struct llvmpipe_context *lp,
fullcolormask = util_format_colormask_full(cbuf0_format_desc, key->blend.rt[0].colormask);
}
variant->opaque =
!key->blend.logicop_enable &&
!key->blend.rt[0].blend_enable &&
/* The scissor is ignored here as only tiles inside the scissoring
* rectangle will refer to this */
no_kill =
fullcolormask &&
!key->stencil[0].enabled &&
!key->alpha.enabled &&
@@ -3546,13 +3569,82 @@ generate_variant(struct llvmpipe_context *lp,
!key->blend.alpha_to_coverage &&
!key->depth.enabled &&
!shader->info.base.uses_kill &&
!shader->info.base.writes_samplemask
? TRUE : FALSE;
!shader->info.base.writes_samplemask;
variant->opaque =
no_kill &&
!key->blend.logicop_enable &&
!key->blend.rt[0].blend_enable
? TRUE : FALSE;
variant->potentially_opaque =
no_kill &&
!key->blend.logicop_enable &&
key->blend.rt[0].blend_enable &&
key->blend.rt[0].rgb_func == PIPE_BLEND_ADD &&
key->blend.rt[0].rgb_dst_factor == PIPE_BLENDFACTOR_INV_SRC_ALPHA &&
key->blend.rt[0].alpha_func == key->blend.rt[0].rgb_func &&
key->blend.rt[0].alpha_dst_factor == key->blend.rt[0].rgb_dst_factor &&
shader->base.type == PIPE_SHADER_IR_TGSI &&
/*
* FIXME: for NIR, all of the fields of info.xxx (except info.base)
* are zeros, hence shader analysis (here and elsewhere) using these
* bits cannot work and will silently fail (cbuf is the only pointer
* field, hence causing a crash).
*/
shader->info.cbuf[0][3].file != TGSI_FILE_NULL
? TRUE : FALSE;
/* We only care about opaque blits for now */
if (variant->opaque &&
(shader->kind == LP_FS_KIND_BLIT_RGBA ||
shader->kind == LP_FS_KIND_BLIT_RGB1)) {
unsigned target, min_img_filter, mag_img_filter, min_mip_filter;
enum pipe_format texture_format;
texture_format = key->samplers[0].texture_state.format;
target = key->samplers[0].texture_state.target;
min_img_filter = key->samplers[0].sampler_state.min_img_filter;
mag_img_filter = key->samplers[0].sampler_state.mag_img_filter;
if (key->samplers[0].texture_state.level_zero_only) {
min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
} else {
min_mip_filter = key->samplers[0].sampler_state.min_mip_filter;
}
if (target == PIPE_TEXTURE_2D &&
min_img_filter == PIPE_TEX_FILTER_NEAREST &&
mag_img_filter == PIPE_TEX_FILTER_NEAREST &&
min_mip_filter == PIPE_TEX_MIPFILTER_NONE &&
((texture_format &&
util_is_format_compatible(util_format_description(texture_format),
cbuf0_format_desc)) ||
(shader->kind == LP_FS_KIND_BLIT_RGB1 &&
(texture_format == PIPE_FORMAT_B8G8R8A8_UNORM ||
texture_format == PIPE_FORMAT_B8G8R8X8_UNORM) &&
(key->cbuf_format[0] == PIPE_FORMAT_B8G8R8A8_UNORM ||
key->cbuf_format[0] == PIPE_FORMAT_B8G8R8X8_UNORM))))
variant->blit = 1;
}
/* Whether this is a candidate for the linear path */
linear =
!key->stencil[0].enabled &&
!key->depth.enabled &&
!shader->info.base.uses_kill &&
!key->blend.logicop_enable &&
(key->cbuf_format[0] == PIPE_FORMAT_B8G8R8A8_UNORM ||
key->cbuf_format[0] == PIPE_FORMAT_B8G8R8X8_UNORM);
memcpy(&variant->key, key, sizeof *key);
if ((LP_DEBUG & DEBUG_FS) || (gallivm_debug & GALLIVM_DEBUG_IR)) {
lp_debug_fs_variant(variant);
}
llvmpipe_fs_variant_fastpath(variant);
lp_jit_init_types(variant);
if (variant->jit_function[RAST_EDGE_TEST] == NULL)
@@ -3565,6 +3657,36 @@ generate_variant(struct llvmpipe_context *lp,
}
}
if (linear) {
/* Currently keeping both the old fastpaths and new linear path
* active. The older code is still somewhat faster for the cases
* it covers.
*
* XXX: consider restricting this to aero-mode only.
*/
if (fullcolormask &&
!key->alpha.enabled &&
!key->blend.alpha_to_coverage) {
llvmpipe_fs_variant_linear_fastpath(variant);
}
/* If the original fastpath doesn't cover this variant, try the new
* code:
*/
if (variant->jit_linear == NULL) {
if (shader->kind == LP_FS_KIND_BLIT_RGBA ||
shader->kind == LP_FS_KIND_BLIT_RGB1 ||
shader->kind == LP_FS_KIND_LLVM_LINEAR) {
llvmpipe_fs_variant_linear_llvm(lp, shader, variant);
}
}
} else {
if (LP_DEBUG & DEBUG_LINEAR) {
lp_debug_fs_variant(variant);
debug_printf(" ----> no linear path for this variant\n");
}
}
/*
* Compile everything
*/
@@ -3587,6 +3709,19 @@ generate_variant(struct llvmpipe_context *lp,
variant->jit_function[RAST_WHOLE] = variant->jit_function[RAST_EDGE_TEST];
}
if (linear) {
if (variant->linear_function) {
variant->jit_linear_llvm = (lp_jit_linear_llvm_func)
gallivm_jit_function(variant->gallivm, variant->linear_function);
}
/*
* This must be done after LLVM compilation, as it will call the JIT'ed
* code to determine active inputs.
*/
lp_linear_check_variant(variant);
}
if (needs_caching) {
lp_disk_cache_insert_shader(screen, &cached, ir_sha1_cache_key);
}
@@ -3697,6 +3832,9 @@ llvmpipe_create_fs_state(struct pipe_context *pipe,
debug_printf("\n");
}
/* This will put a derived copy of the tokens into shader->base.tokens */
llvmpipe_fs_analyse(shader, templ->tokens);
return shader;
}
@@ -4155,11 +4293,16 @@ make_variant_key(struct llvmpipe_context *lp,
&lp->images[PIPE_SHADER_FRAGMENT][i]);
}
}
if (shader->kind == LP_FS_KIND_AERO_MINIFICATION) {
key->samplers[0].sampler_state.min_img_filter = PIPE_TEX_FILTER_NEAREST;
key->samplers[0].sampler_state.mag_img_filter = PIPE_TEX_FILTER_NEAREST;
}
return key;
}
/**
* Update fragment shader state. This is called just prior to drawing
* something when some fragment-related state has changed.
@@ -48,6 +48,16 @@ struct lp_fragment_shader;
#define RAST_EDGE_TEST 1
enum lp_fs_kind
{
LP_FS_KIND_GENERAL = 0,
LP_FS_KIND_BLIT_RGBA,
LP_FS_KIND_BLIT_RGB1,
LP_FS_KIND_AERO_MINIFICATION,
LP_FS_KIND_LLVM_LINEAR
};
struct lp_sampler_static_state
{
/*
@@ -137,6 +147,13 @@ struct lp_fs_variant_list_item
struct lp_fragment_shader_variant
{
/*
* Whether some primitives can be opaque.
*/
unsigned potentially_opaque:1;
unsigned blit:1;
unsigned linear_input_mask:16;
struct pipe_reference reference;
boolean opaque;
@@ -150,6 +167,17 @@ struct lp_fragment_shader_variant
lp_jit_frag_func jit_function[2];
lp_jit_linear_func jit_linear;
lp_jit_linear_func jit_linear_blit;
/* Functions within the linear path:
*/
LLVMValueRef linear_function;
lp_jit_linear_llvm_func jit_linear_llvm;
/* Bitmask to say what cbufs are unswizzled */
unsigned unswizzled_cbufs;
/* Total number of LLVM instructions generated */
unsigned nr_instrs;
@@ -172,6 +200,13 @@ struct lp_fragment_shader
struct pipe_reference reference;
struct lp_tgsi_info info;
/*
* Analysis results
*/
enum lp_fs_kind kind;
struct lp_fs_variant_list_item variants;
struct draw_fragment_shader *draw_data;
@@ -187,9 +222,31 @@ struct lp_fragment_shader
};
void
llvmpipe_fs_analyse(struct lp_fragment_shader *shader,
const struct tgsi_token *tokens);
void
llvmpipe_fs_variant_fastpath(struct lp_fragment_shader_variant *variant);
void
llvmpipe_fs_variant_linear_fastpath(struct lp_fragment_shader_variant *variant);
void
llvmpipe_fs_variant_linear_llvm(struct llvmpipe_context *lp,
struct lp_fragment_shader *shader,
struct lp_fragment_shader_variant *variant);
void
lp_debug_fs_variant(struct lp_fragment_shader_variant *variant);
const char *
lp_debug_fs_kind(enum lp_fs_kind kind);
void
lp_linear_check_variant(struct lp_fragment_shader_variant *variant);
void
llvmpipe_destroy_fs(struct llvmpipe_context *llvmpipe,
struct lp_fragment_shader *shader);
@@ -0,0 +1,204 @@
/**************************************************************************
*
* Copyright 2010-2021 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 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
* THE COPYRIGHT HOLDERS, AUTHORS 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.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
**************************************************************************/
#include "util/u_memory.h"
#include "util/u_math.h"
#include "tgsi/tgsi_parse.h"
#include "tgsi/tgsi_text.h"
#include "tgsi/tgsi_util.h"
#include "tgsi/tgsi_dump.h"
#include "lp_debug.h"
#include "lp_state.h"
/*
* Detect Aero minification shaders.
*
* Aero does not use texture mimaps when a window gets animated and its shaped
* bended. Instead it uses the average of 4 nearby texels. This is the simplest
* of such shader, but there are several variations:
*
* FRAG
* DCL IN[0], GENERIC[1], PERSPECTIVE
* DCL IN[1], GENERIC[2], PERSPECTIVE
* DCL IN[2], GENERIC[3], PERSPECTIVE
* DCL OUT[0], COLOR
* DCL SAMP[0]
* DCL TEMP[0..3]
* IMM FLT32 { 0.2500, 0.0000, 0.0000, 0.0000 }
* MOV TEMP[0].x, IN[0].zzzz
* MOV TEMP[0].y, IN[0].wwww
* MOV TEMP[1].x, IN[1].zzzz
* MOV TEMP[1].y, IN[1].wwww
* TEX TEMP[0], TEMP[0], SAMP[0], 2D
* TEX TEMP[2], IN[0], SAMP[0], 2D
* TEX TEMP[3], IN[1], SAMP[0], 2D
* TEX TEMP[1], TEMP[1], SAMP[0], 2D
* ADD TEMP[0], TEMP[0], TEMP[2]
* ADD TEMP[0], TEMP[3], TEMP[0]
* ADD TEMP[0], TEMP[1], TEMP[0]
* MUL TEMP[0], TEMP[0], IN[2]
* MUL TEMP[0], TEMP[0], IMM[0].xxxx
* MOV OUT[0], TEMP[0]
* END
*
* Texture coordinates are interleaved like the Gaussian blur shaders, but
* unlike the later there isn't structure in the sub-pixel positioning of the
* texels, other than being disposed in a diamond-like shape. For example,
* these are the relative offsets of the texels relative to the average:
*
* x offset y offset
* --------------------
* 0.691834 -0.21360
* -0.230230 -0.64160
* -0.692406 0.21356
* 0.230802 0.64160
*
* These shaders are typically used with linear min/mag filtering, but the
* linear filtering provides very little visual improvement compared to the
* performance impact it has. The ultimate purpose of detecting these shaders
* is to override with nearest texture filtering.
*/
static inline boolean
match_aero_minification_shader(const struct tgsi_token *tokens,
const struct lp_tgsi_info *info)
{
struct tgsi_parse_context parse;
unsigned coord_mask;
boolean has_quarter_imm;
unsigned index, chan;
if ((info->base.opcode_count[TGSI_OPCODE_TEX] != 4 &&
info->base.opcode_count[TGSI_OPCODE_SAMPLE] != 4) ||
info->num_texs != 4) {
return FALSE;
}
/*
* Ensure the texture coordinates are interleaved as in the example above.
*/
coord_mask = 0;
for (index = 0; index < 4; ++index) {
const struct lp_tgsi_texture_info *tex = &info->tex[index];
if (tex->sampler_unit != 0 ||
tex->texture_unit != 0 ||
tex->coord[0].file != TGSI_FILE_INPUT ||
tex->coord[1].file != TGSI_FILE_INPUT ||
tex->coord[0].u.index != tex->coord[1].u.index ||
(tex->coord[0].swizzle % 2) != 0 ||
tex->coord[1].swizzle != tex->coord[0].swizzle + 1) {
return FALSE;
}
coord_mask |= 1 << (tex->coord[0].u.index*2 + tex->coord[0].swizzle/2);
}
if (coord_mask != 0xf) {
return FALSE;
}
/*
* Ensure it has the 0.25 immediate.
*/
has_quarter_imm = FALSE;
tgsi_parse_init(&parse, tokens);
while (!tgsi_parse_end_of_tokens(&parse)) {
tgsi_parse_token(&parse);
switch (parse.FullToken.Token.Type) {
case TGSI_TOKEN_TYPE_DECLARATION:
break;
case TGSI_TOKEN_TYPE_INSTRUCTION:
goto finished;
case TGSI_TOKEN_TYPE_IMMEDIATE:
{
const unsigned size =
parse.FullToken.FullImmediate.Immediate.NrTokens - 1;
assert(size <= 4);
for (chan = 0; chan < size; ++chan) {
if (parse.FullToken.FullImmediate.u[chan].Float == 0.25f) {
has_quarter_imm = TRUE;
goto finished;
}
}
}
break;
case TGSI_TOKEN_TYPE_PROPERTY:
break;
default:
assert(0);
goto finished;
}
}
finished:
tgsi_parse_free(&parse);
if (!has_quarter_imm) {
return FALSE;
}
return TRUE;
}
void
llvmpipe_fs_analyse(struct lp_fragment_shader *shader,
const struct tgsi_token *tokens)
{
shader->kind = LP_FS_KIND_GENERAL;
if (shader->kind == LP_FS_KIND_GENERAL &&
shader->info.base.num_inputs <= LP_MAX_LINEAR_INPUTS &&
shader->info.base.num_outputs == 1 &&
!shader->info.indirect_textures &&
!shader->info.sampler_texture_units_different &&
!shader->info.unclamped_immediates &&
shader->info.num_texs <= LP_MAX_LINEAR_TEXTURES &&
(shader->info.base.opcode_count[TGSI_OPCODE_TEX] +
shader->info.base.opcode_count[TGSI_OPCODE_SAMPLE] +
shader->info.base.opcode_count[TGSI_OPCODE_MOV] +
shader->info.base.opcode_count[TGSI_OPCODE_MUL] +
shader->info.base.opcode_count[TGSI_OPCODE_RET] +
shader->info.base.opcode_count[TGSI_OPCODE_END] ==
shader->info.base.num_instructions)) {
shader->kind = LP_FS_KIND_LLVM_LINEAR;
}
if (shader->kind == LP_FS_KIND_GENERAL &&
match_aero_minification_shader(tokens, &shader->info)) {
shader->kind = LP_FS_KIND_AERO_MINIFICATION;
}
}
@@ -0,0 +1,238 @@
/**************************************************************************
*
* Copyright 2010-2021 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 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
* THE COPYRIGHT HOLDERS, AUTHORS 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.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
**************************************************************************/
#include "pipe/p_config.h"
#include "util/u_math.h"
#include "util/u_cpu_detect.h"
#include "util/u_sse.h"
#include "lp_jit.h"
#include "lp_state_fs.h"
#include "lp_debug.h"
#if defined(PIPE_ARCH_SSE)
#include <emmintrin.h>
static void
no_op(const struct lp_jit_context *context,
uint32_t x,
uint32_t y,
uint32_t facing,
const void *a0,
const void *dadx,
const void *dady,
uint8_t **cbufs,
uint8_t *depth,
uint64_t mask,
struct lp_jit_thread_data *thread_data,
unsigned *strides,
unsigned depth_stride,
unsigned *color_sample_stride,
unsigned depth_sample_stride)
{
}
/*
* m ? a : b
*/
static inline __m128i
mm_select_si128(__m128i m, __m128i a, __m128i b)
{
__m128i res;
/*
* TODO: use PBLENVB when available.
*/
res = _mm_or_si128(_mm_and_si128(m, a),
_mm_andnot_si128(m, b));
return res;
}
/*
* *p = m ? a : *p;
*/
static inline void
mm_store_mask_si128(__m128i *p, __m128i m, __m128i a)
{
_mm_store_si128(p, mm_select_si128(m, a, _mm_load_si128(p)));
}
/**
* Expand the mask from a 16 bit integer to a 4 x 4 x 32 bit vector mask, ie.
* 1 bit -> 32bits.
*/
static inline void
expand_mask(uint32_t int_mask,
__m128i *vec_mask)
{
__m128i inv_mask = _mm_set1_epi32(~int_mask & 0xffff);
__m128i zero = _mm_setzero_si128();
vec_mask[0] = _mm_and_si128(inv_mask, _mm_setr_epi32(0x0001, 0x0002, 0x0004, 0x0008));
vec_mask[1] = _mm_and_si128(inv_mask, _mm_setr_epi32(0x0010, 0x0020, 0x0040, 0x0080));
inv_mask = _mm_srli_epi32(inv_mask, 8);
vec_mask[2] = _mm_and_si128(inv_mask, _mm_setr_epi32(0x0001, 0x0002, 0x0004, 0x0008));
vec_mask[3] = _mm_and_si128(inv_mask, _mm_setr_epi32(0x0010, 0x0020, 0x0040, 0x0080));
vec_mask[0] = _mm_cmpeq_epi32(vec_mask[0], zero);
vec_mask[1] = _mm_cmpeq_epi32(vec_mask[1], zero);
vec_mask[2] = _mm_cmpeq_epi32(vec_mask[2], zero);
vec_mask[3] = _mm_cmpeq_epi32(vec_mask[3], zero);
}
/**
* Draw opaque color (for debugging).
*/
static void
opaque_color(uint8_t **cbufs, unsigned *strides,
uint32_t int_mask,
uint32_t color)
{
__m128i *cbuf = (__m128i *)cbufs[0];
unsigned stride = strides[0] / sizeof *cbuf;
__m128i vec_mask[4];
__m128i vec_color = _mm_set1_epi32(color);
expand_mask(int_mask, vec_mask);
mm_store_mask_si128(cbuf, vec_mask[0], vec_color); cbuf += stride;
mm_store_mask_si128(cbuf, vec_mask[1], vec_color); cbuf += stride;
mm_store_mask_si128(cbuf, vec_mask[2], vec_color); cbuf += stride;
mm_store_mask_si128(cbuf, vec_mask[3], vec_color);
}
/**
* Draw opaque red (for debugging).
*/
static void
red(const struct lp_jit_context *context,
uint32_t x,
uint32_t y,
uint32_t facing,
const void *a0,
const void *dadx,
const void *dady,
uint8_t **cbufs,
uint8_t *depth,
uint64_t int_mask,
struct lp_jit_thread_data *thread_data,
unsigned *strides,
unsigned depth_stride,
unsigned *sample_stride,
unsigned depth_sample_stride)
{
opaque_color(cbufs, strides, int_mask, 0xffff0000);
(void)facing;
(void)depth;
(void)thread_data;
}
/**
* Draw opaque green (for debugging).
*/
static void
green(const struct lp_jit_context *context,
uint32_t x,
uint32_t y,
uint32_t facing,
const void *a0,
const void *dadx,
const void *dady,
uint8_t **cbufs,
uint8_t *depth,
uint64_t int_mask,
struct lp_jit_thread_data *thread_data,
unsigned *strides,
unsigned depth_stride,
unsigned *sample_stride,
unsigned depth_sample_stride)
{
opaque_color(cbufs, strides, int_mask, 0xff00ff00);
(void)facing;
(void)depth;
(void)thread_data;
}
void
llvmpipe_fs_variant_fastpath(struct lp_fragment_shader_variant *variant)
{
variant->jit_function[RAST_WHOLE] = NULL;
variant->jit_function[RAST_EDGE_TEST] = NULL;
if (LP_DEBUG & DEBUG_NO_FASTPATH)
return;
if (variant->key.cbuf_format[0] != PIPE_FORMAT_B8G8R8A8_UNORM &&
variant->key.cbuf_format[0] != PIPE_FORMAT_B8G8R8X8_UNORM) {
return;
}
if (0) {
variant->jit_function[RAST_WHOLE] = red;
variant->jit_function[RAST_EDGE_TEST] = red;
}
if (0) {
variant->jit_function[RAST_WHOLE] = green;
variant->jit_function[RAST_EDGE_TEST] = green;
}
if (0) {
variant->jit_function[RAST_WHOLE] = no_op;
variant->jit_function[RAST_EDGE_TEST] = no_op;
}
/* Make it easier to see triangles:
*/
if ((LP_DEBUG & DEBUG_LINEAR) || (LP_PERF & PERF_NO_SHADE)) {
variant->jit_function[RAST_EDGE_TEST] = red;
variant->jit_function[RAST_WHOLE] = green;
}
}
#else
void
llvmpipe_fs_variant_fastpath(struct lp_fragment_shader_variant *variant)
{
}
#endif
@@ -0,0 +1,713 @@
/**************************************************************************
*
* Copyright 2010-2021 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 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
* THE COPYRIGHT HOLDERS, AUTHORS 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.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
**************************************************************************/
#include "pipe/p_config.h"
#include "util/u_math.h"
#include "util/u_cpu_detect.h"
#include "util/u_pack_color.h"
#include "util/u_surface.h"
#include "util/u_sse.h"
#include "lp_jit.h"
#include "lp_rast.h"
#include "lp_debug.h"
#include "lp_state_fs.h"
#include "lp_linear_priv.h"
#if defined(PIPE_ARCH_SSE)
#include <emmintrin.h>
struct nearest_sampler {
PIPE_ALIGN_VAR(16) uint32_t out[64];
const struct lp_jit_texture *texture;
float fsrc_x; /* src_x0 */
float fsrc_y; /* src_y0 */
float fdsdx; /* sx */
float fdsdy; /* sx */
float fdtdx; /* sy */
float fdtdy; /* sy */
int width;
int y;
const uint32_t *(*fetch)(struct nearest_sampler *samp);
};
struct linear_interp {
PIPE_ALIGN_VAR(16) uint32_t out[64];
__m128i a0;
__m128i dadx;
__m128i dady;
int width; /* rounded up to multiple of 4 */
boolean is_constant;
};
/* Organize all the information needed for blending in one place.
* Could have blend function pointer here, but we currently always
* know which one we want to call.
*/
struct color_blend {
const uint32_t *src;
uint8_t *color;
int stride;
int width; /* the exact width */
};
/* Organize all the information needed for running each of the shaders
* in one place.
*/
struct shader {
PIPE_ALIGN_VAR(16) uint32_t out0[64];
const uint32_t *src0;
const uint32_t *src1;
__m128i const0;
int width; /* rounded up to multiple of 4 */
};
/* For a row of pixels, perform add/one/inv_src_alpha (ie
* premultiplied alpha) blending between the incoming pixels and the
* destination buffer.
*
* Used to implement the BLIT_RGBA + blend shader, there are no
* operations from the pixel shader left to implement at this level -
* effectively the pixel shader was just a texture fetch which has
* already been performed. This routine then purely implements
* blending.
*/
static void
blend_premul(struct color_blend *blend)
{
const uint32_t *src = blend->src; /* aligned */
uint32_t *dst = (uint32_t *)blend->color; /* unaligned */
int width = blend->width;
int i;
__m128i tmp;
union { __m128i m128; uint ui[4]; } dstreg;
blend->color += blend->stride;
for (i = 0; i + 3 < width; i += 4) {
tmp = _mm_loadu_si128((const __m128i *)&dst[i]); /* UNALIGNED READ */
dstreg.m128 = util_sse2_blend_premul_4(*(const __m128i *)&src[i],
tmp);
_mm_storeu_si128((__m128i *)&dst[i], dstreg.m128); /* UNALIGNED WRITE */
}
if (i < width) {
int j;
for (j = 0; j < width - i ; j++) {
dstreg.ui[j] = dst[i+j];
}
dstreg.m128 = util_sse2_blend_premul_4(*(const __m128i *)&src[i],
dstreg.m128);
for (; i < width; i++)
dst[i] = dstreg.ui[i&3];
}
}
static void
blend_noop(struct color_blend *blend)
{
memcpy(blend->color, blend->src, blend->width * sizeof(unsigned));
blend->color += blend->stride;
}
static void
init_blend(struct color_blend *blend,
int x, int y, int width, int height,
uint8_t *color,
int stride)
{
blend->color = color + x * 4 + y * stride;
blend->stride = stride;
blend->width = width;
}
/*
* Perform nearest filtered lookup of a row of texels. Texture lookup
* is assumed to be axis aligned but with arbitrary scaling.
*
* Texture coordinate interpolation is performed in 24.8 fixed point.
* Note that the longest span we will encounter is 64 pixels long,
* meaning that 8 fractional bits is more than sufficient to represent
* the shallowest gradient possible within this span.
*
* After 64 pixels (ie. in the next tile), the starting point will be
* recalculated with floating point arithmetic.
*
* XXX: migrate this to use Jose's quad blitter texture fetch routines.
*/
static const uint32_t *
fetch_row(struct nearest_sampler *samp)
{
int y = samp->y++;
uint32_t *row = samp->out;
const struct lp_jit_texture *texture = samp->texture;
int yy = util_iround(samp->fsrc_y + samp->fdtdy * y);
const uint32_t *src_row =
(const uint32_t *)((const uint8_t *)texture->base +
yy * texture->row_stride[0]);
int iscale_x = samp->fdsdx * 256;
int acc = samp->fsrc_x * 256 + 128;
int width = samp->width;
int i;
for (i = 0; i < width; i++) {
row[i] = src_row[acc>>8];
acc += iscale_x;
}
return row;
}
/* Version of fetch_row which can cope with texture edges. In
* practise, aero never triggers this.
*/
static const uint32_t *
fetch_row_clamped(struct nearest_sampler *samp)
{
int y = samp->y++;
uint32_t *row = samp->out;
const struct lp_jit_texture *texture = samp->texture;
int yy = util_iround(samp->fsrc_y + samp->fdtdy * y);
const uint32_t *src_row =
(const uint32_t *)((const uint8_t *)texture->base +
CLAMP(yy, 0, texture->height-1) *
texture->row_stride[0]);
float src_x0 = samp->fsrc_x;
float scale_x = samp->fdsdx;
int width = samp->width;
int i;
for (i = 0; i < width; i++) {
row[i] = src_row[CLAMP(util_iround(src_x0 + i*scale_x),0,texture->width-1)];
}
return row;
}
/* It vary rarely happens that some non-axis-aligned texturing creeps
* into the linear path. Handle it here. The alternative would be
* more pre-checking or an option to fallback by returning false from
* jit_linear.
*/
static const uint32_t *
fetch_row_xy_clamped(struct nearest_sampler *samp)
{
int y = samp->y++;
uint32_t *row = samp->out;
const struct lp_jit_texture *texture = samp->texture;
float yrow = samp->fsrc_y + samp->fdtdy * y;
float xrow = samp->fsrc_x + samp->fdsdy * y;
int width = samp->width;
int i;
for (i = 0; i < width; i++) {
int yy = util_iround(yrow + samp->fdtdx * i);
int xx = util_iround(xrow + samp->fdsdx * i);
const uint32_t *src_row =
(const uint32_t *)((const uint8_t *)texture->base +
CLAMP(yy, 0, texture->height-1) *
texture->row_stride[0]);
row[i] = src_row[CLAMP(xx,0,texture->width-1)];
}
return row;
}
static boolean
init_nearest_sampler(struct nearest_sampler *samp,
const struct lp_jit_texture *texture,
int x0, int y0,
int width, int height,
float s0, float dsdx, float dsdy,
float t0, float dtdx, float dtdy,
float w0, float dwdx, float dwdy)
{
int i;
float oow = 1.0f / w0;
if (dwdx != 0.0 || dwdy != 0.0)
return FALSE;
samp->texture = texture;
samp->width = width;
samp->fdsdx = dsdx * texture->width * oow;
samp->fdsdy = dsdy * texture->width * oow;
samp->fdtdx = dtdx * texture->height * oow;
samp->fdtdy = dtdy * texture->height * oow;
samp->fsrc_x = (samp->fdsdx * x0 +
samp->fdsdy * y0 +
s0 * texture->width * oow - 0.5f);
samp->fsrc_y = (samp->fdtdx * x0 +
samp->fdtdy * y0 +
t0 * texture->height * oow - 0.5f);
samp->y = 0;
/* Because we want to permit consumers of this data to round up to
* the next multiple of 4, and because we don't want valgrind to
* complain about uninitialized reads, set the last bit of the
* buffer to zero:
*/
for (i = width; i & 3; i++)
samp->out[i] = 0;
if (dsdy != 0 || dtdx != 0)
{
/* Arbitrary texture lookup:
*/
samp->fetch = fetch_row_xy_clamped;
}
else
{
/* Axis aligned stretch blit, abitrary scaling factors including
* flipped, minifying and magnifying:
*/
int isrc_x = util_iround(samp->fsrc_x);
int isrc_y = util_iround(samp->fsrc_y);
int isrc_x1 = util_iround(samp->fsrc_x + width * samp->fdsdx);
int isrc_y1 = util_iround(samp->fsrc_y + height * samp->fdtdy);
/* Look at the maximum and minimum texture coordinates we will be
* fetching and figure out if we need to use clamping. There is
* similar code in u_blit_sw.c which takes a better approach to
* this which could be substituted later.
*/
if (isrc_x <= texture->width && isrc_x >= 0 &&
isrc_y <= texture->height && isrc_y >= 0 &&
isrc_x1 <= texture->width && isrc_x1 >= 0 &&
isrc_y1 <= texture->height && isrc_y1 >= 0)
{
samp->fetch = fetch_row;
}
else {
samp->fetch = fetch_row_clamped;
}
}
return TRUE;
}
static const uint32_t *
shade_rgb1(struct shader *shader)
{
const __m128i rgb1 = _mm_set1_epi32(0xff000000);
const uint32_t *src0 = shader->src0;
uint32_t *dst = shader->out0;
int width = shader->width;
int i;
for (i = 0; i + 3 < width; i += 4) {
__m128i s = *(const __m128i *)&src0[i];
*(__m128i *)&dst[i] = _mm_or_si128(s, rgb1);
}
return shader->out0;
}
static void
init_shader(struct shader *shader,
int x, int y, int width, int height)
{
shader->width = align(width, 4);
}
/* Linear shader which implements the BLIT_RGBA shader with the
* additional constraints imposed by lp_setup_is_blit().
*/
static boolean
blit_rgba_blit(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
const struct lp_jit_context *context = &state->jit_context;
const struct lp_jit_texture *texture = &context->textures[0];
const uint8_t *src;
unsigned src_stride;
int src_x, src_y;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
/* Require w==1.0:
*/
if (a0[0][3] != 1.0 ||
dadx[0][3] != 0.0 ||
dady[0][3] != 0.0)
return FALSE;
src_x = x + util_iround(a0[1][0]*texture->width - 0.5f);
src_y = y + util_iround(a0[1][1]*texture->height - 0.5f);
src = texture->base;
src_stride = texture->row_stride[0];
/* Fall back to blit_rgba() if clamping required:
*/
if (src_x < 0 ||
src_y < 0 ||
src_x + width > texture->width ||
src_y + height > texture->height)
return FALSE;
util_copy_rect(color, PIPE_FORMAT_B8G8R8A8_UNORM, stride,
x, y,
width, height,
src, src_stride,
src_x, src_y);
return TRUE;
}
/* Linear shader which implements the BLIT_RGB1 shader, with the
* additional constraints imposed by lp_setup_is_blit().
*/
static boolean
blit_rgb1_blit(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
const struct lp_jit_context *context = &state->jit_context;
const struct lp_jit_texture *texture = &context->textures[0];
const uint8_t *src;
unsigned src_stride;
int src_x, src_y;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
/* Require w==1.0:
*/
if (a0[0][3] != 1.0 ||
dadx[0][3] != 0.0 ||
dady[0][3] != 0.0)
return FALSE;
color += x * 4 + y * stride;
src_x = x + util_iround(a0[1][0]*texture->width - 0.5f);
src_y = y + util_iround(a0[1][1]*texture->height - 0.5f);
src = texture->base;
src_stride = texture->row_stride[0];
src += src_x * 4;
src += src_y * src_stride;
if (src_x < 0 ||
src_y < 0 ||
src_x + width > texture->width ||
src_y + height > texture->height)
return FALSE;
for (y = 0; y < height; y++) {
const uint32_t *src_row = (const uint32_t *)src;
uint32_t *dst_row = (uint32_t *)color;
for (x = 0; x < width; x++) {
*dst_row++ = *src_row++ | 0xff000000;
}
color += stride;
src += src_stride;
}
return TRUE;
}
/* Linear shader variant implementing the BLIT_RGBA shader without
* blending.
*/
static boolean
blit_rgba(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
const struct lp_jit_context *context = &state->jit_context;
struct nearest_sampler samp;
struct color_blend blend;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
if (!init_nearest_sampler(&samp,
&context->textures[0],
x, y, width, height,
a0[1][0], dadx[1][0], dady[1][0],
a0[1][1], dadx[1][1], dady[1][1],
a0[0][3], dadx[0][3], dady[0][3]))
return FALSE;
init_blend(&blend,
x, y, width, height,
color, stride);
/* Rasterize the rectangle and run the shader:
*/
for (y = 0; y < height; y++) {
blend.src = samp.fetch(&samp);
blend_noop(&blend);
}
return TRUE;
}
static boolean
blit_rgb1(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
const struct lp_jit_context *context = &state->jit_context;
struct nearest_sampler samp;
struct color_blend blend;
struct shader shader;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
if (!init_nearest_sampler(&samp,
&context->textures[0],
x, y, width, height,
a0[1][0], dadx[1][0], dady[1][0],
a0[1][1], dadx[1][1], dady[1][1],
a0[0][3], dadx[0][3], dady[0][3]))
return FALSE;
init_blend(&blend,
x, y, width, height,
color, stride);
init_shader(&shader,
x, y, width, height);
/* Rasterize the rectangle and run the shader:
*/
for (y = 0; y < height; y++) {
shader.src0 = samp.fetch(&samp);
blend.src = shade_rgb1(&shader);
blend_noop(&blend);
}
return TRUE;
}
/* Linear shader variant implementing the BLIT_RGBA shader with
* one/inv_src_alpha blending.
*/
static boolean
blit_rgba_blend_premul(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
const struct lp_jit_context *context = &state->jit_context;
struct nearest_sampler samp;
struct color_blend blend;
LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
if (!init_nearest_sampler(&samp,
&context->textures[0],
x, y, width, height,
a0[1][0], dadx[1][0], dady[1][0],
a0[1][1], dadx[1][1], dady[1][1],
a0[0][3], dadx[0][3], dady[0][3]))
return FALSE;
init_blend(&blend,
x, y, width, height,
color, stride);
/* Rasterize the rectangle and run the shader:
*/
for (y = 0; y < height; y++) {
blend.src = samp.fetch(&samp);
blend_premul(&blend);
}
return TRUE;
}
/* Linear shader which always emits red. Used for debugging.
*/
static boolean
linear_red(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
union util_color uc;
util_pack_color_ub(0xff, 0, 0, 0xff,
PIPE_FORMAT_B8G8R8A8_UNORM, &uc);
util_fill_rect(color,
PIPE_FORMAT_B8G8R8A8_UNORM,
stride,
x,
y,
width,
height,
&uc);
return TRUE;
}
/* Noop linear shader variant, for debugging.
*/
static boolean
linear_no_op(const struct lp_rast_state *state,
unsigned x, unsigned y,
unsigned width, unsigned height,
const float (*a0)[4],
const float (*dadx)[4],
const float (*dady)[4],
uint8_t *color,
unsigned stride)
{
return TRUE;
}
/* Check for ADD/ONE/INV_SRC_ALPHA, ie premultiplied-alpha blending.
*/
static boolean
is_one_inv_src_alpha_blend(const struct lp_fragment_shader_variant *variant)
{
return
!variant->key.blend.logicop_enable &&
variant->key.blend.rt[0].blend_enable &&
variant->key.blend.rt[0].rgb_func == PIPE_BLEND_ADD &&
variant->key.blend.rt[0].rgb_src_factor == PIPE_BLENDFACTOR_ONE &&
variant->key.blend.rt[0].rgb_dst_factor == PIPE_BLENDFACTOR_INV_SRC_ALPHA &&
variant->key.blend.rt[0].alpha_func == PIPE_BLEND_ADD &&
variant->key.blend.rt[0].alpha_src_factor == PIPE_BLENDFACTOR_ONE &&
variant->key.blend.rt[0].alpha_dst_factor == PIPE_BLENDFACTOR_INV_SRC_ALPHA &&
variant->key.blend.rt[0].colormask == 0xf;
}
/* Examine the fragment shader varient and determine whether we can
* substitute a fastpath linear shader implementation.
*/
void
llvmpipe_fs_variant_linear_fastpath(struct lp_fragment_shader_variant *variant)
{
enum pipe_format tex_format = variant->key.samplers[0].texture_state.format;
if (LP_PERF & PERF_NO_SHADE) {
variant->jit_linear = linear_red;
return;
}
if (variant->shader->kind == LP_FS_KIND_BLIT_RGBA &&
tex_format == PIPE_FORMAT_B8G8R8A8_UNORM &&
is_nearest_clamp_sampler(&variant->key.samplers[0])) {
if (variant->opaque) {
variant->jit_linear_blit = blit_rgba_blit;
variant->jit_linear = blit_rgba;
}
else if (is_one_inv_src_alpha_blend(variant) &&
util_get_cpu_caps()->has_sse2) {
variant->jit_linear = blit_rgba_blend_premul;
}
return;
}
if (variant->shader->kind == LP_FS_KIND_BLIT_RGB1 &&
variant->opaque &&
(tex_format == PIPE_FORMAT_B8G8R8A8_UNORM ||
tex_format == PIPE_FORMAT_B8G8R8X8_UNORM) &&
is_nearest_clamp_sampler(&variant->key.samplers[0])) {
variant->jit_linear_blit = blit_rgb1_blit;
variant->jit_linear = blit_rgb1;
return;
}
if (0) {
variant->jit_linear = linear_no_op;
return;
}
}
#else
void
llvmpipe_fs_variant_linear_fastpath(struct lp_fragment_shader_variant *variant)
{
/* don't bother if there is no SSE */
}
#endif
@@ -0,0 +1,522 @@
/**************************************************************************
*
* Copyright 2010-2021 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 <limits.h>
#include "util/simple_list.h"
#include "pipe/p_defines.h"
#include "util/u_inlines.h"
#include "util/u_memory.h"
#include "util/u_pointer.h"
#include "util/format/u_format.h"
#include "util/u_dump.h"
#include "util/u_string.h"
#include "util/os_time.h"
#include "pipe/p_shader_tokens.h"
#include "draw/draw_context.h"
#include "tgsi/tgsi_dump.h"
#include "tgsi/tgsi_scan.h"
#include "tgsi/tgsi_parse.h"
#include "gallivm/lp_bld_type.h"
#include "gallivm/lp_bld_const.h"
#include "gallivm/lp_bld_conv.h"
#include "gallivm/lp_bld_init.h"
#include "gallivm/lp_bld_intr.h"
#include "gallivm/lp_bld_logic.h"
#include "gallivm/lp_bld_tgsi.h"
#include "gallivm/lp_bld_swizzle.h"
#include "gallivm/lp_bld_flow.h"
#include "gallivm/lp_bld_printf.h"
#include "gallivm/lp_bld_debug.h"
#include "lp_bld_alpha.h"
#include "lp_bld_blend.h"
#include "lp_bld_depth.h"
#include "lp_bld_interp.h"
#include "lp_context.h"
#include "lp_debug.h"
#include "lp_perf.h"
#include "lp_screen.h"
#include "lp_setup.h"
#include "lp_state.h"
#include "lp_tex_sample.h"
#include "lp_flush.h"
#include "lp_state_fs.h"
/**
* Sampler.
*/
struct linear_sampler
{
struct lp_build_sampler_aos base;
LLVMValueRef texels_ptrs[LP_MAX_LINEAR_TEXTURES];
LLVMValueRef counter;
unsigned instance;
};
/**
* Provide texels to the TGSI translation.
*
* We don't actually do any texture sampling here, but simply hand the
* precomputed row of texels.
*/
static LLVMValueRef
emit_fetch_texel_linear(const struct lp_build_sampler_aos *base,
struct lp_build_context *bld,
unsigned target, /* TGSI_TEXTURE_* */
unsigned unit,
LLVMValueRef coords,
const struct lp_derivatives derivs,
enum lp_build_tex_modifier modifier)
{
struct linear_sampler *sampler = (struct linear_sampler *)base;
LLVMValueRef texels_ptr;
LLVMValueRef texel;
if (sampler->instance >= LP_MAX_LINEAR_TEXTURES) {
assert(FALSE);
return bld->undef;
}
/* Pointer to a row of texels */
texels_ptr = sampler->texels_ptrs[sampler->instance];
texel = lp_build_pointer_get(bld->gallivm->builder, texels_ptr,
sampler->counter);
assert(LLVMTypeOf(texel) == bld->vec_type);
/*
* We have a struct lp_linear_sampler instance per TEX instruction,
* _not_ per unit, as each TEX intruction will need separate storage for the
* texels.
*/
(void)unit;
++sampler->instance;
return texel;
}
/**
* Generates the main body of the fragment shader
* Supports generating code for 4 pixel blocks and individual pixels
*/
static LLVMValueRef
llvm_fragment_body(struct lp_build_context *bld,
struct lp_fragment_shader *shader,
struct lp_fragment_shader_variant *variant,
struct linear_sampler* sampler,
LLVMValueRef *inputs_ptrs,
LLVMValueRef consts_ptr,
LLVMValueRef blend_color,
LLVMValueRef alpha_ref,
struct lp_type fs_type,
LLVMValueRef dst)
{
const unsigned char bgra_swizzles[4] = {2, 1, 0, 3};
LLVMValueRef inputs[PIPE_MAX_SHADER_INPUTS];
LLVMValueRef outputs[PIPE_MAX_SHADER_OUTPUTS];
LLVMBuilderRef builder = bld->gallivm->builder;
struct gallivm_state *gallivm = bld->gallivm;
LLVMValueRef src1 = lp_build_zero(gallivm, fs_type);
LLVMValueRef result = NULL;
unsigned i;
sampler->instance = 0;
/*
* Advance inputs
*/
for (i = 0; i < shader->info.base.num_inputs; ++i) {
LLVMValueRef inputs_ptr;
LLVMValueRef input;
inputs_ptr = inputs_ptrs[i];
input = lp_build_pointer_get(builder, inputs_ptr, sampler->counter);
assert(LLVMTypeOf(input) == bld->vec_type);
inputs[i] = input;
}
for ( ; i < PIPE_MAX_SHADER_INPUTS; ++i) {
inputs[i] = bld->undef;
}
/*
* Translate the TGSI
*/
for (i = 0; i < PIPE_MAX_SHADER_OUTPUTS; ++i) {
outputs[i] = bld->undef;
}
lp_build_tgsi_aos(gallivm, shader->base.tokens, fs_type,
bgra_swizzles,
consts_ptr, inputs, outputs,
&sampler->base,
&shader->info.base);
/*
* Blend output color
*/
for (i = 0; i < shader->info.base.num_outputs; ++i) {
LLVMValueRef mask = NULL;
LLVMValueRef output;
unsigned cbuf;
if (!outputs[i])
continue;
output = LLVMBuildLoad(builder, outputs[i], "");
lp_build_name(output, "output%u", i);
cbuf = shader->info.base.output_semantic_index[i];
lp_build_name(output, "cbuf%u", cbuf);
if (shader->info.base.output_semantic_name[i] != TGSI_SEMANTIC_COLOR || cbuf != 0)
continue;
/* Perform alpha test if necessary */
if (variant->key.alpha.enabled) {
LLVMTypeRef vec_type = lp_build_vec_type(gallivm, fs_type);
LLVMValueRef broadcast_alpha = lp_build_broadcast(gallivm, vec_type, alpha_ref);
mask = lp_build_cmp(bld, variant->key.alpha.func, output, broadcast_alpha);
/* XXX is 4 correct? */
mask = lp_build_swizzle_scalar_aos(bld, mask, bgra_swizzles[3], 4);
lp_build_name(mask, "alpha_test_mask");
}
result = lp_build_blend_aos(gallivm,
&variant->key.blend,
variant->key.cbuf_format[i],
fs_type,
cbuf, /* rt */
output, /* src */
NULL, /* src_alpha */
src1, /* src1 */
NULL, /* src1_alpha */
dst,
mask,
blend_color, /* const_ */
NULL, /* const_alpha */
bgra_swizzles,
4);
}
return result;
}
/**
* Generate a function that executes the fragment shader in a linear fashion.
*/
void
llvmpipe_fs_variant_linear_llvm(struct llvmpipe_context *lp,
struct lp_fragment_shader *shader,
struct lp_fragment_shader_variant *variant)
{
struct gallivm_state *gallivm = variant->gallivm;
char func_name[256];
struct lp_type fs_type;
LLVMTypeRef ret_type;
LLVMTypeRef arg_types[4];
LLVMTypeRef func_type;
LLVMValueRef context_ptr;
LLVMValueRef x;
LLVMValueRef y;
LLVMValueRef width;
LLVMValueRef excess;
LLVMValueRef function;
LLVMBasicBlockRef block;
LLVMBuilderRef builder;
struct lp_build_context bld;
struct linear_sampler sampler;
struct lp_build_if_state ifstate;
struct lp_build_for_loop_state loop;
LLVMValueRef consts_ptr;
LLVMValueRef interpolators_ptr;
LLVMValueRef samplers_ptr;
LLVMValueRef color0_ptr;
LLVMValueRef blend_color;
LLVMValueRef alpha_ref;
LLVMValueRef inputs_ptrs[LP_MAX_LINEAR_INPUTS];
LLVMTypeRef int8t = LLVMInt8TypeInContext(gallivm->context);
LLVMTypeRef int32t = LLVMInt32TypeInContext(gallivm->context);
LLVMTypeRef pint8t = LLVMPointerType(int8t, 0);
LLVMTypeRef pixelt = LLVMVectorType(int32t, 4);
unsigned attrib;
unsigned i;
memset(&fs_type, 0, sizeof fs_type);
fs_type.floating = FALSE;
fs_type.sign = FALSE;
fs_type.norm = TRUE;
fs_type.width = 8;
fs_type.length = 16;
if (LP_DEBUG & DEBUG_TGSI) {
tgsi_dump(shader->base.tokens, 0);
}
/*
* Generate the function prototype. Any change here must be reflected in
* lp_jit.h's lp_jit_frag_func function pointer type, and vice-versa.
*/
snprintf(func_name, sizeof(func_name), "fs%u_variant%u_linear",
shader->no, variant->no);
ret_type = pint8t;
arg_types[0] = variant->jit_linear_context_ptr_type; /* context */
arg_types[1] = int32t; /* x */
arg_types[2] = int32t; /* y */
arg_types[3] = int32t; /* width */
func_type = LLVMFunctionType(ret_type, arg_types, ARRAY_SIZE(arg_types), 0);
function = LLVMAddFunction(gallivm->module, func_name, func_type);
LLVMSetFunctionCallConv(function, LLVMCCallConv);
variant->linear_function = function;
/* XXX: need to propagate noalias down into color param now we are
* passing a pointer-to-pointer?
*/
for (i = 0; i < ARRAY_SIZE(arg_types); ++i) {
if (LLVMGetTypeKind(arg_types[i]) == LLVMPointerTypeKind) {
lp_add_function_attr(function, i + 1, LP_FUNC_ATTR_NOALIAS);
}
}
context_ptr = LLVMGetParam(function, 0);
x = LLVMGetParam(function, 1);
y = LLVMGetParam(function, 2);
width = LLVMGetParam(function, 3);
lp_build_name(context_ptr, "context");
lp_build_name(x, "x");
lp_build_name(y, "y");
lp_build_name(width, "width");
/*
* Function body
*/
block = LLVMAppendBasicBlockInContext(gallivm->context, function, "entry");
builder = gallivm->builder;
LLVMPositionBuilderAtEnd(builder, block);
lp_build_context_init(&bld, gallivm, fs_type);
/*
* Get context data
*/
consts_ptr = lp_jit_linear_context_constants(gallivm, context_ptr);
interpolators_ptr = lp_jit_linear_context_inputs(gallivm, context_ptr);
samplers_ptr = lp_jit_linear_context_tex(gallivm, context_ptr);
color0_ptr = lp_jit_linear_context_color0(gallivm, context_ptr);
color0_ptr = LLVMBuildLoad(builder, color0_ptr, "");
color0_ptr = LLVMBuildBitCast(builder, color0_ptr, LLVMPointerType(bld.vec_type, 0), "");
blend_color = lp_jit_linear_context_blend_color(gallivm, context_ptr);
blend_color = LLVMBuildLoad(builder, blend_color, "");
blend_color = lp_build_broadcast(gallivm, LLVMVectorType(int32t, 4), blend_color);
blend_color = LLVMBuildBitCast(builder, blend_color, LLVMVectorType(int8t, 16), "");
alpha_ref = lp_jit_linear_context_alpha_ref(gallivm, context_ptr);
alpha_ref = LLVMBuildLoad(builder, alpha_ref, "");
/*
* Invoke the input interpolators
*/
for (attrib = 0; attrib < shader->info.base.num_inputs; ++attrib) {
LLVMValueRef index;
LLVMValueRef elem;
LLVMValueRef fetch_ptr;
LLVMValueRef inputs_ptr;
assert(attrib < LP_MAX_LINEAR_INPUTS);
if (attrib >= LP_MAX_LINEAR_INPUTS) {
break;
}
index = LLVMConstInt(int32t, attrib, 0);
elem = lp_build_array_get(bld.gallivm, interpolators_ptr, index);
assert(LLVMGetTypeKind(LLVMTypeOf(elem)) == LLVMPointerTypeKind);
fetch_ptr = lp_build_pointer_get(builder, elem,
LLVMConstInt(int32t, 0, 0));
assert(LLVMGetTypeKind(LLVMTypeOf(fetch_ptr)) == LLVMPointerTypeKind);
/* Pointer to a row of interpolated inputs */
elem = LLVMBuildBitCast(builder, elem, pint8t, "");
inputs_ptr = LLVMBuildCall(builder, fetch_ptr, &elem, 1, "");
assert(LLVMGetTypeKind(LLVMTypeOf(inputs_ptr)) == LLVMPointerTypeKind);
/* Mark the function read-only so that LLVM can optimize it away */
lp_add_function_attr(inputs_ptr, -1, LP_FUNC_ATTR_READONLY);
lp_add_function_attr(inputs_ptr, -1, LP_FUNC_ATTR_NOUNWIND);
lp_build_name(inputs_ptr, "input%u_ptr", attrib);
inputs_ptrs[attrib] = inputs_ptr;
}
/*
* Invoke and hook up the texture samplers.
*/
memset(&sampler, 0, sizeof sampler);
sampler.base.emit_fetch_texel = &emit_fetch_texel_linear;
for (attrib = 0; attrib < shader->info.num_texs; ++attrib) {
LLVMValueRef index;
LLVMValueRef elem;
LLVMValueRef fetch_ptr;
LLVMValueRef texels_ptr;
assert(attrib < LP_MAX_LINEAR_TEXTURES);
if (attrib >= LP_MAX_LINEAR_TEXTURES) {
break;
}
index = LLVMConstInt(int32t, attrib, 0);
elem = lp_build_array_get(bld.gallivm, samplers_ptr, index);
assert(LLVMGetTypeKind(LLVMTypeOf(elem)) == LLVMPointerTypeKind);
fetch_ptr = lp_build_pointer_get(builder, elem, LLVMConstInt(int32t, 0, 0));
assert(LLVMGetTypeKind(LLVMTypeOf(fetch_ptr)) == LLVMPointerTypeKind);
/* Pointer to a row of texels */
elem = LLVMBuildBitCast(builder, elem, pint8t, "");
texels_ptr = LLVMBuildCall(builder, fetch_ptr, &elem, 1, "");
assert(LLVMGetTypeKind(LLVMTypeOf(texels_ptr)) == LLVMPointerTypeKind);
/* Mark the function read-only so that LLVM can optimize it away */
lp_add_function_attr(texels_ptr, -1, LP_FUNC_ATTR_READONLY);
lp_add_function_attr(texels_ptr, -1, LP_FUNC_ATTR_NOUNWIND);
lp_build_name(texels_ptr, "tex%u_ptr", attrib);
sampler.texels_ptrs[attrib] = texels_ptr;
}
excess = LLVMBuildAnd(builder, width, LLVMConstInt(int32t, 3, 0), "");
width = LLVMBuildLShr(builder, width, LLVMConstInt(int32t, 2, 0), "");
/* Loop over blocks of 4 pixels */
lp_build_for_loop_begin(&loop, gallivm, LLVMConstInt(int32t, 0, 0), LLVMIntULT, width, LLVMConstInt(int32t, 1, 0));
{
LLVMValueRef value;
sampler.counter = loop.counter;
/* Read 4 pixels */
value = lp_build_pointer_get_unaligned(builder, color0_ptr, loop.counter, 4);
/* Perform fragment shader body */
value = llvm_fragment_body(&bld, shader, variant, &sampler, inputs_ptrs, consts_ptr, blend_color, alpha_ref, fs_type, value);
/* Write 4 pixels */
lp_build_pointer_set_unaligned(builder, color0_ptr, loop.counter, value, 4);
}
lp_build_for_loop_end(&loop);
/* Compute the edge pixels (width % 4) */
lp_build_if(&ifstate, gallivm, LLVMBuildICmp(builder, LLVMIntNE, excess, LLVMConstInt(int32t, 0, 0), ""));
{
struct lp_build_loop_state loop_read, loop_write;
LLVMValueRef buf, elem, result, pixel_ptr;
LLVMValueRef buf_ptr = lp_build_alloca(gallivm, pixelt, "");
sampler.counter = width;
/* Get the i32* pixel pointer from the <i16x8>* element pointer */
pixel_ptr = LLVMBuildGEP(gallivm->builder, color0_ptr, &width, 1, "");
pixel_ptr = LLVMBuildBitCast(gallivm->builder, pixel_ptr, LLVMPointerType(int32t, 0), "");
/* Copy individual pixels from memory to local buffer */
lp_build_loop_begin(&loop_read, gallivm, LLVMConstInt(int32t, 0, 0));
{
elem = lp_build_pointer_get(gallivm->builder, pixel_ptr, loop_read.counter);
buf = LLVMBuildLoad(gallivm->builder, buf_ptr, "");
buf = LLVMBuildInsertElement(builder, buf, elem, loop_read.counter, "");
LLVMBuildStore(builder, buf, buf_ptr);
}
lp_build_loop_end_cond(&loop_read, excess, LLVMConstInt(int32t, 1, 0), LLVMIntUGE);
/* Perform fragment shader body */
buf = LLVMBuildLoad(gallivm->builder, buf_ptr, "");
buf = LLVMBuildBitCast(builder, buf, bld.vec_type, "");
result = llvm_fragment_body(&bld, shader, variant, &sampler, inputs_ptrs, consts_ptr, blend_color, alpha_ref, fs_type, buf);
result = LLVMBuildBitCast(builder, result, pixelt, "");
/* Write individual pixels from local buffer to the memory */
lp_build_loop_begin(&loop_write, gallivm, LLVMConstInt(int32t, 0, 0));
{
elem = LLVMBuildExtractElement(builder, result, loop_write.counter, "");
lp_build_pointer_set(gallivm->builder, pixel_ptr, loop_write.counter, elem);
}
lp_build_loop_end_cond(&loop_write, excess, LLVMConstInt(int32t, 1, 0), LLVMIntUGE);
}
lp_build_endif(&ifstate);
color0_ptr = LLVMBuildBitCast(builder, color0_ptr, pint8t, "");
LLVMBuildRet(builder, color0_ptr);
gallivm_verify_function(gallivm, function);
}
@@ -32,7 +32,7 @@
#include "lp_setup.h"
#include "draw/draw_context.h"
struct lp_rast_state {
struct lp_rasterizer_state {
struct pipe_rasterizer_state lp_state;
struct pipe_rasterizer_state draw_state;
};
@@ -63,7 +63,7 @@ llvmpipe_create_rasterizer_state(struct pipe_context *pipe,
/* Partition rasterizer state into what we want the draw module to
* handle, and what we'll look after ourselves.
*/
struct lp_rast_state *state = MALLOC_STRUCT(lp_rast_state);
struct lp_rasterizer_state *state = MALLOC_STRUCT(lp_rasterizer_state);
if (!state)
return NULL;
@@ -102,8 +102,8 @@ static void
llvmpipe_bind_rasterizer_state(struct pipe_context *pipe, void *handle)
{
struct llvmpipe_context *llvmpipe = llvmpipe_context(pipe);
const struct lp_rast_state *state =
(const struct lp_rast_state *) handle;
const struct lp_rasterizer_state *state =
(const struct lp_rasterizer_state *) handle;
if (state) {
llvmpipe->rasterizer = &state->lp_state;
@@ -125,6 +125,7 @@ llvmpipe_bind_rasterizer_state(struct pipe_context *pipe, void *handle)
state->lp_state.line_rectangular);
lp_setup_set_point_state( llvmpipe->setup,
state->lp_state.point_size,
state->lp_state.point_tri_clip,
state->lp_state.point_size_per_vertex,
state->lp_state.sprite_coord_enable,
state->lp_state.sprite_coord_mode,
@@ -178,8 +178,12 @@ llvmpipe_set_sampler_views(struct pipe_context *pipe,
}
else if (shader == PIPE_SHADER_COMPUTE) {
llvmpipe->cs_dirty |= LP_CSNEW_SAMPLER_VIEW;
} else {
}
else if (shader == PIPE_SHADER_FRAGMENT) {
llvmpipe->dirty |= LP_NEW_SAMPLER_VIEW;
lp_setup_set_fragment_sampler_views(llvmpipe->setup,
llvmpipe->num_sampler_views[PIPE_SHADER_FRAGMENT],
llvmpipe->sampler_views[PIPE_SHADER_FRAGMENT]);
}
}
@@ -71,6 +71,7 @@ struct lp_setup_args
LLVMValueRef a0;
LLVMValueRef dadx;
LLVMValueRef dady;
LLVMValueRef key;
/* Derived:
*/
@@ -708,7 +709,7 @@ generate_setup_variant(struct lp_setup_variant_key *key,
char func_name[64];
LLVMTypeRef vec4f_type;
LLVMTypeRef func_type;
LLVMTypeRef arg_types[7];
LLVMTypeRef arg_types[8];
LLVMBasicBlockRef block;
LLVMBuilderRef builder;
int64_t t0 = 0, t1;
@@ -752,6 +753,7 @@ generate_setup_variant(struct lp_setup_variant_key *key,
arg_types[4] = LLVMPointerType(vec4f_type, 0); /* a0, aligned */
arg_types[5] = LLVMPointerType(vec4f_type, 0); /* dadx, aligned */
arg_types[6] = LLVMPointerType(vec4f_type, 0); /* dady, aligned */
arg_types[7] = LLVMPointerType(vec4f_type, 0); /* key (placeholder) */
func_type = LLVMFunctionType(LLVMVoidTypeInContext(gallivm->context),
arg_types, ARRAY_SIZE(arg_types), 0);
@@ -769,6 +771,7 @@ generate_setup_variant(struct lp_setup_variant_key *key,
args.a0 = LLVMGetParam(variant->function, 4);
args.dadx = LLVMGetParam(variant->function, 5);
args.dady = LLVMGetParam(variant->function, 6);
args.key = LLVMGetParam(variant->function, 7);
lp_build_name(args.v0, "in_v0");
lp_build_name(args.v1, "in_v1");
@@ -777,6 +780,7 @@ generate_setup_variant(struct lp_setup_variant_key *key,
lp_build_name(args.a0, "out_a0");
lp_build_name(args.dadx, "out_dadx");
lp_build_name(args.dady, "out_dady");
lp_build_name(args.key, "key");
/*
* Function body
@@ -864,6 +868,7 @@ lp_make_setup_variant_key(struct llvmpipe_context *lp,
key->pgon_offset_scale = lp->rasterizer->offset_scale;
key->pgon_offset_clamp = lp->rasterizer->offset_clamp;
key->uses_constant_interp = 0;
key->pad = 0;
memcpy(key->inputs, fs->inputs, key->num_inputs * sizeof key->inputs[0]);
for (i = 0; i < key->num_inputs; i++) {
@@ -873,8 +878,10 @@ lp_make_setup_variant_key(struct llvmpipe_context *lp,
else
key->inputs[i].interp = LP_INTERP_PERSPECTIVE;
}
if (key->inputs[i].interp == LP_INTERP_CONSTANT) {
key->uses_constant_interp = 1;
}
}
}
@@ -25,6 +25,7 @@ struct lp_setup_variant_key {
unsigned pixel_center_half:1;
unsigned twoside:1;
unsigned floating_point_depth:1;
unsigned uses_constant_interp:1;
unsigned multisample:1;
unsigned pad:3;
@@ -42,7 +43,8 @@ typedef void (*lp_jit_setup_triangle)( const float (*v0)[4],
boolean front_facing,
float (*a0)[4],
float (*dadx)[4],
float (*dady)[4] );
float (*dady)[4],
const struct lp_setup_variant_key *key );
@@ -109,8 +109,6 @@ struct llvmpipe_resource
struct llvmpipe_transfer
{
struct pipe_transfer base;
unsigned long offset;
};
+13
View File
@@ -44,6 +44,10 @@ files_llvmpipe = files(
'lp_jit.c',
'lp_jit.h',
'lp_limits.h',
'lp_linear.c',
'lp_linear_fastpath.c',
'lp_linear_interp.c',
'lp_linear_sampler.c',
'lp_memory.c',
'lp_memory.h',
'lp_perf.c',
@@ -54,7 +58,10 @@ files_llvmpipe = files(
'lp_rast.c',
'lp_rast_debug.c',
'lp_rast.h',
'lp_rast_linear.c',
'lp_rast_linear_fallback.c',
'lp_rast_priv.h',
'lp_rast_rect.c',
'lp_rast_tri.c',
'lp_rast_tri_tmp.h',
'lp_scene.c',
@@ -64,10 +71,12 @@ files_llvmpipe = files(
'lp_screen.c',
'lp_screen.h',
'lp_setup.c',
'lp_setup_analysis.c',
'lp_setup_context.h',
'lp_setup.h',
'lp_setup_line.c',
'lp_setup_point.c',
'lp_setup_rect.c',
'lp_setup_tri.c',
'lp_setup_vbuf.c',
'lp_state_blend.c',
@@ -77,6 +86,10 @@ files_llvmpipe = files(
'lp_state_cs.h',
'lp_state_fs.c',
'lp_state_fs.h',
'lp_state_fs_analysis.c',
'lp_state_fs_fastpath.c',
'lp_state_fs_linear.c',
'lp_state_fs_linear_llvm.c',
'lp_state_gs.c',
'lp_state.h',
'lp_state_rasterizer.c',