fenomas Posted November 9, 2015 Share Posted November 9, 2015 Hi, Two questions related to performance tuning: 1. I don't know what StandardMaterial.isReady does, but it seems to return early in various cases. What can I do to make my materials fall into those cases? This is usually the #1 most costly function in my game. 2. It appears that scene.pick gets called every onPointerMove, even though I'm not using picking or BJS's pointer support in any way. Can I prevent this? In both cases the underlying problem is that v8 deoptimizes the functions, making them dog-slow. This may be fixable in and of itself, but I expect it would be a lot of work. Thanks for any help... Quote Link to comment Share on other sites More sharing options...
JCPalmer Posted November 9, 2015 Share Posted November 9, 2015 1. Set material.checkOnlyOnce = true. Might have side effects, especially if you alter it after creation. 2. Do not know. Just a scene property public notDoingPicking = false; might be a good way to shut it off. If you fork, try it. Quote Link to comment Share on other sites More sharing options...
fenomas Posted November 9, 2015 Author Share Posted November 9, 2015 1. Set material.checkOnlyOnce = true. Might have side effects, especially if you alter it after creation. I'm assuming that's safe to do in some cases and not in others. I'm asking, when is it safe? Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted November 9, 2015 Share Posted November 9, 2015 Hey 1. If you don't change anything on your material after settings initial values then checkOnlyOnce is for you.2. The scene.pick is called to evaluate potential OnPickTrigger actions and define the value of meshUnderPointer. you can turn it off by calling scene.detachControl() Quote Link to comment Share on other sites More sharing options...
fenomas Posted November 10, 2015 Author Share Posted November 10, 2015 2. Gotcha. 1. So if I change a material setting, I need to set checkOnlyOnce to false, then wait one render, then set it back? Or maybe set _wasPreviouslyReady to false or something? Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted November 10, 2015 Share Posted November 10, 2015 1. Correct Quote Link to comment Share on other sites More sharing options...
fenomas Posted November 11, 2015 Author Share Posted November 11, 2015 1. Thanks If you don't mind feedback, this really feels like something that should be managed internally, if the performance affects most users. I know you're not fond of setters or but it's weird that the user should need to set a magic flag to get best performance in the default case, and then set another one after changing certain properties. If it's just me that this is slow for, then never mind of course... Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted November 12, 2015 Share Posted November 12, 2015 I cannot set the checkOnlyOnce = true by default because it will be incredibly hard to understand for most users that they need to call some functions to have their values updated. Regarding setters, we could do it for all material properties (which will be painful and will increase the size of the framework) but what about others properties like fog for instance? this will mean that each time you change the fog, or enable mirrors or any other global properties (not mentioning light properties) that affect shaders you will have to go through all shaders to turn the flag on? I don't think this is a good idea If you are smart enough to think about checkOnlyOnce then I know that you won't be too much bothered to change one flag fenomas 1 Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted November 12, 2015 Share Posted November 12, 2015 To be cleaner, I've just added material.resetCheckReadyOnlyOnceFlag() Vousk-prod. and fenomas 2 Quote Link to comment Share on other sites More sharing options...
fenomas Posted November 12, 2015 Author Share Posted November 12, 2015 This is just feedback, I don't necessarily mean you should make huge changes now. But if there was an opportunity, like major update, using setters when necessary would definitely make the API better. Look at it this way: in any API there are basically three kinds of settings:Settings you can't changeSettings you can change any timeSettings you can change, but it causes side effectsIn a perfect API, the first group should be getters, the second properties, and the third setters*. If those rules are followed then the API documents itself, and cannot be used incorrectly. But otherwise, you start to require users to think about internal implementation, which is precisely what abstraction layers are meant to prevent. * (in practice of course this can mean real JS setters, or setValue() functions, or cached properties are checked each render. To the user it doesn't matter which) Personal note: for my project I started out using three.js, but I found it annoying to memorize all the cases where you have to set special flags every time you update properties. The cleaner API is 100% of the reason I switched to BJS, that's why I feel strongly about it. Quote Link to comment Share on other sites More sharing options...
fenomas Posted November 12, 2015 Author Share Posted November 12, 2015 To be cleaner, I've just added material.resetCheckReadyOnlyOnceFlag() What does it do, is it equivalent to three's needsUpdate? If so you might consider giving it a generic name (like markForUpdate or markDirty), so that people who don't know what the readOnlyOnce flag is doing (i.e. me). Also if other places in the engine needed a similar functionality they could use a consistent name. Quote Link to comment Share on other sites More sharing options...
Vousk-prod. Posted November 12, 2015 Share Posted November 12, 2015 I presume this flag do what you were previously doing by hand : set checkOnlyOnce = false, wait one render and set again checkOnlyOnce = trueSo you can change your material, and simply call this update flag to be able to apply your changes but still with the checkOnlyOnce = true always active for performance. Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted November 12, 2015 Share Posted November 12, 2015 Ok for the rename it is better, you are right. I'm absolutely agree with you that API has to be really SIMPLE. this is why the checkOnlyOnce is false by default. Turning it on will offer better performance but you will have to call markDirty (see, I use the new name already ) to validate your changes We cannot always have simpliity and performance. This specific case is a perfect example. If I want checkOnlyOnce = true AND no need to call markDirty then I will generate a really complex code to maintain because all entities controling values used by the shaders have to go through ALL materials each time you change a value to call markDirty. So, in a general way, I completely agree with you and this is what I want to enforce everywhere in babylon.js but this case is an exception Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted November 12, 2015 Share Posted November 12, 2015 But I have to admit that I would love to be able to provide this API quality to be honest. I need to think deeply on how to optimize it.... fenomas 1 Quote Link to comment Share on other sites More sharing options...
fenomas Posted November 13, 2015 Author Share Posted November 13, 2015 We cannot always have simpliity and performance. This specific case is a perfect example. If I want checkOnlyOnce = true AND no need to call markDirty then I will generate a really complex code to maintain because all entities controling values used by the shaders have to go through ALL materials each time you change a value to call markDirty. Sorry, I'm not sure if my point has gotten across. Probably my language. The underlying issue here is that lots of material settings have side effects when changed, right? That is, BJS needs to know whether the user has done anything to invalidate the material. But it can't check this without doing a lot of work, so it assumes the material might be invalid, and implements several flags that let the user override this, but then you have to manually tell BJS when an invalidation happens. That's basically what's happening, right? What I'm saying is, in general I think such issues occur when an API uses plain properties for settings that have side effects, and the issue can be avoided by converting properties to setters. In other words, any time an API says "when you change property X then you should set flag Y", instead it could just implement X as a setter that updates Y automatically. With that said, three.js is a very mature API and it doesn't do this, so maybe there are considerations I'm overlooking. I don't think it can be performance. (I mean setters are a little slower than properties, but nobody is going to change a material's diffuseTexture thousands of times per frame.) Maybe it's browser support? IE6/8 might not support setters, I don't remember. And again, this is just feedback! I'm not asking you to drop everything and change dozens of properties just so that I don't have to set one flag. But as a general design principle, I hope you can find ways to use setters to hide complexity, where possible. Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted November 13, 2015 Share Posted November 13, 2015 No I think it's me who is not clear Let me try again: let's consider I'm adding setters everywhere to implement X as a setter that updates Y automatically. Now let's consider I'm doing that for texture.uScale for instance. The pseudo code would be: texture.uScale = function(value) { texture._uscale = value; for each material in the scene: if the material uses this texture then mark it as dirty} So setting texture.uScale becomes now a complete scan of all materials in your scene. And this will be the same for, let's say, scene.fogEnable which will have to go through all materials as well. So this is why I say performance penalties are too important to make this change. This is not related to performance hit due to the setter but due to what the setter has to do. Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted November 13, 2015 Share Posted November 13, 2015 In the other hand, I completely agree with you on this: But as a general design principle, I hope you can find ways to use setters to hide complexity, where possible.And I count on you to keep giving feedback whenever you want ! Quote Link to comment Share on other sites More sharing options...
fenomas Posted November 14, 2015 Author Share Posted November 14, 2015 Ahh.. so you're saying the cost here isn't inherent, it just arises because BJS doesn't keep a list of materials using a given texture, so you're asking the (advanced) user to know that instead, is that correct? If so then I certainly see why that's a reasonable tradeoff, but the cost (to you) is that you're signing yourself up for more questions. Starting with this: your sample code suggests that changing uv scale/offsets should invalidate a material, but is that the case? In my scene right now I'm just setting every material's checkReadyOnlyOnce to true, all the time, and nothing seems to break - even though I dynamically change a lot of material parameters - uvscale/offset, emissiveColor, diffuseColor, and others. What sorts of parameters do I need to be careful with? Quote Link to comment Share on other sites More sharing options...
Vousk-prod. Posted November 14, 2015 Share Posted November 14, 2015 This discussion is really really interesting, for sure ! Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted November 15, 2015 Share Posted November 15, 2015 Absolutely and even keeping the list of materials using a texutre is not that easy because of custom materials that people can create. How can I force them to indicate to the texture that they are using it? this will require a massive rework I'm not ready to do uv were an example to make it clearer (I should have used fog actually). The real issue with isReady is that this function compiles the right shader for the current option by defining "#define" to use inside the shader. This is done to provide the more efficient shader possible (for example if fog is off, the shader code for fog willl be removed hence better perf). So if you go there: https://github.com/BabylonJS/Babylon.js/blob/master/src/Materials/babylon.standardMaterial.ts#L20, you're gonna see the options that can control the compilation of the shader: public DIFFUSE = false; public AMBIENT = false; public OPACITY = false; public OPACITYRGB = false; public REFLECTION = false; public EMISSIVE = false; public SPECULAR = false; public BUMP = false; public SPECULAROVERALPHA = false; public CLIPPLANE = false; public ALPHATEST = false; public ALPHAFROMDIFFUSE = false; public POINTSIZE = false; public FOG = false; public LIGHT0 = false; public LIGHT1 = false; public LIGHT2 = false; public LIGHT3 = false; public SPOTLIGHT0 = false; public SPOTLIGHT1 = false; public SPOTLIGHT2 = false; public SPOTLIGHT3 = false; public HEMILIGHT0 = false; public HEMILIGHT1 = false; public HEMILIGHT2 = false; public HEMILIGHT3 = false; public POINTLIGHT0 = false; public POINTLIGHT1 = false; public POINTLIGHT2 = false; public POINTLIGHT3 = false; public DIRLIGHT0 = false; public DIRLIGHT1 = false; public DIRLIGHT2 = false; public DIRLIGHT3 = false; public SPECULARTERM = false; public SHADOW0 = false; public SHADOW1 = false; public SHADOW2 = false; public SHADOW3 = false; public SHADOWS = false; public SHADOWVSM0 = false; public SHADOWVSM1 = false; public SHADOWVSM2 = false; public SHADOWVSM3 = false; public SHADOWPCF0 = false; public SHADOWPCF1 = false; public SHADOWPCF2 = false; public SHADOWPCF3 = false; public DIFFUSEFRESNEL = false; public OPACITYFRESNEL = false; public REFLECTIONFRESNEL = false; public EMISSIVEFRESNEL = false; public FRESNEL = false; public NORMAL = false; public UV1 = false; public UV2 = false; public VERTEXCOLOR = false; public VERTEXALPHA = false; public NUM_BONE_INFLUENCERS = 0; public BonesPerMesh = 0; public INSTANCES = false; public GLOSSINESS = false; public ROUGHNESS = false; public EMISSIVEASILLUMINATION = false; public LINKEMISSIVEWITHDIFFUSE = false; public REFLECTIONFRESNELFROMSPECULAR = false; public LIGHTMAP = false; public USELIGHTMAPASSHADOWMAP = false; public REFLECTIONMAP_3D = false; public REFLECTIONMAP_SPHERICAL = false; public REFLECTIONMAP_PLANAR = false; public REFLECTIONMAP_CUBIC = false; public REFLECTIONMAP_PROJECTION = false; public REFLECTIONMAP_SKYBOX = false; public REFLECTIONMAP_EXPLICIT = false; public REFLECTIONMAP_EQUIRECTANGULAR = false; public INVERTCUBICMAP = false; For example, if you set checkOnlyOnce = true and you use your material with a mesh without bones and with a mesh with bones then it will NOT work. Why? because the first mesh compiles the shader without bone and the second one will not compile a new version because of checkOnlyOnce. This cannot be fixed even with setters unfortunately. Smart shaders are really powerful (If you want more detail on it, I wrote a chapter about them in this book) but the performance and memory gains come at the cost of adapting them per mesh and per frame. fenomas 1 Quote Link to comment Share on other sites More sharing options...
fenomas Posted November 16, 2015 Author Share Posted November 16, 2015 Ah, so changing the settings for a given feature doesn't invalidate the material, but adding and removing features does. That makes sense. And again, I understand this is done for performance, it's just that in my scenes the isReady function was eventually taking >10% of the total processing time, at least in Chrome. Thanks as always! Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted November 16, 2015 Share Posted November 16, 2015 My pleasure! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.