From aa33f980d2a753a9d0e8ca91688afa9e24555e42 Mon Sep 17 00:00:00 2001 From: badlogic Date: Mon, 20 Feb 2017 10:00:57 +0100 Subject: [PATCH] [cocos2dx] First try at optimizing renderer by removing allocation. Meshes temporarily broken --- .../example/Classes/BatchingExample.cpp | 4 +- spine-cocos2dx/src/spine/SkeletonBatch.cpp | 176 ++++++++++-------- spine-cocos2dx/src/spine/SkeletonBatch.h | 34 ++-- spine-cocos2dx/src/spine/SkeletonRenderer.cpp | 69 ++++--- 4 files changed, 158 insertions(+), 125 deletions(-) diff --git a/spine-cocos2dx/example/Classes/BatchingExample.cpp b/spine-cocos2dx/example/Classes/BatchingExample.cpp index e0bfccce7..f6ce87198 100644 --- a/spine-cocos2dx/example/Classes/BatchingExample.cpp +++ b/spine-cocos2dx/example/Classes/BatchingExample.cpp @@ -65,13 +65,13 @@ 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 < 50; i++) { + for (int i = 0; i < 5000; i++) { // Each skeleton node shares the same atlas, skeleton data, and mix times. SkeletonAnimation* skeletonNode = SkeletonAnimation::createWithData(_skeletonData, false); skeletonNode->setAnimationStateData(_stateData); skeletonNode->setAnimation(0, "walk", true); - skeletonNode->addAnimation(0, "jump", false, 3); + skeletonNode->addAnimation(0, "jump", true, RandomHelper::random_int(0, 300) / 100.0f); skeletonNode->addAnimation(0, "run", true); skeletonNode->setPosition(Vec2( diff --git a/spine-cocos2dx/src/spine/SkeletonBatch.cpp b/spine-cocos2dx/src/spine/SkeletonBatch.cpp index 981b7b87b..f540e0d28 100644 --- a/spine-cocos2dx/src/spine/SkeletonBatch.cpp +++ b/spine-cocos2dx/src/spine/SkeletonBatch.cpp @@ -35,83 +35,109 @@ USING_NS_CC; #define EVENT_AFTER_DRAW_RESET_POSITION "director_after_draw" using std::max; +#define INITIAL_SIZE (10000) namespace spine { - static SkeletonBatch* instance = nullptr; - - SkeletonBatch* SkeletonBatch::getInstance () { - if (!instance) instance = new SkeletonBatch(); - return instance; - } - - void SkeletonBatch::destroyInstance () { - if (instance) { - delete instance; - instance = nullptr; - } - } - - SkeletonBatch::SkeletonBatch () - { - _firstCommand = new Command(); - _command = _firstCommand; - - Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_AFTER_DRAW_RESET_POSITION, [this](EventCustom* eventCustom){ - this->update(0); - });; - } - - SkeletonBatch::~SkeletonBatch () { - Director::getInstance()->getEventDispatcher()->removeCustomEventListeners(EVENT_AFTER_DRAW_RESET_POSITION); - - Command* command = _firstCommand; - while (command) { - Command* next = command->next; - delete command; - command = next; - } - } - - void SkeletonBatch::update (float delta) { - _command = _firstCommand; - } - - void SkeletonBatch::addCommand (cocos2d::Renderer* renderer, float globalZOrder, GLuint textureID, GLProgramState* glProgramState, - BlendFunc blendFunc, const TrianglesCommand::Triangles& triangles, const Mat4& transform, uint32_t transformFlags - ) { - if (_command->triangles->verts) { - free(_command->triangles->verts); - _command->triangles->verts = NULL; - } - - _command->triangles->verts = (V3F_C4B_T2F *)malloc(sizeof(V3F_C4B_T2F) * triangles.vertCount); - memcpy(_command->triangles->verts, triangles.verts, sizeof(V3F_C4B_T2F) * triangles.vertCount); - - _command->triangles->vertCount = triangles.vertCount; - _command->triangles->indexCount = triangles.indexCount; - _command->triangles->indices = triangles.indices; - - _command->trianglesCommand->init(globalZOrder, textureID, glProgramState, blendFunc, *_command->triangles, transform); - renderer->addCommand(_command->trianglesCommand); - - if (!_command->next) _command->next = new Command(); - _command = _command->next; - } - - SkeletonBatch::Command::Command () : - next(nullptr) - { - trianglesCommand = new TrianglesCommand(); - triangles = new TrianglesCommand::Triangles(); - } - - SkeletonBatch::Command::~Command () { - if (triangles->verts) { - free(triangles->verts); - } - delete triangles; - delete trianglesCommand; - } +static SkeletonBatch* instance = nullptr; +SkeletonBatch* SkeletonBatch::getInstance () { + if (!instance) instance = new SkeletonBatch(); + return instance; +} + +void SkeletonBatch::destroyInstance () { + if (instance) { + delete instance; + instance = nullptr; + } +} + +SkeletonBatch::SkeletonBatch () { + for (unsigned int i = 0; i < INITIAL_SIZE; i++) { + _commandsPool.push_back(new TrianglesCommand()); + } + + reset (); + + // callback after drawing is finished so we can clear out the batch state + // for the next frame + Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_AFTER_DRAW_RESET_POSITION, [this](EventCustom* eventCustom){ + this->update(0); + });; +} + +SkeletonBatch::~SkeletonBatch () { + Director::getInstance()->getEventDispatcher()->removeCustomEventListeners(EVENT_AFTER_DRAW_RESET_POSITION); + + for (unsigned int i = 0; i < _commandsPool.size(); i++) { + delete _commandsPool[i]; + _commandsPool[i] = nullptr; + } +} + +void SkeletonBatch::update (float delta) { + reset(); +} + +cocos2d::V3F_C4B_T2F* SkeletonBatch::allocateVertices(uint32_t numVertices) { + if (_vertices.size() - _numVertices < numVertices) { + cocos2d::V3F_C4B_T2F* oldData = _vertices.data(); + _vertices.resize((_vertices.size() + numVertices) * 2 + 1); + cocos2d::V3F_C4B_T2F* newData = _vertices.data(); + for (uint32_t i = 0; i < this->_nextFreeCommand; i++) { + TrianglesCommand* command = _commandsPool[i]; + cocos2d::TrianglesCommand::Triangles& triangles = (cocos2d::TrianglesCommand::Triangles&)command->getTriangles(); + triangles.verts = newData + (triangles.verts - oldData); + } + } + + cocos2d::V3F_C4B_T2F* vertices = _vertices.data() + _numVertices; + _numVertices += numVertices; + return vertices; +} + +const cocos2d::TrianglesCommand::Triangles& SkeletonBatch::addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand::Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags) { + TrianglesCommand* command = nextFreeCommand(); + command->init(globalOrder, textureID, glProgramState, blendType, triangles, mv, flags); + renderer->addCommand(command); + return command->getTriangles(); +} + +void SkeletonBatch::reset() { + _nextFreeCommand = 0; + _numVertices = 0; +} + +cocos2d::TrianglesCommand* SkeletonBatch::nextFreeCommand() { + if (_commandsPool.size() <= _nextFreeCommand) { + unsigned int newSize = _commandsPool.size() * 2 + 1; + for (int i = _commandsPool.size(); i < newSize; i++) { + _commandsPool.push_back(new TrianglesCommand()); + } + } + return _commandsPool[_nextFreeCommand++]; +} + +//void SkeletonBatch::addCommand (cocos2d::Renderer* renderer, float globalZOrder, GLuint textureID, GLProgramState* glProgramState, +// BlendFunc blendFunc, const TrianglesCommand::Triangles& triangles, const Mat4& transform, uint32_t transformFlags +// ) { +// if (_command->triangles->verts) { +// free(_command->triangles->verts); +// _command->triangles->verts = NULL; +// } +// +// _command->triangles->verts = (V3F_C4B_T2F *)malloc(sizeof(V3F_C4B_T2F) * triangles.vertCount); +// memcpy(_command->triangles->verts, triangles.verts, sizeof(V3F_C4B_T2F) * triangles.vertCount); +// +// _command->triangles->vertCount = triangles.vertCount; +// _command->triangles->indexCount = triangles.indexCount; +// _command->triangles->indices = triangles.indices; +// +// _command->trianglesCommand->init(globalZOrder, textureID, glProgramState, blendFunc, *_command->triangles, transform); +// renderer->addCommand(_command->trianglesCommand); +// +// if (!_command->next) _command->next = new Command(); +// _command = _command->next; +//} } diff --git a/spine-cocos2dx/src/spine/SkeletonBatch.h b/spine-cocos2dx/src/spine/SkeletonBatch.h index a47560635..84113534d 100644 --- a/spine-cocos2dx/src/spine/SkeletonBatch.h +++ b/spine-cocos2dx/src/spine/SkeletonBatch.h @@ -33,6 +33,7 @@ #include #include "cocos2d.h" +#include namespace spine { @@ -43,28 +44,27 @@ namespace spine { static void destroyInstance (); void update (float delta); - - void addCommand (cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, - cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand:: Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags); + + cocos2d::V3F_C4B_T2F* allocateVertices(uint32_t numVertices); + const cocos2d::TrianglesCommand::Triangles& addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand::Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags); protected: SkeletonBatch (); virtual ~SkeletonBatch (); - - class Command { - public: - Command (); - virtual ~Command (); - - cocos2d::TrianglesCommand* trianglesCommand; - cocos2d::TrianglesCommand::Triangles* triangles; - Command* next; - }; - - Command* _firstCommand; - Command* _command; + + void reset (); + + cocos2d::TrianglesCommand* nextFreeCommand (); + + // pool of commands + std::vector _commandsPool; + uint32_t _nextFreeCommand; + + // pool of vertices + std::vector _vertices; + uint32_t _numVertices; }; - + } #endif // SPINE_SKELETONBATCH_H_ diff --git a/spine-cocos2dx/src/spine/SkeletonRenderer.cpp b/spine-cocos2dx/src/spine/SkeletonRenderer.cpp index 0bdd09f09..5f32f6ea3 100644 --- a/spine-cocos2dx/src/spine/SkeletonRenderer.cpp +++ b/spine-cocos2dx/src/spine/SkeletonRenderer.cpp @@ -189,12 +189,19 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t for (int i = 0, n = _skeleton->slotsCount; i < n; ++i) { spSlot* slot = _skeleton->drawOrder[i]; if (!slot->attachment) continue; - + + cocos2d::TrianglesCommand::Triangles triangles; + switch (slot->attachment->type) { case SP_ATTACHMENT_REGION: { spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment; - spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices, 0, 2); attachmentVertices = getAttachmentVertices(attachment); + triangles.indices = attachmentVertices->_triangles->indices; + triangles.indexCount = attachmentVertices->_triangles->indexCount; + triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount); + triangles.vertCount = attachmentVertices->_triangles->vertCount; + memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount); + spRegionAttachment_computeWorldVertices(attachment, slot->bone, (float*)triangles.verts, 0, 6); color.r = attachment->color.r; color.g = attachment->color.g; color.b = attachment->color.b; @@ -203,8 +210,13 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t } case SP_ATTACHMENT_MESH: { spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment; - spVertexAttachment_computeWorldVertices(SUPER(attachment), slot, 0, attachment->super.worldVerticesLength, _worldVertices, 0, 2); attachmentVertices = getAttachmentVertices(attachment); + triangles.indices = attachmentVertices->_triangles->indices; + triangles.indexCount = attachmentVertices->_triangles->indexCount; + triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount); + triangles.vertCount = attachmentVertices->_triangles->vertCount; + memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount); + spVertexAttachment_computeWorldVertices(SUPER(attachment), slot, 0, triangles.vertCount, (float*)triangles.verts, 0, 6); color.r = attachment->color.r; color.g = attachment->color.g; color.b = attachment->color.b; @@ -220,40 +232,35 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t color.r *= _skeleton->color.r * slot->color.r * multiplier; color.g *= _skeleton->color.g * slot->color.g * multiplier; color.b *= _skeleton->color.b * slot->color.b * multiplier; + + BlendFunc blendFunc; + switch (slot->data->blendMode) { + case SP_BLEND_MODE_ADDITIVE: + blendFunc.src = _premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + blendFunc.dst = GL_ONE; + break; + case SP_BLEND_MODE_MULTIPLY: + blendFunc.src = GL_DST_COLOR; + blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA; + break; + case SP_BLEND_MODE_SCREEN: + blendFunc.src = GL_ONE; + blendFunc.dst = GL_ONE_MINUS_SRC_COLOR; + break; + default: + blendFunc.src = _premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA; + } + + const cocos2d::TrianglesCommand::Triangles& batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, triangles, transform, transformFlags); - - - for (int v = 0, w = 0, vn = attachmentVertices->_triangles->vertCount; v < vn; ++v, w += 2) { - V3F_C4B_T2F* vertex = attachmentVertices->_triangles->verts + v; - vertex->vertices.x = _worldVertices[w]; - vertex->vertices.y = _worldVertices[w + 1]; + for (int v = 0, vn = batchedTriangles.vertCount; v < vn; ++v) { + V3F_C4B_T2F* vertex = batchedTriangles.verts + v; vertex->colors.r = (GLubyte)color.r; vertex->colors.g = (GLubyte)color.g; vertex->colors.b = (GLubyte)color.b; vertex->colors.a = (GLubyte)color.a; } - - BlendFunc blendFunc; - switch (slot->data->blendMode) { - case SP_BLEND_MODE_ADDITIVE: - blendFunc.src = _premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; - blendFunc.dst = GL_ONE; - break; - case SP_BLEND_MODE_MULTIPLY: - blendFunc.src = GL_DST_COLOR; - blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA; - break; - case SP_BLEND_MODE_SCREEN: - blendFunc.src = GL_ONE; - blendFunc.dst = GL_ONE_MINUS_SRC_COLOR; - break; - default: - blendFunc.src = _premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; - blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA; - } - - batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, - *attachmentVertices->_triangles, transform, transformFlags); } if (_debugSlots || _debugBones) {