babylon.engine.occlusionQuery.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. module BABYLON {
  2. export interface Engine {
  3. /**
  4. * Create a new webGL query (you must be sure that queries are supported by checking getCaps() function)
  5. * @return the new query
  6. */
  7. createQuery(): WebGLQuery;
  8. /**
  9. * Delete and release a webGL query
  10. * @param query defines the query to delete
  11. * @return the current engine
  12. */
  13. deleteQuery(query: WebGLQuery): Engine;
  14. /**
  15. * Check if a given query has resolved and got its value
  16. * @param query defines the query to check
  17. * @returns true if the query got its value
  18. */
  19. isQueryResultAvailable(query: WebGLQuery): boolean;
  20. /**
  21. * Gets the value of a given query
  22. * @param query defines the query to check
  23. * @returns the value of the query
  24. */
  25. getQueryResult(query: WebGLQuery): number;
  26. /**
  27. * Initiates an occlusion query
  28. * @param algorithmType defines the algorithm to use
  29. * @param query defines the query to use
  30. * @returns the current engine
  31. * @see http://doc.babylonjs.com/features/occlusionquery
  32. */
  33. beginOcclusionQuery(algorithmType: number, query: WebGLQuery): Engine;
  34. /**
  35. * Ends an occlusion query
  36. * @see http://doc.babylonjs.com/features/occlusionquery
  37. * @param algorithmType defines the algorithm to use
  38. * @returns the current engine
  39. */
  40. endOcclusionQuery(algorithmType: number): Engine;
  41. /**
  42. * Starts a time query (used to measure time spent by the GPU on a specific frame)
  43. * Please note that only one query can be issued at a time
  44. * @returns a time token used to track the time span
  45. */
  46. startTimeQuery(): Nullable<_TimeToken>;
  47. /**
  48. * Ends a time query
  49. * @param token defines the token used to measure the time span
  50. * @returns the time spent (in ns)
  51. */
  52. endTimeQuery(token: _TimeToken): int;
  53. /** @hidden */
  54. _currentNonTimestampToken: Nullable<_TimeToken>;
  55. /** @hidden */
  56. _createTimeQuery(): WebGLQuery;
  57. /** @hidden */
  58. _deleteTimeQuery(query: WebGLQuery): void;
  59. /** @hidden */
  60. _getGlAlgorithmType(algorithmType: number): number;
  61. /** @hidden */
  62. _getTimeQueryResult(query: WebGLQuery): any;
  63. /** @hidden */
  64. _getTimeQueryAvailability(query: WebGLQuery): any;
  65. }
  66. Engine.prototype.createQuery = function(): WebGLQuery {
  67. return this._gl.createQuery();
  68. }
  69. Engine.prototype.deleteQuery = function(query: WebGLQuery): Engine {
  70. this._gl.deleteQuery(query);
  71. return this;
  72. }
  73. Engine.prototype.isQueryResultAvailable = function(query: WebGLQuery): boolean {
  74. return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT_AVAILABLE) as boolean;
  75. }
  76. Engine.prototype.getQueryResult = function(query: WebGLQuery): number {
  77. return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT) as number;
  78. }
  79. Engine.prototype.beginOcclusionQuery = function(algorithmType: number, query: WebGLQuery): Engine {
  80. var glAlgorithm = this._getGlAlgorithmType(algorithmType);
  81. this._gl.beginQuery(glAlgorithm, query);
  82. return this;
  83. }
  84. Engine.prototype.endOcclusionQuery = function(algorithmType: number): Engine {
  85. var glAlgorithm = this._getGlAlgorithmType(algorithmType);
  86. this._gl.endQuery(glAlgorithm);
  87. return this;
  88. }
  89. Engine.prototype._createTimeQuery = function(): WebGLQuery {
  90. let timerQuery = <EXT_disjoint_timer_query>this.getCaps().timerQuery;
  91. if (timerQuery.createQueryEXT) {
  92. return timerQuery.createQueryEXT();
  93. }
  94. return this.createQuery();
  95. }
  96. Engine.prototype._deleteTimeQuery = function(query: WebGLQuery): void {
  97. let timerQuery = <EXT_disjoint_timer_query>this.getCaps().timerQuery;
  98. if (timerQuery.deleteQueryEXT) {
  99. timerQuery.deleteQueryEXT(query);
  100. return;
  101. }
  102. this.deleteQuery(query);
  103. }
  104. Engine.prototype._getTimeQueryResult = function(query: WebGLQuery): any {
  105. let timerQuery = <EXT_disjoint_timer_query>this.getCaps().timerQuery;
  106. if (timerQuery.getQueryObjectEXT) {
  107. return timerQuery.getQueryObjectEXT(query, timerQuery.QUERY_RESULT_EXT);
  108. }
  109. return this.getQueryResult(query);
  110. }
  111. Engine.prototype._getTimeQueryAvailability = function(query: WebGLQuery): any {
  112. let timerQuery = <EXT_disjoint_timer_query>this.getCaps().timerQuery;
  113. if (timerQuery.getQueryObjectEXT) {
  114. return timerQuery.getQueryObjectEXT(query, timerQuery.QUERY_RESULT_AVAILABLE_EXT);
  115. }
  116. return this.isQueryResultAvailable(query);
  117. }
  118. Engine.prototype.startTimeQuery = function(): Nullable<_TimeToken> {
  119. let caps = this.getCaps();
  120. let timerQuery = caps.timerQuery;
  121. if (!timerQuery) {
  122. return null;
  123. }
  124. let token = new _TimeToken();
  125. this._gl.getParameter(timerQuery.GPU_DISJOINT_EXT);
  126. if (caps.canUseTimestampForTimerQuery) {
  127. token._startTimeQuery = this._createTimeQuery();
  128. timerQuery.queryCounterEXT(token._startTimeQuery, timerQuery.TIMESTAMP_EXT);
  129. } else {
  130. if (this._currentNonTimestampToken) {
  131. return this._currentNonTimestampToken;
  132. }
  133. token._timeElapsedQuery = this._createTimeQuery();
  134. if (timerQuery.beginQueryEXT) {
  135. timerQuery.beginQueryEXT(timerQuery.TIME_ELAPSED_EXT, token._timeElapsedQuery);
  136. } else {
  137. this._gl.beginQuery(timerQuery.TIME_ELAPSED_EXT, token._timeElapsedQuery);
  138. }
  139. this._currentNonTimestampToken = token;
  140. }
  141. return token;
  142. }
  143. Engine.prototype.endTimeQuery = function(token: _TimeToken): int {
  144. let caps = this.getCaps();
  145. let timerQuery = caps.timerQuery;
  146. if (!timerQuery || !token) {
  147. return -1;
  148. }
  149. if (caps.canUseTimestampForTimerQuery) {
  150. if (!token._startTimeQuery) {
  151. return -1;
  152. }
  153. if (!token._endTimeQuery) {
  154. token._endTimeQuery = this._createTimeQuery();
  155. timerQuery.queryCounterEXT(token._endTimeQuery, timerQuery.TIMESTAMP_EXT);
  156. }
  157. } else if (!token._timeElapsedQueryEnded) {
  158. if (!token._timeElapsedQuery) {
  159. return -1;
  160. }
  161. if (timerQuery.endQueryEXT) {
  162. timerQuery.endQueryEXT(timerQuery.TIME_ELAPSED_EXT);
  163. } else {
  164. this._gl.endQuery(timerQuery.TIME_ELAPSED_EXT);
  165. }
  166. token._timeElapsedQueryEnded = true;
  167. }
  168. let disjoint = this._gl.getParameter(timerQuery.GPU_DISJOINT_EXT);
  169. let available: boolean = false;
  170. if (token._endTimeQuery) {
  171. available = this._getTimeQueryAvailability(token._endTimeQuery);
  172. } else if (token._timeElapsedQuery) {
  173. available = this._getTimeQueryAvailability(token._timeElapsedQuery);
  174. }
  175. if (available && !disjoint) {
  176. let result = 0;
  177. if (caps.canUseTimestampForTimerQuery) {
  178. if (!token._startTimeQuery || !token._endTimeQuery) {
  179. return -1;
  180. }
  181. let timeStart = this._getTimeQueryResult(token._startTimeQuery);
  182. let timeEnd = this._getTimeQueryResult(token._endTimeQuery);
  183. result = timeEnd - timeStart;
  184. this._deleteTimeQuery(token._startTimeQuery);
  185. this._deleteTimeQuery(token._endTimeQuery);
  186. token._startTimeQuery = null;
  187. token._endTimeQuery = null;
  188. } else {
  189. if (!token._timeElapsedQuery) {
  190. return -1;
  191. }
  192. result = this._getTimeQueryResult(token._timeElapsedQuery);
  193. this._deleteTimeQuery(token._timeElapsedQuery);
  194. token._timeElapsedQuery = null;
  195. token._timeElapsedQueryEnded = false;
  196. this._currentNonTimestampToken = null;
  197. }
  198. return result;
  199. }
  200. return -1;
  201. }
  202. Engine.prototype._getGlAlgorithmType = function(algorithmType: number): number {
  203. return algorithmType === AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE ? this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE : this._gl.ANY_SAMPLES_PASSED;
  204. }
  205. // We also need to update AbstractMesh as there is a portion of the code there
  206. AbstractMesh.prototype._checkOcclusionQuery = function() {
  207. if (this.occlusionType === AbstractMesh.OCCLUSION_TYPE_NONE) {
  208. this._isOccluded = false;
  209. return;
  210. }
  211. var engine = this.getEngine();
  212. if (engine.webGLVersion < 2) {
  213. this._isOccluded = false;
  214. return;
  215. }
  216. if (!engine.isQueryResultAvailable) { // Occlusion query where not referenced
  217. this._isOccluded = false;
  218. return;
  219. }
  220. if (this.isOcclusionQueryInProgress && this._occlusionQuery) {
  221. var isOcclusionQueryAvailable = engine.isQueryResultAvailable(this._occlusionQuery);
  222. if (isOcclusionQueryAvailable) {
  223. var occlusionQueryResult = engine.getQueryResult(this._occlusionQuery);
  224. this._isOcclusionQueryInProgress = false;
  225. this._occlusionInternalRetryCounter = 0;
  226. this._isOccluded = occlusionQueryResult === 1 ? false : true;
  227. }
  228. else {
  229. this._occlusionInternalRetryCounter++;
  230. if (this.occlusionRetryCount !== -1 && this._occlusionInternalRetryCounter > this.occlusionRetryCount) {
  231. this._isOcclusionQueryInProgress = false;
  232. this._occlusionInternalRetryCounter = 0;
  233. // if optimistic set isOccluded to false regardless of the status of isOccluded. (Render in the current render loop)
  234. // if strict continue the last state of the object.
  235. this._isOccluded = this.occlusionType === AbstractMesh.OCCLUSION_TYPE_OPTIMISTIC ? false : this._isOccluded;
  236. }
  237. else {
  238. return;
  239. }
  240. }
  241. }
  242. var scene = this.getScene();
  243. if (scene.getBoundingBoxRenderer) {
  244. var occlusionBoundingBoxRenderer = scene.getBoundingBoxRenderer();
  245. if (!this._occlusionQuery) {
  246. this._occlusionQuery = engine.createQuery();
  247. }
  248. engine.beginOcclusionQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
  249. occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
  250. engine.endOcclusionQuery(this.occlusionQueryAlgorithmType);
  251. this._isOcclusionQueryInProgress = true;
  252. }
  253. }
  254. }