[cocos2dx] Batching of adjacent two color tinted skeletons. See README.md for rules on what does and doesn't break batching

This commit is contained in:
badlogic 2017-03-01 11:28:29 +01:00
parent 4bb4c4d17a
commit 8c6a91c2a1
4 changed files with 45 additions and 7 deletions

View File

@ -69,8 +69,8 @@ The Spine cocos2d-x example works on Windows and Mac OS X.
## Notes
- Images are premultiplied by cocos2d-x, so the Spine atlas images should *not* use premultiplied alpha.
- Two color tinting needs to be enabled on a per-skeleton basis. Call `SkeletonRenderer::setTwoColorTine(true)` or `SkeletonAnimation::setTwoColorTint(true)` after you created the skeleton instance. Note that two color tinting requires a custom shader and vertex format. Skeletons rendered with two color tinting can therefore not be batched with single color tinted skeletons or other 2D cocos2d-x elements like sprites. However, two-color tinted skeletons will be batched if possible when rendered after one another.
* Images are premultiplied by cocos2d-x, so the Spine atlas images should *not* use premultiplied alpha.
* Two color tinting needs to be enabled on a per-skeleton basis. Call `SkeletonRenderer::setTwoColorTine(true)` or `SkeletonAnimation::setTwoColorTint(true)` after you created the skeleton instance. Note that two color tinting requires a custom shader and vertex format. Skeletons rendered with two color tinting can therefore not be batched with single color tinted skeletons or other 2D cocos2d-x elements like sprites. However, two-color tinted skeletons will be batched if possible when rendered after one another. Attaching a child to a two color tinted skeleton will also break the batch.
## Examples

View File

@ -65,7 +65,7 @@ bool BatchingExample::init () {
int xMin = _contentSize.width * 0.10f, xMax = _contentSize.width * 0.90f;
int yMin = 0, yMax = _contentSize.height * 0.7f;
for (int i = 0; i < 100; i++) {
for (int i = 0, j = 0; i < 50; i++) {
// Each skeleton node shares the same atlas, skeleton data, and mix times.
SkeletonAnimation* skeletonNode = SkeletonAnimation::createWithData(_skeletonData, false);
skeletonNode->setAnimationStateData(_stateData);
@ -74,7 +74,11 @@ bool BatchingExample::init () {
skeletonNode->addAnimation(0, "jump", true, RandomHelper::random_int(0, 300) / 100.0f);
skeletonNode->addAnimation(0, "run", true);
// skeletonNode->setTwoColorTint(true);
// alternative setting two color tint for groups of 10 skeletons
// should end up with #skeletons / 10 batches
if (j++ < 10)
skeletonNode->setTwoColorTint(true);
if (j == 20) j = 0;
skeletonNode->setPosition(Vec2(
RandomHelper::random_int(xMin, xMax),

View File

@ -67,7 +67,6 @@ void SkeletonRenderer::initialize () {
setOpacityModifyRGB(true);
setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP));
setTwoColorTint(true);
}
void SkeletonRenderer::setSkeletonData (spSkeletonData *skeletonData, bool ownsSkeletonData) {
@ -332,7 +331,42 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
}
}
if (lastTwoColorTrianglesCommand) lastTwoColorTrianglesCommand->setForceFlush(true);
if (lastTwoColorTrianglesCommand) {
Node* parent = this->getParent();
// We need to decide if we can postpone flushing the current
// batch. We can postpone if the next sibling node is a
// two color tinted skeleton with the same global-z.
// The parent->getChildrenCount() > 100 check is a hack
// as checking for a sibling is an O(n) operation, and if
// all children of this nodes parent are skeletons, we
// are in O(n2) territory.
if (!parent || parent->getChildrenCount() > 100 || getChildrenCount() != 0) {
lastTwoColorTrianglesCommand->setForceFlush(true);
} else {
Vector<Node*>& children = parent->getChildren();
Node* sibling = nullptr;
for (ssize_t i = 0; i < children.size(); i++) {
if (children.at(i) == this) {
if (i < children.size() - 1) {
sibling = children.at(i+1);
break;
}
}
}
if (!sibling) {
lastTwoColorTrianglesCommand->setForceFlush(true);
} else {
SkeletonRenderer* siblingSkeleton = dynamic_cast<SkeletonRenderer*>(sibling);
if (!siblingSkeleton || // flush is next sibling isn't a SkeletonRenderer
!siblingSkeleton->isTwoColorTint() || // flush if next sibling isn't two color tinted
!siblingSkeleton->isVisible() || // flush if next sibling is two color tinted but not visible
(siblingSkeleton->getGlobalZOrder() != this->getGlobalZOrder())) { // flush if next sibling is two color tinted but z-order differs
lastTwoColorTrianglesCommand->setForceFlush(true);
}
}
}
}
if (_debugSlots || _debugBones) {
drawDebug(renderer, transform, transformFlags);

View File

@ -226,7 +226,7 @@ V3F_C4B_C4B_T2F* SkeletonTwoColorBatch::allocateVertices(uint32_t numVertices) {
TwoColorTrianglesCommand* SkeletonTwoColorBatch::addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const TwoColorTriangles& triangles, const cocos2d::Mat4& mv, uint32_t flags) {
TwoColorTrianglesCommand* command = nextFreeCommand();
command->init(globalOrder, textureID, glProgramState, blendType, triangles, mv, flags);
renderer->addCommand(command);
renderer->addCommand(command);
return command;
}