<p dir="ltr"><br>
On Mar 15, 2016 7:48 AM, "Connor Abbott" <<a href="mailto:cwabbott0@gmail.com">cwabbott0@gmail.com</a>> wrote:<br>
><br>
> On Tue, Mar 15, 2016 at 10:43 AM, Connor Abbott <<a href="mailto:cwabbott0@gmail.com">cwabbott0@gmail.com</a>> wrote:<br>
> > On Tue, Mar 15, 2016 at 5:53 AM, Iago Toral <<a href="mailto:itoral@igalia.com">itoral@igalia.com</a>> wrote:<br>
> >> On Mon, 2016-03-14 at 16:48 -0700, Jason Ekstrand wrote:<br>
> >>><br>
> >>><br>
> >>> On Mon, Mar 7, 2016 at 12:46 AM, Samuel Iglesias Gonsálvez<br>
> >>> <<a href="mailto:siglesias@igalia.com">siglesias@igalia.com</a>> wrote:<br>
> >>> From: Connor Abbott <<a href="mailto:connor.w.abbott@intel.com">connor.w.abbott@intel.com</a>><br>
> >>><br>
> >>> v2: Use the bit-size information from the opcode information<br>
> >>> if defined (Iago)<br>
> >>><br>
> >>> Signed-off-by: Iago Toral Quiroga <<a href="mailto:itoral@igalia.com">itoral@igalia.com</a>><br>
> >>><br>
> >>> FIXME: This should be squashed into the previous commit so we<br>
> >>> don't break<br>
> >>> the build. The break happens because the python script that<br>
> >>> generates the<br>
> >>> constant folding pass does not know how to handle the sized<br>
> >>> types introduced<br>
> >>> by the previous commit until this patch, so it ends up<br>
> >>> generating code with<br>
> >>> invalid types. Keep it separated for review purposes.<br>
> >>> ---<br>
> >>> src/compiler/nir/nir_constant_expressions.h | 2 +-<br>
> >>> src/compiler/nir/nir_constant_expressions.py | 246<br>
> >>> +++++++++++++++++----------<br>
> >>> src/compiler/nir/nir_opt_constant_folding.c | 24 ++-<br>
> >>> 3 files changed, 182 insertions(+), 90 deletions(-)<br>
> >>><br>
> >>> diff --git a/src/compiler/nir/nir_constant_expressions.h<br>
> >>> b/src/compiler/nir/nir_constant_expressions.h<br>
> >>> index 97997f2..201f278 100644<br>
> >>> --- a/src/compiler/nir/nir_constant_expressions.h<br>
> >>> +++ b/src/compiler/nir/nir_constant_expressions.h<br>
> >>> @@ -28,4 +28,4 @@<br>
> >>> #include "nir.h"<br>
> >>><br>
> >>> nir_const_value nir_eval_const_opcode(nir_op op, unsigned<br>
> >>> num_components,<br>
> >>> - nir_const_value *src);<br>
> >>> + unsigned bit_size,<br>
> >>> nir_const_value *src);<br>
> >>> diff --git a/src/compiler/nir/nir_constant_expressions.py<br>
> >>> b/src/compiler/nir/nir_constant_expressions.py<br>
> >>> index 32784f6..972d281 100644<br>
> >>> --- a/src/compiler/nir/nir_constant_expressions.py<br>
> >>> +++ b/src/compiler/nir/nir_constant_expressions.py<br>
> >>> @@ -1,4 +1,43 @@<br>
> >>> #! /usr/bin/python2<br>
> >>> +<br>
> >>> +def type_has_size(type_):<br>
> >>> + return type_[-1:].isdigit()<br>
> >>> +<br>
> >>> +def type_sizes(type_):<br>
> >>> + if type_.endswith("8"):<br>
> >>> + return [8]<br>
> >>> + elif type_.endswith("16"):<br>
> >>> + return [16]<br>
> >>> + elif type_.endswith("32"):<br>
> >>> + return [32]<br>
> >>> + elif type_.endswith("64"):<br>
> >>> + return [64]<br>
> >>> + else:<br>
> >>> + return [32, 64]<br>
> >>> +<br>
> >>> +def type_add_size(type_, size):<br>
> >>> + if type_has_size(type_):<br>
> >>> + return type_<br>
> >>> + return type_ + str(size)<br>
> >>> +<br>
> >>> +def get_const_field(type_):<br>
> >>> + if type_ == "int32":<br>
> >>> + return "i"<br>
> >>> + if type_ == "uint32":<br>
> >>> + return "u"<br>
> >>> + if type_ == "int64":<br>
> >>> + return "l"<br>
> >>> + if type_ == "uint64":<br>
> >>> + return "ul"<br>
> >>> + if type_ == "bool32":<br>
> >>> + return "b"<br>
> >>> + if type_ == "float32":<br>
> >>> + return "f"<br>
> >>> + if type_ == "float64":<br>
> >>> + return "d"<br>
> >>> + raise Exception(str(type_))<br>
> >>> + assert(0)<br>
> >>> +<br>
> >>> template = """\<br>
> >>> /*<br>
> >>> * Copyright (C) 2014 Intel Corporation<br>
> >>> @@ -205,110 +244,140 @@ unpack_half_1x16(uint16_t u)<br>
> >>> }<br>
> >>><br>
> >>> /* Some typed vector structures to make things like src0.y<br>
> >>> work */<br>
> >>> -% for type in ["float", "int", "uint", "bool"]:<br>
> >>> -struct ${type}_vec {<br>
> >>> - ${type} x;<br>
> >>> - ${type} y;<br>
> >>> - ${type} z;<br>
> >>> - ${type} w;<br>
> >>> +typedef float float32_t;<br>
> >>> +typedef double float64_t;<br>
> >>> +typedef bool bool32_t;<br>
> >>> +% for type in ["float", "int", "uint"]:<br>
> >>> +% for width in [32, 64]:<br>
> >>> +struct ${type}${width}_vec {<br>
> >>> + ${type}${width}_t x;<br>
> >>> + ${type}${width}_t y;<br>
> >>> + ${type}${width}_t z;<br>
> >>> + ${type}${width}_t w;<br>
> >>> };<br>
> >>> % endfor<br>
> >>> +% endfor<br>
> >>> +<br>
> >>> +struct bool32_vec {<br>
> >>> + bool x;<br>
> >>> + bool y;<br>
> >>> + bool z;<br>
> >>> + bool w;<br>
> >>> +};<br>
> >>><br>
> >>> % for name, op in sorted(opcodes.iteritems()):<br>
> >>> static nir_const_value<br>
> >>> -evaluate_${name}(unsigned num_components, nir_const_value<br>
> >>> *_src)<br>
> >>> +evaluate_${name}(unsigned num_components, unsigned bit_size,<br>
> >>> + nir_const_value *_src)<br>
> >>> {<br>
> >>> nir_const_value _dst_val = { { {0, 0, 0, 0} } };<br>
> >>><br>
> >>> - ## For each non-per-component input, create a variable<br>
> >>> srcN that<br>
> >>> - ## contains x, y, z, and w elements which are filled in<br>
> >>> with the<br>
> >>> - ## appropriately-typed values.<br>
> >>> - % for j in range(op.num_inputs):<br>
> >>> - % if op.input_sizes[j] == 0:<br>
> >>> - <% continue %><br>
> >>> - % elif "src" + str(j) not in op.const_expr:<br>
> >>> - ## Avoid unused variable warnings<br>
> >>> - <% continue %><br>
> >>> - %endif<br>
> >>> -<br>
> >>> - struct ${op.input_types[j]}_vec src${j} = {<br>
> >>> - % for k in range(op.input_sizes[j]):<br>
> >>> - % if op.input_types[j] == "bool":<br>
> >>> - _src[${j}].u[${k}] != 0,<br>
> >>> - % else:<br>
> >>> - _src[${j}].${op.input_types[j][:1]}[${k}],<br>
> >>> - % endif<br>
> >>> - % endfor<br>
> >>> - };<br>
> >>> - % endfor<br>
> >>> + switch (bit_size) {<br>
> >>> + % for bit_size in [32, 64]:<br>
> >>> + case ${bit_size}: {<br>
> >>> + <%<br>
> >>> + output_type = type_add_size(op.output_type, bit_size)<br>
> >>> + input_types = [type_add_size(type_, bit_size) for type_<br>
> >>> in op.input_types]<br>
> >>> + %><br>
> >>> +<br>
> >>> + ## For each non-per-component input, create a variable<br>
> >>> srcN that<br>
> >>> + ## contains x, y, z, and w elements which are filled in<br>
> >>> with the<br>
> >>> + ## appropriately-typed values.<br>
> >>> + % for j in range(op.num_inputs):<br>
> >>> + % if op.input_sizes[j] == 0:<br>
> >>> + <% continue %><br>
> >>> + % elif "src" + str(j) not in op.const_expr:<br>
> >>> + ## Avoid unused variable warnings<br>
> >>> + <% continue %><br>
> >>> + %endif<br>
> >>><br>
> >>> - % if op.output_size == 0:<br>
> >>> - ## For per-component instructions, we need to iterate<br>
> >>> over the<br>
> >>> - ## components and apply the constant expression one<br>
> >>> component<br>
> >>> - ## at a time.<br>
> >>> - for (unsigned _i = 0; _i < num_components; _i++) {<br>
> >>> - ## For each per-component input, create a variable<br>
> >>> srcN that<br>
> >>> - ## contains the value of the current (_i'th)<br>
> >>> component.<br>
> >>> - % for j in range(op.num_inputs):<br>
> >>> - % if op.input_sizes[j] != 0:<br>
> >>> - <% continue %><br>
> >>> - % elif "src" + str(j) not in op.const_expr:<br>
> >>> - ## Avoid unused variable warnings<br>
> >>> - <% continue %><br>
> >>> - % elif op.input_types[j] == "bool":<br>
> >>> - bool src${j} = _src[${j}].u[_i] != 0;<br>
> >>> + struct ${input_types[j]}_vec src${j} = {<br>
> >>> + % for k in range(op.input_sizes[j]):<br>
> >>> + % if input_types[j] == "bool32":<br>
> >>> + _src[${j}].u[${k}] != 0,<br>
> >>> % else:<br>
> >>> - ${op.input_types[j]} src${j} =<br>
> >>> _src[${j}].${op.input_types[j][:1]}[_i];<br>
> >>> +<br>
> >>> _src[${j}].${get_const_field(input_types[j])}[${k}],<br>
> >>> % endif<br>
> >>> % endfor<br>
> >>> + };<br>
> >>> + % endfor<br>
> >>> +<br>
> >>> + % if op.output_size == 0:<br>
> >>> + ## For per-component instructions, we need to<br>
> >>> iterate over the<br>
> >>> + ## components and apply the constant expression one<br>
> >>> component<br>
> >>> + ## at a time.<br>
> >>> + for (unsigned _i = 0; _i < num_components; _i++) {<br>
> >>> + ## For each per-component input, create a<br>
> >>> variable srcN that<br>
> >>> + ## contains the value of the current (_i'th)<br>
> >>> component.<br>
> >>> + % for j in range(op.num_inputs):<br>
> >>> + % if op.input_sizes[j] != 0:<br>
> >>> + <% continue %><br>
> >>> + % elif "src" + str(j) not in op.const_expr:<br>
> >>> + ## Avoid unused variable warnings<br>
> >>> + <% continue %><br>
> >>> + % elif input_types[j] == "bool32":<br>
> >>> + bool src${j} = _src[${j}].u[_i] != 0;<br>
> >>> + % else:<br>
> >>> + ${input_types[j]}_t src${j} =<br>
> >>> +<br>
> >>> _src[${j}].${get_const_field(input_types[j])}[_i];<br>
> >>> + % endif<br>
> >>> + % endfor<br>
> >>> +<br>
> >>> + ## Create an appropriately-typed variable dst and<br>
> >>> assign the<br>
> >>> + ## result of the const_expr to it. If const_expr<br>
> >>> already contains<br>
> >>> + ## writes to dst, just include const_expr<br>
> >>> directly.<br>
> >>> + % if "dst" in op.const_expr:<br>
> >>> + ${output_type}_t dst;<br>
> >>> + ${op.const_expr}<br>
> >>> + % else:<br>
> >>> + ${output_type}_t dst = ${op.const_expr};<br>
> >>> + % endif<br>
> >>> +<br>
> >>> + ## Store the current component of the actual<br>
> >>> destination to the<br>
> >>> + ## value of dst.<br>
> >>> + % if output_type == "bool32":<br>
> >>> + ## Sanitize the C value to a proper NIR bool<br>
> >>> + _dst_val.u[_i] = dst ? NIR_TRUE : NIR_FALSE;<br>
> >>> + % else:<br>
> >>> + _dst_val.${get_const_field(output_type)}[_i] =<br>
> >>> dst;<br>
> >>> + % endif<br>
> >>> + }<br>
> >>> + % else:<br>
> >>> + ## In the non-per-component case, create a struct<br>
> >>> dst with<br>
> >>> + ## appropriately-typed elements x, y, z, and w and<br>
> >>> assign the result<br>
> >>> + ## of the const_expr to all components of dst, or<br>
> >>> include the<br>
> >>> + ## const_expr directly if it writes to dst already.<br>
> >>> + struct ${output_type}_vec dst;<br>
> >>><br>
> >>> - ## Create an appropriately-typed variable dst and<br>
> >>> assign the<br>
> >>> - ## result of the const_expr to it. If const_expr<br>
> >>> already contains<br>
> >>> - ## writes to dst, just include const_expr directly.<br>
> >>> % if "dst" in op.const_expr:<br>
> >>> - ${op.output_type} dst;<br>
> >>> ${op.const_expr}<br>
> >>> % else:<br>
> >>> - ${op.output_type} dst = ${op.const_expr};<br>
> >>> + ## Splat the value to all components. This way<br>
> >>> expressions which<br>
> >>> + ## write the same value to all components don't<br>
> >>> need to explicitly<br>
> >>> + ## write to dest. One such example is fnoise<br>
> >>> which has a<br>
> >>> + ## const_expr of 0.0f.<br>
> >>> + dst.x = dst.y = dst.z = dst.w = ${op.const_expr};<br>
> >>> % endif<br>
> >>><br>
> >>> - ## Store the current component of the actual<br>
> >>> destination to the<br>
> >>> - ## value of dst.<br>
> >>> - % if op.output_type == "bool":<br>
> >>> - ## Sanitize the C value to a proper NIR bool<br>
> >>> - _dst_val.u[_i] = dst ? NIR_TRUE : NIR_FALSE;<br>
> >>> - % else:<br>
> >>> - _dst_val.${op.output_type[:1]}[_i] = dst;<br>
> >>> - % endif<br>
> >>> - }<br>
> >>> - % else:<br>
> >>> - ## In the non-per-component case, create a struct dst<br>
> >>> with<br>
> >>> - ## appropriately-typed elements x, y, z, and w and<br>
> >>> assign the result<br>
> >>> - ## of the const_expr to all components of dst, or<br>
> >>> include the<br>
> >>> - ## const_expr directly if it writes to dst already.<br>
> >>> - struct ${op.output_type}_vec dst;<br>
> >>> -<br>
> >>> - % if "dst" in op.const_expr:<br>
> >>> - ${op.const_expr}<br>
> >>> - % else:<br>
> >>> - ## Splat the value to all components. This way<br>
> >>> expressions which<br>
> >>> - ## write the same value to all components don't need<br>
> >>> to explicitly<br>
> >>> - ## write to dest. One such example is fnoise which<br>
> >>> has a<br>
> >>> - ## const_expr of 0.0f.<br>
> >>> - dst.x = dst.y = dst.z = dst.w = ${op.const_expr};<br>
> >>> + ## For each component in the destination, copy the<br>
> >>> value of dst to<br>
> >>> + ## the actual destination.<br>
> >>> + % for k in range(op.output_size):<br>
> >>> + % if output_type == "bool32":<br>
> >>> + ## Sanitize the C value to a proper NIR bool<br>
> >>> + _dst_val.u[${k}] = dst.${"xyzw"[k]} ?<br>
> >>> NIR_TRUE : NIR_FALSE;<br>
> >>> + % else:<br>
> >>> + _dst_val.${get_const_field(output_type)}[${k}]<br>
> >>> = dst.${"xyzw"[k]};<br>
> >>> + % endif<br>
> >>> + % endfor<br>
> >>> % endif<br>
> >>><br>
> >>> - ## For each component in the destination, copy the<br>
> >>> value of dst to<br>
> >>> - ## the actual destination.<br>
> >>> - % for k in range(op.output_size):<br>
> >>> - % if op.output_type == "bool":<br>
> >>> - ## Sanitize the C value to a proper NIR bool<br>
> >>> - _dst_val.u[${k}] = dst.${"xyzw"[k]} ? NIR_TRUE :<br>
> >>> NIR_FALSE;<br>
> >>> - % else:<br>
> >>> - _dst_val.${op.output_type[:1]}[${k}] =<br>
> >>> dst.${"xyzw"[k]};<br>
> >>> - % endif<br>
> >>> - % endfor<br>
> >>> - % endif<br>
> >>> + break;<br>
> >>> + }<br>
> >>> + % endfor<br>
> >>> +<br>
> >>> + default:<br>
> >>> + unreachable("unknown bit width");<br>
> >>> + }<br>
> >>><br>
> >>> return _dst_val;<br>
> >>> }<br>
> >>> @@ -316,12 +385,12 @@ evaluate_${name}(unsigned<br>
> >>> num_components, nir_const_value *_src)<br>
> >>><br>
> >>> nir_const_value<br>
> >>> nir_eval_const_opcode(nir_op op, unsigned num_components,<br>
> >>> - nir_const_value *src)<br>
> >>> + unsigned bit_width, nir_const_value<br>
> >>> *src)<br>
> >>> {<br>
> >>> switch (op) {<br>
> >>> % for name in sorted(opcodes.iterkeys()):<br>
> >>> case nir_op_${name}: {<br>
> >>> - return evaluate_${name}(num_components, src);<br>
> >>> + return evaluate_${name}(num_components, bit_width,<br>
> >>> src);<br>
> >>> break;<br>
> >>> }<br>
> >>> % endfor<br>
> >>> @@ -333,4 +402,7 @@ nir_eval_const_opcode(nir_op op, unsigned<br>
> >>> num_components,<br>
> >>> from nir_opcodes import opcodes<br>
> >>> from mako.template import Template<br>
> >>><br>
> >>> -print Template(template).render(opcodes=opcodes)<br>
> >>> +print Template(template).render(opcodes=opcodes,<br>
> >>> type_sizes=type_sizes,<br>
> >>> + type_has_size=type_has_size,<br>
> >>> + type_add_size=type_add_size,<br>
> >>> +<br>
> >>> get_const_field=get_const_field)<br>
> >>> diff --git a/src/compiler/nir/nir_opt_constant_folding.c<br>
> >>> b/src/compiler/nir/nir_opt_constant_folding.c<br>
> >>> index 04876a4..29905a0 100644<br>
> >>> --- a/src/compiler/nir/nir_opt_constant_folding.c<br>
> >>> +++ b/src/compiler/nir/nir_opt_constant_folding.c<br>
> >>> @@ -46,10 +46,23 @@ constant_fold_alu_instr(nir_alu_instr<br>
> >>> *instr, void *mem_ctx)<br>
> >>> if (!instr->dest.dest.is_ssa)<br>
> >>> return false;<br>
> >>><br>
> >>> + unsigned bit_size = 0;<br>
> >>> + if (!(nir_op_infos[instr->op].output_type &<br>
> >>> NIR_ALU_TYPE_SIZE_MASK))<br>
> >>> + bit_size = instr->dest.dest.ssa.bit_size;<br>
> >>> + else<br>
> >>> + bit_size = nir_op_infos[instr->op].output_type &<br>
> >>> NIR_ALU_TYPE_SIZE_MASK;<br>
> >>><br>
> >>><br>
> >>> This isn't right. We need to look at all the unsized types and try to<br>
> >>> pull it from one of those. We shouldn't fall back to grabbing from<br>
> >>> the sized type.<br>
> >><br>
> >> Ok, so you don't like that in the case that the alu operation has a<br>
> >> sized destination we grab the bit-size from the opcode definition? I am<br>
> >> not sure I see the problem with that... isn't the opcode mandating a<br>
> >> specific bit-size in that case? How can the bit-size we want be<br>
> >> different from that?<br>
> >><br>
> >>><br>
> >>> +<br>
> >>> for (unsigned i = 0; i <<br>
> >>> nir_op_infos[instr->op].num_inputs; i++) {<br>
> >>> if (!instr->src[i].src.is_ssa)<br>
> >>> return false;<br>
> >>><br>
> >>> + if (bit_size == 0) {<br>
> >>> + if (!(nir_op_infos[instr->op].input_sizes[i] &<br>
> >>> NIR_ALU_TYPE_SIZE_MASK))<br>
> >>> + bit_size = instr->src[i].src.ssa->bit_size;<br>
> >>> + else<br>
> >>> + bit_size = nir_op_infos[instr->op].input_sizes[i]<br>
> >>> & NIR_ALU_TYPE_SIZE_MASK;<br>
> >>><br>
> >>><br>
> >>> Same here. If they don't have any unsized sources or destinations to<br>
> >>> grab from, we should let bit_size be zero.<br>
> >><br>
> >> But if we have an opcode with all sized 64-bit types then...<br>
> >><br>
> >>> Also, if we have multiple sources with the same unsized type, we<br>
> >>> should assert that the sizes match.<br>
> >>><br>
> >>><br>
> >>> + }<br>
> >>> +<br>
> >>> nir_instr *src_instr =<br>
> >>> instr->src[i].src.ssa->parent_instr;<br>
> >>><br>
> >>> if (src_instr->type != nir_instr_type_load_const)<br>
> >>> @@ -58,24 +71,31 @@ constant_fold_alu_instr(nir_alu_instr<br>
> >>> *instr, void *mem_ctx)<br>
> >>><br>
> >>> for (unsigned j = 0; j <<br>
> >>> nir_ssa_alu_instr_src_components(instr, i);<br>
> >>> j++) {<br>
> >>> - src[i].u[j] =<br>
> >>> load_const->value.u[instr->src[i].swizzle[j]];<br>
> >>> + if (load_const->def.bit_size == 64)<br>
> >>> + src[i].ul[j] =<br>
> >>> load_const->value.ul[instr->src[i].swizzle[j]];<br>
> >>> + else<br>
> >>> + src[i].u[j] =<br>
> >>> load_const->value.u[instr->src[i].swizzle[j]];<br>
> >>> }<br>
> >>><br>
> >>> /* We shouldn't have any source modifiers in the<br>
> >>> optimization loop. */<br>
> >>> assert(!instr->src[i].abs && !instr->src[i].negate);<br>
> >>> }<br>
> >>><br>
> >>> + if (bit_size == 0)<br>
> >>> + bit_size = 32;<br>
> >><br>
> >> ... this default to 32 here would not be correct any more. If at this<br>
> >> point the bit-size is 0 (meaning that all inputs and output are sized)<br>
> >> then we should take the bit-size from the opcode's output type, which is<br>
> >> known to be sized, right?<br>
> ><br>
> > Not quite. xd<br>
><br>
> Err, whoops...<br>
><br>
> Not quite. If any of the operands or the destination is already sized<br>
> by the opcode, then the constant propagation code we generated has all<br>
> of the information it needs. The only case where we need to figure<br>
> anything out is if there are unsized types, in which case we know the<br>
> bitsizes match -- we just need to find the first unsized type and pass<br>
> the bitsize of that. If all the operands and the destination are<br>
> sized, then the bit_size will be ignored and it's fine if it's 0.</p>
<p dir="ltr">More to the point, you could have an instruction with, say, two unsized inputs and a sized destination (comparison operations are an example of this). In this case, the two sources have to match but they don't have to match the destination; the destination size is fixed. Dies that make sense?</p>
<p dir="ltr">> ><br>
> >><br>
> >>><br>
> >>> Then it'll get set here.<br>
> >>><br>
> >>><br>
> >>> +<br>
> >>> /* We shouldn't have any saturate modifiers in the<br>
> >>> optimization loop. */<br>
> >>> assert(!instr->dest.saturate);<br>
> >>><br>
> >>> nir_const_value dest =<br>
> >>> nir_eval_const_opcode(instr->op,<br>
> >>> instr->dest.dest.ssa.num_components,<br>
> >>> - src);<br>
> >>> + bit_size, src);<br>
> >>><br>
> >>> nir_load_const_instr *new_instr =<br>
> >>> nir_load_const_instr_create(mem_ctx,<br>
> >>><br>
> >>> instr->dest.dest.ssa.num_components);<br>
> >>><br>
> >>> + new_instr->def.bit_size = instr->dest.dest.ssa.bit_size;<br>
> >>> new_instr->value = dest;<br>
> >>><br>
> >>> nir_instr_insert_before(&instr->instr, &new_instr->instr);<br>
> >>> --<br>
> >>> 2.7.0<br>
> >>><br>
> >>> _______________________________________________<br>
> >>> mesa-dev mailing list<br>
> >>> <a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
> >>> <a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev">https://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
> >>><br>
> >>><br>
> >>> _______________________________________________<br>
> >>> mesa-dev mailing list<br>
> >>> <a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
> >>> <a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev">https://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
> >><br>
> >><br>
> >> _______________________________________________<br>
> >> mesa-dev mailing list<br>
> >> <a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
> >> <a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev">https://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</p>