diff --git a/cocos/renderer/CCGLProgramState.cpp b/cocos/renderer/CCGLProgramState.cpp index 1affba8235..591b087636 100644 --- a/cocos/renderer/CCGLProgramState.cpp +++ b/cocos/renderer/CCGLProgramState.cpp @@ -65,10 +65,20 @@ UniformValue::UniformValue(Uniform *uniform, GLProgram* glprogram) { } +UniformValue::UniformValue(const UniformValue& o) +{ + *this = o; +} + UniformValue::~UniformValue() { - if (_type == Type::CALLBACK_FN) - delete _value.callback; + if (_type == Type::CALLBACK_FN) + delete _value.callback; + + if (_uniform->type == GL_SAMPLER_2D) + { + CC_SAFE_RELEASE(_value.tex.texture); + } } void UniformValue::apply() @@ -147,12 +157,12 @@ void UniformValue::apply() void UniformValue::setCallback(const std::function &callback) { - // delete previously set callback - // TODO: memory will leak if the user does: - // value->setCallback(); - // value->setFloat(); + // delete previously set callback + // TODO: memory will leak if the user does: + // value->setCallback(); + // value->setFloat(); if (_type == Type::CALLBACK_FN) - delete _value.callback; + delete _value.callback; _value.callback = new (std::nothrow) std::function(); *_value.callback = callback; @@ -165,8 +175,26 @@ void UniformValue::setTexture(GLuint textureId, GLuint textureUnit) //CCASSERT(_uniform->type == GL_SAMPLER_2D, "Wrong type. expecting GL_SAMPLER_2D"); _value.tex.textureId = textureId; _value.tex.textureUnit = textureUnit; + _value.tex.texture = nullptr; _type = Type::VALUE; } + +void UniformValue::setTexture(Texture2D* texture, GLuint textureUnit) +{ + CCASSERT(texture != nullptr, "texture is nullptr"); + + if (texture != _value.tex.texture) + { + CC_SAFE_RELEASE(_value.tex.texture); + CC_SAFE_RETAIN(texture); + _value.tex.texture = texture; + + _value.tex.textureId = texture->getName(); + _value.tex.textureUnit = textureUnit; + _type = Type::VALUE; + } +} + void UniformValue::setInt(int value) { CCASSERT(_uniform->type == GL_INT, "Wrong type: expecting GL_INT"); @@ -218,7 +246,6 @@ void UniformValue::setVec3v(ssize_t size, const Vec3* pointer) _value.v3f.pointer = (const float*)pointer; _value.v3f.size = (GLsizei)size; _type = Type::POINTER; - } void UniformValue::setVec4(const Vec4& value) @@ -243,6 +270,20 @@ void UniformValue::setMat4(const Mat4& value) _type = Type::VALUE; } +UniformValue& UniformValue::operator=(const UniformValue& o) +{ + _uniform = o._uniform; + _glprogram = o._glprogram; + _type = o._type; + _value = o._value; + + if (_uniform->type == GL_SAMPLER_2D) + { + CC_SAFE_RETAIN(_value.tex.texture); + } + return *this; +} + // // // VertexAttribValue @@ -396,7 +437,13 @@ GLProgramState::~GLProgramState() #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) Director::getInstance()->getEventDispatcher()->removeEventListener(_backToForegroundlistener); #endif - + + // _uniforms must be cleared before releasing _glprogram since + // the destructor of UniformValue will call a weak pointer + // which points to the member variable in GLProgram. + _uniforms.clear(); + _attributes.clear(); + CC_SAFE_RELEASE(_glprogram); } @@ -446,7 +493,7 @@ bool GLProgramState::init(GLProgram* glprogram) for(auto &uniform : _glprogram->_userUniforms) { UniformValue value(&uniform.second, _glprogram); - _uniforms[uniform.second.location] = value; + _uniforms[uniform.second.location] = std::move(value); _uniformsByName[uniform.first] = uniform.second.location; } @@ -455,10 +502,14 @@ bool GLProgramState::init(GLProgram* glprogram) void GLProgramState::resetGLProgram() { - CC_SAFE_RELEASE(_glprogram); - _glprogram = nullptr; + // _uniforms must be cleared before releasing _glprogram since + // the destructor of UniformValue will call a weak pointer + // which points to the member variable in GLProgram. _uniforms.clear(); _attributes.clear(); + + CC_SAFE_RELEASE(_glprogram); + _glprogram = nullptr; // first texture is GL_TEXTURE1 _textureUnitIndex = 1; _nodeBinding = nullptr; @@ -811,13 +862,45 @@ void GLProgramState::setUniformMat4(GLint uniformLocation, const Mat4& value) void GLProgramState::setUniformTexture(const std::string& uniformName, Texture2D *texture) { CCASSERT(texture, "Invalid texture"); - setUniformTexture(uniformName, texture->getName()); + auto v = getUniformValue(uniformName); + if (v) + { + if (_boundTextureUnits.find(uniformName) != _boundTextureUnits.end()) + { + v->setTexture(texture, _boundTextureUnits[uniformName]); + } + else + { + v->setTexture(texture, _textureUnitIndex); + _boundTextureUnits[uniformName] = _textureUnitIndex++; + } + } + else + { + CCLOG("cocos2d: warning: Uniform not found: %s", uniformName.c_str()); + } } void GLProgramState::setUniformTexture(GLint uniformLocation, Texture2D *texture) { CCASSERT(texture, "Invalid texture"); - setUniformTexture(uniformLocation, texture->getName()); + auto v = getUniformValue(uniformLocation); + if (v) + { + if (_boundTextureUnits.find(v->_uniform->name) != _boundTextureUnits.end()) + { + v->setTexture(texture, _boundTextureUnits[v->_uniform->name]); + } + else + { + v->setTexture(texture, _textureUnitIndex); + _boundTextureUnits[v->_uniform->name] = _textureUnitIndex++; + } + } + else + { + CCLOG("cocos2d: warning: Uniform at location not found: %i", uniformLocation); + } } void GLProgramState::setUniformTexture(const std::string& uniformName, GLuint textureId) diff --git a/cocos/renderer/CCGLProgramState.h b/cocos/renderer/CCGLProgramState.h index fa2a37dbcf..442e795d76 100644 --- a/cocos/renderer/CCGLProgramState.h +++ b/cocos/renderer/CCGLProgramState.h @@ -71,6 +71,8 @@ public: */ UniformValue(Uniform *uniform, GLProgram* glprogram); + UniformValue(const UniformValue& o); + /**Destructor.*/ ~UniformValue(); /**@{ @@ -101,12 +103,23 @@ public: Set texture to uniform value. @param textureId The texture handle. @param textureUnit The binding texture unit to be used in shader. + @deprecated please use setTexture(Texture2D* texture, GLuint textureUnit) instead, + Passing a `textureId` may trigger texture lost issue (https://github.com/cocos2d/cocos2d-x/issues/16871). */ - void setTexture(GLuint textureId, GLuint textureUnit); + CC_DEPRECATED_ATTRIBUTE void setTexture(GLuint textureId, GLuint textureUnit); + + /** + Set texture to uniform value. + @param texture The texture. + @param textureUnit The binding texture unit to be used in shader. + */ + void setTexture(Texture2D* texture, GLuint textureUnit); /**Apply the uniform value to openGL pipeline.*/ void apply(); + UniformValue& operator=(const UniformValue& o); + protected: enum class Type { @@ -134,8 +147,9 @@ protected: float v4Value[4]; float matrixValue[16]; struct { - GLuint textureId; + GLuint textureId; // textureId will be deprecated since we use 'texture->getName()' to get textureId. GLuint textureUnit; + Texture2D* texture; } tex; struct { const float* pointer; @@ -317,7 +331,11 @@ public: void setUniformMat4(const std::string& uniformName, const Mat4& value); void setUniformCallback(const std::string& uniformName, const std::function &callback); void setUniformTexture(const std::string& uniformName, Texture2D *texture); - void setUniformTexture(const std::string& uniformName, GLuint textureId); + /** + * @deprecated, please use setUniformTexture(const std::string& uniformName, Texture2D *texture) instead, + * Passing a `textureId` may trigger texture lost issue (https://github.com/cocos2d/cocos2d-x/issues/16871). + */ + CC_DEPRECATED_ATTRIBUTE void setUniformTexture(const std::string& uniformName, GLuint textureId); /**@}*/ /** @{ @@ -335,7 +353,11 @@ public: void setUniformMat4(GLint uniformLocation, const Mat4& value); void setUniformCallback(GLint uniformLocation, const std::function &callback); void setUniformTexture(GLint uniformLocation, Texture2D *texture); - void setUniformTexture(GLint uniformLocation, GLuint textureId); + /** + * @deprecated, please use setUniformTexture(GLint uniformLocation, Texture2D *texture) instead, + * Passing a `textureId` may trigger texture lost issue (https://github.com/cocos2d/cocos2d-x/issues/16871). + */ + CC_DEPRECATED_ATTRIBUTE void setUniformTexture(GLint uniformLocation, GLuint textureId); /**@}*/ /** diff --git a/cocos/vr/CCVRGenericRenderer.cpp b/cocos/vr/CCVRGenericRenderer.cpp index 869025a96e..f862922759 100644 --- a/cocos/vr/CCVRGenericRenderer.cpp +++ b/cocos/vr/CCVRGenericRenderer.cpp @@ -132,8 +132,8 @@ void VRGenericRenderer::render(Scene* scene, Renderer* renderer) glGetIntegerv(GL_VIEWPORT, origViewport); glViewport(0, 0, _texSize.width, _texSize.height); - renderDistortionMesh(_leftDistortionMesh, texture->getName()); - renderDistortionMesh(_rightDistortionMesh, texture->getName()); + renderDistortionMesh(_leftDistortionMesh, texture); + renderDistortionMesh(_rightDistortionMesh, texture); glViewport(origViewport[0], origViewport[1], origViewport[2], origViewport[3]); @@ -144,14 +144,14 @@ void VRGenericRenderer::render(Scene* scene, Renderer* renderer) CHECK_GL_ERROR_DEBUG(); } -void VRGenericRenderer::renderDistortionMesh(DistortionMesh *mesh, GLint textureID) +void VRGenericRenderer::renderDistortionMesh(DistortionMesh *mesh, Texture2D* texture) { glBindBuffer(GL_ARRAY_BUFFER, mesh->_arrayBufferID); _glProgramState->setVertexAttribPointer("a_position", 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(0 * sizeof(float))); _glProgramState->setVertexAttribPointer("a_textureCoord", 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(2 * sizeof(float))); _glProgramState->setVertexAttribPointer("a_vignette", 1, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(4 * sizeof(float))); - _glProgramState->setUniformTexture("u_textureSampler", textureID); + _glProgramState->setUniformTexture("u_textureSampler", texture); _glProgramState->apply(Mat4::IDENTITY); diff --git a/cocos/vr/CCVRGenericRenderer.h b/cocos/vr/CCVRGenericRenderer.h index 98c3b9eb2a..851d5c98fd 100644 --- a/cocos/vr/CCVRGenericRenderer.h +++ b/cocos/vr/CCVRGenericRenderer.h @@ -63,7 +63,7 @@ public: protected: void setupGLProgram(); - void renderDistortionMesh(DistortionMesh *mesh, GLint textureID); + void renderDistortionMesh(DistortionMesh *mesh, Texture2D* texture); DistortionMesh* createDistortionMesh(VREye::EyeType eyeType); experimental::FrameBuffer* _fb; diff --git a/tests/cpp-tests/Classes/Sprite3DTest/Sprite3DTest.cpp b/tests/cpp-tests/Classes/Sprite3DTest/Sprite3DTest.cpp index 2ad67097f9..7bb170cce0 100644 --- a/tests/cpp-tests/Classes/Sprite3DTest/Sprite3DTest.cpp +++ b/tests/cpp-tests/Classes/Sprite3DTest/Sprite3DTest.cpp @@ -786,6 +786,7 @@ void Sprite3DEffectTest::addNewSpriteWithCoords(Vec2 p) material->setTechnique("outline_noneskinned"); sprite->setMaterial(material); sprite->setScale(6.f); + Director::getInstance()->getTextureCache()->removeUnusedTextures(); //add to scene addChild( sprite );