mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-27 06:10:13 +01:00
i965: Share math functions between brw_wm_glsl.c and brw_wm_emit.c.
This commit is contained in:
parent
7059aa0eff
commit
1e5400c575
3 changed files with 111 additions and 221 deletions
|
|
@ -357,11 +357,27 @@ void emit_mad(struct brw_compile *p,
|
|||
const struct brw_reg *arg0,
|
||||
const struct brw_reg *arg1,
|
||||
const struct brw_reg *arg2);
|
||||
void emit_math1(struct brw_wm_compile *c,
|
||||
GLuint function,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0);
|
||||
void emit_math2(struct brw_wm_compile *c,
|
||||
GLuint function,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0,
|
||||
const struct brw_reg *arg1);
|
||||
void emit_sop(struct brw_compile *p,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
GLuint cond,
|
||||
const struct brw_reg *arg0,
|
||||
const struct brw_reg *arg1);
|
||||
void emit_xpd(struct brw_compile *p,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0,
|
||||
const struct brw_reg *arg1);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -665,11 +665,11 @@ void emit_dph(struct brw_compile *p,
|
|||
}
|
||||
|
||||
|
||||
static void emit_xpd( struct brw_compile *p,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0,
|
||||
const struct brw_reg *arg1 )
|
||||
void emit_xpd(struct brw_compile *p,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0,
|
||||
const struct brw_reg *arg1)
|
||||
{
|
||||
GLuint i;
|
||||
|
||||
|
|
@ -690,41 +690,68 @@ static void emit_xpd( struct brw_compile *p,
|
|||
}
|
||||
|
||||
|
||||
static void emit_math1( struct brw_compile *p,
|
||||
GLuint function,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0 )
|
||||
void emit_math1(struct brw_wm_compile *c,
|
||||
GLuint function,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0)
|
||||
{
|
||||
struct brw_compile *p = &c->func;
|
||||
int dst_chan = _mesa_ffs(mask & WRITEMASK_XYZW) - 1;
|
||||
GLuint saturate = ((mask & SATURATE) ?
|
||||
BRW_MATH_SATURATE_SATURATE :
|
||||
BRW_MATH_SATURATE_NONE);
|
||||
|
||||
if (!(mask & WRITEMASK_XYZW))
|
||||
return; /* Do not emit dead code */
|
||||
|
||||
assert(is_power_of_two(mask & WRITEMASK_XYZW));
|
||||
|
||||
/* If compressed, this will write message reg 2,3 from arg0.x's 16
|
||||
* channels.
|
||||
*/
|
||||
brw_MOV(p, brw_message_reg(2), arg0[0]);
|
||||
|
||||
/* Send two messages to perform all 16 operations:
|
||||
*/
|
||||
brw_math_16(p,
|
||||
dst[dst_chan],
|
||||
brw_push_insn_state(p);
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_NONE);
|
||||
brw_math(p,
|
||||
dst[dst_chan],
|
||||
function,
|
||||
saturate,
|
||||
2,
|
||||
brw_null_reg(),
|
||||
BRW_MATH_DATA_VECTOR,
|
||||
BRW_MATH_PRECISION_FULL);
|
||||
|
||||
if (c->dispatch_width == 16) {
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_2NDHALF);
|
||||
brw_math(p,
|
||||
offset(dst[dst_chan],1),
|
||||
function,
|
||||
(mask & SATURATE) ? BRW_MATH_SATURATE_SATURATE : BRW_MATH_SATURATE_NONE,
|
||||
2,
|
||||
saturate,
|
||||
3,
|
||||
brw_null_reg(),
|
||||
BRW_MATH_DATA_VECTOR,
|
||||
BRW_MATH_PRECISION_FULL);
|
||||
}
|
||||
brw_pop_insn_state(p);
|
||||
}
|
||||
|
||||
|
||||
static void emit_math2( struct brw_compile *p,
|
||||
GLuint function,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0,
|
||||
const struct brw_reg *arg1)
|
||||
void emit_math2(struct brw_wm_compile *c,
|
||||
GLuint function,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0,
|
||||
const struct brw_reg *arg1)
|
||||
{
|
||||
struct brw_compile *p = &c->func;
|
||||
int dst_chan = _mesa_ffs(mask & WRITEMASK_XYZW) - 1;
|
||||
GLuint saturate = ((mask & SATURATE) ?
|
||||
BRW_MATH_SATURATE_SATURATE :
|
||||
BRW_MATH_SATURATE_NONE);
|
||||
|
||||
if (!(mask & WRITEMASK_XYZW))
|
||||
return; /* Do not emit dead code */
|
||||
|
|
@ -735,37 +762,41 @@ static void emit_math2( struct brw_compile *p,
|
|||
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_NONE);
|
||||
brw_MOV(p, brw_message_reg(2), arg0[0]);
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_2NDHALF);
|
||||
brw_MOV(p, brw_message_reg(4), sechalf(arg0[0]));
|
||||
if (c->dispatch_width == 16) {
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_2NDHALF);
|
||||
brw_MOV(p, brw_message_reg(4), sechalf(arg0[0]));
|
||||
}
|
||||
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_NONE);
|
||||
brw_MOV(p, brw_message_reg(3), arg1[0]);
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_2NDHALF);
|
||||
brw_MOV(p, brw_message_reg(5), sechalf(arg1[0]));
|
||||
if (c->dispatch_width == 16) {
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_2NDHALF);
|
||||
brw_MOV(p, brw_message_reg(5), sechalf(arg1[0]));
|
||||
}
|
||||
|
||||
|
||||
/* Send two messages to perform all 16 operations:
|
||||
*/
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_NONE);
|
||||
brw_math(p,
|
||||
dst[dst_chan],
|
||||
function,
|
||||
(mask & SATURATE) ? BRW_MATH_SATURATE_SATURATE : BRW_MATH_SATURATE_NONE,
|
||||
saturate,
|
||||
2,
|
||||
brw_null_reg(),
|
||||
BRW_MATH_DATA_VECTOR,
|
||||
BRW_MATH_PRECISION_FULL);
|
||||
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_2NDHALF);
|
||||
brw_math(p,
|
||||
offset(dst[dst_chan],1),
|
||||
function,
|
||||
(mask & SATURATE) ? BRW_MATH_SATURATE_SATURATE : BRW_MATH_SATURATE_NONE,
|
||||
4,
|
||||
brw_null_reg(),
|
||||
BRW_MATH_DATA_VECTOR,
|
||||
BRW_MATH_PRECISION_FULL);
|
||||
|
||||
/* Send two messages to perform all 16 operations:
|
||||
*/
|
||||
if (c->dispatch_width == 16) {
|
||||
brw_set_compression_control(p, BRW_COMPRESSION_2NDHALF);
|
||||
brw_math(p,
|
||||
offset(dst[dst_chan],1),
|
||||
function,
|
||||
saturate,
|
||||
4,
|
||||
brw_null_reg(),
|
||||
BRW_MATH_DATA_VECTOR,
|
||||
BRW_MATH_PRECISION_FULL);
|
||||
}
|
||||
brw_pop_insn_state(p);
|
||||
}
|
||||
|
||||
|
|
@ -909,11 +940,13 @@ static void emit_txb( struct brw_wm_compile *c,
|
|||
}
|
||||
|
||||
|
||||
static void emit_lit( struct brw_compile *p,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0 )
|
||||
static void emit_lit(struct brw_wm_compile *c,
|
||||
const struct brw_reg *dst,
|
||||
GLuint mask,
|
||||
const struct brw_reg *arg0)
|
||||
{
|
||||
struct brw_compile *p = &c->func;
|
||||
|
||||
assert((mask & WRITEMASK_XW) == 0);
|
||||
|
||||
if (mask & WRITEMASK_Y) {
|
||||
|
|
@ -923,7 +956,7 @@ static void emit_lit( struct brw_compile *p,
|
|||
}
|
||||
|
||||
if (mask & WRITEMASK_Z) {
|
||||
emit_math2(p, BRW_MATH_FUNCTION_POW,
|
||||
emit_math2(c, BRW_MATH_FUNCTION_POW,
|
||||
&dst[2],
|
||||
WRITEMASK_X | (mask & SATURATE),
|
||||
&arg0[1],
|
||||
|
|
@ -1380,27 +1413,27 @@ void brw_wm_emit( struct brw_wm_compile *c )
|
|||
/* Higher math functions:
|
||||
*/
|
||||
case OPCODE_RCP:
|
||||
emit_math1(p, BRW_MATH_FUNCTION_INV, dst, dst_flags, args[0]);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_INV, dst, dst_flags, args[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_RSQ:
|
||||
emit_math1(p, BRW_MATH_FUNCTION_RSQ, dst, dst_flags, args[0]);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_RSQ, dst, dst_flags, args[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_SIN:
|
||||
emit_math1(p, BRW_MATH_FUNCTION_SIN, dst, dst_flags, args[0]);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_SIN, dst, dst_flags, args[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_COS:
|
||||
emit_math1(p, BRW_MATH_FUNCTION_COS, dst, dst_flags, args[0]);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_COS, dst, dst_flags, args[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_EX2:
|
||||
emit_math1(p, BRW_MATH_FUNCTION_EXP, dst, dst_flags, args[0]);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_EXP, dst, dst_flags, args[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_LG2:
|
||||
emit_math1(p, BRW_MATH_FUNCTION_LOG, dst, dst_flags, args[0]);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_LOG, dst, dst_flags, args[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_SCS:
|
||||
|
|
@ -1408,13 +1441,13 @@ void brw_wm_emit( struct brw_wm_compile *c )
|
|||
* fixup for 16-element execution.
|
||||
*/
|
||||
if (dst_flags & WRITEMASK_X)
|
||||
emit_math1(p, BRW_MATH_FUNCTION_COS, dst, (dst_flags&SATURATE)|WRITEMASK_X, args[0]);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_COS, dst, (dst_flags&SATURATE)|WRITEMASK_X, args[0]);
|
||||
if (dst_flags & WRITEMASK_Y)
|
||||
emit_math1(p, BRW_MATH_FUNCTION_SIN, dst+1, (dst_flags&SATURATE)|WRITEMASK_X, args[0]);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_SIN, dst+1, (dst_flags&SATURATE)|WRITEMASK_X, args[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_POW:
|
||||
emit_math2(p, BRW_MATH_FUNCTION_POW, dst, dst_flags, args[0], args[1]);
|
||||
emit_math2(c, BRW_MATH_FUNCTION_POW, dst, dst_flags, args[0], args[1]);
|
||||
break;
|
||||
|
||||
/* Comparisons:
|
||||
|
|
@ -1452,7 +1485,7 @@ void brw_wm_emit( struct brw_wm_compile *c )
|
|||
break;
|
||||
|
||||
case OPCODE_LIT:
|
||||
emit_lit(p, dst, dst_flags, args[0]);
|
||||
emit_lit(c, dst, dst_flags, args[0]);
|
||||
break;
|
||||
|
||||
/* Texturing operations:
|
||||
|
|
|
|||
|
|
@ -550,42 +550,6 @@ static struct brw_reg get_src_reg(struct brw_wm_compile *c,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Same as \sa get_src_reg() but if the register is a literal, emit
|
||||
* a brw_reg encoding the literal.
|
||||
* Note that a brw instruction only allows one src operand to be a literal.
|
||||
* For instructions with more than one operand, only the second can be a
|
||||
* literal. This means that we treat some literals as constants/uniforms
|
||||
* (which why PROGRAM_CONSTANT is checked in fetch_constants()).
|
||||
*
|
||||
*/
|
||||
static struct brw_reg get_src_reg_imm(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst,
|
||||
GLuint srcRegIndex, GLuint channel)
|
||||
{
|
||||
const struct prog_src_register *src = &inst->SrcReg[srcRegIndex];
|
||||
if (src->File == PROGRAM_CONSTANT) {
|
||||
/* a literal */
|
||||
const int component = GET_SWZ(src->Swizzle, channel);
|
||||
const GLfloat *param =
|
||||
c->fp->program.Base.Parameters->ParameterValues[src->Index];
|
||||
GLfloat value = param[component];
|
||||
if (src->Negate & (1 << channel))
|
||||
value = -value;
|
||||
if (src->Abs)
|
||||
value = FABSF(value);
|
||||
#if 0
|
||||
printf(" form immed value %f for chan %d\n", value, channel);
|
||||
#endif
|
||||
return brw_imm_f(value);
|
||||
}
|
||||
else {
|
||||
return get_src_reg(c, inst, srcRegIndex, channel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subroutines are minimal support for resusable instruction sequences.
|
||||
* They are implemented as simply as possible to minimise overhead: there
|
||||
|
|
@ -1013,100 +977,6 @@ static void emit_frontfacing(struct brw_wm_compile *c,
|
|||
brw_set_predicate_control_flag_value(p, 0xff);
|
||||
}
|
||||
|
||||
static void emit_xpd(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst)
|
||||
{
|
||||
int i;
|
||||
struct brw_compile *p = &c->func;
|
||||
GLuint mask = inst->DstReg.WriteMask;
|
||||
for (i = 0; i < 4; i++) {
|
||||
GLuint i2 = (i+2)%3;
|
||||
GLuint i1 = (i+1)%3;
|
||||
if (mask & (1<<i)) {
|
||||
struct brw_reg src0, src1, dst;
|
||||
dst = get_dst_reg(c, inst, i);
|
||||
src0 = negate(get_src_reg(c, inst, 0, i2));
|
||||
src1 = get_src_reg_imm(c, inst, 1, i1);
|
||||
brw_MUL(p, brw_null_reg(), src0, src1);
|
||||
src0 = get_src_reg(c, inst, 0, i1);
|
||||
src1 = get_src_reg_imm(c, inst, 1, i2);
|
||||
brw_set_saturate(p, inst->SaturateMode != SATURATE_OFF);
|
||||
brw_MAC(p, dst, src0, src1);
|
||||
brw_set_saturate(p, 0);
|
||||
}
|
||||
}
|
||||
brw_set_saturate(p, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a scalar instruction, like RCP, RSQ, LOG, EXP.
|
||||
* Note that the result of the function is smeared across the dest
|
||||
* register's X, Y, Z and W channels (subject to writemasking of course).
|
||||
*/
|
||||
static void emit_math1(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst, GLuint func)
|
||||
{
|
||||
struct brw_compile *p = &c->func;
|
||||
struct brw_reg src0, dst;
|
||||
GLuint mask = inst->DstReg.WriteMask;
|
||||
int dst_chan = _mesa_ffs(mask & WRITEMASK_XYZW) - 1;
|
||||
|
||||
if (!(mask & WRITEMASK_XYZW))
|
||||
return;
|
||||
|
||||
assert(is_power_of_two(mask & WRITEMASK_XYZW));
|
||||
|
||||
/* Get first component of source register */
|
||||
dst = get_dst_reg(c, inst, dst_chan);
|
||||
src0 = get_src_reg(c, inst, 0, 0);
|
||||
|
||||
brw_MOV(p, brw_message_reg(2), src0);
|
||||
brw_math(p,
|
||||
dst,
|
||||
func,
|
||||
(inst->SaturateMode != SATURATE_OFF) ? BRW_MATH_SATURATE_SATURATE : BRW_MATH_SATURATE_NONE,
|
||||
2,
|
||||
brw_null_reg(),
|
||||
BRW_MATH_DATA_VECTOR,
|
||||
BRW_MATH_PRECISION_FULL);
|
||||
}
|
||||
|
||||
static void emit_rcp(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst)
|
||||
{
|
||||
emit_math1(c, inst, BRW_MATH_FUNCTION_INV);
|
||||
}
|
||||
|
||||
static void emit_rsq(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst)
|
||||
{
|
||||
emit_math1(c, inst, BRW_MATH_FUNCTION_RSQ);
|
||||
}
|
||||
|
||||
static void emit_sin(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst)
|
||||
{
|
||||
emit_math1(c, inst, BRW_MATH_FUNCTION_SIN);
|
||||
}
|
||||
|
||||
static void emit_cos(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst)
|
||||
{
|
||||
emit_math1(c, inst, BRW_MATH_FUNCTION_COS);
|
||||
}
|
||||
|
||||
static void emit_ex2(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst)
|
||||
{
|
||||
emit_math1(c, inst, BRW_MATH_FUNCTION_EXP);
|
||||
}
|
||||
|
||||
static void emit_lg2(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst)
|
||||
{
|
||||
emit_math1(c, inst, BRW_MATH_FUNCTION_LOG);
|
||||
}
|
||||
|
||||
static void emit_arl(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst)
|
||||
{
|
||||
|
|
@ -1169,36 +1039,6 @@ static void emit_min_max(struct brw_wm_compile *c,
|
|||
release_tmps(c, mark);
|
||||
}
|
||||
|
||||
static void emit_pow(struct brw_wm_compile *c,
|
||||
const struct prog_instruction *inst)
|
||||
{
|
||||
struct brw_compile *p = &c->func;
|
||||
struct brw_reg dst, src0, src1;
|
||||
GLuint mask = inst->DstReg.WriteMask;
|
||||
int dst_chan = _mesa_ffs(mask & WRITEMASK_XYZW) - 1;
|
||||
|
||||
if (!(mask & WRITEMASK_XYZW))
|
||||
return;
|
||||
|
||||
assert(is_power_of_two(mask & WRITEMASK_XYZW));
|
||||
|
||||
dst = get_dst_reg(c, inst, dst_chan);
|
||||
src0 = get_src_reg_imm(c, inst, 0, 0);
|
||||
src1 = get_src_reg_imm(c, inst, 1, 0);
|
||||
|
||||
brw_MOV(p, brw_message_reg(2), src0);
|
||||
brw_MOV(p, brw_message_reg(3), src1);
|
||||
|
||||
brw_math(p,
|
||||
dst,
|
||||
BRW_MATH_FUNCTION_POW,
|
||||
(inst->SaturateMode != SATURATE_OFF) ? BRW_MATH_SATURATE_SATURATE : BRW_MATH_SATURATE_NONE,
|
||||
2,
|
||||
brw_null_reg(),
|
||||
BRW_MATH_DATA_VECTOR,
|
||||
BRW_MATH_PRECISION_FULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* For GLSL shaders, this KIL will be unconditional.
|
||||
* It may be contained inside an IF/ENDIF structure of course.
|
||||
|
|
@ -2594,28 +2434,28 @@ static void brw_wm_emit_glsl(struct brw_context *brw, struct brw_wm_compile *c)
|
|||
emit_dp4(p, dst, dst_flags, args[0], args[1]);
|
||||
break;
|
||||
case OPCODE_XPD:
|
||||
emit_xpd(c, inst);
|
||||
emit_xpd(p, dst, dst_flags, args[0], args[1]);
|
||||
break;
|
||||
case OPCODE_DPH:
|
||||
emit_dph(p, dst, dst_flags, args[0], args[1]);
|
||||
break;
|
||||
case OPCODE_RCP:
|
||||
emit_rcp(c, inst);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_INV, dst, dst_flags, args[0]);
|
||||
break;
|
||||
case OPCODE_RSQ:
|
||||
emit_rsq(c, inst);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_RSQ, dst, dst_flags, args[0]);
|
||||
break;
|
||||
case OPCODE_SIN:
|
||||
emit_sin(c, inst);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_SIN, dst, dst_flags, args[0]);
|
||||
break;
|
||||
case OPCODE_COS:
|
||||
emit_cos(c, inst);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_COS, dst, dst_flags, args[0]);
|
||||
break;
|
||||
case OPCODE_EX2:
|
||||
emit_ex2(c, inst);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_EXP, dst, dst_flags, args[0]);
|
||||
break;
|
||||
case OPCODE_LG2:
|
||||
emit_lg2(c, inst);
|
||||
emit_math1(c, BRW_MATH_FUNCTION_LOG, dst, dst_flags, args[0]);
|
||||
break;
|
||||
case OPCODE_MIN:
|
||||
case OPCODE_MAX:
|
||||
|
|
@ -2654,7 +2494,8 @@ static void brw_wm_emit_glsl(struct brw_context *brw, struct brw_wm_compile *c)
|
|||
emit_alu2(p, brw_MUL, dst, dst_flags, args[0], args[1]);
|
||||
break;
|
||||
case OPCODE_POW:
|
||||
emit_pow(c, inst);
|
||||
emit_math2(c, BRW_MATH_FUNCTION_POW,
|
||||
dst, dst_flags, args[0], args[1]);
|
||||
break;
|
||||
case OPCODE_MAD:
|
||||
emit_mad(p, dst, dst_flags, args[0], args[1], args[2]);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue