sebastian.vesenmayer Posted August 17, 2021 Share Posted August 17, 2021 Hello, I want to read data from objects of classes Like Mesh or ObjectMeshStatic in another thread. But when I do this the microprofiler is not working anymore. Is it safe to use read only methods on Unigine classes from another thread? Regards Sebastian Link to comment
rohit.gonsalves Posted August 17, 2021 Share Posted August 17, 2021 Dear @sebastian.vesenmayer, The question looks very dangerous situation. How I will solve this? UNIGINE main thread is running. Prepare a data structure you want to access from another thread. Probably vertices data. Write a callback on Render class for end of frame CALLBACK_END. Copy required data from Mesh or any UNIGINE object to your data structure as required. You are free to read this data from any other thread with at least one frame latency. So you need to hook it correctly for your workflow. BINGO, It works. Only thing is we are creating redundancy for data. Rohit Link to comment
sebastian.vesenmayer Posted August 17, 2021 Author Share Posted August 17, 2021 @rohit.gonsalves you are right, that it is a dangerous situation because nobody knows what the engine does during this time and if objects are still alive. And yes I want to avoid data redundancy, because i need the complete node hierarchy, positions and orientations. Thanks Link to comment
silent Posted August 18, 2021 Share Posted August 18, 2021 Sebastian, Can you please specify what exactly do you want to do? Which methods are being called? Some example test code will be also a good addition. Thanks! How to submit a good bug report --- FTP server for test scenes and user uploads: ftp://files.unigine.com user: upload password: 6xYkd6vLYWjpW6SN Link to comment
sebastian.vesenmayer Posted August 18, 2021 Author Share Posted August 18, 2021 (edited) I want to access Meshes and Matrices from Node Trees to reduce draw calls when objects are not animated anymore in a separate thread. The merged data will be copied from main thread when it is ready. Do I need own classes for matix vector multiplication or can I use Unigine classes for this in an own thread? Edited August 18, 2021 by sebastian.vesenmayer Link to comment
silent Posted August 18, 2021 Share Posted August 18, 2021 Right now the proposed solution from Rohit should work. To be able to tell more we would need see some code that you are already using. It's also not completely clear why Microprofile is not working, more likely something very bad happens :) How to submit a good bug report --- FTP server for test scenes and user uploads: ftp://files.unigine.com user: upload password: 6xYkd6vLYWjpW6SN Link to comment
sebastian.vesenmayer Posted August 18, 2021 Author Share Posted August 18, 2021 m_optimizerThread=std::make_unique<std::thread>([this] { m_mergedMaterials.clear(); m_mergedMeshes.clear(); Unigine::Vector<Unigine::NodePtr> hierarchy; m_rootNode->getHierarchy(hierarchy); for (auto& node : hierarchy) { if (node->getType() == Unigine::Node::TYPE::OBJECT_MESH_STATIC && node->isEnabled()) { //create transformation matrix for the mesh auto transformNode = node; auto meshMatrix = transformNode->getTransform(); while (transformNode = transformNode->getParent()) { if (transformNode->getParent()) { meshMatrix = transformNode->getTransform() * meshMatrix; } if (m_aboutToDestroy) { return; } } auto objectMeshStatic = Unigine::static_ptr_cast<Unigine::ObjectMeshStatic>(node); //Check for material, add it to list of materials and get index for referencing if (objectMeshStatic->getNumSurfaces() < 1) { continue; } auto usedMaterial = std::string(objectMeshStatic->getMaterial(0)->getName()); auto result = std::find(m_mergedMaterials.begin(), m_mergedMaterials.end(), usedMaterial); auto index = m_mergedMaterials.size(); MergedMesh* mergedMesh = nullptr; if (result != m_mergedMaterials.end()) { index = std::distance(m_mergedMaterials.begin(), result); mergedMesh = &(m_mergedMeshes[index]); } else { m_mergedMaterials.push_back(usedMaterial); m_mergedMeshes.emplace_back(); mergedMesh = &(m_mergedMeshes.back()); } //get mesh data of this node and append it transformed to the combined mesh Unigine::MeshPtr toCopyMesh = Unigine::Mesh::create(); if (objectMeshStatic->getMesh(toCopyMesh)) { for (int i = 0, j = toCopyMesh->getNumSurfaces(); i < j; ++i) { auto vertices = toCopyMesh->getVertices(i); auto texCoords = toCopyMesh->getTexCoords0(i); auto tangents = toCopyMesh->getTangents(i); auto indices = toCopyMesh->getCIndices(i); if (mergedMesh->surface.size() <= i) { mergedMesh->surface.emplace_back(); mergedMesh->surface.back().minVisibleDistance= objectMeshStatic->getMinVisibleDistance(i); mergedMesh->surface.back().maxVisibleDistance= objectMeshStatic->getMaxVisibleDistance(i); } auto& surface = mergedMesh->surface[i]; auto startIndex = surface.mergedVertices.size(); for (int k = 0, l = vertices.size(); k < l; ++k) { auto vertex = meshMatrix * vertices[k]; surface.mergedVertices.push_back(vertices[k]); surface.mergedTexCoords.push_back(texCoords[k]); surface.mergedTangents.push_back(tangents[k]); if (m_aboutToDestroy) { return; } } for (int k = 0, l = indices.size(); k < l; ++k) { surface.mergedIndices.push_back(startIndex + indices[k]); if (m_aboutToDestroy) { return; } } } } } if (m_aboutToDestroy) { return; } } //check for object destruction if (m_aboutToDestroy) return; m_processingOptimization = false; m_optimizationReady = true; } ); That is what I want to do. Link to comment
andrey-kozlov Posted August 18, 2021 Share Posted August 18, 2021 Hello Sebastian, It's not clear which call is problematic here. It also depends on what the other threads do. The general recommendation is not to communicate with nodes, objects, materials etc. when the main loop is executed. Especially when Engine::swap is executed because this is the place where nodes deferred deletion occurs. (https://developer.unigine.com/en/docs/2.14.1/code/fundamentals/thread_safety/?rlang=cpp#dependent_objects ) That's why we provide CPUShader class which is always synchornized with Engine::swap (you can try it https://developer.unigine.com/en/docs/2.14.1/api/library/rendering/class.cpushader?rlang=cpp#runAsync_int_void ) It's totally fine to work with Mesh and mat4 classes in the async thread though. You may go to the gray zone and try to read data in the async thread for the sake of performance. But in that case every scenario is unique and we need to localize which call is problematic. The fact that microprofile doesn't work signals that something really bad happened. A minimal reproducer can be useful. Link to comment
sebastian.vesenmayer Posted August 18, 2021 Author Share Posted August 18, 2021 When I read this correctly from the documentation, main thread waits for cpu shaders to finish and can block the main thread. Not what I want. Looping over hierarchy may be the problem, so I guess I need to copy mesh data from the node tree first and continue in another thread with the calculation. Unigine::Vector<Unigine::NodePtr> hierarchy; m_rootNode->getHierarchy(hierarchy); If it is safe to use Unigine::Math classes in another thread there will be no problem I guess. Link to comment
andrey-kozlov Posted August 18, 2021 Share Posted August 18, 2021 Some other interaction with nodes may be problematic too. Obvious example objectMeshStatic->getMesh(toCopyMesh) if objectMeshStatic is being deleted in the main thread at the moment Link to comment
sebastian.vesenmayer Posted August 19, 2021 Author Share Posted August 19, 2021 I have the implementation in the main thread now and I am going to implement another threaded codepath, when models get to big I am going to switch to the threaded path because of the high amount on matrix/vertex calculations. I have another question on the tangents. I don't know exactly how to transform them, when I bake transformations into a mesh. How should I apply the matrix on the tangents quaternion? Thanks Link to comment
andrey-kozlov Posted August 19, 2021 Share Posted August 19, 2021 You can transform quat to matrix with quat::getMat3, apply transformations, and then transform back with quat::set Link to comment
Recommended Posts