From 0843a0bb177109b70ffd4ee2cacc14b8d014e706 Mon Sep 17 00:00:00 2001 From: Abhishek Gola Date: Mon, 6 Jul 2026 13:36:53 +0530 Subject: [PATCH] fusion fix --- modules/dnn/src/net_impl.cpp | 8 +++++++ modules/dnn/src/net_impl.hpp | 15 ++++++++++++ modules/dnn/src/net_impl2.cpp | 44 +++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/modules/dnn/src/net_impl.cpp b/modules/dnn/src/net_impl.cpp index b2c07c6584..796d9a1a2d 100644 --- a/modules/dnn/src/net_impl.cpp +++ b/modules/dnn/src/net_impl.cpp @@ -154,6 +154,9 @@ void Net::Impl::clear() prepared = false; finalizeLayers = true; + finalized = false; + fusedSnapshotValid = false; + fusedSnapshot.clear(); } @@ -1419,6 +1422,9 @@ void Net::Impl::getLayerShapes(const ShapesVec& netInputShapes, LayerShapes& shapes) { if (mainGraph) { + // Fusion emits block-layout layers; their TransformLayout conversions are + // only inserted by finalize(), so shape inference must run post-finalize. + finalize(); std::vector shapeCache; std::vector typeCache; CV_Assert(layerId == 0); @@ -2551,6 +2557,7 @@ int64 Net::Impl::getFLOPS(const std::vector& netInputShapes, const std::vector& netInputTypes) /*const*/ { if (mainGraph) { + finalize(); // The new graph engine executes in FP32 on CPU regardless of the requested // target, so FP16 input types (e.g. coming from an OpenCL FP16 target) would be // rejected by Layer::getTypes(). Normalize them to FP32 for shape/FLOPS inference. @@ -2584,6 +2591,7 @@ int64 Net::Impl::getFLOPS( const std::vector& netInputTypes) /*const*/ { if (mainGraph) { + finalize(); std::vector inputTypes = filterFP16InputTypes(netInputTypes); LayerShapes shapes; std::vector shapeCache; diff --git a/modules/dnn/src/net_impl.hpp b/modules/dnn/src/net_impl.hpp index 2deee06458..6afdaadb71 100644 --- a/modules/dnn/src/net_impl.hpp +++ b/modules/dnn/src/net_impl.hpp @@ -144,6 +144,18 @@ struct Net::Impl : public detail::NetImplBase bool prepared; // need to rerun graph transformations/optimizations bool finalizeLayers; // need to initialize each layer bool finalized = false; // executors have been selected for the current backend/target + + // Post-fusion (pre block-layout) snapshot so finalize() can re-run from a clean + // state on a backend/target change; useBlockLayout() is destructive and must + // run after backend assignment (see deviceOp handling in graph_block_layout.cpp). + struct FusedGraphSnapshot { + Ptr graph; + std::vector > prog; + std::vector > inputs; + std::vector > outputs; + }; + bool fusedSnapshotValid = false; + std::vector fusedSnapshot; std::vector > argWrappers; std::vector argWrapperData; TracingMode tracingMode; @@ -426,6 +438,9 @@ struct Net::Impl : public detail::NetImplBase void finalize(); // Selects executors for a single graph (recursing into subgraphs). void finalizeGraph(const Ptr& graph, bool useCUDA); + // Save/restore the fused graph so finalize() is re-entrant across backend changes. + void saveFusedSnapshot(); + void restoreFusedSnapshot(); #ifdef HAVE_CUDA Ptr getCudaArgWrapper(Arg arg, Mat& hostMat); #endif diff --git a/modules/dnn/src/net_impl2.cpp b/modules/dnn/src/net_impl2.cpp index b0b66017b9..b9c526a915 100644 --- a/modules/dnn/src/net_impl2.cpp +++ b/modules/dnn/src/net_impl2.cpp @@ -624,6 +624,41 @@ void Net::Impl::finalizeGraph(const Ptr& graph, bool useCUDA) } } +void Net::Impl::saveFusedSnapshot() +{ + fusedSnapshot.clear(); + for (const Ptr& g : allgraphs) { + FusedGraphSnapshot snap; + snap.graph = g; + const std::vector >& prog = g->prog(); + snap.prog = prog; + snap.inputs.reserve(prog.size()); + snap.outputs.reserve(prog.size()); + for (const Ptr& op : prog) { + snap.inputs.push_back(op ? op->inputs : std::vector()); + snap.outputs.push_back(op ? op->outputs : std::vector()); + } + fusedSnapshot.push_back(std::move(snap)); + } +} + +void Net::Impl::restoreFusedSnapshot() +{ + // Roll the graph back to its post-fusion state: undo the layer-input rewiring + // and remove the TransformLayout ops inserted by a previous useBlockLayout(). + for (const FusedGraphSnapshot& snap : fusedSnapshot) { + for (size_t i = 0; i < snap.prog.size(); i++) { + const Ptr& op = snap.prog[i]; + if (!op) + continue; + op->inputs = snap.inputs[i]; + op->outputs = snap.outputs[i]; + } + snap.graph->setProg(snap.prog); + } + totalLayers = updateGraphOfs(mainGraph, 0, true); +} + void Net::Impl::finalize() { #ifdef HAVE_ONNXRUNTIME @@ -637,6 +672,15 @@ void Net::Impl::finalize() if (finalized) return; + // Snapshot the fused graph once so finalize() can re-run cleanly on a + // backend/target change (block layout + buffer assignment are destructive). + if (!fusedSnapshotValid) { + saveFusedSnapshot(); + fusedSnapshotValid = true; + } else { + restoreFusedSnapshot(); + } + bool useCUDA = false; #ifdef HAVE_CUDA argWrappers.clear();