From 217e4bfbaa4422755ce8dbb9072dbd0d57a80fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 27 Jan 2022 16:23:45 +0100 Subject: [PATCH] Shaders: fix MeshVisualizer multidraw vertex ID test colormap wraparound. Forgot that gl_VertexID includes the base offset in the multidraw scenarios, so we need to take all vertices into account, not just the largest view. The wraparound would cause nasty output differences among drivers. --- .../Shaders/Test/MeshVisualizerGLTest.cpp | 22 +++++++++++++----- .../multidraw-vertexid2D.tga | Bin 6639 -> 6638 bytes .../multidraw-vertexid3D.tga | Bin 5818 -> 5821 bytes 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index f14242248..ea3972327 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -3924,7 +3924,8 @@ void MeshVisualizerGLTest::renderMulti2D() { if(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader) for(Trade::MeshData* i: {&circleData, &squareData, &triangleData}) *i = MeshTools::duplicate(*i); - GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({circleData, squareData, triangleData})); + Trade::MeshData concatenated = MeshTools::concatenate({circleData, squareData, triangleData}); + GL::Mesh mesh = MeshTools::compile(concatenated); GL::MeshView circle{mesh}; circle.setCount(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader ? circleData.vertexCount() : circleData.indexCount()); @@ -3952,7 +3953,9 @@ void MeshVisualizerGLTest::renderMulti2D() { .setColor(0xffffcc_rgbf) .setWireframeColor(0xcc0000_rgbf); if(data.flags & MeshVisualizerGL2D::Flag::VertexId) - materialData[0*data.uniformIncrement].setColorMapTransformation(0.5f/circleData.vertexCount(), 1.0f/circleData.vertexCount()); + /* Here, gl_VertexID is taken *including* the base offset, which means + we have to count all vertices to avoid colormap wraparounds */ + materialData[0*data.uniformIncrement].setColorMapTransformation(0.5f/concatenated.vertexCount(), 1.0f/concatenated.vertexCount()); else if(data.flags & MeshVisualizerGL2D::Flag::ObjectId) /* There's at most 4 colors (one every 2 faces) per draw and 3 draws, so make it fit 12 colors */ @@ -3962,7 +3965,9 @@ void MeshVisualizerGLTest::renderMulti2D() { .setWireframeColor(0x0000cc_rgbf) .setWireframeWidth(2.5f); if(data.flags & MeshVisualizerGL2D::Flag::VertexId) - materialData[1*data.uniformIncrement].setColorMapTransformation(0.5f/triangleData.vertexCount(), 1.0f/triangleData.vertexCount()); + /* Here, gl_VertexID is taken *including* the base offset, which means + we have to count all vertices to avoid colormap wraparounds */ + materialData[1*data.uniformIncrement].setColorMapTransformation(0.5f/concatenated.vertexCount(), 1.0f/concatenated.vertexCount()); else if(data.flags & MeshVisualizerGL2D::Flag::ObjectId) /* There's at most 4 colors (one every 2 faces) per draw and 3 draws, so make it fit 12 colors */ @@ -4154,7 +4159,8 @@ void MeshVisualizerGLTest::renderMulti3D() { if(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader) for(Trade::MeshData* i: {&sphereData, &planeData, &coneData}) *i = MeshTools::duplicate(*i); - GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({sphereData, planeData, coneData})); + Trade::MeshData concatenated = MeshTools::concatenate({sphereData, planeData, coneData}); + GL::Mesh mesh = MeshTools::compile(concatenated); GL::MeshView sphere{mesh}; sphere.setCount(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader ? sphereData.vertexCount() : sphereData.indexCount()); @@ -4189,7 +4195,9 @@ void MeshVisualizerGLTest::renderMulti3D() { .setWireframeColor(0xcc0000_rgbf) .setLineLength(0.0f); /* no TBN */ if(data.flags & MeshVisualizerGL3D::Flag::VertexId) - materialData[0*data.uniformIncrement].setColorMapTransformation(0.5f/sphereData.vertexCount(), 1.0f/sphereData.vertexCount()); + /* Here, gl_VertexID is taken *including* the base offset, which means + we have to count all vertices to avoid colormap wraparounds */ + materialData[0*data.uniformIncrement].setColorMapTransformation(0.5f/concatenated.vertexCount(), 1.0f/concatenated.vertexCount()); else if(data.flags & MeshVisualizerGL3D::Flag::ObjectId) /* There's at most 10 colors (one every 2 faces) per draw and 3 draws, so make it fit 30 colors */ @@ -4200,7 +4208,9 @@ void MeshVisualizerGLTest::renderMulti3D() { .setLineLength(0.25f) .setWireframeWidth(2.5f); if(data.flags & MeshVisualizerGL3D::Flag::VertexId) - materialData[1*data.uniformIncrement].setColorMapTransformation(0.5f/coneData.vertexCount(), 1.0f/coneData.vertexCount()); + /* Here, gl_VertexID is taken *including* the base offset, which means + we have to count all vertices to avoid colormap wraparounds */ + materialData[1*data.uniformIncrement].setColorMapTransformation(0.5f/concatenated.vertexCount(), 1.0f/concatenated.vertexCount()); else if(data.flags & MeshVisualizerGL3D::Flag::ObjectId) /* There's at most 10 colors (one every 2 faces) per draw and 3 draws, so make it fit 30 colors */ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-vertexid2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-vertexid2D.tga index fc8195fd8ee3878d24891fe5dc00caa5b6f17279..024ee375f20af71c1d02d9f741dd96a7ce1a1558 100644 GIT binary patch literal 6638 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 H*SUWIQ?#}% literal 6639 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 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-vertexid3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-vertexid3D.tga index e225aca568339d3086bf4bf33bba56e091baab62..1c1826a69f890fc44ac7e618ef2afc03874780a9 100644 GIT binary patch literal 5821 zcmb`KcT`kY9>*0sOHfebFtLDuAOcdRKFTn|%$p=8(b&b_TVji`iw!ACM>+_Ipa>`+ zMLI*7_DI<@ce5v(O?Gp(oXxHU5h;`X-p3m=C!0NI{}|7E=Xz%D{oc?0-d`EUjIkYK z{|Yyddwf`}IezP>jRN zD!(`U_JQHI4h_F~Wa#yyL-esh<%z-YQ-ks|FJzG~Le38aUmT#K1|-qXMX}F?as7eu z{r-v1{F3{8Q~JEqo-Rs%>Xr4xGy920?qm1-M{b;QGDdqQNqKh%eM}m z{q zdx62b7YD8JkSui-@9_(Y43(w6r6^gis@+R>oKPELXfw{fk)>92hr>z4Xw#djsM$@Q z36a?!m4sr5l7|7~kSJuE54GG)yw!&~5hP1|Ls7U!S+$F9J*w7SB2?tk`1?kdS zH36ezZ@K~=oR@`Nl80TP<;hDFnah;$CPpbg6)b~o8y86<7D|tZLt|C)yp_s|ZFJLN zwV7-0$h%fF%#y2aKm8IXf0eR$o$B@`)y?&)+!e~CHx-B|#27Aeou>qhU|N8+gML$&6ly6Y^hy+G@u zY3)V2^R&9t-C-qdgmhFH4i0u6r5tJ_Dza@=vwauS~xsZwN|v%k`&t-VpU9z3`B-fiLPAO zb2p%?X0fKur@hfjQ}5G#&tHFAY%B^wBSSF2bX^qPb%tac!T{9)xdm7==~mM^E85DD zl7yetbDm~W!(n3Qecz4-@7CspO>G_xoo;nK^BX!{TUtDU40io3F(RQiD~w4{VF|(; zEv9I#3b}=7HDhiuMOe{O3(QIF$JGX;PHt#VWngog$9N3*Bi`SdjmH9F@9 z8aKdq*7_n25Elvf~<%e|N4QOm%Q1S8X>tFNIzjw;~j#v15UfFN?@Ugkov%A_40fEa#SfX+v+Yytf zx@NkKrVUnf(1LC{syIy1mrCGKjo$g@mjo^2JEz=lcqL!(?=f>5JKQyOzIt47UMMOk ziYl5Dx+g@~&bYy}(Td)2Xyn(IBdLqmL%OT{5NHUxk}sW#zjDIm;$|y8eyy&1J`x+( z%HUel6lg^#lgJV?*Lu??E4pUE+7{J*)?gS*g7 zIc^`npSk7y2-9XO`o*b|U(VA=nJx$>1+Uf@)egyk3k4uDw58P(35==&WYS*}RJw~V zf2Ky5wph_A3!2GKa8KecUENe~MtmqEMat9-S%}?8{ zcu-5_)j#d9A~vUN`6qBDK692f>P0n1K{*p}iwP)Y{BJUTHyFP{W^n=Io6q>)Ox7rjWp z7NUVK#D&jOh0oIj&oZ!u;GZ|SU9vIGhEec`Q%*B0ClciaSBbKFtkFHj=yus?W`^=8 z^Uun}B8WfB6gW{*VXC7}xhBU!c6`nf0db5H2z9MjGDP)9Bf2M1vv z1fiD43Kj_dT`2fhk>JN-0E_>4pYwyn5f8MGbv|pDcbc$4YVL6&HRq^q_7UxChqe4e zTESDZ8czM_XEksN{!vOaza^T4l+|>LHsZ)c2UO7cKsTzxKhWd!evi}s9^Srga!gL@ z2EaI}zuyx6?Y8i*cf{Y;NWW_0G)W}B3hTfI7)ue}p6*$@yJqd`dUZ#a;KyIl5&lpv z`b#BId)Ua)v1*V46)-r3okU^g_RbkwJ7;X^oW4mz&P8C#0D}WUMD*voqVMmC|5Obr z>Z5i}Eg$0y{z2_bEZ@d1->kv%=^HhU8#)}#la0}vZYN?Nj4k2Q7z=N|) z-=G;?=&-KcVQu@g)os&OwM|>uY6hr!xm@yxI?2}!)F*9JPdBG?9it;o;jpHCbk)=q zty7n`+P~XkzpTZ6Y0EFxN`Ci1`cYFdgs_9ivNLd8hf6x0~(WYO-6> zWcOy{=%!@hLo95Pe%?ZT)=KHRq|G``p#bBwX{%c8mNa6%-5ZT|uRr*W8c%~Q-C(h* zh5ED&4!}%lD_Ki+X4hihSC%$ovaPD#R#|TwUT3SQo1&3C6jes@RsW8p)^`)n-(>YTUCV7Zc13~L~-Rram7SY#YAEGL_s;3+1Uds zoJiWN54=O>lO5Rk(4P=kHX)#Fg8yyu_}yZs>7-41$peG9-hk@L&A)-s7UJW5O334V zbNr&>(K#R&RU1)?IaPKhG{<`tjayhW&a)5?JV9R&R+1is%FN9y#yEL`Z|OMqJUljT zxq=D?y#pP}D~-${O6(j+*|=odIOECiFJVwN7hh*k$GAD9W>2)4lV&p~)n;~zZyxCg zDB9fW(QjR{#yTa8<;9a8k;)!9K~$6;LKUP<8YP`ongn}U%i7lhSP&~>M-&BA5J4%TD4>Xlf?%Nt z0@4$^?uu&zv4YY|LXw&Hn=rDx^WHn>{SnT&=Z4AseeY+!^Si%0xkH9%4EgytXULo( zw6y-;b`{1_#x1<3wYXGgS*h+HWqPac8~Buy*H;*BtfXwNGTvTo5?F1zyT*Kf zjYV*+Wk{`6n8^B=$R<)`7cE9ki>dKqTB4YdB(_f$J6seyri(%GD71d%1;tv6O0<`j z>MSqQU3FjIyWC)HIe9|`WmBcm)+&>LYSUfSW_xSK2h~~}s-18|WEC#5i4fUFiIEsF zHC9YJBc`7fGtZ0dQ^gKxKb22{x+YzAEv~_^e82Fxm)pLHE-^FQ3%7VZIPG4NPfe*K z2e%>%<*OYP7m)7{VDvF)l(F-QRd}PIh~H%tw6RoaZp=qY)gfW#Dq;R6uWTRT>6spf z-Q9A0g`#7gm06A%Pbo*6%vbfz<6|)p(2w(5@i^7FL*Sf%UJL2TKk>z+HcVKy$Ezk( z_~4W~P!tDvU0LCIOz4)r+_Unaur`gCQ)3qWk>b}o*$q2Pgi|!<6sgcALNyY4ZKUfS zSRjh<6vudoFZ^0{ja8aQ18W|fajia#yO#X>>DzuDHjeuVFKpVB6=-NZ$l$YfAdUDhk6M&8QW_IokIZJE`i7vV*5J!4~ zWtl65S>D3haADPTr>jqmAU+%#W%P_fP1PAk&>e?fZ%>EAwMWKaM0^7oaxJ9AW@jf0 zQfWosf_0K9L2dMtpYd;RR-2dKWQvm}LNXR^^}4vkvm!`Xdr>d|IK5DX4ibR)>M{uE z;z4ydW__C=429^t1vMT1jBy)q}jL$K_xv0%|tf?8Xt>xR3y`7 znMiLQtuKp>CJ52J%VbNobvuVOTPD;i)3ey7CY=lUShm;ewx4i-bZY)+)p!tAsAgfEqUq4S-^|vy*-NXSPBv zi&C6L$31_Nj`GS^p{)cd{^-fm(d|t6WvIzea*!4g^5q4s{vG}07slfjMpYN%VJq!z z1Jd*a*kPD67BL(3_mR%n*UpSm=Ip&fJJ?1b4L~at8fBC)Z?x)c9Zc#gna)NNk>*E0 z#CXxf5Vte#_s|PuNS4Cp5{g_`FiIqhCqHQK-@!J9^yWJ#Qe3dG`3m1%9Zrx2dF7U{ zw4g-&B-QZDQ=zQ{DZbtfrlNt6jPzHZsJG;H8K_kfIz9$9IU*@g2461$27487$@ z3f12x(e~-mT+%jbcC+TBTk6IxysL~uTbd7ZO(WdcNo>2-*tnU`v z4q$rRR-vH;CSKe1ywKf9PXXhvuXdRVg8-@eiAq^#y7n!y_N|m<8B`;Dx|gAL6Hh|3yLi4QE?v(2k6{xX{gS@L3BEv z*gi0Y@x0kK9o5~`JKW_xpplI4{9;$D?%ptNb*Jeu1)h(OT99wgpn!T{2ViRo{cZ&! z%6(DY^`;8FBhc_yFKzIG`mv5tBsC3_kDAjw!nJ<*#J9r*ui?p#ng7FZZy&5{{IS9! z=_zo=qE`0J^Y3vR;Bv_wsXEsNacO}CNM|Y366+MyAW*8mRif+PqqDvnmdCi29q`#A zPWeFVJ|XQDPH55sg*OpSQJ<8fLca)A{Iz);Q=SI<20dQ}4-1MN)D_z_6acNxp_XS* zi&&JJt$?BpwVENzaK{b~5dE5?t~#d%5sh`jLXh@yTK!wvyL$TTMta?6#-HDysNq}3 zgEmHO2fl$+b^|q4avK7bFY+UtZ>Z2C0!R592bQ6|{36s+co%@(mMXakn*h>X1p6#t z!qP*2h>_;UNN3pqiy&KFb?y)>SD~kj;AgAwM*mHjmo-9l*o5-n-@oy2&HQtj$;(Ij zwRJjWZ*@xE>l8KU7B=eUebl@4SwHuSLH0MpYfY4^%|_|pO)s^WrLuG}GtL^eSKL z{5O5$%im2ewwNckS|qhiNNl%`@31B5hmmwaKZK-xkTfocGi#f=+VHhA4DAAG_$>I{C}(;6La*$d&h1Zn{qPyh#+QE#7PpD(ushXBJK4pE=yo7! z1(U|^SCa1{joLxd*rqmevzq!wwGr#phWY+7bhSX+X08#kht5cKbs}at1HExkgAVwU z_QN!_b^L`k%X95kXF6$heUk^QP#flLq%p-+*Ji4L9WXC3qpq=}`B~G0nanhIR`o*sS0k>U2#u2d z-Q;Y$bwrOtcrUA;Ge|mdZ=dU~K92yIaI0+UCix6M`RreYuAymi-1TgylkMgiBg@RG z-j+0f8~P3^1DKCE+F$;uj8iL>f%#M?6$tl9U3TMyp4((oHp{1Pl+RkPnCq*UzXtt% zCAws$q1^%#WVt!jcLHsbEj^IR2x2lr9ql7I4zU8qbCVnaxY3UXs<`r5Xk?%T*)8&E z{_>d{6m!-p=B+^&u0$6vN0%){|5${sUV!`!nV;slOKlbkNO zu`-3Y2_KZ2IH^+eu!IM)e(RO*!e#;IJu5ijP}ujh3K;c7UYQVkqijxL{$ZJvs40|ifXCn&g~dtK4} zE{X#a6$h_-DMed_u7ZowV#KbW)o^j(Cf~}d_B-TZs*-=oaQeq4!ykE!rxRLjD3m=Wab#Nl34)AzkJDiEwaGvZKw=aStjpRt8 zIFji94F4X$Z{5j9PjdP)XoSvr=(SzZAZ}kIM|y%IImzygVfUP7b;q*0<5^t^Ff}9q zOx?U4kL}pY?>@kjhVW!*bb>;uAdny7%fkCZSe=Qij&rQGBv#u6R%`O$K*=GVER-id z#+9csn~Tf|g$vM+gQ@$+vwozqzF%TBr?HyS+25|fgMg?$WfXdX3l3m$tk!%Bh{0!8 z#Fb*xMkH+V9``;Uh@hckI$eS&#%iDRF?~?Dg+f3g7Qki{mRySOYrSWscB<{ z(YqSMtZ#k;@dRnhZ_1-!nrJzU#PwCKOUjH4u`(l%SxeU!n!7|Uxr?7%