size_t HdPerfLog::GetCacheMisses(TfToken const& name) { _Lock lock(_mutex); if (HdPerfLog::_CacheEntry* value = TfMapLookupPtr(_cacheMap, name)) return value->GetMisses(); return 0; }
double HdPerfLog::GetCacheHitRatio(TfToken const& name) { _Lock lock(_mutex); if (HdPerfLog::_CacheEntry* value = TfMapLookupPtr(_cacheMap, name)) { return value->GetHitRatio(); } return 0.0; }
/*virtual*/ VtValue UsdMayaGLBatchRenderer::TaskDelegate::Get( SdfPath const& id, TfToken const &key) { _ValueCache *vcache = TfMapLookupPtr(_valueCacheMap, id); VtValue ret; if( vcache && TfMapLookup(*vcache, key, &ret) ) return ret; TF_CODING_ERROR("%s:%s doesn't exist in the value cache\n", id.GetText(), key.GetText()); return VtValue(); }
void HdRenderIndex::InsertRprim(TfToken const& typeId, HdSceneDelegate* sceneDelegate, SdfPath const& rprimId, SdfPath const& instancerId /*= SdfPath()*/) { HD_TRACE_FUNCTION(); HF_MALLOC_TAG_FUNCTION(); if (ARCH_UNLIKELY(TfMapLookupPtr(_rprimMap, rprimId))) { return; } SdfPath const &sceneDelegateId = sceneDelegate->GetDelegateID(); if (!rprimId.HasPrefix(sceneDelegateId)) { TF_CODING_ERROR("Scene Delegate Id (%s) must prefix prim Id (%s)", sceneDelegateId.GetText(), rprimId.GetText()); return; } HdRprim *rprim = _renderDelegate->CreateRprim(typeId, rprimId, instancerId); if (rprim == nullptr) { return; } _rprimIds.Insert(rprimId); _tracker.RprimInserted(rprimId, rprim->GetInitialDirtyBitsMask()); _AllocatePrimId(rprim); _RprimInfo info = { sceneDelegate, rprim }; _rprimMap[rprimId] = std::move(info); SdfPath instanceId = rprim->GetInstancerId(); if (!instanceId.IsEmpty()) { _tracker.InstancerRPrimInserted(instanceId, rprimId); } }
PXR_NAMESPACE_OPEN_SCOPE // --------------------------------------------------------------------------- // Delegate implementation. /* virtual */ VtValue HdStreamTaskController::_Delegate::Get(SdfPath const& id, TfToken const& key) { _ValueCache *vcache = TfMapLookupPtr(_valueCacheMap, id); VtValue ret; if (vcache && TfMapLookup(*vcache, key, &ret)) { return ret; } TF_CODING_ERROR("%s:%s doesn't exist in the value cache\n", id.GetText(), key.GetText()); return VtValue(); }
/*virtual*/ bool HdxSelectionTracker::GetBuffers(HdRenderIndex const* index, VtIntArray* offsets) const { TRACE_FUNCTION(); TfAutoMallocTag2 tag("Hdx", "HdxSelection::GetBuffers"); // XXX: Set minimum size for UBO/SSBO requirements. Seems like this should // be handled by Hydra. Update all uses of minSize below when resolved. const int minSize = 8; size_t numPrims = _selection ? _selection->selectedPrims.size() : 0; if (numPrims == 0) { TF_DEBUG(HDX_SELECTION_SETUP).Msg("No selected prims\n"); offsets->resize(minSize); return false; } // Note that numeric_limits<float>::min for is surprising, so using lowest() // here instead. Doing this for <int> here to avoid copy and paste bugs. int min = std::numeric_limits<int>::max(), max = std::numeric_limits<int>::lowest(); std::vector<int> ids; ids.resize(numPrims); size_t const N = 1000; int const INVALID = -1; WorkParallelForN(numPrims/N + 1, [&ids, &index, this](size_t begin, size_t end) mutable { end = std::min(end*N, ids.size()); begin = begin*N; for (size_t i = begin; i < end; i++) { if (auto const& rprim = index->GetRprim(_selection->selectedPrims[i])) { ids[i] = rprim->GetPrimId(); } else { // silently ignore non-existing prim ids[i] = INVALID; } } }); for (int id : ids) { if (id == INVALID) continue; min = std::min(id, min); max = std::max(id, max); } if (max < min) { offsets->resize(minSize); return false; } // ---------------------------------------------------------------------- // // Buffer Layout // ---------------------------------------------------------------------- // // In the folowing code, we want to build up a buffer that is capable of // driving selection highlighting. To do this, we leverage the fact that all // shaders have access to the drawing coord, namely the ObjectID, // InstanceID, FaceID, VertexID, etc. The idea is to take one such ID and // compare it against a range of known selected values within a given range. // // For example, imaging the ObjectID is 6, then we can know this object is // selected if ID 6 is in the range of selected objects. We then // hierarchically re-apply this scheme for instances and faces. The buffer // layout is as follows: // // Object: [ start index | end index | (offset to next level per object) ] // // So to test if a given object ID is selected, we check if the ID is in the // range [start,end), if so, the object's offset in the buffer is ID-start. // // The value for an object is one of three cases: // // 0 - indicates the object is not selected // 1 - indicates the object is fully selected // N - an offset to the next level of the hierarchy // // The structure described above for objects is also applied for each level // of instancing as well as for faces. All data is aggregated into a single // buffer with the following layout: // // [ object | element | instance level-N | ... | level 0 ] // // Each section above is prefixed with [start,end) ranges and the values of // each range follow the three cases outlined. // // To see these values built incrementally, enable the TF_DEBUG flag // HDX_SELECTION_SETUP. // ---------------------------------------------------------------------- // // Start with individual arrays. Splice arrays once finished. int const SELECT_ALL = 1; int const SELECT_NONE = 0; _DebugPrintArray("ids", ids); std::vector<int> output; output.insert(output.end(), 2+1+max-min, SELECT_NONE); output[0] = min; output[1] = max+1; // XXX: currently, _selectedPrims may have duplicated entries // (e.g. to instances and to faces) for an objPath. // this would cause unreferenced offset buffer allocated in the // result buffer. _DebugPrintArray("objects", output); for (size_t primIndex = 0; primIndex < ids.size(); primIndex++) { // TODO: store ID and path in "ids" vector SdfPath const& objPath = _selection->selectedPrims[primIndex]; int id = ids[primIndex]; if (id == INVALID) continue; TF_DEBUG(HDX_SELECTION_SETUP).Msg("Processing: %d - %s\n", id, objPath.GetText()); // ------------------------------------------------------------------ // // Elements // ------------------------------------------------------------------ // // Find element sizes, for this object. int elemOffset = output.size(); if (VtIntArray const *faceIndices = TfMapLookupPtr(_selection->selectedFaces, objPath)) { if (faceIndices->size()) { int minElem = std::numeric_limits<int>::max(); int maxElem = std::numeric_limits<int>::lowest(); for (int const& elemId : *faceIndices) { minElem = std::min(minElem, elemId); maxElem = std::max(maxElem, elemId); } // Grow the element array to hold elements for this object. output.insert(output.end(), maxElem-minElem+1+2, SELECT_NONE); output[elemOffset+0] = minElem; output[elemOffset+1] = maxElem+1; for (int elemId : *faceIndices) { // TODO: Add support for edge and point selection. output[2+elemOffset+ (elemId-minElem)] = SELECT_ALL; } _DebugPrintArray("elements", output); } else { // Entire object/instance is selected elemOffset = SELECT_ALL; } } else { // Entire object/instance is selected elemOffset = SELECT_ALL; } // ------------------------------------------------------------------ // // Instances // ------------------------------------------------------------------ // // Initialize prevLevel to elemOffset which removes a special case in // the loops below. int prevLevelOffset = elemOffset; if (std::vector<VtIntArray> const * a = TfMapLookupPtr(_selection->selectedInstances, objPath)) { // Different instances can have different number of levels. int numLevels = std::numeric_limits<int>::max(); size_t numInst= a->size(); if (numInst == 0) { numLevels = 0; } else { for (size_t instNum = 0; instNum < numInst; ++instNum) { size_t levelsForInst = a->at(instNum).size(); numLevels = std::min(numLevels, static_cast<int>(levelsForInst)); } } TF_DEBUG(HDX_SELECTION_SETUP).Msg("NumLevels: %d\n", numLevels); if (numLevels == 0) { output[id-min+2] = elemOffset; } for (int level = 0; level < numLevels; ++level) { // Find the required size of the instance vectors. int levelMin = std::numeric_limits<int>::max(); int levelMax = std::numeric_limits<int>::lowest(); for (VtIntArray const &instVec : *a) { _DebugPrintArray("\tinstVec", instVec, false); int instId = instVec[level]; levelMin = std::min(levelMin, instId); levelMax = std::max(levelMax, instId); } TF_DEBUG(HDX_SELECTION_SETUP).Msg( "level-%d: min(%d) max(%d)\n", level, levelMin, levelMax); int objLevelSize = levelMax - levelMin +2+1; int levelOffset = output.size(); output.insert(output.end(), objLevelSize, SELECT_NONE); output[levelOffset + 0] = levelMin; output[levelOffset + 1] = levelMax + 1; for (VtIntArray const& instVec : *a) { int instId = instVec[level] - levelMin+2; output[levelOffset+instId] = prevLevelOffset; } if (level == numLevels-1) { output[id-min+2] = levelOffset; } if (ARCH_UNLIKELY(TfDebug::IsEnabled(HDX_SELECTION_SETUP))){ std::stringstream name; name << "level[" << level << "]"; _DebugPrintArray(name.str(), output); } prevLevelOffset = levelOffset; } } else { output[id-min+2] = elemOffset; } } _DebugPrintArray("final output", output); offsets->resize(output.size()); for (size_t i = 0; i < output.size(); i++) { (*offsets)[i] = output[i]; } _DebugPrintArray("final output", *offsets); return true; }
const HdxIntersector::Hit * UsdMayaGLBatchRenderer::_GetHitInfo( M3dView& view, unsigned int pickResolution, bool singleSelection, const SdfPath& sharedId, const GfMatrix4d &localToWorldSpace) { // Guard against user clicking in viewer before renderer is setup if( !_renderIndex ) return NULL; // Selection only occurs once per display refresh, with all usd objects // simulataneously. If the selectQueue is not empty, that means that // a refresh has occurred, and we need to perform a new selection operation. if( !_selectQueue.empty() ) { TF_DEBUG(PXRUSDMAYAGL_QUEUE_INFO).Msg( "____________ SELECTION STAGE START ______________ (singleSelect = %d)\n", singleSelection ); GfMatrix4d viewMatrix; GfMatrix4d projectionMatrix; px_LegacyViewportUtils::GetViewSelectionMatrices(view, &viewMatrix, &projectionMatrix); // As Maya doesn't support batched selection, intersection testing is // actually performed in the first selection query that happens after a // render. This query occurs in the local space of SOME object, but // we need results in world space so that we have results for every // node available. worldToLocalSpace removes the local space we // happen to be in for the initial query. GfMatrix4d worldToLocalSpace(localToWorldSpace.GetInverse()); _intersector->SetResolution(GfVec2i(pickResolution, pickResolution)); HdxIntersector::Params qparams; qparams.viewMatrix = worldToLocalSpace * viewMatrix; qparams.projectionMatrix = projectionMatrix; qparams.alphaThreshold = 0.1; _selectResults.clear(); for( const auto &renderSetIter : _selectQueue ) { const RenderParams &renderParams = renderSetIter.second.first; const _SdfPathSet &renderPaths = renderSetIter.second.second; SdfPathVector roots(renderPaths.begin(), renderPaths.end()); TF_DEBUG(PXRUSDMAYAGL_QUEUE_INFO).Msg( "--- pickQueue, batch %zx, size %zu\n", renderSetIter.first, renderPaths.size()); TfToken colName = renderParams.geometryCol; HdRprimCollection rprims(colName, renderParams.drawRepr); rprims.SetRootPaths(roots); rprims.SetRenderTags(renderParams.renderTags); qparams.cullStyle = renderParams.cullStyle; qparams.renderTags = renderParams.renderTags; HdxIntersector::Result result; HdxIntersector::HitVector hits; glPushAttrib(GL_VIEWPORT_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_TEXTURE_BIT | GL_POLYGON_BIT); bool r = _intersector->Query(qparams, rprims, &_hdEngine, &result); glPopAttrib(); if( !r ) { continue; } if( singleSelection ) { hits.resize(1); if( !result.ResolveNearest(&hits.front()) ) continue; } else if( !result.ResolveAll(&hits) ) { continue; } for (const HdxIntersector::Hit& hit : hits) { auto itIfExists = _selectResults.insert( std::pair<SdfPath, HdxIntersector::Hit>(hit.delegateId, hit)); const bool &inserted = itIfExists.second; if( inserted ) continue; HdxIntersector::Hit& existingHit = itIfExists.first->second; if( hit.ndcDepth < existingHit.ndcDepth ) existingHit = hit; } } if( singleSelection && _selectResults.size()>1 ) { TF_DEBUG(PXRUSDMAYAGL_QUEUE_INFO).Msg( "!!! multiple singleSel hits found: %zu\n", _selectResults.size()); auto minIt=_selectResults.begin(); for( auto curIt=minIt; curIt!=_selectResults.end(); curIt++ ) { const HdxIntersector::Hit& curHit = curIt->second; const HdxIntersector::Hit& minHit = minIt->second; if( curHit.ndcDepth < minHit.ndcDepth ) minIt = curIt; } if( minIt!=_selectResults.begin() ) _selectResults.erase(_selectResults.begin(),minIt); minIt++; if( minIt!=_selectResults.end() ) _selectResults.erase(minIt,_selectResults.end()); } if( TfDebug::IsEnabled(PXRUSDMAYAGL_QUEUE_INFO) ) { for ( const auto &selectPair : _selectResults) { const SdfPath& path = selectPair.first; const HdxIntersector::Hit& hit = selectPair.second; cout << "NEW HIT: " << path << endl; cout << "\tdelegateId: " << hit.delegateId << endl; cout << "\tobjectId: " << hit.objectId << endl; cout << "\tndcDepth: " << hit.ndcDepth << endl; } } // As we've cached the results in pickBatches, we // can clear out the selection queue. _selectQueue.clear(); // Selection can happen after a refresh but before a draw call, so // clear out the render queue as well _renderQueue.clear(); // If nothing was selected, the view does not refresh, but this // means _selectQueue will not get processed again even if the // user attempts another selection. We fix the renderer state by // scheduling another refresh when the view is next idle. if( _selectResults.empty() ) view.scheduleRefresh(); TF_DEBUG(PXRUSDMAYAGL_QUEUE_INFO).Msg( "^^^^^^^^^^^^ SELECTION STAGE FINISH ^^^^^^^^^^^^^\n"); } return TfMapLookupPtr( _selectResults, sharedId ); }