Skip to content

Commit 87f72d7

Browse files
Spruill-1Copilot
andcommitted
Release v1.5.0
Post-merge fixes + version bump. Fixed (post-merge integration): * DXGI Desktop Duplication / Windows Graphics Capture sources only captured one frame then went stale. SourceNodeFactory::TickAndUploadLiveCaptures was defined but never called. Wired into OnRenderTick before the needsEval gate so live captures advance every tick; the helper marks captured nodes dirty which trips the existing gate and triggers re-eval + present. * Cannot delete the last Output node (regression caught during post-merge validation). EffectGraph::RemoveNode previously refused to delete the last Output 'always keep at least one'. The user- facing consequence: closing an Output's external window via the X button removed the window but left a dangling Output node in the graph. Lifted the protection. The render path tolerates an output-less graph fine -- nothing is needed so evaluation no-ops until the user adds a new Output node. Both right-click->Delete on the canvas and X-button-on-window paths now work end-to-end. * Image Path / Browse... UI showed for live-capture sources where it makes no sense. Filter the panel out for IsDxgiDuplicateOutput and IsWindowsGraphicsCapture nodes. Version bump: Version.h 1.4.1 -> 1.5.0; Package.appxmanifest 1.4.0 -> 1.5.0. CHANGELOG.md [Unreleased] section finalized as [1.5.0] 2026-05-06 with a release header summarizing the work. Build clean, 113/113 tests pass, all 3 headless smokes green (PNG capture, FP32 pixel readback, script batch). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6bc3ec2 commit 87f72d7

7 files changed

Lines changed: 65 additions & 30 deletions

File tree

CHANGELOG.md

Lines changed: 25 additions & 16 deletions
Large diffs are not rendered by default.

Controls/NodeGraphController.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,6 @@ namespace ShaderLab::Controls
552552

553553
for (uint32_t nodeId : m_selection.selectedNodeIds)
554554
{
555-
// RemoveNode() already protects the last Output node.
556555
m_graph->RemoveNode(nodeId);
557556
m_visuals.erase(nodeId);
558557
}

Graph/EffectGraph.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ namespace ShaderLab::Graph
2323

2424
void EffectGraph::RemoveNode(uint32_t nodeId)
2525
{
26-
// Protect the last Output node — always keep at least one.
27-
auto* target = FindNode(nodeId);
28-
if (target && target->type == NodeType::Output)
29-
{
30-
auto outputIds = GetOutputNodeIds();
31-
if (outputIds.size() <= 1)
32-
return; // Refuse to delete the last Output node.
33-
}
26+
// Note: previously this method refused to delete the last Output
27+
// node ("always keep at least one"). That protection was removed
28+
// because closing an Output node's external window now removes
29+
// the node (PresentOutputWindows in MainWindow.xaml.cpp), and
30+
// refusing the removal left the graph with a dangling Output
31+
// node and no window. The render path tolerates an output-less
32+
// graph just fine -- nothing is "needed" so evaluation no-ops
33+
// until the user adds a new Output node.
3434

3535
// Remove all edges referencing this node.
3636
std::erase_if(m_edges, [nodeId](const EffectEdge& e)

MainWindow.RenderTick.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ namespace winrt::ShaderLab::implementation
3939
// up live values immediately without hooking every AddNode site.
4040
UpdateWorkingSpaceNodes();
4141

42+
// Tick live capture providers (DXGI Desktop Duplication, Windows
43+
// Graphics Capture). These don't go through the dirty/video-
44+
// provider path the rest of the source-prep loop uses, so call
45+
// their dedicated tick here. A captured frame marks the source
46+
// node dirty so the existing needsEval gate triggers a re-eval
47+
// and present.
48+
if (auto* dc5 = static_cast<ID2D1DeviceContext5*>(m_renderEngine.D2DDeviceContext()))
49+
{
50+
auto& nodes = const_cast<std::vector<::ShaderLab::Graph::EffectNode>&>(m_graph.Nodes());
51+
if (m_sourceFactory.TickAndUploadLiveCaptures(nodes, dc5))
52+
m_forceRender = true;
53+
}
54+
4255
// Tick clock nodes: advance time.
4356
for (auto& node : const_cast<std::vector<::ShaderLab::Graph::EffectNode>&>(m_graph.Nodes()))
4457
{

MainWindow.xaml.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2784,17 +2784,31 @@ namespace winrt::ShaderLab::implementation
27842784

27852785
// ---- Image source: file path + Browse button ----
27862786
bool isVideoSource = false;
2787+
bool isLiveCaptureSource = false;
27872788
{
27882789
auto isVideoIt = node->properties.find(L"IsVideo");
27892790
if (isVideoIt != node->properties.end())
27902791
{
27912792
auto* bv = std::get_if<bool>(&isVideoIt->second);
27922793
if (bv && *bv) isVideoSource = true;
27932794
}
2795+
// DXGI Desktop Duplication / Windows Graphics Capture sources
2796+
// own their bitmap from a live capture provider; they have no
2797+
// file path to browse to and the "Image Path" UI is misleading.
2798+
for (const auto* key : { L"IsDxgiDuplicateOutput", L"IsWindowsGraphicsCapture" })
2799+
{
2800+
auto it = node->properties.find(key);
2801+
if (it != node->properties.end())
2802+
{
2803+
auto* bv = std::get_if<bool>(&it->second);
2804+
if (bv && *bv) { isLiveCaptureSource = true; break; }
2805+
}
2806+
}
27942807
}
27952808

27962809
if (node->type == ::ShaderLab::Graph::NodeType::Source &&
2797-
!(node->effectClsid.has_value()) && !isVideoSource)
2810+
!(node->effectClsid.has_value()) &&
2811+
!isVideoSource && !isLiveCaptureSource)
27982812
{
27992813
auto pathLabel = Controls::TextBlock();
28002814
pathLabel.Text(L"Image Path");

Package.appxmanifest

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<Identity
1010
Name="ShaderLab"
1111
Publisher="CN=ShaderLab"
12-
Version="1.4.0.0" />
12+
Version="1.5.0.0" />
1313

1414
<mp:PhoneIdentity PhoneProductId="a1b2c3d4-e5f6-7890-abcd-ef1234567890" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
1515

Version.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
namespace ShaderLab
1414
{
1515
constexpr uint32_t VersionMajor = 1;
16-
constexpr uint32_t VersionMinor = 4;
17-
constexpr uint32_t VersionPatch = 1;
16+
constexpr uint32_t VersionMinor = 5;
17+
constexpr uint32_t VersionPatch = 0;
1818

1919
// Human-readable version string.
20-
constexpr const wchar_t* VersionString = L"1.4.1";
20+
constexpr const wchar_t* VersionString = L"1.5.0";
2121

2222
// Graph format version. Increment when serialization format changes.
2323
// Graphs saved with a higher format version cannot be loaded by older apps.

0 commit comments

Comments
 (0)