62.htm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. <!--
  2. demo.design 3D programming FAQ
  3. Idea, texts, screenshots:
  4. Andrew A. Aksyonoff,
  5. shodan@chat.ru
  6. Web-design, illustrations:
  7. Andrey Samoilov,
  8. asy@sense.simbirsk.su
  9. -->
  10. <html>
  11. <head>
  12. <title>demo.design 3D programming FAQ. Оптимизация. Примеры внутренних циклов текстурирования.</title>
  13. <link rel=stylesheet href="../style.css" type="text/css">
  14. </head>
  15. <script language="javascript">
  16. <!--//
  17. browser = navigator.appName;
  18. version = parseFloat(navigator.appVersion);
  19. if (browser == "Netscape" && version >= 3.0) { jsenabled = 1; } else
  20. if (browser == "Microsoft Internet Explorer" && version >= 3.0) { jsenabled = 1; } else { jsenabled = 0; }
  21. function swap(img,ref) { if (jsenabled) {document.images[img].src = ref;} }
  22. function loadtocache(img,ref) { cache[img] = new Image(); cache[img].src = ref; }
  23. if (jsenabled) {
  24. cache = new Array();
  25. loadtocache(0,"../img/xdl.gif");
  26. loadtocache(1,"../img/xfaq.gif");
  27. loadtocache(2,"../img/xlinks.gif");
  28. loadtocache(3,"../img/xauthor.gif");
  29. loadtocache(4,"../img/xe.gif");
  30. loadtocache(5,"../img/xprev.gif");
  31. loadtocache(6,"../img/xnext.gif");}
  32. //-->
  33. </script>
  34. <body bgcolor=white><center>
  35. <!-- Title -->
  36. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  37. <img src="../img/t.gif" width=500 height=1 alt=""><br>
  38. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  39. <img src="../img/t.gif" width=500 height=2 alt=""><br>
  40. <table width=500 cellpadding=0 cellspacing=0 border=0>
  41. <td><img src="../img/t.gif" width=5 height=1 alt=""><a href="../main.htm" onmouseover="swap('logo','../img/xe.gif');" onmouseout="swap('logo','../img/e.gif');"><img src="../img/e.gif" name=logo width=60 height=50 hspace=10 border=0 alt=" в самое начало "></a></td>
  42. <td><p class=pagetitle><img src="../img/t.gif" width=265 height=1 alt=""><br>demo.design<br>3D programming FAQ</td>
  43. <td align=center><p class=navy><a href="../download.htm" onmouseover="swap('dl','../img/xdl.gif');" onmouseout="swap('dl','../img/dl.gif');"><img src="../img/dl.gif" name=dl width=40 height=40 border=0 hspace=5 alt=" download "></a><br>download</td>
  44. <td align=center><p class=navy><a href="../links.htm" onmouseover="swap('links','../img/xlinks.gif');" onmouseout="swap('links','../img/links.gif');"><img src="../img/links.gif" name=links width=40 height=40 border=0 hspace=5 alt=" коллекция линков "></a><br>links</td>
  45. <td align=center><p class=navy><a href="../author.htm" onmouseover="swap('author','../img/xauthor.gif');" onmouseout="swap('author','../img/author.gif');"><img src="../img/author.gif" name=author width=40 height=40 border=0 hspace=5 alt=" автора! "></a><br>author</td>
  46. </table>
  47. <img src="../img/t.gif" width=500 height=4 alt=""><br><img src="../img/b.gif" width=500 height=1 alt=""><br>
  48. <!-- Head -->
  49. <table width=500 cellpadding=0 cellspacing=10 border=0><td><div align=justify>
  50. <p class=title>
  51. <img src="../img/b6.gif" width=70 height=70 align=left hspace=0 alt="">
  52. <img src="../img/t.gif" width=5 height=70 align=left hspace=0 alt="">
  53. ОПТИМИЗАЦИЯ<br>6.2. Примеры внутренних циклов текстурирования
  54. <!-- Article -->
  55. <p>Если немного поработать профайлером, можно выяснить следующую интересную
  56. вещь: большая часть времени на отрисовку сцены тратится именно в процедуре
  57. текстурирования, а в ней, в свою очередь, большая часть времени проходит во
  58. внутреннем цикле (inner loop). Естественно, что его и надо оптимизировать
  59. в первую очередь.
  60. <p>Возьмем этот самый inner loop от обычного аффинного текстурирования (такой
  61. же, на самом деле, используется и в перспективно-корректном) и перепишем на
  62. ассемблере (в критических участках кода на компилятор надеяться не стоит).
  63. Будем использовать 24:8 fixedpoint для u, v, а также 8-битную текстуру
  64. шириной 256 байт.
  65. <pre class=source>
  66. mov eax,u ; 24:8 fixedpoint
  67. mov ebx,v ; 24:8 fixedpoint
  68. mov ecx,length
  69. xor edx,edx
  70. mov esi,texture
  71. mov edi,outputbuffer
  72. inner:
  73. mov dl,ah ; вытащили целую часть u
  74. mov dh,bh ; вытащили целую часть v
  75. ; теперь edx = dx = (100h * v + u) - как раз
  76. ; смещение тексела [v][u] относительно начала
  77. ; текстуры
  78. mov dl,[esi+edx] ; dl = texture[v][u]
  79. mov [edi],dl ; *outputBuffer = dl
  80. add eax,du ; u += du
  81. add ebx,dv ; v += dv
  82. inc edi ; outputBuffer++
  83. loop inner
  84. ; ...
  85. </pre>
  86. <p>Красиво, аккуратно, на ассемблере. Только вот согласно правилам спаривания,
  87. половина команд в этом цикле не спарится, и цикл займет порядка 6-7 тактов.
  88. А на самом деле, чуточку переставив местами команды, можно его загнать
  89. где-то в 4.5 такта:
  90. <pre class=source>
  91. ; ...
  92. inner:
  93. mov dl,ah
  94. add eax,du
  95. mov dh,bh
  96. add ebx,dv
  97. mov dl,[esi+edx]
  98. inc edi
  99. dec ecx
  100. mov [edi-1],dl
  101. jnz inner
  102. ; ...
  103. </pre>
  104. <p>В таком виде любая пара команд отлично спаривается, получаем те самые 4.5
  105. такта. Здесь, правда, есть обращения к внешним переменным du и dv, что
  106. может снизить скорость. Решение - самомодифицирующийся код:
  107. <pre class=source>
  108. ; ...
  109. mov eax,du
  110. mov ebx,dv
  111. mov inner_du,eax
  112. mov inner_dv,ebx
  113. ; ...
  114. inner:
  115. ; ...
  116. add eax,12345678h
  117. org $-4
  118. inner_du dd ?
  119. add edx,12345678h
  120. org $-4
  121. inner_dv dd ?
  122. ; ...
  123. </pre>
  124. <p>Однозначного ответа насчет использования самомодификации нет, а совет, что
  125. можно по этому поводу дать, стандартен - попробуйте, если будет быстрее,
  126. то используйте.
  127. <p>Дальше - больше. 4.5 такта на пиксел - это тоже не предел. В fatmap.txt
  128. (<a href="ftp://ftp.hornet.org/pub/demos/code/3d/trifill/texmap/fatmap.txt">ftp://ftp.hornet.org/pub/demos/code/3d/trifill/texmap/fatmap.txt</a>)
  129. приводится вот такой красивый inner loop на четыре такта.
  130. <pre class=source>
  131. ; текстура должна быть выравнена на 64k
  132. ; линии рисуются справа налево
  133. ; верхние 16 бит ebx = сегмент текстуры
  134. ; bh = целая часть v
  135. ; dh = дробная часть v
  136. ; dl = дробная часть dv
  137. ; ah = целая часть v
  138. ; ecx = u
  139. ; ebp = du
  140. inner:
  141. add ecx,ebp ; u += du
  142. mov al,[ebx] ; al = texture[v][u]
  143. mov bl,ch ; bl = новая целая часть u
  144. add dh,dl ; считаем новую дробную часть v
  145. adc bh,ah ; считаем новую целую часть v
  146. mov [edi+esi],al ; рисуем пиксел
  147. dec esi ;
  148. jnz inner ;
  149. </pre>
  150. <p>Надо, правда, отметить, что он уже требует каких-то ухищрений - а именно,
  151. выравнивания текстуры на 64k и отрисовки строк справа налево. Кроме того,
  152. требует более подробного рассмотрения фрагмент с add и adc, об этом более
  153. подробно рассказано чуть ниже.
  154. <p>И, наконец, цитата из fatmap2.txt - 4-тактовый inner loop, использующий
  155. 16:16 fixedpoint. Недостатки - текстура должна быть выравнена на 64k;
  156. есть две команды adc, которые могут запросто испортить спаривание. Кстати,
  157. рекомендую скачать этот самый fatmap2.txt; например, по этому адресу:
  158. <a href="ftp://ftp.hornet.org/pub/demos/code/3d/trifill/texmap/fatmap2.zip">ftp://ftp.hornet.org/pub/demos/code/3d/trifill/texmap/fatmap2.zip</a>.
  159. <pre class=source>
  160. ; текстура должна быть выравнена на 64k
  161. ;
  162. ; верхние 16 бит | ah/bh/ch/dh | al/bl/cl/dl
  163. ; -----------------+----------------+----------------
  164. ; eax = дробная часть u | - | -
  165. ; ebx = сегмент текстуры | целая часть v | целая часть u
  166. ; edx = дробная часть v | целая часть dv | целая часть du
  167. ; esi = дробная часть du | 0 | 0
  168. ; ebp = дробная часть dv | 0 | 0
  169. ; ecx = длина линии
  170. ; edi = буфер
  171. lea edi,[edi+ecx] ; edi += ecx
  172. neg ecx ; ecx = -ecx
  173. inner:
  174. mov al,[ebx] ; al = texture[v][u]
  175. add edx,ebp ; обновляем дробную часть v
  176. adc bh,dh ; обновляем целую часть v (учитывая
  177. ; перенос от дробной)
  178. add eax,esi ; обновляем дробную часть u
  179. adc bl,dl ; обновляем целую часть u (учитывая
  180. ; перенос от дробной)
  181. mov [edi+ecx],al ; outputBuffer[ecx] = al
  182. inc ecx
  183. jnz inner
  184. </pre>
  185. <p>Этот цикл, с виду, ничем не лучше цикла для 24:8 fixedpoint. Но на самом
  186. деле, он может пригодиться в том случае, если циклу с 24:8 fixedpoint не
  187. хватит точности. Упомянутая нехватка точности проявляется в эффекте "пилы"
  188. внутри относительно больших треугольников, который вовсе не устраняется
  189. добавлением subpixel/subtexel accuracy.
  190. <p>Два последних цикла используют конструкции вида add/adc. Здесь мнения
  191. авторов этих самых циклов явно расходятся с мнениями автора pentopt.txt.
  192. Согласно последнему (и <a href="61.htm#611">п.6.1.1.</a>, соответственно, тоже), add и adc НЕ
  193. спарятся (так как add изменяет регистр флагов, adc - читает из него).
  194. Проведенный эксперимент показал, что они действительно не спариваются, но
  195. он был поставлен на k5; так что на данный момент я достоверной информацией
  196. по этому поводу не располагаю. Впрочем, в любом случае лучше еще чуть-чуть
  197. попереставлять команды - для полной надежности. И для полной надежности,
  198. самостоятельно замеряйте скорость выполнения каждой новой версии цикла и
  199. смотрите, что получилось. Да, совет тривиальный. Но после того, как на моем
  200. k5 цикл из четырех инструкций исполнился, согласно замерам, за такт...
  201. </div>
  202. </td></table>
  203. <!-- Bottom Navigation -->
  204. <img src="../img/b.gif" width=500 height=1 alt=""><br><img src="../img/t.gif" width=500 height=2 alt=""><br>
  205. <table width=500 cellpadding=0 cellspacing=0 border=0>
  206. <td><img src="../img/t.gif" width=5 height=1 alt=""><a href="../main.htm" onmouseover="swap('logo2','../img/xe.gif');" onmouseout="swap('logo2','../img/e.gif');"><img src="../img/e.gif" name=logo2 width=60 height=50 hspace=10 border=0 alt=" в самое начало "></a></td>
  207. <td><p class=pagetitle><img src="../img/t.gif" width=265 height=1 alt=""><br>demo.design<br>3D programming FAQ</td>
  208. <td align=center><p class=navy><a href="61.htm" onmouseover="swap('prev','../img/xprev.gif');" onmouseout="swap('prev','../img/prev.gif');"><img src="../img/prev.gif" name=prev width=40 height=40 border=0 hspace=5 alt=" предыдущая статья "></a><br>previous</td>
  209. <td align=center><p class=navy><a href="../content.htm" onmouseover="swap('faq','../img/xfaq.gif');" onmouseout="swap('faq','../img/faq.gif');"><img src="../img/faq.gif" name=faq width=40 height=40 border=0 hspace=5 alt=" содержание "></a><br>content</td>
  210. <td align=center><p class=navy><a href="63.htm" onmouseover="swap('next','../img/xnext.gif');" onmouseout="swap('next','../img/next.gif');"><img src="../img/next.gif" name=next width=40 height=40 border=0 hspace=5 alt=" следующая статья "></a><br>next</td>
  211. </table>
  212. <img src="../img/t.gif" width=500 height=4 alt=""><br>
  213. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  214. <img src="../img/t.gif" width=500 height=1 alt=""><br>
  215. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  216. </center></body>
  217. </html>