diff --git a/src/gallium/auxiliary/gallivm/lp_bld_sample_aos.c b/src/gallium/auxiliary/gallivm/lp_bld_sample_aos.c index 4090db8e45a..5d501b6ac4b 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_sample_aos.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_sample_aos.c @@ -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)); diff --git a/src/gallium/auxiliary/util/u_sse.h b/src/gallium/auxiliary/util/u_sse.h index cae4138ba01..e372d3b6b64 100644 --- a/src/gallium/auxiliary/util/u_sse.h +++ b/src/gallium/auxiliary/util/u_sse.h @@ -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_ */ diff --git a/src/gallium/auxiliary/util/u_tests.c b/src/gallium/auxiliary/util/u_tests.c index d50dc80ff48..aa381448b05 100644 --- a/src/gallium/auxiliary/util/u_tests.c +++ b/src/gallium/auxiliary/util/u_tests.c @@ -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); diff --git a/src/gallium/drivers/llvmpipe/lp_clear.c b/src/gallium/drivers/llvmpipe/lp_clear.c index 3fd67b79dac..9345d910144 100644 --- a/src/gallium/drivers/llvmpipe/lp_clear.c +++ b/src/gallium/drivers/llvmpipe/lp_clear.c @@ -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; diff --git a/src/gallium/drivers/llvmpipe/lp_context.c b/src/gallium/drivers/llvmpipe/lp_context.c index 361634ed1bd..35f3618e8d7 100644 --- a/src/gallium/drivers/llvmpipe/lp_context.c +++ b/src/gallium/drivers/llvmpipe/lp_context.c @@ -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 diff --git a/src/gallium/drivers/llvmpipe/lp_context.h b/src/gallium/drivers/llvmpipe/lp_context.h index b1adba61db7..22f177eee24 100644 --- a/src/gallium/drivers/llvmpipe/lp_context.h +++ b/src/gallium/drivers/llvmpipe/lp_context.h @@ -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; diff --git a/src/gallium/drivers/llvmpipe/lp_debug.h b/src/gallium/drivers/llvmpipe/lp_debug.h index d76b9be7544..2262cdb0bba 100644 --- a/src/gallium/drivers/llvmpipe/lp_debug.h +++ b/src/gallium/drivers/llvmpipe/lp_debug.h @@ -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; diff --git a/src/gallium/drivers/llvmpipe/lp_jit.c b/src/gallium/drivers/llvmpipe/lp_jit.c index c3bfe3b15b3..3c763240a97 100644 --- a/src/gallium/drivers/llvmpipe/lp_jit.c +++ b/src/gallium/drivers/llvmpipe/lp_jit.c @@ -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); diff --git a/src/gallium/drivers/llvmpipe/lp_jit.h b/src/gallium/drivers/llvmpipe/lp_jit.h index 4f00d44c800..f9ca7706bda 100644 --- a/src/gallium/drivers/llvmpipe/lp_jit.h +++ b/src/gallium/drivers/llvmpipe/lp_jit.h @@ -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; diff --git a/src/gallium/drivers/llvmpipe/lp_limits.h b/src/gallium/drivers/llvmpipe/lp_limits.h index 1b8a37e2bb0..2c90a4fff1e 100644 --- a/src/gallium/drivers/llvmpipe/lp_limits.h +++ b/src/gallium/drivers/llvmpipe/lp_limits.h @@ -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. diff --git a/src/gallium/drivers/llvmpipe/lp_linear.c b/src/gallium/drivers/llvmpipe/lp_linear.c new file mode 100644 index 00000000000..999e7781a9b --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_linear.c @@ -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 diff --git a/src/gallium/drivers/llvmpipe/lp_linear_fastpath.c b/src/gallium/drivers/llvmpipe/lp_linear_fastpath.c new file mode 100644 index 00000000000..be2802976a0 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_linear_fastpath.c @@ -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 + diff --git a/src/gallium/drivers/llvmpipe/lp_linear_interp.c b/src/gallium/drivers/llvmpipe/lp_linear_interp.c new file mode 100644 index 00000000000..1006e40518c --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_linear_interp.c @@ -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< 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 diff --git a/src/gallium/drivers/llvmpipe/lp_linear_priv.h b/src/gallium/drivers/llvmpipe/lp_linear_priv.h new file mode 100644 index 00000000000..756e2398e57 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_linear_priv.h @@ -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 diff --git a/src/gallium/drivers/llvmpipe/lp_linear_sampler.c b/src/gallium/drivers/llvmpipe/lp_linear_sampler.c new file mode 100644 index 00000000000..4f11703bfc2 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_linear_sampler.c @@ -0,0 +1,1009 @@ +/************************************************************************** + * + * 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_debug.h" +#include "lp_state_fs.h" +#include "lp_linear_priv.h" + +#if defined(PIPE_ARCH_SSE) + +#define FIXED16_SHIFT 16 +#define FIXED16_ONE (1<<16) +#define FIXED16_HALF (1<<15) + +/* + * Color tolerance. Allow 1 bit of error in 8 bit unorm colors. + */ +#define FIXED16_TOL (FIXED16_ONE >> 7) + +/* + * Tolerance for texture coordinate derivatives when doing linear filtering. + * + * (Note that extra care needs to be taken when doing linear filtering as + * coordinates may snap up to neighbour texels inside the tile). + */ +#define FIXED16_TOL_DERIV (FIXED16_TOL / TILE_SIZE) + +static inline int +float_to_fixed16(float f) +{ + return f * (float)FIXED16_ONE; +} + +static inline int +fixed16_frac(int x) +{ + return x & (FIXED16_ONE - 1); +} + +static inline int +fixed16_approx(int x, int y, int tol) +{ + return y - tol <= x && x <= y + tol; +} + + +/* + * Unstretched blit of a bgra texture. + */ +static const uint32_t * +fetch_bgra_memcpy(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const struct lp_jit_texture *texture = samp->texture; + const uint32_t *src_row = + (const uint32_t *)((const uint8_t *)texture->base + + (samp->t >> FIXED16_SHIFT) * texture->row_stride[0]); + const int s = samp->s; + const int width = samp->width; + const uint32_t *row; + + src_row = &src_row[s >> FIXED16_SHIFT]; + + if (((uintptr_t)src_row & 0xf) == 0) { + /* The source texels are already aligned. Return them */ + row = src_row; + } else { + memcpy(samp->row, src_row, width * sizeof *row); + row = samp->row; + } + + samp->t += samp->dtdy; + return row; +} + + +/* + * Unstretched blit of a bgrx texture. + */ +static const uint32_t * +fetch_bgrx_memcpy(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const struct lp_jit_texture *texture = samp->texture; + const uint32_t *src_row = + (const uint32_t *)((const uint8_t *)texture->base + + (samp->t >> FIXED16_SHIFT) * texture->row_stride[0]); + const int s = samp->s; + const int width = samp->width; + uint32_t *row = samp->row; + int i; + + src_row = &src_row[s >> FIXED16_SHIFT]; + + for (i = 0; i < width; i++) { + row[i] = src_row[i] | 0xff000000; + } + + samp->t += samp->dtdy; + return row; +} + + +/* + * 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 16.16 fixed point, + * not to be confused with the 1.15 format used by the interpolants. + * + * After 64 pixels (ie. in the next tile), the starting point will be + * recalculated with floating point arithmetic. + */ +static const uint32_t * +fetch_bgra_axis_aligned(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const struct lp_jit_texture *texture = samp->texture; + const uint32_t *src_row = + (const uint32_t *)((const uint8_t *)texture->base + + (samp->t >> FIXED16_SHIFT) * texture->row_stride[0]); + const int dsdx = samp->dsdx; + const int width = samp->width; + uint32_t *row = samp->row; + int s = samp->s; + int i; + + for (i = 0; i < width; i++) { + row[i] = src_row[s>>FIXED16_SHIFT]; + s += dsdx; + } + + samp->t += samp->dtdy; + return row; +} + +static const uint32_t * +fetch_bgrx_axis_aligned(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const struct lp_jit_texture *texture = samp->texture; + const uint32_t *src_row = + (const uint32_t *)((const uint8_t *)texture->base + + (samp->t >> FIXED16_SHIFT) * texture->row_stride[0]); + const int dsdx = samp->dsdx; + const int width = samp->width; + uint32_t *row = samp->row; + int s = samp->s; + int i; + + for (i = 0; i < width; i++) { + row[i] = src_row[s>>FIXED16_SHIFT] | 0xff000000; + s += dsdx; + } + + samp->t += samp->dtdy; + return row; +} + +/* Non-axis aligned, but no clamping or wrapping required + */ +static const uint32_t * +fetch_bgra(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const struct lp_jit_texture *texture = samp->texture; + const uint8_t *src = texture->base; + const int stride = texture->row_stride[0]; + const int dsdx = samp->dsdx; + const int dtdx = samp->dtdx; + const int width = samp->width; + uint32_t *row = samp->row; + int s = samp->s; + int t = samp->t; + int i; + + for (i = 0; i < width; i++) { + const uint8_t *texel = (src + + (t>>FIXED16_SHIFT) * stride + + (s>>FIXED16_SHIFT) * 4); + + row[i] = *(const uint32_t *)texel; + + s += dsdx; + t += dtdx; + } + + samp->s += samp->dsdy; + samp->t += samp->dtdy; + return row; +} + + +static const uint32_t * +fetch_bgrx(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const struct lp_jit_texture *texture = samp->texture; + const uint8_t *src = texture->base; + const int stride = texture->row_stride[0]; + const int dsdx = samp->dsdx; + const int dtdx = samp->dtdx; + const int width = samp->width; + uint32_t *row = samp->row; + int s = samp->s; + int t = samp->t; + int i; + + for (i = 0; i < width; i++) { + const uint8_t *texel = (src + + (t>>FIXED16_SHIFT) * stride + + (s>>FIXED16_SHIFT) * 4); + + row[i] = (*(const uint32_t *)texel) | 0xff000000; + + s += dsdx; + t += dtdx; + } + + samp->s += samp->dsdy; + samp->t += samp->dtdy; + return row; +} + +/* Non-axis aligned, clamped. + */ +static const uint32_t * +fetch_bgra_clamp(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const struct lp_jit_texture *texture = samp->texture; + const uint8_t *src = texture->base; + const int stride = texture->row_stride[0]; + const int tex_height = texture->height - 1; + const int tex_width = texture->width - 1; + const int dsdx = samp->dsdx; + const int dtdx = samp->dtdx; + const int width = samp->width; + uint32_t *row = samp->row; + int s = samp->s; + int t = samp->t; + int i; + + for (i = 0; i < width; i++) { + int ct = CLAMP(t>>FIXED16_SHIFT, 0, tex_height); + int cs = CLAMP(s>>FIXED16_SHIFT, 0, tex_width); + + const uint8_t *texel = (src + + ct * stride + + cs * 4); + + row[i] = *(const uint32_t *)texel; + + s += dsdx; + t += dtdx; + } + + samp->s += samp->dsdy; + samp->t += samp->dtdy; + return row; +} + +static const uint32_t * +fetch_bgrx_clamp(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const struct lp_jit_texture *texture = samp->texture; + const uint8_t *src = texture->base; + const int stride = texture->row_stride[0]; + const int tex_height = texture->height - 1; + const int tex_width = texture->width - 1; + const int dsdx = samp->dsdx; + const int dtdx = samp->dtdx; + const int width = samp->width; + uint32_t *row = samp->row; + int s = samp->s; + int t = samp->t; + int i; + + for (i = 0; i < width; i++) { + int ct = CLAMP(t>>FIXED16_SHIFT, 0, tex_height); + int cs = CLAMP(s>>FIXED16_SHIFT, 0, tex_width); + + const uint8_t *texel = (src + + ct * stride + + cs * 4); + + row[i] = (*(const uint32_t *)texel) | 0xff000000; + + s += dsdx; + t += dtdx; + } + + samp->s += samp->dsdy; + samp->t += samp->dtdy; + return row; +} + +/** + * Fetch and stretch one row. + */ +static inline const uint32_t * +fetch_and_stretch_bgra_row(struct lp_linear_sampler *samp, + int y) +{ + const struct lp_jit_texture *texture = samp->texture; + const uint32_t *data = (const uint32_t *)texture->base; + const int stride = texture->row_stride[0] / sizeof(uint32_t); + const uint32_t * restrict src_row; + uint32_t * restrict dst_row; + const int width = samp->width; + + /* + * Search the stretched row cache first. + */ + + if (y == samp->stretched_row_y[0]) { + samp->stretched_row_index = 1; + return samp->stretched_row[0]; + } + + if (y == samp->stretched_row_y[1]) { + samp->stretched_row_index = 0; + return samp->stretched_row[1]; + } + + /* + * Replace one entry. + */ + + src_row = data + y * stride; + + dst_row = samp->stretched_row[samp->stretched_row_index]; + + if (fixed16_frac(samp->s) == 0 && + samp->dsdx == FIXED16_ONE) { // TODO: could be relaxed + /* + * 1:1 blit on the x direction. + */ + + unsigned i; + + src_row += samp->s >> FIXED16_SHIFT; + + if (((uintptr_t)src_row & 0xf) == 0) { + /* The source texture is already aligned. Return it */ + return src_row; + } + + /* Copy the source texture */ + for (i = 0; i < width; i += 4) { + __m128i src = _mm_loadu_si128((const __m128i *)&src_row[i]); + *(__m128i *)&dst_row[i] = src; + } + } + else { + util_sse2_stretch_row_8unorm((__m128i *)dst_row, + align(width, 4), + src_row, samp->s, samp->dsdx); + } + + samp->stretched_row_y[samp->stretched_row_index] = y; + samp->stretched_row_index ^= 1; + + return dst_row; +} + +/* Maximise only as we fetch unscaled pixels linearly into a size-64 + * temporary. For minimise, we will want to either have a bigger + * temporary or fetch sparsely. + */ +static const uint32_t * +fetch_bgra_axis_aligned_linear(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const int width = samp->width; + const uint32_t * restrict src_row0; + const uint32_t * restrict src_row1; + uint32_t * restrict row = samp->row; + int y = samp->t >> FIXED16_SHIFT; + int w = (samp->t >> 8) & 0xff; + int i; + __m128i wt; + + samp->t += samp->dtdy; + + src_row0 = fetch_and_stretch_bgra_row(samp, y); + + if (w == 0) { + return src_row0; + } + + src_row1 = fetch_and_stretch_bgra_row(samp, y + 1); + + wt = _mm_set1_epi16(w); + + /* Combine the two rows using a constant weight. + */ + for (i = 0; i < width; i += 4) { + __m128i srca = _mm_load_si128((const __m128i *)&src_row0[i]); + __m128i srcb = _mm_load_si128((const __m128i *)&src_row1[i]); + + *(__m128i *)&row[i] = util_sse2_lerp_epi8_fixed88(srca, srcb, &wt, &wt); + } + + return row; +} + +/* Non-axis-aligned version. Don't try to take advantage of + * maximize. + */ +static const uint32_t * +fetch_bgra_linear(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const struct lp_jit_texture *texture = samp->texture; + const int stride = texture->row_stride[0] / sizeof(uint32_t); + const uint32_t *data = (const uint32_t *)texture->base; + const int dsdx = samp->dsdx; + const int dtdx = samp->dtdx; + const int width = samp->width; + uint32_t *row = samp->row; + int s = samp->s; + int t = samp->t; + int i, j; + + for (i = 0; i < width; i += 4) { + union m128i si0, si1, si2, si3, ws, wt; + __m128i si02, si13; + + for (j = 0; j < 4; j++) { + const uint32_t *src = data + (t >> 16) * stride + (s>>16); + + si0.ui[j] = src[0]; + si1.ui[j] = src[1]; + si2.ui[j] = src[stride + 0]; + si3.ui[j] = src[stride + 1]; + + ws.ui[j] = (s>>8) & 0xff; + wt.ui[j] = (t>>8) & 0xff; + + s += dsdx; + t += dtdx; + } + + ws.m = _mm_or_si128(ws.m, _mm_slli_epi32(ws.m, 16)); + ws.m = _mm_or_si128(ws.m, _mm_slli_epi32(ws.m, 8)); + + wt.m = _mm_or_si128(wt.m, _mm_slli_epi32(wt.m, 16)); + wt.m = _mm_or_si128(wt.m, _mm_slli_epi32(wt.m, 8)); + + si02 = util_sse2_lerp_epi8_fixed08(si0.m, si2.m, wt.m); + si13 = util_sse2_lerp_epi8_fixed08(si1.m, si3.m, wt.m); + + *(__m128i *)&row[i] = util_sse2_lerp_epi8_fixed08(si02, si13, ws.m); + } + + samp->s += samp->dsdy; + samp->t += samp->dtdy; + return row; +} + + +/* Clamped, non-axis-aligned version. Don't try to take advantage of + * maximize. + */ +static const uint32_t * +fetch_bgra_clamp_linear(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const struct lp_jit_texture *texture = samp->texture; + const uint32_t *data = (const uint32_t *)texture->base; + const int stride = texture->row_stride[0] / sizeof(uint32_t); + const int tex_height = texture->height - 1; + const int tex_width = texture->width - 1; + const int dsdx = samp->dsdx; + const int dtdx = samp->dtdx; + const int width = samp->width; + uint32_t *row = samp->row; + int s = samp->s; + int t = samp->t; + int i, j; + /* width, height, stride (in pixels) must be smaller than 32768 */ + __m128i dsdx4, dtdx4, s4, t4, stride4, w4, h4, zero, one; + s4 = _mm_set1_epi32(s); + t4 = _mm_set1_epi32(t); + s4 = _mm_add_epi32(s4, _mm_set_epi32(3*dsdx, 2*dsdx, dsdx, 0)); + t4 = _mm_add_epi32(t4, _mm_set_epi32(3*dtdx, 2*dtdx, dtdx, 0)); + dsdx4 = _mm_set1_epi32(4*dsdx); + dtdx4 = _mm_set1_epi32(4*dtdx); + stride4 = _mm_set1_epi32(stride); + w4 = _mm_set1_epi32(tex_width); + h4 = _mm_set1_epi32(tex_height); + zero = _mm_setzero_si128(); + one = _mm_set1_epi32(1); + + for (i = 0; i < width; i += 4) { + union m128i addr[4]; + __m128i ws, wt, wsl, wsh, wtl, wth; + __m128i s4s, t4s, cs0, cs1, ct0, ct1, tmp, si[4]; + + s4s = _mm_srli_epi32(s4, 16); + t4s = _mm_srli_epi32(t4, 16); + cs0 = _mm_min_epi16(_mm_max_epi16(s4s, zero), w4); + cs1 = _mm_add_epi16(s4s, one); + cs1 = _mm_min_epi16(_mm_max_epi16(cs1, zero), w4); + ct0 = _mm_min_epi16(_mm_max_epi16(t4s, zero), h4); + ct1 = _mm_add_epi16(t4s, one); + ct1 = _mm_min_epi16(_mm_max_epi16(ct1, zero), h4); + tmp = _mm_madd_epi16(ct0, stride4); + addr[0].m = _mm_add_epi32(tmp, cs0); + addr[1].m = _mm_add_epi32(tmp, cs1); + tmp = _mm_madd_epi16(ct1, stride4); + addr[2].m = _mm_add_epi32(tmp, cs0); + addr[3].m = _mm_add_epi32(tmp, cs1); + + for (j = 0; j < 4; j++) { + __m128i ld1, ld2, ld3; + si[j] = _mm_cvtsi32_si128(data[addr[j].ui[0]]); + ld1 = _mm_cvtsi32_si128(data[addr[j].ui[1]]); + si[j] = _mm_unpacklo_epi32(si[j], ld1); + ld2 = _mm_cvtsi32_si128(data[addr[j].ui[2]]); + ld3 = _mm_cvtsi32_si128(data[addr[j].ui[3]]); + ld2 = _mm_unpacklo_epi32(ld2, ld3); + si[j] = _mm_unpacklo_epi64(si[j], ld2); + } + + ws = _mm_srli_epi32(s4, 8); + ws = _mm_and_si128(ws, _mm_set1_epi32(0xFF)); + wt = _mm_srli_epi32(t4, 8); + wt = _mm_and_si128(wt, _mm_set1_epi32(0xFF)); + + s4 = _mm_add_epi32(s4, dsdx4); + t4 = _mm_add_epi32(t4, dtdx4); + +#if 0 +/* scalar code for reference */ + for (j = 0; j < 4; j++) { + int s0 = s >> FIXED16_SHIFT; + int t0 = t >> FIXED16_SHIFT; + int cs0 = CLAMP(s0 , 0, tex_width); + int cs1 = CLAMP(s0 + 1, 0, tex_width); + int ct0 = CLAMP(t0 , 0, tex_height); + int ct1 = CLAMP(t0 + 1, 0, tex_height); + + si0.ui[j] = data[ct0 * stride + cs0]; + si1.ui[j] = data[ct0 * stride + cs1]; + si2.ui[j] = data[ct1 * stride + cs0]; + si3.ui[j] = data[ct1 * stride + cs1]; + + ws.ui[j] = (s>>8) & 0xff; + wt.ui[j] = (t>>8) & 0xff; + + s += dsdx; + t += dtdx; + } +#endif + + ws = _mm_or_si128(ws, _mm_slli_epi32(ws, 16)); + wsl = _mm_shuffle_epi32(ws, _MM_SHUFFLE(1,1,0,0)); + wsh = _mm_shuffle_epi32(ws, _MM_SHUFFLE(3,3,2,2)); + + wt = _mm_or_si128(wt, _mm_slli_epi32(wt, 16)); + wtl = _mm_shuffle_epi32(wt, _MM_SHUFFLE(1,1,0,0)); + wth = _mm_shuffle_epi32(wt, _MM_SHUFFLE(3,3,2,2)); + + *(__m128i *)&row[i] = util_sse2_lerp_2d_epi8_fixed88(si[0], si[2], + &si[1], &si[3], + &wtl, &wth, + &wsl, &wsh); + } + + samp->s += samp->dsdy; + samp->t += samp->dtdy; + return row; +} + +static const uint32_t * +fetch_bgrx_axis_aligned_linear(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const __m128i mask = _mm_set1_epi32(0xff000000); + uint32_t *dst_row = samp->row; + const uint32_t *src_row; + int width = samp->width; + int i; + + src_row = fetch_bgra_axis_aligned_linear(&samp->base); + + for (i = 0; i < width; i += 4) { + __m128i bgra = *(__m128i *)&src_row[i]; + __m128i bgrx = _mm_or_si128(bgra, mask); + *(__m128i *)&dst_row[i] = bgrx; + } + + return dst_row; +} + + +static const uint32_t * +fetch_bgrx_clamp_linear(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const __m128i mask = _mm_set1_epi32(0xff000000); + uint32_t *row = samp->row; + int width = samp->width; + int i; + + fetch_bgra_clamp_linear(&samp->base); + + for (i = 0; i < width; i += 4) { + __m128i bgra = *(__m128i *)&row[i]; + __m128i bgrx = _mm_or_si128(bgra, mask); + *(__m128i *)&row[i] = bgrx; + } + + return row; +} + + +static const uint32_t * +fetch_bgrx_linear(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + const __m128i mask = _mm_set1_epi32(0xff000000); + uint32_t *row = samp->row; + int width = samp->width; + int i; + + fetch_bgra_linear(&samp->base); + + for (i = 0; i < width; i += 4) { + __m128i bgra = *(__m128i *)&row[i]; + __m128i bgrx = _mm_or_si128(bgra, mask); + *(__m128i *)&row[i] = bgrx; + } + + return row; +} + + +static boolean +sampler_is_nearest(const struct lp_linear_sampler *samp, + const struct lp_sampler_static_state *sampler_state, + boolean minify) +{ + unsigned img_filter; + + if (minify) + img_filter = sampler_state->sampler_state.min_img_filter; + else + img_filter = sampler_state->sampler_state.mag_img_filter; + + /* Is it obviously nearest? + */ + if (img_filter == PIPE_TEX_FILTER_NEAREST) + return TRUE; + + /* Otherwise look for linear samplers which devolve to nearest. + */ + + /* Needs to be axis aligned. + */ + if (!samp->axis_aligned) + return FALSE; + + if (0) { + /* For maximizing shaders, revert to nearest + */ + if (samp->dsdx < -FIXED16_HALF && samp->dsdx < FIXED16_HALF && + samp->dtdy < -FIXED16_HALF && samp->dtdy < FIXED16_HALF) + return TRUE; + + /* For severely minimising shaders, revert to nearest: + */ + if ((samp->dsdx < 2 * FIXED16_ONE || samp->dsdx > 2 * FIXED16_ONE) && + (samp->dtdy < 2 * FIXED16_ONE || samp->dtdy > 2 * FIXED16_ONE)) + return TRUE; + } + + /* + * Must be near a pixel center: + */ + if (!fixed16_approx(fixed16_frac(samp->s), FIXED16_HALF, FIXED16_TOL) || + !fixed16_approx(fixed16_frac(samp->t), FIXED16_HALF, FIXED16_TOL)) + return FALSE; + + /* + * Must make a full step between pixels: + */ + if (!fixed16_approx(samp->dsdx, FIXED16_ONE, FIXED16_TOL_DERIV) || + !fixed16_approx(samp->dtdy, FIXED16_ONE, FIXED16_TOL_DERIV)) + return FALSE; + + /* Treat it as nearest! + */ + return TRUE; +} + +/* XXX: Lots of static-state parameters being passed in here but very + * little info is extracted from each one. Consolidate it all down to + * something succinct in the prepare phase? + */ +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]) +{ + const struct lp_tgsi_channel_info *schan = &info->coord[0]; + const struct lp_tgsi_channel_info *tchan = &info->coord[1]; + + float w0 = a0[0][3]; + + float s0 = a0[schan->u.index+1][schan->swizzle]; + float dsdx = dadx[schan->u.index+1][schan->swizzle]; + float dsdy = dady[schan->u.index+1][schan->swizzle]; + + float t0 = a0[tchan->u.index+1][tchan->swizzle]; + float dtdx = dadx[tchan->u.index+1][tchan->swizzle]; + float dtdy = dady[tchan->u.index+1][tchan->swizzle]; + + int mins, mint, maxs, maxt; + float oow = 1.0f / w0; + float width_oow = texture->width * oow; + float height_oow = texture->height * oow; + float fdsdx = dsdx * width_oow; + float fdsdy = dsdy * width_oow; + float fdtdx = dtdx * height_oow; + float fdtdy = dtdy * height_oow; + int fetch_width; + int fetch_height; + boolean minify; + boolean need_wrap; + boolean is_nearest; + + samp->texture = texture; + samp->width = width; + + samp->s = float_to_fixed16(fdsdx * x0 + + fdsdy * y0 + + s0 * width_oow); + + samp->t = float_to_fixed16(fdtdx * x0 + + fdtdy * y0 + + t0 * height_oow); + + samp->dsdx = float_to_fixed16(fdsdx); + samp->dsdy = float_to_fixed16(fdsdy); + samp->dtdx = float_to_fixed16(fdtdx); + samp->dtdy = float_to_fixed16(fdtdy); + + + samp->axis_aligned = (samp->dsdy == 0 && + samp->dtdx == 0); // TODO: could be relaxed + + { + int dsdx = samp->dsdx >= 0 ? samp->dsdx : -samp->dsdx; + int dsdy = samp->dsdy >= 0 ? samp->dsdy : -samp->dsdy; + int dtdx = samp->dtdx >= 0 ? samp->dtdx : -samp->dtdx; + int dtdy = samp->dtdy >= 0 ? samp->dtdy : -samp->dtdy; + int rho = MAX4(dsdx, dsdy, dtdx, dtdy); + + minify = (rho > FIXED16_ONE); + } + + is_nearest = sampler_is_nearest(samp, sampler_state, minify); + + if (!is_nearest) { + samp->s -= FIXED16_HALF; + samp->t -= FIXED16_HALF; + } + + /* Check for clamping. This rarely happens as we're rejecting interpolants + * which fall outside the 0..1 range. + */ + + if (is_nearest) { + /* Nearest fetch routines don't employ SSE and always operate one pixel + * at a time. + */ + fetch_width = width - 1; + } + else { + /* Linear fetch routines employ SSE, and always fetch groups of four + * texels. + */ + fetch_width = align(width, 4) - 1; + } + fetch_height = height - 1; + + if (samp->axis_aligned) { + int s0 = samp->s; + int s1 = samp->s + fetch_width * samp->dsdx; + int t0 = samp->t; + int t1 = samp->t + fetch_height * samp->dtdy; + + mins = MIN2(s0, s1); + mint = MIN2(t0, t1); + maxs = MAX2(s0, s1); + maxt = MAX2(t0, t1); + } + else { + int s0 = samp->s; + int s1 = samp->s + fetch_width * samp->dsdx; + int s2 = samp->s + fetch_height * samp->dsdy; + int s3 = samp->s + fetch_width * samp->dsdx + fetch_height * samp->dsdy; + int t0 = samp->t; + int t1 = samp->t + fetch_width * samp->dtdx; + int t2 = samp->t + fetch_height * samp->dtdy; + int t3 = samp->t + fetch_width * samp->dtdx + fetch_height * samp->dtdy; + + mins = MIN4(s0, s1, s2, s3); + mint = MIN4(t0, t1, t2, t3); + maxs = MAX4(s0, s1, s2, s3); + maxt = MAX4(t0, t1, t2, t3); + } + + if (is_nearest) { + need_wrap = (mins < 0 || + mint < 0 || + maxs >= (texture->width << FIXED16_SHIFT) || + maxt >= (texture->height << FIXED16_SHIFT)); + } else { + need_wrap = (mins < 0 || + mint < 0 || + maxs + FIXED16_ONE >= (texture->width << FIXED16_SHIFT) || + maxt + FIXED16_ONE >= (texture->height << FIXED16_SHIFT)); + } + + if (0 && need_wrap) { + debug_printf("%u x %u %s\n", + texture->width, texture->height, + is_nearest ? "nearest" : "linear"); + debug_printf("mins = %f\n", mins*1.0f/FIXED16_ONE); + debug_printf("mint = %f\n", mint*1.0f/FIXED16_ONE); + debug_printf("maxs = %f\n", maxs*1.0f/FIXED16_ONE); + debug_printf("maxt = %f\n", maxt*1.0f/FIXED16_ONE); + debug_printf("\n"); + } + + /* We accept any mode below, but we only implement clamping. + */ + if (need_wrap && + (sampler_state->sampler_state.wrap_s != PIPE_TEX_WRAP_CLAMP_TO_EDGE || + sampler_state->sampler_state.wrap_t != PIPE_TEX_WRAP_CLAMP_TO_EDGE)) { + return FALSE; + } + + if (is_nearest) { + switch (sampler_state->texture_state.format) { + case PIPE_FORMAT_B8G8R8A8_UNORM: + if (need_wrap) + samp->base.fetch = fetch_bgra_clamp; + else if (!samp->axis_aligned) + samp->base.fetch = fetch_bgra; + else if (samp->dsdx != FIXED16_ONE) // TODO: could be relaxed + samp->base.fetch = fetch_bgra_axis_aligned; + else + samp->base.fetch = fetch_bgra_memcpy; + + return TRUE; + + case PIPE_FORMAT_B8G8R8X8_UNORM: + if (need_wrap) + samp->base.fetch = fetch_bgrx_clamp; + else if (!samp->axis_aligned) + samp->base.fetch = fetch_bgrx; + else if (samp->dsdx != FIXED16_ONE) // TODO: could be relaxed + samp->base.fetch = fetch_bgrx_axis_aligned; + else + samp->base.fetch = fetch_bgrx_memcpy; + + return TRUE; + + default: + break; + } + + FAIL("unknown format for nearest"); + } + else { + samp->stretched_row_y[0] = -1; + samp->stretched_row_y[1] = -1; + samp->stretched_row_index = 0; + + switch (sampler_state->texture_state.format) { + case PIPE_FORMAT_B8G8R8A8_UNORM: + if (need_wrap) + samp->base.fetch = fetch_bgra_clamp_linear; + else if (!samp->axis_aligned) + samp->base.fetch = fetch_bgra_linear; + else + samp->base.fetch = fetch_bgra_axis_aligned_linear; + + return TRUE; + + case PIPE_FORMAT_B8G8R8X8_UNORM: + if (need_wrap) + samp->base.fetch = fetch_bgrx_clamp_linear; + else if (!samp->axis_aligned) + samp->base.fetch = fetch_bgrx_linear; + else + samp->base.fetch = fetch_bgrx_axis_aligned_linear; + return TRUE; + + default: + break; + } + + FAIL("unknown format"); + } +} + + +static const uint32_t * +fetch_noop(struct lp_linear_elem *elem) +{ + struct lp_linear_sampler *samp = (struct lp_linear_sampler *)elem; + return samp->row; +} + + +void +lp_linear_init_noop_sampler(struct lp_linear_sampler *samp) +{ + samp->base.fetch = fetch_noop; +} + +/* Check the variant for linear path compatibility. + */ +boolean +lp_linear_check_sampler(const struct lp_sampler_static_state *sampler, + const struct lp_tgsi_texture_info *tex) +{ + if (tex->modifier != LP_BLD_TEX_MODIFIER_NONE) + return FALSE; + + if (tex->target != TGSI_TEXTURE_2D) + return FALSE; + + if (tex->coord[0].file != TGSI_FILE_INPUT || + tex->coord[1].file != TGSI_FILE_INPUT) + return FALSE; + + /* These are the only sampling modes we support at the moment. + * + * Actually we'll accept any mode as we're failing on any + * interpolant which exceeds 0..1. Clamping is applied only to + * avoid invalid reads. + */ + if (!is_nearest_sampler(sampler) && + !is_linear_sampler(sampler)) + return FALSE; + + /* These are the only texture formats we support at the moment + */ + if (sampler->texture_state.format != PIPE_FORMAT_B8G8R8A8_UNORM && + sampler->texture_state.format != PIPE_FORMAT_B8G8R8X8_UNORM) + return FALSE; + + return TRUE; +} + +#else +boolean +lp_linear_check_sampler(const struct lp_sampler_static_state *sampler, + const struct lp_tgsi_texture_info *tex) +{ + return FALSE; +} +#endif diff --git a/src/gallium/drivers/llvmpipe/lp_perf.c b/src/gallium/drivers/llvmpipe/lp_perf.c index a4548bccf1c..6c9fafaad82 100644 --- a/src/gallium/drivers/llvmpipe/lp_perf.c +++ b/src/gallium/drivers/llvmpipe/lp_perf.c @@ -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); diff --git a/src/gallium/drivers/llvmpipe/lp_perf.h b/src/gallium/drivers/llvmpipe/lp_perf.h index ace85c7fef4..61e3fa8ddbc 100644 --- a/src/gallium/drivers/llvmpipe/lp_perf.h +++ b/src/gallium/drivers/llvmpipe/lp_perf.h @@ -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 */ diff --git a/src/gallium/drivers/llvmpipe/lp_rast.c b/src/gallium/drivers/llvmpipe/lp_rast.c index 6b5160667bf..87dc68b992e 100644 --- a/src/gallium/drivers/llvmpipe/lp_rast.c +++ b/src/gallium/drivers/llvmpipe/lp_rast.c @@ -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); diff --git a/src/gallium/drivers/llvmpipe/lp_rast.h b/src/gallium/drivers/llvmpipe/lp_rast.h index 2a2fdceb77f..6e7a16ed649 100644 --- a/src/gallium/drivers/llvmpipe/lp_rast.h +++ b/src/gallium/drivers/llvmpipe/lp_rast.h @@ -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 diff --git a/src/gallium/drivers/llvmpipe/lp_rast_debug.c b/src/gallium/drivers/llvmpipe/lp_rast_debug.c index e36ade0e307..dc46bfd1046 100644 --- a/src/gallium/drivers/llvmpipe/lp_rast_debug.c +++ b/src/gallium/drivers/llvmpipe/lp_rast_debug.c @@ -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"); } } diff --git a/src/gallium/drivers/llvmpipe/lp_rast_linear.c b/src/gallium/drivers/llvmpipe/lp_rast_linear.c new file mode 100644 index 00000000000..3da1d5f84d1 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_rast_linear.c @@ -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 +#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] ); + } + } +} diff --git a/src/gallium/drivers/llvmpipe/lp_rast_linear_fallback.c b/src/gallium/drivers/llvmpipe/lp_rast_linear_fallback.c new file mode 100644 index 00000000000..ede03993475 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_rast_linear_fallback.c @@ -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 +#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); + } + } + } +} diff --git a/src/gallium/drivers/llvmpipe/lp_rast_priv.h b/src/gallium/drivers/llvmpipe/lp_rast_priv.h index c8154348ef9..c4da9cca2ff 100644 --- a/src/gallium/drivers/llvmpipe/lp_rast_priv.h +++ b/src/gallium/drivers/llvmpipe/lp_rast_priv.h @@ -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 diff --git a/src/gallium/drivers/llvmpipe/lp_rast_rect.c b/src/gallium/drivers/llvmpipe/lp_rast_rect.c new file mode 100644 index 00000000000..77b122bddfa --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_rast_rect.c @@ -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 +#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); + } + } + } +} + + diff --git a/src/gallium/drivers/llvmpipe/lp_rast_tri.c b/src/gallium/drivers/llvmpipe/lp_rast_tri.c index 01fbd2d9d74..5874331d463 100644 --- a/src/gallium/drivers/llvmpipe/lp_rast_tri.c +++ b/src/gallium/drivers/llvmpipe/lp_rast_tri.c @@ -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 diff --git a/src/gallium/drivers/llvmpipe/lp_rast_tri_tmp.h b/src/gallium/drivers/llvmpipe/lp_rast_tri_tmp.h index 47280e591a6..bced19c2252 100644 --- a/src/gallium/drivers/llvmpipe/lp_rast_tri_tmp.h +++ b/src/gallium/drivers/llvmpipe/lp_rast_tri_tmp.h @@ -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; diff --git a/src/gallium/drivers/llvmpipe/lp_scene.c b/src/gallium/drivers/llvmpipe/lp_scene.c index 0888341e049..49db1832ead 100644 --- a/src/gallium/drivers/llvmpipe/lp_scene.c +++ b/src/gallium/drivers/llvmpipe/lp_scene.c @@ -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); diff --git a/src/gallium/drivers/llvmpipe/lp_scene.h b/src/gallium/drivers/llvmpipe/lp_scene.h index 4ee2e5a8bc3..a089e6a4983 100644 --- a/src/gallium/drivers/llvmpipe/lp_scene.h +++ b/src/gallium/drivers/llvmpipe/lp_scene.h @@ -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) diff --git a/src/gallium/drivers/llvmpipe/lp_screen.c b/src/gallium/drivers/llvmpipe/lp_screen.c index e7b5f2e50c6..f57e3f95461 100644 --- a/src/gallium/drivers/llvmpipe/lp_screen.c +++ b/src/gallium/drivers/llvmpipe/lp_screen.c @@ -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); diff --git a/src/gallium/drivers/llvmpipe/lp_setup.c b/src/gallium/drivers/llvmpipe/lp_setup.c index 8bd0893465c..4c0c0ad50e9 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup.c +++ b/src/gallium/drivers/llvmpipe/lp_setup.c @@ -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; diff --git a/src/gallium/drivers/llvmpipe/lp_setup.h b/src/gallium/drivers/llvmpipe/lp_setup.h index a3456c281ea..a6e8e1f49ed 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup.h +++ b/src/gallium/drivers/llvmpipe/lp_setup.h @@ -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); diff --git a/src/gallium/drivers/llvmpipe/lp_setup_analysis.c b/src/gallium/drivers/llvmpipe/lp_setup_analysis.c new file mode 100644 index 00000000000..68457e96d42 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_setup_analysis.c @@ -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; +} diff --git a/src/gallium/drivers/llvmpipe/lp_setup_context.h b/src/gallium/drivers/llvmpipe/lp_setup_context.h index e65dbeb06cd..fe645fc9516 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup_context.h +++ b/src/gallium/drivers/llvmpipe/lp_setup_context.h @@ -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 diff --git a/src/gallium/drivers/llvmpipe/lp_setup_line.c b/src/gallium/drivers/llvmpipe/lp_setup_line.c index 38ff15f213e..2688c60ee70 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup_line.c +++ b/src/gallium/drivers/llvmpipe/lp_setup_line.c @@ -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, diff --git a/src/gallium/drivers/llvmpipe/lp_setup_point.c b/src/gallium/drivers/llvmpipe/lp_setup_point.c index bca2d47d135..335b9d0ccfc 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup_point.c +++ b/src/gallium/drivers/llvmpipe/lp_setup_point.c @@ -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); + } } diff --git a/src/gallium/drivers/llvmpipe/lp_setup_rect.c b/src/gallium/drivers/llvmpipe/lp_setup_rect.c new file mode 100644 index 00000000000..7c1234eafe7 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_setup_rect.c @@ -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<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; + } +} diff --git a/src/gallium/drivers/llvmpipe/lp_setup_tri.c b/src/gallium/drivers/llvmpipe/lp_setup_tri.c index 4fb76dd22d2..56494d49a93 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup_tri.c +++ b/src/gallium/drivers/llvmpipe/lp_setup_tri.c @@ -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. diff --git a/src/gallium/drivers/llvmpipe/lp_setup_vbuf.c b/src/gallium/drivers/llvmpipe/lp_setup_vbuf.c index fcc27dc7e75..5ed569c4a97 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup_vbuf.c +++ b/src/gallium/drivers/llvmpipe/lp_setup_vbuf.c @@ -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; diff --git a/src/gallium/drivers/llvmpipe/lp_state.h b/src/gallium/drivers/llvmpipe/lp_state.h index 6887d98487d..fab180a8cfb 100644 --- a/src/gallium/drivers/llvmpipe/lp_state.h +++ b/src/gallium/drivers/llvmpipe/lp_state.h @@ -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); diff --git a/src/gallium/drivers/llvmpipe/lp_state_blend.c b/src/gallium/drivers/llvmpipe/lp_state_blend.c index a1ba3e3f5dc..2c4760a574e 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_blend.c +++ b/src/gallium/drivers/llvmpipe/lp_state_blend.c @@ -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; diff --git a/src/gallium/drivers/llvmpipe/lp_state_derived.c b/src/gallium/drivers/llvmpipe/lp_state_derived.c index 04899dd9bfd..3baf509a73c 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_derived.c +++ b/src/gallium/drivers/llvmpipe/lp_state_derived.c @@ -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; } diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs.c b/src/gallium/drivers/llvmpipe/lp_state_fs.c index d577990c761..cf36d2d14dc 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_fs.c +++ b/src/gallium/drivers/llvmpipe/lp_state_fs.c @@ -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. diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs.h b/src/gallium/drivers/llvmpipe/lp_state_fs.h index d5dd49901e6..9f43665d839 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_fs.h +++ b/src/gallium/drivers/llvmpipe/lp_state_fs.h @@ -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); diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs_analysis.c b/src/gallium/drivers/llvmpipe/lp_state_fs_analysis.c new file mode 100644 index 00000000000..cd397e10824 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_state_fs_analysis.c @@ -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; + } +} diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs_fastpath.c b/src/gallium/drivers/llvmpipe/lp_state_fs_fastpath.c new file mode 100644 index 00000000000..3fbb9142c14 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_state_fs_fastpath.c @@ -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 + + +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 diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs_linear.c b/src/gallium/drivers/llvmpipe/lp_state_fs_linear.c new file mode 100644 index 00000000000..a8d10ae300d --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_state_fs_linear.c @@ -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 + + +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 + diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs_linear_llvm.c b/src/gallium/drivers/llvmpipe/lp_state_fs_linear_llvm.c new file mode 100644 index 00000000000..1333dfef846 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_state_fs_linear_llvm.c @@ -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 +#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 * 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); +} + + diff --git a/src/gallium/drivers/llvmpipe/lp_state_rasterizer.c b/src/gallium/drivers/llvmpipe/lp_state_rasterizer.c index c9d0e17e27e..2b1d65af1c3 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_rasterizer.c +++ b/src/gallium/drivers/llvmpipe/lp_state_rasterizer.c @@ -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, diff --git a/src/gallium/drivers/llvmpipe/lp_state_sampler.c b/src/gallium/drivers/llvmpipe/lp_state_sampler.c index 9303e3bd7ec..5332bb049c2 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_sampler.c +++ b/src/gallium/drivers/llvmpipe/lp_state_sampler.c @@ -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]); } } diff --git a/src/gallium/drivers/llvmpipe/lp_state_setup.c b/src/gallium/drivers/llvmpipe/lp_state_setup.c index c737850be22..e092179cf80 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_setup.c +++ b/src/gallium/drivers/llvmpipe/lp_state_setup.c @@ -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; + } } - } diff --git a/src/gallium/drivers/llvmpipe/lp_state_setup.h b/src/gallium/drivers/llvmpipe/lp_state_setup.h index 18e0ea8f08e..a92a9e7f288 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_setup.h +++ b/src/gallium/drivers/llvmpipe/lp_state_setup.h @@ -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 ); diff --git a/src/gallium/drivers/llvmpipe/lp_texture.h b/src/gallium/drivers/llvmpipe/lp_texture.h index bda52814165..d1ba22b2685 100644 --- a/src/gallium/drivers/llvmpipe/lp_texture.h +++ b/src/gallium/drivers/llvmpipe/lp_texture.h @@ -109,8 +109,6 @@ struct llvmpipe_resource struct llvmpipe_transfer { struct pipe_transfer base; - - unsigned long offset; }; diff --git a/src/gallium/drivers/llvmpipe/meson.build b/src/gallium/drivers/llvmpipe/meson.build index 777830ace92..661eb1e8a2e 100644 --- a/src/gallium/drivers/llvmpipe/meson.build +++ b/src/gallium/drivers/llvmpipe/meson.build @@ -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',