From 78923902089d0a70c096e5f47f42e24fbcd4fa56 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte <jocelyn.turcotte@digia.com> Date: Tue, 1 Jul 2014 19:19:56 +0200 Subject: [PATCH] Expose the scene graph publically through a QSGEngine class This change wraps QSGRenderContext and QSGContext in a new QSGEngine class, and expose a public interface of QSGRenderer through a QSGAbstractRenderer to make it usable on a standalone window or FBO. Change-Id: I2d41187472424f5ea64650a006bcd61f2711f6b9 Reviewed-by: Gunnar Sletta <gunnar.sletta@jollamobile.com> --- examples/quick/scenegraph/scenegraph.pro | 1 + .../quick/scenegraph/sgengine/face-smile.png | Bin 0 -> 15408 bytes examples/quick/scenegraph/sgengine/main.cpp | 52 +++ .../quick/scenegraph/sgengine/sgengine.pro | 11 + .../quick/scenegraph/sgengine/sgengine.qrc | 5 + examples/quick/scenegraph/sgengine/window.cpp | 241 +++++++++++++ examples/quick/scenegraph/sgengine/window.h | 85 +++++ src/quick/items/qquickwindow.cpp | 4 +- .../coreapi/qsgabstractrenderer.cpp | 318 ++++++++++++++++++ .../scenegraph/coreapi/qsgabstractrenderer.h | 103 ++++++ .../coreapi/qsgabstractrenderer_p.h | 77 +++++ src/quick/scenegraph/coreapi/qsgnode.h | 5 +- src/quick/scenegraph/coreapi/qsgrenderer.cpp | 96 ++---- src/quick/scenegraph/coreapi/qsgrenderer_p.h | 65 +--- src/quick/scenegraph/qsgcontext.cpp | 22 +- src/quick/scenegraph/qsgcontext_p.h | 3 + src/quick/scenegraph/scenegraph.pri | 6 + src/quick/scenegraph/util/qsgengine.cpp | 204 +++++++++++ src/quick/scenegraph/util/qsgengine.h | 80 +++++ src/quick/scenegraph/util/qsgengine_p.h | 66 ++++ 20 files changed, 1304 insertions(+), 140 deletions(-) create mode 100644 examples/quick/scenegraph/sgengine/face-smile.png create mode 100644 examples/quick/scenegraph/sgengine/main.cpp create mode 100644 examples/quick/scenegraph/sgengine/sgengine.pro create mode 100644 examples/quick/scenegraph/sgengine/sgengine.qrc create mode 100644 examples/quick/scenegraph/sgengine/window.cpp create mode 100644 examples/quick/scenegraph/sgengine/window.h create mode 100644 src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp create mode 100644 src/quick/scenegraph/coreapi/qsgabstractrenderer.h create mode 100644 src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h create mode 100644 src/quick/scenegraph/util/qsgengine.cpp create mode 100644 src/quick/scenegraph/util/qsgengine.h create mode 100644 src/quick/scenegraph/util/qsgengine_p.h diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro index 4354954445..85ae25907a 100644 --- a/examples/quick/scenegraph/scenegraph.pro +++ b/examples/quick/scenegraph/scenegraph.pro @@ -2,6 +2,7 @@ TEMPLATE = subdirs SUBDIRS += \ customgeometry \ openglunderqml \ + sgengine \ simplematerial \ textureinsgnode \ textureinthread \ diff --git a/examples/quick/scenegraph/sgengine/face-smile.png b/examples/quick/scenegraph/sgengine/face-smile.png new file mode 100644 index 0000000000000000000000000000000000000000..3d66d725781730c7a9376a25113164f8882d9795 GIT binary patch literal 15408 zcmYMbb95%Y6E|Ahwrv|vZF6g5>!-GD+qP|Q+pV|l*0$~T?)P`^eczljnVe+)NHU*H zCNn3I%8F8maCmSaARve`(&DQBIN(1*f%;d!z1HUbW3Z0WKb-%KFaJlN2@ak)|8=@K zidg<9`9C_le=+*kfHIMj694{Rfz^KA00H5Tl@S+F_gK5|aaY$^Y5Niibv_~Fmk==p z9gM<cd4j_+)#AYG4#@h8$&qEnfWtY41{ynt17B7ViO7j3r_RCZHV}r9NCvIAemKqh zth3Jz{C+(=+I-(4yxuZ%?61++%-puMsq?MVe3=#gUjF{bR|Aol&;&8TFxTbUVUp-f z1J|ykG4!W|$uf@u7NYN{Nla)Nz3Rk$@cGB}kH7DJeo29Tt-xv~M8HV!qCtpTV;sIn zXph-Sbh>DuJY++PoKeDz83)Z#h@j-W!-*=Sk#x*rK=eNb?Z96DrGgE5W)$&Af_=f4 z^2-$--`@Yc@q6X*(Gx-zs{f`L5RN%f4<V7@)rG&wDbiC2JN2W6u`GiPGNwsr6+_Y6 znY{%Uxd}W^dxk9hk`b8$wH};XZb1pbx}mNIGYBv<ZY62Sr^xxUfgTd4X<ToD88XB5 ze<yzD^wQx&<YVBI=d<NQ_^lEktnl(&E^=Z3kfqgLa|YE0g(o0|S<*y#%66<5M**80 zAae(0eGKi2>PK0be_JbeY1vOqq>eHa#1dp?AX0wVnUB-?vc{F*MRr)e&iAX}*`2Q) zka~xcYr0qDO6uz~WMgC&KKLkXNaFO&3lZ|X9o%*76}d_(Sbm3#o)wg>|Hj#(pV(Bj zk<yWzXr_Me-O?7=d2CpaA2*cB+u6VKyz#vCyz{(wy>Y#D{aU9FC_?cNy{HB4p@qp6 z0;gZ6fT@dy96XI8;lY3pasU;8#lqgB=LqHA`sqO9phs|R>p9--xJ6FfUxX+H$4*oS zTh;7Pabnwv-Sjeym)^P6obAhJQg!@0Y&pU%_uV@m!Mj{6e}{Cd&JMjO-P_Z1Ix6PG zY1F##xlqx%_x#Ld%Z9)9dK1A>zC`YMHV4a#vLzP>6K?j$@S5u!H1cf%er_zzW@rBt z1Mzs-l$3mG*<XW@Ki>^y9)?$AVV6&cj8{4C9m|b~Qj?dIluTk<w@3E}_ebYD=X>V| z=SRSob`=omsy5IO*dgS2<Co<iqg>=G7dRMM=(I(`yuZ*v_+-9zhQ*z$ui`Qn$-sEn z>uk6_@0(x3HqVT&O@dYPTafSqU}ydqXB)=V@$To=`hD&O`*};o+aUaZHzM2UrGgmo z;@S~1cEEf2I0(6T(Td%EMbYw1CWK|tx)$`(vUpAk4rS0{0k*@o<q45G$X-_eyx-_O z;=7kUgjH-)*xrsv0&;7o7ZXuFH=!l3@RgA*_?lc5j%STFTEF&1g|8bl(8&y~Ie>#f zMF#N%;BfBSUYXx5wU#IERDDQ&Onr)d_Ds-B>EF$81vL2OAOff%V>?8y-hdLdK{}#9 zT?C8}sL~;xL5T?4-iy*BYrE$c@k*L^m2RlcQ+`CuFs|4n$>TR^2YCm|*OptH^((7< zIg&179Y*|B&=JoTr&=cwzy|iCZp<4&asBp42M|!3Zuz`j@chFb815T@mtKu(jgy*N z#@P?|61!i;eZ-a{zEaNBEjybF6Au~VP9Ht;Y~T&ISzXY7*f+Z`V0?srwnQihGNy~G z$7v5v^DC{vm7H%#Hm&8g@|jiS=I6Zp%H(W$>@9z}A^s(P?mL^XAxuVgU$e{f{UDKI zB$^?$A$K}>I(+(Ke-7A(E4KfcoVG2!2+TX|$zAC86|4y!w9K-0Lj;OiG@QHXK4^44 z^d|dC=FO?Ion@gRAR4ya*Pf#fBk>~d5^wFzCx72)Ubx+Zw+KAym;bFYi9G-KyUc81 zhq3b}Dk>7WuSpC-iGUvQ-eOqVQ??BaNJT&~`YvNQW5_m7PkEbbHY~mC&v*3j$e-(u ziH<$R<mcs}=iC`%P6)rpy6Zm41$M6>uFVCFCA!+rm<gchU2kl<9C(yZHfYI;+Ceq5 z<|)b!@vm0jrq>L?;ixv45WHVRHm`+z?fLXOe`W9(vHowV=;`ai{Nh&MwCfyMH>W~0 zR4?nu5063oBv{*mLn`KlBplZdcj?E5ou4eP$CHbc_h(~uRr{=kPjzF?LC+nzur?^E z&}1*2qbs-h0Y=>`+eFz&=ynG}ZNr-K#WBx#apQ!Y_RW@DsIFTqF<}N}pJCIaaU^1H z)B^PRW!0EKKI15kG!{J}v6DdQ6?9k!+4Ixh@jJvJav+GV9^9ux(@`Z~ofFfHSpWDv z!_3qzaE>9Pyxm(}O1rYI>gwZfDMnD+*!ZxJu~UZb3U1I&W0hh*irP)yS#PP>nW47E z@zO;Q*)wml{}FH(xCh(^9sobnH3GiND-As>My7TWV)F<6#cJ+tY6{fOQI;P^=Qq-~ z5~KAFAmlXl>t)<~i-Y1?aV#z_IVk_Mmc!m37DRy>IdyRh+PgIjtUKV5e;T6h_IJim z3|^zLX_$<<(gi+&heTag$<|zg8i(|DdTmQy!|pzzhXlG|r)n?vSu>NySgZ^;3ZJ?2 zpVzY+C)DP>^-X7P`b&F=2c;G{%&G4Pv>NhU1J*gY9+3f~j~(+)m|<~H{LnFD8zrMp zdGr9(HEzC@i>k(UwzaJagK!!5=!aKGp~z^xj+~B-4uVd-&YW*Z!GX$Nmcg(f<!XF} zMG(0<afP%xnQjhI55K_Y;m$t9w059XsiD55bG;0aX>hza0!9!5r-JwMLvXok>-2mj zOX02JVvz7aMvcS%TSVHL!i+L(o4iB{`ZTeMx9xO#NF*m@2rlP4Of$nh46^Q1HH1z! zdR%iJ$?q0dGK7}zBD4vXC81cy;8@zYR-#?pbMqnmqk5|kgyzgO8BXWbm&6dc>1U7t z-E+Sz_YfesGlG7@l!&%gk6LrWdc#a?AAK?U0K0(0Mltr>`IDTq&lI?5!mWNduj`bT z0$o@3d#B;|VeS7}uQ>)l#f=EM15yRq`TROj^cS!<)jg$LH2mljj`^VNIZ_8WXT|EH zy^dq;biOu6FF@FQk<f0!#`^u3wOIxSY3`w7!@T1$&1lCB+*m#0KZrQ2GL*S7*APZM z(&(i^U<pN$)T$OQ$$nPh-_|Sf0y)=AMOpuu3h0)#YLV?gu@RQYAc?wolN<8U01dHP z$NUMei8)oP0E?V`y})B?BcwjgT?<ILs|v#XDx0aVVtm8q9k-?U2KffLV$-X6WUqN| zJ>KVv1(=kDA(|$Mcv0hDN_M;V98^>bvMs2xG5d!)>?H;3#p%`Q&FEcy%fSKGJDHd4 z<cuKXVcIxPSOn2R+@g@pT4D`tClQ}@1f?vs3y9CxneXy+(*r0OiNJ2NgU8(4h6j!} zrm~eJ_8$A}`9;bFQ+d{1ID!30RD;tD<w=nO%|-aI9KCnwWiZ?5RLeE$k*)Gnzd_|F zTU;p^Obba+-YKVq3eb@1@=bcDB|Vm9?MRo}f>|c~0(#pcLP0F?nAiD&Vw+=GwtJ%L zkgHx()<CHYHAD=NB||*R(aN-9hE<I9YmIYRWvlG)=tAs6&*c+_x$RZ&?WC=ZOpe0= z*Pj6)N5Vn14%`CI@UcBw7fHlty-sm)Q>jy#Q@Qsy>6kn9<6hq^9-p-GU?V$vN-yeR z8@vVP6D^jtn`be=dD|3jZ>5XJAy<9h<1t66er}T5&1;f?!>cP&`Z+Y0W<qS1sn`(= z1w1t(MXZMk7gj(MM}<JzTDak?WXf<=eA6*As)aTmn`jwpQC$^8^$0A!ZL#{^B{6%n zVMQ32u3#E|3JRUPiplCD1er8Q(OV^4Ae{JL!$%A*`dvgD!yy;=TCo{62T~XAUa1PN z5SU6E2`r<ShqEPmb?#1jI;hYpV;bm7s_`Y9P5qHElt~x*k5ab)Ch~J7<W&STB$Tq( znA69i7N<e$cCF%nN4j%^7da>W8x;BG-K<5o;ZS*QQKvz<AzO#pK$roZLm*zW=33L- z8xEGpzSM6PPXHx|KpwFx1bPoc(7Ku2<BW=jTP!5Q$FTKeWnUv`YEqeG;qM*C&wQxp zLhacIrDmToJtrwiaq1y6ak8fp*^=BJeZ3)q?ZLFt{*pfM;(_$?Ete48SMoS!*W*cp zgbW;dlrhcuMjon!voqtZtP&qtwBo2m1iGI>y2N31<^Ufy6hew=27d9C(n(Eq48;b% z!AF=+T}4G2>gOgDbbEJv0bAH^)Rj;_JNy(Fk+%4%#oin=fvV*+LY6Q|dcCV0=$R>D z1yCz|QO=7M7CS<5W5E*pE6W2NU?K|cLYs;EF7GVXB6jB=SBKN)Z5(SH9e44?R~CT# zHE+fTjMH0N9k@~9L>3G#Y#oYvG(Xw#AA<1n{ezJzu7kV7pTcO$NuD^yQLVwU6_2KA zo9<LGjj?7Bxn?2N&!!ph@fIQJ;DrxF%`T%)<iqp`^=txzW4mzkk-gkev~zf2{25@m zW_h8ZaLfJB=a#+ZaJwYV%$(E}_VQ6Ea-<im6t%su;4*<}u(c_UYnnpt&cmW<$8}>U zIuC+pigsW`vxjBx7~8mGzg9pgnFY6V`ojL9dcbp6;1gY7U@=>p0u^Erh0d5s74YGz znflx3DLQ+6{qiCEk$ppcP$*8#Hy>Iryz=9Sn?f7f!IowD$qS)Fu3MK_bV9k*o=Z_h ztx_Upkpdo89g~>44pJZ*Yew%veMp-kBqC1{XC~s-=n{~(<P6_l72*X(DxRXDk>Ydy z)6LM=pplRL;#b{@xy+8}^0s;z&iaq+$8oU;UySbU_-Q#p>bPlYEX-(N_>s5^uTED& zX-IT&WD$(H;KZQZJL3*dtmHH3uhX2TH1M!V`C<6_TfiK2pF^naP*m|Ykw5H(Z%SY0 zyX9cl!V@I=#{IVaxaUrfW8RnXKi860zL@9@tQJrSY6a{zj&1a<el43&p7PdkThSF0 z&9S=z6>`-2&c4nI5Ybk3721KXL$<OP56yB~(+^AKI4~OjJh^MxQWOUB`l)k@Zz>B# zmk`JS-)Po2FN-jGB0m`XBZC^f$HZo4F4O#VBS?w`>>VwwGGvlhlfL!7Mk|Bt1F*8l z5O84J#)du9{7J0+E~J)V5wcqI4P5pwa9t-p7Lm47HFMp9-8s$LUo0T`1c`^qL<<2) zLYSmhysvHKo;V~NCkw!#VfjQ@!54{pK%JboF1hyBetkF%w2W1p7GUe)h6`d`PC4uH zmxehGydU!1>-hv10JLM_WbV#;vPgRucDo>(%x-C=1-0u1@0rHz$28|ycExkz2qgWq z3xu6@(>)Hy`w({^+e^0|KY!#<Cm)Xs8Jv+mV3-D)lKP6yE1=geug<Y~#Awb0k@Q2! z8!;`2(Ne?5X-rPAv1c9%rQO#G@Lx~CFlfS(mu!NP1N*!-o3b{)6I-9z^-BBdx6<z! z&pwBjy)QmZEarLaA=N_2Sq!Kr@22c<g1|5#1tY+W@!qxAoVFNJQc$QOZ0133kOhm{ zQjKXQ4z(<k;_Vxf(LF;Bk(?`)YsI>k{q~NIp1C{%F!OL*e-4nF;l+2`IuYwKVRdya zMaA#_Q$CNwHwdKT3m7@t@&O52Zc9IkJG=vQ$||smn5X`dee|}&nY!JE<<emj`ps4} zmUD|!QF5hTMsqTeWE%a_X5qyi$uA_x57|x+c#swoVOJ%Q_0M$wfcs7`KQ5-E!<?&& z#wt0`t|`HRGiaY_SFE&$2gYmzFJMXl=-f)#ZJ-1@4%vR)pF1`f6c<=?LF|WC(V=JX zk!gGplep33V(`0OHiVNhMFp|fls~degxejqw?RLzGM9xJk;ZWQ&E+%^V)#d!2L2!s z_974kN6bh;su0ohGNiD~k{>rGIQ~@?&k%71^_U5og`}-q;8cgYf!UN1GC)$0wvTtU zW5KZEf<g6Ia{Tib|3<f6yaozq+mYxLZr#%RG7HS|T1btDIfr^?-B2B5?U<N43u2wX z@nRd6JcW4>VR6zP+a`z4wYTnDynEJT#yf79;@Ino+flo`OR?H%Xia|)!R55U@y~}3 zr6_~>#46eEphL&10uAYNdi<hNIn^J0uxB?mjMWvcMp1)|S47+X>{>#Yx_3A=F!o*! zHrvGw{2d;*vW6R6l82)(!&;!mZ<UnU1te-WU(wObM@d5|+1G{UK|5NclRpreNimey zV=lEN0=fFMlwsN)X09fL?ZFb=5ThsXknX$wyq{Iv5s3WNYiJ!-5zZP{o$_g`#h*Dt ztIhy3+${{DY}JLGRTnI_b6B~fQ2oV<nb9rvE4<3P2}Ly?!z>3QDqH0yV4Bw5OAQYu z#ML=xPXIAlEM4Nt6eQPWn84e6w(^Fq^E7Aak?%P2e$4X&o@gg_)dL9C){CPji*DV4 zkm_!jeE4M3yMK~l7S3;UI=pkGHvT$8Dc$pBN12n<NwC%yI;C&u9|)haK{qif$3B?` z<`HERHAguf#h9mNg+h#(b?O%!WhTx$sF&SoCKX1WVOb%T5t#PFv%~~f=zHm~UQ#ar zxjCyAN&fyk((rP%JUtauF;Qq-cDvDS{MhhPDveP$=I80MK&JVz1Bzth!j<ZjnO7ac zxbWMS(>T5Bt>d`cXx!4vQ(N_42P><DJ?J!l$A>^d;nqUW7<wgTxiJ>uCP#Ww8cpId z9ED7ien1M8rkfbX=$^@%SPi2iAh@BPvE((PuoMxbiL(6;ZUCb4@Vq>Lwtxglx^!5U zlGtwDsZY(&$f9&t454({pbS;f)g*~mDTjMz-%uIE8H%WMNVIR1-Nr`DyOO2j3T#y( zhK>_k72wf^{pDdW&^qUP)Z5j4+-_Hxhj%l~_c}D*yJ5!l>zU1YXZ0%Jsmss{hCP zVH}lSLO1$&BQu@4zW>{~<XL47ZxZZWqFp(It-o(PDkRp^&&_JBLq|TXJynkjOtbOh zbmDC<9)g$qx4YxnPsl=n-ii}ihIjYjOH9x6L?Tc9=^o)Qyn82C0?jpr%Fd8tX?7cc zZNK2RtD7I*Lvx`A=#w+W*A)NX>rP$ahe0P<WUM-`RQP+Ihsjutk0c|5i1X?6Oi_$R z&lO{tW1k_H))G5m4RvV2z*-(*NU>}?*DGrdobz8FR(zRO1~*(@((e^X&=f0_+BAy{ ziyJM=kOC$ZrO*<8`W-4*cY6FA4LM%IY7YgRws_+GML=_=PNH*N?|fda6U*$)XT~xM znYx%c<sPVW6i>Dn&Yg72WtKiEq>Sq-MzWZc=~fDQ5a#`Y7~UIOd4$1lB1>m|T|xtw zYoE)&hL}EYe>6qpmad>2rY%9r2MlMHR)OilrA-Qgqe@`mmq5rG7rgq~tzRk|(sbQ5 zN#OojxaR2eejSAA5YiB+C?Makch<D|Zt8v|AEFU&`Vb9s|90%9G-zjRNi|}#G^EKJ z^Sip})`EU#yij998*fBtUkY_%okhaNuZWsNj{Hrx+i3OLlDD;SugtIF`nd3@6)dfY zobN*3x#8bHD45`S{<(A_!}#vJ3BvuCAJZ089=$uIwM36*yd_h1LSs_9;r<Yb{*h{+ zdhKWLflCoz(}&G|p)&Ri{_*WB9SHNwNWhW$zMJS(QBHVCQU9TL@_YfJ+{)|8niaLt zsO)p{lfY=1W=g5$l(qUmWCD~rK_Qr})$Moy>~ehmm+W6wZbHG}=~o$n(XY9rBZ>r9 z4ULdSvqTDM$rt!A3#tLR3k;Mt86osujxI}WHrFXvaBO`wISNiTU!!t^>H87N2+%7~ zP(No@DV3zmDWxjhsyHoEl9;+m6+tCLq35`yC^%Fc!VHabB$@@^iTt<TT20)aUTc;l zd{3^lqfWkF9$O2(<469Hjy12AOKM9!Mk}%MiPu&4mU))tr;L1mund=}J0JYd5Hl?7 z^0Xv1LLrqJR5Q%oX}6+)nEF$uG?o#Zg5{T{rH^zwK>Y?cToz0O9{i*3{x$mzth5== zeF31scL?;~xXLLH4{Np$#Kb1EIrkMcU<=O=cTUF|`(pc`Cv*nBpXrONj1*c2S-~kF zcOXF=ZB@{_dz0!9AbS)4Mkdf!()PwEWO62cengg$R-6LF8PQ;{<vU(+Y}lh~tm_Rd zM<j})pCa5oA>mn_xJ_r>f^dla$YT@_*FU$sxJ`m>zP^x@qX>)QiHRd2MrcoCP&%`g zE~=lK;5Jo6J1x)z+cC!RyUcHPq%-3C{Rb2;%5x3z)!j)i<gIA-3Kko~`4x&i^_uIA z!$su_wILV95X~nG@Rsh-U*2%2=I^;nhs)doa2M9@003mCX*vEwh!ul4QH&K0kA-`1 zg-zJm9iLQn(q)rDxGIaD4YYFzql~HDEB8IzT=yOAe`rd)f1}1I_~%;TJ>tD&8!4sm z1yH$aQqTh0yV_Ko4bbD=jg!;K4T`~}vQ`mJND1qB!|Z-=ksI$W{QW6xD<`LLa<_26 zUKW{Rwh0nVp6*~T3&#QjDVnw!P1uZwbE{A>jY$!OiW5KtSd@*c3%T4{sDp1u1P}7{ zyE8<~g>3W$OzR;_b8Yo^{53~B@pr`kR$JbUIeGpeoZpl%EZ1_mbiPEd5mXgE<n8=X z|6}LF-~OGlei;O{9_o+npr3CVbjU7`Tpe=*(zfh<#E@r#=%(=}NRsFumc2@U%79w( z#N}6<W&RXBuM~3}$|=tdp+g3m5!saTo{$4<M#P%0+=DZus7RoJYV3V{YVxmQaTxAV zHIJ$a>xdqjYgrrD%+}a{mYw|gQHf?iAKcfK+^DY7hFmfl{zg~#FyW$}_9kkDE5tMh z=W$%?>8+yh&b2vnE|=;-+rY%B#LfuBGc3CSz^<Qe)%~b#FC(K5*`3eW&7N)uHG;;< zsf#1x{6lvXWZBI!55OfNICx(pbb!{AkD9yN2npf*^I<eq;$~y#MJSGm(Q3u%EL5ab z8iU3&pcuh{-DPJ5H*x7g5C+#l@Sl_C3iaT9xwl2e^Q}ajo!EY8*^yi5;BC#8J??F5 zaLw+MJ)Q5Jkta{uXSVMnU8iTU%}isVp~%Q6K|$>f|BN9FVixpqw&O~hc%@ZwO&cMZ zLw!N6qkPL9mh%D1t6hB`b=>1b#9Qw#3aZmT&T+`1d3~`dF-Np76%?OTLCb*`sx%<u zHL$5E8}<;P=h^ug&=p@E1dURYrU?4H=-2<dOULywjlK$mK}{OO(=1a80QxjPR8*}o zc<5AfIstJs&d|?_Zp%+~LPY~J-)aS6^fGlRw+NDot+2HUp7G=J{WQ1AB=gS~5SP?k zn&olo*4-~x@LA9X+4!@k$j@8BDtbSKKpM8(X|$;9cM~;gTgWqNq@yauF!8c;@HBq_ z2BjDh3-&_SW)0ylqq~#nastW#%(0DHVU&3ykK6;DnkUQV%Dc8_a(Prs6DBJ01mifA zHk&J)|7rj3?N|WBI+hB?o;D!1L<y6*g06M(H@c=Z8-W)rrgCmL4pN7R4C%BW`Q=Dk zlYx#@DvN|(&DHj4cCRhvuu$;KAh^+<*=q^OkAl--r@#hBYP4VUN1$MT?FVvElgAm@ zH(P>!=pl!puYOL4@M@4oD7(X`LfYSzK4t!N6LNst#i}Ml6Z`e9JApwZ?u$HL=(mQ} zrQxEst|4Tr=Vd@Q>^b*-t@_SFws{=olE{*??25j;lyTq$#gKD`WQ2mjSA%5>E>tji zpzU2L+rkkbDk(&tN{j`a1X`OO;6djUJNmvuUqs6>i?%@M_1|@zv(n#<!Qd$!dqO@7 z_kvBR*msi=F`Kw~;6vtEnWR*@jNl@Y@lEekg3~yuI^^@DVc&J3)OugK2cXMELKCJA zjVOwzS2IUmxPgO#9M1sb`9RGinPq|GMSw^iWh<)FYX1bEua}Qqa$ABF%@p)sbJ4wo zj-(_Qy5Us=5j+oXSBOw0I{)sPh~GTE4y_o`x_%AbOKbFmYIv19cuO|>QHwm#)nB$j zGpGnL1#1F+2O+b-ndHEWhb7@n@luvhMsq-VmkQuuNbrv1L-IUec;Qa-Q&Fa+38Fr} zWKIdP@hT(~8^;?EFi!yUqLPV%Y3z^wYp7DNl@8~Aqb{7l!KxKW8taC=!&sutu9({Z z0#j~?D+=Kmp-w3$h%_21u&%yCLKHMK**T>!&#sN79req!gvNAf-*6H$f(MJAmX-o^ z);x_rvVkCpi42A@8|FRb#}0}lgnl}YiZ};tP$WIXFr8aJ>-n+1<O|9bZwCGL@R;Gm zo8`YJZBjI|5l?)rSnGqM0Di+tdpt&=pVR2A7nz@>W51z&owh6xmO=~-V4vv%mnHl3 zhI9An!NHMm;F08>rW{B^s9IqJK}15(pcko}RQYi)Zw)EH`ahit7lLpv@44(cu)wKw zE5T2Mv}W!x{7t1_MwjJ6NR{WjZ-f&{rDUWWTBP7kwIcDsWu!?}Rvxn!DkMVZ*q$?1 z%z=Uf2rG1SNrR=gP2R1V$>ZfSd2eaj%X|%s@yau?{Ia(sc3~jXn-%($H4A5mT!su$ zsew43!6^H!7mcd=3*!VaOft<D$@CH%!A4!n0DWQ|A-myngL3&=ztzxU5HzjKR>)Vq z1p|CeF^^?jC<$XbScpU>K!6CN@1%O~@`Z2!I-46?yI`ALMR*%ktZ^s!j&#U0ME?YK zVAh^wN^zb9t^lQyhL~3$Jsg;lo+N6UK*=FMDc4C#0n&$JPlH`U&Et?GHFKV?9ALo} zLw{C2zTo=u3q}>AS*4G$hq1k;CPgiU0gpz3PK~hdpv*cn3q}g__)onjXQtV!OaM&) z1t@kJXkehwS;+a#M)nk+4q?^pxXNQ&aj{N*N~SHXXx5>?boR*$05ypwBqTWCghl2j z0EgyDE0H!L+&3$V3)?N@PMPs`O|ilGgC4=(j3<bY?a};iqW<+e_v|&bz;NFKtyGB< zkDF%VG`VPvQiUcIJ;;1h@n91=D|@1lRAik_!)9E#=%Y~GCd3M_<8Agej!?-4ZQKN{ zOz{GS99$wSR7>=m94d<1PQ@iG?uIMlq@WTX!^n;Wx~L<E5f%YGXGP7y|8LFf<f_Ud z;65&0GOEM%mP^t<Ir?QkWs!e15tN1$6Apw3IEfa3)S`~6UIL@6!#RU95A#ja@@0<S z8?$uHR(B&>wO+%lxC1F&loVY-!TjO+mH{D~BY!UC*je5=XPXV2GUUJ>lrhR+^fL-1 zKo~N6%ph{e)JF_)m`r$NCA{Io9y6tOFPR8dA7^SVC+qK@aq2V@V~<Ire0<6>N@F~< zQawH-5=-mtNK0mhS_mr<=h<^szV6Vrb)Af#+F!)d-;iyP7e8gM6ZHnpia}2~lpi5P zqX0nlq=vn63c2u>O_)#4=GzdFG*)pCX2wKp>%b%ZO8ZmLf6fo8=VvmJkt)R*sY@&3 z7ZRVD31;P_)Uu7_#1^FYAv?eqX#<x$J&`&h8OUkCLwcJ9MV&zs_S14VXux$;O+J1A zGEs6{eZ7N(!O4mafa>*nu{^l@3oqRh>Z4zmM<;<W<T0>)O|f0;6o51Z$MiEelguN5 z1k8z-;$;>Wbp;0$des#Zv*TN8&w_<u8s>PU&FE6O4&WaQjeA9cfc9;nH<UeRF=Bf6 zUNRqD=z%Bv^4Nl=H$G3bI*)U<tZ%q;0&XWvV{X$w@E0j0St3}#UbQBk)Uy<Tk3v}^ zp_NbJx@13cPI7!0Qmk?X?DBzHO>&=PyCchA)-K71djkH?D16t0HRLk`Qrv)p6hnoo z$ZqCcBzO<rHKp!HGVF)=KrX*wdPA+d#y!SR>%=(->lUGm3ukBr7O^-<P@4F`yY57K zazw2G@VOvvn!qgCF5(FCr_~kn0&ohYdw%)r6%>JAwYToit2=+HyRUI7(M)#A$Dd9T zNwV2}T5{{dU_1-(OeGRgJ2OyQ!**T5KEI`Dnje6-6vJvCul*VrVc;F2w_llH1R&BH z0;Os=c6M5r8OO_&?_q^O4;?HP7Ke=5<J|7?``Gf)eA75{Fl&NdE-h!(EaE`~994S> z1gfEqfR+zO4irkYh~=^BZrUW%mY3)w-Jv(W6lWBVAjyG*ejmvRs$hy1r8(YJp)U6L zf3c5)o-e}n0X&D_9*7ei=`~DqJuWUSu|1!m9`b%9{+5hbN^-#m9P-Qutm3w1JZkPD zg4@gBbgX+iwTcP`ElMiEgXkWl#<sh(U^HNi%A}wG5g7fCmN@dL2lR#%{!akDcEw@G zqtI9za=0JYZv@i9WM3=q4R?RPpZ(KZh{*m`@MgGgGLmG{0Yr3#LD1w0^ib4k=ot<U z14*3e{hsz1S};c{%PbPZ@Ax2v`DfFd?j<fY03zFfez6Ee9H;FE!jKkZF2ezeCX2em zCb0D;&-`hEMJOa+s%v~M3|O@!qui2?4trUoU=Uh&qf0A0G0u>r4b_9;wPg?NgE!DP z=D7KDCkIl&1vjzvv@SB_r^q|Yh4E#R*l?#v&XXtX|AjkvklgU%EAy`Ezz_Z&Nn@`z zhCAL^yPuPl_WPP&(Vn)X(2M;9joP6qF9?+jf5@IIDg|ZXP<$!$Lt-?dWh@Yd{c}Yn z0VBSLi4hRSz#BmuM&=`Syi-m$0EXre!$&gM!tGU`MGXJR|LnYG!4SmIC3d?a{c@*( zzt<43Ai3R6^yOkZq}$9@w9{XeoEkK-nVdm=JTF>0UsU}kR6v<`^rTswqtGL1SImtN zR=7xr`dtp1_0{L;s9BKABc|rA*t@YPGo{yx(f)c(;)yu4PLR`6zYfw37jlXDXd>J= zZ#tI`Zx_MXc|E>-G)O7~m2ZfQi)6}I{Z(9p26zb@V1Tm^%E=>q1UnPt<Ca`oX0=WF zcwVzzoQD^rcdPCgK!l+{L{5~%&eA1UeyGQiBJkS8$ncEu|Ap7!9ErK;D~1=~9D9&A zz;kw2hoqak$1F0PSIOeApm%0#BvdZwTt{&z^vQ$bBiIq47XC;fjnkMIEDCGUYpksv ztO=ZOuqNvXYaoHLx}~9O(%_1s#Mxd(6DN&8OAz`&!GATLC|_ZqxX9~_3!xqZ<_zWJ zFe5U%BGWwKD2lbP4LV|1U!;<SdxFa&oU#UmcgwYMm)*X4(8O<^uEn0z1K|~HdG|3i zzbrf{yG)I(p4w%8X#bU=%b$llhjM5qzVltO_i#`+W_lTp6`^&KYRLZ+m7>mGb1uIY zY4@b~0;%rG*`-&G&VnpmTU12m9V%iETsXYhmBALNpXz4tJ1hOlDop4z1?lQSy?t%D zBDdY-RzWhu$;*oUNhkpg6Anl+h=(H01A64ofQ{jJnYJ;9{tQ;RTEO`bmp_9OKmg;v zu0OS{bYJ~15#U7hZ4WtTMfI%FEIG`FV_#4m&rX419tb2J2TtAJ<JU5mnBBIWdf#Sw zz%fo6(9NV2vlPkJBWO2HVM*~LmLn7CB#4U3nLY@D8q0KD|B2`0Z2kcQ5CKg(-;cV% z8NrX6C~duRM)z~y&2tBX_#UlaR}vQ?HP;<ScrImWcH3^pBhG^se@IOyL7m~Uj!A;` z0Kg3^#22Quf*;XQ78BJDPp&y^GQXG{-dY^K#=cDD=X5wL-B6qM=k+&$`5WCeZ5Oj@ zFK}&uV&?_tYsr4eG8kEUl8kvASE`c5EnZnw|JWs2o4xWe@E<f6`Mck}zpuDM3$~#* zO3a@K7OyP)7&IwEfU0z^%I4I|0I}m`3Tl}sCBNV+H(?mp2W`~UD>QLg#xsBlgh2p= zZQmwr`XRQbKB<woXVjbbosidY`!XxB_F4Y``jP*?5Owguv?UJCMi>SNO7;l~J#Y4- zKW+Cuca=W$t-8H7@l4teXV4S15I3wg0O)64>YwU37$(VIXCaF>!V5qlQ9}!0f}pc2 zREi{vBR$3|eiY06p&_b3(TjhS<uEAVWuYTFk0sl?eFgwfZp8y50)7b&vqJx#MqnSd z)j<3-?!UbMNT%+_Kc2eI%zM6Uf74o8+FV+?zG*AU;*WDfELyyucS8eMCES4ic%vMA zQWT@13e}}C`XL>njR@*3OM`-CF}-~FM|tuOsV9|@n~f+Qhr_|I1YgfNQzZf=>wL(( zy%@Wrt&)6W8s+-FrV@;&%VOwBs4P-7Kcba*<dC&%w)hqbusUw+0#U*sg^4tkmu_0j z`CX(G@HIiu;nkR^YiWQMp*m3u6U-iTeyrZnCuGaHnf=&hetj@Fyh7sdU*NaC!(74< z<17h#FPx~;p<(T+TpS(x*gp+9+oCauS1VswY`Q(+X}H>xzUeDZnx@-bc5E{n;9U!1 zThg)3iL9P!?US<2cyKxV%zT1mip$I|(e!d@v@+P&6a{>KJq$gJJUro-gj%K-d;<3w zb~@G_ylvfrB~nl1^qB_@He=~^AN?c?Rf>rnQt&2O;g>tsGXh_44`9J)6cY|vg21rn zpU_XuHp7^pY5(AwJLfX^3;8>ilj=3b4FYgV1JUEoDya63f~BO(dBcha0}lE0WkAM` zDGb63sGoN`!*bDf^o(S(cOZX{fjfvZK*KQ!*E7mrBUVS)c$&_51<^ALd`~Hf83VcQ zPwsqK!hdu>IZhJcoO5T+UcG<{EC$Z6l9n66NN;MJb|S&?3ed895ZY=ou6EKPDzQF) zPVB#RI`h;f{pK~1#IBE~DZTu04>x(Vi4e%bgzE?o`HMs*4LZV`>||H^_OzuhfA+cj z4ln!NMcP+9oo(-w+?)9f;7wR$ZJ!!*3?&~o_L#9|K`{4)Gs}8HAKN(w_vvAW?HA{E zl3FXzEUzT*@a+J;18h$a3oD*r(^KTh!SX?J)B0oADVKCOX&U)p;N9cqV&8GA;SHzQ z7t{UF%t~bs$2G(0FW-2E^8UE2fRw(K^4c_57!c<ubooY#D_UenKr0~95GMLtLx5Yy zpw$g3rHa%<()0O`vkxeF<Vip9N)=3^ObE=eRH&f|#9LNQdd=!VJa+pc2oP-)QDdZr zvEjB-`@-{ZF{lNFX(7^&WADR!kO?U;vG@yebSE+lX6XF@@agM!g6g3f!d$4@Wj_Wo zwH21Wt^^+T>senQNK9*eO3Uppku#O~2LW`ro&r#?9AtXCa$N0-Xi^DEaOn)z!@lH2 zd{AeN-l?^8nzXs+oG+?ElHY8hY)$rf{*M#W?m6|edrUlQf{j2)e!(a1joZ6vjG64h zTD<jw^RNi=Z3T20b{Tb9`}U1(!7K?uqz5*E`~I(e%<H<qH+a|EAT#};SFtMP;BLR9 z-+i09-^%<c5JKgvagp*3k51_fy-STzb1S{bqYcn}@2We$*8O<sQrHb~q>)u&#?TV6 zfU$*u3%`Nx0C+yZVUD*?UNoCqi4wtP2t@=`ZS%C<1Y_VF%$y^_F}?tZnCoy<e}Te& z^=m$e1-(tP4@f+%>D5EYo!#^7j_X>$o}CNU55se*AD#16l`Wac@}yg@4M&lUcx1Fb zeo|bxR){oyfv#6zz#@Sqns!RK^+2Flw?#s=77Vz|?cmdt^excRvO3)HT<lT6y-GZo zolr!~p??mm-T?luVg0x``V#pkldpef?-NbG9{0ruY1w~!Q@;J-NCS!XtLRKb|L9pS zj9+J9Pw@remgiTczlZXHaYoG-n?w~ex9viE)^p4C!i@S(w&%+EOhtKdHJ29L6=`9P z2ov1dYG~dt2np(t9imZ|b+H=!qmN}bMihl&>yq0Ojygrx40uU^?G`Kt42f5?D}Ne> zBQ63(7)FxhwlCwXH0g{H$${6PsXXau&$vng^s3p9rO4vStqFA@I7eB__^}Dvt$q;9 z)yk9o3L~}=hO=Ex8t75drjrCqnn@XN(rie)qAO=zU7cmx3;V^RRq1k+H9_B_wgf+= z2OLRHbj~^^9PMCpqCP0H;#&1q#N5uS7&P@NrA4L1>fhdHsc$$-n;5kFxu=1rp{J+r zKEeaPjY`6^ry+0GDTP+N0sW|t%}VaS+1g5}Ruq>;s?v@bI2e|g=e>X!VeTBlvfM^q zt@de2(V6}h)VdpEuDf)b<%_3(tsO>_jbrXML?B)}LX*QCfg9o@3WbGWnr>~PI+8@# zze$2`4&TvtSA8hhP%%>F;AF_bcN2zcqI6)Sp-ee301qUqC%iD^jaxH7H+{qn>|q21 z0O;ew9cG>y0u0n(c32n`Fa}h<fdVdU=N_P_#JA>}c@Xxid|wD}UD!EWa@OhTBcv;3 zU3VM3%Wb%D)`Y1+0{gn)e&m@FhpNgyp%yx<-(D91mLe*PHS1b^i5LD-$Ti2VMq0tV zhh31R_hW`R!YD7`EegRhQ{O=>e%I<9a^^NvVl!G`Gydb#PAq-{wY#dAqkF0gr|YL_ zYN*j*dx{$VY*0mbD-ojx#>5%ri5%I*^)jNfh6lAb(s)*9;ueQ>i?&yR2p});T@dgW z!)Z&rf4g-XQ5t%7Ld?<D1XN4s2%e)PLFJ=$ndrBcTD%`pl;~{I=n+<r$&js|2X&v4 z_H;G5kK~HP;PYuNl%`T1NlJfiT0Q+K@6M#1sZ5*(33SoI4BZOGaewm0_{Qs>xMd<4 z&7+>+Tdl9ayr~m!injG6izqA~iJlart^UFvt(cx{0)H5X1`<!aU7Ng&eqh(8tkhKt z0jV8}u8DSD@&1R5LwC*qf6fqTB^z5@Y}X_Ccpqee_+*raNo=>n`>?1x1j4E-;Tfwa z&29DXR=;XegZokfjEa?ZzLm;?!FG<T>Fa<KeB<*Yo?M0~Ctbu2fMeB))B_x@|2C~= zkx$%$S=9Y9DH+$`K0Oc!*RE%S4E#x&bJmc}(t9p)F}N`sx<Ap}0n=ZcXJF+Gm6&X| zI2#r8j_`w5UrTI7zl|ou$;zl;=SPi!z|4&9PD6}iYcje@o){2AN7D049H_I^W%uYv z@r?B)xIG`h8f4ddq~eLwwzvnbtIRYo-2JofKyByHZl{!p#o#lPKtOWeaY?<^_jCDe z_)U4~l-QER0CzUVy)0b#g3(y-3i_{bu}AkS3KDDVN%0xd&x9Ogy64QP%Kl3K+J&1G zW@J6gYc)kx>k-pOuSYIx4hY(P^UPGMTem2UR`?smPrgaF(5-#P#4Vbl+n-Q+=8Lnl z$SMtO*G2;(AKre|kuQuwLI=2-p6iRx`+`VG;;lqnCn_D^j)n>fzg+|0I*UE+XJ(dx z1i`0v5{w9yA8;xAt^$_uv>C@up^BGS-Svgr<)XbOd*06;Ffd}V?c6GJ>(n6!I+yJU zTQ~Fd_54TY>wgnAf+5J5!7uN(M;UyZf7CnJ3@6J)iQBgOh3gqaQs+=rTu+b`P-)Ue zYe<cn(nl?AVm2e|nG3M}w-wLd1<w$D;AddZlgYjDrm8yz*JM_43OjE3fjP9+u!s6f zgp<79sy1yc44McP@=NKJL#3ETFo+AC^~>O>3aamH5d?V56?Gi`vDxxvD;Uz1yN6x3 z4d1IX7p<r_A$EXmuK{u0*@5Ph0J|<opWrC8k2xhV1=Y80a^#(}bbC%1L+S^JjnCdE zRZqKmbvov=)#L^&l&MtZxRu<K)R?Fvx?Le3zYIg9uNnHMZ%o9%{<iuEj2grj5EKIy z{ER=e>3A&T76e<fPP>=mrW*;b9;nB7W7KEK(}#a%PJjZ9D-@ghPB=F?UNIO59Xe}! zB{cQ&jQ3iQG&;{EY+E?-F<N8y{u|L`)(}OkR&8iXzu+w4o_DEE2mWp~kdrKg$lt>s zMKHZ<wp$DSLFISA+O}$Q=)CXHZx(lz^D86&!EJ!`=|zNcr?2Ldh!)Q?5trf*)7EFe zE`AhH4U-H`nW63BS|>Yz&PB9^8^AjR{iOvG9U=z?u7b3CQDCTyI<)Y0owSQ^E*ya2 z(%X++G~H2fAE0Y%@${YINI;sp_{hA=C$0<d@i#_W8oq4efuXi*9g1itZ_s-|#Dn<# zcrT3vc}+}{ZXP-j0>m@4v6w6n43k<gH&0+Iqqpn&p$^d@)>a6HU~u1h*6^Vx1xz$u zw7jb)Zr6ge5z<V(?wRl~>0u?pt&cet{tfc=DYKW6Z3bh`wRu}%{tMn;`pq5%2^ST{ z^lxk9+4@F9TkiIZ&rPXEP>lEOzJ<mVq-PnQ-m`+71|vgpOCzFFXe2;8>F?15dI3q_ z>k~X8if&<)bw;SeE<4A$Q%$=|4F*$D9^bSpr)S8!fip<7t>x<*7+p8R6EjrJr79MR z)Qg)D?Idi*2+CBqDDS5qc-`>3+uO>%dtqzdXOKg0+Fru&HTZ0ydFhR%9yty|#)Gx3 z7L!%L`$NC9TQ5og=`2_$eF5QXEO!)e=RG#9ijU>b(LvhZH1M;VkLe=fQW<Pv&G=o6 z7KJxAs{PR05f9UUC*j*>3u}fUb&3%FEC3Nn<J8n@F1Y}|Z|B7PUh*hRbRX!PyO~R! zEwrAS*k49ldAO**HG7VCH4%orOf#%QEh}Hv*_B_~ZKw}IW;eo~_ZNI@sp?J<v=Dj3 zGrqtgSfr^%93C!#&eU;bu0_SY7t?v4ZJii@Sq00N%Ax8}0tLf!>PWBcUi6@h{}&3b zPMjET-E;h|tK*t6^45xqkw;8t;1PM(qDR?0hZd@uKk|FYg99aBf*5t*Hw=PUy5X_k zx6rS0%v>)t#BKZ%?730;u2v}SDFx?zUm7BM8+wM{!b{pr*cVOiSiJ99W0x~V0xSkN zN-^AH`~IKbkdB@7NMJ8__jIO#WAQ!&xkiWuaKwTP>flpZeqv?NH#~d$y}Dl6g6mxl zFnz8~d~!`R*2m7pFCpp%R4KEm?haX2dR5V>c!t7|*j|P{T?HU8+Xnc_-k6S7j};fk zfF3(WW+~VxPu-SJ5%(N@ZYY7yh^cA|v0ILTei?Sz5r9*-i@i1eZj&l0Jv4*ERn*Gq z_X5hTI@05BEd42m;l=S*d)~T-oYYk;kFR!4YM_7U^~xwEq{RTc>N^}uNgC!|ox;{w z>8x8+4+ZjMNPAUv4`onEF5~g<6&Fl=NrW#F0Q@Dv$lA1rQ4dSrm((|FNLwbhZaX#g z!x$sxasEaqpxyE0hd)V?(^Chp7@4tA{6+n<^T7?87H8cG_Gw>4_pQH;!FIB}G{O|G zuCGWF@3*<R)qbFb?kmjb&sEg<skmMp<jBoxH{}s6lfnC+$_Zs$g2G5F^>6n%WCL$i zRq|2*mBTxJcaUz4$H^P03ur53ngAfKiH4K~Gr;L_vYYzmt?S`~VxWG6XjZq-PpNW+ z9(H64>DTj~vmrDWlQmOv{4YYi$4E2juc`CZdT5z#1q2&}dFs=kJ^Kc?2De7HCO38> z9A^KhZ-loMQj-uu#xw$A#^H}#1E22Mz@ztKAT$7?zs@6w7wk`>1|}1X^f3m3Bm2rK z0c(Y+TpnHa8-01boD9E?TiL7aLL9<6ob%$Z8ac?mR_`o9y5Ol`ye^KsWXmXVh3jbS zcE+UIWQZl+>|<IiS@@q-jLA#QrQ=oSJmY`%ORXa&PZxe?aZH(c_KZ}|tOzKnFK}r0 zRM@G!0vcOxu{|vU?Jk#(k|uZ7u?;Tm?q<7OCK_Gz?u$wYmHHjtG;rIPMqSn{`U>gV zLMA2-EG~^MO)kwYEd)MJzd2*3He-~B5B)*{mo#_7#+_NNOWWO%g;20ro-Ut>b`PxX zlHCVLu^EW}r2vexQ}m5rKZ|%nx4#uSru{N+f9t4hD%f>AEk{+9SC!SLaysSxOZ1x_ zj@fA~=A|MfV_e8dC#LvpKhRhYWp3<6j5SV7fp(MUF}J4~^68wB%50`5?CM=Ir`vbG zJ*Y0Sm}}-(ZerD<o?|kFA5+v}HXgI_X6VT)QgPcr+hE%q=YJUvfB6REFoF_{9wNz& z^h!LR(6TJxZ^1P>#RfFq4}?^cavS9`#I5ljLH~NrWVKtqsV_Kwm5+)FsQeq+c|A+$ zgLW65)<;eV!VmNAGo<u%2~JxUEr#|UCSp-2ET%bAXZqaerQLhMd(kP6M1`H4JTbj? zd*l!9ziw_pn$8a?J=)WS9KP@Ep@*@*3{bki>g*f`$I+cdgkcvQ5bu3rMh}M?{9}9g z_g0AA7`4q_^}`$Pq!3Xh&bg&w#&0kowfX$7Zw-+qvp14AQ*!Xc<59lYw<CCD$%lJ} zx(X3$IzI&h3Y&xF^ca9WruQ{KNUx3<n_C5^3Ft=H9ltbu=6k<A=pf{Purqz^e|)dB zwkqY1vRjw<@2f7Em%6r#nuCRlyD`ul1jJ*Fo|$=N>h~0rq9TLz(iF2SGziyUC~XwA SNxgs4ATkn);&q}%LH`e8$k9yz literal 0 HcmV?d00001 diff --git a/examples/quick/scenegraph/sgengine/main.cpp b/examples/quick/scenegraph/sgengine/main.cpp new file mode 100644 index 0000000000..9a9450710b --- /dev/null +++ b/examples/quick/scenegraph/sgengine/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "window.h" +#include <QGuiApplication> + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + Window window; + window.show(); + + return app.exec(); +} diff --git a/examples/quick/scenegraph/sgengine/sgengine.pro b/examples/quick/scenegraph/sgengine/sgengine.pro new file mode 100644 index 0000000000..c6507a4982 --- /dev/null +++ b/examples/quick/scenegraph/sgengine/sgengine.pro @@ -0,0 +1,11 @@ +QT += quick + +HEADERS += window.h +SOURCES += window.cpp main.cpp +RESOURCES += \ + sgengine.qrc \ + ../../shared/shared.qrc + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/sgengine +INSTALLS += target diff --git a/examples/quick/scenegraph/sgengine/sgengine.qrc b/examples/quick/scenegraph/sgengine/sgengine.qrc new file mode 100644 index 0000000000..5d55bcfb6f --- /dev/null +++ b/examples/quick/scenegraph/sgengine/sgengine.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/scenegraph/sgengine"> + <file>face-smile.png</file> + </qresource> +</RCC> diff --git a/examples/quick/scenegraph/sgengine/window.cpp b/examples/quick/scenegraph/sgengine/window.cpp new file mode 100644 index 0000000000..9af4029165 --- /dev/null +++ b/examples/quick/scenegraph/sgengine/window.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "window.h" + +#include <QOpenGLContext> +#include <QSGAbstractRenderer> +#include <QSGEngine> +#include <QSGSimpleTextureNode> +#include <QSGTransformNode> +#include <QScreen> +#include <QVariantAnimation> + +class Item { +public: + Item(QSGNode *parentNode, QSGTexture *texture, const QPointF &fromPos, const QPointF &toPos) { + textureNode = new QSGSimpleTextureNode; + textureNode->setRect(QRect(QPoint(), texture->textureSize())); + textureNode->setTexture(texture); + + transformNode = new QSGTransformNode; + transformNode->setFlag(QSGNode::OwnedByParent, false); + transformNode->appendChildNode(textureNode); + parentNode->appendChildNode(transformNode); + + int duration = qrand() / float(RAND_MAX) * 400 + 800; + rotAnimation.setStartValue(qrand() / float(RAND_MAX) * 720 - 180); + rotAnimation.setEndValue(qrand() / float(RAND_MAX) * 720 - 180); + rotAnimation.setDuration(duration); + rotAnimation.start(); + + posAnimation.setStartValue(fromPos); + posAnimation.setEndValue(toPos); + posAnimation.setDuration(duration); + posAnimation.start(); + } + + ~Item() { + delete transformNode; + } + + bool isDone() const { return posAnimation.state() != QAbstractAnimation::Running; } + + void sync() { + QPointF currentPos = posAnimation.currentValue().toPointF(); + QPointF center = textureNode->rect().center(); + QMatrix4x4 m; + m.translate(currentPos.x(), currentPos.y()); + m.translate(center.x(), center.y()); + m.rotate(rotAnimation.currentValue().toFloat(), 0, 0, 1); + m.translate(-center.x(), -center.y()); + transformNode->setMatrix(m); + } + +private: + QSGTransformNode *transformNode; + QSGSimpleTextureNode *textureNode; + QVariantAnimation posAnimation; + QVariantAnimation rotAnimation; +}; + +Window::Window() + : m_initialized(false) + , m_context(new QOpenGLContext) + , m_sgEngine(new QSGEngine) + , m_sgRootNode(new QSGRootNode) +{ + setSurfaceType(QWindow::OpenGLSurface); + QRect g(0, 0, 640, 480); + g.moveCenter(screen()->geometry().center()); + setGeometry(g); + setTitle(QStringLiteral("Click me!")); + + QSurfaceFormat format; + format.setDepthBufferSize(16); + setFormat(format); + m_context->setFormat(format); + m_context->create(); + + m_animationDriver.install(); + connect(&m_animationDriver, &QAnimationDriver::started, this, &Window::update); +} + +Window::~Window() +{ +} + +void Window::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == m_updateTimer.timerId()) { + m_updateTimer.stop(); + + if (!m_context->makeCurrent(this)) + return; + + if (!m_initialized) + initialize(); + + sync(); + render(); + + if (m_animationDriver.isRunning()) { + m_animationDriver.advance(); + update(); + } + } else + QWindow::timerEvent(e); +} + +void Window::exposeEvent(QExposeEvent *) +{ + if (isExposed()) + update(); + else + invalidate(); +} + +void Window::mousePressEvent(QMouseEvent *) +{ + addItems(); +} + +void Window::keyPressEvent(QKeyEvent *) +{ + addItems(); +} + +void Window::addItems() +{ + if (!m_initialized) + return; + + QSGTexture *textures[] = { m_smileTexture.data(), m_qtTexture.data() }; + for (int i = 0; i < 50; ++i) { + QSGTexture *tex = textures[i%2]; + QPointF fromPos(-tex->textureSize().width(), qrand() / float(RAND_MAX) * (height() - tex->textureSize().height())); + QPointF toPos(width(), qrand() / float(RAND_MAX) * height() * 1.5 - height() * 0.25); + m_items.append(QSharedPointer<Item>(new Item(m_sgRootNode.data(), tex, fromPos, toPos))); + } + update(); +} + +void Window::update() +{ + if (!m_updateTimer.isActive()) + m_updateTimer.start(0, this); +} + +void Window::sync() +{ + QList<QSharedPointer<Item> > validItems; + foreach (QSharedPointer<Item> item, m_items) { + if (!item->isDone()) { + validItems.append(item); + item->sync(); + } + } + m_items.swap(validItems); +} + +void Window::render() +{ + m_sgRenderer->setDeviceRect(size()); + m_sgRenderer->setViewportRect(size()); + m_sgRenderer->setProjectionMatrixToRect(QRectF(QPointF(), size())); + m_sgRenderer->renderScene(); + + m_context->swapBuffers(this); +} + +void Window::initialize() +{ + m_sgEngine->initialize(m_context.data()); + m_sgRenderer.reset(m_sgEngine->createRenderer()); + m_sgRenderer->setRootNode(m_sgRootNode.data()); + m_sgRenderer->setClearColor(QColor(32, 32, 32)); + + // With QSGEngine::createTextureFromId + GLuint glTexture; + glGenTextures(1, &glTexture); + glBindTexture(GL_TEXTURE_2D, glTexture); + QImage smile = QImage(":/scenegraph/sgengine/face-smile.png").scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation); + smile = smile.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(!smile.isNull()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, smile.width(), smile.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, smile.constBits()); + m_smileTexture.reset(m_sgEngine->createTextureFromId(glTexture, smile.size(), QFlag(QSGEngine::TextureOwnsGLTexture | QSGEngine::TextureHasAlphaChannel))); + + // With QSGEngine::createTextureFromImage + QImage qtLogo = QImage(":/shared/images/qt-logo.png").scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation); + Q_ASSERT(!qtLogo.isNull()); + m_qtTexture.reset(m_sgEngine->createTextureFromImage(qtLogo)); + m_initialized = true; +} + +void Window::invalidate() +{ + m_updateTimer.stop(); + m_items.clear(); + m_smileTexture.reset(); + m_qtTexture.reset(); + m_sgRenderer.reset(); + m_sgEngine->invalidate(); + m_initialized = false; +} diff --git a/examples/quick/scenegraph/sgengine/window.h b/examples/quick/scenegraph/sgengine/window.h new file mode 100644 index 0000000000..2b239ea430 --- /dev/null +++ b/examples/quick/scenegraph/sgengine/window.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QWindow> + +#include <QAnimationDriver> +#include <QBasicTimer> +#include <QSharedPointer> + +class Item; +QT_FORWARD_DECLARE_CLASS(QSGAbstractRenderer) +QT_FORWARD_DECLARE_CLASS(QSGEngine) +QT_FORWARD_DECLARE_CLASS(QSGRootNode) +QT_FORWARD_DECLARE_CLASS(QSGTexture) + +class Window : public QWindow +{ + Q_OBJECT +public: + Window(); + ~Window(); + + void addItems(); + void update(); + +private: + void timerEvent(QTimerEvent *); + void exposeEvent(QExposeEvent *); + void mousePressEvent(QMouseEvent *); + void keyPressEvent(QKeyEvent *); + + void sync(); + void render(); + void initialize(); + void invalidate(); + + bool m_initialized; + QScopedPointer<QOpenGLContext> m_context; + QScopedPointer<QSGEngine> m_sgEngine; + QScopedPointer<QSGRootNode> m_sgRootNode; + QScopedPointer<QSGAbstractRenderer> m_sgRenderer; + QScopedPointer<QSGTexture> m_smileTexture; + QScopedPointer<QSGTexture> m_qtTexture; + QList<QSharedPointer<Item> > m_items; + + QBasicTimer m_updateTimer; + QAnimationDriver m_animationDriver; +}; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 12be33d790..f1d2f63756 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -350,9 +350,9 @@ void QQuickWindowPrivate::syncSceneGraph() // Copy the current state of clearing from window into renderer. renderer->setClearColor(clearColor); - QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer; + QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearStencilBuffer | QSGAbstractRenderer::ClearDepthBuffer; if (clearBeforeRendering) - mode |= QSGRenderer::ClearColorBuffer; + mode |= QSGAbstractRenderer::ClearColorBuffer; renderer->setClearMode(mode); renderer->setCustomRenderMode(customRenderMode); diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp new file mode 100644 index 0000000000..9409822c8f --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgabstractrenderer_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSGAbstractRenderer + \brief QSGAbstractRenderer gives access to the scene graph nodes and rendering of a QSGEngine. + \inmodule QtQuick + \since 5.4 + + A QSGAbstractRenderer created by a QSGEngine allows you to set your QSGNode + tree through setRootNode() and control the rendering viewport through + setDeviceRect(), setViewportRect() and setProjectionMatrixToRect(). + You can finally trigger the rendering to the desired framebuffer through + renderScene(). + + The QSGAbstractRenderer is only available when used with a QSGEngine + and isn't exposed when used internally by QQuickWindow. + + \sa QSGEngine, QSGNode + */ + +/*! + \enum QSGAbstractRenderer::ClearModeBit + + Used with setClearMode() to indicate which buffer should + be cleared before the scene render. + + \value ClearColorBuffer Clear the color buffer using clearColor(). + \value ClearDepthBuffer Clear the depth buffer. + \value ClearStencilBuffer Clear the stencil buffer. + + \sa setClearMode(), setClearColor() + */ + +/*! + \fn void QSGAbstractRenderer::renderScene(GLuint fboId = 0) + + Render the scene to the specified \a fboId + + If \a fboId isn't specified, the scene graph will be rendered + to the default framebuffer. You will have to call + QOpenGLContext::swapBuffers() yourself afterward. + + The framebuffer specified by \a fboId will be bound automatically. + + \sa QOpenGLContext::swapBuffers(), QOpenGLFramebufferObject::handle() + */ + +/*! + \fn void QSGAbstractRenderer::sceneGraphChanged() + + This signal is emitted on the first modification of a node in + the tree after the last scene render. + */ + +/*! + \internal + */ +QSGAbstractRendererPrivate::QSGAbstractRendererPrivate() + : m_root_node(0) + , m_clear_color(Qt::transparent) + , m_clear_mode(QSGAbstractRenderer::ClearColorBuffer | QSGAbstractRenderer::ClearDepthBuffer) +{ +} + +/*! + \internal + */ +QSGAbstractRenderer::QSGAbstractRenderer(QObject *parent) + : QObject(*new QSGAbstractRendererPrivate, parent) +{ +} + +/*! + \internal + */ +QSGAbstractRenderer::~QSGAbstractRenderer() +{ +} + +/*! + Sets the \a node as the root of the QSGNode scene + that you want to render. You need to provide a \a node + before trying to render the scene. + + \note This doesn't take ownership of \a node. + + \sa rootNode() +*/ +void QSGAbstractRenderer::setRootNode(QSGRootNode *node) +{ + Q_D(QSGAbstractRenderer); + if (d->m_root_node == node) + return; + if (d->m_root_node) { + d->m_root_node->m_renderers.removeOne(this); + nodeChanged(d->m_root_node, QSGNode::DirtyNodeRemoved); + } + d->m_root_node = node; + if (d->m_root_node) { + Q_ASSERT(!d->m_root_node->m_renderers.contains(this)); + d->m_root_node->m_renderers << this; + nodeChanged(d->m_root_node, QSGNode::DirtyNodeAdded); + } +} + +/*! + Returns the root of the QSGNode scene. + + \sa setRootNode() +*/ +QSGRootNode *QSGAbstractRenderer::rootNode() const +{ + Q_D(const QSGAbstractRenderer); + return d->m_root_node; +} + + +/*! + \fn void QSGAbstractRenderer::setDeviceRect(const QSize &size) + \overload + + Sets the \a size of the surface being rendered to. + + \sa deviceRect() + */ + +/*! + Sets \a rect as the geometry of the surface being rendered to. + + \sa deviceRect() + */ +void QSGAbstractRenderer::setDeviceRect(const QRect &rect) +{ + Q_D(QSGAbstractRenderer); + d->m_device_rect = rect; +} + +/*! + Returns the device rect of the surface being rendered to. + + \sa setDeviceRect() + */ +QRect QSGAbstractRenderer::deviceRect() const +{ + Q_D(const QSGAbstractRenderer); + return d->m_device_rect; +} + +/*! + \fn void QSGAbstractRenderer::setViewportRect(const QSize &size) + \overload + + Sets the \a size of the viewport to render + on the surface. + + \sa viewportRect() + */ + +/*! + Sets \a rect as the geometry of the viewport to render + on the surface. + + \sa viewportRect() + */ +void QSGAbstractRenderer::setViewportRect(const QRect &rect) +{ + Q_D(QSGAbstractRenderer); + d->m_viewport_rect = rect; +} + +/*! + Returns the rect of the viewport to render. + + \sa setViewportRect() + */ +QRect QSGAbstractRenderer::viewportRect() const +{ + Q_D(const QSGAbstractRenderer); + return d->m_viewport_rect; +} + +/*! + Convenience method that calls setProjectionMatrix() with an + orthographic matrix generated from \a rect. + + \sa setProjectionMatrix(), projectionMatrix() + */ +void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect) +{ + QMatrix4x4 matrix; + matrix.ortho(rect.x(), + rect.x() + rect.width(), + rect.y() + rect.height(), + rect.y(), + 1, + -1); + setProjectionMatrix(matrix); +} + +/*! + Use \a matrix to project the QSGNode coordinates onto surface pixels. + + \sa projectionMatrix(), setProjectionMatrixToRect() + */ +void QSGAbstractRenderer::setProjectionMatrix(const QMatrix4x4 &matrix) +{ + Q_D(QSGAbstractRenderer); + d->m_projection_matrix = matrix; +} + +/*! + Returns the projection matrix + + \sa setProjectionMatrix(), setProjectionMatrixToRect() + */ +QMatrix4x4 QSGAbstractRenderer::projectionMatrix() const +{ + Q_D(const QSGAbstractRenderer); + return d->m_projection_matrix; +} + +/*! + Use \a color to clear the framebuffer when clearMode() is + set to QSGAbstractRenderer::ClearColorBuffer. + + \sa clearColor(), setClearMode() + */ +void QSGAbstractRenderer::setClearColor(const QColor &color) +{ + Q_D(QSGAbstractRenderer); + d->m_clear_color = color; +} + +/*! + Returns the color that clears the framebuffer at the beginning + of the rendering. + + \sa setClearColor(), clearMode() + */ +QColor QSGAbstractRenderer::clearColor() const +{ + Q_D(const QSGAbstractRenderer); + return d->m_clear_color; +} + +/*! + Defines which attachment of the framebuffer should be cleared + before each scene render with the \a mode flag. + + \sa clearMode(), setClearColor() + */ +void QSGAbstractRenderer::setClearMode(ClearMode mode) +{ + Q_D(QSGAbstractRenderer); + d->m_clear_mode = mode; +} + +/*! + Flags defining which attachment of the framebuffer will be cleared + before each scene render. + + \sa setClearMode(), clearColor() + */ +QSGAbstractRenderer::ClearMode QSGAbstractRenderer::clearMode() const +{ + Q_D(const QSGAbstractRenderer); + return d->m_clear_mode; +} + +/*! + \fn void QSGAbstractRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) + \internal + */ + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h new file mode 100644 index 0000000000..671d62e931 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGABSTRACTRENDERER_H +#define QSGABSTRACTRENDERER_H + +#include "qsgnode.h" + +QT_BEGIN_NAMESPACE + +class QSGAbstractRendererPrivate; + +class Q_QUICK_EXPORT QSGAbstractRenderer : public QObject +{ + Q_OBJECT +public: + enum ClearModeBit + { + ClearColorBuffer = 0x0001, + ClearDepthBuffer = 0x0002, + ClearStencilBuffer = 0x0004 + }; + Q_DECLARE_FLAGS(ClearMode, ClearModeBit) + + virtual ~QSGAbstractRenderer(); + + void setRootNode(QSGRootNode *node); + QSGRootNode *rootNode() const; + void setDeviceRect(const QRect &rect); + inline void setDeviceRect(const QSize &size) { setDeviceRect(QRect(QPoint(), size)); } + QRect deviceRect() const; + + void setViewportRect(const QRect &rect); + inline void setViewportRect(const QSize &size) { setViewportRect(QRect(QPoint(), size)); } + QRect viewportRect() const; + + void setProjectionMatrixToRect(const QRectF &rect); + void setProjectionMatrix(const QMatrix4x4 &matrix); + QMatrix4x4 projectionMatrix() const; + + void setClearColor(const QColor &color); + QColor clearColor() const; + + void setClearMode(ClearMode mode); + ClearMode clearMode() const; + + virtual void renderScene(GLuint fboId = 0) = 0; + +Q_SIGNALS: + void sceneGraphChanged(); + +protected: + QSGAbstractRenderer(QObject *parent = 0); + virtual void nodeChanged(QSGNode *node, QSGNode::DirtyState state) = 0; + +private: + Q_DECLARE_PRIVATE(QSGAbstractRenderer) + friend class QSGRootNode; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGAbstractRenderer::ClearMode) + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h new file mode 100644 index 0000000000..c60a488cc8 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGABSTRACTRENDERER_P_H +#define QSGABSTRACTRENDERER_P_H + +#include "qsgabstractrenderer.h" + +#include "qsgnode.h" +#include <qcolor.h> + +#include <QtCore/private/qobject_p.h> +#include <QtQuick/private/qtquickglobal_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGAbstractRendererPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QSGAbstractRenderer) +public: + static const QSGAbstractRendererPrivate *get(const QSGAbstractRenderer *q) { return q->d_func(); } + + QSGAbstractRendererPrivate(); + void updateProjectionMatrix(); + + QSGRootNode *m_root_node; + QColor m_clear_color; + QSGAbstractRenderer::ClearMode m_clear_mode; + + QRect m_device_rect; + QRect m_viewport_rect; + + QMatrix4x4 m_projection_matrix; + uint m_mirrored : 1; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h index 380b7e01e1..be93fd1fce 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.h +++ b/src/quick/scenegraph/coreapi/qsgnode.h @@ -53,6 +53,7 @@ QT_BEGIN_NAMESPACE #define QSG_RUNTIME_DESCRIPTION #endif +class QSGAbstractRenderer; class QSGRenderer; class QSGNode; @@ -298,11 +299,11 @@ public: private: void notifyNodeChange(QSGNode *node, DirtyState state); - friend class QSGRenderer; + friend class QSGAbstractRenderer; friend class QSGNode; friend class QSGGeometryNode; - QList<QSGRenderer *> m_renderers; + QList<QSGAbstractRenderer *> m_renderers; }; diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index c056507729..3c9042fee5 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -40,18 +40,9 @@ ****************************************************************************/ #include "qsgrenderer_p.h" -#include "qsgnode.h" -#include "qsgmaterial.h" #include "qsgnodeupdater_p.h" -#include "qsggeometry_p.h" - -#include <private/qsgadaptationlayer_p.h> -#include <private/qsgshadersourcebuilder_p.h> #include <qopenglframebufferobject.h> -#include <QtGui/qguiapplication.h> - -#include <qdatetime.h> #include <private/qquickprofiler_p.h> @@ -63,12 +54,12 @@ static QElapsedTimer frameTimer; static qint64 preprocessTime; static qint64 updatePassTime; -void QSGBindable::clear(QSGRenderer::ClearMode mode) const +void QSGBindable::clear(QSGAbstractRenderer::ClearMode mode) const { GLuint bits = 0; - if (mode & QSGRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT; - if (mode & QSGRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT; - if (mode & QSGRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT; + if (mode & QSGAbstractRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT; + if (mode & QSGAbstractRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT; + if (mode & QSGAbstractRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT; QOpenGLContext::currentContext()->functions()->glClear(bits); } @@ -118,18 +109,13 @@ void QSGBindableFboId::bind() const QSGRenderer::QSGRenderer(QSGRenderContext *context) - : QObject() - , m_clear_color(Qt::transparent) - , m_clear_mode(ClearColorBuffer | ClearDepthBuffer) - , m_current_opacity(1) + : m_current_opacity(1) , m_current_determinant(1) , m_device_pixel_ratio(1) , m_context(context) - , m_root_node(0) , m_node_updater(0) , m_bindable(0) , m_changed_emitted(false) - , m_mirrored(false) , m_is_rendering(false) { } @@ -169,37 +155,30 @@ void QSGRenderer::setNodeUpdater(QSGNodeUpdater *updater) m_node_updater = updater; } - -void QSGRenderer::setRootNode(QSGRootNode *node) +bool QSGRenderer::isMirrored() const { - if (m_root_node == node) - return; - if (m_root_node) { - m_root_node->m_renderers.removeOne(this); - nodeChanged(m_root_node, QSGNode::DirtyNodeRemoved); - } - m_root_node = node; - if (m_root_node) { - Q_ASSERT(!m_root_node->m_renderers.contains(this)); - m_root_node->m_renderers << this; - nodeChanged(m_root_node, QSGNode::DirtyNodeAdded); - } + QMatrix4x4 matrix = projectionMatrix(); + // Mirrored relative to the usual Qt coordinate system with origin in the top left corner. + return matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0; } - -void QSGRenderer::renderScene() +void QSGRenderer::renderScene(GLuint fboId) { - class B : public QSGBindable - { - public: - void bind() const { QOpenGLFramebufferObject::bindDefault(); } - } b; - renderScene(b); + if (fboId) { + QSGBindableFboId bindable(fboId); + renderScene(bindable); + } else { + class B : public QSGBindable + { + public: + void bind() const { QOpenGLFramebufferObject::bindDefault(); } + } bindable; + renderScene(bindable); + } } - void QSGRenderer::renderScene(const QSGBindable &bindable) { - if (!m_root_node) + if (!rootNode()) return; m_is_rendering = true; @@ -255,30 +234,6 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) } -void QSGRenderer::setProjectionMatrixToRect(const QRectF &rect) -{ - QMatrix4x4 matrix; - matrix.ortho(rect.x(), - rect.x() + rect.width(), - rect.y() + rect.height(), - rect.y(), - 1, - -1); - setProjectionMatrix(matrix); -} - -void QSGRenderer::setProjectionMatrix(const QMatrix4x4 &matrix) -{ - m_projection_matrix = matrix; - // Mirrored relative to the usual Qt coordinate system with origin in the top left corner. - m_mirrored = matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0; -} - -void QSGRenderer::setClearColor(const QColor &color) -{ - m_clear_color = color; -} - /*! Updates internal data structures and emits the sceneGraphChanged() signal. @@ -309,7 +264,8 @@ void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) void QSGRenderer::preprocess() { - Q_ASSERT(m_root_node); + QSGRootNode *root = rootNode(); + Q_ASSERT(root); // We need to take a copy here, in case any of the preprocess calls deletes a node that // is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs @@ -319,7 +275,7 @@ void QSGRenderer::preprocess() for (QSet<QSGNode *>::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) { QSGNode *n = *it; - if (!nodeUpdater()->isNodeBlocked(n, m_root_node)) { + if (!nodeUpdater()->isNodeBlocked(n, root)) { n->preprocess(); } } @@ -328,7 +284,7 @@ void QSGRenderer::preprocess() if (profileFrames) preprocessTime = frameTimer.nsecsElapsed(); - nodeUpdater()->updateStates(m_root_node); + nodeUpdater()->updateStates(root); if (profileFrames) updatePassTime = frameTimer.nsecsElapsed(); diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h index e24a6b652f..e6e7c13a1d 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -42,54 +42,28 @@ #ifndef QSGRENDERER_P_H #define QSGRENDERER_P_H -#include <qcolor.h> -#include <qset.h> -#include <qhash.h> - +#include "qsgabstractrenderer.h" +#include "qsgabstractrenderer_p.h" #include "qsgnode.h" #include "qsgmaterial.h" -#include <QtQuick/qsgtexture.h> #include <QtQuick/private/qsgcontext_p.h> QT_BEGIN_NAMESPACE -class QSGMaterialShader; -struct QSGMaterialType; -class QOpenGLFramebufferObject; -class TextureReference; class QSGBindable; class QSGNodeUpdater; Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_fatal_render_error(); Q_QUICK_PRIVATE_EXPORT void qsg_set_fatal_renderer_error(); -class Q_QUICK_PRIVATE_EXPORT QSGRenderer : public QObject +class Q_QUICK_PRIVATE_EXPORT QSGRenderer : public QSGAbstractRenderer { - Q_OBJECT public: - enum ClearModeBit - { - ClearColorBuffer = 0x0001, - ClearDepthBuffer = 0x0002, - ClearStencilBuffer = 0x0004 - }; - Q_DECLARE_FLAGS(ClearMode, ClearModeBit) QSGRenderer(QSGRenderContext *context); virtual ~QSGRenderer(); - void setRootNode(QSGRootNode *node); - QSGRootNode *rootNode() const { return m_root_node; } - - void setDeviceRect(const QRect &rect) { m_device_rect = rect; } - inline void setDeviceRect(const QSize &size) { setDeviceRect(QRect(QPoint(), size)); } - QRect deviceRect() const { return m_device_rect; } - - void setViewportRect(const QRect &rect) { m_viewport_rect = rect; } - inline void setViewportRect(const QSize &size) { setViewportRect(QRect(QPoint(), size)); } - QRect viewportRect() const { return m_viewport_rect; } - // Accessed by QSGMaterialShader::RenderState. QMatrix4x4 currentProjectionMatrix() const { return m_current_projection_matrix; } QMatrix4x4 currentModelViewMatrix() const { return m_current_model_view_matrix; } @@ -99,36 +73,22 @@ public: void setDevicePixelRatio(qreal ratio) { m_device_pixel_ratio = ratio; } qreal devicePixelRatio() const { return m_device_pixel_ratio; } - - virtual void setProjectionMatrixToRect(const QRectF &rect); - void setProjectionMatrix(const QMatrix4x4 &matrix); - QMatrix4x4 projectionMatrix() const { return m_projection_matrix; } - bool isMirrored() const { return m_mirrored; } - - void setClearColor(const QColor &color); - QColor clearColor() const { return m_clear_color; } - QSGRenderContext *context() const { return m_context; } - void renderScene(); + bool isMirrored() const; void renderScene(const QSGBindable &bindable); - virtual void nodeChanged(QSGNode *node, QSGNode::DirtyState state); + virtual void renderScene(GLuint fboId = 0) Q_DECL_FINAL Q_DECL_OVERRIDE; + virtual void nodeChanged(QSGNode *node, QSGNode::DirtyState state) Q_DECL_OVERRIDE; QSGNodeUpdater *nodeUpdater() const; void setNodeUpdater(QSGNodeUpdater *updater); inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const; - void setClearMode(ClearMode mode) { m_clear_mode = mode; } - ClearMode clearMode() const { return m_clear_mode; } - virtual void setCustomRenderMode(const QByteArray &) { }; void clearChangedFlag() { m_changed_emitted = false; } -Q_SIGNALS: - void sceneGraphChanged(); // Add, remove, ChangeFlags changes... - protected: virtual void render() = 0; @@ -141,8 +101,6 @@ protected: void markNodeDirtyState(QSGNode *node, QSGNode::DirtyState state) { node->m_dirtyState |= state; } - QColor m_clear_color; - ClearMode m_clear_mode; QMatrix4x4 m_current_projection_matrix; QMatrix4x4 m_current_model_view_matrix; qreal m_current_opacity; @@ -152,31 +110,22 @@ protected: QSGRenderContext *m_context; private: - QSGRootNode *m_root_node; QSGNodeUpdater *m_node_updater; - QRect m_device_rect; - QRect m_viewport_rect; - QSet<QSGNode *> m_nodes_to_preprocess; - QMatrix4x4 m_projection_matrix; - const QSGBindable *m_bindable; uint m_changed_emitted : 1; - uint m_mirrored : 1; uint m_is_rendering : 1; }; -Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderer::ClearMode) - class Q_QUICK_PRIVATE_EXPORT QSGBindable { public: virtual ~QSGBindable() { } virtual void bind() const = 0; - virtual void clear(QSGRenderer::ClearMode mode) const; + virtual void clear(QSGAbstractRenderer::ClearMode mode) const; virtual void reactivate() const; }; diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 1a3565b630..5bc91992e9 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -363,6 +363,7 @@ QSGRenderContext::QSGRenderContext(QSGContext *context) , m_distanceFieldCacheManager(0) , m_brokenIBOs(false) , m_serializedRender(false) + , m_attachToGLContext(true) { } @@ -384,12 +385,7 @@ void QSGRenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fboId) if (m_serializedRender) qsg_framerender_mutex.lock(); - if (fboId) { - QSGBindableFboId bindable(fboId); - renderer->renderScene(bindable); - } else { - renderer->renderScene(); - } + renderer->renderScene(fboId); if (m_serializedRender) qsg_framerender_mutex.unlock(); @@ -441,6 +437,12 @@ QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRaw return cache; } +void QSGRenderContext::setAttachToGLContext(bool attach) +{ + Q_ASSERT(!isValid()); + m_attachToGLContext = attach; +} + #define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext" QSGRenderContext *QSGRenderContext::from(QOpenGLContext *context) @@ -473,7 +475,10 @@ void QSGRenderContext::initialize(QOpenGLContext *context) Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!"); m_gl = context; - m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this)); + if (m_attachToGLContext) { + Q_ASSERT(!context->property(QSG_RENDERCONTEXT_PROPERTY).isValid()); + context->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this)); + } m_sg->renderContextInitialized(this); #ifdef Q_OS_LINUX @@ -541,7 +546,8 @@ void QSGRenderContext::invalidate() delete m_distanceFieldCacheManager; m_distanceFieldCacheManager = 0; - m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant()); + if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this)) + m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant()); m_gl = 0; m_sg->renderContextInvalidated(this); diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index faad57b893..7113ef8530 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -97,6 +97,7 @@ public: QOpenGLContext *openglContext() const { return m_gl; } QSGContext *sceneGraphContext() const { return m_sg; } + bool isValid() const { return m_gl; } virtual void initialize(QOpenGLContext *context); virtual void invalidate(); @@ -117,6 +118,7 @@ public: virtual void compile(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0); virtual void initialize(QSGMaterialShader *shader); + void setAttachToGLContext(bool attach); void registerFontengineForCleanup(QFontEngine *engine); static QSGRenderContext *from(QOpenGLContext *context); @@ -146,6 +148,7 @@ protected: bool m_brokenIBOs; bool m_serializedRender; + bool m_attachToGLContext; }; diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 570d6b92b5..d08fce0336 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -10,12 +10,15 @@ HEADERS += \ $$PWD/coreapi/qsgnode.h \ $$PWD/coreapi/qsgnode_p.h \ $$PWD/coreapi/qsgnodeupdater_p.h \ + $$PWD/coreapi/qsgabstractrenderer.h \ + $$PWD/coreapi/qsgabstractrenderer_p.h \ $$PWD/coreapi/qsgrenderer_p.h \ $$PWD/coreapi/qsgrendernode_p.h \ $$PWD/coreapi/qsggeometry_p.h \ $$PWD/coreapi/qsgmaterialshader_p.h SOURCES += \ + $$PWD/coreapi/qsgabstractrenderer.cpp \ $$PWD/coreapi/qsgbatchrenderer.cpp \ $$PWD/coreapi/qsggeometry.cpp \ $$PWD/coreapi/qsgmaterial.cpp \ @@ -30,6 +33,8 @@ HEADERS += \ $$PWD/util/qsgareaallocator_p.h \ $$PWD/util/qsgatlastexture_p.h \ $$PWD/util/qsgdepthstencilbuffer_p.h \ + $$PWD/util/qsgengine.h \ + $$PWD/util/qsgengine_p.h \ $$PWD/util/qsgflatcolormaterial.h \ $$PWD/util/qsgsimplematerial.h \ $$PWD/util/qsgsimplerectnode.h \ @@ -48,6 +53,7 @@ SOURCES += \ $$PWD/util/qsgareaallocator.cpp \ $$PWD/util/qsgatlastexture.cpp \ $$PWD/util/qsgdepthstencilbuffer.cpp \ + $$PWD/util/qsgengine.cpp \ $$PWD/util/qsgflatcolormaterial.cpp \ $$PWD/util/qsgsimplerectnode.cpp \ $$PWD/util/qsgsimpletexturenode.cpp \ diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp new file mode 100644 index 0000000000..127f624d8b --- /dev/null +++ b/src/quick/scenegraph/util/qsgengine.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgengine_p.h" + +#include <QtQuick/qsgtexture.h> +#include <private/qsgcontext_p.h> +#include <private/qsgrenderer_p.h> +#include <private/qsgtexture_p.h> + +QT_BEGIN_NAMESPACE + + +/*! + \class QSGEngine + \brief The QSGEngine class allows low level rendering of a scene graph. + \inmodule QtQuick + \since 5.4 + + A QSGEngine can be used to render a tree of QSGNode directly on a QWindow + or QOpenGLFramebufferObject without any integration with QML, QQuickWindow + or QQuickItem and the convenience that they provide. + + This means that you must handle event propagation, animation timing, + and node lifetime yourself. + + \note This class is for very low level access to an independent scene graph. + Most of the time you will instead want to subclass QQuickItem and insert + your QSGNode in a normal QtQuick scene by overriding QQuickItem::updatePaintNode(). + + \sa QSGAbstractRenderer + */ + +/*! + \enum QSGEngine::CreateTextureOption + + The CreateTextureOption enums are used to customize how a texture is wrapped. + + \value TextureHasAlphaChannel The texture has an alpha channel and should + be drawn using blending. + + \value TextureOwnsGLTexture The texture object owns the texture id and + will delete the GL texture when the texture object is deleted. + + \value TextureCanUseAtlas The image can be uploaded into a texture atlas. + */ + +QSGEnginePrivate::QSGEnginePrivate() + : sgContext(QSGContext::createDefaultContext()) + , sgRenderContext(new QSGRenderContext(sgContext.data())) +{ +} + +/*! + Constructs a new QSGEngine with its \a parent + */ +QSGEngine::QSGEngine(QObject *parent) + : QObject(*(new QSGEnginePrivate), parent) +{ +} + +/*! + Destroys the engine + */ +QSGEngine::~QSGEngine() +{ +} + +/*! + Initialize the engine with \a context. + + \warning You have to make sure that you call + QOpenGLContext::makeCurrent() on \a context before calling this. + */ +void QSGEngine::initialize(QOpenGLContext *context) +{ + Q_D(QSGEngine); + if (QOpenGLContext::currentContext() != context) { + qWarning("WARNING: The context must be current before calling QSGEngine::initialize."); + return; + } + + if (!d->sgRenderContext->isValid()) { + d->sgRenderContext->setAttachToGLContext(false); + d->sgRenderContext->initialize(context); + connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate); + } +} + +/*! + Invalidate the engine releasing its resources + + You will have to call initialize() and createRenderer() if you + want to use it again. + */ +void QSGEngine::invalidate() +{ + Q_D(QSGEngine); + d->sgRenderContext->invalidate(); +} + +/*! + Returns a renderer that can be used to render a QSGNode tree + + You call initialize() first with the QOpenGLContext that you + want to use with this renderer. This will return a null + renderer otherwise. + */ +QSGAbstractRenderer *QSGEngine::createRenderer() const +{ + Q_D(const QSGEngine); + if (!d->sgRenderContext->isValid()) + return 0; + + QSGRenderer *renderer = d->sgRenderContext->createRenderer(); + renderer->setCustomRenderMode(qgetenv("QSG_VISUALIZE")); + return renderer; +} + +/*! + Creates a texture using the data of \a image + + Valid \a options are TextureCanUseAtlas + + The caller takes ownership of the texture and the + texture should only be used with this engine. + + \sa createTextureFromId(), QSGSimpleTextureNode::setOwnsTexture(), QQuickWindow::createTextureFromImage() + */ +QSGTexture *QSGEngine::createTextureFromImage(const QImage &image, CreateTextureOptions options) const +{ + Q_D(const QSGEngine); + if (!d->sgRenderContext->isValid()) + return 0; + + if (options & TextureCanUseAtlas) + return d->sgRenderContext->createTexture(image); + else + return d->sgRenderContext->createTextureNoAtlas(image); +} + +/*! + Creates a texture object that wraps the GL texture \a id uploaded with \a size + + Valid \a options are TextureHasAlphaChannel and TextureOwnsGLTexture + + The caller takes ownership of the texture object and the + texture should only be used with this engine. + + \sa createTextureFromImage(), QSGSimpleTextureNode::setOwnsTexture(), QQuickWindow::createTextureFromId() + */ +QSGTexture *QSGEngine::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const +{ + Q_D(const QSGEngine); + if (d->sgRenderContext->isValid()) { + QSGPlainTexture *texture = new QSGPlainTexture(); + texture->setTextureId(id); + texture->setHasAlphaChannel(options & TextureHasAlphaChannel); + texture->setOwnsTexture(options & TextureOwnsGLTexture); + texture->setTextureSize(size); + return texture; + } + return 0; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h new file mode 100644 index 0000000000..7aae882a70 --- /dev/null +++ b/src/quick/scenegraph/util/qsgengine.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGENGINE_H +#define QSGENGINE_H + +#include <QtCore/QObject> +#include <QtQuick/qtquickglobal.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; +class QSGAbstractRenderer; +class QSGEnginePrivate; +class QSGTexture; + +class Q_QUICK_EXPORT QSGEngine : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QSGEngine) +public: + enum CreateTextureOption { + TextureHasAlphaChannel = 0x0001, + TextureOwnsGLTexture = 0x0004, + TextureCanUseAtlas = 0x0008 + }; + Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption) + + QSGEngine(QObject *parent = 0); + ~QSGEngine(); + + void initialize(QOpenGLContext *context); + void invalidate(); + + QSGAbstractRenderer *createRenderer() const; + QSGTexture *createTextureFromImage(const QImage &image, CreateTextureOptions options = CreateTextureOption(0)) const; + QSGTexture *createTextureFromId(uint id, const QSize &size, CreateTextureOptions options = CreateTextureOption(0)) const; +}; + +QT_END_NAMESPACE + +#endif // QSGENGINE_H diff --git a/src/quick/scenegraph/util/qsgengine_p.h b/src/quick/scenegraph/util/qsgengine_p.h new file mode 100644 index 0000000000..a7e1599fd5 --- /dev/null +++ b/src/quick/scenegraph/util/qsgengine_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGENGINE_P_H +#define QSGENGINE_P_H + +#include "qsgengine.h" +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QSGContext; +class QSGRenderContext; + +class QSGEnginePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QSGEngine) + +public: + QSGEnginePrivate(); + + QScopedPointer<QSGContext> sgContext; + QScopedPointer<QSGRenderContext> sgRenderContext; +}; + +QT_END_NAMESPACE + +#endif // QSGENGINE_P_H -- GitLab