| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "GLcommon/ObjectNameSpace.h" |
| |
| #include <assert.h> |
| |
| #include "GLcommon/GLEScontext.h" |
| #include "GLcommon/TranslatorIfaces.h" |
| #include "aemu/base/synchronization/Lock.h" |
| #include "aemu/base/containers/Lookup.h" |
| #include "aemu/base/files/PathUtils.h" |
| #include "aemu/base/files/StreamSerializing.h" |
| #include "host-common/crash_reporter.h" |
| #include "host-common/logging.h" |
| #include "snapshot/TextureLoader.h" |
| #include "snapshot/TextureSaver.h" |
| |
| using android::snapshot::ITextureSaver; |
| using android::snapshot::ITextureLoader; |
| using android::snapshot::ITextureSaverPtr; |
| using android::snapshot::ITextureLoaderPtr; |
| using android::snapshot::ITextureLoaderWPtr; |
| |
| NameSpace::NameSpace(NamedObjectType p_type, GlobalNameSpace *globalNameSpace, |
| android::base::Stream* stream, const ObjectData::loadObject_t& loadObject) : |
| m_type(p_type), |
| m_globalNameSpace(globalNameSpace) { |
| if (!stream) return; |
| // When loading from a snapshot, we restores translator states here, but |
| // host GPU states are not touched until postLoadRestore is called. |
| // GlobalNames are not yet generated. |
| size_t objSize = stream->getBe32(); |
| for (size_t obj = 0; obj < objSize; obj++) { |
| ObjectLocalName localName = stream->getBe64(); |
| ObjectDataPtr data = loadObject((NamedObjectType)m_type, |
| localName, stream); |
| if (m_type == NamedObjectType::TEXTURE) { |
| // Texture data are managed differently |
| // They are loaded by GlobalNameSpace before loading |
| // share groups |
| TextureData* texData = (TextureData*)data.get(); |
| if (!texData->getGlobalName()) { |
| GL_LOG("%p: texture data %p is 0 texture.", this, texData); |
| continue; |
| } |
| |
| SaveableTexturePtr saveableTexture = |
| globalNameSpace->getSaveableTextureFromLoad( |
| texData->getGlobalName()); |
| texData->setSaveableTexture(std::move(saveableTexture)); |
| texData->setGlobalName(0); |
| } |
| setObjectData(localName, std::move(data)); |
| } |
| } |
| |
| NameSpace::~NameSpace() { |
| } |
| |
| void NameSpace::postLoad(const ObjectData::getObjDataPtr_t& getObjDataPtr) { |
| for (const auto& objData : m_objectDataMap) { |
| GL_LOG("%p: try to load object %llu", this, objData.first); |
| if (!objData.second) { |
| // bug: 130631787 |
| // emugl::emugl_crash_reporter( |
| // "Fatal: null object data ptr on restore\n"); |
| continue; |
| } |
| objData.second->postLoad(getObjDataPtr); |
| } |
| } |
| |
| void NameSpace::touchTextures() { |
| assert(m_type == NamedObjectType::TEXTURE); |
| for (const auto& obj : m_objectDataMap) { |
| TextureData* texData = (TextureData*)obj.second.get(); |
| if (!texData->needRestore()) { |
| GL_LOG("%p: texture data %p does not need restore", this, texData); |
| continue; |
| } |
| const SaveableTexturePtr& saveableTexture = texData->getSaveableTexture(); |
| if (!saveableTexture.get()) { |
| GL_LOG("%p: warning: no saveableTexture for texture data %p", this, texData); |
| continue; |
| } |
| |
| NamedObjectPtr texNamedObj = saveableTexture->getGlobalObject(); |
| if (!texNamedObj) { |
| GL_LOG("%p: fatal: global object null for texture data %p", this, texData); |
| emugl::emugl_crash_reporter( |
| "fatal: null global texture object in " |
| "NameSpace::touchTextures"); |
| } |
| setGlobalObject(obj.first, texNamedObj); |
| texData->setGlobalName(texNamedObj->getGlobalName()); |
| texData->restore(0, nullptr); |
| } |
| } |
| |
| void NameSpace::postLoadRestore(const ObjectData::getGlobalName_t& getGlobalName) { |
| // Texture data are special, they got the global name from SaveableTexture |
| // This is because texture data can be shared across multiple share groups |
| if (m_type == NamedObjectType::TEXTURE) { |
| touchTextures(); |
| return; |
| } |
| // 2 passes are needed for SHADER_OR_PROGRAM type, because (1) they |
| // live in the same namespace (2) shaders must be created before |
| // programs. |
| int numPasses = m_type == NamedObjectType::SHADER_OR_PROGRAM |
| ? 2 : 1; |
| for (int pass = 0; pass < numPasses; pass ++) { |
| for (const auto& obj : m_objectDataMap) { |
| assert(m_type == ObjectDataType2NamedObjectType( |
| obj.second->getDataType())); |
| // get global names |
| if ((obj.second->getDataType() == PROGRAM_DATA && pass == 0) |
| || (obj.second->getDataType() == SHADER_DATA && |
| pass == 1)) { |
| continue; |
| } |
| genName(obj.second->getGenNameInfo(), obj.first, false); |
| obj.second->restore(obj.first, getGlobalName); |
| } |
| } |
| } |
| |
| void NameSpace::preSave(GlobalNameSpace *globalNameSpace) { |
| if (m_type != NamedObjectType::TEXTURE) { |
| return; |
| } |
| // In case we loaded textures from a previous snapshot and have not yet |
| // restore them to GPU, we do the restoration here. |
| // TODO: skip restoration and write saveableTexture directly to the new |
| // snapshot |
| touchTextures(); |
| for (const auto& obj : m_objectDataMap) { |
| globalNameSpace->preSaveAddTex((TextureData*)obj.second.get()); |
| } |
| } |
| |
| void NameSpace::onSave(android::base::Stream* stream) { |
| stream->putBe32(m_objectDataMap.size()); |
| for (const auto& obj : m_objectDataMap) { |
| stream->putBe64(obj.first); |
| obj.second->onSave(stream, getGlobalName(obj.first)); |
| } |
| } |
| |
| static ObjectDataPtr* nullObjectData = new ObjectDataPtr; |
| static NamedObjectPtr* nullNamedObject = new NamedObjectPtr; |
| |
| ObjectLocalName |
| NameSpace::genName(GenNameInfo genNameInfo, ObjectLocalName p_localName, bool genLocal) |
| { |
| assert(m_type == genNameInfo.m_type); |
| ObjectLocalName localName = p_localName; |
| if (genLocal) { |
| do { |
| localName = ++m_nextName; |
| } while(localName == 0 || |
| nullptr != m_localToGlobalMap.getExceptZero_const(localName)); |
| } |
| |
| auto newObjPtr = NamedObjectPtr( new NamedObject(genNameInfo, m_globalNameSpace)); |
| m_localToGlobalMap.add(localName, newObjPtr); |
| |
| unsigned int globalName = newObjPtr->getGlobalName(); |
| m_globalToLocalMap.add(globalName, localName); |
| return localName; |
| } |
| |
| |
| unsigned int |
| NameSpace::getGlobalName(ObjectLocalName p_localName, bool* found) |
| { |
| auto objPtrPtr = m_localToGlobalMap.getExceptZero_const(p_localName); |
| |
| if (!objPtrPtr) { |
| if (found) *found = false; |
| return 0; |
| } |
| |
| if (found) *found = true; |
| auto res = (*objPtrPtr)->getGlobalName(); |
| return res; |
| } |
| |
| ObjectLocalName |
| NameSpace::getLocalName(unsigned int p_globalName) |
| { |
| auto localPtr = m_globalToLocalMap.get_const(p_globalName); |
| if (!localPtr) return 0; |
| return *localPtr; |
| } |
| |
| NamedObjectPtr NameSpace::getNamedObject(ObjectLocalName p_localName) { |
| auto objPtrPtr = m_localToGlobalMap.get_const(p_localName); |
| if (!objPtrPtr || !(*objPtrPtr)) return nullptr; |
| return *objPtrPtr; |
| } |
| |
| void |
| NameSpace::deleteName(ObjectLocalName p_localName) |
| { |
| auto objPtrPtr = m_localToGlobalMap.getExceptZero(p_localName); |
| if (objPtrPtr) { |
| m_globalToLocalMap.remove((*objPtrPtr)->getGlobalName()); |
| *objPtrPtr = *nullNamedObject; |
| m_localToGlobalMap.remove(p_localName); |
| } |
| |
| m_objectDataMap.erase(p_localName); |
| m_boundMap.remove(p_localName); |
| } |
| |
| bool |
| NameSpace::isObject(ObjectLocalName p_localName) |
| { |
| auto objPtrPtr = m_localToGlobalMap.getExceptZero_const(p_localName); |
| return nullptr != objPtrPtr; |
| } |
| |
| void |
| NameSpace::setGlobalObject(ObjectLocalName p_localName, |
| NamedObjectPtr p_namedObject) { |
| |
| auto objPtrPtr = m_localToGlobalMap.getExceptZero(p_localName); |
| if (objPtrPtr) { |
| m_globalToLocalMap.remove((*objPtrPtr)->getGlobalName()); |
| *objPtrPtr = p_namedObject; |
| } else { |
| m_localToGlobalMap.add(p_localName, p_namedObject); |
| } |
| |
| m_globalToLocalMap.add(p_namedObject->getGlobalName(), p_localName); |
| } |
| |
| void |
| NameSpace::replaceGlobalObject(ObjectLocalName p_localName, |
| NamedObjectPtr p_namedObject) |
| { |
| auto objPtrPtr = m_localToGlobalMap.getExceptZero(p_localName); |
| if (objPtrPtr) { |
| m_globalToLocalMap.remove((*objPtrPtr)->getGlobalName()); |
| *objPtrPtr = p_namedObject; |
| m_globalToLocalMap.add(p_namedObject->getGlobalName(), p_localName); |
| } |
| } |
| |
| // sets that the local name has been bound at least once, to save time later |
| void NameSpace::setBoundAtLeastOnce(ObjectLocalName p_localName) { |
| m_boundMap.add(p_localName, true); |
| } |
| |
| // sets that the local name has been bound at least once, to save time later |
| bool NameSpace::everBound(ObjectLocalName p_localName) const { |
| const bool* boundPtr = m_boundMap.get_const(p_localName); |
| if (!boundPtr) return false; |
| return *boundPtr; |
| } |
| |
| ObjectDataMap::const_iterator NameSpace::objDataMapBegin() const { |
| return m_objectDataMap.begin(); |
| } |
| |
| ObjectDataMap::const_iterator NameSpace::objDataMapEnd() const { |
| return m_objectDataMap.end(); |
| } |
| |
| const ObjectDataPtr& NameSpace::getObjectDataPtr( |
| ObjectLocalName p_localName) { |
| const auto it = m_objectDataMap.find(p_localName); |
| if (it != m_objectDataMap.end()) { |
| return it->second; |
| } |
| return *nullObjectData; |
| } |
| |
| void NameSpace::setObjectData(ObjectLocalName p_localName, |
| ObjectDataPtr data) { |
| m_objectDataMap[p_localName] = std::move(data); |
| } |
| |
| void GlobalNameSpace::preSaveAddEglImage(EglImage* eglImage) { |
| if (!eglImage->globalTexObj) { |
| GL_LOG("%p: egl image %p with null texture object", this, eglImage); |
| emugl::emugl_crash_reporter( |
| "Fatal: egl image with null texture object\n"); |
| } |
| unsigned int globalName = eglImage->globalTexObj->getGlobalName(); |
| android::base::AutoLock lock(m_lock); |
| |
| if (!globalName) { |
| GL_LOG("%p: egl image %p has 0 texture object", this, eglImage); |
| return; |
| } |
| |
| const auto& saveableTexIt = m_textureMap.find(globalName); |
| if (saveableTexIt == m_textureMap.end()) { |
| assert(eglImage->saveableTexture); |
| m_textureMap.emplace(globalName, eglImage->saveableTexture); |
| } else { |
| assert(m_textureMap[globalName] == eglImage->saveableTexture); |
| } |
| } |
| |
| void GlobalNameSpace::preSaveAddTex(TextureData* texture) { |
| android::base::AutoLock lock(m_lock); |
| const auto& saveableTexIt = m_textureMap.find(texture->getGlobalName()); |
| |
| if (!texture->getGlobalName()) { |
| GL_LOG("%p: texture data %p is 0 texture", this, texture); |
| return; |
| } |
| |
| if (saveableTexIt == m_textureMap.end()) { |
| assert(texture->getSaveableTexture()); |
| m_textureMap.emplace(texture->getGlobalName(), |
| texture->getSaveableTexture()); |
| } else { |
| assert(m_textureMap[texture->getGlobalName()] == |
| texture->getSaveableTexture()); |
| } |
| } |
| |
| void GlobalNameSpace::onSave(android::base::Stream* stream, |
| const ITextureSaverPtr& textureSaver, |
| SaveableTexture::saver_t saver) { |
| #if SNAPSHOT_PROFILE > 1 |
| int cleanTexs = 0; |
| int dirtyTexs = 0; |
| #endif // SNAPSHOT_PROFILE > 1 |
| saveCollection( |
| stream, m_textureMap, |
| [saver, &textureSaver |
| #if SNAPSHOT_PROFILE > 1 |
| , &cleanTexs, &dirtyTexs |
| #endif // SNAPSHOT_PROFILE > 1 |
| ]( |
| android::base::Stream* stream, |
| const std::pair<const unsigned int, SaveableTexturePtr>& |
| tex) { |
| stream->putBe32(tex.first); |
| #if SNAPSHOT_PROFILE > 1 |
| if (tex.second.get() && tex.second->isDirty()) { |
| dirtyTexs ++; |
| } else { |
| cleanTexs ++; |
| } |
| #endif // SNAPSHOT_PROFILE > 1 |
| textureSaver->saveTexture( |
| tex.first, |
| [saver, &tex](android::base::Stream* stream, |
| ITextureSaver::Buffer* buffer) { |
| if (!tex.second.get()) return; |
| saver(tex.second.get(), stream, buffer); |
| }); |
| }); |
| clearTextureMap(); |
| #if SNAPSHOT_PROFILE > 1 |
| printf("Dirty texture saved %d, clean texture saved %d\n", |
| dirtyTexs, cleanTexs); |
| #endif // SNAPSHOT_PROFILE > 1 |
| } |
| |
| void GlobalNameSpace::onLoad(android::base::Stream* stream, |
| const ITextureLoaderWPtr& textureLoaderWPtr, |
| SaveableTexture::creator_t creator) { |
| const ITextureLoaderPtr textureLoader = textureLoaderWPtr.lock(); |
| assert(m_textureMap.size() == 0); |
| if (!textureLoader->start()) { |
| fprintf(stderr, |
| "Error: texture file unsupported version or corrupted.\n"); |
| emugl::emugl_crash_reporter( |
| "Error: texture file unsupported version or corrupted.\n"); |
| return; |
| } |
| loadCollection( |
| stream, &m_textureMap, |
| [this, creator, textureLoaderWPtr](android::base::Stream* stream) { |
| unsigned int globalName = stream->getBe32(); |
| // A lot of function wrapping happens here. |
| // When touched, saveableTexture triggers |
| // textureLoader->loadTexture, which sets up the file position |
| // and the mutex, and triggers saveableTexture->loadFromStream |
| // for the real loading. |
| SaveableTexture* saveableTexture = creator( |
| this, [globalName, textureLoaderWPtr]( |
| SaveableTexture* saveableTexture) { |
| auto textureLoader = textureLoaderWPtr.lock(); |
| if (!textureLoader) return; |
| textureLoader->loadTexture( |
| globalName, |
| [saveableTexture]( |
| android::base::Stream* stream) { |
| saveableTexture->loadFromStream(stream); |
| }); |
| }); |
| return std::make_pair(globalName, |
| SaveableTexturePtr(saveableTexture)); |
| }); |
| |
| m_backgroundLoader = |
| std::make_shared<GLBackgroundLoader>( |
| textureLoaderWPtr, *m_eglIface, *m_glesIface, m_textureMap); |
| textureLoader->acquireLoaderThread(m_backgroundLoader); |
| } |
| |
| void GlobalNameSpace::clearTextureMap() { |
| decltype(m_textureMap)().swap(m_textureMap); |
| } |
| |
| void GlobalNameSpace::postLoad(android::base::Stream* stream) { |
| m_backgroundLoader->start(); |
| m_backgroundLoader.reset(); // leave it to TextureLoader |
| } |
| |
| const SaveableTexturePtr& GlobalNameSpace::getSaveableTextureFromLoad( |
| unsigned int oldGlobalName) { |
| assert(m_textureMap.count(oldGlobalName)); |
| return m_textureMap[oldGlobalName]; |
| } |