KiCad PCB EDA Suite
Loading...
Searching...
No Matches
conn_records.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "conn_records.h"
21#include "conn_tasks.h"
22
23#include <wx/thread.h>
24#include <algorithm>
25#include <stdexcept>
26
27namespace SCH_CONNECTIVITY
28{
30 m_keys( aKeys ),
31 m_parses( aVersions ),
32 m_sources( aVersions, KEY_LESS{ aKeys } ),
33 m_records( aVersions, KEY_LESS{ aKeys } )
34{
35}
36
37void RECORD_STORE::Update( const std::vector<FRAME_INSTANCE>& aFrame, INPUT_STORE& aInputs,
38 const BUS_ALIASES& aAliases )
39{
40 wxASSERT( wxThread::IsMain() );
41 try
42 {
43 const bool aliasesChanged = m_parses.SetAliases( aAliases );
44 bool claimNamesChanged = false;
45 std::vector<const SOURCE_CACHE::ENTRY*> sources;
46 RECORD_INPUT input{ false, false, {} };
47 std::set<INST_ID> liveInstances;
48 std::set<INST_ID> changedInstances;
49 std::set<RECORD_KEY, RECORD_ORDER> liveRecords;
50 struct RECORD_JOB
51 {
52 RECORD_KEY key;
53 RECORD_INPUT input;
54 RECORD_GEOMETRY geometry;
55 INSTANCE_CLAIMS claims;
56 };
57 std::vector<RECORD_JOB> jobs;
58 const auto eraseSource = [&]( const ITEM_KEY& key )
59 {
60 if( !claimNamesChanged )
61 {
62 const auto* source = m_sources.Find( key );
63 claimNamesChanged = source && source->value.claim.has_value();
64 }
65
66 m_sources.Erase( key );
67 };
68
69 for( const FRAME_INSTANCE& frame : aFrame )
70 {
71 const INST_ID instance = frame.scope.instance;
72
73 if( !liveInstances.insert( instance ).second )
74 throw std::invalid_argument( "Duplicate connectivity frame instance" );
75
76 const auto* facts = aInputs.FindScreen( frame.screen );
77 const auto* text = aInputs.FindInstance( instance );
78
79 if( !facts || !text )
80 throw std::invalid_argument( "Connectivity frame inputs were not captured" );
81
82 auto old = m_prepared.find( instance );
83
84 const bool instanceChanged = old == m_prepared.end() || aliasesChanged
85 || old->second.sourceVersion != facts->version
86 || old->second.textVersion != text->version
87 || old->second.scope != frame.scope;
88
89 if( !instanceChanged )
90 continue;
91
92 const auto symbolSource = aInputs.VerifiedSymbolSource( frame.screen );
93 const bool reuseClaims = old != m_prepared.end() && symbolSource
94 && old->second.symbolSource == symbolSource && !aliasesChanged
95 && old->second.textVersion == text->version && old->second.scope == frame.scope
96 && facts->value.ruleAreas.empty() && text->value.ruleAreas.empty()
97 && old->second.factCount == facts->value.items.size()
98 && std::ranges::all_of( facts->value.items, [&]( const ITEM_FACT& fact )
99 {
100 if( fact.type != SCH_LINE_T )
101 return true;
102
103 const auto* source = m_sources.Find( { fact.id, instance } );
104 return source && source->value.type == SCH_LINE_T;
105 } );
106
107 // Geometry still changes island membership even when each source's claims are unchanged
108 if( reuseClaims )
109 {
110 old->second.sourceVersion = facts->version;
111 }
112 else
113 {
114 auto prepared = PrepareInstanceClaims( facts->value, text->value, frame.scope, m_keys, m_parses );
115 PREPARED_INSTANCE stamp{ facts->version, text->version, frame.scope, symbolSource,
116 facts->value.items.size(), {} };
117 stamp.items.reserve( prepared.items.size() );
118
119 for( auto& [id, source] : prepared.items )
120 {
121 if( !claimNamesChanged )
122 {
123 const auto* previous = m_sources.Find( { id, instance } );
124 const auto* oldClaim = previous && previous->value.claim ? &*previous->value.claim : nullptr;
125 claimNamesChanged = source.claim ? !oldClaim || source.claim->name != oldClaim->name
126 : oldClaim != nullptr;
127 }
128
129 m_sources.Set( { id, instance }, std::move( source ) );
130 stamp.items.push_back( id );
131 }
132
133 if( old != m_prepared.end() )
134 {
135 for( const KIID& id : old->second.items )
136 {
137 if( !std::binary_search( stamp.items.begin(), stamp.items.end(), id ) )
138 eraseSource( { id, instance } );
139 }
140 }
141
142 m_prepared.insert_or_assign( instance, std::move( stamp ) );
143 }
144
145 changedInstances.insert( instance );
146 const auto& islands = aInputs.Islands( frame.screen, text->value.units ).value;
147
148 for( const ISLAND& island : islands.islands )
149 {
150 const RECORD_KEY key{ instance, island.anchor };
151
152 if( !liveRecords.insert( key ).second )
153 throw std::logic_error( "Duplicate connectivity island anchor" );
154
155 sources.clear();
156 sources.reserve( island.items.size() );
157 input.hasWire = island.hasWire;
158 input.hasBusLine = island.hasBusLine;
159 input.items.clear();
160 input.items.reserve( island.items.size() );
161
162 for( const KIID& id : island.items )
163 {
164 const auto* source = m_sources.Find( { id, instance } );
165
166 if( !source )
167 throw std::logic_error( "Connectivity island source was not prepared" );
168
169 sources.push_back( source );
170 input.items.emplace_back( id, source->version );
171 }
172
173 const auto previous = m_recordInputs.find( key );
174
175 if( previous != m_recordInputs.end() && previous->second == input )
176 continue;
177
178 INSTANCE_CLAIMS claims;
179 claims.instance = instance;
180
181 for( size_t i = 0; i < island.items.size(); ++i )
182 claims.items.emplace( island.items[i], sources[i]->value );
183
184 jobs.push_back( { key, input, { island.items, input.hasWire, input.hasBusLine },
185 std::move( claims ) } );
186 }
187 }
188
189 std::vector<ISLAND_RECORD> results( jobs.size() );
190 ParallelFor( jobs.size(), [&]( size_t i )
191 {
192 results[i] = BuildIslandRecord( jobs[i].geometry, jobs[i].claims, m_keys );
193 } );
194
195 for( size_t i = 0; i < jobs.size(); ++i )
196 {
197 m_records.Set( jobs[i].key, std::move( results[i] ) );
198 m_recordInputs.insert_or_assign( jobs[i].key, std::move( jobs[i].input ) );
199 }
200
201 for( auto it = m_prepared.begin(); it != m_prepared.end(); )
202 {
203 if( liveInstances.contains( it->first ) )
204 {
205 ++it;
206 continue;
207 }
208
209 for( const KIID& id : it->second.items )
210 eraseSource( { id, it->first } );
211
212 it = m_prepared.erase( it );
213 }
214
215 for( auto it = m_recordInputs.begin(); it != m_recordInputs.end(); )
216 {
217 if( ( liveInstances.contains( it->first.inst ) && !changedInstances.contains( it->first.inst ) )
218 || liveRecords.contains( it->first ) )
219 {
220 ++it;
221 continue;
222 }
223
224 m_records.Erase( it->first );
225 it = m_recordInputs.erase( it );
226 }
227
228 if( claimNamesChanged )
229 {
230 std::set<wxString> liveText;
231
232 for( const auto& [key, source] : m_sources.Entries() )
233 {
234 if( source->value.claim )
235 liveText.insert( m_keys.Name( source->value.claim->name ) );
236 }
237
238 m_parses.Retain( liveText );
239 }
240 }
241 catch( ... )
242 {
243 Clear();
244 throw;
245 }
246}
247
249{
250 m_sources.Clear();
251 m_records.Clear();
252 m_prepared.clear();
253 m_recordInputs.clear();
254 m_parses.Retain( {} );
255}
256} // namespace SCH_CONNECTIVITY
Definition kiid.h:46
One sequence for all cache tables for the lifetime of an engine session.
Definition conn_cache.h:37
Main-thread extraction cache.
Definition conn_inputs.h:41
const ISLAND_CACHE::ENTRY & Islands(SCREEN_ID aScreen, const UNIT_SIGNATURE &aUnits)
const SCREEN_CACHE::ENTRY * FindScreen(SCREEN_ID aScreen) const
Definition conn_inputs.h:94
const INSTANCE_CACHE::ENTRY * FindInstance(INST_ID aInstance) const
Definition conn_inputs.h:95
std::optional< std::pair< SCREEN_ID, uint64_t > > VerifiedSymbolSource(SCREEN_ID aScreen) const
std::map< INST_ID, PREPARED_INSTANCE > m_prepared
std::map< RECORD_KEY, RECORD_INPUT, RECORD_ORDER > m_recordInputs
void Update(const std::vector< FRAME_INSTANCE > &aFrame, INPUT_STORE &aInputs, const BUS_ALIASES &aAliases)
RECORD_STORE(CACHE_VERSIONS &aVersions, SESSION_KEYS &aKeys)
Session IDs are dense handles, never a canonical ordering.
Definition conn_keys.h:146
Value keys and the key session of the schematic connectivity engine.
std::map< wxString, std::vector< wxString > > BUS_ALIASES
Bus alias table, from alias name to member texts.
Definition conn_bus.h:40
uint32_t INST_ID
Session handle of a sheet instance KIID_PATH.
Definition conn_keys.h:41
void ParallelFor(size_t aCount, FUNCTION &&aFunction, thread_pool &aPool=GetKiCadThreadPool())
Independent ordinal writes only; preparation and cache commits stay on the caller thread.
Definition conn_tasks.h:72
INSTANCE_CLAIMS PrepareInstanceClaims(const SCREEN_FACTS &aFacts, const INSTANCE_FACTS &aText, const INSTANCE_SCOPE &aScope, SESSION_KEYS &aKeys, BUS_PARSE_CACHE &aParses)
Main-thread interning and parse preparation, followed by a pure per-island fold.
One entry of the captured hierarchy.
Definition conn_frame.h:48
std::map< KIID, SOURCE_CLAIMS > items
Definition conn_claims.h:97
The smallest set of items on one screen that the drawing connects.
A value copy of one connectable item, or of one group of pins that share a number and a position.
Definition conn_facts.h:101
One item or pin in one sheet instance.
Definition conn_keys.h:77
Orders keys by value through SESSION_KEYS::Less().
Definition conn_keys.h:255
Physical inputs to the island record fold.
One island in one sheet instance.
Definition conn_keys.h:87
Input versions and scope that produced the current source claims of one instance.
Everything BuildIslandRecord() reads, as island flags and source claim versions.
@ SCH_LINE_T
Definition typeinfo.h:159