From ff373a83a4547d0bef6c22644ad4fc918d632f74 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sat, 20 Feb 2016 20:44:11 -0800 Subject: [PATCH] Private->Function variables, fragment output, geometry shader tweaks. --- src/xenia/gpu/spirv_shader_translator.cc | 42 ++++++++++++------ src/xenia/gpu/spirv_shader_translator.h | 9 ++-- .../vulkan/shaders/bin/line_quad_list_geom.h | 4 +- .../shaders/bin/line_quad_list_geom.spv | Bin 2344 -> 0 bytes .../shaders/bin/line_quad_list_geom.txt | 4 +- .../gpu/vulkan/shaders/bin/point_list_geom.h | 4 +- .../vulkan/shaders/bin/point_list_geom.spv | Bin 2420 -> 0 bytes .../vulkan/shaders/bin/point_list_geom.txt | 4 +- .../gpu/vulkan/shaders/bin/quad_list_geom.h | 4 +- .../gpu/vulkan/shaders/bin/quad_list_geom.spv | Bin 2084 -> 0 bytes .../gpu/vulkan/shaders/bin/quad_list_geom.txt | 4 +- .../gpu/vulkan/shaders/bin/rect_list_geom.h | 4 +- .../gpu/vulkan/shaders/bin/rect_list_geom.spv | Bin 5204 -> 0 bytes .../gpu/vulkan/shaders/bin/rect_list_geom.txt | 4 +- .../gpu/vulkan/shaders/line_quad_list.geom | 6 +-- src/xenia/gpu/vulkan/shaders/point_list.geom | 6 +-- src/xenia/gpu/vulkan/shaders/quad_list.geom | 6 +-- src/xenia/gpu/vulkan/shaders/rect_list.geom | 6 +-- 18 files changed, 60 insertions(+), 47 deletions(-) delete mode 100644 src/xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.spv delete mode 100644 src/xenia/gpu/vulkan/shaders/bin/point_list_geom.spv delete mode 100644 src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.spv delete mode 100644 src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.spv diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc index f8cf69af9..f46d76bbd 100644 --- a/src/xenia/gpu/spirv_shader_translator.cc +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -83,19 +83,19 @@ void SpirvShaderTranslator::StartTranslation() { registers_type_ = b.makeArrayType(vec4_float_type_, b.makeUintConstant(64), 0); - registers_ptr_ = b.createVariable(spv::StorageClass::StorageClassPrivate, + registers_ptr_ = b.createVariable(spv::StorageClass::StorageClassFunction, registers_type_, "r"); - aL_ = b.createVariable(spv::StorageClass::StorageClassPrivate, + aL_ = b.createVariable(spv::StorageClass::StorageClassFunction, vec4_uint_type_, "aL"); - p0_ = b.createVariable(spv::StorageClass::StorageClassPrivate, bool_type_, + p0_ = b.createVariable(spv::StorageClass::StorageClassFunction, bool_type_, "p0"); - ps_ = b.createVariable(spv::StorageClass::StorageClassPrivate, float_type_, + ps_ = b.createVariable(spv::StorageClass::StorageClassFunction, float_type_, "ps"); - pv_ = b.createVariable(spv::StorageClass::StorageClassPrivate, + pv_ = b.createVariable(spv::StorageClass::StorageClassFunction, vec4_float_type_, "pv"); - a0_ = b.createVariable(spv::StorageClass::StorageClassPrivate, + a0_ = b.createVariable(spv::StorageClass::StorageClassFunction, b.makeUintType(32), "a0"); // Uniform constants. @@ -110,7 +110,7 @@ void SpirvShaderTranslator::StartTranslation() { {float_consts_type, loop_consts_type, bool_consts_type}, "consts_type"); b.addDecoration(consts_struct_type, spv::Decoration::DecorationBlock); - // Constants member decorations + // Constants member decorations. b.addMemberDecoration(consts_struct_type, 0, spv::Decoration::DecorationOffset, 0); b.addMemberDecoration(consts_struct_type, 0, @@ -144,11 +144,11 @@ void SpirvShaderTranslator::StartTranslation() { b.addDecoration(consts_, spv::Decoration::DecorationBinding, 1); } - // Interpolators + // Interpolators. Id interpolators_type = b.makeArrayType(vec4_float_type_, b.makeUintConstant(16), 0); if (is_vertex_shader()) { - // Vertex inputs/outputs + // Vertex inputs/outputs. for (const auto& binding : vertex_bindings()) { for (const auto& attrib : binding.attributes) { Id attrib_type = 0; @@ -204,13 +204,22 @@ void SpirvShaderTranslator::StartTranslation() { b.addDecoration(pos_, spv::Decoration::DecorationBuiltIn, spv::BuiltIn::BuiltInPosition); } else { - // Pixel inputs/outputs + // Pixel inputs from vertex shader. interpolators_ = b.createVariable(spv::StorageClass::StorageClassInput, interpolators_type, "interpolators"); b.addDecoration(interpolators_, spv::Decoration::DecorationNoPerspective); b.addDecoration(interpolators_, spv::Decoration::DecorationLocation, 0); - // Copy interpolators to r[0..16] + // Pixel fragment outputs (one per render target). + Id frag_outputs_type = + b.makeArrayType(vec4_float_type_, b.makeUintConstant(4), 0); + frag_outputs_ = b.createVariable(spv::StorageClass::StorageClassOutput, + frag_outputs_type, "o"); + b.addDecoration(frag_outputs_, spv::Decoration::DecorationLocation, 0); + + // TODO(benvanik): frag depth, etc. + + // Copy interpolators to r[0..16]. b.createNoResultOp(spv::Op::OpCopyMemorySized, {registers_ptr_, interpolators_, b.makeUintConstant(16 * 4 * sizeof(float))}); @@ -916,7 +925,7 @@ Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) { switch (op.storage_source) { case InstructionStorageSource::kRegister: storage_pointer = registers_ptr_; - storage_class = spv::StorageClass::StorageClassPrivate; + storage_class = spv::StorageClass::StorageClassFunction; storage_type = vec4_float_type_; storage_offsets.push_back(storage_index); break; @@ -1040,7 +1049,7 @@ void SpirvShaderTranslator::StoreToResult(Id source_value_id, switch (result.storage_target) { case InstructionStorageTarget::kRegister: storage_pointer = registers_ptr_; - storage_class = spv::StorageClass::StorageClassPrivate; + storage_class = spv::StorageClass::StorageClassFunction; storage_type = vec4_float_type_; storage_offsets.push_back(storage_index); storage_array = true; @@ -1068,7 +1077,12 @@ void SpirvShaderTranslator::StoreToResult(Id source_value_id, break; case InstructionStorageTarget::kColorTarget: assert_true(is_pixel_shader()); - // TODO(benvanik): result.storage_index + assert_not_zero(frag_outputs_); + storage_pointer = frag_outputs_; + storage_class = spv::StorageClass::StorageClassOutput; + storage_type = vec4_float_type_; + storage_offsets.push_back(storage_index); + storage_array = true; break; case InstructionStorageTarget::kDepth: assert_true(is_pixel_shader()); diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h index f56325fc6..aabd6fec1 100644 --- a/src/xenia/gpu/spirv_shader_translator.h +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -54,8 +54,6 @@ class SpirvShaderTranslator : public ShaderTranslator { void ProcessAluInstruction(const ParsedAluInstruction& instr) override; private: - void SetupPushConstants(); - void ProcessVectorAluInstruction(const ParsedAluInstruction& instr); void ProcessScalarAluInstruction(const ParsedAluInstruction& instr); @@ -80,22 +78,23 @@ class SpirvShaderTranslator : public ShaderTranslator { std::unique_ptr builder_; spv::Id glsl_std_450_instruction_set_ = 0; - // Types + // Types. spv::Id float_type_ = 0, bool_type_ = 0; spv::Id vec2_float_type_ = 0, vec3_float_type_ = 0, vec4_float_type_ = 0; spv::Id vec4_uint_type_ = 0; spv::Id vec4_bool_type_ = 0; - // Constants + // Constants. spv::Id vec4_float_zero_ = 0, vec4_float_one_ = 0; - // Array of AMD registers + // Array of AMD registers. // These values are all pointers. spv::Id registers_ptr_ = 0, registers_type_ = 0; spv::Id consts_ = 0, a0_ = 0, aL_ = 0, p0_ = 0; spv::Id ps_ = 0, pv_ = 0; // IDs of previous results spv::Id pos_ = 0; spv::Id interpolators_ = 0; + spv::Id frag_outputs_ = 0; // Map of {binding -> {offset -> spv input}} std::map> vertex_binding_map_; diff --git a/src/xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.h b/src/xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.h index cb3511e37..af848e905 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.h +++ b/src/xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.h @@ -62,12 +62,12 @@ const uint8_t line_quad_list_geom[] = { 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, diff --git a/src/xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.spv b/src/xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.spv deleted file mode 100644 index c8ade8408f0e7a26a1eff170df2428d68b8686c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2344 zcmZ{l>rPWq5QR6i1rg;U0$vb{ct;VnAc~*}T2wS9B>p9hHs&PQ8hSLu$MZ4tMf_vp z`u6FHJE*4_XV%Q@nOU>X!BX#Re+WJL9SN^P@r;IE{X#&W>gsA&FG&xEKIMl{-`?4t z@8s|2Yl{nlVJMVPF*Y0qA}R}iY-BC`$0a8v7bRCEBg!B?9_@0pWybT4O(n52vMZ93 ze$4kxLsWDs?NQNegNl9F+kMmAf7{&8n+JmO7Jh7E?M{|w?Uua#F&{qBpSALx>}!)e z$LOWl#$NVmGwbAy*1M+qmLebW=s8m4{M8db&*7f<|JRe)zk8PB{!}Y2w4%-zlv}Xe zY~&4QE=Ql-p)FxQ?7RIr-~Eyw2v@=-;lpR}@%Rk)leOZJu%$EXl*igWIN(Fu$L#oc z8|wNO8(&?sV4sulfO#j3Iq!Xu$7A2&KHgE<$3D)m7oKSeXX%B{y}<0nm^0pi&x~nm z=AID;xL{An>XWWYCRIZpPsGK0T-H}q#H7NU;@|-ZGfW8|mF7IY>F!=#gIHBL$E4{8 zx0NntPx!`#hoq|#YSIHdEa9Ohdq`_~j#2rUlQ~XElZTIaPfsx4Dn8kTpcqUA5=1)1)g^8KNmh6-Rm>hF>F57id4lm><=6iT4 fJLLc-$M>);8xEd@dw3WV|{;6LG@9P&f@5Odyty*8KM&sV#r+%)%-oSIC*0c|OeN+cqlYLZ> zH65yb|5uZ^=f5?Hzo{l|udk_WfS$eP+<-JG~ z@Gi8E9FuT$_qWUU6kJT&gZ@^=`QjWadi41|`c`n`#b(N!Bd0cfw!xjDKHjk%ZcpOp zi|+1{)w`+G&M`)*TH9c$k3{}|aB>}E=z z!0qJ_yxEJn4ud&MZPu>t+le{Pqj2wW)-kWokj?EZ=HU8>Y1j92D5XcwLO)UKmyT0| zz9%7l!~;83^d|O2@zXc_P8Yu#o6Ps00W+sLjM2y5zJ%MaHL8DAbZbz*3b#*vqPJ_v zzMZ{&S8QT4cwUE#8T&olSTX0O?*5AD>+b4no||y>*q2+#n;>yx)mI8;?%PE+<}5RF z_Ria5{=Y#waAT+7(~v!=yF31!4Bg%EHx;@w@$Z)U0UqZJ^nBD9&2_^?$!c#x;tM(V*2QB zuJe$%y8V9x+4mD-v|oVq5r15;i^$fXEo!`k+^lgPT}<-*efQhMd&dG~4o_g0k;UvK zuq(*(kSDN3~GyehEnLpxA-}*;L%)N@;5o_*F7yU1C{sWzZ{sk~OpBVrE diff --git a/src/xenia/gpu/vulkan/shaders/bin/point_list_geom.txt b/src/xenia/gpu/vulkan/shaders/bin/point_list_geom.txt index 0eecef563..ea6523102 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/point_list_geom.txt +++ b/src/xenia/gpu/vulkan/shaders/bin/point_list_geom.txt @@ -46,10 +46,10 @@ OpDecorate %39 Block OpDecorate %39 Stream 0 OpDecorate %41 Stream 0 - OpMemberDecorate %74 0 Location 1 + OpMemberDecorate %74 0 Location 0 OpDecorate %74 Stream 0 OpDecorate %76 Stream 0 - OpMemberDecorate %77 0 Location 1 + OpMemberDecorate %77 0 Location 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 diff --git a/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.h b/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.h index 7a27bde3a..f168ce835 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.h +++ b/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.h @@ -67,11 +67,11 @@ const uint8_t quad_list_geom[] = { 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, diff --git a/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.spv b/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.spv deleted file mode 100644 index a57165e8afcbae1d14f3f1d47613a26fa566156b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2084 zcmZ{l*-lhJ5QZCO0Yq73b3q)$9mEX~MHB?Z3ocAZyd|Sd%t>&Dz-WwDzLKxtW9WMr zH8Jt~dQOuGYM&HU_1DsMs_G1d&Z({tI(WLnv(P$&p_3;B`Kg>&a=rlX4Q2d8*x20K zoIPmn%+Af-n-^@R^O?8h?qH}R(-Wwi+<&B5&mX(`$cX4Wo^HS zBd5$G?$Opb$eLNB4%!uQdl2!g-rUMQn(EMIec0A^v%U4~pjoZIu5ouE_CUW9o>y8v zf88nWIeMq~|L)ZG?>mdJ=j4h*uqXHNZN24swOO_1V#I($1G1;is6p4>H(Nb>h<-Fi z%#&iAX;zOR;W=4S?|P$GIK+GhF?sT{b}=_-gTKa#ZBUD{GxMmsM@ct6^{f4!)T{P; zQjgm2Ntk);i)R9|Ht!Nkcd6T#q#KiZkYa|Y+PWus2j4|`gt#)i0-3jh-UXM$r*nG` zvM1>S$VF(FEQ4_K48tpsM?ZDHAN|JpI|?_aSjsbo?7d0%o3^&3pGLOM87eZ~@61|^ z(^tDSkHf85pHVVY;L;ej$y~OMRM!RM4#>Kua=X|>z8fi~Wh;hD*sRNWFA<~eti_CR zR_!t7y$UAo`QxOa-1L*3sTG3_(CJelh^;pR{Jo!nl5>pu_IuAbiO9wOAwcl0&SGTdEC@9_w^0}?k@-S11x+^e~4%mTe}e)isD z{vRMExUtU6-$Z%R{asADb9JZH9}%su^Z!YXV{miHzmw}Y`~>9D-Urt%U&ik4^+V?P zOa<=k$sDt1zwlA_8#I@($|?3Nva!}-P440orK+cy~$tD_h|#oQwUGr=07^_VW`pIGd-CSgJvr PSo^G<%QtfVh8Ceea|?_> diff --git a/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.txt b/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.txt index 4a1ed2b02..ee4a83586 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.txt +++ b/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.txt @@ -45,10 +45,10 @@ OpMemberDecorate %39 1 BuiltIn PointSize OpMemberDecorate %39 2 BuiltIn ClipDistance OpDecorate %39 Block - OpMemberDecorate %57 0 Location 1 + OpMemberDecorate %57 0 Location 0 OpDecorate %57 Stream 0 OpDecorate %59 Stream 0 - OpMemberDecorate %60 0 Location 1 + OpMemberDecorate %60 0 Location 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 diff --git a/src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.h b/src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.h index 511aeb2d0..b9598cfa9 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.h +++ b/src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.h @@ -66,12 +66,12 @@ const uint8_t rect_list_geom[] = { 0x20, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, diff --git a/src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.spv b/src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.spv deleted file mode 100644 index 8074f71f5d7169723c5b2ea2533ccd7f0d20bc8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5204 zcmZ{m2a{Dr5QVSqvgU|lhNmcIlvObY6vcoMbHY^?e7fu^JQX8g&N=6tvzT*^zd-+o zsmdz9@4kDgrdGUE@0`=!(=(@M?tS#EIB}(_T7iEnSHD!w%nKOCU-FAjnYgRoDb!@F_m74X!=MS`p^{*1gK`-)ABVOF`u7&2BSHZ(Z5GkajDJ#Tn$4wSq;_E%N&hGri# zxZuRW1)ae~`uiHc*x1OzcBefujCZ9vU!AqTJ=~eqzHHFE#M}e1gNE7{Om8pj3=E$$ zSno%j_-oOw+v@WFtgGK=^tvs4|F^EO<*(aQ_m(hw7*p-U*i|`wpfli}duyNZkvgZh zK8KNuIm&;6{!Xg&RVH{83c&pnJ~Y&G0^^7SyE)L*|#>w3P&&UIhO z-w13U++&Ne+@pKg9H09$<{sppP1o03O$4uiInVq_O-!COwgH=CeETMzJ=n(`F*VwQ z$&Aj`H=b2IV|~u`Bj&pL+>6{Z*JrNhZLW28VU(wryMo<=e&@=4SF$g^n_}mXaJx#cwPc z_5+t11r3QI7r+W_5;i;4Ly>|y= z#-ia6uyxFFPjYjjVFp+Y?kDGPC|G|q$kp&$aTbT+%lhS69FE^ubQ}RLb;!-hS29e!8k z=VI1!t~hf?z|IZ7py7V^CxS1;Jk#{fxViRv0cL!0!xuKb)at;komv-xb3d$SJhd(c z8_zjj0=Az1)LR5D>s<=BUhahP)VmC9JoPRITT{RHViMyO*c8mY*ay!-pSkwhzS}Ry z?BPnVdssrR?fq4lKF>a9sjoas`=0NQ{t0mFE@sRfT>@U-9bJmwSnlK1VDFK8c0IW{ z))~im4VF6PeZ01b!`h-@dix4>)JPY`7YlGcWu|rJnzX( zn6YTM8C+_Rn-dMUfK#W`a4UXe(Qq5M)F3w}8g2*YzD2_w`1D7Eywq?f+_iJ3a-Z(P zXDk}-2A3M-=0w9i;M6HK+>75>G~5R+HOS40hWo*4a6i%T09b!C$aBAPpB}{L`kq61 z77xLVMaRS7Qit4}=y(L2I;DbAo*rhN z_vtCjSTsBhE;Y!_iH2vusZ(lr7QeA*cn(}@ked??&x1#w#S8fKM}xf7@FG0@D>b}? z-&oG;Ny|1G$z{~g@=xf8}y|9i0U)cXN!P5qwTB*q`HDVVzKgJ+@7 sTzhTb?U!Tr@DtcQ