Explorar el Código

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

badlogic hace 8 años
padre
commit
8c6a91c2a1

+ 2 - 2
spine-cocos2dx/README.md

@@ -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
 

+ 6 - 2
spine-cocos2dx/example/Classes/BatchingExample.cpp

@@ -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),

+ 36 - 2
spine-cocos2dx/src/spine/SkeletonRenderer.cpp

@@ -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);

+ 1 - 1
spine-cocos2dx/src/spine/SkeletonTwoColorBatch.cpp

@@ -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;
 }