no more blocker

This commit is contained in:
2026-07-07 20:22:12 +08:00
parent e356329dc9
commit 47da444d74
4 changed files with 88 additions and 199 deletions

View File

@@ -91,21 +91,20 @@ int main(int argc, char **argv)
// QML item tree
{ QStringLiteral("listQuickItems"), QStringLiteral("List the QQuickItem tree (QML item types, names, hierarchy)") },
// SG node selection
// TODO: re-enable when the selection-sync / sub-model-population bug is fixed
// { QStringLiteral("selectScenegraphNode"), QStringLiteral("Select a SG node by address (from listScenegraphNodes) so geometry/material sub-models populate for it") },
// { QStringLiteral("selectScenegraphNode/address"), QStringLiteral("Node address (e.g. 0x56396e6d7400, from listScenegraphNodes)") },
{ QStringLiteral("selectScenegraphNode"), QStringLiteral("Select a SG node by address (from listScenegraphNodes) so geometry/material sub-models populate for it") },
{ QStringLiteral("selectScenegraphNode/address"), QStringLiteral("Node address (e.g. 0x56396e6d7400, from listScenegraphNodes)") },
// Geometry
// { QStringLiteral("getNodeVertices"), QStringLiteral("Get vertex data for a GeometryNode (vertices, attributes, isCoordinate flags)") },
// { QStringLiteral("getNodeVertices/address"), QStringLiteral("Node address (must be a GeometryNode)") },
// { QStringLiteral("getNodeAdjacency"), QStringLiteral("Get adjacency/index data for a GeometryNode (drawing mode + index list)") },
// { QStringLiteral("getNodeAdjacency/address"), QStringLiteral("Node address (must be a GeometryNode)") },
{ QStringLiteral("getNodeVertices"), QStringLiteral("Get vertex data for a GeometryNode (vertices, attributes, isCoordinate flags)") },
{ QStringLiteral("getNodeVertices/address"), QStringLiteral("Node address (must be a GeometryNode)") },
{ QStringLiteral("getNodeAdjacency"), QStringLiteral("Get adjacency/index data for a GeometryNode (drawing mode + index list)") },
{ QStringLiteral("getNodeAdjacency/address"), QStringLiteral("Node address (must be a GeometryNode)") },
// Material/Shader
// { QStringLiteral("getMaterialShaders"), QStringLiteral("List shader stages (Vertex, Fragment, ...) for a node's material") },
// { QStringLiteral("getMaterialShaders/address"), QStringLiteral("Node address (must be a GeometryNode with a material)") },
// { QStringLiteral("getShaderSource"), QStringLiteral("Get shader source code for a shader stage (async: waits for probe response)") },
// { QStringLiteral("getShaderSource/row"), QStringLiteral("Row index from getMaterialShaders (0 = first shader stage)") },
// { QStringLiteral("getMaterialProperties"), QStringLiteral("Get material properties (name/value pairs) for a node's material") },
// { QStringLiteral("getMaterialProperties/address"), QStringLiteral("Node address (must be a GeometryNode with a material)") },
{ QStringLiteral("getMaterialShaders"), QStringLiteral("List shader stages (Vertex, Fragment, ...) for a node's material") },
{ QStringLiteral("getMaterialShaders/address"), QStringLiteral("Node address (must be a GeometryNode with a material)") },
{ QStringLiteral("getShaderSource"), QStringLiteral("Get shader source code for a shader stage (async: waits for probe response)") },
{ QStringLiteral("getShaderSource/row"), QStringLiteral("Row index from getMaterialShaders (0 = first shader stage)") },
{ QStringLiteral("getMaterialProperties"), QStringLiteral("Get material properties (name/value pairs) for a node's material") },
{ QStringLiteral("getMaterialProperties/address"), QStringLiteral("Node address (must be a GeometryNode with a material)") },
// Rendering visualization
{ QStringLiteral("setRenderMode"), QStringLiteral("Set custom render mode for visualization") },
{ QStringLiteral("setRenderMode/mode"), QStringLiteral("Render mode: NormalRendering, VisualizeClipping, VisualizeOverdraw, VisualizeBatches, VisualizeChanges, or VisualizeTraces") },

View File

@@ -398,9 +398,6 @@ QString SceneGraphTools::listScenegraphNodes() const
}
// --- SG node selection ---
// TODO: all code below is disabled until the selection-sync / sub-model-population
// bug is fixed. See PLAN.md "Known issues / Blocked" section.
#if 0
QModelIndex SceneGraphTools::findNodeByAddress(QAbstractItemModel *m, const QString &address) const
{
@@ -466,10 +463,11 @@ QString SceneGraphTools::ensureNodeSelected(const QString &address) const
QStringLiteral("node %1 not found in scene graph tree (still loading?)").arg(address)))
.toJson(QJsonDocument::Compact));
// Create the selection model client BEFORE selecting. This:
// 1. Sends ObjectMonitored to the server, activating the ServerProxyModel
// 2. Starts the 125ms default-selection timer
// 3. Enables echo-back from the server's SelectionModelServer
auto *selModel = m_session->selectionModel(sgModel);
if (!selModel)
return QString::fromUtf8(QJsonDocument(errorJson(
QStringLiteral("selection model not available"))).toJson(QJsonDocument::Compact));
// CRITICAL: Create sub-model RemoteModels BEFORE sending the selection.
// The probe populates and resets sub-models (vertex, adjacency, shader,
@@ -491,62 +489,22 @@ QString SceneGraphTools::ensureNodeSelected(const QString &address) const
QString::fromUtf8(kQuickInspectorIface),
"setSlowMode", QVariantList{ false });
// Wait long enough for:
// 1. ObjectMonitored messages to reach the server and start monitoring
// 2. SelectionModelStateRequest (125ms timer from SelectionModelClient ctor)
// to be sent and the server's response (default selection) to arrive
// 3. Any SG model updates from setSlowMode(false) to settle
// Wait for the SelectionModelClient's 125ms default-selection timer to fire
// and settle before our override, so they don't race.
QEventLoop loop;
settle(loop, 2000);
settle(loop, 300);
// Now send the selection. The server should now be in a stable state with
// the default selection already processed. Our selection will override it.
selModel->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current);
m_selectedAddress = address;
// Also manually construct and send a SelectionModelSelect message directly
// to the server's selection model address, bypassing SelectionModelClient.
// This eliminates any issues with SelectionModelClient state.
{
const auto selAddr = GammaRay::Endpoint::instance()->objectAddress(
QStringLiteral("%1.selection").arg(QString::fromUtf8(kQuickSceneGraphModel)));
if (selAddr != GammaRay::Protocol::InvalidObjectAddress) {
// Check the path length
const auto mi = GammaRay::Protocol::fromQModelIndex(idx);
int pathLen = mi.size();
// Log the path for debugging
QString pathStr;
for (int i = 0; i < mi.size(); ++i)
pathStr += QStringLiteral("(%1,%2) ").arg(mi[i].row).arg(mi[i].column);
GammaRay::Message msg(selAddr, GammaRay::Protocol::SelectionModelSelect);
msg << qint32(1); // 1 range
msg << mi << mi; // topLeft, bottomRight
msg << (QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current);
GammaRay::Endpoint::send(msg);
// Store path info for diagnostics
m_lastSelectPath = QStringLiteral("len=%1 %2").arg(pathLen).arg(pathStr);
}
}
// Select the node via client-side selection model. This sends a
// SelectionModelSelect message to the server, which processes it and
// triggers sgSelectionChanged -> PropertyController::setObject(),
// populating the sub-models with the selected node's data.
if (selModel && idx.isValid())
selModel->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current);
// Wait for the selection to be processed and sub-model data to arrive.
settle(loop, 2000);
// Re-send via direct message to ensure it sticks.
{
const auto selAddr = GammaRay::Endpoint::instance()->objectAddress(
QStringLiteral("%1.selection").arg(QString::fromUtf8(kQuickSceneGraphModel)));
if (selAddr != GammaRay::Protocol::InvalidObjectAddress) {
GammaRay::Message msg(selAddr, GammaRay::Protocol::SelectionModelSelect);
const auto mi = GammaRay::Protocol::fromQModelIndex(idx);
msg << qint32(1);
msg << mi << mi;
msg << (QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current);
GammaRay::Endpoint::send(msg);
}
}
settle(loop, 2000);
m_selectedAddress = address;
// Prime sub-models to fetch their row/column counts and cell data.
if (vModel) { vModel->rowCount(); primeAndWait(vModel, 4, 300, 500, 1500); }
@@ -572,64 +530,20 @@ QString SceneGraphTools::selectScenegraphNode(const QString &address) const
type = sgModel->data(typeIdx, Qt::DisplayRole).toString();
}
// Check selection state and sub-model row counts
auto *sgModel2 = m_session->model(QString::fromUtf8(kQuickSceneGraphModel));
auto *selModel = m_session->selectionModel(sgModel2);
// Check sub-model row counts
auto *vModel = m_session->model(QStringLiteral("%1.sgGeometryVertexModel").arg(QString::fromUtf8(kBaseName)));
auto *aModel = m_session->model(QStringLiteral("%1.sgGeometryAdjacencyModel").arg(QString::fromUtf8(kBaseName)));
auto *sModel = m_session->model(QStringLiteral("%1.shaderModel").arg(QString::fromUtf8(kBaseName)));
auto *mModel = m_session->model(QStringLiteral("%1.materialPropertyModel").arg(QString::fromUtf8(kBaseName)));
// Diagnostic: check if models are registered on the probe (have valid addresses)
auto *ep = GammaRay::Endpoint::instance();
const auto vAddr = ep ? ep->objectAddress(QStringLiteral("%1.sgGeometryVertexModel").arg(QString::fromUtf8(kBaseName))) : 0;
const auto aAddr = ep ? ep->objectAddress(QStringLiteral("%1.sgGeometryAdjacencyModel").arg(QString::fromUtf8(kBaseName))) : 0;
const auto sAddr = ep ? ep->objectAddress(QStringLiteral("%1.shaderModel").arg(QString::fromUtf8(kBaseName))) : 0;
const auto mAddr = ep ? ep->objectAddress(QStringLiteral("%1.materialPropertyModel").arg(QString::fromUtf8(kBaseName))) : 0;
const auto selAddr = ep ? ep->objectAddress(QStringLiteral("%1.selection").arg(QString::fromUtf8(kQuickSceneGraphModel))) : 0;
// Also check the wrong name (in case source model name mismatch)
const auto dotSelAddr = ep ? ep->objectAddress(QStringLiteral(".selection")) : 0;
// Check what the selected index looks like
QString selIdxInfo;
if (selModel && selModel->hasSelection()) {
auto rows = selModel->selectedRows();
if (rows.isEmpty()) {
selIdxInfo = QStringLiteral("hasSelection=true but selectedRows() empty");
} else {
// Walk the parent chain to get the full path
QStringList pathParts;
QModelIndex cur = rows.first();
while (cur.isValid()) {
QString addr = cur.data(Qt::DisplayRole).toString();
pathParts.prepend(QStringLiteral("r%1:%2").arg(cur.row()).arg(addr));
cur = cur.parent();
}
selIdxInfo = pathParts.join(" -> ");
}
} else {
selIdxInfo = QStringLiteral("no selection");
}
QJsonObject out = {
{ QStringLiteral("address"), address },
{ QStringLiteral("type"), type },
{ QStringLiteral("hasGeometry"), vModel && vModel->rowCount() > 0 },
{ QStringLiteral("hasMaterial"), sModel && sModel->rowCount() > 0 },
{ QStringLiteral("_diag_epConnected"), ep ? ep->isConnected() : false },
{ QStringLiteral("_diag_selModelAddr"), qint64(selAddr) },
{ QStringLiteral("_diag_selHasSelection"), selModel ? selModel->hasSelection() : false },
{ QStringLiteral("_diag_selIdxInfo"), selIdxInfo },
{ QStringLiteral("_diag_vertexModelAddr"), qint64(vAddr) },
{ QStringLiteral("_diag_adjacencyModelAddr"), qint64(aAddr) },
{ QStringLiteral("_diag_shaderModelAddr"), qint64(sAddr) },
{ QStringLiteral("_diag_materialPropModelAddr"), qint64(mAddr) },
{ QStringLiteral("_diag_vertexRowCount"), vModel ? vModel->rowCount() : -1 },
{ QStringLiteral("_diag_adjacencyRowCount"), aModel ? aModel->rowCount() : -1 },
{ QStringLiteral("_diag_shaderRowCount"), sModel ? sModel->rowCount() : -1 },
{ QStringLiteral("_diag_materialPropRowCount"), mModel ? mModel->rowCount() : -1 },
{ QStringLiteral("_diag_vertexColCount"), vModel ? vModel->columnCount() : -1 },
{ QStringLiteral("_diag_vertexData00"), vModel && vModel->rowCount() > 0 && vModel->columnCount() > 0 ? vModel->data(vModel->index(0, 0)).toString() : QString() },
{ QStringLiteral("vertexCount"), vModel ? vModel->rowCount() : 0 },
{ QStringLiteral("shaderCount"), sModel ? sModel->rowCount() : 0 },
{ QStringLiteral("propertyCount"), mModel ? mModel->rowCount() : 0 },
};
return QString::fromUtf8(QJsonDocument(out).toJson(QJsonDocument::Compact));
}
@@ -818,8 +732,6 @@ QString SceneGraphTools::getMaterialProperties(const QString &address) const
return QString::fromUtf8(QJsonDocument(out).toJson(QJsonDocument::Compact));
}
#endif // disabled until selection-sync bug is fixed
// --- Rendering visualization ---
QString SceneGraphTools::setRenderMode(const QString &mode) const

View File

@@ -20,10 +20,10 @@
-----------------------
Geometry/material/texture sub-models are populated by the probe-side
PropertyController only for the *currently selected* SG node. Selection is
driven via ObjectBroker::selectionModel(sgModel) which returns a synced
SelectionModelClient. Calling select(index, ClearAndSelect) on it sends the
selection to the probe, which calls setObject() on all PropertyController
extensions, which in turn populate sgGeometryVertexModel, shaderModel, etc.
driven via client-side selModel->select() which sends a SelectionModelSelect
message to the probe. The server's translateSelection() resolves the path,
QItemSelectionModel::select() fires on the probe side, sgSelectionChanged()
triggers PropertyController::setObject() locally, populating sub-models.
Tools that need per-node data call ensureNodeSelected(address) first, which
finds the node index in the (lazily-fetched) SG tree and selects it.
*/
@@ -57,33 +57,30 @@ public:
// --- SceneGraph navigation ---
Q_INVOKABLE QString listScenegraphNodes() const;
// TODO: re-enable when the selection-sync / sub-model-population bug is fixed
// Find a node by address (e.g. "0x56396e6d7400") in the SG tree and select it
// via the synced selection model. Returns JSON with the node's type and which
// sub-models have data. Must be called before geometry/material tools can
// via the probe-side Q_INVOKABLE method. Returns JSON with the node's type and
// which sub-models have data. Must be called before geometry/material tools can
// return data for that node. Selecting a non-GeometryNode clears the
// geometry/material models (they only populate for QSGGeometryNode).
// Q_INVOKABLE QString selectScenegraphNode(const QString &address) const;
Q_INVOKABLE QString selectScenegraphNode(const QString &address) const;
// --- Geometry (requires a GeometryNode to be selected) ---
// TODO: re-enable when the selection-sync / sub-model-population bug is fixed
// Returns vertex data: rows = vertices, columns = attributes.
// Each cell is the display string (e.g. "0.5, 0.5, 0, 1"); headers are
// attribute type names (PositionAttribute, ColorAttribute, ...).
// Also includes isCoordinate per column.
// Q_INVOKABLE QString getNodeVertices(const QString &address) const;
Q_INVOKABLE QString getNodeVertices(const QString &address) const;
// Returns adjacency data: drawing mode + index list.
// Q_INVOKABLE QString getNodeAdjacency(const QString &address) const;
Q_INVOKABLE QString getNodeAdjacency(const QString &address) const;
// --- Material/Shader (requires a GeometryNode to be selected) ---
// TODO: re-enable when the selection-sync / sub-model-population bug is fixed
// List shader stages (Vertex, Fragment, ...) for the selected node's material.
// Q_INVOKABLE QString getMaterialShaders(const QString &address) const;
Q_INVOKABLE QString getMaterialShaders(const QString &address) const;
// Async: request shader source for a row (from getMaterialShaders).
// Uses the MaterialExtensionInterface proxy + gotShader signal.
// Q_INVOKABLE QString getShaderSource(int row) const;
Q_INVOKABLE QString getShaderSource(int row) const;
// Material properties (name/value pairs from AggregatedPropertyModel).
// Q_INVOKABLE QString getMaterialProperties(const QString &address) const;
Q_INVOKABLE QString getMaterialProperties(const QString &address) const;
// --- Rendering visualization ---
// Set custom render mode. mode ∈ {NormalRendering, VisualizeClipping,
@@ -95,7 +92,7 @@ public:
private:
GammaRaySession *m_session;
mutable QString m_selectedAddress; // last selected node address (for sub-model tools)
mutable QString m_lastSelectPath; // diagnostic: last selection path info
QString ensureSession() const;
// Find the QModelIndex for address in the SG tree (prime + search).