Closed ShuraLiu closed 8 years ago
Thanks @ShuraLiu . @ricardoquesada could you please take a look? As i remember you said material id is not implemented as expected, but i may bring performance issue.
Probably similar to #14826 (but not the same)
UPDATE @ShuraLiu Yes, TriangleCommands cannot be batched. I'll take a look at it to see whether or not it is feasible to do it without affecting the performance.
@ricardoquesada the API getMaterialID() is only used when doing batching in CCRenderer.cpp now, and if we generate material ID at that time, not in init(), we can confirm the uniform values are the same or not in differernt TriangleCommands.
@ShuraLiu
There are a few issues with uniforms a material id, and basically is that we should compare them.
and the question is how to compare them in an efficient and reliable way?
currently they are stored in a unordered_map<int,UniformValue>
.
comparing them in each iteration could be very time consuming... but perhaps it is worth it.
perhaps hashing them and update the hash each time a uniform is updated could be another approach.
ideas?
@ricardoquesada Maybe the key is not TriangleCommand but GLProgram. In 2dx if we render different nodes using the same glprogram with different uniforms, we need to add a custom command before the render command of each node, such as : CustomCommand cmd1; renderer->addCommand(&cmd1); TrianglesCommand cmd2; renderer->addCommand(&cmd2); For example, we need to write a subclass of cocos2d::Sprite and in it's draw funciton add two commands into the render queue, one for setting uniforms and one for rendering. But if the GLProgram can be cloned, we do not need to write the new class, but just clone two GLPrograms and set them to different cocos2d::Sprite classes and set their uniforms. for differents Sprites with the same GLProgram, them share the same uniforms, and the same Material ID, and their commands can be batched. What do you think?
Sorry, why do you need to write a custom command to have uniforms? Are you adding attributes as well?
If you are adding custom attributes, then this is a duplicate of #14826, if you just want to add custom uniforms, then you don't need to add a custom command. The idea behind GLProgramState (and not GLProgram) is that you can add uniforms easily. Am I missing something?
@ricardoquesada Because I use the same GLProgram for more than one node, and for the same uniform, the values are different for different nodes. If I set the uniforms in draw function(or visit() func), the values will be covered, only setting unforms in a command can ensure that different nodes take different effects. If I want to set values in draw function, I need to create GLPrograms for each node, although the GLPrograms have the same vert and frag shaders. But if I do it, it's not necessary to check the uniforms when generating material ID. Maybe it's a solution for this issue. Do not check the uniforms, and if the values are different, users need to create a new GLProgram.
Cocos2d-x has two classes:
GLProgram
: which is basically a pointer to a GLProgram: it includes the vertex and fragment shader. You can manually set attributes/uniforms but it is not user friendly, and it is not reusable (consider the uniform/attributes API deprecated).GLProgramState
: which is basically the "state" of the GLProgram. It contains a GLProgram, but also contains a user friendly API to set uniforms and attributes. So, what you need is:
GLProgram
GLProgramState
as needed (one per sprite if needed) and reuse the GLProgram.So, if we want to support custom uniforms we find a way to hash the unordered_map
that is being used in GLProgramState
You are right. I used the GLProgram in wrong way, I should use GLProgramState. The same GLProgramState means the same uniforms, so what about using GLProgramState instead of GLProgram as part of the material ID , and we do not need to hash and compare the uniforms.
@ShuraLiu good point. I think that should work. thanks.
Hi @ricardoquesada , if i understand correctly
right?
More and more devices support opengles 3.0 now, do you consider to add opengles 3.0 support recently? Opengl 3.0 has the instancing feature, which will use gl_instanceID/buffer and glDrawElementsInstanced/glDrawArraysInstanced to batch the drawcall.
Unity 5.4 also support instancing too. http://unitytaiwan.blogspot.tw/2016/05/unite-2016-unity-54-gpu-instancing.html
@minggo correct. I think we can do that following @ShuraLiu 's suggestion of just comparing the pointer to the GLProgramState
.
@congling yes, we have considered supporting it. Of course it is a great feature. Most probably it will be added in v4.0
@ricardoquesada so should use the same GLProgramState
if want to be batched?
@ricardoquesada this might work, but it'll let the programmer to merge the same state.
Consider this scenario: In a game scene with snow, there're hundreds of snowflake around, a shader to let the snowflake(sprite) rotate 6 degree every frame (for 30 fps). The most common way is to create a GLProgram globally, and then create GLProgramState for each snowflake.
But if I want to batch the drawcalls, I need to build up a 60-length array to record all GLProgramStates in the scene and then set it to each snowflake at the frame when the snowflake is generated. At each frame, array_index++; after 60 frames, the array_index will reset to zero.
Am I correct? Thanks
@minggo correct. so when we the GLProgramState()
has custom uniforms we should check if it is using the same GLProgramState()
as the previous batched command triangle. If so, then they can be batched together. I'll work on this tomorrow.
@congling Sorry, I didn't get the example.
If you have hundreds of snowflakes (same GLProgram, same Texture ID, and same uniforms) then they should all reuse the GLProgramState
.
We have a GLProgramStateCache
for this reason... for example, each time that you create an sprite, we call setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, texture));
, so that we can reuse the GLProgramState
.
If you want to use custom uniforms, you should create your own GLProgramState
and replace it.
Am I missing something? thanks.
@ricardoquesada sounds good to me.
fixed in v3 (planned for v3.14) @minggo let me know if you want to have it for v3.13.
@ricardoquesada i think it is ok to just fix it in v3.14.
Hi Ricardo,
Do you plan to go one step further at some point and allow batching even if the glProgramState are different but their glProgram and uniforms are the same? The improvement you did is nice but we have several use case where it's tricky for us to share the glProgramState even if most of the time (but not all the time) consecutive node will have exactly the same uniforms.
Thanks,
Sebastien
@Fraggle I'm no longer working on cocos2d. You should ask @minggo instead. It could be more expensive (resource-wise) to check all possible uniforms, but not that expensive... it could be added, but it would be better to understand the use case first.
In file CCRenderer.cpp, line 876, 2dx checks the MaterialID of the command, and if "newMaterialID == MATERIAL_ID_DO_NOT_BATCH" the command will not do batching. In file CCTrianglesCommand.cpp, line 81, if the glprogram use custom uniforms, the meterialID will be set to MATERIAL_ID_DO_NOT_BATCH, which means the command will never do batching. In the latest version of Spine ,it uses CCTrianglesCommands for rendering, so when I use custom shaders for Spine, it will not do any batching because of the custom uniforms, and it will cost more than 30 drawcalls for a single Spine node in my program, which is really a big problem. I think both the 2dx and Spine need to do something to fix this problem. @minggo