Skip to content

Reference from devices pane - #24

Open
atkurtul wants to merge 3 commits into
devfrom
reference-from-devices-pane
Open

Reference from devices pane#24
atkurtul wants to merge 3 commits into
devfrom
reference-from-devices-pane

Conversation

@atkurtul

Copy link
Copy Markdown
Collaborator

No description provided.

@shamilatesoglu shamilatesoglu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against the settings and device subsystems and the engine's runner/plugin threading, since this change makes something outside the graph drive node state. The Firmware Info fix and the settings-callback hardening are correct, and the devices pane is the right home for the reference. Two items need addressing: the reference listener mutates node state from a non-runner thread, and the frame rate warning fires permanently on Free Run. Two more have no line to anchor to: WaitVBLNode.cpp:73 still says "Check reference source property", which no longer exists, and the Firmware Info commit has an empty body for a bug that is not visible in the diff.

Comment thread Source/ChannelNode.cpp
Comment on lines +276 to +281
void SubscribeToReference()
{
if (!Device || ReferenceListenerId)
return;
ReferenceListenerId = Device->AddReferenceSourceListener([this](NTV2ReferenceSource) { UpdateReferenceSource(); });
UpdateReferenceSource();

@shamilatesoglu shamilatesoglu Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listener runs on the thread that called SetReference, which for a devices pane change is the plugin manager thread (OnMessageFromEditor -> UpdateSettingsCallback -> SetReference), while node state is owned by the runner thread (PathRunner.cpp:537,557). UpdateReferenceSource() therefore writes Channel::StatusMessages concurrently with Channel::Open/Close on the runner, and UpdateStatus() iterates that same map. A lock is the wrong tool: the callback does driver register reads, and the runner reaches the same path from IncrementDropCount on the per-frame drop path. UpdateDeviceProperties can publish the reference on the device instead, and nosEngine.SetNodeDirty(NodeId) returns the per-node warning to the runner via EnqueueOrCallNodeDirtied (RunnerRouter.cpp:68-98).

Comment thread Source/ChannelNode.cpp
Comment on lines +317 to 323
CurrentChannel.SetStatus(aja::Channel::StatusType::Reference, fb::NodeStatusMessageType::INFO,
"Reference: " + Device->ReferenceSourceToString(curRef), "", 0, false);

if (IsInput)
return;

if (GetFrameRateFamily(refFrameRate) != GetFrameRateFamily(FrameRate))

@shamilatesoglu shamilatesoglu Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetReferenceAndFrameRate has no case NTV2_REFERENCE_FREERUN, so refFrameRate stays NTV2_FRAMERATE_UNKNOWN and GetFrameRateFamily returns NTV2_FRAMERATE_INVALID, which never matches a real family. Every output channel on a Free Run device will show "Reference incompatible with frame rate" permanently. IsExternallySynced (WaitVBLNode.cpp:336) already treats FREERUN and an unknown reference rate as their own case rather than a mismatch. This also warns spuriously at load, since SubscribeToReference runs from the Device pin watcher while FrameRate is still INVALID.

Comment thread Source/ChannelNode.cpp
return;
device->RemoveReferenceSourceListener(*ReferenceListenerId);
ReferenceListenerId.reset();
CurrentChannel.ClearStatus(Channel::StatusType::Reference);

@shamilatesoglu shamilatesoglu Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReferenceInvalid is not cleared here. Setting the Device pin to None unsubscribes and leaves Device null, so nothing re-evaluates the warning and it stays on the node with no device attached.

Comment thread Source/ChannelNode.cpp
channelPin.is_interlaced = !IsProgressivePicture(format);
CurrentChannel.Update(std::move(channelPin), true);
// The frame rate may have changed, so re-check it against the reference.
UpdateReferenceSource();

@shamilatesoglu shamilatesoglu Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TryUpdateChannel returns early when !ShouldOpen and when the format is UNKNOWN, so neither the reference line nor the warning is refreshed on those paths and the previous status persists.

Comment thread Source/AJADevice.cpp
Comment on lines +1114 to +1122
// Remember the selection first, so that a later ClearState() restores it instead of the default.
SelectedReference = referenceSource;

NTV2ReferenceSource curRef{};
if ((!GetReference(curRef) || curRef != referenceSource) && !SetReference(referenceSource))
{
nosEngine.LogE("Device %s: Failed to set reference source to '%s'", GetDisplayName().c_str(), referenceValue.c_str());
return false;
}

@shamilatesoglu shamilatesoglu Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SelectedReference is assigned before the card accepts the value, so a refused reference is still stored and re-applied by the next ClearState(); assigning after SetReference succeeds avoids that. Separately, when the card already holds the requested reference SetReference is skipped and listeners are not notified, which makes the notification non-idempotent.

Comment thread Source/AJADevice.cpp
Comment on lines +302 to +305
// Reporting failure makes the settings subsystem keep the default value instead of persisting
// a reference the card never accepted.
if (!device->second->UpdateReferenceSource(nos::InterpretObjectData<const char>(itemValue), false))
return NOS_RESULT_FAILED;

@shamilatesoglu shamilatesoglu Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The behaviour is correct: OnMessageFromEditor skips UpdateEntry and logs "last value remains" when the callback fails (EntryPoint.cpp:120). The comment says default, but what the subsystem keeps is the last value.

Comment thread Source/AJADevice.cpp
ReferenceListeners.Map.erase(id);
}

bool AJADevice::SetReference(const NTV2ReferenceSource inRefSource, const bool inKeepFramePulseSelect)

@shamilatesoglu shamilatesoglu Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dispatching under the lock is load-bearing: RemoveReferenceSourceListener takes the same mutex, so ~ChannelNode on the runner blocks until an in-flight callback finishes, which is what keeps the captured this alive. Worth stating in a comment so the dispatch is not moved out of the lock later.

Comment thread Source/AJADevice.cpp
Comment on lines +361 to 367
// Must outlive the RegisterDevice call below: driverProp.Value is a borrowed pointer into it.
std::string driverPropMessage;
nosDeviceProperty driverProp{};
bool isFirmwareValid = true;
if (!CheckFirmware(firmwareMsg, firmwareMsgDetails)) {
std::string driverPropMessage = firmwareMsg + "\n Details: " + firmwareMsgDetails;
driverPropMessage = firmwareMsg + "\n Details: " + firmwareMsgDetails;
driverProp = {.Name = nos::Name("Firmware Info"), .Value = driverPropMessage.c_str()};

@shamilatesoglu shamilatesoglu Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct fix. DeviceManager::RegisterDevice copies Value into a std::string (sys-device/Source/DeviceSubsystem.cpp:93), so the previous block-scoped string was destroyed before the subsystem read it.

Comment thread Source/Migrations.cpp
Comment on lines +91 to +93
NTV2ReferenceSource parsed{};
if (device->ParseReferenceSource(refValue, parsed))
device->UpdateReferenceSource(refValue, true);

@shamilatesoglu shamilatesoglu Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipping "None" here avoids an error log for a value that never named a reference. The value is parsed twice though, once for the guard and once inside UpdateReferenceSource; a non-logging variant would avoid the second pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants