compat.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /**
  2. * Copyright (C) 2014-2016 Triumph LLC
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. "use strict";
  18. /**
  19. * Compatibility internal API.
  20. * @name compat
  21. * @namespace
  22. * @exports exports as compat
  23. */
  24. b4w.module["__compat"] = function(exports, require) {
  25. var m_cfg = require("__config");
  26. var m_debug = require("__debug");
  27. var m_ext = require("__extensions");
  28. var m_print = require("__print");
  29. var m_render= require("__renderer");
  30. var MIN_VARYINGS_REQUIRED = 10;
  31. var AMD_MESA_RENDER_NAMES = ["R600", "RV610", "RV630", "RV620", "RV635", "RV670",
  32. "RS780", "RS880", "RV770", "RV730", "RV710", "RV740", "CEDAR", "REDWOOD",
  33. "JUNIPER", "CYPRESS", "PALM (Wrestler/Ontario)", "SUMO (Llano)",
  34. "SUMO2 (Llano)", "ARUBA (Trinity/Richland)", "BARTS", "TURKS", "CAICOS",
  35. "CAYMAN"];
  36. exports.NVIDIA_OLD_GPU_CUBEMAP_MAX_SIZE = 256;
  37. var cfg_anim = m_cfg.animation;
  38. var cfg_def = m_cfg.defaults;
  39. var cfg_dbg = m_cfg.debug_subs;
  40. var cfg_ctx = m_cfg.context;
  41. var cfg_lim = m_cfg.context_limits;
  42. var cfg_scs = m_cfg.scenes;
  43. var cfg_sfx = m_cfg.sfx;
  44. var cfg_phy = m_cfg.physics;
  45. var cfg_ldr = m_cfg.assets;
  46. exports.detect_tegra_invalid_enum_issue = function(gl) {
  47. // this hardware don't like context.antialias = true
  48. // get and ignore such error
  49. if (gl.getError() == gl.INVALID_ENUM)
  50. m_print.warn("Possible Tegra invalid enum issue detected, ignoring");
  51. }
  52. exports.set_hardware_defaults = function(gl, print_warnings) {
  53. var warn = function(msg) {
  54. if (print_warnings)
  55. m_print.warn(msg);
  56. }
  57. cfg_lim.max_combined_texture_image_units =
  58. gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
  59. cfg_lim.max_fragment_uniform_vectors =
  60. gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
  61. cfg_lim.max_texture_image_units =
  62. gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
  63. cfg_lim.max_varying_vectors =
  64. gl.getParameter(gl.MAX_VARYING_VECTORS);
  65. cfg_lim.max_vertex_attribs =
  66. gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
  67. cfg_lim.max_vertex_texture_image_units =
  68. gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
  69. cfg_lim.max_vertex_uniform_vectors =
  70. gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);
  71. cfg_lim.max_cube_map_texture_size =
  72. gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE);
  73. cfg_lim.max_renderbuffer_size =
  74. gl.getParameter(gl.MAX_RENDERBUFFER_SIZE);
  75. cfg_lim.max_texture_size = gl.getParameter(gl.MAX_TEXTURE_SIZE);
  76. cfg_lim.max_viewport_dims = gl.getParameter(gl.MAX_VIEWPORT_DIMS);
  77. if (cfg_def.webgl2 && !cfg_dbg.enabled)
  78. cfg_def.compared_mode_depth = true;
  79. if (!cfg_def.webgl2)
  80. cfg_def.msaa_samples = 1;
  81. else {
  82. cfg_def.msaa_samples = Math.min(cfg_def.msaa_samples,
  83. gl.getParameter(gl.MAX_SAMPLES));
  84. if (check_user_agent("Firefox")) {
  85. warn("Firefox and WebGL 2 detected, applying framebuffer hack, disabling anchor visibility");
  86. cfg_def.check_framebuffer_hack = true;
  87. }
  88. if (check_user_agent("Windows") && check_user_agent("Chrome")) {
  89. warn("Windows, Chrome and WebGL 2 detected, applying " +
  90. "multisample hack, disabling MSAA.");
  91. cfg_def.msaa_samples = 1;
  92. }
  93. }
  94. if (check_user_agent("Firefox") && cfg_def.stereo !== "NONE") {
  95. warn("Firefox and Stereo rendering detected, disable texture reusage");
  96. cfg_def.firefox_tex_reuse_hack = true;
  97. }
  98. // TODO: need to check Firefox MSAA --> "Coin Flip" demo shadows.
  99. if (cfg_def.webgl2 && m_debug.check_multisample_issue() ||
  100. check_user_agent("Firefox")) {
  101. warn("Firefox detected, disabling multisample");
  102. cfg_def.msaa_samples = 1;
  103. }
  104. m_render.set_draw_methods();
  105. var depth_tex_available = Boolean(m_ext.get_depth_texture());
  106. // HACK: fix depth issue in Firefox 28
  107. if (check_user_agent("Firefox/28.0") &&
  108. (check_user_agent("Linux") || check_user_agent("Macintosh"))) {
  109. warn("Firefox 28 detected, applying depth hack");
  110. depth_tex_available = false;
  111. }
  112. if (!check_user_agent("Windows Phone"))
  113. if (check_user_agent("iPad") || check_user_agent("iPhone")) {
  114. warn("iOS detected, applying alpha hack, applying vertex "
  115. + "animation mix normals hack, disable smaa. Disable ssao "
  116. + "for performance. Initialize WebAudio context with empty sound. "
  117. + "Applying glow hack.");
  118. if (!cfg_ctx.alpha)
  119. cfg_def.background_color[3] = 1.0;
  120. cfg_def.safari_glow_hack = true;
  121. cfg_def.vert_anim_mix_normals_hack = true;
  122. cfg_def.smaa = false;
  123. cfg_def.ssao = false;
  124. cfg_def.precision = "highp";
  125. cfg_def.init_wa_context_hack = true;
  126. if (Boolean(m_ext.get_pvr()) && cfg_ldr.pvr_available)
  127. cfg_def.compress_format = "pvr";
  128. } else if (check_user_agent("Mac OS X") && check_user_agent("Safari")
  129. && !check_user_agent("Chrome")) {
  130. warn("OS X / Safari detected, force to wait complete loading. " +
  131. "Applying playback rate hack for video textures. " +
  132. "Applying canvas alpha hack.");
  133. cfg_def.safari_canvas_alpha_hack = true;
  134. cfg_sfx.audio_loading_hack = true;
  135. cfg_sfx.clamp_playback_rate_hack = true;
  136. }
  137. if ((check_user_agent("Windows"))
  138. &&(check_user_agent("Chrome/40") ||
  139. check_user_agent("Firefox/33") ||
  140. check_user_agent("Firefox/34") ||
  141. check_user_agent("Firefox/35") ||
  142. check_user_agent("Firefox/36"))) {
  143. warn("Windows/Chrome40 or Firefox33-36 detected. Applying clear procedural skydome hack.");
  144. cfg_def.clear_procedural_sky_hack = true;
  145. }
  146. if (check_user_agent("Chrome") && !detect_mobile() && m_cfg.is_built_in_data()) {
  147. warn("Chrome (non-mobile) was detected for a single HTML-exported "
  148. + "file. \"Background Music\" speakers were changed to \"Background Sound\".");
  149. cfg_def.chrome_html_bkg_music_hack = true;
  150. }
  151. if (check_user_agent("Mac OS X")) {
  152. cfg_def.mac_os_shadow_hack = true;
  153. warn("OS X detected, applying shadows hack.");
  154. }
  155. if (detect_mobile()) {
  156. warn("Mobile detected, applying various hacks for video textures.");
  157. cfg_def.is_mobile_device = true;
  158. if (!(check_user_agent("iPad") || check_user_agent("iPhone"))
  159. && !check_user_agent("Windows Phone")) {
  160. warn("Mobile (not iOS) detected, disable playback rate for video textures.");
  161. cfg_sfx.disable_playback_rate_hack = true;
  162. }
  163. }
  164. if ((check_user_agent("Firefox/35.0") || check_user_agent("Firefox/36.0")) &&
  165. check_user_agent("Windows")) {
  166. warn("Windows/Firefox 35/36 detected, applying shadows slink hack");
  167. cfg_def.shadows_color_slink_hack = true;
  168. }
  169. if (check_user_agent("iPhone") || is_ie11() || check_user_agent("Edge")) {
  170. warn("iPhone, IE11 or Edge detected. Enable sequential video fallback for video textures.");
  171. cfg_def.seq_video_fallback = true;
  172. }
  173. if (cfg_lim.max_varying_vectors < MIN_VARYINGS_REQUIRED) {
  174. warn("Not enough varyings, disable shadows on blend objects");
  175. cfg_def.disable_blend_shadows_hack = true;
  176. }
  177. if (check_user_agent("Windows Phone")) {
  178. warn("Windows Phone detected. Disable debug view mode, "
  179. + "glow materials, ssao, smaa, shadows, reflections, refractions.");
  180. cfg_def.debug_view = false;
  181. cfg_def.precision = "highp";
  182. cfg_def.glow_materials = false;
  183. cfg_def.ssao = false;
  184. cfg_def.smaa = false;
  185. cfg_def.shadows = false;
  186. cfg_def.reflections = false;
  187. cfg_def.refractions = false;
  188. cfg_def.quality_aa_method = false;
  189. }
  190. // TODO: check mobile Firefox
  191. if (check_user_agent("UCBrowser") ||
  192. check_user_agent("Chrome") && check_user_agent("Nexus") && cfg_def.is_mobile_device) {
  193. warn("Mobile Nexus Chrome or UCBrowser detected, disable workers.");
  194. cfg_phy.use_workers = false;
  195. }
  196. // NOTE: check compatibility for particular device
  197. var rinfo = m_ext.get_renderer_info();
  198. if (rinfo) {
  199. var vendor = gl.getParameter(rinfo.UNMASKED_VENDOR_WEBGL);
  200. var renderer = gl.getParameter(rinfo.UNMASKED_RENDERER_WEBGL);
  201. var mali_4x_re = /\b4\d{2}\b/;
  202. if (vendor.indexOf("ARM") > -1 && mali_4x_re.test(renderer)) {
  203. warn("ARM Mali-400 series detected, applying depth and frames blending hacks");
  204. depth_tex_available = false;
  205. cfg_anim.frames_blending_hack = true;
  206. }
  207. if (vendor.indexOf("ARM") > -1 && renderer.indexOf("Mali-T604") > -1) {
  208. warn("ARM Mali-T604 detected, set \"highp\" precision and disable shadows.");
  209. cfg_def.precision = "highp";
  210. cfg_def.shadows = false;
  211. }
  212. if (vendor.indexOf("ARM") > -1 && renderer.indexOf("Mali-T760") > -1) {
  213. warn("ARM Mali-T760 detected, set \"highp\" precision and disable SSAO.");
  214. cfg_def.precision = "highp";
  215. cfg_def.ssao = false;
  216. cfg_def.skinning_hack = true;
  217. if (cfg_def.webgl2) {
  218. cfg_def.msaa_samples = 1;
  219. warn("ARM Mali-T760 and WebGL 2 detected, switch MSAA samples to 1.");
  220. }
  221. }
  222. if (vendor.indexOf("Qualcomm") > -1 && renderer.indexOf("Adreno") > -1) {
  223. warn("Qualcomm Adreno detected, applying shader constants hack.");
  224. cfg_def.shader_constants_hack = true;
  225. if (renderer.indexOf("305") > -1) {
  226. warn("Qualcomm Adreno305 detected, set \"highp\" precision.");
  227. cfg_def.precision = "highp";
  228. }
  229. if (renderer.indexOf("330") > -1) {
  230. warn("Qualcomm Adreno330 detected, set \"highp\" precision.");
  231. cfg_def.precision = "highp";
  232. }
  233. if (renderer.indexOf("420") > -1) {
  234. warn("Qualcomm Adreno420 detected, setting max cubemap size to 4096, "
  235. + "setting max texture size to 4096.");
  236. cfg_lim.max_texture_size = 4096;
  237. cfg_lim.max_cube_map_texture_size = 4096;
  238. }
  239. }
  240. if (vendor.indexOf("NVIDIA") > -1 && renderer.indexOf("Tegra 3") > -1) {
  241. warn("NVIDIA Tegra 3 detected, force low quality for "
  242. + "B4W_LEVELS_OF_QUALITY nodes.");
  243. cfg_def.force_low_quality_nodes = true;
  244. }
  245. if (check_user_agent("Windows") && check_user_agent("Chrome") && !check_user_agent("Edge") &&
  246. (renderer.match(/NVIDIA GeForce 8..0/) || renderer.match(/NVIDIA GeForce 9..0/)
  247. || renderer.match(/NVIDIA GeForce( (G|GT|GTS|GTX))? 2../))) {
  248. warn("Chrome / Windows / NVIDIA GeForce 8/9/200 series detected, " +
  249. "setting max cubemap size to 256, use canvas for resizing.");
  250. cfg_lim.max_cube_map_texture_size = exports.NVIDIA_OLD_GPU_CUBEMAP_MAX_SIZE;
  251. cfg_def.resize_cubemap_canvas_hack = true;
  252. }
  253. if (vendor.indexOf("AMD") > -1 && check_user_agent("Windows") && check_user_agent("Chrome")) {
  254. warn("AMD, Windows and Chrome detected, applying depth hack");
  255. depth_tex_available = false;
  256. }
  257. if (renderer.indexOf("PowerVR") > -1) {
  258. warn("PowerVR series detected, use canvas for resizing. " +
  259. "Disable shadows. " +
  260. "Apply skinning hack, disable skin blending between frames.");
  261. cfg_def.resize_cubemap_canvas_hack = true;
  262. cfg_def.skinning_hack = true;
  263. cfg_def.shadows = false;
  264. // NOTE: uncomment code below in case of cfg_def.shadows == true;
  265. // cfg_def.shadows_color_slink_hack = true;
  266. }
  267. var architecture = "";
  268. for (var i = 0; i < AMD_MESA_RENDER_NAMES.length; i++)
  269. if (renderer.indexOf(AMD_MESA_RENDER_NAMES[i]) > -1) {
  270. architecture = AMD_MESA_RENDER_NAMES[i];
  271. break;
  272. }
  273. if (architecture) {
  274. warn("Architecture " + architecture + " detected. Blending between frames" +
  275. " and shadows on blend objects will be disabled.");
  276. cfg_def.skinning_hack = true;
  277. cfg_def.disable_blend_shadows_hack = true;
  278. }
  279. }
  280. if (cfg_lim.max_vertex_texture_image_units == 0) {
  281. warn("Vertex textures are not allowed. Disabling vertex textures");
  282. cfg_def.allow_vertex_textures = false;
  283. }
  284. if (!depth_tex_available) {
  285. cfg_def.foam = false;
  286. cfg_def.dynamic_grass = false;
  287. cfg_def.water_dynamic = false;
  288. cfg_def.shore_smoothing = false;
  289. cfg_def.shore_distance = false;
  290. cfg_def.smaa = false;
  291. }
  292. cfg_def.use_compression = Boolean(m_ext.get_s3tc()) || cfg_def.compress_format == "pvr";
  293. cfg_def.depth_tex_available = depth_tex_available;
  294. // webglreport.com
  295. if (gl.getShaderPrecisionFormat)
  296. var high = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
  297. if (!gl.getShaderPrecisionFormat || high.precision === 0)
  298. cfg_def.precision = "mediump";
  299. // TODO: remove "medium", bcz it's unused.
  300. // var medium = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER,
  301. // gl.MEDIUM_FLOAT);
  302. if (is_ie11() && check_user_agent("Touch") || check_user_agent("Edge")) {
  303. warn("IE11 and touchscreen or Edge detected. Behaviour of the mouse move sensor will be changed.");
  304. cfg_def.ie11_edge_touchscreen_hack = true;
  305. }
  306. if (is_ie11() || check_user_agent("Edge"))
  307. cfg_def.ie_edge_anchors_floor_hack = true;
  308. if (detect_mobile() && check_user_agent("Firefox")) {
  309. m_print.log("Mobile firefox detected. Applying autoplay media hack."
  310. + "Setting max cubemap size to 4096, "
  311. + "setting max texture size to 4096.");
  312. cfg_def.mobile_firefox_media_hack = true;
  313. cfg_lim.max_texture_size = 4096;
  314. cfg_lim.max_cube_map_texture_size = 4096;
  315. }
  316. if (check_user_agent("Edge")) {
  317. warn("Microsoft Edge detected, set up new minimal texture size.");
  318. cfg_def.edge_min_tex_size_hack = true;
  319. }
  320. }
  321. exports.check_user_agent = check_user_agent;
  322. /**
  323. * for user agent hacks
  324. */
  325. function check_user_agent(str) {
  326. var user_agent = navigator.userAgent;
  327. if (user_agent.indexOf(str) > -1)
  328. return true;
  329. else
  330. return false;
  331. }
  332. exports.detect_mobile = detect_mobile;
  333. function detect_mobile() {
  334. return navigator.userAgent.match(/Windows Phone/i)
  335. ||navigator.userAgent.match(/Android/i)
  336. || navigator.userAgent.match(/webOS/i)
  337. || navigator.userAgent.match(/iPhone/i)
  338. || navigator.userAgent.match(/iPad/i)
  339. || navigator.userAgent.match(/iPod/i)
  340. || navigator.userAgent.match(/BlackBerry/i);
  341. }
  342. exports.apply_context_alpha_hack = function() {
  343. if (check_user_agent("Firefox/35.0") && check_user_agent("Windows")) {
  344. m_print.warn("Windows/Firefox 35 detected, forcing context's alpha");
  345. m_cfg.context.alpha = true;
  346. }
  347. }
  348. /**
  349. * Detect Internet Explorer 11
  350. * @see http://stackoverflow.com/questions/21825157/internet-explorer-11-detection
  351. */
  352. exports.is_ie11 = is_ie11;
  353. function is_ie11() {
  354. return !(window.ActiveXObject) && "ActiveXObject" in window;
  355. }
  356. }